// 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.

package net

import (
	
	
	
)

// If the ifindex is zero, interfaceTable returns mappings of all
// network interfaces. Otherwise it returns a mapping of a specific
// interface.
func interfaceTable( int) ([]Interface, error) {
	,  := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
	if  != nil {
		return nil, os.NewSyscallError("netlinkrib", )
	}
	,  := syscall.ParseNetlinkMessage()
	if  != nil {
		return nil, os.NewSyscallError("parsenetlinkmessage", )
	}
	var  []Interface
:
	for ,  := range  {
		switch .Header.Type {
		case syscall.NLMSG_DONE:
			break 
		case syscall.RTM_NEWLINK:
			 := (*syscall.IfInfomsg)(unsafe.Pointer(&.Data[0]))
			if  == 0 ||  == int(.Index) {
				,  := syscall.ParseNetlinkRouteAttr(&)
				if  != nil {
					return nil, os.NewSyscallError("parsenetlinkrouteattr", )
				}
				 = append(, *newLink(, ))
				if  == int(.Index) {
					break 
				}
			}
		}
	}
	return , nil
}

const (
	// See linux/if_arp.h.
	// Note that Linux doesn't support IPv4 over IPv6 tunneling.
	sysARPHardwareIPv4IPv4 = 768 // IPv4 over IPv4 tunneling
	sysARPHardwareIPv6IPv6 = 769 // IPv6 over IPv6 tunneling
	sysARPHardwareIPv6IPv4 = 776 // IPv6 over IPv4 tunneling
	sysARPHardwareGREIPv4  = 778 // any over GRE over IPv4 tunneling
	sysARPHardwareGREIPv6  = 823 // any over GRE over IPv6 tunneling
)

func newLink( *syscall.IfInfomsg,  []syscall.NetlinkRouteAttr) *Interface {
	 := &Interface{Index: int(.Index), Flags: linkFlags(.Flags)}
	for ,  := range  {
		switch .Attr.Type {
		case syscall.IFLA_ADDRESS:
			// We never return any /32 or /128 IP address
			// prefix on any IP tunnel interface as the
			// hardware address.
			switch len(.Value) {
			case IPv4len:
				switch .Type {
				case sysARPHardwareIPv4IPv4, sysARPHardwareGREIPv4, sysARPHardwareIPv6IPv4:
					continue
				}
			case IPv6len:
				switch .Type {
				case sysARPHardwareIPv6IPv6, sysARPHardwareGREIPv6:
					continue
				}
			}
			var  bool
			for ,  := range .Value {
				if  != 0 {
					 = true
					break
				}
			}
			if  {
				.HardwareAddr = .Value[:]
			}
		case syscall.IFLA_IFNAME:
			.Name = string(.Value[:len(.Value)-1])
		case syscall.IFLA_MTU:
			.MTU = int(*(*uint32)(unsafe.Pointer(&.Value[:4][0])))
		}
	}
	return 
}

func linkFlags( uint32) Flags {
	var  Flags
	if &syscall.IFF_UP != 0 {
		 |= FlagUp
	}
	if &syscall.IFF_RUNNING != 0 {
		 |= FlagRunning
	}
	if &syscall.IFF_BROADCAST != 0 {
		 |= FlagBroadcast
	}
	if &syscall.IFF_LOOPBACK != 0 {
		 |= FlagLoopback
	}
	if &syscall.IFF_POINTOPOINT != 0 {
		 |= FlagPointToPoint
	}
	if &syscall.IFF_MULTICAST != 0 {
		 |= FlagMulticast
	}
	return 
}

// If the ifi is nil, interfaceAddrTable returns addresses for all
// network interfaces. Otherwise it returns addresses for a specific
// interface.
func interfaceAddrTable( *Interface) ([]Addr, error) {
	,  := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
	if  != nil {
		return nil, os.NewSyscallError("netlinkrib", )
	}
	,  := syscall.ParseNetlinkMessage()
	if  != nil {
		return nil, os.NewSyscallError("parsenetlinkmessage", )
	}
	var  []Interface
	if  == nil {
		var  error
		,  = interfaceTable(0)
		if  != nil {
			return nil, 
		}
	}
	,  := addrTable(, , )
	if  != nil {
		return nil, 
	}
	return , nil
}

func addrTable( []Interface,  *Interface,  []syscall.NetlinkMessage) ([]Addr, error) {
	var  []Addr
:
	for ,  := range  {
		switch .Header.Type {
		case syscall.NLMSG_DONE:
			break 
		case syscall.RTM_NEWADDR:
			 := (*syscall.IfAddrmsg)(unsafe.Pointer(&.Data[0]))
			if len() != 0 || .Index == int(.Index) {
				if len() != 0 {
					var  error
					,  = interfaceByIndex(, int(.Index))
					if  != nil {
						return nil, 
					}
				}
				,  := syscall.ParseNetlinkRouteAttr(&)
				if  != nil {
					return nil, os.NewSyscallError("parsenetlinkrouteattr", )
				}
				 := newAddr(, )
				if  != nil {
					 = append(, )
				}
			}
		}
	}
	return , nil
}

func newAddr( *syscall.IfAddrmsg,  []syscall.NetlinkRouteAttr) Addr {
	var  bool
	// Seems like we need to make sure whether the IP interface
	// stack consists of IP point-to-point numbered or unnumbered
	// addressing.
	for ,  := range  {
		if .Attr.Type == syscall.IFA_LOCAL {
			 = true
			break
		}
	}
	for ,  := range  {
		if  && .Attr.Type == syscall.IFA_ADDRESS {
			continue
		}
		switch .Family {
		case syscall.AF_INET:
			return &IPNet{IP: IPv4(.Value[0], .Value[1], .Value[2], .Value[3]), Mask: CIDRMask(int(.Prefixlen), 8*IPv4len)}
		case syscall.AF_INET6:
			 := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(.Prefixlen), 8*IPv6len)}
			copy(.IP, .Value[:])
			return 
		}
	}
	return nil
}

// interfaceMulticastAddrTable returns addresses for a specific
// interface.
func interfaceMulticastAddrTable( *Interface) ([]Addr, error) {
	 := parseProcNetIGMP("/proc/net/igmp", )
	 := parseProcNetIGMP6("/proc/net/igmp6", )
	return append(, ...), nil
}

func parseProcNetIGMP( string,  *Interface) []Addr {
	,  := open()
	if  != nil {
		return nil
	}
	defer .close()
	var (
		 []Addr
		  string
	)
	.readLine() // skip first line
	 := make([]byte, IPv4len)
	for ,  := .readLine(); ; ,  = .readLine() {
		 := splitAtBytes(, " :\r\t\n")
		if len() < 4 {
			continue
		}
		switch {
		case [0] != ' ' && [0] != '\t': // new interface line
			 = [1]
		case len([0]) == 8:
			if  == nil ||  == .Name {
				// The Linux kernel puts the IP
				// address in /proc/net/igmp in native
				// endianness.
				for  := 0; +1 < len([0]);  += 2 {
					[/2], _ = xtoi2([0][:+2], 0)
				}
				 := *(*uint32)(unsafe.Pointer(&[:4][0]))
				 := &IPAddr{IP: IPv4(byte(>>24), byte(>>16), byte(>>8), byte())}
				 = append(, )
			}
		}
	}
	return 
}

func parseProcNetIGMP6( string,  *Interface) []Addr {
	,  := open()
	if  != nil {
		return nil
	}
	defer .close()
	var  []Addr
	 := make([]byte, IPv6len)
	for ,  := .readLine(); ; ,  = .readLine() {
		 := splitAtBytes(, " \r\t\n")
		if len() < 6 {
			continue
		}
		if  == nil || [1] == .Name {
			for  := 0; +1 < len([2]);  += 2 {
				[/2], _ = xtoi2([2][:+2], 0)
			}
			 := &IPAddr{IP: IP{[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15]}}
			 = append(, )
		}
	}
	return 
}