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