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