Skip to content

Commit

Permalink
Add support for the expires option of ip route
Browse files Browse the repository at this point in the history
  • Loading branch information
jrife authored and albertjin committed Nov 7, 2024
1 parent 976bd8d commit d9f7af7
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
16 changes: 16 additions & 0 deletions route.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ type Encap interface {
Equal(Encap) bool
}

type RouteCacheInfo struct {
Users uint32
Age uint32
Expires int32
Error uint32
Used uint32
Id uint32
Ts uint32
Tsage uint32
}

//Protocol describe what was the originator of the route
type RouteProtocol int

Expand Down Expand Up @@ -85,6 +96,8 @@ type Route struct {
QuickACK int
Congctl string
FastOpenNoCookie int
Expires int
CacheInfo *RouteCacheInfo
}

func (r Route) String() string {
Expand Down Expand Up @@ -115,6 +128,9 @@ func (r Route) String() string {
elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags()))
elems = append(elems, fmt.Sprintf("Table: %d", r.Table))
elems = append(elems, fmt.Sprintf("Realm: %d", r.Realm))
if r.Expires != 0 {
elems = append(elems, fmt.Sprintf("Expires: %dsec", r.Expires))
}
return fmt.Sprintf("{%s}", strings.Join(elems, " "))
}

Expand Down
31 changes: 31 additions & 0 deletions route_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,12 @@ func (h *Handle) prepareRouteReq(route *Route, req *nl.NetlinkRequest, msg *nl.R
msg.Type = uint8(route.Type)
}

if route.Expires > 0 {
b := make([]byte, 4)
native.PutUint32(b, uint32(route.Expires))
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_EXPIRES, b))
}

var metrics []*nl.RtAttr
if route.MTU > 0 {
b := nl.Uint32Attr(uint32(route.MTU))
Expand Down Expand Up @@ -1294,6 +1300,25 @@ func (h *Handle) RouteListFilteredIter(family int, filter *Route, filterMask uin
return executeErr
}

// deserializeRouteCacheInfo decodes a RTA_CACHEINFO attribute into a RouteCacheInfo struct
func deserializeRouteCacheInfo(b []byte) (*RouteCacheInfo, error) {
if len(b) != 32 {
return nil, unix.EINVAL
}

e := nl.NativeEndian()
return &RouteCacheInfo{
e.Uint32(b),
e.Uint32(b[4:]),
int32(e.Uint32(b[8:])),
e.Uint32(b[12:]),
e.Uint32(b[16:]),
e.Uint32(b[20:]),
e.Uint32(b[24:]),
e.Uint32(b[28:]),
}, nil
}

// deserializeRoute decodes a binary netlink message into a Route struct
func deserializeRoute(m []byte) (Route, error) {
msg := nl.DeserializeRtMsg(m)
Expand Down Expand Up @@ -1337,6 +1362,12 @@ func deserializeRoute(m []byte) (Route, error) {
route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_PRIORITY:
route.Priority = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_CACHEINFO:
route.CacheInfo, err = deserializeRouteCacheInfo(attr.Value)
if err != nil {
return route, err
}
route.Expires = int(route.CacheInfo.Expires) / 100
case unix.RTA_FLOW:
route.Realm = int(native.Uint32(attr.Value[0:4]))
case unix.RTA_TABLE:
Expand Down
21 changes: 20 additions & 1 deletion route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func TestRoute6AddDel(t *testing.T) {
IP: net.ParseIP("2001:db8::0"),
Mask: net.CIDRMask(64, 128),
}
route := Route{LinkIndex: link.Attrs().Index, Dst: dst}
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Expires: 10}
if err := RouteAdd(&route); err != nil {
t.Fatal(err)
}
Expand All @@ -204,6 +204,25 @@ func TestRoute6AddDel(t *testing.T) {
t.Fatal("Route not added properly")
}

// route expiry is supported by kernel 4.4+
k, m, err := KernelVersion()
if err != nil {
t.Fatal(err)
}
if k > 4 || (k == 4 && m > 4) {
foundExpires := false
for _, route := range routes {
if route.Dst.IP.Equal(dst.IP) {
if route.Expires > 0 && route.Expires <= 10 {
foundExpires = true
}
}
}
if !foundExpires {
t.Fatal("Route 'expires' not set")
}
}

dstIP := net.ParseIP("2001:db8::1")
routeToDstIP, err := RouteGet(dstIP)
if err != nil {
Expand Down

0 comments on commit d9f7af7

Please sign in to comment.