Skip to content

Commit 018edb0

Browse files
committed
libnet/d/overlay: document some encryption code
The overlay-network encryption code is woefully under-documented, which is especially problematic as it operates on under-documented kernel interfaces. Document what I have puzzled out of the implementation for the benefit of the next poor soul to touch this code. Signed-off-by: Cory Snider <[email protected]> (cherry picked from commit d4fd582) Signed-off-by: Cory Snider <[email protected]>
1 parent a1fd2f2 commit 018edb0

File tree

1 file changed

+43
-8
lines changed

1 file changed

+43
-8
lines changed

libnetwork/drivers/overlay/encryption.go

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,36 @@ import (
2222
"github.com/vishvananda/netlink"
2323
)
2424

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+
2547
const (
26-
r = 0xD0C4E3
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+
2755
pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
2856
)
2957

@@ -33,7 +61,9 @@ const (
3361
bidir
3462
)
3563

36-
var spMark = netlink.XfrmMark{Value: uint32(r), Mask: 0xffffffff}
64+
// Mark value for matching packets which should have our IPsec security policy
65+
// applied.
66+
var spMark = netlink.XfrmMark{Value: mark, Mask: 0xffffffff}
3767

3868
type key struct {
3969
value []byte
@@ -47,6 +77,9 @@ func (k *key) String() string {
4777
return ""
4878
}
4979

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.
5083
type spi struct {
5184
forward int
5285
reverse int
@@ -204,7 +237,7 @@ func programMangle(vni uint32, add bool) (err error) {
204237
var (
205238
p = strconv.FormatUint(uint64(overlayutils.VXLANUDPPort()), 10)
206239
c = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8)
207-
m = strconv.FormatUint(uint64(r), 10)
240+
m = strconv.FormatUint(mark, 10)
208241
chain = "OUTPUT"
209242
rule = []string{"-p", "udp", "--dport", p, "-m", "u32", "--u32", c, "-j", "MARK", "--set-mark", m}
210243
a = "-A"
@@ -251,10 +284,12 @@ func programInput(vni uint32, add bool) (err error) {
251284
msg = "remove"
252285
}
253286

287+
// Accept incoming VXLAN datagrams for the VNI which were subjected to IPSec processing.
254288
if err := iptable.ProgramRule(iptables.Filter, chain, action, accept); err != nil {
255289
logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err)
256290
}
257291

292+
// Drop incoming VXLAN datagrams for the VNI which were received in cleartext.
258293
if err := iptable.ProgramRule(iptables.Filter, chain, action, block); err != nil {
259294
logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err)
260295
}
@@ -280,7 +315,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
280315
Proto: netlink.XFRM_PROTO_ESP,
281316
Spi: spi.reverse,
282317
Mode: netlink.XFRM_MODE_TRANSPORT,
283-
Reqid: r,
318+
Reqid: mark,
284319
}
285320
if add {
286321
rSA.Aead = buildAeadAlgo(k, spi.reverse)
@@ -306,7 +341,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
306341
Proto: netlink.XFRM_PROTO_ESP,
307342
Spi: spi.forward,
308343
Mode: netlink.XFRM_MODE_TRANSPORT,
309-
Reqid: r,
344+
Reqid: mark,
310345
}
311346
if add {
312347
fSA.Aead = buildAeadAlgo(k, spi.forward)
@@ -355,7 +390,7 @@ func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
355390
Proto: netlink.XFRM_PROTO_ESP,
356391
Mode: netlink.XFRM_MODE_TRANSPORT,
357392
Spi: fSA.Spi,
358-
Reqid: r,
393+
Reqid: mark,
359394
},
360395
},
361396
}
@@ -569,7 +604,7 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
569604
Proto: netlink.XFRM_PROTO_ESP,
570605
Mode: netlink.XFRM_MODE_TRANSPORT,
571606
Spi: fSA2.Spi,
572-
Reqid: r,
607+
Reqid: mark,
573608
},
574609
},
575610
}
@@ -638,7 +673,7 @@ func clearEncryptionStates() {
638673
}
639674
for _, sa := range saList {
640675
sa := sa
641-
if sa.Reqid == r {
676+
if sa.Reqid == mark {
642677
if err := nlh.XfrmStateDel(&sa); err != nil {
643678
logrus.Warnf("Failed to delete stale SA %s: %v", sa, err)
644679
continue

0 commit comments

Comments
 (0)