1package netlink 2 3import ( 4 "fmt" 5 "net" 6 "syscall" 7 "unsafe" 8 9 "github.com/vishvananda/netlink/nl" 10 "github.com/vishvananda/netns" 11 "golang.org/x/sys/unix" 12) 13 14const ( 15 NDA_UNSPEC = iota 16 NDA_DST 17 NDA_LLADDR 18 NDA_CACHEINFO 19 NDA_PROBES 20 NDA_VLAN 21 NDA_PORT 22 NDA_VNI 23 NDA_IFINDEX 24 NDA_MASTER 25 NDA_LINK_NETNSID 26 NDA_SRC_VNI 27 NDA_MAX = NDA_SRC_VNI 28) 29 30// Neighbor Cache Entry States. 31const ( 32 NUD_NONE = 0x00 33 NUD_INCOMPLETE = 0x01 34 NUD_REACHABLE = 0x02 35 NUD_STALE = 0x04 36 NUD_DELAY = 0x08 37 NUD_PROBE = 0x10 38 NUD_FAILED = 0x20 39 NUD_NOARP = 0x40 40 NUD_PERMANENT = 0x80 41) 42 43// Neighbor Flags 44const ( 45 NTF_USE = 0x01 46 NTF_SELF = 0x02 47 NTF_MASTER = 0x04 48 NTF_PROXY = 0x08 49 NTF_ROUTER = 0x80 50) 51 52// Ndmsg is for adding, removing or receiving information about a neighbor table entry 53type Ndmsg struct { 54 Family uint8 55 Index uint32 56 State uint16 57 Flags uint8 58 Type uint8 59} 60 61func deserializeNdmsg(b []byte) *Ndmsg { 62 var dummy Ndmsg 63 return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0])) 64} 65 66func (msg *Ndmsg) Serialize() []byte { 67 return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:] 68} 69 70func (msg *Ndmsg) Len() int { 71 return int(unsafe.Sizeof(*msg)) 72} 73 74// NeighAdd will add an IP to MAC mapping to the ARP table 75// Equivalent to: `ip neigh add ....` 76func NeighAdd(neigh *Neigh) error { 77 return pkgHandle.NeighAdd(neigh) 78} 79 80// NeighAdd will add an IP to MAC mapping to the ARP table 81// Equivalent to: `ip neigh add ....` 82func (h *Handle) NeighAdd(neigh *Neigh) error { 83 return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL) 84} 85 86// NeighSet will add or replace an IP to MAC mapping to the ARP table 87// Equivalent to: `ip neigh replace....` 88func NeighSet(neigh *Neigh) error { 89 return pkgHandle.NeighSet(neigh) 90} 91 92// NeighSet will add or replace an IP to MAC mapping to the ARP table 93// Equivalent to: `ip neigh replace....` 94func (h *Handle) NeighSet(neigh *Neigh) error { 95 return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE) 96} 97 98// NeighAppend will append an entry to FDB 99// Equivalent to: `bridge fdb append...` 100func NeighAppend(neigh *Neigh) error { 101 return pkgHandle.NeighAppend(neigh) 102} 103 104// NeighAppend will append an entry to FDB 105// Equivalent to: `bridge fdb append...` 106func (h *Handle) NeighAppend(neigh *Neigh) error { 107 return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND) 108} 109 110// NeighAppend will append an entry to FDB 111// Equivalent to: `bridge fdb append...` 112func neighAdd(neigh *Neigh, mode int) error { 113 return pkgHandle.neighAdd(neigh, mode) 114} 115 116// NeighAppend will append an entry to FDB 117// Equivalent to: `bridge fdb append...` 118func (h *Handle) neighAdd(neigh *Neigh, mode int) error { 119 req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK) 120 return neighHandle(neigh, req) 121} 122 123// NeighDel will delete an IP address from a link device. 124// Equivalent to: `ip addr del $addr dev $link` 125func NeighDel(neigh *Neigh) error { 126 return pkgHandle.NeighDel(neigh) 127} 128 129// NeighDel will delete an IP address from a link device. 130// Equivalent to: `ip addr del $addr dev $link` 131func (h *Handle) NeighDel(neigh *Neigh) error { 132 req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK) 133 return neighHandle(neigh, req) 134} 135 136func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error { 137 var family int 138 139 if neigh.Family > 0 { 140 family = neigh.Family 141 } else { 142 family = nl.GetIPFamily(neigh.IP) 143 } 144 145 msg := Ndmsg{ 146 Family: uint8(family), 147 Index: uint32(neigh.LinkIndex), 148 State: uint16(neigh.State), 149 Type: uint8(neigh.Type), 150 Flags: uint8(neigh.Flags), 151 } 152 req.AddData(&msg) 153 154 ipData := neigh.IP.To4() 155 if ipData == nil { 156 ipData = neigh.IP.To16() 157 } 158 159 dstData := nl.NewRtAttr(NDA_DST, ipData) 160 req.AddData(dstData) 161 162 if neigh.LLIPAddr != nil { 163 llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4()) 164 req.AddData(llIPData) 165 } else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil { 166 hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr)) 167 req.AddData(hwData) 168 } 169 170 if neigh.Vlan != 0 { 171 vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan))) 172 req.AddData(vlanData) 173 } 174 175 if neigh.VNI != 0 { 176 vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI))) 177 req.AddData(vniData) 178 } 179 180 if neigh.MasterIndex != 0 { 181 masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex))) 182 req.AddData(masterData) 183 } 184 185 _, err := req.Execute(unix.NETLINK_ROUTE, 0) 186 return err 187} 188 189// NeighList returns a list of IP-MAC mappings in the system (ARP table). 190// Equivalent to: `ip neighbor show`. 191// The list can be filtered by link and ip family. 192func NeighList(linkIndex, family int) ([]Neigh, error) { 193 return pkgHandle.NeighList(linkIndex, family) 194} 195 196// NeighProxyList returns a list of neighbor proxies in the system. 197// Equivalent to: `ip neighbor show proxy`. 198// The list can be filtered by link and ip family. 199func NeighProxyList(linkIndex, family int) ([]Neigh, error) { 200 return pkgHandle.NeighProxyList(linkIndex, family) 201} 202 203// NeighList returns a list of IP-MAC mappings in the system (ARP table). 204// Equivalent to: `ip neighbor show`. 205// The list can be filtered by link and ip family. 206func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) { 207 return h.NeighListExecute(Ndmsg{ 208 Family: uint8(family), 209 Index: uint32(linkIndex), 210 }) 211} 212 213// NeighProxyList returns a list of neighbor proxies in the system. 214// Equivalent to: `ip neighbor show proxy`. 215// The list can be filtered by link, ip family. 216func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) { 217 return h.NeighListExecute(Ndmsg{ 218 Family: uint8(family), 219 Index: uint32(linkIndex), 220 Flags: NTF_PROXY, 221 }) 222} 223 224// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state. 225func NeighListExecute(msg Ndmsg) ([]Neigh, error) { 226 return pkgHandle.NeighListExecute(msg) 227} 228 229// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state. 230func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) { 231 req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP) 232 req.AddData(&msg) 233 234 msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH) 235 if err != nil { 236 return nil, err 237 } 238 239 var res []Neigh 240 for _, m := range msgs { 241 ndm := deserializeNdmsg(m) 242 if msg.Index != 0 && ndm.Index != msg.Index { 243 // Ignore messages from other interfaces 244 continue 245 } 246 247 neigh, err := NeighDeserialize(m) 248 if err != nil { 249 continue 250 } 251 252 res = append(res, *neigh) 253 } 254 255 return res, nil 256} 257 258func NeighDeserialize(m []byte) (*Neigh, error) { 259 msg := deserializeNdmsg(m) 260 261 neigh := Neigh{ 262 LinkIndex: int(msg.Index), 263 Family: int(msg.Family), 264 State: int(msg.State), 265 Type: int(msg.Type), 266 Flags: int(msg.Flags), 267 } 268 269 attrs, err := nl.ParseRouteAttr(m[msg.Len():]) 270 if err != nil { 271 return nil, err 272 } 273 274 for _, attr := range attrs { 275 switch attr.Attr.Type { 276 case NDA_DST: 277 neigh.IP = net.IP(attr.Value) 278 case NDA_LLADDR: 279 // BUG: Is this a bug in the netlink library? 280 // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) 281 // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) 282 attrLen := attr.Attr.Len - unix.SizeofRtAttr 283 if attrLen == 4 { 284 neigh.LLIPAddr = net.IP(attr.Value) 285 } else if attrLen == 16 { 286 // Can be IPv6 or FireWire HWAddr 287 link, err := LinkByIndex(neigh.LinkIndex) 288 if err == nil && link.Attrs().EncapType == "tunnel6" { 289 neigh.IP = net.IP(attr.Value) 290 } else { 291 neigh.HardwareAddr = net.HardwareAddr(attr.Value) 292 } 293 } else { 294 neigh.HardwareAddr = net.HardwareAddr(attr.Value) 295 } 296 case NDA_VLAN: 297 neigh.Vlan = int(native.Uint16(attr.Value[0:2])) 298 case NDA_VNI: 299 neigh.VNI = int(native.Uint32(attr.Value[0:4])) 300 case NDA_MASTER: 301 neigh.MasterIndex = int(native.Uint32(attr.Value[0:4])) 302 } 303 } 304 305 return &neigh, nil 306} 307 308// NeighSubscribe takes a chan down which notifications will be sent 309// when neighbors are added or deleted. Close the 'done' chan to stop subscription. 310func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error { 311 return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false) 312} 313 314// NeighSubscribeAt works like NeighSubscribe plus it allows the caller 315// to choose the network namespace in which to subscribe (ns). 316func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error { 317 return neighSubscribeAt(ns, netns.None(), ch, done, nil, false) 318} 319 320// NeighSubscribeOptions contains a set of options to use with 321// NeighSubscribeWithOptions. 322type NeighSubscribeOptions struct { 323 Namespace *netns.NsHandle 324 ErrorCallback func(error) 325 ListExisting bool 326} 327 328// NeighSubscribeWithOptions work like NeighSubscribe but enable to 329// provide additional options to modify the behavior. Currently, the 330// namespace can be provided as well as an error callback. 331func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error { 332 if options.Namespace == nil { 333 none := netns.None() 334 options.Namespace = &none 335 } 336 return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting) 337} 338 339func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error { 340 s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH) 341 makeRequest := func(family int) error { 342 req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH, 343 unix.NLM_F_DUMP) 344 infmsg := nl.NewIfInfomsg(family) 345 req.AddData(infmsg) 346 if err := s.Send(req); err != nil { 347 return err 348 } 349 return nil 350 } 351 if err != nil { 352 return err 353 } 354 if done != nil { 355 go func() { 356 <-done 357 s.Close() 358 }() 359 } 360 if listExisting { 361 if err := makeRequest(unix.AF_UNSPEC); err != nil { 362 return err 363 } 364 // We have to wait for NLMSG_DONE before making AF_BRIDGE request 365 } 366 go func() { 367 defer close(ch) 368 for { 369 msgs, from, err := s.Receive() 370 if err != nil { 371 if cberr != nil { 372 cberr(err) 373 } 374 return 375 } 376 if from.Pid != nl.PidKernel { 377 if cberr != nil { 378 cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) 379 } 380 continue 381 } 382 for _, m := range msgs { 383 if m.Header.Type == unix.NLMSG_DONE { 384 if listExisting { 385 // This will be called after handling AF_UNSPEC 386 // list request, we have to wait for NLMSG_DONE 387 // before making another request 388 if err := makeRequest(unix.AF_BRIDGE); err != nil { 389 if cberr != nil { 390 cberr(err) 391 } 392 return 393 } 394 listExisting = false 395 } 396 continue 397 } 398 if m.Header.Type == unix.NLMSG_ERROR { 399 native := nl.NativeEndian() 400 error := int32(native.Uint32(m.Data[0:4])) 401 if error == 0 { 402 continue 403 } 404 if cberr != nil { 405 cberr(syscall.Errno(-error)) 406 } 407 return 408 } 409 neigh, err := NeighDeserialize(m.Data) 410 if err != nil { 411 if cberr != nil { 412 cberr(err) 413 } 414 return 415 } 416 ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh} 417 } 418 } 419 }() 420 421 return nil 422} 423