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