/* $USAGI: itf.c,v 1.3 2001/11/01 10:40:16 serizawa Exp $ */
/* itf.c - IP interface registry */
 
/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */
 

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>

#ifdef CONFIG_ATM_IPV6
#include <linux/atmipv6.h>
#else  /* i.e., !ifdef CONFIG_ATM_IPV6 */
#include <linux/atmclip.h>
#endif /* ifdef CONFIG_ATM_IPV6 */

#include <sys/socket.h>
#define _LINUX_NETDEVICE_H /* glibc2 */
#include <linux/if_arp.h>

#include "atmd.h"

#include "table.h"
#include "io.h"
#include "itf.h"
#include "arp.h"


#define COMPONENT "ITF"


ITF *lookup_itf(int number)
{
    ITF *itf;

    for (itf = itfs; itf; itf = itf->next)
	if (itf->number == number) break;
    return itf;
}


ITF *lookup_itf_by_ip(uint32_t ip)
{
    ITF *itf;

    for (itf = itfs; itf; itf = itf->next)
	if (!((itf->local_ip ^ ip) & itf->netmask)) break;
    return itf;
}


void itf_create(int number)
{
#ifdef CONFIG_ATM_IPV6
    ITF *itf;
#endif /* ifdef CONFIG_ATM_IPV6 */

    diag(COMPONENT,DIAG_DEBUG,"ITF CREATE %d",number);
#ifdef CONFIG_ATM_IPV6
    if (lookup_itf(number)) {
	diag(COMPONENT,DIAG_ERROR,"interface is already active");
	return;
    }
    itf = alloc_t(ITF);
    itf->number = number;
    memset(&itf->qos,0,sizeof(struct atm_qos));
    itf->qos.aal = ATM_AAL5;
    itf->qos.txtp.traffic_class = ATM_UBR;
    itf->qos.txtp.max_sdu = RFC1483LLC_LEN+RFC1626_MTU;
    itf->qos.rxtp = itf->qos.txtp;
    itf->sndbuf = 0; /* use system default */
    itf->table = itf->arp_srv = NULL;
    Q_INSERT_HEAD(itfs,itf);
#endif /* ifdef CONFIG_ATM_IPV6 */
}


void itf_up(int number)
{
#ifndef CONFIG_ATM_IPV6
    ITF *itf;
#endif /* ifndef CONFIG_ATM_IPV6 */

    diag(COMPONENT,DIAG_DEBUG,"ITF UP %d",number);
#ifndef CONFIG_ATM_IPV6
    if (lookup_itf(number)) {
	diag(COMPONENT,DIAG_ERROR,"interface is already active");
	return;
    }
    itf = alloc_t(ITF);
    if (ip_itf_info(number,&itf->local_ip,&itf->netmask,&itf->mtu) < 0) {
	diag(COMPONENT,DIAG_WARN,"interface can no be active");
	free(itf);
	return;
    }
    itf->number = number;
    memset(&itf->qos,0,sizeof(struct atm_qos));
    itf->qos.aal = ATM_AAL5;
    itf->qos.txtp.traffic_class = ATM_UBR;
    itf->qos.txtp.max_sdu = RFC1483LLC_LEN+RFC1626_MTU;
    itf->qos.rxtp = itf->qos.txtp;
    itf->sndbuf = 0; /* use system default */
    itf->table = itf->arp_srv = NULL;
    Q_INSERT_HEAD(itfs,itf);
#endif /* ifndef CONFIG_ATM_IPV6 */
}


static void itf_bring_down(ITF *itf)
{
    ENTRY *entry,*next;

    for (entry = itf->table; entry; entry = next) {
	next = entry->next;
#ifndef CONFIG_ATM_IPV6
	discard_entry(entry);
#endif /* ifndef CONFIG_ATM_IPV6 */
    }
#ifndef CONFIG_ATM_IPV6
    Q_REMOVE(itfs,itf);
    free(itf);
#endif /* ifndef CONFIG_ATM_IPV6 */
}


void itf_down(int number)
{
    ITF *itf;

    diag(COMPONENT,DIAG_DEBUG,"ITF DOWN %d",number);
    itf = lookup_itf(number);
    if (!itf) {
	diag(COMPONENT,DIAG_ERROR,"no such interface (%d)",number);
	return;
    }
    itf_bring_down(itf);
}


void itf_change(int number)
{
				/* TODO: support CONFIG_ATM_IPV6 */
#ifndef CONFIG_ATM_IPV6
    ITF *itf;
    ENTRY *entry,*next,*disconnected;
    uint32_t local_ip,netmask;
    int mtu;

    diag(COMPONENT,DIAG_DEBUG,"ITF CHANGE %d",number);
    itf = lookup_itf(number);
    if (!itf) {
	diag(COMPONENT,DIAG_DEBUG,"no interface to change (%d)",number);
	return;
    }
    if (ip_itf_info(number,&local_ip,&netmask,&mtu) < 0) {
	itf_bring_down(itf);
	return;
    }
    disconnected = NULL;
    for (entry = itf->table; entry; entry = next) {
	next = entry->next;
	if ((entry->flags & ATF_PERM) &&
	  !((entry->ip ^ local_ip) & (netmask | itf->netmask))) continue;
	if ((entry->flags & ATF_ARPSRV) && !((entry->ip ^ local_ip) & netmask))
	  {
	    disconnected = entry;
	    discard_vccs(entry);
	    /* @@@ should adjust max_sdu if mtu changed */
	    continue;
	}
	discard_entry(entry);
    }
    itf->local_ip = local_ip;
    itf->netmask = netmask;
    itf->mtu = mtu;
    if (disconnected) vcc_detach(disconnected);
#endif /* ifndef CONFIG_ATM_IPV6 */
}
