netlink/link_linux.go
Alexander Morozov 2e9d285a71 Change logic of setting protinfo attrs
Now interface is supposing that you setting protinfo attrs for link one
by one. But you can get all protinfo attrs with one call.

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
2015-02-17 14:16:48 -08:00

697 lines
18 KiB
Go

package netlink
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
)
var native = nl.NativeEndian()
var lookupByDump = false
func ensureIndex(link *LinkAttrs) {
if link != nil && link.Index == 0 {
newlink, _ := LinkByName(link.Name)
if newlink != nil {
link.Index = newlink.Attrs().Index
}
}
}
// LinkSetUp enables the link device.
// Equivalent to: `ip link set $link up`
func LinkSetUp(link Link) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_UP
msg.Flags = syscall.IFF_UP
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetUp disables link device.
// Equivalent to: `ip link set $link down`
func LinkSetDown(link Link) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_UP
msg.Flags = 0 & ^syscall.IFF_UP
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetMTU sets the mtu of the link device.
// Equivalent to: `ip link set $link mtu $mtu`
func LinkSetMTU(link Link, mtu int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
b := make([]byte, 4)
native.PutUint32(b, uint32(mtu))
data := nl.NewRtAttr(syscall.IFLA_MTU, b)
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetName sets the name of the link device.
// Equivalent to: `ip link set $link name $name`
func LinkSetName(link Link, name string) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetHardwareAddr sets the hardware address of the link device.
// Equivalent to: `ip link set $link address $hwaddr`
func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetMaster sets the master of the link device.
// Equivalent to: `ip link set $link master $master`
func LinkSetMaster(link Link, master *Bridge) error {
index := 0
if master != nil {
masterBase := master.Attrs()
ensureIndex(masterBase)
index = masterBase.Index
}
return LinkSetMasterByIndex(link, index)
}
// LinkSetMasterByIndex sets the master of the link device.
// Equivalent to: `ip link set $link master $master`
func LinkSetMasterByIndex(link Link, masterIndex int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
b := make([]byte, 4)
native.PutUint32(b, uint32(masterIndex))
data := nl.NewRtAttr(syscall.IFLA_MASTER, b)
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetNsPid puts the device into a new network namespace. The
// pid must be a pid of a running process.
// Equivalent to: `ip link set $link netns $pid`
func LinkSetNsPid(link Link, nspid int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
b := make([]byte, 4)
native.PutUint32(b, uint32(nspid))
data := nl.NewRtAttr(syscall.IFLA_NET_NS_PID, b)
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
// LinkSetNsPid 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 {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
b := make([]byte, 4)
native.PutUint32(b, uint32(fd))
data := nl.NewRtAttr(nl.IFLA_NET_NS_FD, b)
req.AddData(data)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func boolAttr(val bool) []byte {
var v uint8
if val {
v = 1
}
return nl.Uint8Attr(v)
}
type vxlanPortRange struct {
Lo, Hi uint16
}
func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId)))
if vxlan.VtepDevIndex != 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex)))
}
if vxlan.SrcAddr != nil {
ip := vxlan.SrcAddr.To4()
if ip != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL, []byte(ip))
} else {
ip = vxlan.SrcAddr.To16()
if ip != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LOCAL6, []byte(ip))
}
}
}
if vxlan.Group != nil {
group := vxlan.Group.To4()
if group != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP, []byte(group))
} else {
group = vxlan.Group.To16()
if group != nil {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GROUP6, []byte(group))
}
}
}
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TTL, nl.Uint8Attr(uint8(vxlan.TTL)))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_TOS, nl.Uint8Attr(uint8(vxlan.TOS)))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LEARNING, boolAttr(vxlan.Learning))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PROXY, boolAttr(vxlan.Proxy))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_RSC, boolAttr(vxlan.RSC))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L2MISS, boolAttr(vxlan.L2miss))
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_L3MISS, boolAttr(vxlan.L3miss))
if vxlan.NoAge {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
} else if vxlan.Age > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(uint32(vxlan.Age)))
}
if vxlan.Limit > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LIMIT, nl.Uint32Attr(uint32(vxlan.Limit)))
}
if vxlan.Port > 0 {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT, nl.Uint16Attr(uint16(vxlan.Port)))
}
if vxlan.PortLow > 0 || vxlan.PortHigh > 0 {
pr := vxlanPortRange{uint16(vxlan.PortLow), uint16(vxlan.PortHigh)}
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, &pr)
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_PORT_RANGE, buf.Bytes())
}
}
// LinkAdd adds a new link device. The type and features of the device
// are taken fromt the parameters in the link object.
// Equivalent to: `ip link add $link`
func LinkAdd(link Link) error {
// TODO: set mtu and hardware address
// TODO: support extra data for macvlan
base := link.Attrs()
if base.Name == "" {
return fmt.Errorf("LinkAttrs.Name cannot be empty!")
}
req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg)
if base.ParentIndex != 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(base.ParentIndex))
data := nl.NewRtAttr(syscall.IFLA_LINK, b)
req.AddData(data)
} else if link.Type() == "ipvlan" {
return fmt.Errorf("Can't create ipvlan link without ParentIndex")
}
nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(base.Name))
req.AddData(nameData)
if base.MTU > 0 {
mtu := nl.NewRtAttr(syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
req.AddData(mtu)
}
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))
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_VLAN_ID, b)
} else if veth, ok := link.(*Veth); ok {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
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.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)))
}
req.AddData(linkInfo)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
if err != nil {
return err
}
ensureIndex(base)
// can't set master during create, so set it afterwards
if base.MasterIndex != 0 {
// TODO: verify MasterIndex is actually a bridge?
return LinkSetMasterByIndex(link, base.MasterIndex)
}
return nil
}
// LinkDel deletes link device. Either Index or Name must be set in
// the link object for it to be deleted. The other values are ignored.
// Equivalent to: `ip link del $link`
func LinkDel(link Link) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(base.Index)
req.AddData(msg)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
return err
}
func linkByNameDump(name string) (Link, error) {
links, err := LinkList()
if err != nil {
return nil, err
}
for _, link := range links {
if link.Attrs().Name == name {
return link, nil
}
}
return nil, fmt.Errorf("Link %s not found", name)
}
// LinkByName finds a link by name and returns a pointer to the object.
func LinkByName(name string) (Link, error) {
if lookupByDump {
return linkByNameDump(name)
}
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg)
nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(name))
req.AddData(nameData)
link, err := execGetLink(req)
if err == syscall.EINVAL {
// older kernels don't support looking up via IFLA_IFNAME
// so fall back to dumping all links
lookupByDump = true
return linkByNameDump(name)
}
return link, err
}
// LinkByIndex finds a link by index and returns a pointer to the object.
func LinkByIndex(index int) (Link, error) {
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Index = int32(index)
req.AddData(msg)
return execGetLink(req)
}
func execGetLink(req *nl.NetlinkRequest) (Link, error) {
msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0)
if err != nil {
if errno, ok := err.(syscall.Errno); ok {
if errno == syscall.ENODEV {
return nil, fmt.Errorf("Link not found")
}
}
return nil, err
}
switch {
case len(msgs) == 0:
return nil, fmt.Errorf("Link not found")
case len(msgs) == 1:
return linkDeserialize(msgs[0])
default:
return nil, fmt.Errorf("More than one link found")
}
}
// linkDeserialize deserializes a raw message received from netlink into
// a link object.
func linkDeserialize(m []byte) (Link, error) {
msg := nl.DeserializeIfInfomsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)}
var link Link
linkType := ""
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.IFLA_LINKINFO:
infos, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, err
}
for _, info := range infos {
switch info.Attr.Type {
case nl.IFLA_INFO_KIND:
linkType = string(info.Value[:len(info.Value)-1])
switch linkType {
case "dummy":
link = &Dummy{}
case "bridge":
link = &Bridge{}
case "vlan":
link = &Vlan{}
case "veth":
link = &Veth{}
case "vxlan":
link = &Vxlan{}
case "ipvlan":
link = &IPVlan{}
default:
link = &Generic{LinkType: linkType}
}
case nl.IFLA_INFO_DATA:
data, err := nl.ParseRouteAttr(info.Value)
if err != nil {
return nil, err
}
switch linkType {
case "vlan":
parseVlanData(link, data)
case "vxlan":
parseVxlanData(link, data)
case "ipvlan":
parseIPVlanData(link, data)
}
}
}
case syscall.IFLA_ADDRESS:
var nonzero bool
for _, b := range attr.Value {
if b != 0 {
nonzero = true
}
}
if nonzero {
base.HardwareAddr = attr.Value[:]
}
case syscall.IFLA_IFNAME:
base.Name = string(attr.Value[:len(attr.Value)-1])
case syscall.IFLA_MTU:
base.MTU = int(native.Uint32(attr.Value[0:4]))
case syscall.IFLA_LINK:
base.ParentIndex = int(native.Uint32(attr.Value[0:4]))
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])
}
}
// Links that don't have IFLA_INFO_KIND are hardware devices
if link == nil {
link = &Device{}
}
*link.Attrs() = base
return link, nil
}
// LinkList gets a list of link devices.
// Equivalent to: `ip link show`
func LinkList() ([]Link, error) {
// NOTE(vish): This duplicates functionality in net/iface_linux.go, but we need
// to get the message ourselves to parse link type.
req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
req.AddData(msg)
msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
if err != nil {
return nil, err
}
res := make([]Link, 0)
for _, m := range msgs {
link, err := linkDeserialize(m)
if err != nil {
return nil, err
}
res = append(res, link)
}
return res, nil
}
func LinkSetHairpin(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_MODE)
}
func LinkSetGuard(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_GUARD)
}
func LinkSetFastLeave(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_FAST_LEAVE)
}
func LinkSetLearning(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING)
}
func LinkSetRootBlock(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROTECT)
}
func LinkSetFlood(link Link, mode bool) error {
return setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD)
}
func setProtinfoAttr(link Link, mode bool, attr int) error {
base := link.Attrs()
ensureIndex(base)
req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
req.AddData(msg)
br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
nl.NewRtAttrChild(br, attr, boolToByte(mode))
req.AddData(br)
_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
if err != nil {
return err
}
return nil
}
func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
vlan := link.(*Vlan)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_VLAN_ID:
vlan.VlanId = int(native.Uint16(datum.Value[0:2]))
}
}
}
func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
vxlan := link.(*Vxlan)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_VXLAN_ID:
vxlan.VxlanId = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_LINK:
vxlan.VtepDevIndex = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_LOCAL:
vxlan.SrcAddr = net.IP(datum.Value[0:4])
case nl.IFLA_VXLAN_LOCAL6:
vxlan.SrcAddr = net.IP(datum.Value[0:16])
case nl.IFLA_VXLAN_GROUP:
vxlan.Group = net.IP(datum.Value[0:4])
case nl.IFLA_VXLAN_GROUP6:
vxlan.Group = net.IP(datum.Value[0:16])
case nl.IFLA_VXLAN_TTL:
vxlan.TTL = int(datum.Value[0])
case nl.IFLA_VXLAN_TOS:
vxlan.TOS = int(datum.Value[0])
case nl.IFLA_VXLAN_LEARNING:
vxlan.Learning = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_PROXY:
vxlan.Proxy = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_RSC:
vxlan.RSC = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_L2MISS:
vxlan.L2miss = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_L3MISS:
vxlan.L3miss = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_AGEING:
vxlan.Age = int(native.Uint32(datum.Value[0:4]))
vxlan.NoAge = vxlan.Age == 0
case nl.IFLA_VXLAN_LIMIT:
vxlan.Limit = int(native.Uint32(datum.Value[0:4]))
case nl.IFLA_VXLAN_PORT:
vxlan.Port = int(native.Uint16(datum.Value[0:2]))
case nl.IFLA_VXLAN_PORT_RANGE:
buf := bytes.NewBuffer(datum.Value[0:4])
var pr vxlanPortRange
if binary.Read(buf, binary.BigEndian, &pr) != nil {
vxlan.PortLow = int(pr.Lo)
vxlan.PortHigh = int(pr.Hi)
}
}
}
}
func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
ipv := link.(*IPVlan)
for _, datum := range data {
if datum.Attr.Type == nl.IFLA_IPVLAN_MODE {
ipv.Mode = IPVlanMode(native.Uint32(datum.Value[0:4]))
return
}
}
}
// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
if rawFlags&syscall.IFF_UP != 0 {
f |= net.FlagUp
}
if rawFlags&syscall.IFF_BROADCAST != 0 {
f |= net.FlagBroadcast
}
if rawFlags&syscall.IFF_LOOPBACK != 0 {
f |= net.FlagLoopback
}
if rawFlags&syscall.IFF_POINTOPOINT != 0 {
f |= net.FlagPointToPoint
}
if rawFlags&syscall.IFF_MULTICAST != 0 {
f |= net.FlagMulticast
}
return f
}