1package netlink 2 3import ( 4 "fmt" 5 "net" 6 "syscall" 7 8 "github.com/vishvananda/netlink/nl" 9) 10 11// RtAttr is shared so it is in netlink_linux.go 12 13// RouteAdd will add a route to the system. 14// Equivalent to: `ip route add $route` 15func RouteAdd(route *Route) error { 16 req := nl.NewNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 17 return routeHandle(route, req, nl.NewRtMsg()) 18} 19 20// RouteAdd will delete a route from the system. 21// Equivalent to: `ip route del $route` 22func RouteDel(route *Route) error { 23 req := nl.NewNetlinkRequest(syscall.RTM_DELROUTE, syscall.NLM_F_ACK) 24 return routeHandle(route, req, nl.NewRtDelMsg()) 25} 26 27func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { 28 if (route.Dst == nil || route.Dst.IP == nil) && route.Src == nil && route.Gw == nil { 29 return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil") 30 } 31 32 msg.Scope = uint8(route.Scope) 33 family := -1 34 var rtAttrs []*nl.RtAttr 35 36 if route.Dst != nil && route.Dst.IP != nil { 37 dstLen, _ := route.Dst.Mask.Size() 38 msg.Dst_len = uint8(dstLen) 39 dstFamily := nl.GetIPFamily(route.Dst.IP) 40 family = dstFamily 41 var dstData []byte 42 if dstFamily == FAMILY_V4 { 43 dstData = route.Dst.IP.To4() 44 } else { 45 dstData = route.Dst.IP.To16() 46 } 47 rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData)) 48 } 49 50 if route.Src != nil { 51 srcFamily := nl.GetIPFamily(route.Src) 52 if family != -1 && family != srcFamily { 53 return fmt.Errorf("source and destination ip are not the same IP family") 54 } 55 family = srcFamily 56 var srcData []byte 57 if srcFamily == FAMILY_V4 { 58 srcData = route.Src.To4() 59 } else { 60 srcData = route.Src.To16() 61 } 62 // The commonly used src ip for routes is actually PREFSRC 63 rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PREFSRC, srcData)) 64 } 65 66 if route.Gw != nil { 67 gwFamily := nl.GetIPFamily(route.Gw) 68 if family != -1 && family != gwFamily { 69 return fmt.Errorf("gateway, source, and destination ip are not the same IP family") 70 } 71 family = gwFamily 72 var gwData []byte 73 if gwFamily == FAMILY_V4 { 74 gwData = route.Gw.To4() 75 } else { 76 gwData = route.Gw.To16() 77 } 78 rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData)) 79 } 80 81 msg.Family = uint8(family) 82 83 req.AddData(msg) 84 for _, attr := range rtAttrs { 85 req.AddData(attr) 86 } 87 88 var ( 89 b = make([]byte, 4) 90 native = nl.NativeEndian() 91 ) 92 native.PutUint32(b, uint32(route.LinkIndex)) 93 94 req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b)) 95 96 _, err := req.Execute(syscall.NETLINK_ROUTE, 0) 97 return err 98} 99 100// RouteList gets a list of routes in the system. 101// Equivalent to: `ip route show`. 102// The list can be filtered by link and ip family. 103func RouteList(link Link, family int) ([]Route, error) { 104 req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) 105 msg := nl.NewIfInfomsg(family) 106 req.AddData(msg) 107 108 msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) 109 if err != nil { 110 return nil, err 111 } 112 113 index := 0 114 if link != nil { 115 base := link.Attrs() 116 ensureIndex(base) 117 index = base.Index 118 } 119 120 native := nl.NativeEndian() 121 var res []Route 122MsgLoop: 123 for _, m := range msgs { 124 msg := nl.DeserializeRtMsg(m) 125 126 if msg.Flags&syscall.RTM_F_CLONED != 0 { 127 // Ignore cloned routes 128 continue 129 } 130 131 if msg.Table != syscall.RT_TABLE_MAIN { 132 // Ignore non-main tables 133 continue 134 } 135 136 attrs, err := nl.ParseRouteAttr(m[msg.Len():]) 137 if err != nil { 138 return nil, err 139 } 140 141 route := Route{Scope: Scope(msg.Scope)} 142 for _, attr := range attrs { 143 switch attr.Attr.Type { 144 case syscall.RTA_GATEWAY: 145 route.Gw = net.IP(attr.Value) 146 case syscall.RTA_PREFSRC: 147 route.Src = net.IP(attr.Value) 148 case syscall.RTA_DST: 149 route.Dst = &net.IPNet{ 150 IP: attr.Value, 151 Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), 152 } 153 case syscall.RTA_OIF: 154 routeIndex := int(native.Uint32(attr.Value[0:4])) 155 if link != nil && routeIndex != index { 156 // Ignore routes from other interfaces 157 continue MsgLoop 158 } 159 route.LinkIndex = routeIndex 160 } 161 } 162 res = append(res, route) 163 } 164 165 return res, nil 166} 167 168// RouteGet gets a route to a specific destination from the host system. 169// Equivalent to: 'ip route get'. 170func RouteGet(destination net.IP) ([]Route, error) { 171 req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_REQUEST) 172 family := nl.GetIPFamily(destination) 173 var destinationData []byte 174 var bitlen uint8 175 if family == FAMILY_V4 { 176 destinationData = destination.To4() 177 bitlen = 32 178 } else { 179 destinationData = destination.To16() 180 bitlen = 128 181 } 182 msg := &nl.RtMsg{} 183 msg.Family = uint8(family) 184 msg.Dst_len = bitlen 185 req.AddData(msg) 186 187 rtaDst := nl.NewRtAttr(syscall.RTA_DST, destinationData) 188 req.AddData(rtaDst) 189 190 msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) 191 if err != nil { 192 return nil, err 193 } 194 195 native := nl.NativeEndian() 196 var res []Route 197 for _, m := range msgs { 198 msg := nl.DeserializeRtMsg(m) 199 attrs, err := nl.ParseRouteAttr(m[msg.Len():]) 200 if err != nil { 201 return nil, err 202 } 203 204 route := Route{} 205 for _, attr := range attrs { 206 switch attr.Attr.Type { 207 case syscall.RTA_GATEWAY: 208 route.Gw = net.IP(attr.Value) 209 case syscall.RTA_PREFSRC: 210 route.Src = net.IP(attr.Value) 211 case syscall.RTA_DST: 212 route.Dst = &net.IPNet{ 213 IP: attr.Value, 214 Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), 215 } 216 case syscall.RTA_OIF: 217 routeIndex := int(native.Uint32(attr.Value[0:4])) 218 route.LinkIndex = routeIndex 219 } 220 } 221 res = append(res, route) 222 } 223 return res, nil 224 225} 226