github.com/vishvananda/netlink@v1.3.0/addr_linux.go (about) 1 package netlink 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 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 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 family := nl.GetIPFamily(addr.IP) 78 msg := nl.NewIfAddrmsg(family) 79 msg.Scope = uint8(addr.Scope) 80 if link == nil { 81 msg.Index = uint32(addr.LinkIndex) 82 } else { 83 base := link.Attrs() 84 if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { 85 return fmt.Errorf("label must begin with interface name") 86 } 87 h.ensureIndex(base) 88 msg.Index = uint32(base.Index) 89 } 90 mask := addr.Mask 91 if addr.Peer != nil { 92 mask = addr.Peer.Mask 93 } 94 prefixlen, masklen := mask.Size() 95 msg.Prefixlen = uint8(prefixlen) 96 req.AddData(msg) 97 98 var localAddrData []byte 99 if family == FAMILY_V4 { 100 localAddrData = addr.IP.To4() 101 } else { 102 localAddrData = addr.IP.To16() 103 } 104 105 localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData) 106 req.AddData(localData) 107 var peerAddrData []byte 108 if addr.Peer != nil { 109 if family == FAMILY_V4 { 110 peerAddrData = addr.Peer.IP.To4() 111 } else { 112 peerAddrData = addr.Peer.IP.To16() 113 } 114 } else { 115 peerAddrData = localAddrData 116 } 117 118 addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData) 119 req.AddData(addressData) 120 121 if addr.Flags != 0 { 122 if addr.Flags <= 0xff { 123 msg.IfAddrmsg.Flags = uint8(addr.Flags) 124 } else { 125 b := make([]byte, 4) 126 native.PutUint32(b, uint32(addr.Flags)) 127 flagsData := nl.NewRtAttr(unix.IFA_FLAGS, b) 128 req.AddData(flagsData) 129 } 130 } 131 132 if family == FAMILY_V4 { 133 // Automatically set the broadcast address if it is unset and the 134 // subnet is large enough to sensibly have one (/30 or larger). 135 // See: RFC 3021 136 if addr.Broadcast == nil && prefixlen < 31 { 137 calcBroadcast := make(net.IP, masklen/8) 138 for i := range localAddrData { 139 calcBroadcast[i] = localAddrData[i] | ^mask[i] 140 } 141 addr.Broadcast = calcBroadcast 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 func AddrList(link Link, family int) ([]Addr, error) { 173 return pkgHandle.AddrList(link, family) 174 } 175 176 // AddrList gets a list of IP addresses in the system. 177 // Equivalent to: `ip addr show`. 178 // The list can be filtered by link and ip family. 179 func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { 180 req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP) 181 msg := nl.NewIfAddrmsg(family) 182 req.AddData(msg) 183 184 msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR) 185 if err != nil { 186 return nil, err 187 } 188 189 indexFilter := 0 190 if link != nil { 191 base := link.Attrs() 192 h.ensureIndex(base) 193 indexFilter = base.Index 194 } 195 196 var res []Addr 197 for _, m := range msgs { 198 addr, msgFamily, err := parseAddr(m) 199 if err != nil { 200 return res, err 201 } 202 203 if link != nil && addr.LinkIndex != indexFilter { 204 // Ignore messages from other interfaces 205 continue 206 } 207 208 if family != FAMILY_ALL && msgFamily != family { 209 continue 210 } 211 212 res = append(res, addr) 213 } 214 215 return res, nil 216 } 217 218 func parseAddr(m []byte) (addr Addr, family int, err error) { 219 msg := nl.DeserializeIfAddrmsg(m) 220 221 family = -1 222 addr.LinkIndex = -1 223 224 attrs, err1 := nl.ParseRouteAttr(m[msg.Len():]) 225 if err1 != nil { 226 err = err1 227 return 228 } 229 230 family = int(msg.Family) 231 addr.LinkIndex = int(msg.Index) 232 233 var local, dst *net.IPNet 234 for _, attr := range attrs { 235 switch attr.Attr.Type { 236 case unix.IFA_ADDRESS: 237 dst = &net.IPNet{ 238 IP: attr.Value, 239 Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), 240 } 241 case unix.IFA_LOCAL: 242 // iproute2 manual: 243 // If a peer address is specified, the local address 244 // cannot have a prefix length. The network prefix is 245 // associated with the peer rather than with the local 246 // address. 247 n := 8 * len(attr.Value) 248 local = &net.IPNet{ 249 IP: attr.Value, 250 Mask: net.CIDRMask(n, n), 251 } 252 case unix.IFA_BROADCAST: 253 addr.Broadcast = attr.Value 254 case unix.IFA_LABEL: 255 addr.Label = string(attr.Value[:len(attr.Value)-1]) 256 case unix.IFA_FLAGS: 257 addr.Flags = int(native.Uint32(attr.Value[0:4])) 258 case unix.IFA_CACHEINFO: 259 ci := nl.DeserializeIfaCacheInfo(attr.Value) 260 addr.PreferedLft = int(ci.Prefered) 261 addr.ValidLft = int(ci.Valid) 262 } 263 } 264 265 // libnl addr.c comment: 266 // IPv6 sends the local address as IFA_ADDRESS with no 267 // IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS 268 // with IFA_ADDRESS being the peer address if they differ 269 // 270 // But obviously, as there are IPv6 PtP addresses, too, 271 // IFA_LOCAL should also be handled for IPv6. 272 if local != nil { 273 if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) { 274 addr.IPNet = dst 275 } else { 276 addr.IPNet = local 277 addr.Peer = dst 278 } 279 } else { 280 addr.IPNet = dst 281 } 282 283 addr.Scope = int(msg.Scope) 284 285 return 286 } 287 288 type AddrUpdate struct { 289 LinkAddress net.IPNet 290 LinkIndex int 291 Flags int 292 Scope int 293 PreferedLft int 294 ValidLft int 295 NewAddr bool // true=added false=deleted 296 } 297 298 // AddrSubscribe takes a chan down which notifications will be sent 299 // when addresses change. Close the 'done' chan to stop subscription. 300 func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { 301 return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil, false) 302 } 303 304 // AddrSubscribeAt works like AddrSubscribe plus it allows the caller 305 // to choose the network namespace in which to subscribe (ns). 306 func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { 307 return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil, false) 308 } 309 310 // AddrSubscribeOptions contains a set of options to use with 311 // AddrSubscribeWithOptions. 312 type AddrSubscribeOptions struct { 313 Namespace *netns.NsHandle 314 ErrorCallback func(error) 315 ListExisting bool 316 ReceiveBufferSize int 317 ReceiveBufferForceSize bool 318 ReceiveTimeout *unix.Timeval 319 } 320 321 // AddrSubscribeWithOptions work like AddrSubscribe but enable to 322 // provide additional options to modify the behavior. Currently, the 323 // namespace can be provided as well as an error callback. 324 func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error { 325 if options.Namespace == nil { 326 none := netns.None() 327 options.Namespace = &none 328 } 329 return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting, 330 options.ReceiveBufferSize, options.ReceiveTimeout, options.ReceiveBufferForceSize) 331 } 332 333 func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool, 334 rcvbuf int, rcvTimeout *unix.Timeval, rcvBufForce bool) error { 335 s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR) 336 if err != nil { 337 return err 338 } 339 if rcvTimeout != nil { 340 if err := s.SetReceiveTimeout(rcvTimeout); err != nil { 341 return err 342 } 343 } 344 if rcvbuf != 0 { 345 err = s.SetReceiveBufferSize(rcvbuf, rcvBufForce) 346 if err != nil { 347 return err 348 } 349 } 350 if done != nil { 351 go func() { 352 <-done 353 s.Close() 354 }() 355 } 356 if listExisting { 357 req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR, 358 unix.NLM_F_DUMP) 359 infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC) 360 req.AddData(infmsg) 361 if err := s.Send(req); err != nil { 362 return err 363 } 364 } 365 go func() { 366 defer close(ch) 367 for { 368 msgs, from, err := s.Receive() 369 if err != nil { 370 if cberr != nil { 371 cberr(fmt.Errorf("Receive failed: %v", 372 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 continue 385 } 386 if m.Header.Type == unix.NLMSG_ERROR { 387 error := int32(native.Uint32(m.Data[0:4])) 388 if error == 0 { 389 continue 390 } 391 if cberr != nil { 392 cberr(fmt.Errorf("error message: %v", 393 syscall.Errno(-error))) 394 } 395 continue 396 } 397 msgType := m.Header.Type 398 if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR { 399 if cberr != nil { 400 cberr(fmt.Errorf("bad message type: %d", msgType)) 401 } 402 continue 403 } 404 405 addr, _, err := parseAddr(m.Data) 406 if err != nil { 407 if cberr != nil { 408 cberr(fmt.Errorf("could not parse address: %v", err)) 409 } 410 continue 411 } 412 413 ch <- AddrUpdate{LinkAddress: *addr.IPNet, 414 LinkIndex: addr.LinkIndex, 415 NewAddr: msgType == unix.RTM_NEWADDR, 416 Flags: addr.Flags, 417 Scope: addr.Scope, 418 PreferedLft: addr.PreferedLft, 419 ValidLft: addr.ValidLft} 420 } 421 } 422 }() 423 424 return nil 425 }