/* atmarp.c - RFC1577 ATMARP control */
 
/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h> /* struct ifreq */

#include <linux/atm.h>
#include <linux/atmarp.h>
#include <linux/atmclip.h>
#include <linux/netdevice.h> /* for MAX_ADDR_LEN for if_arp.h */
#include <linux/if_arp.h> /* for ATF_PERM and ATF_PUBL */
#include <linux/version.h>

#include "atm.h"


#define DUMP_DIR "/var/run"
#define BUF_SIZE 4096


static int print_table(void)
{
    char buffer[BUF_SIZE];
    int fd,size;

    if ((fd = open(DUMP_DIR "/atmarpd.table",O_RDONLY)) < 0) {
	perror("open " DUMP_DIR "/atmarpd.table");
	return 1;
    }
    while ((size = read(fd,buffer,BUF_SIZE))) {
	if (size < 0) {
	    perror("read " DUMP_DIR "/atmarpd.table");
	    return 1;
	}
	if (write(0,buffer,size) < 0) {
	    perror("write stdout");
	    return 1;
	}
    }
    return 0;
}


static void usage(const char *name)
{
    fprintf(stderr,"usage: %s -a\n",name);
    fprintf(stderr,"%6s %s -c [[atm]N]\n","",name);
    fprintf(stderr,"%6s %s -q ip_addr qos_spec\n","",name);
    fprintf(stderr,"%6s %s -s ip_addr [itf.]vpi.vci [pcr value] [qos spec] "
      "[temp] [pub]\n%8s [null]\n","",name,"");
    fprintf(stderr,"%6s %s -s ip_addr atm_addr [pcr value] [qos spec] [temp] "
      "[pub] [arpsrv]\n","",name);
    fprintf(stderr,"%6s %s -d ip_addr [arpsrv]\n","",name);
    exit(1);
}


int main(int argc,char **argv)
{
    struct hostent *hostent;
    struct atmarpreq req;
    int c,op,s,i,req_num,num;
    const char *qos;
    char *here,*end;

    op = 0;
    qos = NULL;
    while ((c = getopt(argc,argv,"acdqs")) != EOF)
	switch (c) {
	    case 'a':
		if (argc != optind) usage(argv[0]);
		return print_table();
	    case 'c':
		if (op) usage(argv[0]);
		op = SIOCMKCLIP;
		break;
	    case 'q':
		if (op) usage(argv[0]);
		op = SIOCSARP;
		qos = ""; /* non-NULL */
		break;
	    case 's':
		if (op) usage(argv[0]);
		op = SIOCSARP;
		break;
	    case 'd':
		if (op) usage(argv[0]);
		op = SIOCDARP;
		break;
	    default:
		usage(argv[0]);
	}
    switch (op) {
	case SIOCMKCLIP:
	    if (argc == optind) req_num = -1;
	    else {
		if (argc != optind+1) usage(argv[0]);
		here = argv[optind];
		if (strlen(here) > 3 && !strncmp(here,"atm",3)) here += 3;
		req_num = strtoul(here,&end,10);
		if (*end || (here[0] == '0' && here[1])) {
		    usage(argv[0]);
		    return 1;
		}
	    }
	    if ((s = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0) {
		perror("socket");
		return 1;
	    }
	    if ((num = ioctl(s,op,req_num)) < 0) {
		perror("ioctl SIOCMKCLIP");
		return 1;
	    }
	    if (req_num == -1) printf("atm%d\n",num);
	    return 0;
	case SIOCSARP:
	    if (argc < optind+2) usage(argv[0]);
	    if (qos && argc > optind+2) usage(argv[0]);
	    break;
	case SIOCDARP:
	    if (argc < optind+1) usage(argv[0]);
	    break;
	default:
	    usage(argv[0]);
    }
    ((struct sockaddr_in *) &req.arp_pa)->sin_family = AF_INET;
    if (strspn(argv[optind],"0123456789.") == strlen(argv[optind])) {
	if ((((struct sockaddr_in *) &req.arp_pa)->sin_addr.s_addr =
	  inet_addr(argv[optind])) == -1) {
	    fprintf(stderr,"%s: invalid address\n",argv[optind]);
	    return 1;
	}
    }
    else {
	if (!(hostent = gethostbyname(argv[optind]))) {
	    fprintf(stderr,"%s: no such host\n",argv[optind]);
	    return 1;
	}
	if (hostent->h_addrtype != AF_INET) {
	    fprintf(stderr,"%s: unknown protocol family\n",argv[0]);
	    return 1;
	}
	memcpy(&((struct sockaddr_in *) &req.arp_pa)->sin_addr,hostent->h_addr,
	  hostent->h_length);
    }
    req.arp_flags = ATF_PERM;
    if (op == SIOCSARP && qos) {
	req.arp_flags |= ATF_DEFQOS;
	if (text2qos(argv[optind+1],&req.arp_qos,0)) usage(argv[0]);
    }
    if (op == SIOCSARP && !qos) {
	memset(&req.arp_qos,0,sizeof(req.arp_qos));
	for (i = optind+2; i < argc; i++)
	    if (!strcmp(argv[i],"temp")) req.arp_flags &= ~ATF_PERM;
	    else if (!strcmp(argv[i],"pub")) req.arp_flags |= ATF_PUBL;
	    else if (!strcmp(argv[i],"null")) req.arp_flags |= ATF_NULL;
	    else if (!strcmp(argv[i],"arpsrv")) req.arp_flags |= ATF_ARPSRV;
	    else if (!strcmp(argv[i],"qos")) {
		    if (++i >= argc) usage(argv[0]);
		    if (text2qos(argv[i],&req.arp_qos,0)) usage(argv[0]);
		}
	    else if (!strcmp(argv[i],"pcr")) {
		    if (++i >= argc) usage(argv[0]);
		    req.arp_qos.txtp.traffic_class =
		      req.arp_qos.rxtp.traffic_class = ATM_CBR;
		    req.arp_qos.txtp.max_pcr = req.arp_qos.rxtp.max_pcr =
		      strtol(argv[i],&end,0);
		    if (*end) usage(argv[0]);
		}
	    else usage(argv[0]);
	if (text2atm(argv[optind+1],(struct sockaddr *) &req.arp_ha,
	  sizeof(req.arp_ha),T2A_NAME) < 0) {
	    fprintf(stderr,"%s: invalid ATM address\n",argv[optind+1]);
	    return 1;
	}
    }
    if (op == SIOCDARP && optind+1 < argc) {
	if (optind+2 < argc || strcmp(argv[optind+1],"arpsrv")) usage(argv[0]);
	req.arp_flags |= ATF_ARPSRV;
    }
    if (!req.arp_qos.aal) req.arp_qos.aal = ATM_AAL5;
    if ((s = socket(AF_INET,SOCK_DGRAM,0)) < 0) {
	perror("socket");
	return 1;
    }
#if LINUX_VERSION_CODE >= 0x20100
    *req.arp_dev = 0;
#endif
    if (ioctl(s,op,&req) < 0) {
	perror("ioctl SIOCxARP");
	return 1;
    }
    return 0;
}
