/*
 *   $Id: device-bsd44.c,v 1.22 2006/10/08 19:25:29 psavola Exp $
 *
 *   Authors:
 *    Craig Metz		<cmetz@inner.net>
 *
 *   This software is Copyright 1996,1997 by the above mentioned author(s), 
 *   All Rights Reserved.
 *
 *   The license which is distributed with this software in the file COPYRIGHT
 *   applies to this software. If your distribution is missing this file, you
 *   may request it from <pekkas@netcore.fi>.
 *
 */

#include <config.h>
#include <includes.h>
#include <radvd.h>
#include <defaults.h>
#include <pathnames.h>		/* for PATH_PROC_NET_IF_INET6 */

static uint8_t ll_prefix[] = { 0xfe, 0x80 };

/*
 * this function gets the hardware type and address of an interface,
 * determines the link layer token length and checks it against
 * the defined prefixes
 */
int
setup_deviceinfo(int sock, struct Interface *iface)
{
	struct ifconf ifconf;
	struct ifreq ifr;
	unsigned int nlen;
	uint8_t *p, *end;
	struct AdvPrefix *prefix;
	char zero[sizeof(iface->if_addr)];

	/* just allocate 8192 bytes, should be more than enough.. */
	if (!(ifconf.ifc_buf = malloc(ifconf.ifc_len = (32 << 8))))
	{
		flog(LOG_CRIT, "malloc failed: %s", strerror(errno));
		goto ret;
	}

	if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0)
	{
		flog(LOG_ERR, "ioctl(SIOCGIFCONF) failed: %s(%d)", strerror(errno), errno);
		goto ret;
	}

 	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, iface->Name, IFNAMSIZ-1);
	ifr.ifr_name[IFNAMSIZ-1] = '\0';

	if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
		flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed for %s: %s", iface->Name, strerror(errno));
		goto ret;
	}	

	dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->Name, ifr.ifr_mtu);
	iface->if_maxmtu = ifr.ifr_mtu;

	p = (uint8_t *)ifconf.ifc_buf;
	end = p + ifconf.ifc_len;
	nlen = strlen(iface->Name);

	while(p < end)
	{
		p += IFNAMSIZ;
		
		if ((p + 2) >= end)
			break;
			
		if ((p + *p) >= end)
			break;
			
		if ((*(p + 1) == AF_LINK) &&
		    (((struct sockaddr_dl *)p)->sdl_nlen == nlen) &&
		    (!memcmp(iface->Name, ((struct sockaddr_dl *)p)->sdl_data, nlen)))
		{
		
			if (((struct sockaddr_dl *)p)->sdl_alen > sizeof(iface->if_addr))
			{
				flog(LOG_ERR, "address length %d too big for",
					((struct sockaddr_dl *)p)->sdl_alen,
					iface->Name);
				goto ret;
			}
		
			memcpy(iface->if_hwaddr, LLADDR((struct sockaddr_dl *)p), ((struct sockaddr_dl *)p)->sdl_alen);
			iface->if_hwaddr_len = ((struct sockaddr_dl *)p)->sdl_alen << 3;

          		switch(((struct sockaddr_dl *)p)->sdl_type) {
            		case IFT_ETHER:
            		case IFT_ISO88023:
            			iface->if_prefix_len = 64;
              			break;
            		case IFT_FDDI:
            			iface->if_prefix_len = 64;
              			break;
            		default:
            			iface->if_prefix_len = -1;
				iface->if_maxmtu = -1;
				break;
          		}

			dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->Name,
				iface->if_hwaddr_len);

			dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->Name,
				iface->if_prefix_len);

			if (iface->if_prefix_len != -1) {
				memset(zero, 0, ((struct sockaddr_dl *)p)->sdl_alen);
				if (!memcmp(iface->if_hwaddr, zero, ((struct sockaddr_dl *)p)->sdl_alen))
					flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!",
						iface->Name);
			}
			
			prefix = iface->AdvPrefixList;
			while (prefix)
			{
				if ((iface->if_prefix_len != -1) &&
					(iface->if_prefix_len != prefix->PrefixLen))
				{
					flog(LOG_WARNING, "prefix length should be %d for %s",
						iface->if_prefix_len, iface->Name);
 				}
 			
 				prefix = prefix->next;
			}
          		
          		free(ifconf.ifc_buf);
          		return 0;
        	}
        
    		p += *p;	
	}

ret:
	iface->if_maxmtu = -1;
	iface->if_hwaddr_len = -1;
	iface->if_prefix_len = -1;
	free(ifconf.ifc_buf);
	return -1;
}

int setup_linklocal_addr(int sock, struct Interface *iface)
{
	struct ifconf ifconf;
	unsigned int nlen;
	uint8_t *p, *end;
	int index = 0;

	/* just allocate 8192 bytes, should be more than enough.. */
	if (!(ifconf.ifc_buf = malloc(ifconf.ifc_len = (32 << 8))))
	{
		flog(LOG_CRIT, "malloc failed: %s", strerror(errno));
		goto ret;
	}

	if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0)
	{
		flog(LOG_ERR, "ioctl(SIOCGIFCONF) failed: %s(%d)", strerror(errno), errno);
		goto ret;
	}

	p = (uint8_t *)ifconf.ifc_buf;
	end = p + ifconf.ifc_len;
	nlen = strlen(iface->Name);

	while(p < end)
  	{
		p += IFNAMSIZ;
	
		if ((p + 2) >= end)
			break;
			
		if ((p + *p) >= end)
			break;
			
		if ((*(p + 1) == AF_LINK) &&
		    (((struct sockaddr_dl *)p)->sdl_nlen == nlen) &&
		    (!memcmp(iface->Name, ((struct sockaddr_dl *)p)->sdl_data, nlen)))
		{
			index = ((struct sockaddr_dl *)p)->sdl_index;
		}
		
   	 	if (index && (*(p + 1) == AF_INET6))
		  if (!memcmp(&((struct sockaddr_in6 *)p)->sin6_addr, ll_prefix, sizeof(ll_prefix)))
		  {
			memcpy(&iface->if_addr, &((struct sockaddr_in6 *)p)->sin6_addr, sizeof(struct in6_addr));
			iface->if_index = index;

			free(ifconf.ifc_buf);
			return 0;
      	  	  }
      	  
		p += *p;

	}

ret:
	flog(LOG_ERR, "no linklocal address configured for %s", iface->Name);
	free(ifconf.ifc_buf);
	return -1;
}

int setup_allrouters_membership(int sock, struct Interface *iface)
{
	return (0);
}

int check_allrouters_membership(int sock, struct Interface *iface)
{
	return (0);
}

int
set_interface_linkmtu(const char *iface, uint32_t mtu)
{
	dlog(LOG_DEBUG, 4, "setting LinkMTU (%u) for %s is not supported",
	     mtu, iface);
	return -1;
}

int
set_interface_curhlim(const char *iface, uint8_t hlim)
{
	dlog(LOG_DEBUG, 4, "setting CurHopLimit (%u) for %s is not supported",
	     hlim, iface);
	return -1;
}

int
set_interface_reachtime(const char *iface, uint32_t rtime)
{
	dlog(LOG_DEBUG, 4, "setting BaseReachableTime (%u) for %s is not supported",
	     rtime, iface);
	return -1;
}

int
set_interface_retranstimer(const char *iface, uint32_t rettimer)
{
	dlog(LOG_DEBUG, 4, "setting RetransTimer (%u) for %s is not supported",
	     rettimer, iface);
	return -1;
}

