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