diff --git a/protocols/netlink/netlink_reader.go b/protocols/netlink/netlink_reader.go index abe223070c7861d526ba53066ab8d0d9a07e8e0f..0896b96723306b4291c0d280bca10224bf331815 100644 --- a/protocols/netlink/netlink_reader.go +++ b/protocols/netlink/netlink_reader.go @@ -66,20 +66,6 @@ func (nr *NetlinkReader) Read() { } } -// create a path from a route -func createPathFromRoute(r *netlink.Route) (*route.Path, error) { - nlPath, err := route.NewNlPathFromRoute(r, true) - - if err != nil { - return nil, fmt.Errorf("Error while creating path object from route object: %v", err) - } - - return &route.Path{ - Type: route.NetlinkPathType, - NetlinkPath: nlPath, - }, nil -} - // propagate changes to all subscribed clients func (nr *NetlinkReader) propagateChanges(routes []netlink.Route) { nr.removePathsFromClients(routes) @@ -88,66 +74,63 @@ func (nr *NetlinkReader) propagateChanges(routes []netlink.Route) { // Add given paths to clients func (nr *NetlinkReader) addPathsToClients(routes []netlink.Route) { + // If there where no routes yet, just skip this funktion. There's nothing to delete + if len(routes) == 0 { + nr.mu.RUnlock() + return + } + // only advertise changed routes nr.mu.RLock() advertise := route.NetlinkRouteDiff(routes, nr.routes) nr.mu.RUnlock() - for _, r := range advertise { - if isBioRoute(r) { - log.WithFields(routeLogFields(r)).Debug("Skipping bio route") - continue - } - - // create pfx and path from route - pfx := bnet.NewPfxFromIPNet(r.Dst) - path, err := createPathFromRoute(&r) - if err != nil { - log.WithError(err).WithFields(log.Fields{ - "prefix": pfx.String(), - "path": path.String(), - }).Error("Unable to create path") - continue - } + for _, client := range nr.ClientManager.Clients() { + for _, r := range advertise { + if isBioRoute(r) { + log.WithFields(routeLogFields(r)).Debug("Skipping bio route") + continue + } - if nr.filter != nil { - var reject bool - // TODO: Implement filter that cann handle netlinkRoute objects - path, reject = nr.filter.ProcessTerms(pfx, path) - if reject { + // create pfx and path from route + pfx, paths, err := route.NewPathsFromNlRoute(r, true) + if err != nil { log.WithError(err).WithFields(log.Fields{ "prefix": pfx.String(), - "path": path.String(), - }).Debug("Skipping route due to filter") - + }).Error("Unable to create path") continue } - } - for _, client := range nr.ClientManager.Clients() { - log.WithFields(log.Fields{ - "pfx": pfx, - "path": path, - }).Debug("NetlinkReader - client.AddPath") - client.AddPath(pfx, path) + for _, path := range paths { + var p *route.Path + if nr.filter != nil { + var reject bool + p, reject := nr.filter.ProcessTerms(pfx, path) + if reject { + log.WithError(err).WithFields(log.Fields{ + "prefix": pfx.String(), + "path": p.String(), + }).Debug("Skipping route due to filter") + continue + } + } + + log.WithFields(log.Fields{ + "pfx": pfx, + "path": p, + }).Debug("NetlinkReader - client.AddPath") + client.AddPath(pfx, p) + } } } } -// Is route a BIO-Written route? -func isBioRoute(r netlink.Route) bool { - return uint32(r.Protocol) == route.ProtoBio -} - // Remove given paths from clients func (nr *NetlinkReader) removePathsFromClients(routes []netlink.Route) { nr.mu.RLock() - // get the number of routes - routeLength := len(nr.routes) - // If there where no routes yet, just skip this funktion. There's nothing to delete - if routeLength == 0 { + if len(nr.routes) == 0 { nr.mu.RUnlock() return } @@ -156,39 +139,51 @@ func (nr *NetlinkReader) removePathsFromClients(routes []netlink.Route) { withdraw := route.NetlinkRouteDiff(nr.routes, routes) nr.mu.RUnlock() - for _, r := range withdraw { - // Is it a BIO-Written route? if so, skip it, dont advertise it - if r.Protocol == route.ProtoBio { - continue - } - - // create pfx and path from route - pfx := bnet.NewPfxFromIPNet(r.Dst) - path, err := createPathFromRoute(&r) - if err != nil { - log.WithError(err).Error("Unable to create path") - continue - } + for _, client := range nr.ClientManager.Clients() { + for _, r := range withdraw { + if isBioRoute(r) { + log.WithFields(routeLogFields(r)).Debug("Skipping bio route") + continue + } - if nr.filter != nil { - var reject bool - // TODO: Implement filter that cann handle netlinkRoute objects - path, reject = nr.filter.ProcessTerms(pfx, path) - if reject { + // create pfx and path from route + pfx, paths, err := route.NewPathsFromNlRoute(r, true) + if err != nil { + log.WithError(err).WithFields(log.Fields{ + "prefix": pfx.String(), + }).Error("Unable to create path") continue } - } - for _, client := range nr.ClientManager.Clients() { - log.WithFields(log.Fields{ - "pfx": pfx, - "path": path, - }).Debug("NetlinkReader - client.RemovePath") - client.RemovePath(pfx, path) + for _, path := range paths { + var p *route.Path + if nr.filter != nil { + var reject bool + p, reject = nr.filter.ProcessTerms(pfx, path) + if reject { + log.WithError(err).WithFields(log.Fields{ + "prefix": pfx.String(), + "path": p.String(), + }).Debug("Skipping route due to filter") + continue + } + } + + log.WithFields(log.Fields{ + "pfx": pfx, + "path": p, + }).Debug("NetlinkReader - client.RemovePath") + client.RemovePath(pfx, p) + } } } } +// Is route a BIO-Written route? +func isBioRoute(r netlink.Route) bool { + return uint32(r.Protocol) == route.ProtoBio +} + func routeLogFields(route netlink.Route) log.Fields { return log.Fields{ "LinkIndex": route.LinkIndex, diff --git a/protocols/netlink/netlink_writer.go b/protocols/netlink/netlink_writer.go index 1ebe54205ce1b86105788401a5605276bd7be6a8..a66b05099082c608587568a8988777cd5fb45c17 100644 --- a/protocols/netlink/netlink_writer.go +++ b/protocols/netlink/netlink_writer.go @@ -66,12 +66,8 @@ func (nw *NetlinkWriter) AddPath(pfx bnet.Prefix, path *route.Path) error { // if no route exists, add that route if existingPaths == nil || !ok { - paths := make([]*route.Path, 1) - paths = append(paths, path) - nw.pathTable[pfx] = paths - - // add the route to kernel - return nw.addKernel(pfx, path) + nw.pathTable[pfx] = []*route.Path{path} + return nw.addKernel(pfx) } // if the new path is already in, don't do anything @@ -81,11 +77,20 @@ func (nw *NetlinkWriter) AddPath(pfx bnet.Prefix, path *route.Path) error { } } - existingPaths = append(existingPaths, path) - nw.pathTable[pfx] = existingPaths + // if newly added path is a ecmp path to the existing paths, add it + if path.ECMP(existingPaths[0]) { + nw.removeKernel(pfx, existingPaths) + existingPaths = append(existingPaths, path) + nw.pathTable[pfx] = existingPaths + + return nw.addKernel(pfx) + } + + // if newly added path is no ecmp path to the existing ones, remove all old and only add the new + nw.removeKernel(pfx, existingPaths) + nw.pathTable[pfx] = []*route.Path{path} + return nw.addKernel(pfx) - // now add to netlink - return nw.addKernel(pfx, path) } // RemovePath removes a path from the Kernel using netlink This function is triggered by the loc_rib, cause we are subscribed as client in the loc_rib @@ -106,7 +111,7 @@ func (nw *NetlinkWriter) RemovePath(pfx bnet.Prefix, path *route.Path) bool { removeIdx = idx remove = true - err := nw.removeKernel(pfx, path) + err := nw.removeKernel(pfx, []*route.Path{path}) if err != nil { log.WithError(err).Errorf("Error while removing path %s for prefix %s", path.String(), pfx.String()) remove = false @@ -125,19 +130,15 @@ func (nw *NetlinkWriter) RemovePath(pfx bnet.Prefix, path *route.Path) bool { } // Add pfx/path to kernel -func (nw *NetlinkWriter) addKernel(pfx bnet.Prefix, path *route.Path) error { - route, err := nw.createRoute(pfx, path) - if err != nil { - log.Errorf("Error while creating route: %v", err) - return fmt.Errorf("Error while creating route: %v", err) - } +func (nw *NetlinkWriter) addKernel(pfx bnet.Prefix) error { + route := nw.createRoute(pfx, nw.pathTable[pfx]) log.WithFields(log.Fields{ "Prefix": pfx.String(), "Table": route.Table, }).Debug("AddPath to netlink") - err = netlink.RouteAdd(route) + err := netlink.RouteAdd(route) if err != nil { log.Errorf("Error while adding route: %v", err) return fmt.Errorf("Error while adding route: %v", err) @@ -147,17 +148,14 @@ func (nw *NetlinkWriter) addKernel(pfx bnet.Prefix, path *route.Path) error { } // remove pfx/path from kernel -func (nw *NetlinkWriter) removeKernel(pfx bnet.Prefix, path *route.Path) error { +func (nw *NetlinkWriter) removeKernel(pfx bnet.Prefix, paths []*route.Path) error { + route := nw.createRoute(pfx, nw.pathTable[pfx]) + log.WithFields(log.Fields{ "Prefix": pfx.String(), }).Debug("Remove from netlink") - route, err := nw.createRoute(pfx, path) - if err != nil { - return fmt.Errorf("Error while creating route: %v", err) - } - - err = netlink.RouteDel(route) + err := netlink.RouteDel(route) if err != nil { return fmt.Errorf("Error while removing route: %v", err) } @@ -166,62 +164,23 @@ func (nw *NetlinkWriter) removeKernel(pfx bnet.Prefix, path *route.Path) error { } // create a route from a prefix and a path -func (nw *NetlinkWriter) createRoute(pfx bnet.Prefix, path *route.Path) (*netlink.Route, error) { - if path.Type != route.NetlinkPathType { +func (nw *NetlinkWriter) createRoute(pfx bnet.Prefix, paths []*route.Path) *netlink.Route { + route := &netlink.Route{ + Dst: pfx.GetIPNet(), + Table: int(nw.options.RoutingTable), // config dependent + Protocol: route.ProtoBio, } - switch path.Type { - case route.NetlinkPathType: - return nw.createRouteFromNetlink(pfx, path) + multiPath := make([]*netlink.NexthopInfo, 0) - case route.BGPPathType: - return nw.createRouteFromBGPPath(pfx, path) - - default: - return nil, fmt.Errorf("PathType %d is not supported for adding to netlink", path.Type) + for _, path := range paths { + nextHop := &netlink.NexthopInfo{ + Gw: path.NextHop().Bytes(), + } + multiPath = append(multiPath, nextHop) } -} - -func (nw *NetlinkWriter) createRouteFromNetlink(pfx bnet.Prefix, path *route.Path) (*netlink.Route, error) { - nlPath := path.NetlinkPath - - log.WithFields(log.Fields{ - "Dst": nlPath.Dst, - "Src": nlPath.Src, - "NextHop": nlPath.NextHop, - "Priority": nlPath.Priority, - "Protocol": nlPath.Protocol, - "Type": nlPath.Type, - "Table": nw.options.RoutingTable, - }).Debug("created route") - - return &netlink.Route{ - Dst: nlPath.Dst.GetIPNet(), - Src: nlPath.Src.Bytes(), - Gw: nlPath.NextHop.Bytes(), - Priority: nlPath.Priority, - Type: nlPath.Type, - Table: int(nw.options.RoutingTable), // config dependent - Protocol: route.ProtoBio, // fix - }, nil -} - -func (nw *NetlinkWriter) createRouteFromBGPPath(pfx bnet.Prefix, path *route.Path) (*netlink.Route, error) { - bgpPath := path.BGPPath - log.WithFields(log.Fields{ - "Dst": pfx, - "NextHop": bgpPath.NextHop, - "Protocol": "BGP", - "BGPIdentifier": bgpPath.BGPIdentifier, - "Table": nw.options.RoutingTable, - }).Debug("created route") - - return &netlink.Route{ - Dst: pfx.GetIPNet(), - Gw: bgpPath.NextHop.Bytes(), - Table: int(nw.options.RoutingTable), // config dependent - Protocol: route.ProtoBio, // fix - }, nil + route.MultiPath = multiPath + return route } diff --git a/route/netlink_path.go b/route/netlink_path.go index ebd721cd54e2ed9cc1d4ebeb12b5b5ae897ceae0..c9d19b19fcfdb9621755536462c5b4a96a4e9674 100644 --- a/route/netlink_path.go +++ b/route/netlink_path.go @@ -21,7 +21,6 @@ const ( // 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 @@ -42,12 +41,12 @@ func NewNlPathFromBgpPath(p *BGPPath) *NetlinkPath { } // NewNlPathFromRoute creates a new NetlinkPath object from a netlink.Route object -func NewNlPathFromRoute(r *netlink.Route, kernel bool) (*NetlinkPath, error) { +func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*Path, 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") + return bnet.Prefix{}, nil, fmt.Errorf("Cannot create NlPath, since source and destination are both nil") } if r.Src == nil && r.Dst != nil { @@ -73,26 +72,45 @@ func NewNlPathFromRoute(r *netlink.Route, kernel bool) (*NetlinkPath, error) { dst = bnet.NewPfxFromIPNet(r.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 + paths := make([]*Path, 0) + + if len(r.MultiPath) > 0 { + for _, multiPath := range r.MultiPath { + nextHop, _ := bnet.IPFromBytes(multiPath.Gw) + paths = append(paths, &Path{ + Type: NetlinkPathType, + NetlinkPath: &NetlinkPath{ + Src: src, + NextHop: nextHop, + Priority: r.Priority, + Protocol: r.Protocol, + Type: r.Type, + Table: r.Table, + Kernel: kernel, + }, + }) + } + } else { + nextHop, _ := bnet.IPFromBytes(r.Gw) + paths = append(paths, &Path{ + Type: NetlinkPathType, + NetlinkPath: &NetlinkPath{ + Src: src, + NextHop: nextHop, + Priority: r.Priority, + Protocol: r.Protocol, + Type: r.Type, + Table: r.Table, + Kernel: kernel, + }, + }) + } + + return dst, paths, nil } // Select compares s with t and 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 } @@ -138,7 +156,28 @@ func (s *NetlinkPath) Select(t *NetlinkPath) int8 { // ECMP determines if path s and t are equal in terms of ECMP func (s *NetlinkPath) ECMP(t *NetlinkPath) bool { - return false + + if s.Src != t.Src { + return false + } + + if s.Priority != t.Priority { + return false + } + + if s.Protocol != t.Protocol { + return false + } + + if s.Type != t.Type { + return false + } + + if s.Table != t.Table { + return false + } + + return true } // Copy duplicates the current object @@ -153,8 +192,7 @@ func (s *NetlinkPath) Copy() *NetlinkPath { // Print all known information about a route in logfile friendly format func (s *NetlinkPath) String() string { - ret := fmt.Sprintf("Destination: %s, ", s.Dst.String()) - ret += fmt.Sprintf("Source: %s, ", s.Src.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) @@ -165,8 +203,7 @@ func (s *NetlinkPath) String() string { // 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\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) diff --git a/route/path_test.go b/route/path_test.go index d409cbd9b6a162e6a7ce42308df11cc94fde0569..c76cc96171591ae8af6077aac2052c97e0f52b96 100644 --- a/route/path_test.go +++ b/route/path_test.go @@ -360,16 +360,17 @@ func TestNewNlPath(t *testing.T) { } } -func TestNewNlPathFromNetlinkRoute(t *testing.T) { +func TestNewPathsFromNetlinkRoute(t *testing.T) { tests := []struct { - name string - source *netlink.Route - expected *NetlinkPath - expectError bool + name string + source netlink.Route + expectedPfx bnet.Prefix + expectedPaths []*Path + expectError bool }{ { name: "Simple", - source: &netlink.Route{ + source: netlink.Route{ Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(), Src: bnet.IPv4(456).Bytes(), Gw: bnet.IPv4(789).Bytes(), @@ -378,127 +379,172 @@ func TestNewNlPathFromNetlinkRoute(t *testing.T) { Table: 254, Type: 1, }, - expected: &NetlinkPath{ - Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), - Src: bnet.IPv4(456), - NextHop: bnet.IPv4(789), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - Kernel: true, - }, - expectError: false, - }, - { - name: "No source, no destination", - source: &netlink.Route{ - Gw: bnet.IPv4(789).Bytes(), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - }, - expected: &NetlinkPath{}, - expectError: true, - }, - { - name: "No source but destination", - source: &netlink.Route{ - Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(), - Gw: bnet.IPv4(789).Bytes(), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - }, - expected: &NetlinkPath{ - Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), - Src: bnet.IPv4FromOctets(0, 0, 0, 0), - NextHop: bnet.IPv4(789), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - Kernel: true, - }, - expectError: false, - }, - { - name: "Source but no destination", - source: &netlink.Route{ - Src: bnet.IPv4(456).Bytes(), - Gw: bnet.IPv4(789).Bytes(), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - }, - expected: &NetlinkPath{ - Dst: bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0), - Src: bnet.IPv4(456), - NextHop: bnet.IPv4(789), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - Kernel: true, - }, - expectError: false, - }, - { - name: "No source but destination IPv6", - source: &netlink.Route{ - Dst: bnet.NewPfx(bnet.IPv6(2001, 0), 48).GetIPNet(), - Gw: bnet.IPv6(2001, 2).Bytes(), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - }, - expected: &NetlinkPath{ - Dst: bnet.NewPfx(bnet.IPv6(2001, 0), 48), - Src: bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), - NextHop: bnet.IPv6(2001, 2), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - Kernel: true, + expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), + expectedPaths: []*Path{ + { + Type: NetlinkPathType, + NetlinkPath: &NetlinkPath{ + Src: bnet.IPv4(456), + NextHop: bnet.IPv4(789), + Protocol: ProtoKernel, + Priority: 1, + Table: 254, + Type: 1, + Kernel: true, + }, + }, }, expectError: false, }, { - name: "Source but no destination IPv6", - source: &netlink.Route{ - Src: bnet.IPv6(2001, 0).Bytes(), - Gw: bnet.IPv6(2001, 2).Bytes(), + name: "Multiple nexthop", + source: netlink.Route{ + Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(), + Src: bnet.IPv4(456).Bytes(), + MultiPath: []*netlink.NexthopInfo{ + { + LinkIndex: 1, + Hops: 1, + Gw: bnet.IPv4(123).Bytes(), + Flags: 0, + NewDst: nil, + Encap: nil, + }, { + LinkIndex: 2, + Hops: 1, + Gw: bnet.IPv4(345).Bytes(), + Flags: 0, + NewDst: nil, + Encap: nil, + }, + }, Protocol: ProtoKernel, Priority: 1, Table: 254, Type: 1, }, - expected: &NetlinkPath{ - Dst: bnet.NewPfx(bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), 0), - Src: bnet.IPv6(2001, 0), - NextHop: bnet.IPv6(2001, 2), - Protocol: ProtoKernel, - Priority: 1, - Table: 254, - Type: 1, - Kernel: true, + expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), + expectedPaths: []*Path{ + { + Type: NetlinkPathType, + NetlinkPath: &NetlinkPath{ + Src: bnet.IPv4(456), + NextHop: bnet.IPv4(123), + Protocol: ProtoKernel, + Priority: 1, + Table: 254, + Type: 1, + Kernel: true, + }, + }, { + Type: NetlinkPathType, + NetlinkPath: &NetlinkPath{ + Src: bnet.IPv4(456), + NextHop: bnet.IPv4(345), + Protocol: ProtoKernel, + Priority: 1, + Table: 254, + Type: 1, + Kernel: true, + }, + }, }, expectError: false, }, + // { + // name: "No source but destination", + // source: &netlink.Route{ + // Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(), + // Gw: bnet.IPv4(789).Bytes(), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // }, + // expected: &NetlinkPath{ + // Src: bnet.IPv4FromOctets(0, 0, 0, 0), + // NextHop: bnet.IPv4(789), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // Kernel: true, + // }, + // expectError: false, + // }, + // { + // name: "Source but no destination", + // source: &netlink.Route{ + // Src: bnet.IPv4(456).Bytes(), + // Gw: bnet.IPv4(789).Bytes(), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // }, + // expected: &NetlinkPath{ + // Src: bnet.IPv4(456), + // NextHop: bnet.IPv4(789), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // Kernel: true, + // }, + // expectError: false, + // }, + // { + // name: "No source but destination IPv6", + // source: &netlink.Route{ + // Dst: bnet.NewPfx(bnet.IPv6(2001, 0), 48).GetIPNet(), + // Gw: bnet.IPv6(2001, 2).Bytes(), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // }, + // expected: &NetlinkPath{ + // Src: bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), + // NextHop: bnet.IPv6(2001, 2), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // Kernel: true, + // }, + // expectError: false, + // }, + // { + // name: "Source but no destination IPv6", + // source: &netlink.Route{ + // Src: bnet.IPv6(2001, 0).Bytes(), + // Gw: bnet.IPv6(2001, 2).Bytes(), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // }, + // expected: &NetlinkPath{ + // Src: bnet.IPv6(2001, 0), + // NextHop: bnet.IPv6(2001, 2), + // Protocol: ProtoKernel, + // Priority: 1, + // Table: 254, + // Type: 1, + // Kernel: true, + // }, + // expectError: false, + // }, } for _, test := range tests { - converted, err := NewNlPathFromRoute(test.source, true) + pfx, paths, err := NewPathsFromNlRoute(test.source, true) if test.expectError { assert.Error(t, err) } else { assert.NoError(t, err) - assert.Equalf(t, test.expected, converted, test.name) + assert.Equalf(t, test.expectedPaths, paths, test.name) + assert.Equalf(t, test.expectedPfx, pfx, test.name) } } }