diff --git a/examples/kernel/kernel b/examples/kernel/kernel new file mode 100755 index 0000000000000000000000000000000000000000..2d2539712ff92936264c2e613e2bf56b180d5293 Binary files /dev/null and b/examples/kernel/kernel differ diff --git a/examples/kernel/main.go b/examples/kernel/main.go new file mode 100644 index 0000000000000000000000000000000000000000..714e4b9f264c76ba684f878782d46a67ca518f5e --- /dev/null +++ b/examples/kernel/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + "time" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/kernel" + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable/vrf" + log "github.com/sirupsen/logrus" +) + +func main() { + vrf, err := vrf.New("inet.0") + if err != nil { + log.Errorf("Unable to create VRF: %v", err) + os.Exit(1) + } + + rib4 := vrf.IPv4UnicastRIB() + rib4.AddPath(bnet.NewPfx(bnet.IPv4FromOctets(8, 8, 8, 0), 24), &route.Path{ + Type: route.StaticPathType, + StaticPath: &route.StaticPath{ + NextHop: bnet.IPv4FromOctets(127, 0, 0, 1), + }, + }) + + k, err := kernel.New() + if err != nil { + log.Errorf("Unable to create protocol kernel: %v", err) + os.Exit(1) + } + defer k.Dispose() + + rib4.Register(k) + + time.Sleep(time.Second * 10) +} diff --git a/protocols/kernel/kernel.go b/protocols/kernel/kernel.go new file mode 100644 index 0000000000000000000000000000000000000000..dd525c3b670c45282228eda939cad7c669667ba9 --- /dev/null +++ b/protocols/kernel/kernel.go @@ -0,0 +1,64 @@ +package kernel + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/bio-routing/bio-rd/routingtable" +) + +type Kernel struct { + osKernel osKernel +} + +type osKernel interface { + AddPath(pfx net.Prefix, path *route.Path) error + RemovePath(pfx net.Prefix, path *route.Path) bool + uninit() error +} + +func New() (*Kernel, error) { + k := &Kernel{} + err := k.init() + if err != nil { + return nil, err + } + + return k, nil +} + +func (k *Kernel) AddPath(pfx net.Prefix, path *route.Path) error { + return k.osKernel.AddPath(pfx, path) +} + +func (k *Kernel) RemovePath(pfx net.Prefix, path *route.Path) bool { + return k.osKernel.RemovePath(pfx, path) +} + +func (k *Kernel) UpdateNewClient(routingtable.RouteTableClient) error { + return nil +} + +func (k *Kernel) Register(routingtable.RouteTableClient) { +} + +func (k *Kernel) RegisterWithOptions(routingtable.RouteTableClient, routingtable.ClientOptions) { +} + +func (k *Kernel) Unregister(routingtable.RouteTableClient) { +} + +func (k *Kernel) RouteCount() int64 { + return -1 +} + +func (k *Kernel) ClientCount() uint64 { + return 0 +} + +func (k *Kernel) Dump() []*route.Route { + return nil +} + +func (k *Kernel) Dispose() { + k.osKernel.uninit() +} diff --git a/protocols/kernel/kernel_linux.go b/protocols/kernel/kernel_linux.go new file mode 100644 index 0000000000000000000000000000000000000000..c94fab318d13ad66dc810035448792ef97314089 --- /dev/null +++ b/protocols/kernel/kernel_linux.go @@ -0,0 +1,127 @@ +package kernel + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/pkg/errors" + "github.com/vishvananda/netlink" + + bnet "github.com/bio-routing/bio-rd/net" +) + +const ( + protoBio = 45 +) + +func (k *Kernel) init() error { + lk, err := newLinuxKernel() + if err != nil { + return errors.Wrap(err, "Unable to initialize linux kernel") + } + + err = lk.init() + if err != nil { + return errors.Wrap(err, "Init failed") + } + k.osKernel = lk + return nil +} + +func (k *Kernel) uninit() error { + return k.osKernel.uninit() +} + +type linuxKernel struct { + h *netlink.Handle + routes map[bnet.Prefix]struct{} +} + +func newLinuxKernel() (*linuxKernel, error) { + h, err := netlink.NewHandle() + if err != nil { + return nil, errors.Wrap(err, "Unable to get Netlink handle") + } + + return &linuxKernel{ + h: h, + routes: make(map[bnet.Prefix]struct{}), + }, nil +} + +func (lk *linuxKernel) init() error { + err := lk.cleanup() + if err != nil { + return errors.Wrap(err, "Cleanup failed") + } + + return nil +} + +func (lk *linuxKernel) uninit() error { + return lk.cleanup() +} + +func (lk *linuxKernel) cleanup() error { + filter := &netlink.Route{ + Protocol: protoBio, + } + + routes, err := lk.h.RouteListFiltered(0, filter, netlink.RT_FILTER_PROTOCOL) + if err != nil { + return errors.Wrap(err, "Unable to get routes") + } + + for i := range routes { + err = lk.h.RouteDel(&routes[i]) + if err != nil { + return errors.Wrap(err, "Unable to remove route") + } + } + + return nil +} + +func (lk *linuxKernel) AddPath(pfx net.Prefix, path *route.Path) error { + r := &netlink.Route{ + Protocol: protoBio, + Dst: pfx.GetIPNet(), + Gw: path.NextHop().ToNetIP(), + } + + if _, found := lk.routes[pfx]; !found { + err := lk.h.RouteAdd(r) + if err != nil { + return errors.Wrap(err, "Unable to add route") + } + + lk.routes[pfx] = struct{}{} + return nil + } + + err := lk.h.RouteReplace(r) + if err != nil { + return errors.Wrap(err, "Unable to replace route") + } + + return nil +} + +func (lk *linuxKernel) RemovePath(pfx net.Prefix, path *route.Path) bool { + if _, found := lk.routes[pfx]; !found { + return false + } + + r := &netlink.Route{ + Protocol: protoBio, + Dst: pfx.GetIPNet(), + Gw: path.NextHop().ToNetIP(), + } + + err := lk.h.RouteDel(r) + if err != nil { + return false + } + + delete(lk.routes, pfx) + return true +}