/*
 * GRE IP/IP (really ANY/IP)
 * RFC 1701 and 1702
 *
 * Francis.Dupont@inria.fr March 1998,
 * from SunOS implementation, September 1993
 */

/*
 * Configuration tool:
 *
 * exemple:
 *  ifconfig lo0 10.0.0.1 alias
 *  ifconfig gre0 myhost 10.0.0.2
 *  confgre gre0 ptype 0x800 hishost
 *  ifconfig gre0 up
 *  sysctl -w "net.inet.gre.debug=0"
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>

#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netinet/if_gre.h>

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>

struct gre_data gd;
int s;
char name[30];
int val;
struct hostent *hp;
int setgre = 0;

main(argc, argv)
    int argc;
    char *argv[];
{
    if (argc < 2) {
	fprintf(stderr, "usage: %s interface\n%s%s%s%s\n",
		argv[0],
		"\t[ <peer_address>]\n",
		"\t[ flags <flags>]\n",
		"\t[ ptype <ptype>]\n",
		"\t[ key <inband_flow>]\n",
		"\t[ seq <outband_flow>]\n");
	exit(1);
    }
    argc--, argv++;
    strncpy(name, *argv, sizeof(name));
    argc--, argv++;

    (void)bzero((caddr_t)&gd, sizeof (struct gre_data));
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
	perror("confgre: socket");
	exit(1);
    }
    /* get status */
    strncpy(gd.gre_name, name, sizeof(gd.gre_name));
    if (ioctl(s, SIOCGIFGRED, (caddr_t)&gd) < 0) {
	perror("confgre: ioctl (SIOCGIFGRED)");
	exit(1);
    }
    while (argc > 0) {
	if (strcmp(*argv, "flags") == 0) {
	    /* flags & version */
	    argc--, argv++;
	    setgre = 1;
	    if (argc == 0) {
		fprintf(stderr, "confgre: no argument for flags\n");
		exit(1);
	    }
	    (void) sscanf(*argv, "%i", &val);
	    gd.gre_flags = (u_int16_t)val;
	    argc--, argv++;
	    continue;
	}
	if (strcmp(*argv, "ptype") == 0) {
	    /* protocol type */
	    argc--, argv++;
	    setgre = 1;
	    if (argc == 0) {
		fprintf(stderr, "confgre: no argument for ptype\n");
		exit(1);
	    }
	    (void) sscanf(*argv, "%i", &val);
	    gd.gre_ptype = (u_int16_t)val;
	    argc--, argv++;
	    continue;
	}
	if (strcmp(*argv, "key") == 0) {
	    /* key */
	    argc--, argv++;
	    setgre = 1;
	    if (argc == 0) {
		fprintf(stderr, "confgre: no argument for key\n");
		exit(1);
	    }
	    (void) sscanf(*argv, "%i", &val);
	    gd.gre_key = (u_int32_t)val;
	    argc--, argv++;
	    continue;
	}
	if (strcmp(*argv, "seq") == 0) {
	    /* sequence number */
	    argc--, argv++;
	    setgre = 1;
	    if (argc == 0) {
		fprintf(stderr, "confgre: no argument for seq\n");
		exit(1);
	    }
	    (void) sscanf(*argv, "%i", &val);
	    gd.gre_seq = (u_int32_t)val;
	    argc--, argv++;
	    continue;
	}
	/* set peer addr */
	setgre = 1;
	hp =gethostbyname(*argv);
	if (hp && hp->h_addrtype == AF_INET)
	    bcopy(hp->h_addr, (caddr_t)&gd.gre_peer, hp->h_length);
	else {
	    fprintf(stderr, "confgre: bad peer %s\n", *argv);
	}
	argc--, argv++;
    }
    if (setgre) {
	if (ioctl(s, SIOCSIFGRED, (caddr_t)&gd) < 0) {
	    perror("confgre: ioctl (SIOCSIFGRED)");
	    exit(1);
	}
    }
    printf("%s: ", name);
    printf("peer = %s ", inet_ntoa(gd.gre_peer));
    printf("flags = 0x%04x ", (int)gd.gre_flags);
    printf("ptype = 0x%04x ", (int)gd.gre_ptype);
    printf("key = %u ", gd.gre_key);
    printf("seq = %u\n", gd.gre_seq);
    exit(0);
}
