github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/addr_linux.go (about) 1 package netlink 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "syscall" 8 9 "github.com/sagernet/netlink/nl" 10 "github.com/vishvananda/netns" 11 "golang.org/x/sys/unix" 12 ) 13 14 // AddrAdd will add an IP address to a link device. 15 // 16 // Equivalent to: `ip addr add $addr dev $link` 17 // 18 // If `addr` is an IPv4 address and the broadcast address is not given, it 19 // will be automatically computed based on the IP mask if /30 or larger. 20 func AddrAdd(link Link, addr *Addr) error { 21 return pkgHandle.AddrAdd(link, addr) 22 } 23 24 // AddrAdd will add an IP address to a link device. 25 // 26 // Equivalent to: `ip addr add $addr dev $link` 27 // 28 // If `addr` is an IPv4 address and the broadcast address is not given, it 29 // will be automatically computed based on the IP mask if /30 or larger. 30 func (h *Handle) AddrAdd(link Link, addr *Addr) error { 31 req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK) 32 return h.addrHandle(link, addr, req) 33 } 34 35 // AddrReplace will replace (or, if not present, add) an IP address on a link device. 36 // 37 // Equivalent to: `ip addr replace $addr dev $link` 38 // 39 // If `addr` is an IPv4 address and the broadcast address is not given, it 40 // will be automatically computed based on the IP mask if /30 or larger. 41 func AddrReplace(link Link, addr *Addr) error { 42 return pkgHandle.AddrReplace(link, addr) 43 } 44 45 // AddrReplace will replace (or, if not present, add) an IP address on a link device. 46 // 47 // Equivalent to: `ip addr replace $addr dev $link` 48 // 49 // If `addr` is an IPv4 address and the broadcast address is not given, it 50 // will be automatically computed based on the IP mask if /30 or larger. 51 func (h *Handle) AddrReplace(link Link, addr *Addr) error { 52 req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK) 53 return h.addrHandle(link, addr, req) 54 } 55 56 // AddrDel will delete an IP address from a link device. 57 // 58 // Equivalent to: `ip addr del $addr dev $link` 59 // 60 // If `addr` is an IPv4 address and the broadcast address is not given, it 61 // will be automatically computed based on the IP mask if /30 or larger. 62 func AddrDel(link Link, addr *Addr) error { 63 return pkgHandle.AddrDel(link, addr) 64 } 65 66 // AddrDel will delete an IP address from a link device. 67 // Equivalent to: `ip addr del $addr dev $link` 68 // 69 // If `addr` is an IPv4 address and the broadcast address is not given, it 70 // will be automatically computed based on the IP mask if /30 or larger. 71 func (h *Handle) AddrDel(link Link, addr *Addr) error { 72 req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK) 73 return h.addrHandle(link, addr, req) 74 } 75 76 func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { 77 base := link.Attrs() 78 if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { 79 return fmt.Errorf("label must begin with interface name") 80 } 81 h.ensureIndex(base) 82 83 family := nl.GetIPFamily(addr.IP) 84 85 msg := nl.NewIfAddrmsg(family) 86 msg.Index = uint32(base.Index) 87 msg.Scope = uint8(addr.Scope) 88 mask := addr.Mask 89 if addr.Peer != nil { 90 mask = addr.Peer.Mask 91 } 92 prefixlen, masklen := mask.Size() 93 msg.Prefixlen = uint8(prefixlen) 94 req.AddData(msg) 95 96 var localAddrData []byte 97 if family == FAMILY_V4 { 98 localAddrData = addr.IP.To4() 99 } else { 100 localAddrData = addr.IP.To16() 101 } 102 103 localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData) 104 req.AddData(localData) 105 var peerAddrData []byte 106 if addr.Peer != nil { 107 if family == FAMILY_V4 { 108 peerAddrData = addr.Peer.IP.To4() 109 } else { 110 peerAddrData = addr.Peer.IP.To16() 111 } 112 } else { 113 peerAddrData = localAddrData 114 } 115 116 addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData) 117 req.AddData(addressData) 118 119 if addr.Flags != 0 { 120 if addr.Flags <= 0xff { 121 msg.IfAddrmsg.Flags = uint8(addr.Flags) 122 } else { 123 b := make([]byte, 4) 124 native.PutUint32(b, uint32(addr.Flags)) 125 flagsData := nl.NewRtAttr(unix.IFA_FLAGS, b) 126 req.AddData(flagsData) 127 } 128 } 129 130 if family == FAMILY_V4 { 131 // Automatically set the broadcast address if it is unset and the 132 // subnet is large enough to sensibly have one (/30 or larger). 133 // See: RFC 3021 134 if addr.Broadcast == nil && prefixlen < 31 { 135 calcBroadcast := make(net.IP, masklen/8) 136 for i := range localAddrData { 137 calcBroadcast[i] = localAddrData[i] | ^mask[i] 138 } 139 addr.Broadcast = calcBroadcast 140 } 141 142 if addr.Broadcast != nil { 143 req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast)) 144 } 145 146 if addr.Label != "" { 147 labelData := nl.NewRtAttr(unix.IFA_LABEL, nl.ZeroTerminated(addr.Label)) 148 req.AddData(labelData) 149 } 150 } 151 152 // 0 is the default value for these attributes. However, 0 means "expired", while the least-surprising default 153 // value should be "forever". To compensate for that, only add the attributes if at least one of the values is 154 // non-zero, which means the caller has explicitly set them 155 if addr.ValidLft > 0 || addr.PreferedLft > 0 { 156 cachedata := nl.IfaCacheInfo{unix.IfaCacheinfo{ 157 Valid: uint32(addr.ValidLft), 158 Prefered: uint32(addr.PreferedLft), 159 }} 160 req.AddData(nl.NewRtAttr(unix.IFA_CACHEINFO, cachedata.Serialize())) 161 } 162 163 _, err := req.Execute(unix.NETLINK_ROUTE, 0) 164 return err 165 } 166 167 // AddrList gets a list of IP addresses in the system. 168 // Equivalent to: `ip addr show`. 169 // The list can be filtered by link and ip family. 170 func AddrList(link Link, family int) ([]Addr, error) { 171 return pkgHandle.AddrList(link, family) 172 } 173 174 // AddrList gets a list of IP addresses in the system. 175 // Equivalent to: `ip addr show`. 176 // The list can be filtered by link and ip family. 177 func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { 178 req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) 179 msg := nl.NewIfAddrmsg(family) 180 req.AddData(msg) 181 182 msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR) 183 if err != nil { 184 return nil, err 185 } 186 187 indexFilter := 0 188 if link != nil { 189 base := link.Attrs() 190 h.ensureIndex(base) 191 indexFilter = base.Index 192 } 193 194 var res []Addr 195 for _, m := range msgs { 196 addr, msgFamily, err := parseAddr(m) 197 if err != nil { 198 return res, err 199 } 200 201 if link != nil && addr.LinkIndex != indexFilter { 202 // Ignore messages from other interfaces 203 continue 204 } 205 206 if family != FAMILY_ALL && msgFamily != family { 207 continue 208 } 209 210 res = append(res, addr) 211 } 212 213 return res, nil 214 } 215 216 func parseAddr(m []byte) (addr Addr, family int, err error) { 217 msg := nl.DeserializeIfAddrmsg(m) 218 219 family = -1 220 addr.LinkIndex = -1 221 222 attrs, err1 := nl.ParseRouteAttr(m[msg.Len():]) 223 if err1 != nil { 224 err = err1 225 return 226 } 227 228 family = int(msg.Family) 229 addr.LinkIndex = int(msg.Index) 230 231 var local, dst *net.IPNet 232 for _, attr := range attrs { 233 switch attr.Attr.Type { 234 case unix.IFA_ADDRESS: 235 dst = &net.IPNet{ 236 IP: attr.Value, 237 Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), 238 } 239 case unix.IFA_LOCAL: 240 // iproute2 manual: 241 // If a peer address is specified, the local address 242 // cannot have a prefix length. The network prefix is 243 // associated with the peer rather than with the local 244 // address. 245 n := 8 * len(attr.Value) 246 local = &net.IPNet{ 247 IP: attr.Value, 248 Mask: net.CIDRMask(n, n), 249 } 250 case unix.IFA_BROADCAST: 251 addr.Broadcast = attr.Value 252 case unix.IFA_LABEL: 253 addr.Label = string(attr.Value[:len(attr.Value)-1]) 254 case unix.IFA_FLAGS: 255 addr.Flags = int(native.Uint32(attr.Value[0:4])) 256 case unix.IFA_CACHEINFO: 257 ci := nl.DeserializeIfaCacheInfo(attr.Value) 258 addr.PreferedLft = int(ci.Prefered) 259 addr.ValidLft = int(ci.Valid) 260 } 261 } 262 263 // libnl addr.c comment: 264 // IPv6 sends the local address as IFA_ADDRESS with no 265 // IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS 266 // with IFA_ADDRESS being the peer address if they differ 267 // 268 // But obviously, as there are IPv6 PtP addresses, too, 269 // IFA_LOCAL should also be handled for IPv6. 270 if local != nil { 271 if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) { 272 addr.IPNet = dst 273 } else { 274 addr.IPNet = local 275 addr.Peer = dst 276 } 277 } else { 278 addr.IPNet = dst 279 } 280 281 addr.Scope = int(msg.Scope) 282 283 return 284 } 285 286 type AddrUpdate struct { 287 LinkAddress net.IPNet 288 LinkIndex int 289 Flags int 290 Scope int 291 PreferedLft int 292 ValidLft int 293 NewAddr bool // true=added false=deleted 294 } 295 296 // AddrSubscribe takes a chan down which notifications will be sent 297 // when addresses change. Close the 'done' chan to stop subscription. 298 func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { 299 return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil) 300 } 301 302 // AddrSubscribeAt works like AddrSubscribe plus it allows the caller 303 // to choose the network namespace in which to subscribe (ns). 304 func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { 305 return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil) 306 } 307 308 // AddrSubscribeOptions contains a set of options to use with 309 // AddrSubscribeWithOptions. 310 type AddrSubscribeOptions struct { 311 Namespace *netns.NsHandle 312 ErrorCallback func(error) 313 ListExisting bool 314 ReceiveBufferSize int 315 ReceiveTimeout *unix.Timeval 316 } 317 318 // AddrSubscribeWithOptions work like AddrSubscribe but enable to 319 // provide additional options to modify the behavior. Currently, the 320 // namespace can be provided as well as an error callback. 321 func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error { 322 if options.Namespace == nil { 323 none := netns.None() 324 options.Namespace = &none 325 } 326 return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting, options.ReceiveBufferSize, options.ReceiveTimeout) 327 } 328 329 func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool, rcvbuf int, rcvTimeout *unix.Timeval) error { 330 s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR) 331 if err != nil { 332 return err 333 } 334 if rcvTimeout != nil { 335 if err := s.SetReceiveTimeout(rcvTimeout); err != nil { 336 return err 337 } 338 } 339 340 if done != nil { 341 go func() { 342 <-done 343 s.Close() 344 }() 345 } 346 if rcvbuf != 0 { 347 err = pkgHandle.SetSocketReceiveBufferSize(rcvbuf, false) 348 if err != nil { 349 return err 350 } 351 } 352 if listExisting { 353 req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR, 354 unix.NLM_F_DUMP) 355 infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC) 356 req.AddData(infmsg) 357 if err := s.Send(req); err != nil { 358 return err 359 } 360 } 361 go func() { 362 defer close(ch) 363 for { 364 msgs, from, err := s.Receive() 365 if err != nil { 366 if cberr != nil { 367 cberr(fmt.Errorf("Receive failed: %v", 368 err)) 369 } 370 return 371 } 372 if from.Pid != nl.PidKernel { 373 if cberr != nil { 374 cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)) 375 } 376 continue 377 } 378 for _, m := range msgs { 379 if m.Header.Type == unix.NLMSG_DONE { 380 continue 381 } 382 if m.Header.Type == unix.NLMSG_ERROR { 383 error := int32(native.Uint32(m.Data[0:4])) 384 if error == 0 { 385 continue 386 } 387 if cberr != nil { 388 cberr(fmt.Errorf("error message: %v", 389 syscall.Errno(-error))) 390 } 391 continue 392 } 393 msgType := m.Header.Type 394 if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR { 395 if cberr != nil { 396 cberr(fmt.Errorf("bad message type: %d", msgType)) 397 } 398 continue 399 } 400 401 addr, _, err := parseAddr(m.Data) 402 if err != nil { 403 if cberr != nil { 404 cberr(fmt.Errorf("could not parse address: %v", err)) 405 } 406 continue 407 } 408 409 ch <- AddrUpdate{LinkAddress: *addr.IPNet, 410 LinkIndex: addr.LinkIndex, 411 NewAddr: msgType == unix.RTM_NEWADDR, 412 Flags: addr.Flags, 413 Scope: addr.Scope, 414 PreferedLft: addr.PreferedLft, 415 ValidLft: addr.ValidLft} 416 } 417 } 418 }() 419 420 return nil 421 }