// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Netlink sockets and messages

package syscall

import (
	
	
)

// Round the length of a netlink message up to align it properly.
func nlmAlignOf( int) int {
	return ( + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1)
}

// Round the length of a netlink route attribute up to align it
// properly.
func rtaAlignOf( int) int {
	return ( + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
}

// NetlinkRouteRequest represents a request message to receive routing
// and link states from the kernel.
type NetlinkRouteRequest struct {
	Header NlMsghdr
	Data   RtGenmsg
}

func ( *NetlinkRouteRequest) () []byte {
	 := make([]byte, .Header.Len)
	*(*uint32)(unsafe.Pointer(&[0:4][0])) = .Header.Len
	*(*uint16)(unsafe.Pointer(&[4:6][0])) = .Header.Type
	*(*uint16)(unsafe.Pointer(&[6:8][0])) = .Header.Flags
	*(*uint32)(unsafe.Pointer(&[8:12][0])) = .Header.Seq
	*(*uint32)(unsafe.Pointer(&[12:16][0])) = .Header.Pid
	[16] = .Data.Family
	return 
}

func newNetlinkRouteRequest(, ,  int) []byte {
	 := &NetlinkRouteRequest{}
	.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg)
	.Header.Type = uint16()
	.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST
	.Header.Seq = uint32()
	.Data.Family = uint8()
	return .toWireFormat()
}

var pageBufPool = &sync.Pool{New: func() any {
	 := make([]byte, Getpagesize())
	return &
}}

// NetlinkRIB returns routing information base, as known as RIB, which
// consists of network facility information, states and parameters.
func (,  int) ([]byte, error) {
	,  := Socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE)
	if  != nil {
		return nil, 
	}
	defer Close()
	 := &SockaddrNetlink{Family: AF_NETLINK}
	if  := Bind(, );  != nil {
		return nil, 
	}
	 := newNetlinkRouteRequest(, 1, )
	if  := Sendto(, , 0, );  != nil {
		return nil, 
	}
	,  := Getsockname()
	if  != nil {
		return nil, 
	}
	,  := .(*SockaddrNetlink)
	if ! {
		return nil, EINVAL
	}
	var  []byte

	 := pageBufPool.Get().(*[]byte)
	defer pageBufPool.Put()
:
	for {
		 := *
		, ,  := Recvfrom(, , 0)
		if  != nil {
			return nil, 
		}
		if  < NLMSG_HDRLEN {
			return nil, EINVAL
		}
		 = [:]
		 = append(, ...)
		,  := ParseNetlinkMessage()
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if .Header.Seq != 1 || .Header.Pid != .Pid {
				return nil, EINVAL
			}
			if .Header.Type == NLMSG_DONE {
				break 
			}
			if .Header.Type == NLMSG_ERROR {
				return nil, EINVAL
			}
		}
	}
	return , nil
}

// NetlinkMessage represents a netlink message.
type NetlinkMessage struct {
	Header NlMsghdr
	Data   []byte
}

// ParseNetlinkMessage parses b as an array of netlink messages and
// returns the slice containing the NetlinkMessage structures.
func ( []byte) ([]NetlinkMessage, error) {
	var  []NetlinkMessage
	for len() >= NLMSG_HDRLEN {
		, , ,  := netlinkMessageHeaderAndData()
		if  != nil {
			return nil, 
		}
		 := NetlinkMessage{Header: *, Data: [:int(.Len)-NLMSG_HDRLEN]}
		 = append(, )
		 = [:]
	}
	return , nil
}

func netlinkMessageHeaderAndData( []byte) (*NlMsghdr, []byte, int, error) {
	 := (*NlMsghdr)(unsafe.Pointer(&[0]))
	 := nlmAlignOf(int(.Len))
	if int(.Len) < NLMSG_HDRLEN ||  > len() {
		return nil, nil, 0, EINVAL
	}
	return , [NLMSG_HDRLEN:], , nil
}

// NetlinkRouteAttr represents a netlink route attribute.
type NetlinkRouteAttr struct {
	Attr  RtAttr
	Value []byte
}

// ParseNetlinkRouteAttr parses m's payload as an array of netlink
// route attributes and returns the slice containing the
// NetlinkRouteAttr structures.
func ( *NetlinkMessage) ([]NetlinkRouteAttr, error) {
	var  []byte
	switch .Header.Type {
	case RTM_NEWLINK, RTM_DELLINK:
		 = .Data[SizeofIfInfomsg:]
	case RTM_NEWADDR, RTM_DELADDR:
		 = .Data[SizeofIfAddrmsg:]
	case RTM_NEWROUTE, RTM_DELROUTE:
		 = .Data[SizeofRtMsg:]
	default:
		return nil, EINVAL
	}
	var  []NetlinkRouteAttr
	for len() >= SizeofRtAttr {
		, , ,  := netlinkRouteAttrAndValue()
		if  != nil {
			return nil, 
		}
		 := NetlinkRouteAttr{Attr: *, Value: [:int(.Len)-SizeofRtAttr]}
		 = append(, )
		 = [:]
	}
	return , nil
}

func netlinkRouteAttrAndValue( []byte) (*RtAttr, []byte, int, error) {
	 := (*RtAttr)(unsafe.Pointer(&[0]))
	if int(.Len) < SizeofRtAttr || int(.Len) > len() {
		return nil, nil, 0, EINVAL
	}
	return , [SizeofRtAttr:], rtaAlignOf(int(.Len)), nil
}