diff --git a/.gitignore b/.gitignore
index c97ea2a6069695630909b3884565f91629e3965e..740e0fd72bccc0e15acad46fc9cb87677431958c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@
 bio-rd
 examples/bgp
 examples/bmp
+examples/netlink/netlink
 
 # bazel directories
 /bazel-*
diff --git a/README.md b/README.md
index ef6538c6580315b8814a997a61ec7f1344b22a25..0254191f9601b7c9cbc863a5fc906ff2c749c28a 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,10 @@ A re-implementation of BGP, IS-IS and OSPF in go. We value respect and robustnes
 
     cd examples/bmp/ && go build
 
+#### Netlink
+
+    cd examples/netlink && go build
+
 ### Run Tests
 
     go test -v -cover ./...
diff --git a/examples/netlink/main.go b/examples/netlink/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa4e493acb4e37c1f2745f5593e39811870ecacc
--- /dev/null
+++ b/examples/netlink/main.go
@@ -0,0 +1,63 @@
+package main
+
+import (
+	"net"
+	"os"
+	"time"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/bio-routing/bio-rd/protocols/bgp/server"
+	"github.com/bio-routing/bio-rd/protocols/netlink"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	log "github.com/sirupsen/logrus"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+)
+
+func strAddr(s string) uint32 {
+	ret, _ := bnet.StrToAddr(s)
+	return ret
+}
+
+func main() {
+	log.SetLevel(log.DebugLevel)
+
+	f, err := os.OpenFile("/var/log/bio-rd.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+	if err != nil {
+		log.Fatalf("error opening file: %v", err)
+	}
+	defer f.Close()
+
+	log.SetOutput(f)
+
+	log.Info("bio-routing started...\n")
+
+	cfg := &config.Global{
+		Listen: true,
+		LocalAddressList: []net.IP{
+			net.IPv4(169, 254, 0, 2),
+		},
+	}
+
+	rib := locRIB.New()
+	b := server.NewBgpServer()
+	startBGPServer(b, rib, cfg)
+
+	// Netlink communication
+	n := protocolnetlink.NewNetlink(&config.Netlink{
+		HoldTime:       time.Second * 15,
+		UpdateInterval: time.Second * 15,
+		RoutingTable:   config.RtMain,
+	}, rib)
+	n.Start()
+
+	go func() {
+		for {
+			log.Debugf("LocRIB count: %d", rib.Count())
+			log.Debugf(rib.String())
+			time.Sleep(time.Second * 10)
+		}
+	}()
+
+	select {}
+}
diff --git a/examples/netlink/main_ipv4.go b/examples/netlink/main_ipv4.go
new file mode 100644
index 0000000000000000000000000000000000000000..12bb67ae2305208035c00d46ed634c72a2dee454
--- /dev/null
+++ b/examples/netlink/main_ipv4.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+	"time"
+
+	"github.com/bio-routing/bio-rd/routingtable"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/bio-routing/bio-rd/protocols/bgp/server"
+	"github.com/bio-routing/bio-rd/routingtable/filter"
+	log "github.com/sirupsen/logrus"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+)
+
+func startBGPServer(b server.BGPServer, rib *locRIB.LocRIB, cfg *config.Global) {
+	err := b.Start(cfg)
+	if err != nil {
+		log.Fatalf("Unable to start BGP server: %v", err)
+	}
+
+	b.AddPeer(config.Peer{
+		AdminEnabled:      true,
+		LocalAS:           65200,
+		PeerAS:            65100,
+		PeerAddress:       bnet.IPv4FromOctets(169, 254, 0, 1),
+		LocalAddress:      bnet.IPv4FromOctets(169, 254, 0, 2),
+		ReconnectInterval: time.Second * 20,
+		HoldTime:          time.Second * 20,
+		KeepAlive:         time.Second * 20,
+		Passive:           false,
+		RouterID:          b.RouterID(),
+
+		//AddPathSend: routingtable.ClientOptions{
+		//	MaxPaths: 10,
+		//},
+		//RouteServerClient: true,
+		IPv4: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewAcceptAllFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				MaxPaths: 10,
+			},
+			AddPathRecv: true,
+		},
+	})
+}
diff --git a/examples/netlink/main_ipv6.go b/examples/netlink/main_ipv6.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc26484b2e7f855eca6eeb75c6d51df436167f47
--- /dev/null
+++ b/examples/netlink/main_ipv6.go
@@ -0,0 +1,72 @@
+// +build ipv6
+
+package main
+
+import (
+	"net"
+	"time"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/bio-routing/bio-rd/protocols/bgp/server"
+	"github.com/bio-routing/bio-rd/routingtable"
+	"github.com/bio-routing/bio-rd/routingtable/filter"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+	"github.com/sirupsen/logrus"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+)
+
+func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
+
+	err := b.Start(&config.Global{
+		Listen: true,
+		LocalAddressList: []net.IP{
+			net.IP{0x20, 0x01, 0x6, 0x78, 0x1, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0xca, 0xfe},
+		},
+	})
+	if err != nil {
+		logrus.Fatalf("Unable to start BGP server: %v", err)
+	}
+
+	b.AddPeer(config.Peer{
+		AdminEnabled:      true,
+		LocalAS:           65200,
+		PeerAS:            202739,
+		PeerAddress:       bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 1),
+		LocalAddress:      bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe),
+		ReconnectInterval: time.Second * 15,
+		HoldTime:          time.Second * 90,
+		KeepAlive:         time.Second * 30,
+		Passive:           true,
+		RouterID:          b.RouterID(),
+		IPv6: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewAcceptAllFilter(),
+			ExportFilter: filter.NewDrainFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				BestOnly: true,
+			},
+		},
+	})
+
+	b.AddPeer(config.Peer{
+		AdminEnabled:      true,
+		LocalAS:           65200,
+		PeerAS:            65400,
+		PeerAddress:       bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xcafe, 0, 0, 0, 5),
+		LocalAddress:      bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe),
+		ReconnectInterval: time.Second * 15,
+		HoldTime:          time.Second * 90,
+		KeepAlive:         time.Second * 30,
+		Passive:           true,
+		RouterID:          b.RouterID(),
+		IPv6: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewDrainFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				BestOnly: true,
+			},
+		},
+	})
+}
diff --git a/protocols/netlink/netlink_reader.go b/protocols/netlink/netlink_reader.go
index 7fcfbbb2b745f335cc0ca5d4325b74c2f10666c7..dba4f45bb70b7b8d5794e5197437f8df5931e221 100644
--- a/protocols/netlink/netlink_reader.go
+++ b/protocols/netlink/netlink_reader.go
@@ -16,11 +16,11 @@ import (
 
 // Constants for IP family
 const (
-	IPFamily4 = 4 // IPv4
-	IPFamily6 = 6 // IPv6
+	IPFamily4 int = 4 // IPv4
+	IPFamily6 int = 6 // IPv6
 )
 
-// NetlinkReader read routes from the Linux Kernel and propagates it to the locRIB
+// NetlinkReader reads routes from the Linux Kernel and propagates them to the locRIB
 type NetlinkReader struct {
 	clientManager *routingtable.ClientManager
 	options       *config.Netlink
@@ -49,7 +49,7 @@ func (nr *NetlinkReader) Read() {
 
 	for {
 		// Family doesn't matter. I only filter by the rt_table here
-		routes, err := netlink.RouteListFiltered(int(IPFamily4), &netlink.Route{Table: int(nr.options.RoutingTable)}, netlink.RT_FILTER_TABLE)
+		routes, err := netlink.RouteListFiltered(IPFamily4, &netlink.Route{Table: int(nr.options.RoutingTable)}, netlink.RT_FILTER_TABLE)
 		if err != nil {
 			log.WithError(err).Panic("Failed to read routes from kernel")
 		}
@@ -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,62 @@ func (nr *NetlinkReader) propagateChanges(routes []netlink.Route) {
 
 // Add given paths to clients
 func (nr *NetlinkReader) addPathsToClients(routes []netlink.Route) {
+	// If there were no routes yet, just skip this function. There's nothing to add
+	if len(routes) == 0 {
+		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 there were no routes yet, just skip this function. There's nothing to delete
+	if len(nr.routes) == 0 {
 		nr.mu.RUnlock()
 		return
 	}
@@ -156,39 +138,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..f0fb324b8f66fd0f859dab51b58ef1f4f085e2d3 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,11 +130,10 @@ 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)
+func (nw *NetlinkWriter) addKernel(pfx bnet.Prefix) error {
+	route, err := nw.createRoute(pfx, nw.pathTable[pfx])
 	if err != nil {
-		log.Errorf("Error while creating route: %v", err)
-		return fmt.Errorf("Error while creating route: %v", err)
+		return fmt.Errorf("Could not create Route: %v", err.Error())
 	}
 
 	log.WithFields(log.Fields{
@@ -147,16 +151,16 @@ 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, err := nw.createRoute(pfx, nw.pathTable[pfx])
+	if err != nil {
+		return fmt.Errorf("Could not create Route: %v", err.Error())
+	}
+
 	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)
 	if err != nil {
 		return fmt.Errorf("Error while removing route: %v", err)
@@ -166,62 +170,29 @@ 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, error) {
+	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
+	if len(multiPath) == 1 {
+		route.Gw = multiPath[0].Gw
+	} else if len(multiPath) > 1 {
+		route.MultiPath = multiPath
+	} else {
+		return nil, fmt.Errorf("No destination address specified. At least one NextHop has to be specified in path")
+	}
 
+	return route, nil
 }
diff --git a/route/netlink_path.go b/route/netlink_path.go
index ebd721cd54e2ed9cc1d4ebeb12b5b5ae897ceae0..cb374fe8c7672d417641b07c22187620feabea58 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,29 +41,29 @@ 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 {
 		dst = bnet.NewPfxFromIPNet(r.Dst)
 		if dst.Addr().IsIPv4() {
-			src = bnet.IPv4FromOctets(0, 0, 0, 0)
+			src = bnet.IPv4(0)
 		} else {
-			src = bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0)
+			src = bnet.IPv6(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)
+			dst = bnet.NewPfx(bnet.IPv4(0), 0)
 		} else {
-			dst = bnet.NewPfx(bnet.IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), 0)
+			dst = bnet.NewPfx(bnet.IPv6(0, 0), 0)
 		}
 	}
 
@@ -73,39 +72,58 @@ 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 {
+	if s.NextHop.Compare(t.NextHop) < 0 {
 		return -1
 	}
 
-	if s.NextHop.Compare(t.NextHop) < 0 {
+	if s.NextHop.Compare(t.NextHop) > 0 {
 		return 1
 	}
 
-	if s.Src.Compare(t.Src) > 0 {
+	if s.Src.Compare(t.Src) < 0 {
 		return -1
 	}
 
-	if s.Src.Compare(t.Src) < 0 {
+	if s.Src.Compare(t.Src) > 0 {
 		return 1
 	}
 
@@ -138,7 +156,7 @@ 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
+	return s.Src == t.Src && s.Priority == t.Priority && s.Protocol == t.Protocol && s.Type == t.Type && s.Table == t.Table
 }
 
 // Copy duplicates the current object
@@ -153,8 +171,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 +182,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..84b3cde044750bcc75eb44ac89ff894c619d5cf3 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,33 +379,81 @@ 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,
+			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: "No source, no destination",
-			source: &netlink.Route{
-				Gw:       bnet.IPv4(789).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{},
-			expectError: 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{
+			source: netlink.Route{
 				Dst:      bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
 				Gw:       bnet.IPv4(789).Bytes(),
 				Protocol: ProtoKernel,
@@ -412,21 +461,26 @@ func TestNewNlPathFromNetlinkRoute(t *testing.T) {
 				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,
+			expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
+			expectedPaths: []*Path{
+				{
+					Type: NetlinkPathType,
+					NetlinkPath: &NetlinkPath{
+						Src:      bnet.IPv4(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{
+			source: netlink.Route{
 				Src:      bnet.IPv4(456).Bytes(),
 				Gw:       bnet.IPv4(789).Bytes(),
 				Protocol: ProtoKernel,
@@ -434,71 +488,382 @@ func TestNewNlPathFromNetlinkRoute(t *testing.T) {
 				Table:    254,
 				Type:     1,
 			},
-			expected: &NetlinkPath{
-				Dst:      bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0),
-				Src:      bnet.IPv4(456),
-				NextHop:  bnet.IPv4(789),
+			expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0),
+			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: "No source but no destination",
+			source: netlink.Route{
+				Gw:       bnet.IPv4(789).Bytes(),
 				Protocol: ProtoKernel,
 				Priority: 1,
 				Table:    254,
 				Type:     1,
-				Kernel:   true,
 			},
-			expectError: false,
+			expectedPfx:   bnet.Prefix{},
+			expectedPaths: []*Path{},
+			expectError:   true,
 		},
 		{
 			name: "No source but destination IPv6",
-			source: &netlink.Route{
+			source: netlink.Route{
 				Dst:      bnet.NewPfx(bnet.IPv6(2001, 0), 48).GetIPNet(),
-				Gw:       bnet.IPv6(2001, 2).Bytes(),
+				Gw:       bnet.IPv6(2001, 123).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.IPv6(2001, 0), 48),
+			expectedPaths: []*Path{
+				{
+					Type: NetlinkPathType,
+					NetlinkPath: &NetlinkPath{
+						Src:      bnet.IPv6(0, 0),
+						NextHop:  bnet.IPv6(2001, 123),
+						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(),
+			source: netlink.Route{
+				Src:      bnet.IPv6(2001, 456).Bytes(),
+				Gw:       bnet.IPv6(2001, 789).Bytes(),
 				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),
+			expectedPfx: bnet.NewPfx(bnet.IPv6(0, 0), 0),
+			expectedPaths: []*Path{
+				{
+					Type: NetlinkPathType,
+					NetlinkPath: &NetlinkPath{
+						Src:      bnet.IPv6(2001, 456),
+						NextHop:  bnet.IPv6(2001, 789),
+						Protocol: ProtoKernel,
+						Priority: 1,
+						Table:    254,
+						Type:     1,
+						Kernel:   true,
+					},
+				},
+			},
+			expectError: false,
+		},
+		{
+			name: "no source no destination",
+			source: netlink.Route{
+				Gw:       bnet.IPv4(123).Bytes(),
 				Protocol: ProtoKernel,
 				Priority: 1,
 				Table:    254,
 				Type:     1,
-				Kernel:   true,
 			},
-			expectError: false,
+			expectedPfx:   bnet.NewPfx(bnet.IPv4(0), 0),
+			expectedPaths: []*Path{{}},
+			expectError:   true,
 		},
 	}
 
 	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)
 		}
 	}
 }
+
+func TestECMP(t *testing.T) {
+	tests := []struct {
+		name  string
+		left  *Path
+		right *Path
+		ecmp  bool
+	}{
+		{
+			name: "BGP Path ecmp",
+			left: &Path{
+				Type: BGPPathType,
+				BGPPath: &BGPPath{
+					LocalPref: 100,
+					ASPathLen: 10,
+					MED:       1,
+					Origin:    123,
+				},
+			},
+			right: &Path{
+				Type: BGPPathType,
+				BGPPath: &BGPPath{
+					LocalPref: 100,
+					ASPathLen: 10,
+					MED:       1,
+					Origin:    123,
+				},
+			},
+			ecmp: true,
+		}, {
+			name: "BGP Path not ecmp",
+			left: &Path{
+				Type: BGPPathType,
+				BGPPath: &BGPPath{
+					LocalPref: 100,
+					ASPathLen: 10,
+					MED:       1,
+					Origin:    123,
+				},
+			},
+			right: &Path{
+				Type: BGPPathType,
+				BGPPath: &BGPPath{
+					LocalPref: 100,
+					ASPathLen: 5,
+					MED:       1,
+					Origin:    123,
+				},
+			},
+			ecmp: false,
+		}, {
+			name: "Netlink Path ecmp",
+			left: &Path{
+				Type: NetlinkPathType,
+				NetlinkPath: &NetlinkPath{
+					Src:      bnet.IPv4(123),
+					Priority: 1,
+					Protocol: 1,
+					Type:     1,
+					Table:    1,
+				},
+			},
+			right: &Path{
+				Type: NetlinkPathType,
+				NetlinkPath: &NetlinkPath{
+					Src:      bnet.IPv4(123),
+					Priority: 1,
+					Protocol: 1,
+					Type:     1,
+					Table:    1,
+				},
+			},
+			ecmp: true,
+		}, {
+			name: "Netlink Path not ecmp",
+			left: &Path{
+				Type: NetlinkPathType,
+				NetlinkPath: &NetlinkPath{
+					Src:      bnet.IPv4(123),
+					Priority: 1,
+					Protocol: 1,
+					Type:     1,
+					Table:    1,
+				},
+			},
+			right: &Path{
+				Type: NetlinkPathType,
+				NetlinkPath: &NetlinkPath{
+					Src:      bnet.IPv4(123),
+					Priority: 2,
+					Protocol: 1,
+					Type:     1,
+					Table:    1,
+				},
+			},
+			ecmp: false,
+		}, {
+			name: "static Path ecmp",
+			left: &Path{
+				Type: StaticPathType,
+				StaticPath: &StaticPath{
+					NextHop: bnet.IPv4(123),
+				},
+			},
+			right: &Path{
+				Type: StaticPathType,
+				StaticPath: &StaticPath{
+					NextHop: bnet.IPv4(123),
+				},
+			},
+			ecmp: true,
+		}, {
+			name: "static Path not ecmp",
+			left: &Path{
+				Type: StaticPathType,
+				StaticPath: &StaticPath{
+					NextHop: bnet.IPv4(123),
+				},
+			},
+			right: &Path{
+				Type: StaticPathType,
+				StaticPath: &StaticPath{
+					NextHop: bnet.IPv4(345),
+				},
+			},
+			// ECMP is always true for staticPath
+			ecmp: true,
+		},
+	}
+
+	for _, test := range tests {
+		ecmp := test.left.ECMP(test.right)
+		assert.Equal(t, test.ecmp, ecmp, test.name)
+	}
+}
+
+func TestNetlinkPathSelect(t *testing.T) {
+	tests := []struct {
+		name     string
+		left     *NetlinkPath
+		right    *NetlinkPath
+		expected int8
+	}{
+		{
+			name: "equal",
+			left: &NetlinkPath{
+				NextHop:  bnet.IPv4(123),
+				Src:      bnet.IPv4(234),
+				Priority: 1,
+				Protocol: 1,
+				Table:    1,
+			},
+			right: &NetlinkPath{
+				NextHop:  bnet.IPv4(123),
+				Src:      bnet.IPv4(234),
+				Priority: 1,
+				Protocol: 1,
+				Table:    1,
+			},
+			expected: 0,
+		},
+		{
+			name: "nextHop smaller",
+			left: &NetlinkPath{
+				NextHop: bnet.IPv4(1),
+			},
+			right: &NetlinkPath{
+				NextHop: bnet.IPv4(2),
+			},
+			expected: -1,
+		},
+		{
+			name: "nextHop bigger",
+			left: &NetlinkPath{
+				NextHop: bnet.IPv4(2),
+			},
+			right: &NetlinkPath{
+				NextHop: bnet.IPv4(1),
+			},
+			expected: 1,
+		},
+		{
+			name: "src smaller",
+			left: &NetlinkPath{
+				Src: bnet.IPv4(1),
+			},
+			right: &NetlinkPath{
+				Src: bnet.IPv4(2),
+			},
+			expected: -1,
+		},
+		{
+			name: "src bigger",
+			left: &NetlinkPath{
+				Src: bnet.IPv4(2),
+			},
+			right: &NetlinkPath{
+				Src: bnet.IPv4(1),
+			},
+			expected: 1,
+		},
+		{
+			name: "priority smaller",
+			left: &NetlinkPath{
+				Priority: 1,
+			},
+			right: &NetlinkPath{
+				Priority: 2,
+			},
+			expected: -1,
+		},
+		{
+			name: "priority bigger",
+			left: &NetlinkPath{
+				Priority: 2,
+			},
+			right: &NetlinkPath{
+				Priority: 1,
+			},
+			expected: 1,
+		},
+		{
+			name: "protocol smaller",
+			left: &NetlinkPath{
+				Protocol: 1,
+			},
+			right: &NetlinkPath{
+				Protocol: 2,
+			},
+			expected: -1,
+		},
+		{
+			name: "protocol bigger",
+			left: &NetlinkPath{
+				Protocol: 2,
+			},
+			right: &NetlinkPath{
+				Protocol: 1,
+			},
+			expected: 1,
+		},
+		{
+			name: "table smaller",
+			left: &NetlinkPath{
+				Table: 1,
+			},
+			right: &NetlinkPath{
+				Table: 2,
+			},
+			expected: -1,
+		},
+		{
+			name: "table bigger",
+			left: &NetlinkPath{
+				Table: 2,
+			},
+			right: &NetlinkPath{
+				Table: 1,
+			},
+			expected: 1,
+		},
+	}
+
+	for _, test := range tests {
+		result := test.left.Select(test.right)
+		assert.Equal(t, test.expected, result, test.name)
+	}
+
+}
diff --git a/routingtable/locRIB/loc_rib.go b/routingtable/locRIB/loc_rib.go
index 1c7935e41b9d55912e8a240c1f22afaa18f1616e..b1f975f65fad28e31f3fcd327836ad4f518e0f4f 100644
--- a/routingtable/locRIB/loc_rib.go
+++ b/routingtable/locRIB/loc_rib.go
@@ -175,6 +175,23 @@ func (a *LocRIB) ContainsPfxPath(pfx net.Prefix, p *route.Path) bool {
 	return false
 }
 
+func (a *LocRIB) String() string {
+	a.mu.RLock()
+	defer a.mu.RUnlock()
+
+	ret := ""
+	routes := a.rt.Dump()
+	for idx, r := range routes {
+		if idx < len(routes)-1 {
+			ret += fmt.Sprintf("%s, ", r.Prefix().String())
+		} else {
+			ret += fmt.Sprintf("%s", r.Prefix().String())
+		}
+	}
+
+	return ret
+}
+
 func (a *LocRIB) Print() string {
 	a.mu.RLock()
 	defer a.mu.RUnlock()