From fc4189694ba7ae86d857b39a5550547d031c23d0 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Mon, 13 Jan 2020 14:16:16 +0800 Subject: [PATCH] iproute2: initial version --- pkg/util/iproute2/address.go | 122 ++++++++++++++++++++++++++++++ pkg/util/iproute2/address_test.go | 70 +++++++++++++++++ pkg/util/iproute2/doc.go | 1 + pkg/util/iproute2/link.go | 88 +++++++++++++++++++++ pkg/util/iproute2/link_test.go | 54 +++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 pkg/util/iproute2/address.go create mode 100644 pkg/util/iproute2/address_test.go create mode 100644 pkg/util/iproute2/doc.go create mode 100644 pkg/util/iproute2/link.go create mode 100644 pkg/util/iproute2/link_test.go diff --git a/pkg/util/iproute2/address.go b/pkg/util/iproute2/address.go new file mode 100644 index 0000000000..b90b874e15 --- /dev/null +++ b/pkg/util/iproute2/address.go @@ -0,0 +1,122 @@ +package iproute2 + +import ( + "net" + + "github.com/vishvananda/netlink" +) + +type Address struct { + *Link + + addrBad bool + addrs []*netlink.Addr +} + +func NewAddress(ifname string, addresses ...string) *Address { + l := NewLink(ifname) + r := &Address{ + Link: l, + } + + addrs := make([]*netlink.Addr, len(addresses)) + for i, address := range addresses { + addr, err := netlink.ParseAddr(address) + if err != nil { + r.addrBad = true + r.addErr(err, "parse addr") + } + addrs[i] = addr + } + r.addrs = addrs + return r +} + +func (address *Address) link() (link netlink.Link, ok bool) { + if address.addrBad { + return + } + link = address.Link.link + if link != nil { + ok = true + } + return +} + +func (address *Address) Exact() *Address { + link, ok := address.link() + if !ok { + return address + } + for _, addr := range address.addrs { + err := netlink.AddrReplace(link, addr) + if err != nil { + address.addErr(err, "Exact: AddrReplace %s", addr) + } + } + + oldAddrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) + if err != nil { + address.addErr(err, "Exact: AddrList") + } + for _, oldAddr := range oldAddrs { + del := true + for _, addr := range address.addrs { + if oldAddr.Equal(*addr) { + del = false + break + } + } + if del { + err := netlink.AddrDel(link, &oldAddr) + if err != nil { + address.addErr(err, "Exact: AddrDel %s", oldAddr) + } + } + } + return address +} + +func (address *Address) Add() *Address { + link, ok := address.link() + if !ok { + return address + } + for _, addr := range address.addrs { + err := netlink.AddrReplace(link, addr) + if err != nil { + address.addErr(err, "Add: AddrReplace %s ", addr) + } + } + return address +} + +func (address *Address) Del() *Address { + link, ok := address.link() + if !ok { + return address + } + for _, addr := range address.addrs { + err := netlink.AddrDel(link, addr) + if err != nil { + address.addErr(err, "Del: AddrDel %s ", addr) + } + } + return address +} + +func (address *Address) List4() ([]net.IPNet, error) { + link, ok := address.link() + if !ok { + return nil, address.Err() + } + oldAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + if err != nil { + return nil, err + } + r := make([]net.IPNet, len(oldAddrs)) + for i, oldAddr := range oldAddrs { + r[i] = *oldAddr.IPNet + } + return r, nil +} diff --git a/pkg/util/iproute2/address_test.go b/pkg/util/iproute2/address_test.go new file mode 100644 index 0000000000..f6458d761d --- /dev/null +++ b/pkg/util/iproute2/address_test.go @@ -0,0 +1,70 @@ +package iproute2 + +import ( + "testing" +) + +func TestAddress(t *testing.T) { + ifname := genDummyName(t) + dum := addDummy(t, ifname) + defer delDummy(t, dum) + + emptyT := func(t *testing.T) { + l := NewAddress(ifname) + l.Exact() + if err := l.Err(); err != nil { + t.Fatalf("got err: %v", err) + } + if ipnets, err := l.List4(); err != nil { + t.Fatalf("list4 err: %v", err) + } else if len(ipnets) > 0 { + t.Fatalf("want empty, got %#v", ipnets) + } + } + + t.Run("exact some", func(t *testing.T) { + want := "10.168.222.236/24" + l := NewAddress(ifname, want, "fe80::222:d5ff:fe9e:28d1/64") + l.Exact() + if err := l.Err(); err != nil { + t.Fatalf("got err: %v", err) + } + if ipnets, err := l.List4(); err != nil { + t.Fatalf("list4 err: %v", err) + } else if len(ipnets) != 1 { + t.Fatalf("want 1, got %#v", ipnets) + } else if got := ipnets[0]; got.String() != want { + t.Fatalf("want %s, got %s", want, got.String()) + } + t.Run("empty", emptyT) + t.Run("empty empty", emptyT) + }) + +} + +func TestAddress_nopriv(t *testing.T) { + t.Run("bad address", func(t *testing.T) { + addresses := []string{ + "192.168.2.1.1", + "192.168.2.1/33", + "192.168.2.1", + "0.0.0.0", + } + address := NewAddress("lo", addresses...) + address.Exact().Add().Del().List4() + if nerr := len(address.errs); nerr != len(addresses) { + t.Errorf("want %d err, got %d: %v ", len(addresses), nerr, address.Err()) + } + }) + t.Run("good address", func(t *testing.T) { + address := NewAddress("lo", + "192.168.2.1/0", + "192.168.2.1/1", + "0.0.0.0/0", + ) + address.List4() + if err := address.Err(); err != nil { + t.Errorf("got err: %v", err) + } + }) +} diff --git a/pkg/util/iproute2/doc.go b/pkg/util/iproute2/doc.go new file mode 100644 index 0000000000..3dcd3a17df --- /dev/null +++ b/pkg/util/iproute2/doc.go @@ -0,0 +1 @@ +package iproute2 // import "yunion.io/x/onecloud/pkg/util/iproute2" diff --git a/pkg/util/iproute2/link.go b/pkg/util/iproute2/link.go new file mode 100644 index 0000000000..0f357525c6 --- /dev/null +++ b/pkg/util/iproute2/link.go @@ -0,0 +1,88 @@ +package iproute2 + +import ( + "net" + + "github.com/vishvananda/netlink" + + "yunion.io/x/pkg/errors" +) + +type Link struct { + ifname string + link netlink.Link + + errs []error +} + +func NewLink(ifname string) *Link { + l := &Link{ + ifname: ifname, + } + { + link, err := netlink.LinkByName(l.ifname) + if err != nil { + l.addErr(err, "LinkByName %s", ifname) + return l + } + l.link = link + } + return l +} + +func (l *Link) addErr(err error, fmtStr string, vals ...interface{}) { + l.errs = append(l.errs, errors.Wrapf(err, fmtStr, vals...)) +} + +func (l *Link) Err() error { + err := errors.NewAggregate(l.errs) + if err != nil { + return errors.Wrapf(err, "Link %s", l.ifname) + } + return nil +} + +func (l *Link) ResetErr() { + l.errs = nil +} + +func (l *Link) Up() *Link { + if l.link != nil { + if err := netlink.LinkSetUp(l.link); err != nil { + l.addErr(err, "LinkSetUp") + } + } + return l +} + +func (l *Link) Down() *Link { + if l.link != nil { + if err := netlink.LinkSetDown(l.link); err != nil { + l.addErr(err, "LinkSetDown") + } + } + return l +} + +func (l *Link) MTU(mtu int) *Link { + if l.link != nil { + if err := netlink.LinkSetMTU(l.link, mtu); err != nil { + l.addErr(err, "LinkSetMTU") + } + } + return l +} + +func (l *Link) Address(address string) *Link { + if l.link != nil { + hwaddr, err := net.ParseMAC(address) + if err != nil { + l.addErr(err, "bad hwaddr: %s", address) + return l + } + if err := netlink.LinkSetHardwareAddr(l.link, hwaddr); err != nil { + l.addErr(err, "LinkSetHardwareAddr") + } + } + return l +} diff --git a/pkg/util/iproute2/link_test.go b/pkg/util/iproute2/link_test.go new file mode 100644 index 0000000000..4bd1b8f910 --- /dev/null +++ b/pkg/util/iproute2/link_test.go @@ -0,0 +1,54 @@ +package iproute2 + +import ( + "fmt" + "testing" + + "github.com/vishvananda/netlink" +) + +func genDummyName(t *testing.T) string { + for i := 0; i < 100; i++ { + name := fmt.Sprintf("dummy%d", i) + if _, err := netlink.LinkByName(name); err != nil { + return name + } + } + t.Fatalf("can't even find a dummy name") + return "" +} + +func addDummy(t *testing.T, name string) *netlink.Dummy { + attrs := netlink.NewLinkAttrs() + attrs.Name = name + dum := &netlink.Dummy{ + LinkAttrs: attrs, + } + if err := netlink.LinkAdd(dum); err != nil { + t.Skipf("add %s: %v", name, err) + } + return dum +} + +func delDummy(t *testing.T, dum *netlink.Dummy) { + if err := netlink.LinkDel(dum); err != nil { + t.Errorf("del %s: %v", dum.Name, err) + } +} + +func TestLink(t *testing.T) { + ifname := genDummyName(t) + dum := addDummy(t, ifname) + defer delDummy(t, dum) + + l := NewLink(ifname) + l.Up().MTU(100).Address("00:11:22:33:44:55") + if err := l.Err(); err != nil { + t.Fatalf("got error: %v", err) + } + + l.Down().MTU(120).Address("00:11:22:33:44:88") + if err := l.Err(); err != nil { + t.Fatalf("got error: %v", err) + } +}