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