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