package tenus import ( "fmt" "net" "os" "syscall" "github.com/docker/libcontainer/netlink" "github.com/docker/libcontainer/system" ) // LinkOptions allows you to specify network link options. type LinkOptions struct { // MAC address MacAddr string // Maximum Transmission Unit MTU int // Link network flags i.e. FlagUp, FlagLoopback, FlagMulticast Flags net.Flags // Network namespace in which the network link should be created Ns int } // Linker is a generic Linux network link type Linker interface { // NetInterface returns the link's logical network interface NetInterface() *net.Interface // DeleteLink deletes the link from Linux host DeleteLink() error // SetLinkMTU sets the link's MTU. SetLinkMTU(int) error // SetLinkMacAddress sets the link's MAC address. SetLinkMacAddress(string) error // SetLinkUp brings the link up SetLinkUp() error // SetLinkDown brings the link down SetLinkDown() error // SetLinkIp configures the link's IP address SetLinkIp(net.IP, *net.IPNet) error // UnsetLinkIp remove and IP address from the link UnsetLinkIp(net.IP, *net.IPNet) error // SetLinkDefaultGw configures the link's default gateway SetLinkDefaultGw(*net.IP) error // SetLinkNetNsPid moves the link to network namespace specified by PID SetLinkNetNsPid(int) error // SetLinkNetInNs configures network settings of the link in network namespace SetLinkNetInNs(int, net.IP, *net.IPNet, *net.IP) error } // Link has a logical network interface type Link struct { ifc *net.Interface } // NewLink creates new network link on Linux host. // // It is equivalent of running: ip link add name ${ifcName} type dummy // NewLink returns Linker which is initialized to a pointer of type Link if the // link was created successfully on the Linux host. // It returns error if the network link could not be created on Linux host. func NewLink(ifcName string) (Linker, error) { if ok, err := NetInterfaceNameValid(ifcName); !ok { return nil, err } if _, err := net.InterfaceByName(ifcName); err == nil { return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName) } if err := netlink.NetworkLinkAdd(ifcName, "dummy"); err != nil { return nil, fmt.Errorf("Could not create new link %s: %s", ifcName, err) } newIfc, err := net.InterfaceByName(ifcName) if err != nil { return nil, fmt.Errorf("Could not find the new interface: %s", err) } return &Link{ ifc: newIfc, }, nil } // NewLinkFrom creates new tenus link on Linux host from an existing interface of given name func NewLinkFrom(ifcName string) (Linker, error) { if ok, err := NetInterfaceNameValid(ifcName); !ok { return nil, err } newIfc, err := net.InterfaceByName(ifcName) if err != nil { return nil, fmt.Errorf("Could not find the new interface: %s", err) } return &Link{ ifc: newIfc, }, nil } // NewLinkWithOptions creates new network link on Linux host and sets some of its network // parameters passed in as LinkOptions // // Calling NewLinkWithOptions is equivalent of running following commands one after another if // particular option is passed in as a parameter: // ip link add name ${ifcName} type dummy // ip link set dev ${ifcName} address ${MAC address} // ip link set dev ${ifcName} mtu ${MTU value} // ip link set dev ${ifcName} up // NewLinkWithOptions returns Linker which is initialized to a pointer of type Link if the network // link with given LinkOptions was created successfully on the Linux host. // It attempts to delete the link if any of the LinkOptions are incorrect or if setting the options // failed and returns error. func NewLinkWithOptions(ifcName string, opts LinkOptions) (Linker, error) { if ok, err := NetInterfaceNameValid(ifcName); !ok { return nil, err } if _, err := net.InterfaceByName(ifcName); err == nil { return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName) } if err := netlink.NetworkLinkAdd(ifcName, "dummy"); err != nil { return nil, fmt.Errorf("Could not create new link %s: %s", ifcName, err) } newIfc, err := net.InterfaceByName(ifcName) if err != nil { return nil, fmt.Errorf("Could not find the new interface: %s", err) } if (opts != LinkOptions{}) { errOpts := setLinkOptions(newIfc, opts) if errOpts != nil { if errDel := DeleteLink(newIfc.Name); err != nil { return nil, fmt.Errorf("Incorrect options specified: %s. Attempt to delete the link failed: %s", errOpts, errDel) } return nil, fmt.Errorf("Could not set link options: %s", errOpts) } } return &Link{ ifc: newIfc, }, nil } // DeleteLink deletes netowrk link from Linux Host // It is equivalent of running: ip link delete dev ${name} func DeleteLink(name string) error { return netlink.NetworkLinkDel(name) } // NetInterface returns link's logical network interface. func (l *Link) NetInterface() *net.Interface { return l.ifc } // DeleteLink deletes link interface on Linux host. // It is equivalent of running: ip link delete dev ${interface name} func (l *Link) DeleteLink() error { return netlink.NetworkLinkDel(l.NetInterface().Name) } // SetLinkMTU sets link's MTU. // It is equivalent of running: ip link set dev ${interface name} mtu ${MTU value} func (l *Link) SetLinkMTU(mtu int) error { return netlink.NetworkSetMTU(l.NetInterface(), mtu) } // SetLinkMacAddress sets link's MAC address. // It is equivalent of running: ip link set dev ${interface name} address ${address} func (l *Link) SetLinkMacAddress(macaddr string) error { return netlink.NetworkSetMacAddress(l.NetInterface(), macaddr) } // SetLinkUp brings the link up. // It is equivalent of running: ip link set dev ${interface name} up func (l *Link) SetLinkUp() error { return netlink.NetworkLinkUp(l.NetInterface()) } // SetLinkDown brings the link down. // It is equivalent of running: ip link set dev ${interface name} down func (l *Link) SetLinkDown() error { return netlink.NetworkLinkDown(l.NetInterface()) } // SetLinkIp configures the link's IP address. // It is equivalent of running: ip address add ${address}/${mask} dev ${interface name} func (l *Link) SetLinkIp(ip net.IP, network *net.IPNet) error { return netlink.NetworkLinkAddIp(l.NetInterface(), ip, network) } // UnsetLinkIp configures the link's IP address. // It is equivalent of running: ip address del ${address}/${mask} dev ${interface name} func (l *Link) UnsetLinkIp(ip net.IP, network *net.IPNet) error { return netlink.NetworkLinkDelIp(l.NetInterface(), ip, network) } // SetLinkDefaultGw configures the link's default Gateway. // It is equivalent of running: ip route add default via ${ip address} func (l *Link) SetLinkDefaultGw(gw *net.IP) error { return netlink.AddDefaultGw(gw.String(), l.NetInterface().Name) } // SetLinkNetNsPid moves the link to Network namespace specified by PID. func (l *Link) SetLinkNetNsPid(nspid int) error { return netlink.NetworkSetNsPid(l.NetInterface(), nspid) } // SetLinkNetInNs configures network settings of the link in network namespace specified by PID. func (l *Link) SetLinkNetInNs(nspid int, ip net.IP, network *net.IPNet, gw *net.IP) error { origNs, _ := NetNsHandle(os.Getpid()) defer syscall.Close(int(origNs)) defer system.Setns(origNs, syscall.CLONE_NEWNET) if err := SetNetNsToPid(nspid); err != nil { return fmt.Errorf("Setting network namespace failed: %s", err) } if err := netlink.NetworkLinkAddIp(l.NetInterface(), ip, network); err != nil { return fmt.Errorf("Unable to set IP: %s in pid: %d network namespace", ip.String(), nspid) } if err := netlink.NetworkLinkUp(l.ifc); err != nil { return fmt.Errorf("Unable to bring %s interface UP: %d", l.ifc.Name, nspid) } if gw != nil { if err := netlink.AddDefaultGw(gw.String(), l.NetInterface().Name); err != nil { return fmt.Errorf("Unable to set Default gateway: %s in pid: %d network namespace", gw.String(), nspid) } } return nil } // SetLinkNsFd sets the link's Linux namespace to the one specified by filesystem path. func (l *Link) SetLinkNsFd(nspath string) error { fd, err := syscall.Open(nspath, syscall.O_RDONLY, 0) if err != nil { return fmt.Errorf("Could not attach to Network namespace: %s", err) } return netlink.NetworkSetNsFd(l.NetInterface(), fd) } // SetLinkNsToDocker sets the link's Linux namespace to a running Docker one specified by Docker name. func (l *Link) SetLinkNsToDocker(name string, dockerHost string) error { pid, err := DockerPidByName(name, dockerHost) if err != nil { return fmt.Errorf("Failed to find docker %s : %s", name, err) } return l.SetLinkNetNsPid(pid) } // RenameInterfaceByName renames an interface of given name. func RenameInterfaceByName(old string, newName string) error { iface, err := net.InterfaceByName(old) if err != nil { return err } return netlink.NetworkChangeName(iface, newName) } // setLinkOptions validates and sets link's various options passed in as LinkOptions. func setLinkOptions(ifc *net.Interface, opts LinkOptions) error { macaddr, mtu, flags, ns := opts.MacAddr, opts.MTU, opts.Flags, opts.Ns // if MTU is passed in LinkOptions if mtu != 0 { if err := validMtu(mtu); err != nil { return err } if err := netlink.NetworkSetMTU(ifc, mtu); err != nil { return fmt.Errorf("Unable to set MTU: %s", err) } } // if MacAddress is passed in LinkOptions if macaddr != "" { if err := validMacAddress(macaddr); err != nil { return err } if err := netlink.NetworkSetMacAddress(ifc, macaddr); err != nil { return fmt.Errorf("Unable to set MAC Address: %s", err) } } // if ns is passed in LinkOptions if ns != 0 { if err := validNs(ns); err != nil { return err } if err := netlink.NetworkSetNsPid(ifc, ns); err != nil { return fmt.Errorf("Unable to set Network namespace: %s", err) } } // if flags is passed in LinkOptions if flags != 0 { if err := validFlags(flags); err != nil { return err } if ns != 0 && (ns != 1 || ns != os.Getpid()) { if (flags & syscall.IFF_UP) == syscall.IFF_UP { origNs, _ := NetNsHandle(os.Getpid()) defer syscall.Close(int(origNs)) defer system.Setns(origNs, syscall.CLONE_NEWNET) if err := SetNetNsToPid(ns); err != nil { return fmt.Errorf("Switching to %d network namespace failed: %s", ns, err) } if err := netlink.NetworkLinkUp(ifc); err != nil { return fmt.Errorf("Unable to bring %s interface UP: %d", ifc.Name, ns) } } } else { if err := netlink.NetworkLinkUp(ifc); err != nil { return fmt.Errorf("Could not bring up network link %s: %s", ifc.Name, err) } } } return nil }