Skip to content

Commit

Permalink
cilium: ipsec, add cleanup xfrm routine
Browse files Browse the repository at this point in the history
After a timeout delete xfrm rules not matching current version. In
this way we can perform non-disruptive updates to the version. This
still requires a cilium restart however until we attach an inotify
event to the file.

xfrm state cleanup is still an over-approximation because states do
not use mark values yet. A follow up series can clean this up.

Signed-off-by: John Fastabend <john.fastabend@gmail.com>
  • Loading branch information
jrfastab authored and ianvernon committed Mar 22, 2019
1 parent b698972 commit 3f12fb6
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 1 deletion.
65 changes: 64 additions & 1 deletion pkg/datapath/linux/ipsec/ipsec_linux.go
Expand Up @@ -27,6 +27,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"

"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
"github.com/cilium/cilium/pkg/logging/logfields"
Expand Down Expand Up @@ -181,6 +182,41 @@ func ipSecDeletePolicy(src, local net.IP) error {
return nil
}

func ipsecDeleteXfrmSpi(spi uint8) {
var err error
scopedLog := log.WithFields(logrus.Fields{
"spi": spi,
})

xfrmPolicyList, err := netlink.XfrmPolicyList(0)
if err != nil {
scopedLog.WithError(err).Warning("deleting previous SPI, xfrm policy list error")
return
}

for _, p := range xfrmPolicyList {
if p.Tmpls[0].Spi != int(spi) &&
((p.Mark != nil && (p.Mark.Value&linux_defaults.RouteMarkMask) == linux_defaults.RouteMarkDecrypt) ||
(p.Mark != nil && (p.Mark.Value&linux_defaults.RouteMarkMask) == linux_defaults.RouteMarkEncrypt)) {
if err := netlink.XfrmPolicyDel(&p); err != nil {
scopedLog.WithError(err).Warning("deleting old xfrm policy failed")
}
}
}
xfrmStateList, err := netlink.XfrmStateList(0)
if err != nil {
scopedLog.WithError(err).Warning("deleting previous SPI, xfrm state list error")
return
}
for _, s := range xfrmStateList {
if s.Spi != int(spi) {
if err := netlink.XfrmStateDel(&s); err != nil {
scopedLog.WithError(err).Warning("deleting old xfrm state failed")
}
}
}
}

/* UpsertIPsecEndpoint updates the IPSec context for a new endpoint inserted in
* the ipcache. Currently we support a global crypt/auth keyset that will encrypt
* all traffic between endpoints. An IPSec context consists of two pieces a policy
Expand Down Expand Up @@ -309,10 +345,14 @@ func LoadIPSecKeysFile(path string) (uint8, error) {

func loadIPSecKeys(r io.Reader) (uint8, error) {
var spi uint8
scopedLog := log.WithFields(logrus.Fields{
"spi": spi,
})

scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
var oldSpi uint8
offset := 0

ipSecKey := &ipSecKey{
Expand All @@ -334,7 +374,10 @@ func loadIPSecKeys(r io.Reader) (uint8, error) {
offset = -1
}
if spiI > linux_defaults.IPsecMaxKeyVersion {
return 0, fmt.Errorf("encryption Key space exhausted, id must be less than %d. Attempted %q", linux_defaults.IPsecMaxKeyVersion, s[0])
return 0, fmt.Errorf("encryption Key space exhausted, id must be nonzero and less than %d. Attempted %q", linux_defaults.IPsecMaxKeyVersion, s[0])
}
if spiI == 0 {
return 0, fmt.Errorf("zero is not a valid key to disable encryption use `--enable-ipsec=false`, id must be nonzero and less than %d. Attempted %q", linux_defaults.IPsecMaxKeyVersion, s[0])
}
spi = uint8(spiI)

Expand All @@ -361,10 +404,30 @@ func loadIPSecKeys(r io.Reader) (uint8, error) {
ipSecKey.Spi = spi

if len(s) == 6+offset {
if ipSecKeysGlobal[s[5+offset]] != nil {
oldSpi = ipSecKeysGlobal[s[5+offset]].Spi
}
ipSecKeysGlobal[s[5+offset]] = ipSecKey
} else {
if ipSecKeysGlobal[""] != nil {
oldSpi = ipSecKeysGlobal[""].Spi
}
ipSecKeysGlobal[""] = ipSecKey
}

scopedLog.WithError(err).Warning("newtimer: oldSPI %u new spi %u", oldSpi, ipSecKey.Spi)
// Detect a version change and call cleanup routine to remove old
// keys after a timeout period. We also want to ensure on restart
// we remove any stale keys for example when a restart changes keys.
// In the restart case oldSpi will be '0' and cause the delete logic
// to run.
if oldSpi != ipSecKey.Spi {
go func() {
time.Sleep(linux_defaults.IPsecKeyDeleteDelay)
scopedLog.Info("New encryption keys reclaiming SPI")
ipsecDeleteXfrmSpi(ipSecKey.Spi)
}()
}
}
return spi, nil
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/datapath/linux/linux_defaults/linux_defaults.go
Expand Up @@ -14,6 +14,10 @@

package linux_defaults

import (
"time"
)

// Linux specific constants used in Linux datapath
const (
// RouteTableIPSec is the default table ID to use for IPSec routing rules
Expand Down Expand Up @@ -41,4 +45,8 @@ const (

// IPsecMarkMask is the mask required for the IPsec SPI and encrypt/decrypt bits
IPsecMarkMask = 0xFF00

// IPsecKeyDeleteDelay is the time to wait before removing old keys when
// the IPsec key is changing.
IPsecKeyDeleteDelay = 5 * time.Minute
)

0 comments on commit 3f12fb6

Please sign in to comment.