From 9438c6ff27186c0d0d366452fab5f953404164b3 Mon Sep 17 00:00:00 2001 From: JC Martin Date: Sat, 29 Oct 2016 22:46:24 -0700 Subject: [PATCH] Add support for VTI and IPIP --- addr.go | 8 +++++ addr_linux.go | 22 +++++++++--- addr_test.go | 11 ++++++ link.go | 35 +++++++++++++++++++ link_linux.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ link_test.go | 45 ++++++++++++++++++++++--- nl/link_linux.go | 29 ++++++++++++++++ 7 files changed, 229 insertions(+), 9 deletions(-) diff --git a/addr.go b/addr.go index 079fff3..dea8817 100644 --- a/addr.go +++ b/addr.go @@ -13,6 +13,7 @@ type Addr struct { Label string Flags int Scope int + Peer *net.IPNet } // String returns $ip/$netmask $label @@ -43,3 +44,10 @@ func (a Addr) Equal(x Addr) bool { // ignore label for comparison return a.IP.Equal(x.IP) && sizea == sizeb } + +func (a Addr) PeerEqual(x Addr) bool { + sizea, _ := a.Peer.Mask.Size() + sizeb, _ := x.Peer.Mask.Size() + // ignore label for comparison + return a.Peer.IP.Equal(x.Peer.IP) && sizea == sizeb +} diff --git a/addr_linux.go b/addr_linux.go index 8820c02..f314a7a 100644 --- a/addr_linux.go +++ b/addr_linux.go @@ -56,17 +56,27 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error msg.Prefixlen = uint8(prefixlen) req.AddData(msg) - var addrData []byte + var localAddrData []byte if family == FAMILY_V4 { - addrData = addr.IP.To4() + localAddrData = addr.IP.To4() } else { - addrData = addr.IP.To16() + localAddrData = addr.IP.To16() } - localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData) + localData := nl.NewRtAttr(syscall.IFA_LOCAL, localAddrData) req.AddData(localData) + var peerAddrData []byte + if addr.Peer != nil { + if family == FAMILY_V4 { + peerAddrData = addr.Peer.IP.To4() + } else { + peerAddrData = addr.Peer.IP.To16() + } + } else { + peerAddrData = localAddrData + } - addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData) + addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, peerAddrData) req.AddData(addressData) if addr.Flags != 0 { @@ -161,11 +171,13 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) { IP: attr.Value, Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), } + addr.Peer = dst case syscall.IFA_LOCAL: local = &net.IPNet{ IP: attr.Value, Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), } + addr.IPNet = local case syscall.IFA_LABEL: addr.Label = string(attr.Value[:len(attr.Value)-1]) case IFA_FLAGS: diff --git a/addr_test.go b/addr_test.go index ca74ad3..c834a30 100644 --- a/addr_test.go +++ b/addr_test.go @@ -13,6 +13,7 @@ func TestAddr(t *testing.T) { } // TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels? var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)} + var peer = &net.IPNet{IP: net.IPv4(127, 0, 0, 3), Mask: net.CIDRMask(24, 32)} var addrTests = []struct { addr *Addr expected *Addr @@ -37,6 +38,10 @@ func TestAddr(t *testing.T) { &Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE}, &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE}, }, + { + &Addr{IPNet: address, Peer: peer}, + &Addr{IPNet: address, Peer: peer, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, } tearDown := setUpNetlinkTest(t) @@ -77,6 +82,12 @@ func TestAddr(t *testing.T) { t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope) } + if tt.expected.Peer != nil { + if !addrs[0].PeerEqual(*tt.expected) { + t.Fatalf("Peer Address ip no set properly, got=%s, expected=%s", addrs[0].Peer, tt.expected.Peer) + } + } + // Pass FAMILY_V4, we should get the same results as FAMILY_ALL addrs, err = AddrList(link, FAMILY_V4) if err != nil { diff --git a/link.go b/link.go index 0e160a9..4cd6e8e 100644 --- a/link.go +++ b/link.go @@ -589,6 +589,41 @@ func (gretap *Gretap) Type() string { return "gretap" } +type Iptun struct { + LinkAttrs + Ttl uint8 + Tos uint8 + PMtuDisc uint8 + Link uint32 + Local net.IP + Remote net.IP +} + +func (iptun *Iptun) Attrs() *LinkAttrs { + return &iptun.LinkAttrs +} + +func (iptun *Iptun) Type() string { + return "ipip" +} + +type Vti struct { + LinkAttrs + IKey uint32 + OKey uint32 + Link uint32 + Local net.IP + Remote net.IP +} + +func (vti *Vti) Attrs() *LinkAttrs { + return &vti.LinkAttrs +} + +func (iptun *Vti) Type() string { + return "vti" +} + // iproute2 supported devices; // vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | diff --git a/link_linux.go b/link_linux.go index 9a428b7..9fdb45a 100644 --- a/link_linux.go +++ b/link_linux.go @@ -783,6 +783,10 @@ func (h *Handle) LinkAdd(link Link) error { } } else if gretap, ok := link.(*Gretap); ok { addGretapAttrs(gretap, linkInfo) + } else if iptun, ok := link.(*Iptun); ok { + addIptunAttrs(iptun, linkInfo) + } else if vti, ok := link.(*Vti); ok { + addVtiAttrs(vti, linkInfo) } req.AddData(linkInfo) @@ -1006,6 +1010,10 @@ func LinkDeserialize(m []byte) (Link, error) { link = &Macvtap{} case "gretap": link = &Gretap{} + case "ipip": + link = &Iptun{} + case "vti": + link = &Vti{} default: link = &GenericLink{LinkType: linkType} } @@ -1029,6 +1037,10 @@ func LinkDeserialize(m []byte) (Link, error) { parseMacvtapData(link, data) case "gretap": parseGretapData(link, data) + case "ipip": + parseIptunData(link, data) + case "vti": + parseVtiData(link, data) } } } @@ -1518,3 +1530,79 @@ func parseLinkXdp(data []byte) (*LinkXdp, error) { } return xdp, nil } + +func addIptunAttrs(iptun *Iptun, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + ip := iptun.Local.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_LOCAL, []byte(ip)) + } + + ip = iptun.Remote.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_REMOTE, []byte(ip)) + } + + if iptun.Link != 0 { + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_LINK, nl.Uint32Attr(iptun.Link)) + } + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_PMTUDISC, nl.Uint8Attr(iptun.PMtuDisc)) + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_TTL, nl.Uint8Attr(iptun.Ttl)) + nl.NewRtAttrChild(data, nl.IFLA_IPTUN_TOS, nl.Uint8Attr(iptun.Tos)) +} + +func parseIptunData(link Link, data []syscall.NetlinkRouteAttr) { + iptun := link.(*Iptun) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_IPTUN_LOCAL: + iptun.Local = net.IP(datum.Value[0:4]) + case nl.IFLA_IPTUN_REMOTE: + iptun.Remote = net.IP(datum.Value[0:4]) + case nl.IFLA_IPTUN_TTL: + iptun.Ttl = uint8(datum.Value[0]) + case nl.IFLA_IPTUN_TOS: + iptun.Tos = uint8(datum.Value[0]) + case nl.IFLA_IPTUN_PMTUDISC: + iptun.PMtuDisc = uint8(datum.Value[0]) + } + } +} + +func addVtiAttrs(vti *Vti, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + + ip := vti.Local.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VTI_LOCAL, []byte(ip)) + } + + ip = vti.Remote.To4() + if ip != nil { + nl.NewRtAttrChild(data, nl.IFLA_VTI_REMOTE, []byte(ip)) + } + + if vti.Link != 0 { + nl.NewRtAttrChild(data, nl.IFLA_VTI_LINK, nl.Uint32Attr(vti.Link)) + } + + nl.NewRtAttrChild(data, nl.IFLA_VTI_IKEY, htonl(vti.IKey)) + nl.NewRtAttrChild(data, nl.IFLA_VTI_OKEY, htonl(vti.OKey)) +} + +func parseVtiData(link Link, data []syscall.NetlinkRouteAttr) { + vti := link.(*Vti) + for _, datum := range data { + switch datum.Attr.Type { + case nl.IFLA_VTI_LOCAL: + vti.Local = net.IP(datum.Value[0:4]) + case nl.IFLA_VTI_REMOTE: + vti.Remote = net.IP(datum.Value[0:4]) + case nl.IFLA_VTI_IKEY: + vti.IKey = ntohl(datum.Value[0:4]) + case nl.IFLA_VTI_OKEY: + vti.OKey = ntohl(datum.Value[0:4]) + } + } +} diff --git a/link_test.go b/link_test.go index 34c0772..bf79231 100644 --- a/link_test.go +++ b/link_test.go @@ -21,7 +21,6 @@ func testLinkAddDel(t *testing.T, link Link) { if err != nil { t.Fatal(err) } - num := len(links) if err := LinkAdd(link); err != nil { t.Fatal(err) @@ -120,6 +119,20 @@ func testLinkAddDel(t *testing.T, link Link) { } } + if _, ok := link.(*Vti); ok { + _, ok := result.(*Vti) + if !ok { + t.Fatal("Result of create is not a vti") + } + } + + if _, ok := link.(*Iptun); ok { + _, ok := result.(*Iptun) + if !ok { + t.Fatal("Result of create is not a iptun") + } + } + if err = LinkDel(link); err != nil { t.Fatal(err) } @@ -129,9 +142,10 @@ func testLinkAddDel(t *testing.T, link Link) { t.Fatal(err) } - if len(links) != num { - t.Fatal("Link not removed properly") - return + for _, l := range links { + if l.Attrs().Name == link.Attrs().Name { + t.Fatal("Link not removed properly") + } } } @@ -969,3 +983,26 @@ func TestLinkXdp(t *testing.T) { t.Fatal(err) } } + +func TestLinkAddDelIptun(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Iptun{ + LinkAttrs: LinkAttrs{Name: "iptunfoo"}, + PMtuDisc: 1, + Local: net.IPv4(127, 0, 0, 1), + Remote: net.IPv4(127, 0, 0, 1)}) +} + +func TestLinkAddDelVti(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Vti{ + LinkAttrs: LinkAttrs{Name: "vtifoo"}, + IKey: 0x101, + OKey: 0x101, + Local: net.IPv4(127, 0, 0, 1), + Remote: net.IPv4(127, 0, 0, 1)}) +} diff --git a/nl/link_linux.go b/nl/link_linux.go index 115f60b..e2144a7 100644 --- a/nl/link_linux.go +++ b/nl/link_linux.go @@ -418,3 +418,32 @@ const ( IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */ IFLA_XDP_MAX = IFLA_XDP_ATTACHED ) + +const ( + IFLA_IPTUN_UNSPEC = iota + IFLA_IPTUN_LINK + IFLA_IPTUN_LOCAL + IFLA_IPTUN_REMOTE + IFLA_IPTUN_TTL + IFLA_IPTUN_TOS + IFLA_IPTUN_ENCAP_LIMIT + IFLA_IPTUN_FLOWINFO + IFLA_IPTUN_FLAGS + IFLA_IPTUN_PROTO + IFLA_IPTUN_PMTUDISC + IFLA_IPTUN_6RD_PREFIX + IFLA_IPTUN_6RD_RELAY_PREFIX + IFLA_IPTUN_6RD_PREFIXLEN + IFLA_IPTUN_6RD_RELAY_PREFIXLEN + IFLA_IPTUN_MAX = IFLA_IPTUN_6RD_RELAY_PREFIXLEN +) + +const ( + IFLA_VTI_UNSPEC = iota + IFLA_VTI_LINK + IFLA_VTI_IKEY + IFLA_VTI_OKEY + IFLA_VTI_LOCAL + IFLA_VTI_REMOTE + IFLA_VTI_MAX = IFLA_VTI_REMOTE +)