diff --git a/nl/nl_linux.go b/nl/nl_linux.go index 9c2499e..72f2813 100644 --- a/nl/nl_linux.go +++ b/nl/nl_linux.go @@ -248,6 +248,9 @@ done: continue } res = append(res, m.Data) + if m.Header.Flags&syscall.NLM_F_MULTI == 0 { + break done + } } } return res, nil diff --git a/route_linux.go b/route_linux.go index 2e502ff..81182f3 100644 --- a/route_linux.go +++ b/route_linux.go @@ -164,3 +164,62 @@ func RouteList(link Link, family int) ([]Route, error) { return res, 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) { + req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.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(syscall.RTA_DST, destinationData) + req.AddData(rtaDst) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) + if err != nil { + return nil, err + } + + native := nl.NativeEndian() + res := make([]Route, 0) + for _, m := range msgs { + msg := nl.DeserializeRtMsg(m) + attrs, err := nl.ParseRouteAttr(m[msg.Len():]) + if err != nil { + return nil, err + } + + route := Route{} + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_GATEWAY: + route.Gw = net.IP(attr.Value) + case syscall.RTA_PREFSRC: + route.Src = net.IP(attr.Value) + case syscall.RTA_DST: + route.Dst = &net.IPNet{ + IP: attr.Value, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), + } + case syscall.RTA_OIF: + routeIndex := int(native.Uint32(attr.Value[0:4])) + route.LinkIndex = routeIndex + } + } + res = append(res, route) + } + return res, nil + +} diff --git a/route_test.go b/route_test.go index c1cc16d..7f8d42c 100644 --- a/route_test.go +++ b/route_test.go @@ -34,7 +34,17 @@ func TestRouteAddDel(t *testing.T) { t.Fatal(err) } if len(routes) != 1 { - t.Fatal("Link not removed properly") + t.Fatal("Link not added properly") + } + + dstIP := net.ParseIP("192.168.0.42") + routeToDstIP, err := RouteGet(dstIP) + if err != nil { + t.Fatal(err) + } + + if len(routeToDstIP) == 0 { + t.Fatal("Default route not present") } err = RouteDel(&route)