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