/* $USAGI: pvctool.c,v 1.1 2001/11/01 10:56:41 serizawa Exp $ */
/* pvctool.c - management tool for PVCs */
/*
 * Copyright (C) 2001 USAGI/WIDE Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* Based on atmarpd.c - ATMARP demon */
/* Based on atmarp.c - RFC1577 ATMARP control */
 
/* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if_arp.h>

#include <stdint.h>

#include <linux/atmarp.h>
#include <linux/atmipv6.h>

#include "atm.h"
#include "atmd.h"
#include "atmarpd.h"

#include "io.h"
#include "table.h"


#ifndef NULL
#define NULL ((void *) 0)
#endif

#define COMPONENT "PVCTOOL"


ITF *itfs = NULL;

int debug;
int pretty = A2T_PRETTY | A2T_NAME | A2T_LOCAL;
int merge = 0;

#define BUF_SIZE 4096

int invoke_daemon(struct atmarp_req *req, int *reply);
void open_kernel(void);
void close_kernel(void);

static int send_request(struct atmarp_req *req)
{
    /* This is a dummy IF, which call daemon functions directly. */

    int len,reply;

    open_kernel();

    len = invoke_daemon(req, &reply);

    close_kernel();

    if (len < 0) {
	perror("read");
	exit(1);
    }
    if (len != sizeof(int)) {
	fprintf(stderr,"bad read: %d != %d\n",len,sizeof(int));
	exit(1);
    }
    if (*(int *) &reply < 0) {
	fprintf(stderr,"atmarp: %s\n",strerror(-*(int *) &reply));
	exit(1);
    }

    return *(int *) &reply;
}


static void usage(const char *name)
{
    fprintf(stderr,"usage: %s -c [[atm]N]\n",name);
    fprintf(stderr,"%6s %s -S N [itf.]vpi.vci \n","",name);
				/* TODO: verify */
#if 0  /* undocumented */
    fprintf(stderr,"%6s %s -q ip_addr [qos qos_spec] [sndbuf bytes]\n","",name);
    fprintf(stderr,"%6s %s -s ip_addr [itf.]vpi.vci [pcr value] [qos spec] "
      "[sndbuf bytes]\n%8s [temp] [pub] [null]\n","",name,"");
    fprintf(stderr,"%6s %s -s ip_addr atm_addr [pcr value] [qos spec] "
      "[sndbuf bytes] [temp]\n%8s [pub] [arpsrv]\n","",name,"");
    fprintf(stderr,"%6s %s -d ip_addr [arpsrv]\n","",name);
    fprintf(stderr,"%6s %s -Q ip_addr\n","",name);
#endif
    fprintf(stderr,"%6s %s -V\n","",name);
    exit(1);
}

int main(int argc,char **argv)
{
    struct atmarp_req req;
    int c,i,num;
    char *here,*end;

    set_verbosity(NULL,DIAG_DEBUG);
    set_logfile("stderr");

    req.type = 0;
    while ((c = getopt(argc,argv,"cdqQSV")) != EOF)
	switch (c) {
	    case 'c':
		if (req.type) usage(argv[0]);
		req.type = art_create;
		break;
	    case 'd':
		if (req.type) usage(argv[0]);
		req.type = art_delete;
		break;
	    case 'q':
		if (req.type) usage(argv[0]);
		req.type = art_qos;
		break;
	    case 'Q':
		if (req.type) usage(argv[0]);
		req.type = art_query;
		break;
	    case 'S':
		if (req.type) usage(argv[0]);
		req.type = art_setdirect;
		break;
	    case 'V':
		printf("%s\n",VERSION);
		return 0;
	    default:
		usage(argv[0]);
	}
    switch (req.type) {
	case art_create:
	    if (argc == optind) req.itf = -1;
	    else {
		if (argc != optind+1) usage(argv[0]);
		here = argv[optind];
		if (strlen(here) > 3 && !strncmp(here,"atm",3)) here += 3;
		req.itf = strtoul(here,&end,10);
		if (*end || (here[0] == '0' && here[1])) {
		    usage(argv[0]);
		    return 1;
		}
	    }
	    num = send_request(&req);
	    if (req.itf == -1) printf("atm%d\n",num);
	    return 0;
	case art_qos:
	    if (argc < optind+1) usage(argv[0]);
	    /* fall through */
	case art_set:
        case art_setdirect:
	    if (argc < optind+2) usage(argv[0]);
	    break;
	case art_query:
	    if (argc != optind+1) usage(argv[0]);
	    break;
	case art_delete:
	    if (argc < optind+1) usage(argv[0]);
	    break;
	default:
	    usage(argv[0]);
    }
    if (req.type == art_setdirect) {
        req.number = strtol(argv[optind],&end,0);
	if (*end) usage(argv[0]);
    }
    else {
    req.ip = text2ip(argv[optind],NULL,T2I_NAME | T2I_ERROR);
    if (req.ip == INADDR_NONE) return 1;
    }
    req.flags = ATF_PERM;
    if (req.type == art_qos) {
	memset(&req.qos,0,sizeof(req.qos));
	req.sndbuf = 0;
	for (i = optind+1; i < argc; i++)
	    if (!strcmp(argv[i],"qos")) {
		    if (++i >= argc) usage(argv[0]);
		    if (text2qos(argv[i],&req.qos,0)) usage(argv[0]);
		}
	    else if (!strcmp(argv[i],"sndbuf")) {
		    if (++i >= argc) usage(argv[0]);
		    req.sndbuf = strtol(argv[i],&end,0);
		    if (*end) usage(argv[0]);
		}
	    else if (i != optind+1 || argc != optind+2 ||
		  text2qos(argv[optind+1],&req.qos,0)) usage(argv[0]);
    }
    if ((req.type == art_set)||(req.type == art_setdirect)) {
	memset(&req.qos,0,sizeof(req.qos));
	req.sndbuf = 0;
	for (i = optind+2; i < argc; i++)
	    if (!strcmp(argv[i],"temp")) req.flags &= ~ATF_PERM;
	    else if (!strcmp(argv[i],"pub")) req.flags |= ATF_PUBL;
	    else if (!strcmp(argv[i],"null")) req.flags |= ATF_NULL;
	    else if (!strcmp(argv[i],"arpsrv")) req.flags |= ATF_ARPSRV;
	    else if (!strcmp(argv[i],"qos")) {
		    if (++i >= argc) usage(argv[0]);
		    if (text2qos(argv[i],&req.qos,0)) usage(argv[0]);
		}
	    else if (!strcmp(argv[i],"sndbuf")) {
		    if (++i >= argc) usage(argv[0]);
		    req.sndbuf = strtol(argv[i],&end,0);
		    if (*end) usage(argv[0]);
		}
	    else if (!strcmp(argv[i],"pcr")) {
		    if (++i >= argc) usage(argv[0]);
		    req.qos.txtp.traffic_class = req.qos.rxtp.traffic_class =
		      ATM_CBR;
		    req.qos.txtp.max_pcr = req.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.addr,
	  sizeof(req.addr),T2A_NAME) < 0) {
	    fprintf(stderr,"%s: invalid ATM address\n",argv[optind+1]);
	    return 1;
	}
    }
    if (req.type == art_delete && optind+1 < argc) {
	if (optind+2 < argc || strcmp(argv[optind+1],"arpsrv")) usage(argv[0]);
	req.flags |= ATF_ARPSRV;
    }
    if (!req.qos.aal) req.qos.aal = ATM_AAL5;
    send_request(&req);
    return 0;
}

