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) 12 13// IFA_FLAGS is a u32 attribute. 14const IFA_FLAGS = 0x8 15 16// AddrAdd will add an IP address to a link device. 17// Equivalent to: `ip addr add $addr dev $link` 18func AddrAdd(link Link, addr *Addr) error { 19 return pkgHandle.AddrAdd(link, addr) 20} 21 22// AddrAdd will add an IP address to a link device. 23// Equivalent to: `ip addr add $addr dev $link` 24func (h *Handle) AddrAdd(link Link, addr *Addr) error { 25 req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) 26 return h.addrHandle(link, addr, req) 27} 28 29// AddrReplace will replace (or, if not present, add) an IP address on a link device. 30// Equivalent to: `ip addr replace $addr dev $link` 31func AddrReplace(link Link, addr *Addr) error { 32 return pkgHandle.AddrReplace(link, addr) 33} 34 35// AddrReplace will replace (or, if not present, add) an IP address on a link device. 36// Equivalent to: `ip addr replace $addr dev $link` 37func (h *Handle) AddrReplace(link Link, addr *Addr) error { 38 req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|syscall.NLM_F_ACK) 39 return h.addrHandle(link, addr, req) 40} 41 42// AddrDel will delete an IP address from a link device. 43// Equivalent to: `ip addr del $addr dev $link` 44func AddrDel(link Link, addr *Addr) error { 45 return pkgHandle.AddrDel(link, addr) 46} 47 48// AddrDel will delete an IP address from a link device. 49// Equivalent to: `ip addr del $addr dev $link` 50func (h *Handle) AddrDel(link Link, addr *Addr) error { 51 req := h.newNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) 52 return h.addrHandle(link, addr, req) 53} 54 55func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { 56 base := link.Attrs() 57 if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { 58 return fmt.Errorf("label must begin with interface name") 59 } 60 h.ensureIndex(base) 61 62 family := nl.GetIPFamily(addr.IP) 63 64 msg := nl.NewIfAddrmsg(family) 65 msg.Index = uint32(base.Index) 66 msg.Scope = uint8(addr.Scope) 67 prefixlen, masklen := addr.Mask.Size() 68 msg.Prefixlen = uint8(prefixlen) 69 req.AddData(msg) 70 71 var localAddrData []byte 72 if family == FAMILY_V4 { 73 localAddrData = addr.IP.To4() 74 } else { 75 localAddrData = addr.IP.To16() 76 } 77 78 localData := nl.NewRtAttr(syscall.IFA_LOCAL, localAddrData) 79 req.AddData(localData) 80 var peerAddrData []byte 81 if addr.Peer != nil { 82 if family == FAMILY_V4 { 83 peerAddrData = addr.Peer.IP.To4() 84 } else { 85 peerAddrData = addr.Peer.IP.To16() 86 } 87 } else { 88 peerAddrData = localAddrData 89 } 90 91 addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, peerAddrData) 92 req.AddData(addressData) 93 94 if addr.Flags != 0 { 95 if addr.Flags <= 0xff { 96 msg.IfAddrmsg.Flags = uint8(addr.Flags) 97 } else { 98 b := make([]byte, 4) 99 native.PutUint32(b, uint32(addr.Flags)) 100 flagsData := nl.NewRtAttr(IFA_FLAGS, b) 101 req.AddData(flagsData) 102 } 103 } 104 105 if addr.Broadcast == nil { 106 calcBroadcast := make(net.IP, masklen/8) 107 for i := range localAddrData { 108 calcBroadcast[i] = localAddrData[i] | ^addr.Mask[i] 109 } 110 addr.Broadcast = calcBroadcast 111 } 112 req.AddData(nl.NewRtAttr(syscall.IFA_BROADCAST, addr.Broadcast)) 113 114 if addr.Label != "" { 115 labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) 116 req.AddData(labelData) 117 } 118 119 _, err := req.Execute(syscall.NETLINK_ROUTE, 0) 120 return err 121} 122 123// AddrList gets a list of IP addresses in the system. 124// Equivalent to: `ip addr show`. 125// The list can be filtered by link and ip family. 126func AddrList(link Link, family int) ([]Addr, error) { 127 return pkgHandle.AddrList(link, family) 128} 129 130// AddrList gets a list of IP addresses in the system. 131// Equivalent to: `ip addr show`. 132// The list can be filtered by link and ip family. 133func (h *Handle) AddrList(link Link, family int) ([]Addr, error) { 134 req := h.newNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP) 135 msg := nl.NewIfInfomsg(family) 136 req.AddData(msg) 137 138 msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR) 139 if err != nil { 140 return nil, err 141 } 142 143 indexFilter := 0 144 if link != nil { 145 base := link.Attrs() 146 h.ensureIndex(base) 147 indexFilter = base.Index 148 } 149 150 var res []Addr 151 for _, m := range msgs { 152 addr, msgFamily, ifindex, err := parseAddr(m) 153 if err != nil { 154 return res, err 155 } 156 157 if link != nil && ifindex != indexFilter { 158 // Ignore messages from other interfaces 159 continue 160 } 161 162 if family != FAMILY_ALL && msgFamily != family { 163 continue 164 } 165 166 res = append(res, addr) 167 } 168 169 return res, nil 170} 171 172func parseAddr(m []byte) (addr Addr, family, index int, err error) { 173 msg := nl.DeserializeIfAddrmsg(m) 174 175 family = -1 176 index = -1 177 178 attrs, err1 := nl.ParseRouteAttr(m[msg.Len():]) 179 if err1 != nil { 180 err = err1 181 return 182 } 183 184 family = int(msg.Family) 185 index = int(msg.Index) 186 187 var local, dst *net.IPNet 188 for _, attr := range attrs { 189 switch attr.Attr.Type { 190 case syscall.IFA_ADDRESS: 191 dst = &net.IPNet{ 192 IP: attr.Value, 193 Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), 194 } 195 addr.Peer = dst 196 case syscall.IFA_LOCAL: 197 local = &net.IPNet{ 198 IP: attr.Value, 199 Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)), 200 } 201 addr.IPNet = local 202 case syscall.IFA_BROADCAST: 203 addr.Broadcast = attr.Value 204 case syscall.IFA_LABEL: 205 addr.Label = string(attr.Value[:len(attr.Value)-1]) 206 case IFA_FLAGS: 207 addr.Flags = int(native.Uint32(attr.Value[0:4])) 208 case nl.IFA_CACHEINFO: 209 ci := nl.DeserializeIfaCacheInfo(attr.Value) 210 addr.PreferedLft = int(ci.IfaPrefered) 211 addr.ValidLft = int(ci.IfaValid) 212 } 213 } 214 215 // IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS 216 if local != nil { 217 addr.IPNet = local 218 } else { 219 addr.IPNet = dst 220 } 221 addr.Scope = int(msg.Scope) 222 223 return 224} 225 226type AddrUpdate struct { 227 LinkAddress net.IPNet 228 LinkIndex int 229 Flags int 230 Scope int 231 PreferedLft int 232 ValidLft int 233 NewAddr bool // true=added false=deleted 234} 235 236// AddrSubscribe takes a chan down which notifications will be sent 237// when addresses change. Close the 'done' chan to stop subscription. 238func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error { 239 return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil) 240} 241 242// AddrSubscribeAt works like AddrSubscribe plus it allows the caller 243// to choose the network namespace in which to subscribe (ns). 244func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error { 245 return addrSubscribeAt(ns, netns.None(), ch, done, nil) 246} 247 248// AddrSubscribeOptions contains a set of options to use with 249// AddrSubscribeWithOptions. 250type AddrSubscribeOptions struct { 251 Namespace *netns.NsHandle 252 ErrorCallback func(error) 253} 254 255// AddrSubscribeWithOptions work like AddrSubscribe but enable to 256// provide additional options to modify the behavior. Currently, the 257// namespace can be provided as well as an error callback. 258func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error { 259 if options.Namespace == nil { 260 none := netns.None() 261 options.Namespace = &none 262 } 263 return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback) 264} 265 266func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error)) error { 267 s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR) 268 if err != nil { 269 return err 270 } 271 if done != nil { 272 go func() { 273 <-done 274 s.Close() 275 }() 276 } 277 go func() { 278 defer close(ch) 279 for { 280 msgs, err := s.Receive() 281 if err != nil { 282 if cberr != nil { 283 cberr(err) 284 } 285 return 286 } 287 for _, m := range msgs { 288 msgType := m.Header.Type 289 if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR { 290 if cberr != nil { 291 cberr(fmt.Errorf("bad message type: %d", msgType)) 292 } 293 return 294 } 295 296 addr, _, ifindex, err := parseAddr(m.Data) 297 if err != nil { 298 if cberr != nil { 299 cberr(fmt.Errorf("could not parse address: %v", err)) 300 } 301 return 302 } 303 304 ch <- AddrUpdate{LinkAddress: *addr.IPNet, 305 LinkIndex: ifindex, 306 NewAddr: msgType == syscall.RTM_NEWADDR, 307 Flags: addr.Flags, 308 Scope: addr.Scope, 309 PreferedLft: addr.PreferedLft, 310 ValidLft: addr.ValidLft} 311 } 312 } 313 }() 314 315 return nil 316} 317