Skip to content
Snippets Groups Projects
netlink_path.go 3.44 KiB
Newer Older
cedi's avatar
cedi committed
package route

import (
	"fmt"

	bnet "github.com/bio-routing/bio-rd/net"
	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
)

const (
	ProtoBio = 45
)

// NetlinkPath represents a path learned via Netlink of a route
type NetlinkPath struct {
	Dst      bnet.Prefix
	Src      bnet.IP
	NextHop  bnet.IP // GW
	Priority int
	Protocol int
	Type     int
	Table    int
	Kernel   bool // True if the route is already installed in the kernel
}

func NewNlPathFromBgpPath(p *BGPPath) *NetlinkPath {
	return &NetlinkPath{
		Src:      p.Source,
		NextHop:  p.NextHop,
		Protocol: ProtoBio,
		Kernel:   false,
	}
}

func NewNlPathFromRoute(r *netlink.Route, kernel bool) (*NetlinkPath, error) {
	var src bnet.IP
	var dst bnet.Prefix

	if r.Src == nil && r.Dst == nil {
		return nil, fmt.Errorf("Cannot create NlPath, since source and destination are both nil")
	}

	if r.Src == nil && r.Dst != nil {
		dst = bnet.NewPfxFromIPNet(r.Dst)
		if dst.Addr().IsIPv4() {
			src = bnet.IPv4FromOctets(0, 0, 0, 0)
		} else {
			src = bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0)
		}
	}

	if r.Src != nil && r.Dst == nil {
		src, _ = bnet.IPFromBytes(r.Src)
		if src.IsIPv4() {
			dst = bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0)
		} else {
			dst = bnet.NewPfx(bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), 0)
		}
	}

	if r.Src != nil && r.Dst != nil {
		src, _ = bnet.IPFromBytes(r.Src)
		dst = bnet.NewPfxFromIPNet(r.Dst)
	}

	log.Warnf("IPFromBytes: %v goes to %v", r.Src, src)
	log.Warnf("IPFromBytes: %v goes to %v", r.Dst, dst)

	nextHop, _ := bnet.IPFromBytes(r.Gw)

	return &NetlinkPath{
		Dst:      dst,
		Src:      src,
		NextHop:  nextHop,
		Priority: r.Priority,
		Protocol: r.Protocol,
		Type:     r.Type,
		Table:    r.Table,
		Kernel:   kernel,
	}, nil
}

// Compare returns negative if s < t, 0 if paths are equal, positive if s > t
func (s *NetlinkPath) Select(t *NetlinkPath) int8 {
	if !s.Dst.Equal(t.Dst) {
		return 1
	}

	if s.NextHop.Compare(t.NextHop) > 0 {
		return -1
	}

	if s.NextHop.Compare(t.NextHop) < 0 {
		return 1
	}

	if s.Src.Compare(t.Src) > 0 {
		return -1
	}

	if s.Src.Compare(t.Src) < 0 {
		return 1
	}

	if s.Priority < t.Priority {
		return -1
	}

	if s.Priority > t.Priority {
		return 1
	}

	if s.Protocol < t.Protocol {
		return -1
	}

	if s.Protocol > t.Protocol {
		return 1
	}

	if s.Table < t.Table {
		return -1
	}

	if s.Table > t.Table {
		return 1
	}

	return 0
}

// ECMP determines if path s and t are equal in terms of ECMP
func (s *NetlinkPath) ECMP(t *NetlinkPath) bool {
	return true
}

func (s *NetlinkPath) Copy() *NetlinkPath {
	if s == nil {
		return nil
	}

	cp := *s
	return &cp
}

// get all known information about a route in a machine readable form
func (s *NetlinkPath) String() string {
	ret := fmt.Sprintf("Destination: %s, ", s.Dst.String())
	ret += fmt.Sprintf("Source: %s, ", s.Src.String())
	ret += fmt.Sprintf("NextHop: %s, ", s.NextHop.String())
	ret += fmt.Sprintf("Priority: %d, ", s.Priority)
	ret += fmt.Sprintf("Type: %d, ", s.Type)
	ret += fmt.Sprintf("Table: %d", s.Table)

	return ret
}

// Pretty Print all known information about a route in human readable form
func (s *NetlinkPath) Print() string {
	ret := fmt.Sprintf("\t\tDestination: %s\n", s.Dst.String())
	ret += fmt.Sprintf("\t\tSource: %s\n", s.Src.String())
	ret += fmt.Sprintf("\t\tNextHop: %s\n", s.NextHop.String())
	ret += fmt.Sprintf("\t\tPriority: %d\n", s.Priority)
	ret += fmt.Sprintf("\t\tType: %d\n", s.Type)
	ret += fmt.Sprintf("\t\tTable: %d\n", s.Table)

	return ret
}