1package netlink
2
3import (
4	"fmt"
5	"net"
6	"syscall"
7	"unsafe"
8
9	"github.com/vishvananda/netlink/nl"
10	"github.com/vishvananda/netns"
11	"golang.org/x/sys/unix"
12)
13
14const (
15	NDA_UNSPEC = iota
16	NDA_DST
17	NDA_LLADDR
18	NDA_CACHEINFO
19	NDA_PROBES
20	NDA_VLAN
21	NDA_PORT
22	NDA_VNI
23	NDA_IFINDEX
24	NDA_MASTER
25	NDA_LINK_NETNSID
26	NDA_SRC_VNI
27	NDA_MAX = NDA_SRC_VNI
28)
29
30// Neighbor Cache Entry States.
31const (
32	NUD_NONE       = 0x00
33	NUD_INCOMPLETE = 0x01
34	NUD_REACHABLE  = 0x02
35	NUD_STALE      = 0x04
36	NUD_DELAY      = 0x08
37	NUD_PROBE      = 0x10
38	NUD_FAILED     = 0x20
39	NUD_NOARP      = 0x40
40	NUD_PERMANENT  = 0x80
41)
42
43// Neighbor Flags
44const (
45	NTF_USE    = 0x01
46	NTF_SELF   = 0x02
47	NTF_MASTER = 0x04
48	NTF_PROXY  = 0x08
49	NTF_ROUTER = 0x80
50)
51
52// Ndmsg is for adding, removing or receiving information about a neighbor table entry
53type Ndmsg struct {
54	Family uint8
55	Index  uint32
56	State  uint16
57	Flags  uint8
58	Type   uint8
59}
60
61func deserializeNdmsg(b []byte) *Ndmsg {
62	var dummy Ndmsg
63	return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
64}
65
66func (msg *Ndmsg) Serialize() []byte {
67	return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
68}
69
70func (msg *Ndmsg) Len() int {
71	return int(unsafe.Sizeof(*msg))
72}
73
74// NeighAdd will add an IP to MAC mapping to the ARP table
75// Equivalent to: `ip neigh add ....`
76func NeighAdd(neigh *Neigh) error {
77	return pkgHandle.NeighAdd(neigh)
78}
79
80// NeighAdd will add an IP to MAC mapping to the ARP table
81// Equivalent to: `ip neigh add ....`
82func (h *Handle) NeighAdd(neigh *Neigh) error {
83	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
84}
85
86// NeighSet will add or replace an IP to MAC mapping to the ARP table
87// Equivalent to: `ip neigh replace....`
88func NeighSet(neigh *Neigh) error {
89	return pkgHandle.NeighSet(neigh)
90}
91
92// NeighSet will add or replace an IP to MAC mapping to the ARP table
93// Equivalent to: `ip neigh replace....`
94func (h *Handle) NeighSet(neigh *Neigh) error {
95	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
96}
97
98// NeighAppend will append an entry to FDB
99// Equivalent to: `bridge fdb append...`
100func NeighAppend(neigh *Neigh) error {
101	return pkgHandle.NeighAppend(neigh)
102}
103
104// NeighAppend will append an entry to FDB
105// Equivalent to: `bridge fdb append...`
106func (h *Handle) NeighAppend(neigh *Neigh) error {
107	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
108}
109
110// NeighAppend will append an entry to FDB
111// Equivalent to: `bridge fdb append...`
112func neighAdd(neigh *Neigh, mode int) error {
113	return pkgHandle.neighAdd(neigh, mode)
114}
115
116// NeighAppend will append an entry to FDB
117// Equivalent to: `bridge fdb append...`
118func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
119	req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
120	return neighHandle(neigh, req)
121}
122
123// NeighDel will delete an IP address from a link device.
124// Equivalent to: `ip addr del $addr dev $link`
125func NeighDel(neigh *Neigh) error {
126	return pkgHandle.NeighDel(neigh)
127}
128
129// NeighDel will delete an IP address from a link device.
130// Equivalent to: `ip addr del $addr dev $link`
131func (h *Handle) NeighDel(neigh *Neigh) error {
132	req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
133	return neighHandle(neigh, req)
134}
135
136func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
137	var family int
138
139	if neigh.Family > 0 {
140		family = neigh.Family
141	} else {
142		family = nl.GetIPFamily(neigh.IP)
143	}
144
145	msg := Ndmsg{
146		Family: uint8(family),
147		Index:  uint32(neigh.LinkIndex),
148		State:  uint16(neigh.State),
149		Type:   uint8(neigh.Type),
150		Flags:  uint8(neigh.Flags),
151	}
152	req.AddData(&msg)
153
154	ipData := neigh.IP.To4()
155	if ipData == nil {
156		ipData = neigh.IP.To16()
157	}
158
159	dstData := nl.NewRtAttr(NDA_DST, ipData)
160	req.AddData(dstData)
161
162	if neigh.LLIPAddr != nil {
163		llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
164		req.AddData(llIPData)
165	} else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
166		hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
167		req.AddData(hwData)
168	}
169
170	if neigh.Vlan != 0 {
171		vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
172		req.AddData(vlanData)
173	}
174
175	if neigh.VNI != 0 {
176		vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
177		req.AddData(vniData)
178	}
179
180	if neigh.MasterIndex != 0 {
181		masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
182		req.AddData(masterData)
183	}
184
185	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
186	return err
187}
188
189// NeighList returns a list of IP-MAC mappings in the system (ARP table).
190// Equivalent to: `ip neighbor show`.
191// The list can be filtered by link and ip family.
192func NeighList(linkIndex, family int) ([]Neigh, error) {
193	return pkgHandle.NeighList(linkIndex, family)
194}
195
196// NeighProxyList returns a list of neighbor proxies in the system.
197// Equivalent to: `ip neighbor show proxy`.
198// The list can be filtered by link and ip family.
199func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
200	return pkgHandle.NeighProxyList(linkIndex, family)
201}
202
203// NeighList returns a list of IP-MAC mappings in the system (ARP table).
204// Equivalent to: `ip neighbor show`.
205// The list can be filtered by link and ip family.
206func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
207	return h.NeighListExecute(Ndmsg{
208		Family: uint8(family),
209		Index:  uint32(linkIndex),
210	})
211}
212
213// NeighProxyList returns a list of neighbor proxies in the system.
214// Equivalent to: `ip neighbor show proxy`.
215// The list can be filtered by link, ip family.
216func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
217	return h.NeighListExecute(Ndmsg{
218		Family: uint8(family),
219		Index:  uint32(linkIndex),
220		Flags:  NTF_PROXY,
221	})
222}
223
224// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
225func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
226	return pkgHandle.NeighListExecute(msg)
227}
228
229// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
230func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
231	req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
232	req.AddData(&msg)
233
234	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
235	if err != nil {
236		return nil, err
237	}
238
239	var res []Neigh
240	for _, m := range msgs {
241		ndm := deserializeNdmsg(m)
242		if msg.Index != 0 && ndm.Index != msg.Index {
243			// Ignore messages from other interfaces
244			continue
245		}
246
247		neigh, err := NeighDeserialize(m)
248		if err != nil {
249			continue
250		}
251
252		res = append(res, *neigh)
253	}
254
255	return res, nil
256}
257
258func NeighDeserialize(m []byte) (*Neigh, error) {
259	msg := deserializeNdmsg(m)
260
261	neigh := Neigh{
262		LinkIndex: int(msg.Index),
263		Family:    int(msg.Family),
264		State:     int(msg.State),
265		Type:      int(msg.Type),
266		Flags:     int(msg.Flags),
267	}
268
269	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
270	if err != nil {
271		return nil, err
272	}
273
274	for _, attr := range attrs {
275		switch attr.Attr.Type {
276		case NDA_DST:
277			neigh.IP = net.IP(attr.Value)
278		case NDA_LLADDR:
279			// BUG: Is this a bug in the netlink library?
280			// #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
281			// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
282			attrLen := attr.Attr.Len - unix.SizeofRtAttr
283			if attrLen == 4 {
284				neigh.LLIPAddr = net.IP(attr.Value)
285			} else if attrLen == 16 {
286				// Can be IPv6 or FireWire HWAddr
287				link, err := LinkByIndex(neigh.LinkIndex)
288				if err == nil && link.Attrs().EncapType == "tunnel6" {
289					neigh.IP = net.IP(attr.Value)
290				} else {
291					neigh.HardwareAddr = net.HardwareAddr(attr.Value)
292				}
293			} else {
294				neigh.HardwareAddr = net.HardwareAddr(attr.Value)
295			}
296		case NDA_VLAN:
297			neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
298		case NDA_VNI:
299			neigh.VNI = int(native.Uint32(attr.Value[0:4]))
300		case NDA_MASTER:
301			neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
302		}
303	}
304
305	return &neigh, nil
306}
307
308// NeighSubscribe takes a chan down which notifications will be sent
309// when neighbors are added or deleted. Close the 'done' chan to stop subscription.
310func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
311	return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
312}
313
314// NeighSubscribeAt works like NeighSubscribe plus it allows the caller
315// to choose the network namespace in which to subscribe (ns).
316func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
317	return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
318}
319
320// NeighSubscribeOptions contains a set of options to use with
321// NeighSubscribeWithOptions.
322type NeighSubscribeOptions struct {
323	Namespace     *netns.NsHandle
324	ErrorCallback func(error)
325	ListExisting  bool
326}
327
328// NeighSubscribeWithOptions work like NeighSubscribe but enable to
329// provide additional options to modify the behavior. Currently, the
330// namespace can be provided as well as an error callback.
331func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
332	if options.Namespace == nil {
333		none := netns.None()
334		options.Namespace = &none
335	}
336	return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
337}
338
339func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
340	s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
341	makeRequest := func(family int) error {
342		req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
343			unix.NLM_F_DUMP)
344		infmsg := nl.NewIfInfomsg(family)
345		req.AddData(infmsg)
346		if err := s.Send(req); err != nil {
347			return err
348		}
349		return nil
350	}
351	if err != nil {
352		return err
353	}
354	if done != nil {
355		go func() {
356			<-done
357			s.Close()
358		}()
359	}
360	if listExisting {
361		if err := makeRequest(unix.AF_UNSPEC); err != nil {
362			return err
363		}
364		// We have to wait for NLMSG_DONE before making AF_BRIDGE request
365	}
366	go func() {
367		defer close(ch)
368		for {
369			msgs, from, err := s.Receive()
370			if err != nil {
371				if cberr != nil {
372					cberr(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					if listExisting {
385						// This will be called after handling AF_UNSPEC
386						// list request, we have to wait for NLMSG_DONE
387						// before making another request
388						if err := makeRequest(unix.AF_BRIDGE); err != nil {
389							if cberr != nil {
390								cberr(err)
391							}
392							return
393						}
394						listExisting = false
395					}
396					continue
397				}
398				if m.Header.Type == unix.NLMSG_ERROR {
399					native := nl.NativeEndian()
400					error := int32(native.Uint32(m.Data[0:4]))
401					if error == 0 {
402						continue
403					}
404					if cberr != nil {
405						cberr(syscall.Errno(-error))
406					}
407					return
408				}
409				neigh, err := NeighDeserialize(m.Data)
410				if err != nil {
411					if cberr != nil {
412						cberr(err)
413					}
414					return
415				}
416				ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
417			}
418		}
419	}()
420
421	return nil
422}
423