1// Package nl has low level primitives for making Netlink calls.
2package nl
3
4import (
5	"bytes"
6	"encoding/binary"
7	"fmt"
8	"net"
9	"runtime"
10	"sync"
11	"sync/atomic"
12	"syscall"
13	"unsafe"
14
15	"github.com/vishvananda/netns"
16	"golang.org/x/sys/unix"
17)
18
19const (
20	// Family type definitions
21	FAMILY_ALL  = unix.AF_UNSPEC
22	FAMILY_V4   = unix.AF_INET
23	FAMILY_V6   = unix.AF_INET6
24	FAMILY_MPLS = unix.AF_MPLS
25	// Arbitrary set value (greater than default 4k) to allow receiving
26	// from kernel more verbose messages e.g. for statistics,
27	// tc rules or filters, or other more memory requiring data.
28	RECEIVE_BUFFER_SIZE = 65536
29	// Kernel netlink pid
30	PidKernel uint32 = 0
31)
32
33// SupportedNlFamilies contains the list of netlink families this netlink package supports
34var SupportedNlFamilies = []int{unix.NETLINK_ROUTE, unix.NETLINK_XFRM, unix.NETLINK_NETFILTER}
35
36var nextSeqNr uint32
37
38// GetIPFamily returns the family type of a net.IP.
39func GetIPFamily(ip net.IP) int {
40	if len(ip) <= net.IPv4len {
41		return FAMILY_V4
42	}
43	if ip.To4() != nil {
44		return FAMILY_V4
45	}
46	return FAMILY_V6
47}
48
49var nativeEndian binary.ByteOrder
50
51// NativeEndian gets native endianness for the system
52func NativeEndian() binary.ByteOrder {
53	if nativeEndian == nil {
54		var x uint32 = 0x01020304
55		if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
56			nativeEndian = binary.BigEndian
57		} else {
58			nativeEndian = binary.LittleEndian
59		}
60	}
61	return nativeEndian
62}
63
64// Byte swap a 16 bit value if we aren't big endian
65func Swap16(i uint16) uint16 {
66	if NativeEndian() == binary.BigEndian {
67		return i
68	}
69	return (i&0xff00)>>8 | (i&0xff)<<8
70}
71
72// Byte swap a 32 bit value if aren't big endian
73func Swap32(i uint32) uint32 {
74	if NativeEndian() == binary.BigEndian {
75		return i
76	}
77	return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
78}
79
80type NetlinkRequestData interface {
81	Len() int
82	Serialize() []byte
83}
84
85// IfInfomsg is related to links, but it is used for list requests as well
86type IfInfomsg struct {
87	unix.IfInfomsg
88}
89
90// Create an IfInfomsg with family specified
91func NewIfInfomsg(family int) *IfInfomsg {
92	return &IfInfomsg{
93		IfInfomsg: unix.IfInfomsg{
94			Family: uint8(family),
95		},
96	}
97}
98
99func DeserializeIfInfomsg(b []byte) *IfInfomsg {
100	return (*IfInfomsg)(unsafe.Pointer(&b[0:unix.SizeofIfInfomsg][0]))
101}
102
103func (msg *IfInfomsg) Serialize() []byte {
104	return (*(*[unix.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
105}
106
107func (msg *IfInfomsg) Len() int {
108	return unix.SizeofIfInfomsg
109}
110
111func (msg *IfInfomsg) EncapType() string {
112	switch msg.Type {
113	case 0:
114		return "generic"
115	case unix.ARPHRD_ETHER:
116		return "ether"
117	case unix.ARPHRD_EETHER:
118		return "eether"
119	case unix.ARPHRD_AX25:
120		return "ax25"
121	case unix.ARPHRD_PRONET:
122		return "pronet"
123	case unix.ARPHRD_CHAOS:
124		return "chaos"
125	case unix.ARPHRD_IEEE802:
126		return "ieee802"
127	case unix.ARPHRD_ARCNET:
128		return "arcnet"
129	case unix.ARPHRD_APPLETLK:
130		return "atalk"
131	case unix.ARPHRD_DLCI:
132		return "dlci"
133	case unix.ARPHRD_ATM:
134		return "atm"
135	case unix.ARPHRD_METRICOM:
136		return "metricom"
137	case unix.ARPHRD_IEEE1394:
138		return "ieee1394"
139	case unix.ARPHRD_INFINIBAND:
140		return "infiniband"
141	case unix.ARPHRD_SLIP:
142		return "slip"
143	case unix.ARPHRD_CSLIP:
144		return "cslip"
145	case unix.ARPHRD_SLIP6:
146		return "slip6"
147	case unix.ARPHRD_CSLIP6:
148		return "cslip6"
149	case unix.ARPHRD_RSRVD:
150		return "rsrvd"
151	case unix.ARPHRD_ADAPT:
152		return "adapt"
153	case unix.ARPHRD_ROSE:
154		return "rose"
155	case unix.ARPHRD_X25:
156		return "x25"
157	case unix.ARPHRD_HWX25:
158		return "hwx25"
159	case unix.ARPHRD_PPP:
160		return "ppp"
161	case unix.ARPHRD_HDLC:
162		return "hdlc"
163	case unix.ARPHRD_LAPB:
164		return "lapb"
165	case unix.ARPHRD_DDCMP:
166		return "ddcmp"
167	case unix.ARPHRD_RAWHDLC:
168		return "rawhdlc"
169	case unix.ARPHRD_TUNNEL:
170		return "ipip"
171	case unix.ARPHRD_TUNNEL6:
172		return "tunnel6"
173	case unix.ARPHRD_FRAD:
174		return "frad"
175	case unix.ARPHRD_SKIP:
176		return "skip"
177	case unix.ARPHRD_LOOPBACK:
178		return "loopback"
179	case unix.ARPHRD_LOCALTLK:
180		return "ltalk"
181	case unix.ARPHRD_FDDI:
182		return "fddi"
183	case unix.ARPHRD_BIF:
184		return "bif"
185	case unix.ARPHRD_SIT:
186		return "sit"
187	case unix.ARPHRD_IPDDP:
188		return "ip/ddp"
189	case unix.ARPHRD_IPGRE:
190		return "gre"
191	case unix.ARPHRD_PIMREG:
192		return "pimreg"
193	case unix.ARPHRD_HIPPI:
194		return "hippi"
195	case unix.ARPHRD_ASH:
196		return "ash"
197	case unix.ARPHRD_ECONET:
198		return "econet"
199	case unix.ARPHRD_IRDA:
200		return "irda"
201	case unix.ARPHRD_FCPP:
202		return "fcpp"
203	case unix.ARPHRD_FCAL:
204		return "fcal"
205	case unix.ARPHRD_FCPL:
206		return "fcpl"
207	case unix.ARPHRD_FCFABRIC:
208		return "fcfb0"
209	case unix.ARPHRD_FCFABRIC + 1:
210		return "fcfb1"
211	case unix.ARPHRD_FCFABRIC + 2:
212		return "fcfb2"
213	case unix.ARPHRD_FCFABRIC + 3:
214		return "fcfb3"
215	case unix.ARPHRD_FCFABRIC + 4:
216		return "fcfb4"
217	case unix.ARPHRD_FCFABRIC + 5:
218		return "fcfb5"
219	case unix.ARPHRD_FCFABRIC + 6:
220		return "fcfb6"
221	case unix.ARPHRD_FCFABRIC + 7:
222		return "fcfb7"
223	case unix.ARPHRD_FCFABRIC + 8:
224		return "fcfb8"
225	case unix.ARPHRD_FCFABRIC + 9:
226		return "fcfb9"
227	case unix.ARPHRD_FCFABRIC + 10:
228		return "fcfb10"
229	case unix.ARPHRD_FCFABRIC + 11:
230		return "fcfb11"
231	case unix.ARPHRD_FCFABRIC + 12:
232		return "fcfb12"
233	case unix.ARPHRD_IEEE802_TR:
234		return "tr"
235	case unix.ARPHRD_IEEE80211:
236		return "ieee802.11"
237	case unix.ARPHRD_IEEE80211_PRISM:
238		return "ieee802.11/prism"
239	case unix.ARPHRD_IEEE80211_RADIOTAP:
240		return "ieee802.11/radiotap"
241	case unix.ARPHRD_IEEE802154:
242		return "ieee802.15.4"
243
244	case 65534:
245		return "none"
246	case 65535:
247		return "void"
248	}
249	return fmt.Sprintf("unknown%d", msg.Type)
250}
251
252func rtaAlignOf(attrlen int) int {
253	return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1)
254}
255
256func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
257	msg := NewIfInfomsg(family)
258	parent.children = append(parent.children, msg)
259	return msg
260}
261
262// Extend RtAttr to handle data and children
263type RtAttr struct {
264	unix.RtAttr
265	Data     []byte
266	children []NetlinkRequestData
267}
268
269// Create a new Extended RtAttr object
270func NewRtAttr(attrType int, data []byte) *RtAttr {
271	return &RtAttr{
272		RtAttr: unix.RtAttr{
273			Type: uint16(attrType),
274		},
275		children: []NetlinkRequestData{},
276		Data:     data,
277	}
278}
279
280// NewRtAttrChild adds an RtAttr as a child to the parent and returns the new attribute
281//
282// Deprecated: Use AddRtAttr() on the parent object
283func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
284	return parent.AddRtAttr(attrType, data)
285}
286
287// AddRtAttr adds an RtAttr as a child and returns the new attribute
288func (a *RtAttr) AddRtAttr(attrType int, data []byte) *RtAttr {
289	attr := NewRtAttr(attrType, data)
290	a.children = append(a.children, attr)
291	return attr
292}
293
294// AddChild adds an existing NetlinkRequestData as a child.
295func (a *RtAttr) AddChild(attr NetlinkRequestData) {
296	a.children = append(a.children, attr)
297}
298
299func (a *RtAttr) Len() int {
300	if len(a.children) == 0 {
301		return (unix.SizeofRtAttr + len(a.Data))
302	}
303
304	l := 0
305	for _, child := range a.children {
306		l += rtaAlignOf(child.Len())
307	}
308	l += unix.SizeofRtAttr
309	return rtaAlignOf(l + len(a.Data))
310}
311
312// Serialize the RtAttr into a byte array
313// This can't just unsafe.cast because it must iterate through children.
314func (a *RtAttr) Serialize() []byte {
315	native := NativeEndian()
316
317	length := a.Len()
318	buf := make([]byte, rtaAlignOf(length))
319
320	next := 4
321	if a.Data != nil {
322		copy(buf[next:], a.Data)
323		next += rtaAlignOf(len(a.Data))
324	}
325	if len(a.children) > 0 {
326		for _, child := range a.children {
327			childBuf := child.Serialize()
328			copy(buf[next:], childBuf)
329			next += rtaAlignOf(len(childBuf))
330		}
331	}
332
333	if l := uint16(length); l != 0 {
334		native.PutUint16(buf[0:2], l)
335	}
336	native.PutUint16(buf[2:4], a.Type)
337	return buf
338}
339
340type NetlinkRequest struct {
341	unix.NlMsghdr
342	Data    []NetlinkRequestData
343	RawData []byte
344	Sockets map[int]*SocketHandle
345}
346
347// Serialize the Netlink Request into a byte array
348func (req *NetlinkRequest) Serialize() []byte {
349	length := unix.SizeofNlMsghdr
350	dataBytes := make([][]byte, len(req.Data))
351	for i, data := range req.Data {
352		dataBytes[i] = data.Serialize()
353		length = length + len(dataBytes[i])
354	}
355	length += len(req.RawData)
356
357	req.Len = uint32(length)
358	b := make([]byte, length)
359	hdr := (*(*[unix.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
360	next := unix.SizeofNlMsghdr
361	copy(b[0:next], hdr)
362	for _, data := range dataBytes {
363		for _, dataByte := range data {
364			b[next] = dataByte
365			next = next + 1
366		}
367	}
368	// Add the raw data if any
369	if len(req.RawData) > 0 {
370		copy(b[next:length], req.RawData)
371	}
372	return b
373}
374
375func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
376	req.Data = append(req.Data, data)
377}
378
379// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization
380func (req *NetlinkRequest) AddRawData(data []byte) {
381	req.RawData = append(req.RawData, data...)
382}
383
384// Execute the request against a the given sockType.
385// Returns a list of netlink messages in serialized format, optionally filtered
386// by resType.
387func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
388	var (
389		s   *NetlinkSocket
390		err error
391	)
392
393	if req.Sockets != nil {
394		if sh, ok := req.Sockets[sockType]; ok {
395			s = sh.Socket
396			req.Seq = atomic.AddUint32(&sh.Seq, 1)
397		}
398	}
399	sharedSocket := s != nil
400
401	if s == nil {
402		s, err = getNetlinkSocket(sockType)
403		if err != nil {
404			return nil, err
405		}
406		defer s.Close()
407	} else {
408		s.Lock()
409		defer s.Unlock()
410	}
411
412	if err := s.Send(req); err != nil {
413		return nil, err
414	}
415
416	pid, err := s.GetPid()
417	if err != nil {
418		return nil, err
419	}
420
421	var res [][]byte
422
423done:
424	for {
425		msgs, from, err := s.Receive()
426		if err != nil {
427			return nil, err
428		}
429		if from.Pid != PidKernel {
430			return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, PidKernel)
431		}
432		for _, m := range msgs {
433			if m.Header.Seq != req.Seq {
434				if sharedSocket {
435					continue
436				}
437				return nil, fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, req.Seq)
438			}
439			if m.Header.Pid != pid {
440				continue
441			}
442			if m.Header.Type == unix.NLMSG_DONE {
443				break done
444			}
445			if m.Header.Type == unix.NLMSG_ERROR {
446				native := NativeEndian()
447				error := int32(native.Uint32(m.Data[0:4]))
448				if error == 0 {
449					break done
450				}
451				return nil, syscall.Errno(-error)
452			}
453			if resType != 0 && m.Header.Type != resType {
454				continue
455			}
456			res = append(res, m.Data)
457			if m.Header.Flags&unix.NLM_F_MULTI == 0 {
458				break done
459			}
460		}
461	}
462	return res, nil
463}
464
465// Create a new netlink request from proto and flags
466// Note the Len value will be inaccurate once data is added until
467// the message is serialized
468func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
469	return &NetlinkRequest{
470		NlMsghdr: unix.NlMsghdr{
471			Len:   uint32(unix.SizeofNlMsghdr),
472			Type:  uint16(proto),
473			Flags: unix.NLM_F_REQUEST | uint16(flags),
474			Seq:   atomic.AddUint32(&nextSeqNr, 1),
475		},
476	}
477}
478
479type NetlinkSocket struct {
480	fd  int32
481	lsa unix.SockaddrNetlink
482	sync.Mutex
483}
484
485func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
486	fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol)
487	if err != nil {
488		return nil, err
489	}
490	s := &NetlinkSocket{
491		fd: int32(fd),
492	}
493	s.lsa.Family = unix.AF_NETLINK
494	if err := unix.Bind(fd, &s.lsa); err != nil {
495		unix.Close(fd)
496		return nil, err
497	}
498
499	return s, nil
500}
501
502// GetNetlinkSocketAt opens a netlink socket in the network namespace newNs
503// and positions the thread back into the network namespace specified by curNs,
504// when done. If curNs is close, the function derives the current namespace and
505// moves back into it when done. If newNs is close, the socket will be opened
506// in the current network namespace.
507func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) {
508	c, err := executeInNetns(newNs, curNs)
509	if err != nil {
510		return nil, err
511	}
512	defer c()
513	return getNetlinkSocket(protocol)
514}
515
516// executeInNetns sets execution of the code following this call to the
517// network namespace newNs, then moves the thread back to curNs if open,
518// otherwise to the current netns at the time the function was invoked
519// In case of success, the caller is expected to execute the returned function
520// at the end of the code that needs to be executed in the network namespace.
521// Example:
522// func jobAt(...) error {
523//      d, err := executeInNetns(...)
524//      if err != nil { return err}
525//      defer d()
526//      < code which needs to be executed in specific netns>
527//  }
528// TODO: his function probably belongs to netns pkg.
529func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
530	var (
531		err       error
532		moveBack  func(netns.NsHandle) error
533		closeNs   func() error
534		unlockThd func()
535	)
536	restore := func() {
537		// order matters
538		if moveBack != nil {
539			moveBack(curNs)
540		}
541		if closeNs != nil {
542			closeNs()
543		}
544		if unlockThd != nil {
545			unlockThd()
546		}
547	}
548	if newNs.IsOpen() {
549		runtime.LockOSThread()
550		unlockThd = runtime.UnlockOSThread
551		if !curNs.IsOpen() {
552			if curNs, err = netns.Get(); err != nil {
553				restore()
554				return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err)
555			}
556			closeNs = curNs.Close
557		}
558		if err := netns.Set(newNs); err != nil {
559			restore()
560			return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err)
561		}
562		moveBack = netns.Set
563	}
564	return restore, nil
565}
566
567// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
568// and subscribe it to multicast groups passed in variable argument list.
569// Returns the netlink socket on which Receive() method can be called
570// to retrieve the messages from the kernel.
571func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
572	fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, protocol)
573	if err != nil {
574		return nil, err
575	}
576	s := &NetlinkSocket{
577		fd: int32(fd),
578	}
579	s.lsa.Family = unix.AF_NETLINK
580
581	for _, g := range groups {
582		s.lsa.Groups |= (1 << (g - 1))
583	}
584
585	if err := unix.Bind(fd, &s.lsa); err != nil {
586		unix.Close(fd)
587		return nil, err
588	}
589
590	return s, nil
591}
592
593// SubscribeAt works like Subscribe plus let's the caller choose the network
594// namespace in which the socket would be opened (newNs). Then control goes back
595// to curNs if open, otherwise to the netns at the time this function was called.
596func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) {
597	c, err := executeInNetns(newNs, curNs)
598	if err != nil {
599		return nil, err
600	}
601	defer c()
602	return Subscribe(protocol, groups...)
603}
604
605func (s *NetlinkSocket) Close() {
606	fd := int(atomic.SwapInt32(&s.fd, -1))
607	unix.Close(fd)
608}
609
610func (s *NetlinkSocket) GetFd() int {
611	return int(atomic.LoadInt32(&s.fd))
612}
613
614func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
615	fd := int(atomic.LoadInt32(&s.fd))
616	if fd < 0 {
617		return fmt.Errorf("Send called on a closed socket")
618	}
619	if err := unix.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
620		return err
621	}
622	return nil
623}
624
625func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetlink, error) {
626	fd := int(atomic.LoadInt32(&s.fd))
627	if fd < 0 {
628		return nil, nil, fmt.Errorf("Receive called on a closed socket")
629	}
630	var fromAddr *unix.SockaddrNetlink
631	var rb [RECEIVE_BUFFER_SIZE]byte
632	nr, from, err := unix.Recvfrom(fd, rb[:], 0)
633	if err != nil {
634		return nil, nil, err
635	}
636	fromAddr, ok := from.(*unix.SockaddrNetlink)
637	if !ok {
638		return nil, nil, fmt.Errorf("Error converting to netlink sockaddr")
639	}
640	if nr < unix.NLMSG_HDRLEN {
641		return nil, nil, fmt.Errorf("Got short response from netlink")
642	}
643	rb2 := make([]byte, nr)
644	copy(rb2, rb[:nr])
645	nl, err := syscall.ParseNetlinkMessage(rb2)
646	if err != nil {
647		return nil, nil, err
648	}
649	return nl, fromAddr, nil
650}
651
652// SetSendTimeout allows to set a send timeout on the socket
653func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error {
654	// Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine
655	// remains stuck on a send on a closed fd
656	return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_SNDTIMEO, timeout)
657}
658
659// SetReceiveTimeout allows to set a receive timeout on the socket
660func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error {
661	// Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine
662	// remains stuck on a recvmsg on a closed fd
663	return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout)
664}
665
666func (s *NetlinkSocket) GetPid() (uint32, error) {
667	fd := int(atomic.LoadInt32(&s.fd))
668	lsa, err := unix.Getsockname(fd)
669	if err != nil {
670		return 0, err
671	}
672	switch v := lsa.(type) {
673	case *unix.SockaddrNetlink:
674		return v.Pid, nil
675	}
676	return 0, fmt.Errorf("Wrong socket type")
677}
678
679func ZeroTerminated(s string) []byte {
680	bytes := make([]byte, len(s)+1)
681	for i := 0; i < len(s); i++ {
682		bytes[i] = s[i]
683	}
684	bytes[len(s)] = 0
685	return bytes
686}
687
688func NonZeroTerminated(s string) []byte {
689	bytes := make([]byte, len(s))
690	for i := 0; i < len(s); i++ {
691		bytes[i] = s[i]
692	}
693	return bytes
694}
695
696func BytesToString(b []byte) string {
697	n := bytes.Index(b, []byte{0})
698	return string(b[:n])
699}
700
701func Uint8Attr(v uint8) []byte {
702	return []byte{byte(v)}
703}
704
705func Uint16Attr(v uint16) []byte {
706	native := NativeEndian()
707	bytes := make([]byte, 2)
708	native.PutUint16(bytes, v)
709	return bytes
710}
711
712func Uint32Attr(v uint32) []byte {
713	native := NativeEndian()
714	bytes := make([]byte, 4)
715	native.PutUint32(bytes, v)
716	return bytes
717}
718
719func Uint64Attr(v uint64) []byte {
720	native := NativeEndian()
721	bytes := make([]byte, 8)
722	native.PutUint64(bytes, v)
723	return bytes
724}
725
726func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
727	var attrs []syscall.NetlinkRouteAttr
728	for len(b) >= unix.SizeofRtAttr {
729		a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
730		if err != nil {
731			return nil, err
732		}
733		ra := syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(*a), Value: vbuf[:int(a.Len)-unix.SizeofRtAttr]}
734		attrs = append(attrs, ra)
735		b = b[alen:]
736	}
737	return attrs, nil
738}
739
740func netlinkRouteAttrAndValue(b []byte) (*unix.RtAttr, []byte, int, error) {
741	a := (*unix.RtAttr)(unsafe.Pointer(&b[0]))
742	if int(a.Len) < unix.SizeofRtAttr || int(a.Len) > len(b) {
743		return nil, nil, 0, unix.EINVAL
744	}
745	return a, b[unix.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
746}
747
748// SocketHandle contains the netlink socket and the associated
749// sequence counter for a specific netlink family
750type SocketHandle struct {
751	Seq    uint32
752	Socket *NetlinkSocket
753}
754
755// Close closes the netlink socket
756func (sh *SocketHandle) Close() {
757	if sh.Socket != nil {
758		sh.Socket.Close()
759	}
760}
761