This updates the netlink dependency to include recent updates, including a fix when setting prosmic mode on a bridge and additions for creating qdisc/classes/filters. This is necessary for some upcoming additions to CNI
},
{
"ImportPath": "github.com/vishvananda/netlink",
- "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+ "Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
},
{
"ImportPath": "github.com/vishvananda/netlink/nl",
- "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+ "Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
},
{
"ImportPath": "github.com/vishvananda/netns",
- "Rev": "8ba1072b58e0c2a240eb5f6120165c7776c3e7b8"
+ "Rev": "54f0e4339ce73702a0607f49922aaa1e749b418d"
},
{
"ImportPath": "golang.org/x/sys/unix",
- sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
# modprobe ip_gre or else the first gre device can't be deleted
- sudo modprobe ip_gre
+ # modprobe nf_conntrack for the conntrack testing
+ - sudo modprobe nf_conntrack
+ - sudo modprobe nf_conntrack_netlink
+ - sudo modprobe nf_conntrack_ipv4
+ - sudo modprobe nf_conntrack_ipv6
install:
- go get github.com/vishvananda/netns
// include a mask, so it stores the address as a net.IPNet.
type Addr struct {
*net.IPNet
- Label string
- Flags int
- Scope int
- Peer *net.IPNet
- Broadcast net.IP
+ Label string
+ Flags int
+ Scope int
+ Peer *net.IPNet
+ Broadcast net.IP
+ PreferedLft int
+ ValidLft int
}
// String returns $ip/$netmask $label
return h.addrHandle(link, addr, req)
}
+// AddrReplace will replace (or, if not present, add) an IP address on a link device.
+// Equivalent to: `ip addr replace $addr dev $link`
+func AddrReplace(link Link, addr *Addr) error {
+ return pkgHandle.AddrReplace(link, addr)
+}
+
+// AddrReplace will replace (or, if not present, add) an IP address on a link device.
+// Equivalent to: `ip addr replace $addr dev $link`
+func (h *Handle) AddrReplace(link Link, addr *Addr) error {
+ req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|syscall.NLM_F_ACK)
+ return h.addrHandle(link, addr, req)
+}
+
// AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func AddrDel(link Link, addr *Addr) error {
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
addr.IPNet = local
+ case syscall.IFA_BROADCAST:
+ addr.Broadcast = attr.Value
case syscall.IFA_LABEL:
addr.Label = string(attr.Value[:len(attr.Value)-1])
case IFA_FLAGS:
addr.Flags = int(native.Uint32(attr.Value[0:4]))
+ case nl.IFA_CACHEINFO:
+ ci := nl.DeserializeIfaCacheInfo(attr.Value)
+ addr.PreferedLft = int(ci.IfaPrefered)
+ addr.ValidLft = int(ci.IfaValid)
}
}
type AddrUpdate struct {
LinkAddress net.IPNet
LinkIndex int
+ Flags int
+ Scope int
+ PreferedLft int
+ ValidLft int
NewAddr bool // true=added false=deleted
}
continue
}
- ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: ifindex, NewAddr: msgType == syscall.RTM_NEWADDR}
+ ch <- AddrUpdate{LinkAddress: *addr.IPNet,
+ LinkIndex: ifindex,
+ NewAddr: msgType == syscall.RTM_NEWADDR,
+ Flags: addr.Flags,
+ Scope: addr.Scope,
+ PreferedLft: addr.PreferedLft,
+ ValidLft: addr.ValidLft}
}
}
}()
--- /dev/null
+package netlink
+
+import (
+ "fmt"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+// BridgeVlanList gets a map of device id to bridge vlan infos.
+// Equivalent to: `bridge vlan show`
+func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
+ return pkgHandle.BridgeVlanList()
+}
+
+// BridgeVlanList gets a map of device id to bridge vlan infos.
+// Equivalent to: `bridge vlan show`
+func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
+ req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
+ msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
+
+ msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
+ if err != nil {
+ return nil, err
+ }
+ ret := make(map[int32][]*nl.BridgeVlanInfo)
+ for _, m := range msgs {
+ msg := nl.DeserializeIfInfomsg(m)
+
+ attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+ if err != nil {
+ return nil, err
+ }
+ for _, attr := range attrs {
+ switch attr.Attr.Type {
+ case nl.IFLA_AF_SPEC:
+ //nested attr
+ nestAttrs, err := nl.ParseRouteAttr(attr.Value)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse nested attr %v", err)
+ }
+ for _, nestAttr := range nestAttrs {
+ switch nestAttr.Attr.Type {
+ case nl.IFLA_BRIDGE_VLAN_INFO:
+ vlanInfo := nl.DeserializeBridgeVlanInfo(nestAttr.Value)
+ ret[msg.Index] = append(ret[msg.Index], vlanInfo)
+ }
+ }
+ }
+ }
+ }
+ return ret, nil
+}
+
+// BridgeVlanAdd adds a new vlan filter entry
+// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
+ return pkgHandle.BridgeVlanAdd(link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanAdd adds a new vlan filter entry
+// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
+ return h.bridgeVlanModify(syscall.RTM_SETLINK, link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanDel adds a new vlan filter entry
+// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
+ return pkgHandle.BridgeVlanDel(link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanDel adds a new vlan filter entry
+// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
+ return h.bridgeVlanModify(syscall.RTM_DELLINK, link, vid, pvid, untagged, self, master)
+}
+
+func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error {
+ base := link.Attrs()
+ h.ensureIndex(base)
+ req := h.newNetlinkRequest(cmd, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ br := nl.NewRtAttr(nl.IFLA_AF_SPEC, nil)
+ var flags uint16
+ if self {
+ flags |= nl.BRIDGE_FLAGS_SELF
+ }
+ if master {
+ flags |= nl.BRIDGE_FLAGS_MASTER
+ }
+ if flags > 0 {
+ nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
+ }
+ vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
+ if pvid {
+ vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
+ }
+ if untagged {
+ vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
+ }
+ nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
+ req.AddData(br)
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ if err != nil {
+ return err
+ }
+ return nil
+}
--- /dev/null
+package netlink
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+// ConntrackTableType Conntrack table for the netlink operation
+type ConntrackTableType uint8
+
+const (
+ // ConntrackTable Conntrack table
+ // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK 1
+ ConntrackTable = 1
+ // ConntrackExpectTable Conntrack expect table
+ // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2
+ ConntrackExpectTable = 2
+)
+
+const (
+ // backward compatibility with golang 1.6 which does not have io.SeekCurrent
+ seekCurrent = 1
+)
+
+// InetFamily Family type
+type InetFamily uint8
+
+// -L [table] [options] List conntrack or expectation table
+// -G [table] parameters Get conntrack or expectation
+
+// -I [table] parameters Create a conntrack or expectation
+// -U [table] parameters Update a conntrack
+// -E [table] [options] Show events
+
+// -C [table] Show counter
+// -S Show statistics
+
+// ConntrackTableList returns the flow list of a table of a specific family
+// conntrack -L [table] [options] List conntrack or expectation table
+func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+ return pkgHandle.ConntrackTableList(table, family)
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table
+// conntrack -F [table] Flush table
+// The flush operation applies to all the family types
+func ConntrackTableFlush(table ConntrackTableType) error {
+ return pkgHandle.ConntrackTableFlush(table)
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
+// conntrack -D [table] parameters Delete conntrack or expectation
+func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
+ return pkgHandle.ConntrackDeleteFilter(table, family, filter)
+}
+
+// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
+// conntrack -L [table] [options] List conntrack or expectation table
+func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+ res, err := h.dumpConntrackTable(table, family)
+ if err != nil {
+ return nil, err
+ }
+
+ // Deserialize all the flows
+ var result []*ConntrackFlow
+ for _, dataRaw := range res {
+ result = append(result, parseRawData(dataRaw))
+ }
+
+ return result, nil
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
+// conntrack -F [table] Flush table
+// The flush operation applies to all the family types
+func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
+ req := h.newConntrackRequest(table, syscall.AF_INET, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
+ _, err := req.Execute(syscall.NETLINK_NETFILTER, 0)
+ return err
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
+// conntrack -D [table] parameters Delete conntrack or expectation
+func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
+ res, err := h.dumpConntrackTable(table, family)
+ if err != nil {
+ return 0, err
+ }
+
+ var matched uint
+ for _, dataRaw := range res {
+ flow := parseRawData(dataRaw)
+ if match := filter.MatchConntrackFlow(flow); match {
+ req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
+ // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
+ req2.AddRawData(dataRaw[4:])
+ req2.Execute(syscall.NETLINK_NETFILTER, 0)
+ matched++
+ }
+ }
+
+ return matched, nil
+}
+
+func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {
+ // Create the Netlink request object
+ req := h.newNetlinkRequest((int(table)<<8)|operation, flags)
+ // Add the netfilter header
+ msg := &nl.Nfgenmsg{
+ NfgenFamily: uint8(family),
+ Version: nl.NFNETLINK_V0,
+ ResId: 0,
+ }
+ req.AddData(msg)
+ return req
+}
+
+func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) {
+ req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, syscall.NLM_F_DUMP)
+ return req.Execute(syscall.NETLINK_NETFILTER, 0)
+}
+
+// The full conntrack flow structure is very complicated and can be found in the file:
+// http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h
+// For the time being, the structure below allows to parse and extract the base information of a flow
+type ipTuple struct {
+ SrcIP net.IP
+ DstIP net.IP
+ Protocol uint8
+ SrcPort uint16
+ DstPort uint16
+}
+
+type ConntrackFlow struct {
+ FamilyType uint8
+ Forward ipTuple
+ Reverse ipTuple
+}
+
+func (s *ConntrackFlow) String() string {
+ // conntrack cmd output:
+ // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001
+ return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d",
+ nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
+ s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort,
+ s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort)
+}
+
+// This method parse the ip tuple structure
+// The message structure is the following:
+// <len, [CTA_IP_V4_SRC|CTA_IP_V6_SRC], 16 bytes for the IP>
+// <len, [CTA_IP_V4_DST|CTA_IP_V6_DST], 16 bytes for the IP>
+// <len, NLA_F_NESTED|nl.CTA_TUPLE_PROTO, 1 byte for the protocol, 3 bytes of padding>
+// <len, CTA_PROTO_SRC_PORT, 2 bytes for the source port, 2 bytes of padding>
+// <len, CTA_PROTO_DST_PORT, 2 bytes for the source port, 2 bytes of padding>
+func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) {
+ for i := 0; i < 2; i++ {
+ _, t, _, v := parseNfAttrTLV(reader)
+ switch t {
+ case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC:
+ tpl.SrcIP = v
+ case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST:
+ tpl.DstIP = v
+ }
+ }
+ // Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO
+ reader.Seek(4, seekCurrent)
+ _, t, _, v := parseNfAttrTLV(reader)
+ if t == nl.CTA_PROTO_NUM {
+ tpl.Protocol = uint8(v[0])
+ }
+ // Skip some padding 3 bytes
+ reader.Seek(3, seekCurrent)
+ for i := 0; i < 2; i++ {
+ _, t, _ := parseNfAttrTL(reader)
+ switch t {
+ case nl.CTA_PROTO_SRC_PORT:
+ parseBERaw16(reader, &tpl.SrcPort)
+ case nl.CTA_PROTO_DST_PORT:
+ parseBERaw16(reader, &tpl.DstPort)
+ }
+ // Skip some padding 2 byte
+ reader.Seek(2, seekCurrent)
+ }
+}
+
+func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) {
+ isNested, attrType, len = parseNfAttrTL(r)
+
+ value = make([]byte, len)
+ binary.Read(r, binary.BigEndian, &value)
+ return isNested, attrType, len, value
+}
+
+func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
+ binary.Read(r, nl.NativeEndian(), &len)
+ len -= nl.SizeofNfattr
+
+ binary.Read(r, nl.NativeEndian(), &attrType)
+ isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
+ attrType = attrType & (nl.NLA_F_NESTED - 1)
+
+ return isNested, attrType, len
+}
+
+func parseBERaw16(r *bytes.Reader, v *uint16) {
+ binary.Read(r, binary.BigEndian, v)
+}
+
+func parseRawData(data []byte) *ConntrackFlow {
+ s := &ConntrackFlow{}
+ // First there is the Nfgenmsg header
+ // consume only the family field
+ reader := bytes.NewReader(data)
+ binary.Read(reader, nl.NativeEndian(), &s.FamilyType)
+
+ // skip rest of the Netfilter header
+ reader.Seek(3, seekCurrent)
+ // The message structure is the following:
+ // <len, NLA_F_NESTED|CTA_TUPLE_ORIG> 4 bytes
+ // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
+ // flow information of the forward flow
+ // <len, NLA_F_NESTED|CTA_TUPLE_REPLY> 4 bytes
+ // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
+ // flow information of the reverse flow
+ for reader.Len() > 0 {
+ nested, t, l := parseNfAttrTL(reader)
+ if nested && t == nl.CTA_TUPLE_ORIG {
+ if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
+ parseIpTuple(reader, &s.Forward)
+ }
+ } else if nested && t == nl.CTA_TUPLE_REPLY {
+ if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
+ parseIpTuple(reader, &s.Reverse)
+
+ // Got all the useful information stop parsing
+ break
+ } else {
+ // Header not recognized skip it
+ reader.Seek(int64(l), seekCurrent)
+ }
+ }
+ }
+
+ return s
+}
+
+// Conntrack parameters and options:
+// -n, --src-nat ip source NAT ip
+// -g, --dst-nat ip destination NAT ip
+// -j, --any-nat ip source or destination NAT ip
+// -m, --mark mark Set mark
+// -c, --secmark secmark Set selinux secmark
+// -e, --event-mask eventmask Event mask, eg. NEW,DESTROY
+// -z, --zero Zero counters while listing
+// -o, --output type[,...] Output format, eg. xml
+// -l, --label label[,...] conntrack labels
+
+// Common parameters and options:
+// -s, --src, --orig-src ip Source address from original direction
+// -d, --dst, --orig-dst ip Destination address from original direction
+// -r, --reply-src ip Source addres from reply direction
+// -q, --reply-dst ip Destination address from reply direction
+// -p, --protonum proto Layer 4 Protocol, eg. 'tcp'
+// -f, --family proto Layer 3 Protocol, eg. 'ipv6'
+// -t, --timeout timeout Set timeout
+// -u, --status status Set status, eg. ASSURED
+// -w, --zone value Set conntrack zone
+// --orig-zone value Set zone for original direction
+// --reply-zone value Set zone for reply direction
+// -b, --buffer-size Netlink socket buffer size
+// --mask-src ip Source mask address
+// --mask-dst ip Destination mask address
+
+// Filter types
+type ConntrackFilterType uint8
+
+const (
+ ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction
+ ConntrackOrigDstIP // -orig-dst ip Destination address from original direction
+ ConntrackNatSrcIP // -src-nat ip Source NAT ip
+ ConntrackNatDstIP // -dst-nat ip Destination NAT ip
+ ConntrackNatAnyIP // -any-nat ip Source or destination NAT ip
+)
+
+type CustomConntrackFilter interface {
+ // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches
+ // the filter or false otherwise
+ MatchConntrackFlow(flow *ConntrackFlow) bool
+}
+
+type ConntrackFilter struct {
+ ipFilter map[ConntrackFilterType]net.IP
+}
+
+// AddIP adds an IP to the conntrack filter
+func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
+ if f.ipFilter == nil {
+ f.ipFilter = make(map[ConntrackFilterType]net.IP)
+ }
+ if _, ok := f.ipFilter[tp]; ok {
+ return errors.New("Filter attribute already present")
+ }
+ f.ipFilter[tp] = ip
+ return nil
+}
+
+// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
+// false otherwise
+func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
+ if len(f.ipFilter) == 0 {
+ // empty filter always not match
+ return false
+ }
+
+ match := true
+ // -orig-src ip Source address from original direction
+ if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
+ match = match && elem.Equal(flow.Forward.SrcIP)
+ }
+
+ // -orig-dst ip Destination address from original direction
+ if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
+ match = match && elem.Equal(flow.Forward.DstIP)
+ }
+
+ // -src-nat ip Source NAT ip
+ if elem, found := f.ipFilter[ConntrackNatSrcIP]; match && found {
+ match = match && elem.Equal(flow.Reverse.SrcIP)
+ }
+
+ // -dst-nat ip Destination NAT ip
+ if elem, found := f.ipFilter[ConntrackNatDstIP]; match && found {
+ match = match && elem.Equal(flow.Reverse.DstIP)
+ }
+
+ // -any-nat ip Source or destination NAT ip
+ if elem, found := f.ipFilter[ConntrackNatAnyIP]; match && found {
+ match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
+ }
+
+ return match
+}
+
+var _ CustomConntrackFilter = (*ConntrackFilter)(nil)
--- /dev/null
+// +build !linux
+
+package netlink
+
+// ConntrackTableType Conntrack table for the netlink operation
+type ConntrackTableType uint8
+
+// InetFamily Family type
+type InetFamily uint8
+
+// ConntrackFlow placeholder
+type ConntrackFlow struct{}
+
+// ConntrackFilter placeholder
+type ConntrackFilter struct{}
+
+// ConntrackTableList returns the flow list of a table of a specific family
+// conntrack -L [table] [options] List conntrack or expectation table
+func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+ return nil, ErrNotImplemented
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table
+// conntrack -F [table] Flush table
+// The flush operation applies to all the family types
+func ConntrackTableFlush(table ConntrackTableType) error {
+ return ErrNotImplemented
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
+// conntrack -D [table] parameters Delete conntrack or expectation
+func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
+ return 0, ErrNotImplemented
+}
+
+// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
+// conntrack -L [table] [options] List conntrack or expectation table
+func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+ return nil, ErrNotImplemented
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
+// conntrack -F [table] Flush table
+// The flush operation applies to all the family types
+func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
+ return ErrNotImplemented
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
+// conntrack -D [table] parameters Delete conntrack or expectation
+func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
+ return 0, ErrNotImplemented
+}
package netlink
-import "fmt"
+import (
+ "fmt"
+
+ "github.com/vishvananda/netlink/nl"
+)
type Filter interface {
Attrs() *FilterAttrs
}
}
+// Constants used in TcU32Sel.Flags.
+const (
+ TC_U32_TERMINAL = nl.TC_U32_TERMINAL
+ TC_U32_OFFSET = nl.TC_U32_OFFSET
+ TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
+ TC_U32_EAT = nl.TC_U32_EAT
+)
+
+// Sel of the U32 filters that contains multiple TcU32Key. This is the copy
+// and the frontend representation of nl.TcU32Sel. It is serialized into canonical
+// nl.TcU32Sel with the appropriate endianness.
+type TcU32Sel struct {
+ Flags uint8
+ Offshift uint8
+ Nkeys uint8
+ Pad uint8
+ Offmask uint16
+ Off uint16
+ Offoff int16
+ Hoff int16
+ Hmask uint32
+ Keys []TcU32Key
+}
+
+// TcU32Key contained of Sel in the U32 filters. This is the copy and the frontend
+// representation of nl.TcU32Key. It is serialized into chanonical nl.TcU32Sel
+// with the appropriate endianness.
+type TcU32Key struct {
+ Mask uint32
+ Val uint32
+ Off int32
+ OffMask int32
+}
+
// U32 filters on many packet related properties
type U32 struct {
FilterAttrs
ClassId uint32
RedirIndex int
+ Sel *TcU32Sel
Actions []Action
}
"errors"
"fmt"
"syscall"
+ "unsafe"
"github.com/vishvananda/netlink/nl"
)
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
if u32, ok := filter.(*U32); ok {
- // match all
- sel := nl.TcU32Sel{
- Nkeys: 1,
- Flags: nl.TC_U32_TERMINAL,
+ // Convert TcU32Sel into nl.TcU32Sel as it is without copy.
+ sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel))
+ if sel == nil {
+ // match all
+ sel = &nl.TcU32Sel{
+ Nkeys: 1,
+ Flags: nl.TC_U32_TERMINAL,
+ }
+ sel.Keys = append(sel.Keys, nl.TcU32Key{})
}
- sel.Keys = append(sel.Keys, nl.TcU32Key{})
+
+ if native != networkOrder {
+ // Copy TcU32Sel.
+ cSel := *sel
+ keys := make([]nl.TcU32Key, cap(sel.Keys))
+ copy(keys, sel.Keys)
+ cSel.Keys = keys
+ sel = &cSel
+
+ // Handle the endianness of attributes
+ sel.Offmask = native.Uint16(htons(sel.Offmask))
+ sel.Hmask = native.Uint32(htonl(sel.Hmask))
+ for i, key := range sel.Keys {
+ sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
+ sel.Keys[i].Val = native.Uint32(htonl(key.Val))
+ }
+ }
+ sel.Nkeys = uint8(len(sel.Keys))
nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
if u32.ClassId != 0 {
nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
case nl.TCA_U32_SEL:
detailed = true
sel := nl.DeserializeTcU32Sel(datum.Value)
- // only parse if we have a very basic redirect
- if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
- return detailed, nil
+ u32.Sel = (*TcU32Sel)(unsafe.Pointer(sel))
+ if native != networkOrder {
+ // Handle the endianness of attributes
+ u32.Sel.Offmask = native.Uint16(htons(sel.Offmask))
+ u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask))
+ for i, key := range u32.Sel.Keys {
+ u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
+ u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val))
+ }
}
case nl.TCA_U32_ACT:
tables, err := nl.ParseRouteAttr(datum.Value)
u32.RedirIndex = int(action.Ifindex)
}
}
+ case nl.TCA_U32_CLASSID:
+ u32.ClassId = native.Uint32(datum.Value)
}
}
return detailed, nil
--- /dev/null
+package netlink
+
+import (
+ "fmt"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+type GenlOp struct {
+ ID uint32
+ Flags uint32
+}
+
+type GenlMulticastGroup struct {
+ ID uint32
+ Name string
+}
+
+type GenlFamily struct {
+ ID uint16
+ HdrSize uint32
+ Name string
+ Version uint32
+ MaxAttr uint32
+ Ops []GenlOp
+ Groups []GenlMulticastGroup
+}
+
+func parseOps(b []byte) ([]GenlOp, error) {
+ attrs, err := nl.ParseRouteAttr(b)
+ if err != nil {
+ return nil, err
+ }
+ ops := make([]GenlOp, 0, len(attrs))
+ for _, a := range attrs {
+ nattrs, err := nl.ParseRouteAttr(a.Value)
+ if err != nil {
+ return nil, err
+ }
+ var op GenlOp
+ for _, na := range nattrs {
+ switch na.Attr.Type {
+ case nl.GENL_CTRL_ATTR_OP_ID:
+ op.ID = native.Uint32(na.Value)
+ case nl.GENL_CTRL_ATTR_OP_FLAGS:
+ op.Flags = native.Uint32(na.Value)
+ }
+ }
+ ops = append(ops, op)
+ }
+ return ops, nil
+}
+
+func parseMulticastGroups(b []byte) ([]GenlMulticastGroup, error) {
+ attrs, err := nl.ParseRouteAttr(b)
+ if err != nil {
+ return nil, err
+ }
+ groups := make([]GenlMulticastGroup, 0, len(attrs))
+ for _, a := range attrs {
+ nattrs, err := nl.ParseRouteAttr(a.Value)
+ if err != nil {
+ return nil, err
+ }
+ var g GenlMulticastGroup
+ for _, na := range nattrs {
+ switch na.Attr.Type {
+ case nl.GENL_CTRL_ATTR_MCAST_GRP_NAME:
+ g.Name = nl.BytesToString(na.Value)
+ case nl.GENL_CTRL_ATTR_MCAST_GRP_ID:
+ g.ID = native.Uint32(na.Value)
+ }
+ }
+ groups = append(groups, g)
+ }
+ return groups, nil
+}
+
+func (f *GenlFamily) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
+ for _, a := range attrs {
+ switch a.Attr.Type {
+ case nl.GENL_CTRL_ATTR_FAMILY_NAME:
+ f.Name = nl.BytesToString(a.Value)
+ case nl.GENL_CTRL_ATTR_FAMILY_ID:
+ f.ID = native.Uint16(a.Value)
+ case nl.GENL_CTRL_ATTR_VERSION:
+ f.Version = native.Uint32(a.Value)
+ case nl.GENL_CTRL_ATTR_HDRSIZE:
+ f.HdrSize = native.Uint32(a.Value)
+ case nl.GENL_CTRL_ATTR_MAXATTR:
+ f.MaxAttr = native.Uint32(a.Value)
+ case nl.GENL_CTRL_ATTR_OPS:
+ ops, err := parseOps(a.Value)
+ if err != nil {
+ return err
+ }
+ f.Ops = ops
+ case nl.GENL_CTRL_ATTR_MCAST_GROUPS:
+ groups, err := parseMulticastGroups(a.Value)
+ if err != nil {
+ return err
+ }
+ f.Groups = groups
+ }
+ }
+
+ return nil
+}
+
+func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) {
+ families := make([]*GenlFamily, 0, len(msgs))
+ for _, m := range msgs {
+ attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
+ if err != nil {
+ return nil, err
+ }
+ family := &GenlFamily{}
+ if err := family.parseAttributes(attrs); err != nil {
+ return nil, err
+ }
+
+ families = append(families, family)
+ }
+ return families, nil
+}
+
+func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_CTRL_CMD_GETFAMILY,
+ Version: nl.GENL_CTRL_VERSION,
+ }
+ req := h.newNetlinkRequest(nl.GENL_ID_CTRL, syscall.NLM_F_DUMP)
+ req.AddData(msg)
+ msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+ if err != nil {
+ return nil, err
+ }
+ return parseFamilies(msgs)
+}
+
+func GenlFamilyList() ([]*GenlFamily, error) {
+ return pkgHandle.GenlFamilyList()
+}
+
+func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_CTRL_CMD_GETFAMILY,
+ Version: nl.GENL_CTRL_VERSION,
+ }
+ req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name)))
+ msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+ if err != nil {
+ return nil, err
+ }
+ families, err := parseFamilies(msgs)
+ if len(families) != 1 {
+ return nil, fmt.Errorf("invalid response for GENL_CTRL_CMD_GETFAMILY")
+ }
+ return families[0], nil
+}
+
+func GenlFamilyGet(name string) (*GenlFamily, error) {
+ return pkgHandle.GenlFamilyGet(name)
+}
--- /dev/null
+// +build !linux
+
+package netlink
+
+type GenlOp struct{}
+
+type GenlMulticastGroup struct{}
+
+type GenlFamily struct{}
+
+func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
+ return nil, ErrNotImplemented
+}
+
+func GenlFamilyList() ([]*GenlFamily, error) {
+ return nil, ErrNotImplemented
+}
+
+func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
+ return nil, ErrNotImplemented
+}
+
+func GenlFamilyGet(name string) (*GenlFamily, error) {
+ return nil, ErrNotImplemented
+}
--- /dev/null
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "syscall"
+
+ "github.com/vishvananda/netlink/nl"
+)
+
+type PDP struct {
+ Version uint32
+ TID uint64
+ PeerAddress net.IP
+ MSAddress net.IP
+ Flow uint16
+ NetNSFD uint32
+ ITEI uint32
+ OTEI uint32
+}
+
+func (pdp *PDP) String() string {
+ elems := []string{}
+ elems = append(elems, fmt.Sprintf("Version: %d", pdp.Version))
+ if pdp.Version == 0 {
+ elems = append(elems, fmt.Sprintf("TID: %d", pdp.TID))
+ } else if pdp.Version == 1 {
+ elems = append(elems, fmt.Sprintf("TEI: %d/%d", pdp.ITEI, pdp.OTEI))
+ }
+ elems = append(elems, fmt.Sprintf("MS-Address: %s", pdp.MSAddress))
+ elems = append(elems, fmt.Sprintf("Peer-Address: %s", pdp.PeerAddress))
+ return fmt.Sprintf("{%s}", strings.Join(elems, " "))
+}
+
+func (p *PDP) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
+ for _, a := range attrs {
+ switch a.Attr.Type {
+ case nl.GENL_GTP_ATTR_VERSION:
+ p.Version = native.Uint32(a.Value)
+ case nl.GENL_GTP_ATTR_TID:
+ p.TID = native.Uint64(a.Value)
+ case nl.GENL_GTP_ATTR_PEER_ADDRESS:
+ p.PeerAddress = net.IP(a.Value)
+ case nl.GENL_GTP_ATTR_MS_ADDRESS:
+ p.MSAddress = net.IP(a.Value)
+ case nl.GENL_GTP_ATTR_FLOW:
+ p.Flow = native.Uint16(a.Value)
+ case nl.GENL_GTP_ATTR_NET_NS_FD:
+ p.NetNSFD = native.Uint32(a.Value)
+ case nl.GENL_GTP_ATTR_I_TEI:
+ p.ITEI = native.Uint32(a.Value)
+ case nl.GENL_GTP_ATTR_O_TEI:
+ p.OTEI = native.Uint32(a.Value)
+ }
+ }
+ return nil
+}
+
+func parsePDP(msgs [][]byte) ([]*PDP, error) {
+ pdps := make([]*PDP, 0, len(msgs))
+ for _, m := range msgs {
+ attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
+ if err != nil {
+ return nil, err
+ }
+ pdp := &PDP{}
+ if err := pdp.parseAttributes(attrs); err != nil {
+ return nil, err
+ }
+ pdps = append(pdps, pdp)
+ }
+ return pdps, nil
+}
+
+func (h *Handle) GTPPDPList() ([]*PDP, error) {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return nil, err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_GETPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_DUMP)
+ req.AddData(msg)
+ msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+ if err != nil {
+ return nil, err
+ }
+ return parsePDP(msgs)
+}
+
+func GTPPDPList() ([]*PDP, error) {
+ return pkgHandle.GTPPDPList()
+}
+
+func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
+ msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+ if err != nil {
+ return nil, err
+ }
+ pdps, err := parsePDP(msgs)
+ if err != nil {
+ return nil, err
+ }
+ if len(pdps) != 1 {
+ return nil, fmt.Errorf("invalid reqponse for GENL_GTP_CMD_GETPDP")
+ }
+ return pdps[0], nil
+}
+
+func (h *Handle) GTPPDPByTID(link Link, tid int) (*PDP, error) {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return nil, err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_GETPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), 0)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(uint64(tid))))
+ return gtpPDPGet(req)
+}
+
+func GTPPDPByTID(link Link, tid int) (*PDP, error) {
+ return pkgHandle.GTPPDPByTID(link, tid)
+}
+
+func (h *Handle) GTPPDPByITEI(link Link, itei int) (*PDP, error) {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return nil, err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_GETPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), 0)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(1)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(uint32(itei))))
+ return gtpPDPGet(req)
+}
+
+func GTPPDPByITEI(link Link, itei int) (*PDP, error) {
+ return pkgHandle.GTPPDPByITEI(link, itei)
+}
+
+func (h *Handle) GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return nil, err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_GETPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), 0)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(addr.To4())))
+ return gtpPDPGet(req)
+}
+
+func GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
+ return pkgHandle.GTPPDPByMSAddress(link, addr)
+}
+
+func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_NEWPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_PEER_ADDRESS, []byte(pdp.PeerAddress.To4())))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(pdp.MSAddress.To4())))
+
+ switch pdp.Version {
+ case 0:
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_FLOW, nl.Uint16Attr(pdp.Flow)))
+ case 1:
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_O_TEI, nl.Uint32Attr(pdp.OTEI)))
+ default:
+ return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
+ }
+ _, err = req.Execute(syscall.NETLINK_GENERIC, 0)
+ return err
+}
+
+func GTPPDPAdd(link Link, pdp *PDP) error {
+ return pkgHandle.GTPPDPAdd(link, pdp)
+}
+
+func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
+ f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+ if err != nil {
+ return err
+ }
+ msg := &nl.Genlmsg{
+ Command: nl.GENL_GTP_CMD_DELPDP,
+ Version: nl.GENL_GTP_VERSION,
+ }
+ req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+ req.AddData(msg)
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+
+ switch pdp.Version {
+ case 0:
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
+ case 1:
+ req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
+ default:
+ return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
+ }
+ _, err = req.Execute(syscall.NETLINK_GENERIC, 0)
+ return err
+}
+
+func GTPPDPDel(link Link, pdp *PDP) error {
+ return pkgHandle.GTPPDPDel(link, pdp)
+}
type LinkXdp struct {
Fd int
Attached bool
+ Flags uint32
}
// Device links cannot be created via netlink. These links
// Bridge links are simple linux bridges
type Bridge struct {
LinkAttrs
+ MulticastSnooping *bool
+ HelloTime *uint32
}
func (bridge *Bridge) Attrs() *LinkAttrs {
UDPCSum bool
NoAge bool
GBP bool
+ FlowBased bool
Age int
Limit int
Port int
LacpRate BondLacpRate
AdSelect BondAdSelect
// looking at iproute tool AdInfo can only be retrived. It can't be set.
- AdInfo *BondAdInfo
+ AdInfo *BondAdInfo
+ AdActorSysPrio int
+ AdUserPortKey int
+ AdActorSystem net.HardwareAddr
+ TlbDynamicLb int
}
func NewLinkBond(atr LinkAttrs) *Bond {
PackersPerSlave: -1,
LacpRate: -1,
AdSelect: -1,
+ AdActorSysPrio: -1,
+ AdUserPortKey: -1,
+ AdActorSystem: nil,
+ TlbDynamicLb: -1,
}
}
EncapType uint16
EncapFlags uint16
Link uint32
+ FlowBased bool
}
func (gretap *Gretap) Attrs() *LinkAttrs {
return "vrf"
}
+type GTP struct {
+ LinkAttrs
+ FD0 int
+ FD1 int
+ Role int
+ PDPHashsize int
+}
+
+func (gtp *GTP) Attrs() *LinkAttrs {
+ return >p.LinkAttrs
+}
+
+func (gtp *GTP) Type() string {
+ return "gtp"
+}
+
// iproute2 supported devices;
// vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
// bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
// gre | gretap | ip6gre | ip6gretap | vti | nlmon |
// bond_slave | ipvlan
+
+// LinkNotFoundError wraps the various not found errors when
+// getting/reading links. This is intended for better error
+// handling by dependent code so that "not found error" can
+// be distinguished from other errors
+type LinkNotFoundError struct {
+ error
+}
}
}
+func (h *Handle) LinkSetARPOff(link Link) error {
+ base := link.Attrs()
+ h.ensureIndex(base)
+ req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Change |= syscall.IFF_NOARP
+ msg.Flags |= syscall.IFF_NOARP
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+func LinkSetARPOff(link Link) error {
+ return pkgHandle.LinkSetARPOff(link)
+}
+
+func (h *Handle) LinkSetARPOn(link Link) error {
+ base := link.Attrs()
+ h.ensureIndex(base)
+ req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+ msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+ msg.Change |= syscall.IFF_NOARP
+ msg.Flags &= ^uint32(syscall.IFF_NOARP)
+ msg.Index = int32(base.Index)
+ req.AddData(msg)
+
+ _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+ return err
+}
+
+func LinkSetARPOn(link Link) error {
+ return pkgHandle.LinkSetARPOn(link)
+}
+
func (h *Handle) SetPromiscOn(link Link) error {
base := link.Attrs()
h.ensureIndex(base)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_PROMISC
- msg.Flags = syscall.IFF_UP
+ msg.Flags = syscall.IFF_PROMISC
msg.Index = int32(base.Index)
req.AddData(msg)
return err
}
+func BridgeSetMcastSnoop(link Link, on bool) error {
+ return pkgHandle.BridgeSetMcastSnoop(link, on)
+}
+
+func (h *Handle) BridgeSetMcastSnoop(link Link, on bool) error {
+ bridge := link.(*Bridge)
+ bridge.MulticastSnooping = &on
+ return h.linkModify(bridge, syscall.NLM_F_ACK)
+}
+
func SetPromiscOn(link Link) error {
return pkgHandle.SetPromiscOn(link)
}
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_PROMISC
- msg.Flags = 0 & ^syscall.IFF_UP
+ msg.Flags = 0 & ^syscall.IFF_PROMISC
msg.Index = int32(base.Index)
req.AddData(msg)
func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+
+ if vxlan.FlowBased {
+ vxlan.VxlanId = 0
+ }
+
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.GBP {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{})
}
+ if vxlan.FlowBased {
+ nl.NewRtAttrChild(data, nl.IFLA_VXLAN_FLOWBASED, boolAttr(vxlan.FlowBased))
+ }
if vxlan.NoAge {
nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
} else if vxlan.Age > 0 {
if bond.AdSelect >= 0 {
nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect)))
}
+ if bond.AdActorSysPrio >= 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_ACTOR_SYS_PRIO, nl.Uint16Attr(uint16(bond.AdActorSysPrio)))
+ }
+ if bond.AdUserPortKey >= 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_USER_PORT_KEY, nl.Uint16Attr(uint16(bond.AdUserPortKey)))
+ }
+ if bond.AdActorSystem != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_ACTOR_SYSTEM, []byte(bond.AdActorSystem))
+ }
+ if bond.TlbDynamicLb >= 0 {
+ nl.NewRtAttrChild(data, nl.IFLA_BOND_TLB_DYNAMIC_LB, nl.Uint8Attr(uint8(bond.TlbDynamicLb)))
+ }
}
// 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 (h *Handle) LinkAdd(link Link) error {
- // TODO: set mtu and hardware address
+ return h.linkModify(link, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+}
+
+func (h *Handle) linkModify(link Link, flags int) error {
// TODO: support extra data for macvlan
base := link.Attrs()
return nil
}
- req := h.newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+ req := h.newNetlinkRequest(syscall.RTM_NEWLINK, flags)
msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
// TODO: make it shorter
req.AddData(qlen)
}
+ if base.HardwareAddr != nil {
+ hwaddr := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(base.HardwareAddr))
+ req.AddData(hwaddr)
+ }
+
if base.Namespace != nil {
var attr *nl.RtAttr
switch base.Namespace.(type) {
addVtiAttrs(vti, linkInfo)
} else if vrf, ok := link.(*Vrf); ok {
addVrfAttrs(vrf, linkInfo)
+ } else if bridge, ok := link.(*Bridge); ok {
+ addBridgeAttrs(bridge, linkInfo)
+ } else if gtp, ok := link.(*GTP); ok {
+ addGTPAttrs(gtp, linkInfo)
}
req.AddData(linkInfo)
return link, nil
}
}
- return nil, fmt.Errorf("Link %s not found", name)
+ return nil, LinkNotFoundError{fmt.Errorf("Link %s not found", name)}
}
func (h *Handle) linkByAliasDump(alias string) (Link, error) {
return link, nil
}
}
- return nil, fmt.Errorf("Link alias %s not found", alias)
+ return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)}
}
// LinkByName finds a link by name and returns a pointer to the object.
if err != nil {
if errno, ok := err.(syscall.Errno); ok {
if errno == syscall.ENODEV {
- return nil, fmt.Errorf("Link not found")
+ return nil, LinkNotFoundError{fmt.Errorf("Link not found")}
}
}
return nil, err
switch {
case len(msgs) == 0:
- return nil, fmt.Errorf("Link not found")
+ return nil, LinkNotFoundError{fmt.Errorf("Link not found")}
case len(msgs) == 1:
return LinkDeserialize(nil, msgs[0])
link = &Vti{}
case "vrf":
link = &Vrf{}
+ case "gtp":
+ link = >P{}
default:
link = &GenericLink{LinkType: linkType}
}
parseVtiData(link, data)
case "vrf":
parseVrfData(link, data)
+ case "bridge":
+ parseBridgeData(link, data)
+ case "gtp":
+ parseGTPData(link, data)
}
}
}
return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD)
}
+func LinkSetBrProxyArp(link Link, mode bool) error {
+ return pkgHandle.LinkSetBrProxyArp(link, mode)
+}
+
+func (h *Handle) LinkSetBrProxyArp(link Link, mode bool) error {
+ return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP)
+}
+
+func LinkSetBrProxyArpWiFi(link Link, mode bool) error {
+ return pkgHandle.LinkSetBrProxyArpWiFi(link, mode)
+}
+
+func (h *Handle) LinkSetBrProxyArpWiFi(link Link, mode bool) error {
+ return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP_WIFI)
+}
+
func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
base := link.Attrs()
h.ensureIndex(base)
vxlan.UDPCSum = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_GBP:
vxlan.GBP = true
+ case nl.IFLA_VXLAN_FLOWBASED:
+ vxlan.FlowBased = int8(datum.Value[0]) != 0
case nl.IFLA_VXLAN_AGEING:
vxlan.Age = int(native.Uint32(datum.Value[0:4]))
vxlan.NoAge = vxlan.Age == 0
}
func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
- bond := NewLinkBond(NewLinkAttrs())
+ bond := link.(*Bond)
for i := range data {
switch data[i].Attr.Type {
case nl.IFLA_BOND_MODE:
bond.AdSelect = BondAdSelect(data[i].Value[0])
case nl.IFLA_BOND_AD_INFO:
// TODO: implement
+ case nl.IFLA_BOND_AD_ACTOR_SYS_PRIO:
+ bond.AdActorSysPrio = int(native.Uint16(data[i].Value[0:2]))
+ case nl.IFLA_BOND_AD_USER_PORT_KEY:
+ bond.AdUserPortKey = int(native.Uint16(data[i].Value[0:2]))
+ case nl.IFLA_BOND_AD_ACTOR_SYSTEM:
+ bond.AdActorSystem = net.HardwareAddr(data[i].Value[0:6])
+ case nl.IFLA_BOND_TLB_DYNAMIC_LB:
+ bond.TlbDynamicLb = int(data[i].Value[0])
}
}
}
func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+ if gretap.FlowBased {
+ // In flow based mode, no other attributes need to be configured
+ nl.NewRtAttrChild(data, nl.IFLA_GRE_COLLECT_METADATA, boolAttr(gretap.FlowBased))
+ return
+ }
+
ip := gretap.Local.To4()
if ip != nil {
nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
gre.EncapType = native.Uint16(datum.Value[0:2])
case nl.IFLA_GRE_ENCAP_FLAGS:
gre.EncapFlags = native.Uint16(datum.Value[0:2])
+ case nl.IFLA_GRE_COLLECT_METADATA:
+ gre.FlowBased = int8(datum.Value[0]) != 0
}
}
}
b := make([]byte, 4)
native.PutUint32(b, uint32(xdp.Fd))
nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FD, b)
+ native.PutUint32(b, xdp.Flags)
+ nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FLAGS, b)
req.AddData(attrs)
}
xdp.Fd = int(native.Uint32(attr.Value[0:4]))
case nl.IFLA_XDP_ATTACHED:
xdp.Attached = attr.Value[0] != 0
+ case nl.IFLA_XDP_FLAGS:
+ xdp.Flags = native.Uint32(attr.Value[0:4])
}
}
return xdp, nil
}
}
}
+
+func addBridgeAttrs(bridge *Bridge, linkInfo *nl.RtAttr) {
+ data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+ if bridge.MulticastSnooping != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_BR_MCAST_SNOOPING, boolToByte(*bridge.MulticastSnooping))
+ }
+ if bridge.HelloTime != nil {
+ nl.NewRtAttrChild(data, nl.IFLA_BR_HELLO_TIME, nl.Uint32Attr(*bridge.HelloTime))
+ }
+}
+
+func parseBridgeData(bridge Link, data []syscall.NetlinkRouteAttr) {
+ br := bridge.(*Bridge)
+ for _, datum := range data {
+ switch datum.Attr.Type {
+ case nl.IFLA_BR_HELLO_TIME:
+ helloTime := native.Uint32(datum.Value[0:4])
+ br.HelloTime = &helloTime
+ case nl.IFLA_BR_MCAST_SNOOPING:
+ mcastSnooping := datum.Value[0] == 1
+ br.MulticastSnooping = &mcastSnooping
+ }
+ }
+}
+
+func addGTPAttrs(gtp *GTP, linkInfo *nl.RtAttr) {
+ data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+ nl.NewRtAttrChild(data, nl.IFLA_GTP_FD0, nl.Uint32Attr(uint32(gtp.FD0)))
+ nl.NewRtAttrChild(data, nl.IFLA_GTP_FD1, nl.Uint32Attr(uint32(gtp.FD1)))
+ nl.NewRtAttrChild(data, nl.IFLA_GTP_PDP_HASHSIZE, nl.Uint32Attr(131072))
+ if gtp.Role != nl.GTP_ROLE_GGSN {
+ nl.NewRtAttrChild(data, nl.IFLA_GTP_ROLE, nl.Uint32Attr(uint32(gtp.Role)))
+ }
+}
+
+func parseGTPData(link Link, data []syscall.NetlinkRouteAttr) {
+ gtp := link.(*GTP)
+ for _, datum := range data {
+ switch datum.Attr.Type {
+ case nl.IFLA_GTP_FD0:
+ gtp.FD0 = int(native.Uint32(datum.Value))
+ case nl.IFLA_GTP_FD1:
+ gtp.FD1 = int(native.Uint32(datum.Value))
+ case nl.IFLA_GTP_PDP_HASHSIZE:
+ gtp.PDPHashsize = int(native.Uint32(datum.Value))
+ case nl.IFLA_GTP_ROLE:
+ gtp.Role = int(native.Uint32(datum.Value))
+ }
+ }
+}
return ErrNotImplemented
}
-func LinkSetMaster(link Link, master *Link) error {
+func LinkSetMaster(link Link, master *Bridge) error {
return ErrNotImplemented
}
return ErrNotImplemented
}
+func LinkSetARPOff(link Link) error {
+ return ErrNotImplemented
+}
+
+func LinkSetARPOn(link Link) error {
+ return ErrNotImplemented
+}
+
func LinkByName(name string) (Link, error) {
return nil, ErrNotImplemented
}
func (msg *IfAddrmsg) Len() int {
return syscall.SizeofIfAddrmsg
}
+
+// struct ifa_cacheinfo {
+// __u32 ifa_prefered;
+// __u32 ifa_valid;
+// __u32 cstamp; /* created timestamp, hundredths of seconds */
+// __u32 tstamp; /* updated timestamp, hundredths of seconds */
+// };
+
+const IFA_CACHEINFO = 6
+const SizeofIfaCacheInfo = 0x10
+
+type IfaCacheInfo struct {
+ IfaPrefered uint32
+ IfaValid uint32
+ Cstamp uint32
+ Tstamp uint32
+}
+
+func (msg *IfaCacheInfo) Len() int {
+ return SizeofIfaCacheInfo
+}
+
+func DeserializeIfaCacheInfo(b []byte) *IfaCacheInfo {
+ return (*IfaCacheInfo)(unsafe.Pointer(&b[0:SizeofIfaCacheInfo][0]))
+}
+
+func (msg *IfaCacheInfo) Serialize() []byte {
+ return (*(*[SizeofIfaCacheInfo]byte)(unsafe.Pointer(msg)))[:]
+}
--- /dev/null
+package nl
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+const (
+ SizeofBridgeVlanInfo = 0x04
+)
+
+/* Bridge Flags */
+const (
+ BRIDGE_FLAGS_MASTER = iota /* Bridge command to/from master */
+ BRIDGE_FLAGS_SELF /* Bridge command to/from lowerdev */
+)
+
+/* Bridge management nested attributes
+ * [IFLA_AF_SPEC] = {
+ * [IFLA_BRIDGE_FLAGS]
+ * [IFLA_BRIDGE_MODE]
+ * [IFLA_BRIDGE_VLAN_INFO]
+ * }
+ */
+const (
+ IFLA_BRIDGE_FLAGS = iota
+ IFLA_BRIDGE_MODE
+ IFLA_BRIDGE_VLAN_INFO
+)
+
+const (
+ BRIDGE_VLAN_INFO_MASTER = 1 << iota
+ BRIDGE_VLAN_INFO_PVID
+ BRIDGE_VLAN_INFO_UNTAGGED
+ BRIDGE_VLAN_INFO_RANGE_BEGIN
+ BRIDGE_VLAN_INFO_RANGE_END
+)
+
+// struct bridge_vlan_info {
+// __u16 flags;
+// __u16 vid;
+// };
+
+type BridgeVlanInfo struct {
+ Flags uint16
+ Vid uint16
+}
+
+func (b *BridgeVlanInfo) Serialize() []byte {
+ return (*(*[SizeofBridgeVlanInfo]byte)(unsafe.Pointer(b)))[:]
+}
+
+func DeserializeBridgeVlanInfo(b []byte) *BridgeVlanInfo {
+ return (*BridgeVlanInfo)(unsafe.Pointer(&b[0:SizeofBridgeVlanInfo][0]))
+}
+
+func (b *BridgeVlanInfo) PortVID() bool {
+ return b.Flags&BRIDGE_VLAN_INFO_PVID > 0
+}
+
+func (b *BridgeVlanInfo) EngressUntag() bool {
+ return b.Flags&BRIDGE_VLAN_INFO_UNTAGGED > 0
+}
+
+func (b *BridgeVlanInfo) String() string {
+ return fmt.Sprintf("%+v", *b)
+}
+
+/* New extended info filters for IFLA_EXT_MASK */
+const (
+ RTEXT_FILTER_VF = 1 << iota
+ RTEXT_FILTER_BRVLAN
+ RTEXT_FILTER_BRVLAN_COMPRESSED
+)
--- /dev/null
+package nl
+
+import "unsafe"
+
+// Track the message sizes for the correct serialization/deserialization
+const (
+ SizeofNfgenmsg = 4
+ SizeofNfattr = 4
+ SizeofNfConntrack = 376
+ SizeofNfctTupleHead = 52
+)
+
+var L4ProtoMap = map[uint8]string{
+ 6: "tcp",
+ 17: "udp",
+}
+
+// All the following constants are coming from:
+// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+
+// enum cntl_msg_types {
+// IPCTNL_MSG_CT_NEW,
+// IPCTNL_MSG_CT_GET,
+// IPCTNL_MSG_CT_DELETE,
+// IPCTNL_MSG_CT_GET_CTRZERO,
+// IPCTNL_MSG_CT_GET_STATS_CPU,
+// IPCTNL_MSG_CT_GET_STATS,
+// IPCTNL_MSG_CT_GET_DYING,
+// IPCTNL_MSG_CT_GET_UNCONFIRMED,
+//
+// IPCTNL_MSG_MAX
+// };
+const (
+ IPCTNL_MSG_CT_GET = 1
+ IPCTNL_MSG_CT_DELETE = 2
+)
+
+// #define NFNETLINK_V0 0
+const (
+ NFNETLINK_V0 = 0
+)
+
+// #define NLA_F_NESTED (1 << 15)
+const (
+ NLA_F_NESTED = (1 << 15)
+)
+
+// enum ctattr_type {
+// CTA_UNSPEC,
+// CTA_TUPLE_ORIG,
+// CTA_TUPLE_REPLY,
+// CTA_STATUS,
+// CTA_PROTOINFO,
+// CTA_HELP,
+// CTA_NAT_SRC,
+// #define CTA_NAT CTA_NAT_SRC /* backwards compatibility */
+// CTA_TIMEOUT,
+// CTA_MARK,
+// CTA_COUNTERS_ORIG,
+// CTA_COUNTERS_REPLY,
+// CTA_USE,
+// CTA_ID,
+// CTA_NAT_DST,
+// CTA_TUPLE_MASTER,
+// CTA_SEQ_ADJ_ORIG,
+// CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG,
+// CTA_SEQ_ADJ_REPLY,
+// CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY,
+// CTA_SECMARK, /* obsolete */
+// CTA_ZONE,
+// CTA_SECCTX,
+// CTA_TIMESTAMP,
+// CTA_MARK_MASK,
+// CTA_LABELS,
+// CTA_LABELS_MASK,
+// __CTA_MAX
+// };
+const (
+ CTA_TUPLE_ORIG = 1
+ CTA_TUPLE_REPLY = 2
+ CTA_STATUS = 3
+ CTA_TIMEOUT = 8
+ CTA_MARK = 9
+ CTA_PROTOINFO = 4
+)
+
+// enum ctattr_tuple {
+// CTA_TUPLE_UNSPEC,
+// CTA_TUPLE_IP,
+// CTA_TUPLE_PROTO,
+// CTA_TUPLE_ZONE,
+// __CTA_TUPLE_MAX
+// };
+// #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
+const (
+ CTA_TUPLE_IP = 1
+ CTA_TUPLE_PROTO = 2
+)
+
+// enum ctattr_ip {
+// CTA_IP_UNSPEC,
+// CTA_IP_V4_SRC,
+// CTA_IP_V4_DST,
+// CTA_IP_V6_SRC,
+// CTA_IP_V6_DST,
+// __CTA_IP_MAX
+// };
+// #define CTA_IP_MAX (__CTA_IP_MAX - 1)
+const (
+ CTA_IP_V4_SRC = 1
+ CTA_IP_V4_DST = 2
+ CTA_IP_V6_SRC = 3
+ CTA_IP_V6_DST = 4
+)
+
+// enum ctattr_l4proto {
+// CTA_PROTO_UNSPEC,
+// CTA_PROTO_NUM,
+// CTA_PROTO_SRC_PORT,
+// CTA_PROTO_DST_PORT,
+// CTA_PROTO_ICMP_ID,
+// CTA_PROTO_ICMP_TYPE,
+// CTA_PROTO_ICMP_CODE,
+// CTA_PROTO_ICMPV6_ID,
+// CTA_PROTO_ICMPV6_TYPE,
+// CTA_PROTO_ICMPV6_CODE,
+// __CTA_PROTO_MAX
+// };
+// #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
+const (
+ CTA_PROTO_NUM = 1
+ CTA_PROTO_SRC_PORT = 2
+ CTA_PROTO_DST_PORT = 3
+)
+
+// enum ctattr_protoinfo {
+// CTA_PROTOINFO_UNSPEC,
+// CTA_PROTOINFO_TCP,
+// CTA_PROTOINFO_DCCP,
+// CTA_PROTOINFO_SCTP,
+// __CTA_PROTOINFO_MAX
+// };
+// #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
+const (
+ CTA_PROTOINFO_TCP = 1
+)
+
+// enum ctattr_protoinfo_tcp {
+// CTA_PROTOINFO_TCP_UNSPEC,
+// CTA_PROTOINFO_TCP_STATE,
+// CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+// CTA_PROTOINFO_TCP_WSCALE_REPLY,
+// CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+// CTA_PROTOINFO_TCP_FLAGS_REPLY,
+// __CTA_PROTOINFO_TCP_MAX
+// };
+// #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+const (
+ CTA_PROTOINFO_TCP_STATE = 1
+ CTA_PROTOINFO_TCP_WSCALE_ORIGINAL = 2
+ CTA_PROTOINFO_TCP_WSCALE_REPLY = 3
+ CTA_PROTOINFO_TCP_FLAGS_ORIGINAL = 4
+ CTA_PROTOINFO_TCP_FLAGS_REPLY = 5
+)
+
+// /* General form of address family dependent message.
+// */
+// struct nfgenmsg {
+// __u8 nfgen_family; /* AF_xxx */
+// __u8 version; /* nfnetlink version */
+// __be16 res_id; /* resource id */
+// };
+type Nfgenmsg struct {
+ NfgenFamily uint8
+ Version uint8
+ ResId uint16 // big endian
+}
+
+func (msg *Nfgenmsg) Len() int {
+ return SizeofNfgenmsg
+}
+
+func DeserializeNfgenmsg(b []byte) *Nfgenmsg {
+ return (*Nfgenmsg)(unsafe.Pointer(&b[0:SizeofNfgenmsg][0]))
+}
+
+func (msg *Nfgenmsg) Serialize() []byte {
+ return (*(*[SizeofNfgenmsg]byte)(unsafe.Pointer(msg)))[:]
+}
--- /dev/null
+package nl
+
+import (
+ "unsafe"
+)
+
+const SizeofGenlmsg = 4
+
+const (
+ GENL_ID_CTRL = 0x10
+ GENL_CTRL_VERSION = 2
+ GENL_CTRL_NAME = "nlctrl"
+)
+
+const (
+ GENL_CTRL_CMD_GETFAMILY = 3
+)
+
+const (
+ GENL_CTRL_ATTR_UNSPEC = iota
+ GENL_CTRL_ATTR_FAMILY_ID
+ GENL_CTRL_ATTR_FAMILY_NAME
+ GENL_CTRL_ATTR_VERSION
+ GENL_CTRL_ATTR_HDRSIZE
+ GENL_CTRL_ATTR_MAXATTR
+ GENL_CTRL_ATTR_OPS
+ GENL_CTRL_ATTR_MCAST_GROUPS
+)
+
+const (
+ GENL_CTRL_ATTR_OP_UNSPEC = iota
+ GENL_CTRL_ATTR_OP_ID
+ GENL_CTRL_ATTR_OP_FLAGS
+)
+
+const (
+ GENL_ADMIN_PERM = 1 << iota
+ GENL_CMD_CAP_DO
+ GENL_CMD_CAP_DUMP
+ GENL_CMD_CAP_HASPOL
+)
+
+const (
+ GENL_CTRL_ATTR_MCAST_GRP_UNSPEC = iota
+ GENL_CTRL_ATTR_MCAST_GRP_NAME
+ GENL_CTRL_ATTR_MCAST_GRP_ID
+)
+
+const (
+ GENL_GTP_VERSION = 0
+ GENL_GTP_NAME = "gtp"
+)
+
+const (
+ GENL_GTP_CMD_NEWPDP = iota
+ GENL_GTP_CMD_DELPDP
+ GENL_GTP_CMD_GETPDP
+)
+
+const (
+ GENL_GTP_ATTR_UNSPEC = iota
+ GENL_GTP_ATTR_LINK
+ GENL_GTP_ATTR_VERSION
+ GENL_GTP_ATTR_TID
+ GENL_GTP_ATTR_PEER_ADDRESS
+ GENL_GTP_ATTR_MS_ADDRESS
+ GENL_GTP_ATTR_FLOW
+ GENL_GTP_ATTR_NET_NS_FD
+ GENL_GTP_ATTR_I_TEI
+ GENL_GTP_ATTR_O_TEI
+ GENL_GTP_ATTR_PAD
+)
+
+type Genlmsg struct {
+ Command uint8
+ Version uint8
+}
+
+func (msg *Genlmsg) Len() int {
+ return SizeofGenlmsg
+}
+
+func DeserializeGenlmsg(b []byte) *Genlmsg {
+ return (*Genlmsg)(unsafe.Pointer(&b[0:SizeofGenlmsg][0]))
+}
+
+func (msg *Genlmsg) Serialize() []byte {
+ return (*(*[SizeofGenlmsg]byte)(unsafe.Pointer(msg)))[:]
+}
IFLA_BRPORT_FAST_LEAVE
IFLA_BRPORT_LEARNING
IFLA_BRPORT_UNICAST_FLOOD
- IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD
+ IFLA_BRPORT_PROXYARP
+ IFLA_BRPORT_LEARNING_SYNC
+ IFLA_BRPORT_PROXYARP_WIFI
+ IFLA_BRPORT_MAX = IFLA_BRPORT_PROXYARP_WIFI
)
const (
IFLA_BOND_AD_LACP_RATE
IFLA_BOND_AD_SELECT
IFLA_BOND_AD_INFO
+ IFLA_BOND_AD_ACTOR_SYS_PRIO
+ IFLA_BOND_AD_USER_PORT_KEY
+ IFLA_BOND_AD_ACTOR_SYSTEM
+ IFLA_BOND_TLB_DYNAMIC_LB
)
const (
IFLA_XDP_UNSPEC = iota
IFLA_XDP_FD /* fd of xdp program to attach, or -1 to remove */
IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */
- IFLA_XDP_MAX = IFLA_XDP_ATTACHED
+ IFLA_XDP_FLAGS /* xdp prog related flags */
+ IFLA_XDP_MAX = IFLA_XDP_FLAGS
)
const (
IFLA_VRF_UNSPEC = iota
IFLA_VRF_TABLE
)
+
+const (
+ IFLA_BR_UNSPEC = iota
+ IFLA_BR_FORWARD_DELAY
+ IFLA_BR_HELLO_TIME
+ IFLA_BR_MAX_AGE
+ IFLA_BR_AGEING_TIME
+ IFLA_BR_STP_STATE
+ IFLA_BR_PRIORITY
+ IFLA_BR_VLAN_FILTERING
+ IFLA_BR_VLAN_PROTOCOL
+ IFLA_BR_GROUP_FWD_MASK
+ IFLA_BR_ROOT_ID
+ IFLA_BR_BRIDGE_ID
+ IFLA_BR_ROOT_PORT
+ IFLA_BR_ROOT_PATH_COST
+ IFLA_BR_TOPOLOGY_CHANGE
+ IFLA_BR_TOPOLOGY_CHANGE_DETECTED
+ IFLA_BR_HELLO_TIMER
+ IFLA_BR_TCN_TIMER
+ IFLA_BR_TOPOLOGY_CHANGE_TIMER
+ IFLA_BR_GC_TIMER
+ IFLA_BR_GROUP_ADDR
+ IFLA_BR_FDB_FLUSH
+ IFLA_BR_MCAST_ROUTER
+ IFLA_BR_MCAST_SNOOPING
+ IFLA_BR_MCAST_QUERY_USE_IFADDR
+ IFLA_BR_MCAST_QUERIER
+ IFLA_BR_MCAST_HASH_ELASTICITY
+ IFLA_BR_MCAST_HASH_MAX
+ IFLA_BR_MCAST_LAST_MEMBER_CNT
+ IFLA_BR_MCAST_STARTUP_QUERY_CNT
+ IFLA_BR_MCAST_LAST_MEMBER_INTVL
+ IFLA_BR_MCAST_MEMBERSHIP_INTVL
+ IFLA_BR_MCAST_QUERIER_INTVL
+ IFLA_BR_MCAST_QUERY_INTVL
+ IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
+ IFLA_BR_MCAST_STARTUP_QUERY_INTVL
+ IFLA_BR_NF_CALL_IPTABLES
+ IFLA_BR_NF_CALL_IP6TABLES
+ IFLA_BR_NF_CALL_ARPTABLES
+ IFLA_BR_VLAN_DEFAULT_PVID
+ IFLA_BR_PAD
+ IFLA_BR_VLAN_STATS_ENABLED
+ IFLA_BR_MCAST_STATS_ENABLED
+ IFLA_BR_MCAST_IGMP_VERSION
+ IFLA_BR_MCAST_MLD_VERSION
+ IFLA_BR_MAX = IFLA_BR_MCAST_MLD_VERSION
+)
+
+const (
+ IFLA_GTP_UNSPEC = iota
+ IFLA_GTP_FD0
+ IFLA_GTP_FD1
+ IFLA_GTP_PDP_HASHSIZE
+ IFLA_GTP_ROLE
+)
+
+const (
+ GTP_ROLE_GGSN = iota
+ GTP_ROLE_SGSN
+)
)
// SupportedNlFamilies contains the list of netlink families this netlink package supports
-var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM}
+var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM, syscall.NETLINK_NETFILTER}
var nextSeqNr uint32
type NetlinkRequest struct {
syscall.NlMsghdr
Data []NetlinkRequestData
+ RawData []byte
Sockets map[int]*SocketHandle
}
dataBytes[i] = data.Serialize()
length = length + len(dataBytes[i])
}
+ length += len(req.RawData)
+
req.Len = uint32(length)
b := make([]byte, length)
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next = next + 1
}
}
+ // Add the raw data if any
+ if len(req.RawData) > 0 {
+ copy(b[next:length], req.RawData)
+ }
return b
}
}
}
+// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization
+func (req *NetlinkRequest) AddRawData(data []byte) {
+ if data != nil {
+ req.RawData = append(req.RawData, data...)
+ }
+}
+
// Execute the request against a the given sockType.
// Returns a list of netlink messages in serialized format, optionally filtered
// by resType.
}
type NetlinkSocket struct {
- fd int
+ fd int32
lsa syscall.SockaddrNetlink
sync.Mutex
}
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
- fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
+ fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, protocol)
if err != nil {
return nil, err
}
s := &NetlinkSocket{
- fd: fd,
+ fd: int32(fd),
}
s.lsa.Family = syscall.AF_NETLINK
if err := syscall.Bind(fd, &s.lsa); err != nil {
return nil, err
}
s := &NetlinkSocket{
- fd: fd,
+ fd: int32(fd),
}
s.lsa.Family = syscall.AF_NETLINK
}
func (s *NetlinkSocket) Close() {
- syscall.Close(s.fd)
- s.fd = -1
+ fd := int(atomic.SwapInt32(&s.fd, -1))
+ syscall.Close(fd)
}
func (s *NetlinkSocket) GetFd() int {
- return s.fd
+ return int(atomic.LoadInt32(&s.fd))
}
func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
- if s.fd < 0 {
+ fd := int(atomic.LoadInt32(&s.fd))
+ if fd < 0 {
return fmt.Errorf("Send called on a closed socket")
}
- if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
+ if err := syscall.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
return err
}
return nil
}
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
- if s.fd < 0 {
+ fd := int(atomic.LoadInt32(&s.fd))
+ if fd < 0 {
return nil, fmt.Errorf("Receive called on a closed socket")
}
rb := make([]byte, syscall.Getpagesize())
- nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
+ nr, _, err := syscall.Recvfrom(fd, rb, 0)
if err != nil {
return nil, err
}
}
func (s *NetlinkSocket) GetPid() (uint32, error) {
- lsa, err := syscall.Getsockname(s.fd)
+ fd := int(atomic.LoadInt32(&s.fd))
+ lsa, err := syscall.Getsockname(fd)
if err != nil {
return 0, err
}
// Protinfo represents bridge flags from netlink.
type Protinfo struct {
- Hairpin bool
- Guard bool
- FastLeave bool
- RootBlock bool
- Learning bool
- Flood bool
+ Hairpin bool
+ Guard bool
+ FastLeave bool
+ RootBlock bool
+ Learning bool
+ Flood bool
+ ProxyArp bool
+ ProxyArpWiFi bool
}
// String returns a list of enabled flags
if prot.Flood {
boolStrings = append(boolStrings, "Flood")
}
+ if prot.ProxyArp {
+ boolStrings = append(boolStrings, "ProxyArp")
+ }
+ if prot.ProxyArpWiFi {
+ boolStrings = append(boolStrings, "ProxyArpWiFi")
+ }
return strings.Join(boolStrings, " ")
}
pi.Learning = byteToBool(info.Value[0])
case nl.IFLA_BRPORT_UNICAST_FLOOD:
pi.Flood = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_PROXYARP:
+ pi.ProxyArp = byteToBool(info.Value[0])
+ case nl.IFLA_BRPORT_PROXYARP_WIFI:
+ pi.ProxyArpWiFi = byteToBool(info.Value[0])
}
}
return &pi
}
if filter != nil {
switch {
- case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
+ case filterMask&RT_FILTER_TABLE != 0 && filter.Table != syscall.RT_TABLE_UNSPEC && route.Table != filter.Table:
continue
case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
continue
// Create a new network namespace
newns, _ := netns.New()
+ netns.Set(newns)
defer newns.Close()
// Do something with the network namespace
// Equal determines if two network handles refer to the same network
// namespace. This is done by comparing the device and inode that the
-// file descripors point to.
+// file descriptors point to.
func (ns NsHandle) Equal(other NsHandle) bool {
if ns == other {
return true
"amd64": 308,
"arm64": 268,
"arm": 375,
+ "mips": 4344,
+ "mipsle": 4344,
"ppc64": 350,
"ppc64le": 350,
"s390x": 339,
return -1, ErrNotImplemented
}
+func GetFromPath(path string) (NsHandle, error) {
+ return -1, ErrNotImplemented
+}
+
func GetFromName(name string) (NsHandle, error) {
return -1, ErrNotImplemented
}
return -1, ErrNotImplemented
}
+func GetFromThread(pid, tid int) (NsHandle, error) {
+ return -1, ErrNotImplemented
+}
+
func GetFromDocker(id string) (NsHandle, error) {
return -1, ErrNotImplemented
}