Carlos de Paula d49011702b Refactor faasd and faas-containerd merge
The use of containerd and CNI functions has been refactored to reuse
the same codebase.

Added all network functionality to own directory and package. Removed
netlink and weave library in favor of using CNI plugin result files.

Rename containers handler to functions to clear-up functionality.

Signed-off-by: Carlos de Paula <me@carlosedp.com>
2020-02-04 10:12:43 +00:00

1082 lines
28 KiB
Go

package netlink
import (
"fmt"
"net"
"strings"
"syscall"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)
// RtAttr is shared so it is in netlink_linux.go
const (
SCOPE_UNIVERSE Scope = unix.RT_SCOPE_UNIVERSE
SCOPE_SITE Scope = unix.RT_SCOPE_SITE
SCOPE_LINK Scope = unix.RT_SCOPE_LINK
SCOPE_HOST Scope = unix.RT_SCOPE_HOST
SCOPE_NOWHERE Scope = unix.RT_SCOPE_NOWHERE
)
const (
RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
RT_FILTER_SCOPE
RT_FILTER_TYPE
RT_FILTER_TOS
RT_FILTER_IIF
RT_FILTER_OIF
RT_FILTER_DST
RT_FILTER_SRC
RT_FILTER_GW
RT_FILTER_TABLE
RT_FILTER_HOPLIMIT
)
const (
FLAG_ONLINK NextHopFlag = unix.RTNH_F_ONLINK
FLAG_PERVASIVE NextHopFlag = unix.RTNH_F_PERVASIVE
)
var testFlags = []flagString{
{f: FLAG_ONLINK, s: "onlink"},
{f: FLAG_PERVASIVE, s: "pervasive"},
}
func listFlags(flag int) []string {
var flags []string
for _, tf := range testFlags {
if flag&int(tf.f) != 0 {
flags = append(flags, tf.s)
}
}
return flags
}
func (r *Route) ListFlags() []string {
return listFlags(r.Flags)
}
func (n *NexthopInfo) ListFlags() []string {
return listFlags(n.Flags)
}
type MPLSDestination struct {
Labels []int
}
func (d *MPLSDestination) Family() int {
return nl.FAMILY_MPLS
}
func (d *MPLSDestination) Decode(buf []byte) error {
d.Labels = nl.DecodeMPLSStack(buf)
return nil
}
func (d *MPLSDestination) Encode() ([]byte, error) {
return nl.EncodeMPLSStack(d.Labels...), nil
}
func (d *MPLSDestination) String() string {
s := make([]string, 0, len(d.Labels))
for _, l := range d.Labels {
s = append(s, fmt.Sprintf("%d", l))
}
return strings.Join(s, "/")
}
func (d *MPLSDestination) Equal(x Destination) bool {
o, ok := x.(*MPLSDestination)
if !ok {
return false
}
if d == nil && o == nil {
return true
}
if d == nil || o == nil {
return false
}
if d.Labels == nil && o.Labels == nil {
return true
}
if d.Labels == nil || o.Labels == nil {
return false
}
if len(d.Labels) != len(o.Labels) {
return false
}
for i := range d.Labels {
if d.Labels[i] != o.Labels[i] {
return false
}
}
return true
}
type MPLSEncap struct {
Labels []int
}
func (e *MPLSEncap) Type() int {
return nl.LWTUNNEL_ENCAP_MPLS
}
func (e *MPLSEncap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("lack of bytes")
}
buf = buf[:l]
typ := native.Uint16(buf[2:])
if typ != nl.MPLS_IPTUNNEL_DST {
return fmt.Errorf("unknown MPLS Encap Type: %d", typ)
}
e.Labels = nl.DecodeMPLSStack(buf[4:])
return nil
}
func (e *MPLSEncap) Encode() ([]byte, error) {
s := nl.EncodeMPLSStack(e.Labels...)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.MPLS_IPTUNNEL_DST)
return append(hdr, s...), nil
}
func (e *MPLSEncap) String() string {
s := make([]string, 0, len(e.Labels))
for _, l := range e.Labels {
s = append(s, fmt.Sprintf("%d", l))
}
return strings.Join(s, "/")
}
func (e *MPLSEncap) Equal(x Encap) bool {
o, ok := x.(*MPLSEncap)
if !ok {
return false
}
if e == nil && o == nil {
return true
}
if e == nil || o == nil {
return false
}
if e.Labels == nil && o.Labels == nil {
return true
}
if e.Labels == nil || o.Labels == nil {
return false
}
if len(e.Labels) != len(o.Labels) {
return false
}
for i := range e.Labels {
if e.Labels[i] != o.Labels[i] {
return false
}
}
return true
}
// SEG6 definitions
type SEG6Encap struct {
Mode int
Segments []net.IP
}
func (e *SEG6Encap) Type() int {
return nl.LWTUNNEL_ENCAP_SEG6
}
func (e *SEG6Encap) Decode(buf []byte) error {
if len(buf) < 4 {
return fmt.Errorf("lack of bytes")
}
native := nl.NativeEndian()
// Get Length(l) & Type(typ) : 2 + 2 bytes
l := native.Uint16(buf)
if len(buf) < int(l) {
return fmt.Errorf("lack of bytes")
}
buf = buf[:l] // make sure buf size upper limit is Length
typ := native.Uint16(buf[2:])
// LWTUNNEL_ENCAP_SEG6 has only one attr type SEG6_IPTUNNEL_SRH
if typ != nl.SEG6_IPTUNNEL_SRH {
return fmt.Errorf("unknown SEG6 Type: %d", typ)
}
var err error
e.Mode, e.Segments, err = nl.DecodeSEG6Encap(buf[4:])
return err
}
func (e *SEG6Encap) Encode() ([]byte, error) {
s, err := nl.EncodeSEG6Encap(e.Mode, e.Segments)
native := nl.NativeEndian()
hdr := make([]byte, 4)
native.PutUint16(hdr, uint16(len(s)+4))
native.PutUint16(hdr[2:], nl.SEG6_IPTUNNEL_SRH)
return append(hdr, s...), err
}
func (e *SEG6Encap) String() string {
segs := make([]string, 0, len(e.Segments))
// append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
}
str := fmt.Sprintf("mode %s segs %d [ %s ]", nl.SEG6EncapModeString(e.Mode),
len(e.Segments), strings.Join(segs, " "))
return str
}
func (e *SEG6Encap) Equal(x Encap) bool {
o, ok := x.(*SEG6Encap)
if !ok {
return false
}
if e == o {
return true
}
if e == nil || o == nil {
return false
}
if e.Mode != o.Mode {
return false
}
if len(e.Segments) != len(o.Segments) {
return false
}
for i := range e.Segments {
if !e.Segments[i].Equal(o.Segments[i]) {
return false
}
}
return true
}
// SEG6LocalEncap definitions
type SEG6LocalEncap struct {
Flags [nl.SEG6_LOCAL_MAX]bool
Action int
Segments []net.IP // from SRH in seg6_local_lwt
Table int // table id for End.T and End.DT6
InAddr net.IP
In6Addr net.IP
Iif int
Oif int
}
func (e *SEG6LocalEncap) Type() int {
return nl.LWTUNNEL_ENCAP_SEG6_LOCAL
}
func (e *SEG6LocalEncap) Decode(buf []byte) error {
attrs, err := nl.ParseRouteAttr(buf)
if err != nil {
return err
}
native := nl.NativeEndian()
for _, attr := range attrs {
switch attr.Attr.Type {
case nl.SEG6_LOCAL_ACTION:
e.Action = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_ACTION] = true
case nl.SEG6_LOCAL_SRH:
e.Segments, err = nl.DecodeSEG6Srh(attr.Value[:])
e.Flags[nl.SEG6_LOCAL_SRH] = true
case nl.SEG6_LOCAL_TABLE:
e.Table = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_TABLE] = true
case nl.SEG6_LOCAL_NH4:
e.InAddr = net.IP(attr.Value[0:4])
e.Flags[nl.SEG6_LOCAL_NH4] = true
case nl.SEG6_LOCAL_NH6:
e.In6Addr = net.IP(attr.Value[0:16])
e.Flags[nl.SEG6_LOCAL_NH6] = true
case nl.SEG6_LOCAL_IIF:
e.Iif = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_IIF] = true
case nl.SEG6_LOCAL_OIF:
e.Oif = int(native.Uint32(attr.Value[0:4]))
e.Flags[nl.SEG6_LOCAL_OIF] = true
}
}
return err
}
func (e *SEG6LocalEncap) Encode() ([]byte, error) {
var err error
native := nl.NativeEndian()
res := make([]byte, 8)
native.PutUint16(res, 8) // length
native.PutUint16(res[2:], nl.SEG6_LOCAL_ACTION)
native.PutUint32(res[4:], uint32(e.Action))
if e.Flags[nl.SEG6_LOCAL_SRH] {
srh, err := nl.EncodeSEG6Srh(e.Segments)
if err != nil {
return nil, err
}
attr := make([]byte, 4)
native.PutUint16(attr, uint16(len(srh)+4))
native.PutUint16(attr[2:], nl.SEG6_LOCAL_SRH)
attr = append(attr, srh...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_TABLE] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_TABLE)
native.PutUint32(attr[4:], uint32(e.Table))
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_NH4] {
attr := make([]byte, 4)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH4)
ipv4 := e.InAddr.To4()
if ipv4 == nil {
err = fmt.Errorf("SEG6_LOCAL_NH4 has invalid IPv4 address")
return nil, err
}
attr = append(attr, ipv4...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_NH6] {
attr := make([]byte, 4)
native.PutUint16(attr, 20)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_NH6)
attr = append(attr, e.In6Addr...)
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_IIF] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_IIF)
native.PutUint32(attr[4:], uint32(e.Iif))
res = append(res, attr...)
}
if e.Flags[nl.SEG6_LOCAL_OIF] {
attr := make([]byte, 8)
native.PutUint16(attr, 8)
native.PutUint16(attr[2:], nl.SEG6_LOCAL_OIF)
native.PutUint32(attr[4:], uint32(e.Oif))
res = append(res, attr...)
}
return res, err
}
func (e *SEG6LocalEncap) String() string {
strs := make([]string, 0, nl.SEG6_LOCAL_MAX)
strs = append(strs, fmt.Sprintf("action %s", nl.SEG6LocalActionString(e.Action)))
if e.Flags[nl.SEG6_LOCAL_TABLE] {
strs = append(strs, fmt.Sprintf("table %d", e.Table))
}
if e.Flags[nl.SEG6_LOCAL_NH4] {
strs = append(strs, fmt.Sprintf("nh4 %s", e.InAddr))
}
if e.Flags[nl.SEG6_LOCAL_NH6] {
strs = append(strs, fmt.Sprintf("nh6 %s", e.In6Addr))
}
if e.Flags[nl.SEG6_LOCAL_IIF] {
link, err := LinkByIndex(e.Iif)
if err != nil {
strs = append(strs, fmt.Sprintf("iif %d", e.Iif))
} else {
strs = append(strs, fmt.Sprintf("iif %s", link.Attrs().Name))
}
}
if e.Flags[nl.SEG6_LOCAL_OIF] {
link, err := LinkByIndex(e.Oif)
if err != nil {
strs = append(strs, fmt.Sprintf("oif %d", e.Oif))
} else {
strs = append(strs, fmt.Sprintf("oif %s", link.Attrs().Name))
}
}
if e.Flags[nl.SEG6_LOCAL_SRH] {
segs := make([]string, 0, len(e.Segments))
//append segment backwards (from n to 0) since seg#0 is the last segment.
for i := len(e.Segments); i > 0; i-- {
segs = append(segs, fmt.Sprintf("%s", e.Segments[i-1]))
}
strs = append(strs, fmt.Sprintf("segs %d [ %s ]", len(e.Segments), strings.Join(segs, " ")))
}
return strings.Join(strs, " ")
}
func (e *SEG6LocalEncap) Equal(x Encap) bool {
o, ok := x.(*SEG6LocalEncap)
if !ok {
return false
}
if e == o {
return true
}
if e == nil || o == nil {
return false
}
// compare all arrays first
for i := range e.Flags {
if e.Flags[i] != o.Flags[i] {
return false
}
}
if len(e.Segments) != len(o.Segments) {
return false
}
for i := range e.Segments {
if !e.Segments[i].Equal(o.Segments[i]) {
return false
}
}
// compare values
if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) {
return false
}
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif {
return false
}
return true
}
// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func RouteAdd(route *Route) error {
return pkgHandle.RouteAdd(route)
}
// RouteAdd will add a route to the system.
// Equivalent to: `ip route add $route`
func (h *Handle) RouteAdd(route *Route) error {
flags := unix.NLM_F_CREATE | unix.NLM_F_EXCL | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
// RouteReplace will add a route to the system.
// Equivalent to: `ip route replace $route`
func RouteReplace(route *Route) error {
return pkgHandle.RouteReplace(route)
}
// RouteReplace will add a route to the system.
// Equivalent to: `ip route replace $route`
func (h *Handle) RouteReplace(route *Route) error {
flags := unix.NLM_F_CREATE | unix.NLM_F_REPLACE | unix.NLM_F_ACK
req := h.newNetlinkRequest(unix.RTM_NEWROUTE, flags)
return h.routeHandle(route, req, nl.NewRtMsg())
}
// RouteDel will delete a route from the system.
// Equivalent to: `ip route del $route`
func RouteDel(route *Route) error {
return pkgHandle.RouteDel(route)
}
// RouteDel will delete a route from the system.
// Equivalent to: `ip route del $route`
func (h *Handle) RouteDel(route *Route) error {
req := h.newNetlinkRequest(unix.RTM_DELROUTE, unix.NLM_F_ACK)
return h.routeHandle(route, req, nl.NewRtDelMsg())
}
func (h *Handle) routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil && route.MPLSDst == nil {
return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
}
family := -1
var rtAttrs []*nl.RtAttr
if route.Dst != nil && route.Dst.IP != nil {
dstLen, _ := route.Dst.Mask.Size()
msg.Dst_len = uint8(dstLen)
dstFamily := nl.GetIPFamily(route.Dst.IP)
family = dstFamily
var dstData []byte
if dstFamily == FAMILY_V4 {
dstData = route.Dst.IP.To4()
} else {
dstData = route.Dst.IP.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, dstData))
} else if route.MPLSDst != nil {
family = nl.FAMILY_MPLS
msg.Dst_len = uint8(20)
msg.Type = unix.RTN_UNICAST
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, nl.EncodeMPLSStack(*route.MPLSDst)))
}
if route.NewDst != nil {
if family != -1 && family != route.NewDst.Family() {
return fmt.Errorf("new destination and destination are not the same address family")
}
buf, err := route.NewDst.Encode()
if err != nil {
return err
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_NEWDST, buf))
}
if route.Encap != nil {
buf := make([]byte, 2)
native.PutUint16(buf, uint16(route.Encap.Type()))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP_TYPE, buf))
buf, err := route.Encap.Encode()
if err != nil {
return err
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_ENCAP, buf))
}
if route.Src != nil {
srcFamily := nl.GetIPFamily(route.Src)
if family != -1 && family != srcFamily {
return fmt.Errorf("source and destination ip are not the same IP family")
}
family = srcFamily
var srcData []byte
if srcFamily == FAMILY_V4 {
srcData = route.Src.To4()
} else {
srcData = route.Src.To16()
}
// The commonly used src ip for routes is actually PREFSRC
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PREFSRC, srcData))
}
if route.Gw != nil {
gwFamily := nl.GetIPFamily(route.Gw)
if family != -1 && family != gwFamily {
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
}
family = gwFamily
var gwData []byte
if gwFamily == FAMILY_V4 {
gwData = route.Gw.To4()
} else {
gwData = route.Gw.To16()
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_GATEWAY, gwData))
}
if len(route.MultiPath) > 0 {
buf := []byte{}
for _, nh := range route.MultiPath {
rtnh := &nl.RtNexthop{
RtNexthop: unix.RtNexthop{
Hops: uint8(nh.Hops),
Ifindex: int32(nh.LinkIndex),
Flags: uint8(nh.Flags),
},
}
children := []nl.NetlinkRequestData{}
if nh.Gw != nil {
gwFamily := nl.GetIPFamily(nh.Gw)
if family != -1 && family != gwFamily {
return fmt.Errorf("gateway, source, and destination ip are not the same IP family")
}
if gwFamily == FAMILY_V4 {
children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To4())))
} else {
children = append(children, nl.NewRtAttr(unix.RTA_GATEWAY, []byte(nh.Gw.To16())))
}
}
if nh.NewDst != nil {
if family != -1 && family != nh.NewDst.Family() {
return fmt.Errorf("new destination and destination are not the same address family")
}
buf, err := nh.NewDst.Encode()
if err != nil {
return err
}
children = append(children, nl.NewRtAttr(unix.RTA_NEWDST, buf))
}
if nh.Encap != nil {
buf := make([]byte, 2)
native.PutUint16(buf, uint16(nh.Encap.Type()))
children = append(children, nl.NewRtAttr(unix.RTA_ENCAP_TYPE, buf))
buf, err := nh.Encap.Encode()
if err != nil {
return err
}
children = append(children, nl.NewRtAttr(unix.RTA_ENCAP, buf))
}
rtnh.Children = children
buf = append(buf, rtnh.Serialize()...)
}
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_MULTIPATH, buf))
}
if route.Table > 0 {
if route.Table >= 256 {
msg.Table = unix.RT_TABLE_UNSPEC
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Table))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_TABLE, b))
} else {
msg.Table = uint8(route.Table)
}
}
if route.Priority > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Priority))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_PRIORITY, b))
}
if route.Tos > 0 {
msg.Tos = uint8(route.Tos)
}
if route.Protocol > 0 {
msg.Protocol = uint8(route.Protocol)
}
if route.Type > 0 {
msg.Type = uint8(route.Type)
}
var metrics []*nl.RtAttr
// TODO: support other rta_metric values
if route.MTU > 0 {
b := nl.Uint32Attr(uint32(route.MTU))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_MTU, b))
}
if route.AdvMSS > 0 {
b := nl.Uint32Attr(uint32(route.AdvMSS))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_ADVMSS, b))
}
if route.Hoplimit > 0 {
b := nl.Uint32Attr(uint32(route.Hoplimit))
metrics = append(metrics, nl.NewRtAttr(unix.RTAX_HOPLIMIT, b))
}
if metrics != nil {
attr := nl.NewRtAttr(unix.RTA_METRICS, nil)
for _, metric := range metrics {
attr.AddChild(metric)
}
rtAttrs = append(rtAttrs, attr)
}
msg.Flags = uint32(route.Flags)
msg.Scope = uint8(route.Scope)
msg.Family = uint8(family)
req.AddData(msg)
for _, attr := range rtAttrs {
req.AddData(attr)
}
var (
b = make([]byte, 4)
native = nl.NativeEndian()
)
native.PutUint32(b, uint32(route.LinkIndex))
req.AddData(nl.NewRtAttr(unix.RTA_OIF, b))
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
return err
}
// RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`.
// The list can be filtered by link and ip family.
func RouteList(link Link, family int) ([]Route, error) {
return pkgHandle.RouteList(link, family)
}
// RouteList gets a list of routes in the system.
// Equivalent to: `ip route show`.
// The list can be filtered by link and ip family.
func (h *Handle) RouteList(link Link, family int) ([]Route, error) {
var routeFilter *Route
if link != nil {
routeFilter = &Route{
LinkIndex: link.Attrs().Index,
}
}
return h.RouteListFiltered(family, routeFilter, RT_FILTER_OIF)
}
// RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct
func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
return pkgHandle.RouteListFiltered(family, filter, filterMask)
}
// RouteListFiltered gets a list of routes in the system filtered with specified rules.
// All rules must be defined in RouteFilter struct
func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(family)
req.AddData(infmsg)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
if err != nil {
return nil, err
}
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
if msg.Flags&unix.RTM_F_CLONED != 0 {
// Ignore cloned routes
continue
}
if msg.Table != unix.RT_TABLE_MAIN {
if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
// Ignore non-main tables
continue
}
}
route, err := deserializeRoute(m)
if err != nil {
return nil, err
}
if filter != nil {
switch {
case filterMask&RT_FILTER_TABLE != 0 && filter.Table != unix.RT_TABLE_UNSPEC && route.Table != filter.Table:
continue
case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
continue
case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope:
continue
case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type:
continue
case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
continue
case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
continue
case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
continue
case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw):
continue
case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
continue
case filterMask&RT_FILTER_DST != 0:
if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) {
if !ipNetEqual(route.Dst, filter.Dst) {
continue
}
}
case filterMask&RT_FILTER_HOPLIMIT != 0 && route.Hoplimit != filter.Hoplimit:
continue
}
}
res = append(res, route)
}
return res, nil
}
// deserializeRoute decodes a binary netlink message into a Route struct
func deserializeRoute(m []byte) (Route, error) {
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return Route{}, err
}
route := Route{
Scope: Scope(msg.Scope),
Protocol: int(msg.Protocol),
Table: int(msg.Table),
Type: int(msg.Type),
Tos: int(msg.Tos),
Flags: int(msg.Flags),
}
native := nl.NativeEndian()
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs {
switch attr.Attr.Type {
case unix.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case unix.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case unix.RTA_DST:
if msg.Family == nl.FAMILY_MPLS {
stack := nl.DecodeMPLSStack(attr.Value)
if len(stack) == 0 || len(stack) > 1 {
return route, fmt.Errorf("invalid MPLS RTA_DST")
}
route.MPLSDst = &stack[0]
} else {
route.Dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
}
case unix.RTA_OIF:
route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_IIF:
route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_PRIORITY:
route.Priority = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_TABLE:
route.Table = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_MULTIPATH:
parseRtNexthop := func(value []byte) (*NexthopInfo, []byte, error) {
if len(value) < unix.SizeofRtNexthop {
return nil, nil, fmt.Errorf("lack of bytes")
}
nh := nl.DeserializeRtNexthop(value)
if len(value) < int(nh.RtNexthop.Len) {
return nil, nil, fmt.Errorf("lack of bytes")
}
info := &NexthopInfo{
LinkIndex: int(nh.RtNexthop.Ifindex),
Hops: int(nh.RtNexthop.Hops),
Flags: int(nh.RtNexthop.Flags),
}
attrs, err := nl.ParseRouteAttr(value[unix.SizeofRtNexthop:int(nh.RtNexthop.Len)])
if err != nil {
return nil, nil, err
}
var encap, encapType syscall.NetlinkRouteAttr
for _, attr := range attrs {
switch attr.Attr.Type {
case unix.RTA_GATEWAY:
info.Gw = net.IP(attr.Value)
case unix.RTA_NEWDST:
var d Destination
switch msg.Family {
case nl.FAMILY_MPLS:
d = &MPLSDestination{}
}
if err := d.Decode(attr.Value); err != nil {
return nil, nil, err
}
info.NewDst = d
case unix.RTA_ENCAP_TYPE:
encapType = attr
case unix.RTA_ENCAP:
encap = attr
}
}
if len(encap.Value) != 0 && len(encapType.Value) != 0 {
typ := int(native.Uint16(encapType.Value[0:2]))
var e Encap
switch typ {
case nl.LWTUNNEL_ENCAP_MPLS:
e = &MPLSEncap{}
if err := e.Decode(encap.Value); err != nil {
return nil, nil, err
}
}
info.Encap = e
}
return info, value[int(nh.RtNexthop.Len):], nil
}
rest := attr.Value
for len(rest) > 0 {
info, buf, err := parseRtNexthop(rest)
if err != nil {
return route, err
}
route.MultiPath = append(route.MultiPath, info)
rest = buf
}
case unix.RTA_NEWDST:
var d Destination
switch msg.Family {
case nl.FAMILY_MPLS:
d = &MPLSDestination{}
}
if err := d.Decode(attr.Value); err != nil {
return route, err
}
route.NewDst = d
case unix.RTA_ENCAP_TYPE:
encapType = attr
case unix.RTA_ENCAP:
encap = attr
case unix.RTA_METRICS:
metrics, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return route, err
}
for _, metric := range metrics {
switch metric.Attr.Type {
case unix.RTAX_MTU:
route.MTU = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_ADVMSS:
route.AdvMSS = int(native.Uint32(metric.Value[0:4]))
case unix.RTAX_HOPLIMIT:
route.Hoplimit = int(native.Uint32(metric.Value[0:4]))
}
}
}
}
if len(encap.Value) != 0 && len(encapType.Value) != 0 {
typ := int(native.Uint16(encapType.Value[0:2]))
var e Encap
switch typ {
case nl.LWTUNNEL_ENCAP_MPLS:
e = &MPLSEncap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_SEG6:
e = &SEG6Encap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
case nl.LWTUNNEL_ENCAP_SEG6_LOCAL:
e = &SEG6LocalEncap{}
if err := e.Decode(encap.Value); err != nil {
return route, err
}
}
route.Encap = e
}
return route, nil
}
// RouteGet gets a route to a specific destination from the host system.
// Equivalent to: 'ip route get'.
func RouteGet(destination net.IP) ([]Route, error) {
return pkgHandle.RouteGet(destination)
}
// RouteGet gets a route to a specific destination from the host system.
// Equivalent to: 'ip route get'.
func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_REQUEST)
family := nl.GetIPFamily(destination)
var destinationData []byte
var bitlen uint8
if family == FAMILY_V4 {
destinationData = destination.To4()
bitlen = 32
} else {
destinationData = destination.To16()
bitlen = 128
}
msg := &nl.RtMsg{}
msg.Family = uint8(family)
msg.Dst_len = bitlen
req.AddData(msg)
rtaDst := nl.NewRtAttr(unix.RTA_DST, destinationData)
req.AddData(rtaDst)
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWROUTE)
if err != nil {
return nil, err
}
var res []Route
for _, m := range msgs {
route, err := deserializeRoute(m)
if err != nil {
return nil, err
}
res = append(res, route)
}
return res, nil
}
// RouteSubscribe takes a chan down which notifications will be sent
// when routes are added or deleted. Close the 'done' chan to stop subscription.
func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}
// RouteSubscribeAt works like RouteSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(ns, netns.None(), ch, done, nil, false)
}
// RouteSubscribeOptions contains a set of options to use with
// RouteSubscribeWithOptions.
type RouteSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}
// RouteSubscribeWithOptions work like RouteSubscribe but enable to
// provide additional options to modify the behavior. Currently, the
// namespace can be provided as well as an error callback.
func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, options RouteSubscribeOptions) error {
if options.Namespace == nil {
none := netns.None()
options.Namespace = &none
}
return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}
func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_ROUTE, unix.RTNLGRP_IPV6_ROUTE)
if err != nil {
return err
}
if done != nil {
go func() {
<-done
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETROUTE,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
msgs, from, err := s.Receive()
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
if from.Pid != nl.PidKernel {
if cberr != nil {
cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
}
continue
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
route, err := deserializeRoute(m.Data)
if err != nil {
if cberr != nil {
cberr(err)
}
return
}
ch <- RouteUpdate{Type: m.Header.Type, Route: route}
}
}
}()
return nil
}