From 0b0678677f8b576c7717e20952017df674479a23 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 14 Jul 2016 15:38:05 -0700 Subject: [PATCH 1/3] Update vishvananda/netlink to e73bad4 Signed-off-by: Alessandro Boch --- libnetwork/Godeps/Godeps.json | 4 +- .../vishvananda/netlink/addr_linux.go | 13 +++- .../vishvananda/netlink/filter_linux.go | 4 +- .../vishvananda/netlink/link_linux.go | 17 ++++- .../vishvananda/netlink/nl/nl_linux.go | 63 +++++++++++++++++-- .../netlink/nl/xfrm_state_linux.go | 30 +++++++++ .../vishvananda/netlink/route_linux.go | 13 +++- .../vishvananda/netlink/xfrm_state.go | 15 ++++- .../vishvananda/netlink/xfrm_state_linux.go | 24 +++++++ 9 files changed, 165 insertions(+), 18 deletions(-) diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index 1aceae3d71..800e235921 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -398,11 +398,11 @@ }, { "ImportPath": "github.com/vishvananda/netlink", - "Rev": "734d02c3e202f682c74b71314b2c61eec0170fd4" + "Rev": "e73bad418fd727ed3a02830b1af1ad0283a1de6c" }, { "ImportPath": "github.com/vishvananda/netlink/nl", - "Rev": "734d02c3e202f682c74b71314b2c61eec0170fd4" + "Rev": "e73bad418fd727ed3a02830b1af1ad0283a1de6c" }, { "ImportPath": "github.com/vishvananda/netns", diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go index b5eec65052..8820c02052 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go @@ -8,6 +8,7 @@ import ( "syscall" "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" ) // IFA_FLAGS is a u32 attribute. @@ -192,7 +193,17 @@ type AddrUpdate struct { // AddrSubscribe takes a chan down which notifications will be sent // when addresses change. Close the 'done' chan to stop subscription. func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { - s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR) + return addrSubscribe(netns.None(), netns.None(), ch, done) +} + +// AddrSubscribeAt works like AddrSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { + return addrSubscribe(ns, netns.None(), ch, done) +} + +func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR) if err != nil { return err } diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_linux.go index b403dad2f8..d9aedca765 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_linux.go @@ -143,7 +143,7 @@ func (h *Handle) FilterAdd(filter Filter) error { if u32.RedirIndex != 0 { u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...) } - if err := encodeActions(actionsAttr, u32.Actions); err != nil { + if err := EncodeActions(actionsAttr, u32.Actions); err != nil { return err } } else if fw, ok := filter.(*Fw); ok { @@ -309,7 +309,7 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) { attrs.Bindcnt = int(tcgen.Bindcnt) } -func encodeActions(attr *nl.RtAttr, actions []Action) error { +func EncodeActions(attr *nl.RtAttr, actions []Action) error { tabIndex := int(nl.TCA_ACT_TAB) for _, action := range actions { diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go index 08a93843d6..cfa5bf3e6c 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go @@ -10,6 +10,7 @@ import ( "unsafe" "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" ) const SizeofLinkStats = 0x5c @@ -425,7 +426,7 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum)) } if vxlan.GBP { - nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP)) + nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{}) } if vxlan.NoAge { nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0)) @@ -1011,7 +1012,17 @@ type LinkUpdate struct { // LinkSubscribe takes a chan down which notifications will be sent // when links change. Close the 'done' chan to stop subscription. func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error { - s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) + return linkSubscribe(netns.None(), netns.None(), ch, done) +} + +// LinkSubscribeAt works like LinkSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { + return linkSubscribe(ns, netns.None(), ch, done) +} + +func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) if err != nil { return err } @@ -1152,7 +1163,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { case nl.IFLA_VXLAN_UDP_CSUM: vxlan.UDPCSum = int8(datum.Value[0]) != 0 case nl.IFLA_VXLAN_GBP: - vxlan.GBP = int8(datum.Value[0]) != 0 + vxlan.GBP = true case nl.IFLA_VXLAN_AGEING: vxlan.Age = int(native.Uint32(datum.Value[0:4])) vxlan.NoAge = vxlan.Age == 0 diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go index fd5550a306..17683a7556 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go @@ -331,24 +331,63 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { // moves back into it when done. If newNs is close, the socket will be opened // in the current network namespace. func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) { - var err error + c, err := executeInNetns(newNs, curNs) + if err != nil { + return nil, err + } + defer c() + return getNetlinkSocket(protocol) +} +// executeInNetns sets execution of the code following this call to the +// network namespace newNs, then moves the thread back to curNs if open, +// otherwise to the current netns at the time the function was invoked +// In case of success, the caller is expected to execute the returned function +// at the end of the code that needs to be executed in the network namespace. +// Example: +// func jobAt(...) error { +// d, err := executeInNetns(...) +// if err != nil { return err} +// defer d() +// < code which needs to be executed in specific netns> +// } +// TODO: his function probably belongs to netns pkg. +func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) { + var ( + err error + moveBack func(netns.NsHandle) error + closeNs func() error + unlockThd func() + ) + restore := func() { + // order matters + if moveBack != nil { + moveBack(curNs) + } + if closeNs != nil { + closeNs() + } + if unlockThd != nil { + unlockThd() + } + } if newNs.IsOpen() { runtime.LockOSThread() - defer runtime.UnlockOSThread() + unlockThd = runtime.UnlockOSThread if !curNs.IsOpen() { if curNs, err = netns.Get(); err != nil { + restore() return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err) } - defer curNs.Close() + closeNs = curNs.Close } if err := netns.Set(newNs); err != nil { + restore() return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err) } - defer netns.Set(curNs) + moveBack = netns.Set } - - return getNetlinkSocket(protocol) + return restore, nil } // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE) @@ -377,6 +416,18 @@ func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { return s, nil } +// SubscribeAt works like Subscribe plus let's the caller choose the network +// namespace in which the socket would be opened (newNs). Then control goes back +// to curNs if open, otherwise to the netns at the time this function was called. +func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) { + c, err := executeInNetns(newNs, curNs) + if err != nil { + return nil, err + } + defer c() + return Subscribe(protocol, groups...) +} + func (s *NetlinkSocket) Close() { syscall.Close(s.fd) s.fd = -1 diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go index 482b4f37a0..856db3da18 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux.go @@ -10,6 +10,7 @@ const ( SizeofXfrmUsersaInfo = 0xe0 SizeofXfrmAlgo = 0x44 SizeofXfrmAlgoAuth = 0x48 + SizeofXfrmAlgoAEAD = 0x48 SizeofXfrmEncapTmpl = 0x18 SizeofXfrmUsersaFlush = 0x8 ) @@ -194,6 +195,35 @@ func (msg *XfrmAlgoAuth) Serialize() []byte { // char alg_key[0]; // } +type XfrmAlgoAEAD struct { + AlgName [64]byte + AlgKeyLen uint32 + AlgICVLen uint32 + AlgKey []byte +} + +func (msg *XfrmAlgoAEAD) Len() int { + return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8) +} + +func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD { + ret := XfrmAlgoAEAD{} + copy(ret.AlgName[:], b[0:64]) + ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64])) + ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68])) + ret.AlgKey = b[72:ret.Len()] + return &ret +} + +func (msg *XfrmAlgoAEAD) Serialize() []byte { + b := make([]byte, msg.Len()) + copy(b[0:64], msg.AlgName[:]) + copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:]) + copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:]) + copy(b[72:msg.Len()], msg.AlgKey[:]) + return b +} + // struct xfrm_encap_tmpl { // __u16 encap_type; // __be16 encap_sport; diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go index 5d684ad795..6ba3ea1264 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go @@ -6,6 +6,7 @@ import ( "syscall" "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" ) // RtAttr is shared so it is in netlink_linux.go @@ -421,7 +422,17 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) { // RouteSubscribe takes a chan down which notifications will be sent // when routes are added or deleted. Close the 'done' chan to stop subscription. func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error { - s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE) + return routeSubscribeAt(netns.None(), netns.None(), ch, done) +} + +// RouteSubscribeAt works like RouteSubscribe plus it allows the caller +// to choose the network namespace in which to subscribe (ns). +func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { + return routeSubscribeAt(ns, netns.None(), ch, done) +} + +func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error { + s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE) if err != nil { return err } diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go index 6ffbcdb476..e25cde26db 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state.go @@ -10,10 +10,18 @@ type XfrmStateAlgo struct { Name string Key []byte TruncateLen int // Auth only + ICVLen int // AEAD only } func (a XfrmStateAlgo) String() string { - return fmt.Sprintf("{Name: %s, Key: 0x%x, TruncateLen: %d}", a.Name, a.Key, a.TruncateLen) + base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key) + if a.TruncateLen != 0 { + base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen) + } + if a.ICVLen != 0 { + base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen) + } + return fmt.Sprintf("%s}", base) } // EncapType is an enum representing the optional packet encapsulation. @@ -73,12 +81,13 @@ type XfrmState struct { Mark *XfrmMark Auth *XfrmStateAlgo Crypt *XfrmStateAlgo + Aead *XfrmStateAlgo Encap *XfrmStateEncap } func (sa XfrmState) String() string { - return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Encap: %v", - sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Encap) + return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v,Encap: %v", + sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap) } func (sa XfrmState) Print(stats bool) string { if !stats { diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go index 5f294c713d..b100f71bd1 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_linux.go @@ -35,6 +35,20 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte { return algo.Serialize() } +func writeStateAlgoAead(a *XfrmStateAlgo) []byte { + algo := nl.XfrmAlgoAEAD{ + AlgKeyLen: uint32(len(a.Key) * 8), + AlgICVLen: uint32(a.ICVLen), + AlgKey: a.Key, + } + end := len(a.Name) + if end > 64 { + end = 64 + } + copy(algo.AlgName[:end], a.Name) + return algo.Serialize() +} + func writeMark(m *XfrmMark) []byte { mark := &nl.XfrmMark{ Value: m.Value, @@ -97,6 +111,10 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error { out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt)) req.AddData(out) } + if state.Aead != nil { + out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead)) + req.AddData(out) + } if state.Encap != nil { encapData := make([]byte, nl.SizeofXfrmEncapTmpl) encap := nl.DeserializeXfrmEncapTmpl(encapData) @@ -271,6 +289,12 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) { state.Auth.Name = nl.BytesToString(algo.AlgName[:]) state.Auth.Key = algo.AlgKey state.Auth.TruncateLen = int(algo.AlgTruncLen) + case nl.XFRMA_ALG_AEAD: + state.Aead = new(XfrmStateAlgo) + algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:]) + state.Aead.Name = nl.BytesToString(algo.AlgName[:]) + state.Aead.Key = algo.AlgKey + state.Aead.ICVLen = int(algo.AlgICVLen) case nl.XFRMA_ENCAP: encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:]) state.Encap = new(XfrmStateEncap) From 253c103b8c5c4daa32499b7935c5ab08023cf845 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 14 Jul 2016 16:32:39 -0700 Subject: [PATCH 2/3] Use aead for dataplane encryption Signed-off-by: Alessandro Boch --- libnetwork/drivers/overlay/encryption.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libnetwork/drivers/overlay/encryption.go b/libnetwork/drivers/overlay/encryption.go index 36d2a2f2b7..e36fa0ff3a 100644 --- a/libnetwork/drivers/overlay/encryption.go +++ b/libnetwork/drivers/overlay/encryption.go @@ -2,6 +2,7 @@ package overlay import ( "bytes" + "encoding/binary" "encoding/hex" "fmt" "net" @@ -216,7 +217,6 @@ func programMangle(vni uint32, add bool) (err error) { func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (fSA *netlink.XfrmState, rSA *netlink.XfrmState, err error) { var ( - crypt *netlink.XfrmStateAlgo action = "Removing" xfrmProgram = ns.NlHandle().XfrmStateDel ) @@ -224,7 +224,6 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f if add { action = "Adding" xfrmProgram = ns.NlHandle().XfrmStateAdd - crypt = &netlink.XfrmStateAlgo{Name: "cbc(aes)", Key: k.value} } if dir&reverse > 0 { @@ -236,7 +235,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f Mode: netlink.XFRM_MODE_TRANSPORT, } if add { - rSA.Crypt = crypt + rSA.Aead = buildAeadAlgo(k, spi.reverse) } exists, err := saExists(rSA) @@ -261,7 +260,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f Mode: netlink.XFRM_MODE_TRANSPORT, } if add { - fSA.Crypt = crypt + fSA.Aead = buildAeadAlgo(k, spi.forward) } exists, err := saExists(fSA) @@ -363,6 +362,16 @@ func buildSPI(src, dst net.IP, st uint32) int { return spi } +func buildAeadAlgo(k *key, s int) *netlink.XfrmStateAlgo { + salt := make([]byte, 4) + binary.BigEndian.PutUint32(salt, uint32(s)) + return &netlink.XfrmStateAlgo{ + Name: "rfc4106(gcm(aes))", + Key: append(k.value, salt...), + ICVLen: 64, + } +} + func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error { d.secMap.Lock() for node, indices := range d.secMap.nodes { From ddff1b5a876631de0326bf41b1f22f6816572bbb Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Tue, 19 Jul 2016 11:51:29 -0700 Subject: [PATCH 3/3] Use fnv1-a to construct the SPI Signed-off-by: Alessandro Boch --- libnetwork/drivers/overlay/encryption.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libnetwork/drivers/overlay/encryption.go b/libnetwork/drivers/overlay/encryption.go index e36fa0ff3a..ec8c4a8419 100644 --- a/libnetwork/drivers/overlay/encryption.go +++ b/libnetwork/drivers/overlay/encryption.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "hash/fnv" "net" "sync" "syscall" @@ -353,13 +354,13 @@ func spExists(sp *netlink.XfrmPolicy) (bool, error) { } func buildSPI(src, dst net.IP, st uint32) int { - spi := int(st) - f := src[len(src)-4:] - t := dst[len(dst)-4:] - for i := 0; i < 4; i++ { - spi = spi ^ (int(f[i])^int(t[3-i]))<