From a35025569e0af071586f1a5ee7a4959d308ad653 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Thu, 18 Jun 2015 15:59:16 -0700 Subject: [PATCH 1/7] Updated Godeps Signed-off-by: Jana Radhakrishnan --- libnetwork/Godeps/Godeps.json | 5 + .../docker/libcontainer/netlink/MAINTAINERS | 2 + .../docker/libcontainer/netlink/netlink.go | 31 + .../libcontainer/netlink/netlink_linux.go | 1307 +++++++++++++++++ .../libcontainer/netlink/netlink_linux_arm.go | 5 + .../netlink/netlink_linux_notarm.go | 7 + .../netlink/netlink_linux_test.go | 408 +++++ .../netlink/netlink_unsupported.go | 88 ++ 8 files changed, 1853 insertions(+) create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go create mode 100644 libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index 0771cfbf5f..e0506650ff 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -69,6 +69,11 @@ "Comment": "v1.4.1-4106-g637023a", "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" }, + { + "ImportPath": "github.com/docker/libcontainer/netlink", + "Comment": "v1.4.0-495-g3e66118", + "Rev": "3e661186ba24f259d3860f067df052c7f6904bee" + }, { "ImportPath": "github.com/docker/libcontainer/user", "Comment": "v1.4.0-495-g3e66118", diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS new file mode 100644 index 0000000000..1cb551364d --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/MAINTAINERS @@ -0,0 +1,2 @@ +Michael Crosby (@crosbymichael) +Guillaume J. Charmes (@creack) diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go new file mode 100644 index 0000000000..9088366061 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink.go @@ -0,0 +1,31 @@ +// Packet netlink provide access to low level Netlink sockets and messages. +// +// Actual implementations are in: +// netlink_linux.go +// netlink_darwin.go +package netlink + +import ( + "errors" + "net" +) + +var ( + ErrWrongSockType = errors.New("Wrong socket type") + ErrShortResponse = errors.New("Got short response from netlink") + ErrInterfaceExists = errors.New("Network interface already exists") +) + +// A Route is a subnet associated with the interface to reach it. +type Route struct { + *net.IPNet + Iface *net.Interface + Default bool +} + +// An IfAddr defines IP network settings for a given network interface +type IfAddr struct { + Iface *net.Interface + IP net.IP + IPNet *net.IPNet +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go new file mode 100644 index 0000000000..c438ec300f --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go @@ -0,0 +1,1307 @@ +package netlink + +import ( + "encoding/binary" + "fmt" + "io" + "math/rand" + "net" + "os" + "sync/atomic" + "syscall" + "unsafe" +) + +const ( + IFNAMSIZ = 16 + DEFAULT_CHANGE = 0xFFFFFFFF + IFLA_INFO_KIND = 1 + IFLA_INFO_DATA = 2 + VETH_INFO_PEER = 1 + IFLA_MACVLAN_MODE = 1 + IFLA_VLAN_ID = 1 + IFLA_NET_NS_FD = 28 + IFLA_ADDRESS = 1 + IFLA_BRPORT_MODE = 4 + SIOC_BRADDBR = 0x89a0 + SIOC_BRDELBR = 0x89a1 + SIOC_BRADDIF = 0x89a2 +) + +const ( + MACVLAN_MODE_PRIVATE = 1 << iota + MACVLAN_MODE_VEPA + MACVLAN_MODE_BRIDGE + MACVLAN_MODE_PASSTHRU +) + +var nextSeqNr uint32 + +type ifreqHwaddr struct { + IfrnName [IFNAMSIZ]byte + IfruHwaddr syscall.RawSockaddr +} + +type ifreqIndex struct { + IfrnName [IFNAMSIZ]byte + IfruIndex int32 +} + +type ifreqFlags struct { + IfrnName [IFNAMSIZ]byte + Ifruflags uint16 +} + +var native binary.ByteOrder + +func init() { + var x uint32 = 0x01020304 + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + native = binary.BigEndian + } else { + native = binary.LittleEndian + } +} + +func getIpFamily(ip net.IP) int { + if len(ip) <= net.IPv4len { + return syscall.AF_INET + } + if ip.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +type NetlinkRequestData interface { + Len() int + ToWireFormat() []byte +} + +type IfInfomsg struct { + syscall.IfInfomsg +} + +func newIfInfomsg(family int) *IfInfomsg { + return &IfInfomsg{ + IfInfomsg: syscall.IfInfomsg{ + Family: uint8(family), + }, + } +} + +func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg { + msg := newIfInfomsg(family) + parent.children = append(parent.children, msg) + return msg +} + +func (msg *IfInfomsg) ToWireFormat() []byte { + length := syscall.SizeofIfInfomsg + b := make([]byte, length) + b[0] = msg.Family + b[1] = 0 + native.PutUint16(b[2:4], msg.Type) + native.PutUint32(b[4:8], uint32(msg.Index)) + native.PutUint32(b[8:12], msg.Flags) + native.PutUint32(b[12:16], msg.Change) + return b +} + +func (msg *IfInfomsg) Len() int { + return syscall.SizeofIfInfomsg +} + +type IfAddrmsg struct { + syscall.IfAddrmsg +} + +func newIfAddrmsg(family int) *IfAddrmsg { + return &IfAddrmsg{ + IfAddrmsg: syscall.IfAddrmsg{ + Family: uint8(family), + }, + } +} + +func (msg *IfAddrmsg) ToWireFormat() []byte { + length := syscall.SizeofIfAddrmsg + b := make([]byte, length) + b[0] = msg.Family + b[1] = msg.Prefixlen + b[2] = msg.Flags + b[3] = msg.Scope + native.PutUint32(b[4:8], msg.Index) + return b +} + +func (msg *IfAddrmsg) Len() int { + return syscall.SizeofIfAddrmsg +} + +type RtMsg struct { + syscall.RtMsg +} + +func newRtMsg() *RtMsg { + return &RtMsg{ + RtMsg: syscall.RtMsg{ + Table: syscall.RT_TABLE_MAIN, + Scope: syscall.RT_SCOPE_UNIVERSE, + Protocol: syscall.RTPROT_BOOT, + Type: syscall.RTN_UNICAST, + }, + } +} + +func (msg *RtMsg) ToWireFormat() []byte { + length := syscall.SizeofRtMsg + b := make([]byte, length) + b[0] = msg.Family + b[1] = msg.Dst_len + b[2] = msg.Src_len + b[3] = msg.Tos + b[4] = msg.Table + b[5] = msg.Protocol + b[6] = msg.Scope + b[7] = msg.Type + native.PutUint32(b[8:12], msg.Flags) + return b +} + +func (msg *RtMsg) Len() int { + return syscall.SizeofRtMsg +} + +func rtaAlignOf(attrlen int) int { + return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) +} + +type RtAttr struct { + syscall.RtAttr + Data []byte + children []NetlinkRequestData +} + +func newRtAttr(attrType int, data []byte) *RtAttr { + return &RtAttr{ + RtAttr: syscall.RtAttr{ + Type: uint16(attrType), + }, + children: []NetlinkRequestData{}, + Data: data, + } +} + +func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr { + attr := newRtAttr(attrType, data) + parent.children = append(parent.children, attr) + return attr +} + +func (a *RtAttr) Len() int { + if len(a.children) == 0 { + return (syscall.SizeofRtAttr + len(a.Data)) + } + + l := 0 + for _, child := range a.children { + l += child.Len() + } + l += syscall.SizeofRtAttr + return rtaAlignOf(l + len(a.Data)) +} + +func (a *RtAttr) ToWireFormat() []byte { + length := a.Len() + buf := make([]byte, rtaAlignOf(length)) + + if a.Data != nil { + copy(buf[4:], a.Data) + } else { + next := 4 + for _, child := range a.children { + childBuf := child.ToWireFormat() + copy(buf[next:], childBuf) + next += rtaAlignOf(len(childBuf)) + } + } + + if l := uint16(length); l != 0 { + native.PutUint16(buf[0:2], l) + } + native.PutUint16(buf[2:4], a.Type) + return buf +} + +func uint32Attr(t int, n uint32) *RtAttr { + buf := make([]byte, 4) + native.PutUint32(buf, n) + return newRtAttr(t, buf) +} + +type NetlinkRequest struct { + syscall.NlMsghdr + Data []NetlinkRequestData +} + +func (rr *NetlinkRequest) ToWireFormat() []byte { + length := rr.Len + dataBytes := make([][]byte, len(rr.Data)) + for i, data := range rr.Data { + dataBytes[i] = data.ToWireFormat() + length += uint32(len(dataBytes[i])) + } + b := make([]byte, length) + native.PutUint32(b[0:4], length) + native.PutUint16(b[4:6], rr.Type) + native.PutUint16(b[6:8], rr.Flags) + native.PutUint32(b[8:12], rr.Seq) + native.PutUint32(b[12:16], rr.Pid) + + next := 16 + for _, data := range dataBytes { + copy(b[next:], data) + next += len(data) + } + return b +} + +func (rr *NetlinkRequest) AddData(data NetlinkRequestData) { + if data != nil { + rr.Data = append(rr.Data, data) + } +} + +func newNetlinkRequest(proto, flags int) *NetlinkRequest { + return &NetlinkRequest{ + NlMsghdr: syscall.NlMsghdr{ + Len: uint32(syscall.NLMSG_HDRLEN), + Type: uint16(proto), + Flags: syscall.NLM_F_REQUEST | uint16(flags), + Seq: atomic.AddUint32(&nextSeqNr, 1), + }, + } +} + +type NetlinkSocket struct { + fd int + lsa syscall.SockaddrNetlink +} + +func getNetlinkSocket() (*NetlinkSocket, error) { + fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE) + if err != nil { + return nil, err + } + s := &NetlinkSocket{ + fd: fd, + } + s.lsa.Family = syscall.AF_NETLINK + if err := syscall.Bind(fd, &s.lsa); err != nil { + syscall.Close(fd) + return nil, err + } + + return s, nil +} + +func (s *NetlinkSocket) Close() { + syscall.Close(s.fd) +} + +func (s *NetlinkSocket) Send(request *NetlinkRequest) error { + if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil { + return err + } + return nil +} + +func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) { + rb := make([]byte, syscall.Getpagesize()) + nr, _, err := syscall.Recvfrom(s.fd, rb, 0) + if err != nil { + return nil, err + } + if nr < syscall.NLMSG_HDRLEN { + return nil, ErrShortResponse + } + rb = rb[:nr] + return syscall.ParseNetlinkMessage(rb) +} + +func (s *NetlinkSocket) GetPid() (uint32, error) { + lsa, err := syscall.Getsockname(s.fd) + if err != nil { + return 0, err + } + switch v := lsa.(type) { + case *syscall.SockaddrNetlink: + return v.Pid, nil + } + return 0, ErrWrongSockType +} + +func (s *NetlinkSocket) CheckMessage(m syscall.NetlinkMessage, seq, pid uint32) error { + if m.Header.Seq != seq { + return fmt.Errorf("netlink: invalid seq %d, expected %d", m.Header.Seq, seq) + } + if m.Header.Pid != pid { + return fmt.Errorf("netlink: wrong pid %d, expected %d", m.Header.Pid, pid) + } + if m.Header.Type == syscall.NLMSG_DONE { + return io.EOF + } + if m.Header.Type == syscall.NLMSG_ERROR { + e := int32(native.Uint32(m.Data[0:4])) + if e == 0 { + return io.EOF + } + return syscall.Errno(-e) + } + return nil +} + +func (s *NetlinkSocket) HandleAck(seq uint32) error { + pid, err := s.GetPid() + if err != nil { + return err + } + +outer: + for { + msgs, err := s.Receive() + if err != nil { + return err + } + for _, m := range msgs { + if err := s.CheckMessage(m, seq, pid); err != nil { + if err == io.EOF { + break outer + } + return err + } + } + } + + return nil +} + +func zeroTerminated(s string) []byte { + return []byte(s + "\000") +} + +func nonZeroTerminated(s string) []byte { + return []byte(s) +} + +// Add a new network link of a specified type. +// This is identical to running: ip link add $name type $linkType +func NetworkLinkAdd(name string, linkType string) error { + if name == "" || linkType == "" { + return fmt.Errorf("Neither link name nor link type can be empty!") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType)) + wb.AddData(linkInfo) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) + wb.AddData(nameData) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Delete a network link. +// This is identical to running: ip link del $name +func NetworkLinkDel(name string) error { + if name == "" { + return fmt.Errorf("Network link name can not be empty!") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + iface, err := net.InterfaceByName(name) + if err != nil { + return err + } + + wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + wb.AddData(msg) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Bring up a particular network interface. +// This is identical to running: ip link set dev $name up +func NetworkLinkUp(iface *net.Interface) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + msg.Flags = syscall.IFF_UP + msg.Change = syscall.IFF_UP + wb.AddData(msg) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Bring down a particular network interface. +// This is identical to running: ip link set $name down +func NetworkLinkDown(iface *net.Interface) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + msg.Flags = 0 & ^syscall.IFF_UP + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Set link layer address ie. MAC Address. +// This is identical to running: ip link set dev $name address $macaddress +func NetworkSetMacAddress(iface *net.Interface, macaddr string) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + hwaddr, err := net.ParseMAC(macaddr) + if err != nil { + return err + } + + var ( + MULTICAST byte = 0x1 + ) + + if hwaddr[0]&0x1 == MULTICAST { + return fmt.Errorf("Multicast MAC Address is not supported: %s", macaddr) + } + + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + + macdata := make([]byte, 6) + copy(macdata, hwaddr) + data := newRtAttr(IFLA_ADDRESS, macdata) + wb.AddData(data) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +// Set link Maximum Transmission Unit +// This is identical to running: ip link set dev $name mtu $MTU +// bridge is a bitch here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=292088 +// https://bugzilla.redhat.com/show_bug.cgi?id=697021 +// There is a discussion about how to deal with ifcs joining bridge with MTU > 1500 +// Regular network nterfaces do seem to work though! +func NetworkSetMTU(iface *net.Interface, mtu int) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +// Set link queue length +// This is identical to running: ip link set dev $name txqueuelen $QLEN +func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + wb.AddData(uint32Attr(syscall.IFLA_TXQLEN, uint32(txQueueLen))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +func networkMasterAction(iface *net.Interface, rtattr *RtAttr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + wb.AddData(rtattr) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Add an interface to bridge. +// This is identical to running: ip link set $name master $master +func NetworkSetMaster(iface, master *net.Interface) error { + data := uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)) + return networkMasterAction(iface, data) +} + +// Remove an interface from the bridge +// This is is identical to to running: ip link $name set nomaster +func NetworkSetNoMaster(iface *net.Interface) error { + data := uint32Attr(syscall.IFLA_MASTER, 0) + return networkMasterAction(iface, data) +} + +func networkSetNsAction(iface *net.Interface, rtattr *RtAttr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + wb.AddData(msg) + wb.AddData(rtattr) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Move a particular network interface to a particular network namespace +// specified by PID. This is identical to running: ip link set dev $name netns $pid +func NetworkSetNsPid(iface *net.Interface, nspid int) error { + data := uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)) + return networkSetNsAction(iface, data) +} + +// Move a particular network interface to a particular mounted +// network namespace specified by file descriptor. +// This is idential to running: ip link set dev $name netns $fd +func NetworkSetNsFd(iface *net.Interface, fd int) error { + data := uint32Attr(IFLA_NET_NS_FD, uint32(fd)) + return networkSetNsAction(iface, data) +} + +// Rename a particular interface to a different name +// !!! Note that you can't rename an active interface. You need to bring it down before renaming it. +// This is identical to running: ip link set dev ${oldName} name ${newName} +func NetworkChangeName(iface *net.Interface, newName string) error { + if len(newName) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", newName) + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + wb.AddData(msg) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(newName)) + wb.AddData(nameData) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Add a new VETH pair link on the host +// This is identical to running: ip link add name $name type veth peer name $peername +func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1)) + wb.AddData(nameData) + + txqLen := make([]byte, 4) + native.PutUint32(txqLen, uint32(txQueueLen)) + txqData := newRtAttr(syscall.IFLA_TXQLEN, txqLen) + wb.AddData(txqData) + + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth")) + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil) + + newIfInfomsgChild(nest3, syscall.AF_UNSPEC) + newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2)) + + txqLen2 := make([]byte, 4) + native.PutUint32(txqLen2, uint32(txQueueLen)) + newRtAttrChild(nest3, syscall.IFLA_TXQLEN, txqLen2) + + wb.AddData(nest1) + + if err := s.Send(wb); err != nil { + return err + } + + if err := s.HandleAck(wb.Seq); err != nil { + if os.IsExist(err) { + return ErrInterfaceExists + } + + return err + } + + return nil +} + +// Add a new VLAN interface with masterDev as its upper device +// This is identical to running: +// ip link add name $name link $masterdev type vlan id $id +func NetworkLinkAddVlan(masterDev, vlanDev string, vlanId uint16) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + masterDevIfc, err := net.InterfaceByName(masterDev) + if err != nil { + return err + } + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("vlan")) + + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + vlanData := make([]byte, 2) + native.PutUint16(vlanData, vlanId) + newRtAttrChild(nest2, IFLA_VLAN_ID, vlanData) + wb.AddData(nest1) + + wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) + wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(vlanDev))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +// MacVlan link has LowerDev, UpperDev and operates in Mode mode +// This simplifies the code when creating MacVlan or MacVtap interface +type MacVlanLink struct { + MasterDev string + SlaveDev string + mode string +} + +func (m MacVlanLink) Mode() uint32 { + modeMap := map[string]uint32{ + "private": MACVLAN_MODE_PRIVATE, + "vepa": MACVLAN_MODE_VEPA, + "bridge": MACVLAN_MODE_BRIDGE, + "passthru": MACVLAN_MODE_PASSTHRU, + } + + return modeMap[m.mode] +} + +// Add MAC VLAN network interface with masterDev as its upper device +// This is identical to running: +// ip link add name $name link $masterdev type macvlan mode $mode +func networkLinkMacVlan(dev_type string, mcvln *MacVlanLink) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + + masterDevIfc, err := net.InterfaceByName(mcvln.MasterDev) + if err != nil { + return err + } + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil) + newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated(dev_type)) + + nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil) + macVlanData := make([]byte, 4) + native.PutUint32(macVlanData, mcvln.Mode()) + newRtAttrChild(nest2, IFLA_MACVLAN_MODE, macVlanData) + wb.AddData(nest1) + + wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index))) + wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(mcvln.SlaveDev))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +func NetworkLinkAddMacVlan(masterDev, macVlanDev string, mode string) error { + return networkLinkMacVlan("macvlan", &MacVlanLink{ + MasterDev: masterDev, + SlaveDev: macVlanDev, + mode: mode, + }) +} + +func NetworkLinkAddMacVtap(masterDev, macVlanDev string, mode string) error { + return networkLinkMacVlan("macvtap", &MacVlanLink{ + MasterDev: masterDev, + SlaveDev: macVlanDev, + mode: mode, + }) +} + +func networkLinkIpAction(action, flags int, ifa IfAddr) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + family := getIpFamily(ifa.IP) + + wb := newNetlinkRequest(action, flags) + + msg := newIfAddrmsg(family) + msg.Index = uint32(ifa.Iface.Index) + prefixLen, _ := ifa.IPNet.Mask.Size() + msg.Prefixlen = uint8(prefixLen) + wb.AddData(msg) + + var ipData []byte + if family == syscall.AF_INET { + ipData = ifa.IP.To4() + } else { + ipData = ifa.IP.To16() + } + + localData := newRtAttr(syscall.IFA_LOCAL, ipData) + wb.AddData(localData) + + addrData := newRtAttr(syscall.IFA_ADDRESS, ipData) + wb.AddData(addrData) + + if err := s.Send(wb); err != nil { + return err + } + + return s.HandleAck(wb.Seq) +} + +// Delete an IP address from an interface. This is identical to: +// ip addr del $ip/$ipNet dev $iface +func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return networkLinkIpAction( + syscall.RTM_DELADDR, + syscall.NLM_F_ACK, + IfAddr{iface, ip, ipNet}, + ) +} + +// Add an Ip address to an interface. This is identical to: +// ip addr add $ip/$ipNet dev $iface +func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return networkLinkIpAction( + syscall.RTM_NEWADDR, + syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK, + IfAddr{iface, ip, ipNet}, + ) +} + +// Returns an array of IPNet for all the currently routed subnets on ipv4 +// This is similar to the first column of "ip route" output +func NetworkGetRoutes() ([]Route, error) { + s, err := getNetlinkSocket() + if err != nil { + return nil, err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) + + msg := newIfInfomsg(syscall.AF_UNSPEC) + wb.AddData(msg) + + if err := s.Send(wb); err != nil { + return nil, err + } + + pid, err := s.GetPid() + if err != nil { + return nil, err + } + + res := make([]Route, 0) + +outer: + for { + msgs, err := s.Receive() + if err != nil { + return nil, err + } + for _, m := range msgs { + if err := s.CheckMessage(m, wb.Seq, pid); err != nil { + if err == io.EOF { + break outer + } + return nil, err + } + if m.Header.Type != syscall.RTM_NEWROUTE { + continue + } + + var r Route + + msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0])) + + if msg.Flags&syscall.RTM_F_CLONED != 0 { + // Ignore cloned routes + continue + } + + if msg.Table != syscall.RT_TABLE_MAIN { + // Ignore non-main tables + continue + } + + if msg.Family != syscall.AF_INET { + // Ignore non-ipv4 routes + continue + } + + if msg.Dst_len == 0 { + // Default routes + r.Default = true + } + + attrs, err := syscall.ParseNetlinkRouteAttr(&m) + if err != nil { + return nil, err + } + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_DST: + ip := attr.Value + r.IPNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)), + } + case syscall.RTA_OIF: + index := int(native.Uint32(attr.Value[0:4])) + r.Iface, _ = net.InterfaceByIndex(index) + } + } + if r.Default || r.IPNet != nil { + res = append(res, r) + } + } + } + + return res, nil +} + +// Add a new route table entry. +func AddRoute(destination, source, gateway, device string) error { + if destination == "" && source == "" && gateway == "" { + return fmt.Errorf("one of destination, source or gateway must not be blank") + } + + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + + wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + msg := newRtMsg() + currentFamily := -1 + var rtAttrs []*RtAttr + + if destination != "" { + destIP, destNet, err := net.ParseCIDR(destination) + if err != nil { + return fmt.Errorf("destination CIDR %s couldn't be parsed", destination) + } + destFamily := getIpFamily(destIP) + currentFamily = destFamily + destLen, bits := destNet.Mask.Size() + if destLen == 0 && bits == 0 { + return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination) + } + msg.Family = uint8(destFamily) + msg.Dst_len = uint8(destLen) + var destData []byte + if destFamily == syscall.AF_INET { + destData = destIP.To4() + } else { + destData = destIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData)) + } + + if source != "" { + srcIP := net.ParseIP(source) + if srcIP == nil { + return fmt.Errorf("source IP %s couldn't be parsed", source) + } + srcFamily := getIpFamily(srcIP) + if currentFamily != -1 && currentFamily != srcFamily { + return fmt.Errorf("source and destination ip were not the same IP family") + } + currentFamily = srcFamily + msg.Family = uint8(srcFamily) + var srcData []byte + if srcFamily == syscall.AF_INET { + srcData = srcIP.To4() + } else { + srcData = srcIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_PREFSRC, srcData)) + } + + if gateway != "" { + gwIP := net.ParseIP(gateway) + if gwIP == nil { + return fmt.Errorf("gateway IP %s couldn't be parsed", gateway) + } + gwFamily := getIpFamily(gwIP) + if currentFamily != -1 && currentFamily != gwFamily { + return fmt.Errorf("gateway, source, and destination ip were not the same IP family") + } + msg.Family = uint8(gwFamily) + var gwData []byte + if gwFamily == syscall.AF_INET { + gwData = gwIP.To4() + } else { + gwData = gwIP.To16() + } + rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData)) + } + + wb.AddData(msg) + for _, attr := range rtAttrs { + wb.AddData(attr) + } + + iface, err := net.InterfaceByName(device) + if err != nil { + return err + } + wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index))) + + if err := s.Send(wb); err != nil { + return err + } + return s.HandleAck(wb.Seq) +} + +// Add a new default gateway. Identical to: +// ip route add default via $ip +func AddDefaultGw(ip, device string) error { + return AddRoute("", "", ip, device) +} + +// THIS CODE DOES NOT COMMUNICATE WITH KERNEL VIA RTNETLINK INTERFACE +// IT IS HERE FOR BACKWARDS COMPATIBILITY WITH OLDER LINUX KERNELS +// WHICH SHIP WITH OLDER NOT ENTIRELY FUNCTIONAL VERSION OF NETLINK +func getIfSocket() (fd int, err error) { + for _, socket := range []int{ + syscall.AF_INET, + syscall.AF_PACKET, + syscall.AF_INET6, + } { + if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil { + break + } + } + if err == nil { + return fd, nil + } + return -1, err +} + +// Create the actual bridge device. This is more backward-compatible than +// netlink.NetworkLinkAdd and works on RHEL 6. +func CreateBridge(name string, setMacAddr bool) error { + if len(name) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", name) + } + + s, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(s) + + nameBytePtr, err := syscall.BytePtrFromString(name) + if err != nil { + return err + } + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 { + return err + } + if setMacAddr { + return SetMacAddress(name, randMacAddr()) + } + return nil +} + +// Delete the actual bridge device. +func DeleteBridge(name string) error { + s, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(s) + + nameBytePtr, err := syscall.BytePtrFromString(name) + if err != nil { + return err + } + + var ifr ifreqFlags + copy(ifr.IfrnName[:len(ifr.IfrnName)-1], []byte(name)) + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), + syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifr))); err != 0 { + return err + } + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), + SIOC_BRDELBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 { + return err + } + return nil +} + +// Add a slave to abridge device. This is more backward-compatible than +// netlink.NetworkSetMaster and works on RHEL 6. +func AddToBridge(iface, master *net.Interface) error { + if len(master.Name) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", master.Name) + } + + s, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(s) + + ifr := ifreqIndex{} + copy(ifr.IfrnName[:len(ifr.IfrnName)-1], master.Name) + ifr.IfruIndex = int32(iface.Index) + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDIF, uintptr(unsafe.Pointer(&ifr))); err != 0 { + return err + } + + return nil +} + +func randMacAddr() string { + hw := make(net.HardwareAddr, 6) + for i := 0; i < 6; i++ { + hw[i] = byte(rand.Intn(255)) + } + hw[0] &^= 0x1 // clear multicast bit + hw[0] |= 0x2 // set local assignment bit (IEEE802) + return hw.String() +} + +func SetMacAddress(name, addr string) error { + if len(name) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", name) + } + + hw, err := net.ParseMAC(addr) + if err != nil { + return err + } + + s, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(s) + + ifr := ifreqHwaddr{} + ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER + copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name) + + for i := 0; i < 6; i++ { + ifr.IfruHwaddr.Data[i] = ifrDataByte(hw[i]) + } + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 { + return err + } + return nil +} + +func SetHairpinMode(iface *net.Interface, enabled bool) error { + s, err := getNetlinkSocket() + if err != nil { + return err + } + defer s.Close() + req := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := newIfInfomsg(syscall.AF_BRIDGE) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + req.AddData(msg) + + mode := []byte{0} + if enabled { + mode[0] = byte(1) + } + + br := newRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil) + newRtAttrChild(br, IFLA_BRPORT_MODE, mode) + req.AddData(br) + if err := s.Send(req); err != nil { + return err + } + + return s.HandleAck(req.Seq) +} + +func ChangeName(iface *net.Interface, newName string) error { + if len(newName) >= IFNAMSIZ { + return fmt.Errorf("Interface name %s too long", newName) + } + + fd, err := getIfSocket() + if err != nil { + return err + } + defer syscall.Close(fd) + + data := [IFNAMSIZ * 2]byte{} + // the "-1"s here are very important for ensuring we get proper null + // termination of our new C strings + copy(data[:IFNAMSIZ-1], iface.Name) + copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName) + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 { + return errno + } + + return nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go new file mode 100644 index 0000000000..779e58a771 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_arm.go @@ -0,0 +1,5 @@ +package netlink + +func ifrDataByte(b byte) uint8 { + return uint8(b) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go new file mode 100644 index 0000000000..f151722a1b --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_notarm.go @@ -0,0 +1,7 @@ +// +build !arm + +package netlink + +func ifrDataByte(b byte) int8 { + return int8(b) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go new file mode 100644 index 0000000000..3f6511abfe --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux_test.go @@ -0,0 +1,408 @@ +package netlink + +import ( + "net" + "strings" + "syscall" + "testing" +) + +type testLink struct { + name string + linkType string +} + +func addLink(t *testing.T, name string, linkType string) { + if err := NetworkLinkAdd(name, linkType); err != nil { + t.Fatalf("Unable to create %s link: %s", name, err) + } +} + +func readLink(t *testing.T, name string) *net.Interface { + iface, err := net.InterfaceByName(name) + if err != nil { + t.Fatalf("Could not find %s interface: %s", name, err) + } + + return iface +} + +func deleteLink(t *testing.T, name string) { + if err := NetworkLinkDel(name); err != nil { + t.Fatalf("Unable to delete %s link: %s", name, err) + } +} + +func upLink(t *testing.T, name string) { + iface := readLink(t, name) + if err := NetworkLinkUp(iface); err != nil { + t.Fatalf("Could not bring UP %#v interface: %s", iface, err) + } +} + +func downLink(t *testing.T, name string) { + iface := readLink(t, name) + if err := NetworkLinkDown(iface); err != nil { + t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err) + } +} + +func ipAssigned(iface *net.Interface, ip net.IP) bool { + addrs, _ := iface.Addrs() + + for _, addr := range addrs { + args := strings.SplitN(addr.String(), "/", 2) + if args[0] == ip.String() { + return true + } + } + + return false +} + +func TestNetworkLinkAddDel(t *testing.T) { + if testing.Short() { + return + } + + testLinks := []testLink{ + {"tstEth", "dummy"}, + {"tstBr", "bridge"}, + } + + for _, tl := range testLinks { + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + readLink(t, tl.name) + } +} + +func TestNetworkLinkUpDown(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{name: "tstEth", linkType: "dummy"} + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + upLink(t, tl.name) + ifcAfterUp := readLink(t, tl.name) + + if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP { + t.Fatalf("Could not bring UP %#v initerface", tl) + } + + downLink(t, tl.name) + ifcAfterDown := readLink(t, tl.name) + + if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP { + t.Fatalf("Could not bring DOWN %#v initerface", tl) + } +} + +func TestNetworkSetMacAddress(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{name: "tstEth", linkType: "dummy"} + macaddr := "22:ce:e0:99:63:6f" + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + ifcBeforeSet := readLink(t, tl.name) + + if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil { + t.Fatalf("Could not set %s MAC address on %#v interface: %s", macaddr, tl, err) + } + + ifcAfterSet := readLink(t, tl.name) + + if ifcAfterSet.HardwareAddr.String() != macaddr { + t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl) + } +} + +func TestNetworkSetMTU(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{name: "tstEth", linkType: "dummy"} + mtu := 1400 + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + ifcBeforeSet := readLink(t, tl.name) + + if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil { + t.Fatalf("Could not set %d MTU on %#v interface: %s", mtu, tl, err) + } + + ifcAfterSet := readLink(t, tl.name) + + if ifcAfterSet.MTU != mtu { + t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl) + } +} + +func TestNetworkSetMasterNoMaster(t *testing.T) { + if testing.Short() { + return + } + + master := testLink{"tstBr", "bridge"} + slave := testLink{"tstEth", "dummy"} + testLinks := []testLink{master, slave} + + for _, tl := range testLinks { + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + upLink(t, tl.name) + } + + masterIfc := readLink(t, master.name) + slaveIfc := readLink(t, slave.name) + if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil { + t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err) + } + + // Trying to figure out a way to test which will not break on RHEL6. + // We could check for existence of /sys/class/net/tstEth/upper_tstBr + // which should point to the ../tstBr which is the UPPER device i.e. network bridge + + if err := NetworkSetNoMaster(slaveIfc); err != nil { + t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err) + } +} + +func TestNetworkChangeName(t *testing.T) { + if testing.Short() { + return + } + + tl := testLink{"tstEth", "dummy"} + newName := "newTst" + + addLink(t, tl.name, tl.linkType) + + linkIfc := readLink(t, tl.name) + if err := NetworkChangeName(linkIfc, newName); err != nil { + deleteLink(t, tl.name) + t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err) + } + + readLink(t, newName) + deleteLink(t, newName) +} + +func TestNetworkLinkAddVlan(t *testing.T) { + if testing.Short() { + return + } + + tl := struct { + name string + id uint16 + }{ + name: "tstVlan", + id: 32, + } + masterLink := testLink{"tstEth", "dummy"} + + addLink(t, masterLink.name, masterLink.linkType) + defer deleteLink(t, masterLink.name) + + if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil { + t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err) + } + + readLink(t, tl.name) +} + +func TestNetworkLinkAddMacVlan(t *testing.T) { + if testing.Short() { + return + } + + tl := struct { + name string + mode string + }{ + name: "tstVlan", + mode: "private", + } + masterLink := testLink{"tstEth", "dummy"} + + addLink(t, masterLink.name, masterLink.linkType) + defer deleteLink(t, masterLink.name) + + if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil { + t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err) + } + + readLink(t, tl.name) +} + +func TestNetworkLinkAddMacVtap(t *testing.T) { + if testing.Short() { + return + } + + tl := struct { + name string + mode string + }{ + name: "tstVtap", + mode: "private", + } + masterLink := testLink{"tstEth", "dummy"} + + addLink(t, masterLink.name, masterLink.linkType) + defer deleteLink(t, masterLink.name) + + if err := NetworkLinkAddMacVtap(masterLink.name, tl.name, tl.mode); err != nil { + t.Fatalf("Unable to create %#v MAC VTAP interface: %s", tl, err) + } + + readLink(t, tl.name) +} + +func TestAddDelNetworkIp(t *testing.T) { + if testing.Short() { + return + } + + ifaceName := "lo" + ip := net.ParseIP("127.0.1.1") + mask := net.IPv4Mask(255, 255, 255, 255) + ipNet := &net.IPNet{IP: ip, Mask: mask} + + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + t.Skip("No 'lo' interface; skipping tests") + } + + if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil { + t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err) + } + + if !ipAssigned(iface, ip) { + t.Fatalf("Could not locate address '%s' in lo address list.", ip.String()) + } + + if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil { + t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err) + } + + if ipAssigned(iface, ip) { + t.Fatalf("Located address '%s' in lo address list after removal.", ip.String()) + } +} + +func TestAddRouteSourceSelection(t *testing.T) { + tstIp := "127.1.1.1" + tl := testLink{name: "tstEth", linkType: "dummy"} + + addLink(t, tl.name, tl.linkType) + defer deleteLink(t, tl.name) + + ip := net.ParseIP(tstIp) + mask := net.IPv4Mask(255, 255, 255, 255) + ipNet := &net.IPNet{IP: ip, Mask: mask} + + iface, err := net.InterfaceByName(tl.name) + if err != nil { + t.Fatalf("Lost created link %#v", tl) + } + + if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil { + t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err) + } + + upLink(t, tl.name) + defer downLink(t, tl.name) + + if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil { + t.Fatalf("Failed to add route with source address") + } +} + +func TestCreateVethPair(t *testing.T) { + if testing.Short() { + return + } + + var ( + name1 = "veth1" + name2 = "veth2" + ) + + if err := NetworkCreateVethPair(name1, name2, 0); err != nil { + t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err) + } + defer NetworkLinkDel(name1) + + readLink(t, name1) + readLink(t, name2) +} + +// +// netlink package tests which do not use RTNETLINK +// +func TestCreateBridgeWithMac(t *testing.T) { + if testing.Short() { + return + } + + name := "testbridge" + + if err := CreateBridge(name, true); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err != nil { + t.Fatal(err) + } + + // cleanup and tests + + if err := DeleteBridge(name); err != nil { + t.Fatal(err) + } + + if _, err := net.InterfaceByName(name); err == nil { + t.Fatalf("expected error getting interface because %s bridge was deleted", name) + } +} + +func TestSetMacAddress(t *testing.T) { + if testing.Short() { + return + } + + name := "testmac" + mac := randMacAddr() + + if err := NetworkLinkAdd(name, "bridge"); err != nil { + t.Fatal(err) + } + defer NetworkLinkDel(name) + + if err := SetMacAddress(name, mac); err != nil { + t.Fatal(err) + } + + iface, err := net.InterfaceByName(name) + if err != nil { + t.Fatal(err) + } + + if iface.HardwareAddr.String() != mac { + t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac) + } +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go new file mode 100644 index 0000000000..4b11bf8ba5 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_unsupported.go @@ -0,0 +1,88 @@ +// +build !linux + +package netlink + +import ( + "errors" + "net" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func NetworkGetRoutes() ([]Route, error) { + return nil, ErrNotImplemented +} + +func NetworkLinkAdd(name string, linkType string) error { + return ErrNotImplemented +} + +func NetworkLinkDel(name string) error { + return ErrNotImplemented +} + +func NetworkLinkUp(iface *net.Interface) error { + return ErrNotImplemented +} + +func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return ErrNotImplemented +} + +func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { + return ErrNotImplemented +} + +func AddRoute(destination, source, gateway, device string) error { + return ErrNotImplemented +} + +func AddDefaultGw(ip, device string) error { + return ErrNotImplemented +} + +func NetworkSetMTU(iface *net.Interface, mtu int) error { + return ErrNotImplemented +} + +func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error { + return ErrNotImplemented +} + +func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error { + return ErrNotImplemented +} + +func NetworkChangeName(iface *net.Interface, newName string) error { + return ErrNotImplemented +} + +func NetworkSetNsFd(iface *net.Interface, fd int) error { + return ErrNotImplemented +} + +func NetworkSetNsPid(iface *net.Interface, nspid int) error { + return ErrNotImplemented +} + +func NetworkSetMaster(iface, master *net.Interface) error { + return ErrNotImplemented +} + +func NetworkLinkDown(iface *net.Interface) error { + return ErrNotImplemented +} + +func CreateBridge(name string, setMacAddr bool) error { + return ErrNotImplemented +} + +func DeleteBridge(name string) error { + return ErrNotImplemented +} + +func AddToBridge(iface, master *net.Interface) error { + return ErrNotImplemented +} From 885056b2439a1a978be2b7df02c6c04c82694d94 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Thu, 18 Jun 2015 15:59:49 -0700 Subject: [PATCH 2/7] Use ioctls to create bridge The netlink way of creating bridge has problems in older kernels like the one used on RHEL 6 (which is a supported one). So trying to use ioctl method to create bridge so that it works on any version. Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/bridge/setup_device.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/libnetwork/drivers/bridge/setup_device.go b/libnetwork/drivers/bridge/setup_device.go index 1234f0bf42..66d55897f9 100644 --- a/libnetwork/drivers/bridge/setup_device.go +++ b/libnetwork/drivers/bridge/setup_device.go @@ -1,15 +1,15 @@ package bridge import ( - log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/types" + bri "github.com/docker/libcontainer/netlink" "github.com/vishvananda/netlink" ) // SetupDevice create a new bridge interface/ func setupDevice(config *networkConfiguration, i *bridgeInterface) error { + var setMac bool + // We only attempt to create the bridge when the requested device name is // the default one. if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge { @@ -27,15 +27,10 @@ func setupDevice(config *networkConfiguration, i *bridgeInterface) error { // was not supported before that. kv, err := kernel.GetKernelVersion() if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) { - i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC() - log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr) + setMac = true } - // Call out to netlink to create the device. - if err = netlink.LinkAdd(i.Link); err != nil { - return types.InternalErrorf("Failed to program bridge link: %s", err.Error()) - } - return nil + return bri.CreateBridge(config.BridgeName, setMac) } // SetupDeviceUp ups the given bridge interface. From 15759edb382a9305eff37e4df028b3c4cc782dec Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Mon, 29 Jun 2015 23:10:30 -0700 Subject: [PATCH 3/7] Fix networking issues in RHEL/Centos 6.6 Some parts of the bridge driver code needs to use a different kernel api or use the already existing apis in slightly different ways to make the bridge driver work in RHEL/Centos 6.6. This PR provides those fixes. Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/bridge/bridge.go | 42 +++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 9df5323c2c..1523a72889 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -2,12 +2,14 @@ package bridge import ( "errors" + "fmt" "net" "os/exec" "strconv" "sync" "github.com/Sirupsen/logrus" + bri "github.com/docker/libcontainer/netlink" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/ipallocator" "github.com/docker/libnetwork/iptables" @@ -754,6 +756,20 @@ func (d *driver) DeleteNetwork(nid types.UUID) error { return err } +func addToBridge(ifaceName, bridgeName string) error { + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + return fmt.Errorf("could not find interface %s: %v", ifaceName, err) + } + + master, err := net.InterfaceByName(bridgeName) + if err != nil { + return fmt.Errorf("could not find bridge %s: %v", bridgeName, err) + } + + return bri.AddToBridge(iface, master) +} + func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { var ( ipv6Addr *net.IPNet @@ -821,27 +837,27 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn }() // Generate a name for what will be the host side pipe interface - name1, err := netutils.GenerateIfaceName(vethPrefix, vethLen) + hostIfName, err := netutils.GenerateIfaceName(vethPrefix, vethLen) if err != nil { return err } // Generate a name for what will be the sandbox side pipe interface - name2, err := netutils.GenerateIfaceName(vethPrefix, vethLen) + containerIfName, err := netutils.GenerateIfaceName(vethPrefix, vethLen) if err != nil { return err } // Generate and add the interface pipe host <-> sandbox veth := &netlink.Veth{ - LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0}, - PeerName: name2} + LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0}, + PeerName: containerIfName} if err = netlink.LinkAdd(veth); err != nil { return err } // Get the host side pipe interface handler - host, err := netlink.LinkByName(name1) + host, err := netlink.LinkByName(hostIfName) if err != nil { return err } @@ -852,7 +868,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn }() // Get the sandbox side pipe interface handler - sbox, err := netlink.LinkByName(name2) + sbox, err := netlink.LinkByName(containerIfName) if err != nil { return err } @@ -879,9 +895,8 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } // Attach host side pipe interface into the bridge - if err = netlink.LinkSetMaster(host, - &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil { - return err + if err = addToBridge(hostIfName, config.BridgeName); err != nil { + return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err) } if !config.EnableUserlandProxy { @@ -898,11 +913,16 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + // Down the interface before configuring mac address. + if err := netlink.LinkSetDown(sbox); err != nil { + return fmt.Errorf("could not set link down for container interface %s: %v", containerIfName, err) + } + // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. mac := electMacAddress(epConfig, ip4) err = netlink.LinkSetHardwareAddr(sbox, mac) if err != nil { - return err + return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) } endpoint.macAddress = mac @@ -934,7 +954,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } // Create the sandbox side pipe interface - endpoint.srcName = name2 + endpoint.srcName = containerIfName endpoint.addr = ipv4Addr if config.EnableIPv6 { From 10444cb44877a68b8d24b0386925c98785db437b Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Tue, 30 Jun 2015 11:08:16 -0700 Subject: [PATCH 4/7] Manually bring up the host side veth interface In preparation for the new update of vishvananda/netlink package we need to bringup the host veth interface manually. Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/bridge/bridge.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 1523a72889..a0960986a0 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -926,6 +926,11 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } endpoint.macAddress = mac + // Up the host interface after finishing all netlink configuration + if err := netlink.LinkSetUp(host); err != nil { + return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err) + } + // v6 address for the sandbox side pipe interface ipv6Addr = &net.IPNet{} if config.EnableIPv6 { From a22d29b7cf840cbeda81a41e5064b0741d4c3008 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Tue, 30 Jun 2015 11:10:08 -0700 Subject: [PATCH 5/7] Update vishvananda/netlink package PR to update to vishvananda/netlink package Signed-off-by: Jana Radhakrishnan --- libnetwork/Godeps/Godeps.json | 2 +- .../github.com/vishvananda/netlink/README.md | 8 +- .../github.com/vishvananda/netlink/addr.go | 4 +- .../vishvananda/netlink/addr_linux.go | 18 ++- .../github.com/vishvananda/netlink/link.go | 31 ++++- .../vishvananda/netlink/link_linux.go | 88 ++++++++++--- .../vishvananda/netlink/link_test.go | 119 +++++++++++++++++- .../vishvananda/netlink/neigh_linux.go | 2 +- .../vishvananda/netlink/nl/link_linux.go | 15 +++ .../vishvananda/netlink/nl/nl_linux.go | 22 ++-- .../vishvananda/netlink/nl/xfrm_linux.go | 7 +- .../vishvananda/netlink/protinfo.go | 2 +- .../vishvananda/netlink/route_linux.go | 4 +- .../vishvananda/netlink/xfrm_policy_linux.go | 2 +- .../vishvananda/netlink/xfrm_state_linux.go | 2 +- 15 files changed, 276 insertions(+), 50 deletions(-) diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index e0506650ff..c2cb16e60f 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -132,7 +132,7 @@ }, { "ImportPath": "github.com/vishvananda/netlink", - "Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c" + "Rev": "20397a138846e4d6590e01783ed023ed7e1c38a6" }, { "ImportPath": "github.com/vishvananda/netns", diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md index 555f886523..8cd50a93b6 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/README.md @@ -43,13 +43,19 @@ import ( ) func main() { - mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}} + la := netlink.NewLinkAttrs() + la.Name = "foo" + mybridge := &netlink.Bridge{la}} _ := netlink.LinkAdd(mybridge) eth1, _ := netlink.LinkByName("eth1") netlink.LinkSetMaster(eth1, mybridge) } ``` +Note `NewLinkAttrs` constructor, it sets default values in structure. For now +it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're +using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to +`0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`. Add a new ip address to loopback: diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go index 5c12f4e998..9bbaf508e0 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go @@ -14,8 +14,8 @@ type Addr struct { } // String returns $ip/$netmask $label -func (addr Addr) String() string { - return fmt.Sprintf("%s %s", addr.IPNet, addr.Label) +func (a Addr) String() string { + return fmt.Sprintf("%s %s", a.IPNet, a.Label) } // ParseAddr parses the string representation of an address in the 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 dd26f4aec7..19aac0fb97 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 @@ -81,7 +81,7 @@ func AddrList(link Link, family int) ([]Addr, error) { index = base.Index } - res := make([]Addr, 0) + var res []Addr for _, m := range msgs { msg := nl.DeserializeIfAddrmsg(m) @@ -95,11 +95,17 @@ func AddrList(link Link, family int) ([]Addr, error) { return nil, err } + var local, dst *net.IPNet var addr Addr for _, attr := range attrs { switch attr.Attr.Type { case syscall.IFA_ADDRESS: - addr.IPNet = &net.IPNet{ + dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), + } + case syscall.IFA_LOCAL: + local = &net.IPNet{ IP: attr.Value, Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), } @@ -107,6 +113,14 @@ func AddrList(link Link, family int) ([]Addr, error) { addr.Label = string(attr.Value[:len(attr.Value)-1]) } } + + // IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS + if local != nil { + addr.IPNet = local + } else { + addr.IPNet = dst + } + res = append(res, addr) } diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go index 276c2f80b0..af51e3f0f4 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go @@ -10,16 +10,29 @@ type Link interface { Type() string } +type ( + NsPid int + NsFd int +) + // LinkAttrs represents data shared by most link types type LinkAttrs struct { Index int MTU int - TxQLen uint32 // Transmit Queue Length + TxQLen int // Transmit Queue Length Name string HardwareAddr net.HardwareAddr Flags net.Flags - ParentIndex int // index of the parent link device - MasterIndex int // must be the index of a bridge + ParentIndex int // index of the parent link device + MasterIndex int // must be the index of a bridge + Namespace interface{} // nil | NsPid | NsFd +} + +// NewLinkAttrs returns LinkAttrs structure filled with default values +func NewLinkAttrs() LinkAttrs { + return LinkAttrs{ + TxQLen: -1, + } } // Device links cannot be created via netlink. These links @@ -76,9 +89,21 @@ func (vlan *Vlan) Type() string { return "vlan" } +type MacvlanMode uint16 + +const ( + MACVLAN_MODE_DEFAULT MacvlanMode = iota + MACVLAN_MODE_PRIVATE + MACVLAN_MODE_VEPA + MACVLAN_MODE_BRIDGE + MACVLAN_MODE_PASSTHRU + MACVLAN_MODE_SOURCE +) + // Macvlan links have ParentIndex set in their Attrs() type Macvlan struct { LinkAttrs + Mode MacvlanMode } func (macvlan *Macvlan) Attrs() *LinkAttrs { 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 aedea165d9..2b800d148a 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 @@ -13,6 +13,15 @@ import ( var native = nl.NativeEndian() var lookupByDump = false +var macvlanModes = [...]uint32{ + 0, + nl.MACVLAN_MODE_PRIVATE, + nl.MACVLAN_MODE_VEPA, + nl.MACVLAN_MODE_BRIDGE, + nl.MACVLAN_MODE_PASSTHRU, + nl.MACVLAN_MODE_SOURCE, +} + func ensureIndex(link *LinkAttrs) { if link != nil && link.Index == 0 { newlink, _ := LinkByName(link.Name) @@ -39,7 +48,7 @@ func LinkSetUp(link Link) error { return err } -// LinkSetUp disables link device. +// LinkSetDown disables link device. // Equivalent to: `ip link set $link down` func LinkSetDown(link Link) error { base := link.Attrs() @@ -67,7 +76,7 @@ func LinkSetMTU(link Link, mtu int) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_MTU req.AddData(msg) b := make([]byte, 4) @@ -91,7 +100,7 @@ func LinkSetName(link Link, name string) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_IFNAME req.AddData(msg) data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name)) @@ -112,7 +121,7 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_ADDRESS req.AddData(msg) data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr)) @@ -145,7 +154,7 @@ func LinkSetMasterByIndex(link Link, masterIndex int) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_MASTER req.AddData(msg) b := make([]byte, 4) @@ -170,7 +179,7 @@ func LinkSetNsPid(link Link, nspid int) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_NET_NS_PID req.AddData(msg) b := make([]byte, 4) @@ -183,7 +192,7 @@ func LinkSetNsPid(link Link, nspid int) error { return err } -// LinkSetNsPid puts the device into a new network namespace. The +// LinkSetNsFd puts the device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `ip link set $link netns $ns` func LinkSetNsFd(link Link, fd int) error { @@ -195,7 +204,7 @@ func LinkSetNsFd(link Link, fd int) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = nl.IFLA_NET_NS_FD req.AddData(msg) b := make([]byte, 4) @@ -312,11 +321,28 @@ func LinkAdd(link Link) error { req.AddData(mtu) } + if base.TxQLen >= 0 { + qlen := nl.NewRtAttr(syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) + req.AddData(qlen) + } + + if base.Namespace != nil { + var attr *nl.RtAttr + switch base.Namespace.(type) { + case NsPid: + val := nl.Uint32Attr(uint32(base.Namespace.(NsPid))) + attr = nl.NewRtAttr(syscall.IFLA_NET_NS_PID, val) + case NsFd: + val := nl.Uint32Attr(uint32(base.Namespace.(NsFd))) + attr = nl.NewRtAttr(nl.IFLA_NET_NS_FD, val) + } + + req.AddData(attr) + } + linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) - nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen)) - if vlan, ok := link.(*Vlan); ok { b := make([]byte, 2) native.PutUint16(b, uint16(vlan.VlanId)) @@ -327,15 +353,23 @@ func LinkAdd(link Link) error { peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil) nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC) nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName)) - nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen)) + if base.TxQLen >= 0 { + nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen))) + } if base.MTU > 0 { nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU))) } + } else if vxlan, ok := link.(*Vxlan); ok { addVxlanAttrs(vxlan, linkInfo) } else if ipv, ok := link.(*IPVlan); ok { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode))) + } else if macv, ok := link.(*Macvlan); ok { + if macv.Mode != MACVLAN_MODE_DEFAULT { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode])) + } } req.AddData(linkInfo) @@ -483,6 +517,8 @@ func linkDeserialize(m []byte) (Link, error) { link = &Vxlan{} case "ipvlan": link = &IPVlan{} + case "macvlan": + link = &Macvlan{} default: link = &Generic{LinkType: linkType} } @@ -498,6 +534,8 @@ func linkDeserialize(m []byte) (Link, error) { parseVxlanData(link, data) case "ipvlan": parseIPVlanData(link, data) + case "macvlan": + parseMacvlanData(link, data) } } } @@ -520,7 +558,7 @@ func linkDeserialize(m []byte) (Link, error) { case syscall.IFLA_MASTER: base.MasterIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_TXQLEN: - base.TxQLen = native.Uint32(attr.Value[0:4]) + base.TxQLen = int(native.Uint32(attr.Value[0:4])) } } // Links that don't have IFLA_INFO_KIND are hardware devices @@ -547,8 +585,7 @@ func LinkList() ([]Link, error) { return nil, err } - res := make([]Link, 0) - + var res []Link for _, m := range msgs { link, err := linkDeserialize(m) if err != nil { @@ -593,7 +630,7 @@ func setProtinfoAttr(link Link, mode bool, attr int) error { msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST msg.Index = int32(base.Index) - msg.Change = nl.DEFAULT_CHANGE + msg.Change = syscall.IFLA_PROTINFO | syscall.NLA_F_NESTED req.AddData(msg) br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil) @@ -674,6 +711,27 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { } } +func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) { + macv := link.(*Macvlan) + for _, datum := range data { + if datum.Attr.Type == nl.IFLA_MACVLAN_MODE { + switch native.Uint32(datum.Value[0:4]) { + case nl.MACVLAN_MODE_PRIVATE: + macv.Mode = MACVLAN_MODE_PRIVATE + case nl.MACVLAN_MODE_VEPA: + macv.Mode = MACVLAN_MODE_VEPA + case nl.MACVLAN_MODE_BRIDGE: + macv.Mode = MACVLAN_MODE_BRIDGE + case nl.MACVLAN_MODE_PASSTHRU: + macv.Mode = MACVLAN_MODE_PASSTHRU + case nl.MACVLAN_MODE_SOURCE: + macv.Mode = MACVLAN_MODE_SOURCE + } + return + } + } +} + // copied from pkg/net_linux.go func linkFlags(rawFlags uint32) net.Flags { var f net.Flags diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go index 05b8e95586..2edfcbe377 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go @@ -8,7 +8,10 @@ import ( "github.com/vishvananda/netns" ) -const testTxQLen uint32 = 100 +const ( + testTxQLen int = 100 + defaultTxQLen int = 1000 +) func testLinkAddDel(t *testing.T, link Link) { links, err := LinkList() @@ -50,9 +53,9 @@ func testLinkAddDel(t *testing.T, link Link) { } } - if veth, ok := link.(*Veth); ok { - if veth.TxQLen != testTxQLen { - t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen) + if veth, ok := result.(*Veth); ok { + if rBase.TxQLen != base.TxQLen { + t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen) } if rBase.MTU != base.MTU { t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU) @@ -91,6 +94,16 @@ func testLinkAddDel(t *testing.T, link Link) { } } + if macv, ok := link.(*Macvlan); ok { + other, ok := result.(*Macvlan) + if !ok { + t.Fatal("Result of create is not a macvlan") + } + if macv.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode) + } + } + if err = LinkDel(link); err != nil { t.Fatal(err) } @@ -199,7 +212,10 @@ func TestLinkAddDelMacvlan(t *testing.T) { t.Fatal(err) } - testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}}) + testLinkAddDel(t, &Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_PRIVATE, + }) if err := LinkDel(parent); err != nil { t.Fatal(err) @@ -213,6 +229,99 @@ func TestLinkAddDelVeth(t *testing.T) { testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"}) } +func TestLinkAddVethWithDefaultTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + + veth := &Veth{LinkAttrs: la, PeerName: "bar"} + if err := LinkAdd(veth); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if veth, ok := link.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != defaultTxQLen { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen) + } + } + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if veth, ok := peer.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != defaultTxQLen { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen) + } + } +} + +func TestLinkAddVethWithZeroTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + la.TxQLen = 0 + + veth := &Veth{LinkAttrs: la, PeerName: "bar"} + if err := LinkAdd(veth); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if veth, ok := link.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != 0 { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0) + } + } + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if veth, ok := peer.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != 0 { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0) + } + } +} + +func TestLinkAddDummyWithTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + la.TxQLen = 1500 + + dummy := &Dummy{LinkAttrs: la} + if err := LinkAdd(dummy); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if dummy, ok := link.(*Dummy); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if dummy.TxQLen != 1500 { + t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500) + } + } +} + func TestLinkAddDelBridgeMaster(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go index 1fdaa3a37e..620a0ee708 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go @@ -141,7 +141,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) { return nil, err } - res := make([]Neigh, 0) + var res []Neigh for _, m := range msgs { ndm := deserializeNdmsg(m) if linkIndex != 0 && int(ndm.Index) != linkIndex { diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go index ab0dede65d..64ef5fdbd2 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go @@ -79,3 +79,18 @@ const ( // not defined in syscall IFLA_NET_NS_FD = 28 ) + +const ( + IFLA_MACVLAN_UNSPEC = iota + IFLA_MACVLAN_MODE + IFLA_MACVLAN_FLAGS + IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS +) + +const ( + MACVLAN_MODE_PRIVATE = 1 + MACVLAN_MODE_VEPA = 2 + MACVLAN_MODE_BRIDGE = 4 + MACVLAN_MODE_PASSTHRU = 8 + MACVLAN_MODE_SOURCE = 16 +) 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 72f2813773..a554adbf86 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 @@ -172,16 +172,16 @@ type NetlinkRequest struct { } // Serialize the Netlink Request into a byte array -func (msg *NetlinkRequest) Serialize() []byte { +func (req *NetlinkRequest) Serialize() []byte { length := syscall.SizeofNlMsghdr - dataBytes := make([][]byte, len(msg.Data)) - for i, data := range msg.Data { + dataBytes := make([][]byte, len(req.Data)) + for i, data := range req.Data { dataBytes[i] = data.Serialize() length = length + len(dataBytes[i]) } - msg.Len = uint32(length) + req.Len = uint32(length) b := make([]byte, length) - hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:] + hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:] next := syscall.SizeofNlMsghdr copy(b[0:next], hdr) for _, data := range dataBytes { @@ -193,9 +193,9 @@ func (msg *NetlinkRequest) Serialize() []byte { return b } -func (msg *NetlinkRequest) AddData(data NetlinkRequestData) { +func (req *NetlinkRequest) AddData(data NetlinkRequestData) { if data != nil { - msg.Data = append(msg.Data, data) + req.Data = append(req.Data, data) } } @@ -218,11 +218,11 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro return nil, err } - res := make([][]byte, 0) + var res [][]byte done: for { - msgs, err := s.Recieve() + msgs, err := s.Receive() if err != nil { return nil, err } @@ -294,7 +294,7 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE) // and subscribe it to multicast groups passed in variable argument list. -// Returns the netlink socket on whic hReceive() method can be called +// Returns the netlink socket on which Receive() method can be called // to retrieve the messages from the kernel. func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) { fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol) @@ -329,7 +329,7 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error { return nil } -func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) { +func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) { rb := make([]byte, syscall.Getpagesize()) nr, _, err := syscall.Recvfrom(s.fd, rb, 0) if err != nil { diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go index d953130870..d24637d278 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux.go @@ -104,9 +104,8 @@ func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet { ip := x.ToIP() if GetIPFamily(ip) == FAMILY_V4 { return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)} - } else { - return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)} } + return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)} } func (x *XfrmAddress) FromIP(ip net.IP) { @@ -125,8 +124,8 @@ func DeserializeXfrmAddress(b []byte) *XfrmAddress { return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0])) } -func (msg *XfrmAddress) Serialize() []byte { - return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:] +func (x *XfrmAddress) Serialize() []byte { + return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:] } // struct xfrm_selector { diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go index 79396da7ca..f39ab8f4e8 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo.go @@ -16,7 +16,7 @@ type Protinfo struct { // String returns a list of enabled flags func (prot *Protinfo) String() string { - boolStrings := make([]string, 0) + var boolStrings []string if prot.Hairpin { boolStrings = append(boolStrings, "Hairpin") } 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 43872aa417..88fa6db0fc 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 @@ -119,7 +119,7 @@ func RouteList(link Link, family int) ([]Route, error) { } native := nl.NativeEndian() - res := make([]Route, 0) + var res []Route for _, m := range msgs { msg := nl.DeserializeRtMsg(m) @@ -193,7 +193,7 @@ func RouteGet(destination net.IP) ([]Route, error) { } native := nl.NativeEndian() - res := make([]Route, 0) + var res []Route for _, m := range msgs { msg := nl.DeserializeRtMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) diff --git a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go index 6fe1b63757..2daf6dc8b3 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_linux.go @@ -84,7 +84,7 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) { return nil, err } - res := make([]XfrmPolicy, 0) + var res []XfrmPolicy for _, m := range msgs { msg := nl.DeserializeXfrmUserpolicyInfo(m) 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 0f1fbd0e06..5f44ec8525 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 @@ -118,7 +118,7 @@ func XfrmStateList(family int) ([]XfrmState, error) { return nil, err } - res := make([]XfrmState, 0) + var res []XfrmState for _, m := range msgs { msg := nl.DeserializeXfrmUsersaInfo(m) From 70429527b0cfde1b84191bc2be7d595a0468e5c2 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Tue, 30 Jun 2015 13:41:28 -0700 Subject: [PATCH 6/7] Honor driver side resolv.conf file For the moment in 1.7.1 since we provide a resolv.conf set api to the driver honor that so that for host driver we can use the the host's /etc/resolv.conf file as is rather than putting the contents through a filtering logic. It should be noted that the driver side capability to set the resolv.conf file is most likely going to go away in the future but this should be fine for 1.7.1 Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/host/host.go | 6 ++- libnetwork/endpoint.go | 18 +++++++++ libnetwork/libnetwork_test.go | 68 +++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/libnetwork/drivers/host/host.go b/libnetwork/drivers/host/host.go index 072cc890ab..5dbc4ef2fb 100644 --- a/libnetwork/drivers/host/host.go +++ b/libnetwork/drivers/host/host.go @@ -57,7 +57,11 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, // Join method is invoked when a Sandbox is attached to an endpoint. func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - return (jinfo.SetHostsPath("/etc/hosts")) + if err := jinfo.SetHostsPath("/etc/hosts"); err != nil { + return err + } + + return jinfo.SetResolvConfPath("/etc/resolv.conf") } // Leave method is invoked when a Sandbox detaches from an endpoint. diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 8539ef5ae8..6d757001b1 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -811,9 +811,19 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error { return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath) } +func copyFile(src, dst string) error { + sBytes, err := ioutil.ReadFile(src) + if err != nil { + return err + } + + return ioutil.WriteFile(dst, sBytes, 0644) +} + func (ep *endpoint) setupDNS() error { ep.Lock() container := ep.container + joinInfo := ep.joinInfo ep.Unlock() if container == nil { @@ -830,6 +840,14 @@ func (ep *endpoint) setupDNS() error { return err } + if joinInfo.resolvConfPath != "" { + if err := copyFile(joinInfo.resolvConfPath, container.config.resolvConfPath); err != nil { + return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", joinInfo.resolvConfPath, container.config.resolvConfPath, err) + } + + return nil + } + resolvConf, err := resolvconf.Get() if err != nil { return err diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index b890f07613..a13933187d 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -1564,6 +1564,74 @@ func TestEnableIPv6(t *testing.T) { } } +func TestResolvConfHost(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888") + + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + n, err := controller.NetworkByName("testhost") + if err != nil { + t.Fatal(err) + } + + ep1, err := n.CreateEndpoint("ep1", nil) + if err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + err = ep1.Join(containerID, + libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(containerID) + if err != nil { + t.Fatal(err) + } + }() + + finfo, err := os.Stat(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + fmode := (os.FileMode)(0644) + if finfo.Mode() != fmode { + t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String()) + } + + content, err := ioutil.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, tmpResolvConf) { + t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content)) + } +} + func TestResolvConf(t *testing.T) { if !netutils.IsRunningInContainer() { defer netutils.SetupTestNetNS(t)() From 00456020f5c39e5f9ce77b5ba5eabddcc0981b3d Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Thu, 2 Jul 2015 10:39:43 -0700 Subject: [PATCH 7/7] Adjust overlay driver for netlink api change Signed-off-by: Jana Radhakrishnan --- libnetwork/drivers/overlay/ov_network.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnetwork/drivers/overlay/ov_network.go b/libnetwork/drivers/overlay/ov_network.go index 4133f2f3c6..be33e49b7c 100644 --- a/libnetwork/drivers/overlay/ov_network.go +++ b/libnetwork/drivers/overlay/ov_network.go @@ -155,7 +155,7 @@ func (n *network) initSandbox() error { func (n *network) watchMiss(nlSock *nl.NetlinkSocket) { for { - msgs, err := nlSock.Recieve() + msgs, err := nlSock.Receive() if err != nil { logrus.Errorf("Failed to receive from netlink: %v ", err) continue