github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/drivers/overlay/encryption.go (about)

     1  //go:build linux
     2  
     3  package overlay
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/binary"
     9  	"encoding/hex"
    10  	"fmt"
    11  	"hash/fnv"
    12  	"net"
    13  	"strconv"
    14  	"sync"
    15  	"syscall"
    16  
    17  	"github.com/containerd/log"
    18  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/drivers/overlay/overlayutils"
    19  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/iptables"
    20  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/ns"
    21  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/types"
    22  	"github.com/vishvananda/netlink"
    23  )
    24  
    25  /*
    26  Encrypted overlay networks use IPsec in transport mode to encrypt and
    27  authenticate the VXLAN UDP datagrams. This driver implements a bespoke control
    28  plane which negotiates the security parameters for each peer-to-peer tunnel.
    29  
    30  IPsec Terminology
    31  
    32   - ESP: IPSec Encapsulating Security Payload
    33   - SPI: Security Parameter Index
    34   - ICV: Integrity Check Value
    35   - SA: Security Association https://en.wikipedia.org/wiki/IPsec#Security_association
    36  
    37  
    38  Developer documentation for Linux IPsec is rather sparse online. The following
    39  slide deck provides a decent overview.
    40  https://libreswan.org/wiki/images/e/e0/Netdev-0x12-ipsec-flow.pdf
    41  
    42  The Linux IPsec stack is part of XFRM, the netlink packet transformation
    43  interface.
    44  https://man7.org/linux/man-pages/man8/ip-xfrm.8.html
    45  */
    46  
    47  const (
    48  	// Value used to mark outgoing packets which should have our IPsec
    49  	// processing applied. It is also used as a label to identify XFRM
    50  	// states (Security Associations) and policies (Security Policies)
    51  	// programmed by us so we know which ones we can clean up without
    52  	// disrupting other VPN connections on the system.
    53  	mark = 0xD0C4E3
    54  
    55  	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
    56  )
    57  
    58  const (
    59  	forward = iota + 1
    60  	reverse
    61  	bidir
    62  )
    63  
    64  // Mark value for matching packets which should have our IPsec security policy
    65  // applied.
    66  var spMark = netlink.XfrmMark{Value: mark, Mask: 0xffffffff}
    67  
    68  type key struct {
    69  	value []byte
    70  	tag   uint32
    71  }
    72  
    73  func (k *key) String() string {
    74  	if k != nil {
    75  		return fmt.Sprintf("(key: %s, tag: 0x%x)", hex.EncodeToString(k.value)[0:5], k.tag)
    76  	}
    77  	return ""
    78  }
    79  
    80  // Security Parameter Indices for the IPsec flows between local node and a
    81  // remote peer, which identify the Security Associations (XFRM states) to be
    82  // applied when encrypting and decrypting packets.
    83  type spi struct {
    84  	forward int
    85  	reverse int
    86  }
    87  
    88  func (s *spi) String() string {
    89  	return fmt.Sprintf("SPI(FWD: 0x%x, REV: 0x%x)", uint32(s.forward), uint32(s.reverse))
    90  }
    91  
    92  type encrMap struct {
    93  	nodes map[string][]*spi
    94  	sync.Mutex
    95  }
    96  
    97  func (e *encrMap) String() string {
    98  	e.Lock()
    99  	defer e.Unlock()
   100  	b := new(bytes.Buffer)
   101  	for k, v := range e.nodes {
   102  		b.WriteString("\n")
   103  		b.WriteString(k)
   104  		b.WriteString(":")
   105  		b.WriteString("[")
   106  		for _, s := range v {
   107  			b.WriteString(s.String())
   108  			b.WriteString(",")
   109  		}
   110  		b.WriteString("]")
   111  	}
   112  	return b.String()
   113  }
   114  
   115  func (d *driver) checkEncryption(nid string, rIP net.IP, isLocal, add bool) error {
   116  	log.G(context.TODO()).Debugf("checkEncryption(%.7s, %v, %t)", nid, rIP, isLocal)
   117  
   118  	n := d.network(nid)
   119  	if n == nil || !n.secure {
   120  		return nil
   121  	}
   122  
   123  	if len(d.keys) == 0 {
   124  		return types.ForbiddenErrorf("encryption key is not present")
   125  	}
   126  
   127  	lIP := net.ParseIP(d.bindAddress)
   128  	aIP := net.ParseIP(d.advertiseAddress)
   129  	nodes := map[string]net.IP{}
   130  
   131  	switch {
   132  	case isLocal:
   133  		if err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
   134  			if !aIP.Equal(pEntry.vtep) {
   135  				nodes[pEntry.vtep.String()] = pEntry.vtep
   136  			}
   137  			return false
   138  		}); err != nil {
   139  			log.G(context.TODO()).Warnf("Failed to retrieve list of participating nodes in overlay network %.5s: %v", nid, err)
   140  		}
   141  	default:
   142  		if len(d.network(nid).endpoints) > 0 {
   143  			nodes[rIP.String()] = rIP
   144  		}
   145  	}
   146  
   147  	log.G(context.TODO()).Debugf("List of nodes: %s", nodes)
   148  
   149  	if add {
   150  		for _, rIP := range nodes {
   151  			if err := setupEncryption(lIP, aIP, rIP, d.secMap, d.keys); err != nil {
   152  				log.G(context.TODO()).Warnf("Failed to program network encryption between %s and %s: %v", lIP, rIP, err)
   153  			}
   154  		}
   155  	} else {
   156  		if len(nodes) == 0 {
   157  			if err := removeEncryption(lIP, rIP, d.secMap); err != nil {
   158  				log.G(context.TODO()).Warnf("Failed to remove network encryption between %s and %s: %v", lIP, rIP, err)
   159  			}
   160  		}
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  // setupEncryption programs the encryption parameters for secure communication
   167  // between the local node and a remote node.
   168  func setupEncryption(localIP, advIP, remoteIP net.IP, em *encrMap, keys []*key) error {
   169  	log.G(context.TODO()).Debugf("Programming encryption between %s and %s", localIP, remoteIP)
   170  	rIPs := remoteIP.String()
   171  
   172  	indices := make([]*spi, 0, len(keys))
   173  
   174  	for i, k := range keys {
   175  		spis := &spi{buildSPI(advIP, remoteIP, k.tag), buildSPI(remoteIP, advIP, k.tag)}
   176  		dir := reverse
   177  		if i == 0 {
   178  			dir = bidir
   179  		}
   180  		fSA, rSA, err := programSA(localIP, remoteIP, spis, k, dir, true)
   181  		if err != nil {
   182  			log.G(context.TODO()).Warn(err)
   183  		}
   184  		indices = append(indices, spis)
   185  		if i != 0 {
   186  			continue
   187  		}
   188  		err = programSP(fSA, rSA, true)
   189  		if err != nil {
   190  			log.G(context.TODO()).Warn(err)
   191  		}
   192  	}
   193  
   194  	em.Lock()
   195  	em.nodes[rIPs] = indices
   196  	em.Unlock()
   197  
   198  	return nil
   199  }
   200  
   201  func removeEncryption(localIP, remoteIP net.IP, em *encrMap) error {
   202  	em.Lock()
   203  	indices, ok := em.nodes[remoteIP.String()]
   204  	em.Unlock()
   205  	if !ok {
   206  		return nil
   207  	}
   208  	for i, idxs := range indices {
   209  		dir := reverse
   210  		if i == 0 {
   211  			dir = bidir
   212  		}
   213  		fSA, rSA, err := programSA(localIP, remoteIP, idxs, nil, dir, false)
   214  		if err != nil {
   215  			log.G(context.TODO()).Warn(err)
   216  		}
   217  		if i != 0 {
   218  			continue
   219  		}
   220  		err = programSP(fSA, rSA, false)
   221  		if err != nil {
   222  			log.G(context.TODO()).Warn(err)
   223  		}
   224  	}
   225  	return nil
   226  }
   227  
   228  func programMangle(vni uint32, add bool) error {
   229  	var (
   230  		m      = strconv.FormatUint(mark, 10)
   231  		chain  = "OUTPUT"
   232  		rule   = append(matchVXLAN(overlayutils.VXLANUDPPort(), vni), "-j", "MARK", "--set-mark", m)
   233  		a      = iptables.Append
   234  		action = "install"
   235  	)
   236  
   237  	// TODO IPv6 support
   238  	iptable := iptables.GetIptable(iptables.IPv4)
   239  
   240  	if !add {
   241  		a = iptables.Delete
   242  		action = "remove"
   243  	}
   244  
   245  	if err := iptable.ProgramRule(iptables.Mangle, chain, a, rule); err != nil {
   246  		return fmt.Errorf("could not %s mangle rule: %w", action, err)
   247  	}
   248  
   249  	return nil
   250  }
   251  
   252  func programInput(vni uint32, add bool) error {
   253  	var (
   254  		plainVxlan = matchVXLAN(overlayutils.VXLANUDPPort(), vni)
   255  		chain      = "INPUT"
   256  		msg        = "add"
   257  	)
   258  
   259  	rule := func(policy, jump string) []string {
   260  		args := append([]string{"-m", "policy", "--dir", "in", "--pol", policy}, plainVxlan...)
   261  		return append(args, "-j", jump)
   262  	}
   263  
   264  	// TODO IPv6 support
   265  	iptable := iptables.GetIptable(iptables.IPv4)
   266  
   267  	if !add {
   268  		msg = "remove"
   269  	}
   270  
   271  	action := func(a iptables.Action) iptables.Action {
   272  		if !add {
   273  			return iptables.Delete
   274  		}
   275  		return a
   276  	}
   277  
   278  	// Drop incoming VXLAN datagrams for the VNI which were received in cleartext.
   279  	// Insert at the top of the chain so the packets are dropped even if an
   280  	// administrator-configured rule exists which would otherwise unconditionally
   281  	// accept incoming VXLAN traffic.
   282  	if err := iptable.ProgramRule(iptables.Filter, chain, action(iptables.Insert), rule("none", "DROP")); err != nil {
   283  		return fmt.Errorf("could not %s input drop rule: %w", msg, err)
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (fSA *netlink.XfrmState, rSA *netlink.XfrmState, err error) {
   290  	var (
   291  		action      = "Removing"
   292  		xfrmProgram = ns.NlHandle().XfrmStateDel
   293  	)
   294  
   295  	if add {
   296  		action = "Adding"
   297  		xfrmProgram = ns.NlHandle().XfrmStateAdd
   298  	}
   299  
   300  	if dir&reverse > 0 {
   301  		rSA = &netlink.XfrmState{
   302  			Src:   remoteIP,
   303  			Dst:   localIP,
   304  			Proto: netlink.XFRM_PROTO_ESP,
   305  			Spi:   spi.reverse,
   306  			Mode:  netlink.XFRM_MODE_TRANSPORT,
   307  			Reqid: mark,
   308  		}
   309  		if add {
   310  			rSA.Aead = buildAeadAlgo(k, spi.reverse)
   311  		}
   312  
   313  		exists, err := saExists(rSA)
   314  		if err != nil {
   315  			exists = !add
   316  		}
   317  
   318  		if add != exists {
   319  			log.G(context.TODO()).Debugf("%s: rSA{%s}", action, rSA)
   320  			if err := xfrmProgram(rSA); err != nil {
   321  				log.G(context.TODO()).Warnf("Failed %s rSA{%s}: %v", action, rSA, err)
   322  			}
   323  		}
   324  	}
   325  
   326  	if dir&forward > 0 {
   327  		fSA = &netlink.XfrmState{
   328  			Src:   localIP,
   329  			Dst:   remoteIP,
   330  			Proto: netlink.XFRM_PROTO_ESP,
   331  			Spi:   spi.forward,
   332  			Mode:  netlink.XFRM_MODE_TRANSPORT,
   333  			Reqid: mark,
   334  		}
   335  		if add {
   336  			fSA.Aead = buildAeadAlgo(k, spi.forward)
   337  		}
   338  
   339  		exists, err := saExists(fSA)
   340  		if err != nil {
   341  			exists = !add
   342  		}
   343  
   344  		if add != exists {
   345  			log.G(context.TODO()).Debugf("%s fSA{%s}", action, fSA)
   346  			if err := xfrmProgram(fSA); err != nil {
   347  				log.G(context.TODO()).Warnf("Failed %s fSA{%s}: %v.", action, fSA, err)
   348  			}
   349  		}
   350  	}
   351  
   352  	return
   353  }
   354  
   355  // getMinimalIP returns the address in its shortest form
   356  // If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned.
   357  // Otherwise ip is returned unchanged.
   358  func getMinimalIP(ip net.IP) net.IP {
   359  	if ip != nil && ip.To4() != nil {
   360  		return ip.To4()
   361  	}
   362  	return ip
   363  }
   364  
   365  func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
   366  	action := "Removing"
   367  	xfrmProgram := ns.NlHandle().XfrmPolicyDel
   368  	if add {
   369  		action = "Adding"
   370  		xfrmProgram = ns.NlHandle().XfrmPolicyAdd
   371  	}
   372  
   373  	// Create a congruent cidr
   374  	s := getMinimalIP(fSA.Src)
   375  	d := getMinimalIP(fSA.Dst)
   376  	fullMask := net.CIDRMask(8*len(s), 8*len(s))
   377  
   378  	fPol := &netlink.XfrmPolicy{
   379  		Src:     &net.IPNet{IP: s, Mask: fullMask},
   380  		Dst:     &net.IPNet{IP: d, Mask: fullMask},
   381  		Dir:     netlink.XFRM_DIR_OUT,
   382  		Proto:   syscall.IPPROTO_UDP,
   383  		DstPort: int(overlayutils.VXLANUDPPort()),
   384  		Mark:    &spMark,
   385  		Tmpls: []netlink.XfrmPolicyTmpl{
   386  			{
   387  				Src:   fSA.Src,
   388  				Dst:   fSA.Dst,
   389  				Proto: netlink.XFRM_PROTO_ESP,
   390  				Mode:  netlink.XFRM_MODE_TRANSPORT,
   391  				Spi:   fSA.Spi,
   392  				Reqid: mark,
   393  			},
   394  		},
   395  	}
   396  
   397  	exists, err := spExists(fPol)
   398  	if err != nil {
   399  		exists = !add
   400  	}
   401  
   402  	if add != exists {
   403  		log.G(context.TODO()).Debugf("%s fSP{%s}", action, fPol)
   404  		if err := xfrmProgram(fPol); err != nil {
   405  			log.G(context.TODO()).Warnf("%s fSP{%s}: %v", action, fPol, err)
   406  		}
   407  	}
   408  
   409  	return nil
   410  }
   411  
   412  func saExists(sa *netlink.XfrmState) (bool, error) {
   413  	_, err := ns.NlHandle().XfrmStateGet(sa)
   414  	switch err {
   415  	case nil:
   416  		return true, nil
   417  	case syscall.ESRCH:
   418  		return false, nil
   419  	default:
   420  		err = fmt.Errorf("Error while checking for SA existence: %v", err)
   421  		log.G(context.TODO()).Warn(err)
   422  		return false, err
   423  	}
   424  }
   425  
   426  func spExists(sp *netlink.XfrmPolicy) (bool, error) {
   427  	_, err := ns.NlHandle().XfrmPolicyGet(sp)
   428  	switch err {
   429  	case nil:
   430  		return true, nil
   431  	case syscall.ENOENT:
   432  		return false, nil
   433  	default:
   434  		err = fmt.Errorf("Error while checking for SP existence: %v", err)
   435  		log.G(context.TODO()).Warn(err)
   436  		return false, err
   437  	}
   438  }
   439  
   440  func buildSPI(src, dst net.IP, st uint32) int {
   441  	b := make([]byte, 4)
   442  	binary.BigEndian.PutUint32(b, st)
   443  	h := fnv.New32a()
   444  	h.Write(src)
   445  	h.Write(b)
   446  	h.Write(dst)
   447  	return int(binary.BigEndian.Uint32(h.Sum(nil)))
   448  }
   449  
   450  func buildAeadAlgo(k *key, s int) *netlink.XfrmStateAlgo {
   451  	salt := make([]byte, 4)
   452  	binary.BigEndian.PutUint32(salt, uint32(s))
   453  	return &netlink.XfrmStateAlgo{
   454  		Name:   "rfc4106(gcm(aes))",
   455  		Key:    append(k.value, salt...),
   456  		ICVLen: 64,
   457  	}
   458  }
   459  
   460  func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error {
   461  	d.secMap.Lock()
   462  	for node, indices := range d.secMap.nodes {
   463  		idxs, stop := f(node, indices)
   464  		if idxs != nil {
   465  			d.secMap.nodes[node] = idxs
   466  		}
   467  		if stop {
   468  			break
   469  		}
   470  	}
   471  	d.secMap.Unlock()
   472  	return nil
   473  }
   474  
   475  func (d *driver) setKeys(keys []*key) error {
   476  	// Remove any stale policy, state
   477  	clearEncryptionStates()
   478  	// Accept the encryption keys and clear any stale encryption map
   479  	d.Lock()
   480  	d.keys = keys
   481  	d.secMap = &encrMap{nodes: map[string][]*spi{}}
   482  	d.Unlock()
   483  	log.G(context.TODO()).Debugf("Initial encryption keys: %v", keys)
   484  	return nil
   485  }
   486  
   487  // updateKeys allows to add a new key and/or change the primary key and/or prune an existing key
   488  // The primary key is the key used in transmission and will go in first position in the list.
   489  func (d *driver) updateKeys(newKey, primary, pruneKey *key) error {
   490  	log.G(context.TODO()).Debugf("Updating Keys. New: %v, Primary: %v, Pruned: %v", newKey, primary, pruneKey)
   491  
   492  	log.G(context.TODO()).Debugf("Current: %v", d.keys)
   493  
   494  	var (
   495  		newIdx = -1
   496  		priIdx = -1
   497  		delIdx = -1
   498  		lIP    = net.ParseIP(d.bindAddress)
   499  		aIP    = net.ParseIP(d.advertiseAddress)
   500  	)
   501  
   502  	d.Lock()
   503  	defer d.Unlock()
   504  
   505  	// add new
   506  	if newKey != nil {
   507  		d.keys = append(d.keys, newKey)
   508  		newIdx += len(d.keys)
   509  	}
   510  	for i, k := range d.keys {
   511  		if primary != nil && k.tag == primary.tag {
   512  			priIdx = i
   513  		}
   514  		if pruneKey != nil && k.tag == pruneKey.tag {
   515  			delIdx = i
   516  		}
   517  	}
   518  
   519  	if (newKey != nil && newIdx == -1) ||
   520  		(primary != nil && priIdx == -1) ||
   521  		(pruneKey != nil && delIdx == -1) {
   522  		return types.InvalidParameterErrorf("cannot find proper key indices while processing key update:"+
   523  			"(newIdx,priIdx,delIdx):(%d, %d, %d)", newIdx, priIdx, delIdx)
   524  	}
   525  
   526  	if priIdx != -1 && priIdx == delIdx {
   527  		return types.InvalidParameterErrorf("attempting to both make a key (index %d) primary and delete it", priIdx)
   528  	}
   529  
   530  	d.secMapWalk(func(rIPs string, spis []*spi) ([]*spi, bool) {
   531  		rIP := net.ParseIP(rIPs)
   532  		return updateNodeKey(lIP, aIP, rIP, spis, d.keys, newIdx, priIdx, delIdx), false
   533  	})
   534  
   535  	// swap primary
   536  	if priIdx != -1 {
   537  		d.keys[0], d.keys[priIdx] = d.keys[priIdx], d.keys[0]
   538  	}
   539  	// prune
   540  	if delIdx != -1 {
   541  		if delIdx == 0 {
   542  			delIdx = priIdx
   543  		}
   544  		d.keys = append(d.keys[:delIdx], d.keys[delIdx+1:]...)
   545  	}
   546  
   547  	log.G(context.TODO()).Debugf("Updated: %v", d.keys)
   548  
   549  	return nil
   550  }
   551  
   552  /********************************************************
   553   * Steady state: rSA0, rSA1, rSA2, fSA1, fSP1
   554   * Rotation --> -rSA0, +rSA3, +fSA2, +fSP2/-fSP1, -fSA1
   555   * Steady state: rSA1, rSA2, rSA3, fSA2, fSP2
   556   *********************************************************/
   557  
   558  // Spis and keys are sorted in such away the one in position 0 is the primary
   559  func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, priIdx, delIdx int) []*spi {
   560  	log.G(context.TODO()).Debugf("Updating keys for node: %s (%d,%d,%d)", rIP, newIdx, priIdx, delIdx)
   561  
   562  	spis := idxs
   563  	log.G(context.TODO()).Debugf("Current: %v", spis)
   564  
   565  	// add new
   566  	if newIdx != -1 {
   567  		spis = append(spis, &spi{
   568  			forward: buildSPI(aIP, rIP, curKeys[newIdx].tag),
   569  			reverse: buildSPI(rIP, aIP, curKeys[newIdx].tag),
   570  		})
   571  	}
   572  
   573  	if delIdx != -1 {
   574  		// -rSA0
   575  		programSA(lIP, rIP, spis[delIdx], nil, reverse, false)
   576  	}
   577  
   578  	if newIdx > -1 {
   579  		// +rSA2
   580  		programSA(lIP, rIP, spis[newIdx], curKeys[newIdx], reverse, true)
   581  	}
   582  
   583  	if priIdx > 0 {
   584  		// +fSA2
   585  		fSA2, _, _ := programSA(lIP, rIP, spis[priIdx], curKeys[priIdx], forward, true)
   586  
   587  		// +fSP2, -fSP1
   588  		s := getMinimalIP(fSA2.Src)
   589  		d := getMinimalIP(fSA2.Dst)
   590  		fullMask := net.CIDRMask(8*len(s), 8*len(s))
   591  
   592  		fSP1 := &netlink.XfrmPolicy{
   593  			Src:     &net.IPNet{IP: s, Mask: fullMask},
   594  			Dst:     &net.IPNet{IP: d, Mask: fullMask},
   595  			Dir:     netlink.XFRM_DIR_OUT,
   596  			Proto:   syscall.IPPROTO_UDP,
   597  			DstPort: int(overlayutils.VXLANUDPPort()),
   598  			Mark:    &spMark,
   599  			Tmpls: []netlink.XfrmPolicyTmpl{
   600  				{
   601  					Src:   fSA2.Src,
   602  					Dst:   fSA2.Dst,
   603  					Proto: netlink.XFRM_PROTO_ESP,
   604  					Mode:  netlink.XFRM_MODE_TRANSPORT,
   605  					Spi:   fSA2.Spi,
   606  					Reqid: mark,
   607  				},
   608  			},
   609  		}
   610  		log.G(context.TODO()).Debugf("Updating fSP{%s}", fSP1)
   611  		if err := ns.NlHandle().XfrmPolicyUpdate(fSP1); err != nil {
   612  			log.G(context.TODO()).Warnf("Failed to update fSP{%s}: %v", fSP1, err)
   613  		}
   614  
   615  		// -fSA1
   616  		programSA(lIP, rIP, spis[0], nil, forward, false)
   617  	}
   618  
   619  	// swap
   620  	if priIdx > 0 {
   621  		swp := spis[0]
   622  		spis[0] = spis[priIdx]
   623  		spis[priIdx] = swp
   624  	}
   625  	// prune
   626  	if delIdx != -1 {
   627  		if delIdx == 0 {
   628  			delIdx = priIdx
   629  		}
   630  		spis = append(spis[:delIdx], spis[delIdx+1:]...)
   631  	}
   632  
   633  	log.G(context.TODO()).Debugf("Updated: %v", spis)
   634  
   635  	return spis
   636  }
   637  
   638  func (n *network) maxMTU() int {
   639  	mtu := 1500
   640  	if n.mtu != 0 {
   641  		mtu = n.mtu
   642  	}
   643  	mtu -= vxlanEncap
   644  	if n.secure {
   645  		// In case of encryption account for the
   646  		// esp packet expansion and padding
   647  		mtu -= pktExpansion
   648  		mtu -= (mtu % 4)
   649  	}
   650  	return mtu
   651  }
   652  
   653  func clearEncryptionStates() {
   654  	nlh := ns.NlHandle()
   655  	spList, err := nlh.XfrmPolicyList(netlink.FAMILY_ALL)
   656  	if err != nil {
   657  		log.G(context.TODO()).Warnf("Failed to retrieve SP list for cleanup: %v", err)
   658  	}
   659  	saList, err := nlh.XfrmStateList(netlink.FAMILY_ALL)
   660  	if err != nil {
   661  		log.G(context.TODO()).Warnf("Failed to retrieve SA list for cleanup: %v", err)
   662  	}
   663  	for _, sp := range spList {
   664  		sp := sp
   665  		if sp.Mark != nil && sp.Mark.Value == spMark.Value {
   666  			if err := nlh.XfrmPolicyDel(&sp); err != nil {
   667  				log.G(context.TODO()).Warnf("Failed to delete stale SP %s: %v", sp, err)
   668  				continue
   669  			}
   670  			log.G(context.TODO()).Debugf("Removed stale SP: %s", sp)
   671  		}
   672  	}
   673  	for _, sa := range saList {
   674  		sa := sa
   675  		if sa.Reqid == mark {
   676  			if err := nlh.XfrmStateDel(&sa); err != nil {
   677  				log.G(context.TODO()).Warnf("Failed to delete stale SA %s: %v", sa, err)
   678  				continue
   679  			}
   680  			log.G(context.TODO()).Debugf("Removed stale SA: %s", sa)
   681  		}
   682  	}
   683  }