/*
 * 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.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "freeswan.h"
#include <net/pfkeyv2.h>
#include <net/pfkey.h>
#include "pfkeylib.h"
#include "pfkey_internal.h"

static char version[] = "$USAGI: pfkey.c,v 1.9 2002/01/15 16:56:55 mk Exp $"; 
static char *basename;

#ifdef PFKEY_UTIL_DEBUG
#define DEBUG_MSG(fmt, arg...) fprintf(stderr,fmt,##arg)
#else
#define DEBUG_MSG(fmt,arg...) \
        do { } while (0)
#endif /* PFKEY_UTIL_DEBUG */

#define ADDRESS_DELIM  "/" /* address/prefix */
#define LIFETIME_DELIM "/" /* allocation/byte/add/use */
#define MAX_AUTH_KEY_LEN 1024
#define MAX_ESP_KEY_LEN 1024

#define OPT_APPEND	1
#define OPT_DELETE	2
#define OPT_FLUSH	3
#define OPT_TYPE	4
#define OPT_SPI		5
#define OPT_PFS		6
#define OPT_HARD	7
#define OPT_SOFT	8
#define OPT_PROTO	9
#define OPT_SRC		10
#define OPT_DST		11
#define OPT_SPORT	12
#define OPT_DPORT	13
#define OPT_AUTH	14
#define OPT_ESP		15
#define OPT_AUTHKEY	16
#define OPT_ESPKEY	17
#define OPT_VERSION	18
#define OPT_HELP	19
#define OPT_LIST	20


static struct option const longoptions[] =
{
	{"append",		required_argument, (int *)0, OPT_APPEND},	/* A */
	{"delete",		required_argument, (int *)0, OPT_DELETE},	/* D */
	{"flush",		required_argument, (int *)0, OPT_FLUSH},	/* F */
	{"type",		required_argument, (int *)0, OPT_TYPE},		/* T */
	{"spi", 		required_argument, (int *)0, OPT_SPI},		/* S */
	{"pfs",			no_argument,       (int *)0, OPT_PFS},		/* P */
	{"hard",		required_argument, (int *)0, OPT_HARD},   
	{"soft",		required_argument, (int *)0, OPT_SOFT},   
	{"protocol",		required_argument, (int *)0, OPT_PROTO},	/* p */
	{"source",		required_argument, (int *)0, OPT_SRC},		/* s */
	{"src",			required_argument, (int *)0, OPT_SRC},		/* s */
	{"destination",		required_argument, (int *)0, OPT_DST},		/* d */
	{"dst",			required_argument, (int *)0, OPT_DST},		/* d */
	{"source-port",		required_argument, (int *)0, OPT_SPORT}, 
	{"sport",		required_argument, (int *)0, OPT_SPORT}, 
	{"destination-port",	required_argument, (int *)0, OPT_DPORT}, 
	{"dport",		required_argument, (int *)0, OPT_DPORT}, 
	{"auth", 		required_argument, (int *)0, OPT_AUTH}, 
	{"esp",			required_argument, (int *)0, OPT_ESP}, 
	{"authkey",		required_argument, (int *)0, OPT_AUTHKEY},
	{"espkey",		required_argument, (int *)0, OPT_ESPKEY}, 
	{"version",		no_argument,       (int *)0, OPT_VERSION},	/* v */
	{"help",		no_argument,       (int *)0, OPT_HELP},		/* h */ 
	{"list",		no_argument,       (int *)0, OPT_LIST},		/* L */
	{0, 0, 0, 0}
};

static const char *shortoptions = "A:D:F:T:S:P:p:s:d:vhL";

void print_header(FILE *stream);
void print_version(FILE *stream);
void print_usage(FILE *stream);
int parse_address(struct sockaddr *address, char *optarg);
int parse_lifetime(struct lifetime *lifetime, char *optarg);
int is_allnumber(const char *s);
int char2hex(char c);
int parse_keybits(char *keystr, char *key, int keybits);
int parse_option(int argc, char **argv, struct query_handle *q);
int show_status(void);

int main(int argc, char **argv)
{
	int i, c, tmp;
	int error = 0;
	int pfkey_sock = 0;
	struct query_handle query;

	basename = strrchr(argv[0], '/');
	if (basename)
		basename++;
	else
		basename = argv[0];

	memset(&query, 0, sizeof(query));
	if (parse_option(argc, argv, &query)<0) {
		exit(EXIT_FAILURE);
	}

	if (getuid() != 0) {
		fprintf(stderr, "You must be privilage user.\n");
		return -1;
	}	

	pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);

	if (pfkey_sock < 0) {
		fprintf(stderr, "socket failed with %s\n", strerror(pfkey_sock));
	}

	switch (query.msgtype) {
	case SADB_ADD:
	case SADB_DELETE:
	case SADB_X_ADDFLOW:
	case SADB_X_DELFLOW:
		if (((struct sockaddr*)&(query.address_s))->sa_family !=
			 ((struct sockaddr*)&(query.address_s))->sa_family) {
			fprintf(stderr, "address family of source and destination are deffrent\n");
			exit(EXIT_FAILURE);
		}
		switch (((struct sockaddr*)&(query.address_s))->sa_family) {
		case AF_INET:
			((struct sockaddr_in*)&(query.address_s))->sin_port = htons((uint16_t) query.port_s);
			((struct sockaddr_in*)&(query.address_d))->sin_port = htons((uint16_t) query.port_d);
			break;
		case AF_INET6:
			((struct sockaddr_in6*)&(query.address_s))->sin6_port = htons((uint16_t) query.port_s);
			((struct sockaddr_in6*)&(query.address_d))->sin6_port = htons((uint16_t) query.port_d);
			break;
		default:
			fprintf(stderr, "address family is unknown\n");
			exit(EXIT_FAILURE);
		}
		break;
	default:
	}

	switch (query.msgtype) {
	case SADB_ADD:
		error = pfkey_send_add(pfkey_sock, &query, SADB_SASTATE_MATURE);
		if (error<0) fprintf(stderr, "append SA failed\n");
		break;
	case SADB_DELETE:
		error = pfkey_send_delete(pfkey_sock,&query);
		if (error<0) fprintf(stderr, "delete SA failed\n");
		break;	
	case SADB_X_ADDFLOW:
		error = pfkey_send_addflow(pfkey_sock, &query);
		if (error<0) fprintf(stderr, "append policy failed\n");
		break;
	case SADB_X_DELFLOW:
		error = pfkey_send_delflow(pfkey_sock, &query);
		if (error<0) fprintf(stderr, "delete policy failed\n");
		break;
	case SADB_FLUSH:
		switch (query.satype) {
		case SADB_SATYPE_AH:
		case SADB_SATYPE_ESP:
			error = pfkey_send_flush(pfkey_sock, &query);
			if (error<0) fprintf(stderr, "flush failed\n");
			break;
		default:
			error = pfkey_send_flush(pfkey_sock, &query);
			if (error<0) fprintf(stderr, "flush failed\n");
			error = pfkey_send_flush_sp(pfkey_sock, &query);
			if (error<0) fprintf(stderr, "flush_sp failed\n");
			break;
		}
		break;
	case SADB_X_FLUSH_SP:
		error = pfkey_send_flush_sp(pfkey_sock, &query);
		if (error<0) fprintf(stderr, "flush_sp_failed\n");
		break;
	default:
		print_header(stderr);
		print_usage(stderr);
		error = -EINVAL;
	}
	
	close(pfkey_sock);

err:
	return error;
}

void print_header(FILE *stream)
{
	fprintf(stream,
		"Security Association/Security Policy Management Tool\n"
		"Copyright (C)2001 USAGI/WIDE Project,  All Rights Reserved.\n"
		"\n");
}

void print_version(FILE *stream) 
{
	fprintf(stream, "%s: %s\n", basename, version);
}

void print_usage(FILE *stream)
{
	fprintf(stream,
		"Usage:\n"
		"   To append Security Association / Security Policy:\n"
		"\tpfkey\t -A|--append                   {sa|sp}\n"
			"\t\t -T|--type                     {ah|esp}\n"
			"\t\t -S|--spi                      spi\n"
			"\t\t[-P|--pfs]\n"
			"\t\t[   --hard                     allocs/bytes/age/usetime]\n"
			"\t\t[   --soft                     allocs/bytes/age/usetime]\n"
			"\t\t[-p|--protocol                 {any|tcp|udp}]\n"
			"\t\t -s|--src|--source             src_address/prefixlen\n"
			"\t\t[--sport|--source-port      src_port]\n"
			"\t\t -d|--dst|--destination        dst_address/prefixlen\n"
			"\t\t[--dport|--destination-port dst_port]\n"
			"\t\t    --auth                     {hmac-md5|hmac-sha1}\n"
			"\t\t    --authkey                  key\n"
			"\t\t    --esp                      {des-cbc|3des-cbc|aes-cbc}\n" 
			"\t\t    --espkey                   key\n"
		"\n"
		"    To delete Security Assosiation / Security Policy:\n"
		"\tpfkey\t -D|--delete                   {sa|sp}\n"
			"\t\t -S|--spi                      spi\n"
			"\t\t -s|--src|--source             src_address/prefixlen\n"
			"\t\t[--sport|--source-port      src_port]\n"
			"\t\t -d|--dst|--destination        dst_address/prefixlen\n"
			"\t\t[--dport|--destination-port dst_port]\n"
		"\n"
		"    To flush Security Association:\n"
		"\tpfkey\t -F|--flush                    {any|ah|esp}\n"
		"\n"
		"    To show status:\n"
		"\tpfkey\t -L|--list\n" 
		"\n"
		"    To show version:\n"
		"\tpfkey\t -v|--version\n"
		"\n"
		"    To show this message:\n"
		"\tpfkey\t -h|--help\n"
	);
}

int parse_option(int argc, char **argv, struct query_handle *q)
{
	int c,i,tmp;
	int error;
	int option_index;

	if (argc < 2) {
		print_header(stderr);
		print_usage(stderr);
		return -1;
	}


	while ((c = getopt_long(argc, argv, shortoptions, longoptions, &option_index)) != EOF) {
		error = 0;

		/* get arguments */

		DEBUG_MSG("optarg:=%s\n", optarg);

		switch(c){
		case 'A':
		case OPT_APPEND: /* sadb_msg_type */
			if (!strcasecmp("sa", optarg)) {
				q->msgtype = SADB_ADD;
			} else if (!strcasecmp("sp", optarg)) {
				q->msgtype = SADB_X_ADDFLOW;
			} else {
				fprintf(stderr, "specify sa or sp\n");
				goto err;
			}
			break;
		case 'D':
		case OPT_DELETE: /* sadb_msg_type */
			if (!strcasecmp("sa", optarg)) {
				q->msgtype = SADB_DELETE;
			} else if (!strcasecmp("sp", optarg)) {
				q->msgtype = SADB_X_DELFLOW;
			} else {
				fprintf(stderr, "specify sa or sp\n");
				goto err;
			}
			break;
		case 'F':
		case OPT_FLUSH:
			q->msgtype = SADB_FLUSH;
			if (optarg) {
				if (!strcasecmp("AH", optarg)) {
					q->satype = SADB_SATYPE_AH;
				} else if (!strcasecmp("ESP", optarg)) {
					q->satype = SADB_SATYPE_ESP;
				} else if (!strcasecmp("SP", optarg)) {
					q->msgtype = SADB_X_FLUSH_SP;
				} else {
					q->satype = SADB_SATYPE_UNSPEC;
				}
			} else {
				q->satype = SADB_SATYPE_UNSPEC;
			}
			break;
		case 'T':
		case OPT_TYPE:
			if (optarg) {
				if (!strcasecmp("AH", optarg)) {
					q->satype = SADB_SATYPE_AH;
				} else if (!strcasecmp("ESP", optarg)) {
					q->satype = SADB_SATYPE_ESP;
				} else {
					q->satype = SADB_SATYPE_UNSPEC;
				}
			} else {
				q->satype = SADB_SATYPE_UNSPEC;
			}
	                break;
		case 'S':
		case OPT_SPI: /* sadb_msg_spi */
			if (!strncasecmp("0x", optarg, 2)) {
				q->spi = strtoul(optarg+2, NULL, 16);	
			} else {
				if (!strncasecmp("any", optarg, 3)) {
					q->spi = 0xFFFFFFFF;
				} else {
					q->spi = strtoul(optarg, NULL, 10);
				}
			}
			DEBUG_MSG("spi:=0x%x\n", q->spi);
			break;
		case 'P':
		case OPT_PFS: /* sadb_sa_replay */
			q->flags = SADB_SAFLAGS_PFS;
			break;
		case OPT_HARD: /* sadb_ext_lifetime */
			error = parse_lifetime(&(q->lifetime_h), optarg);
			if (error) {
				fprintf(stderr, "lifetime hard is invalid value\n");
				goto err;
			}
			break;
		case OPT_SOFT: /* sadb_ext_lifetime */
			error = parse_lifetime(&(q->lifetime_s), optarg);
			if (error) {
				fprintf(stderr, "lifetime hard is invalid value\n");
				goto err;
			}
			break;
		case 'p':
		case OPT_PROTO: /* transport layer protocol */
			if (!strcasecmp("tcp", optarg)) {
				q->protocol = IPPROTO_TCP;
			} else if (!strcasecmp("udp", optarg)) {
				q->protocol = IPPROTO_UDP;
			} else {
				q->protocol = 0;
			}
			break;
		case 's':
		case OPT_SRC: /* The side of address parameters is source */
			tmp = parse_address((struct sockaddr*)&(q->address_s), optarg);
			if (tmp<0) {
				fprintf(stderr, "parse_address is failed\n");
				goto err;
			} else {
				q->prefixlen_s = (uint8_t) tmp;
			}
			break;
		case 'd':
		case OPT_DST: /* The side of address parameters is distination */
			tmp = parse_address((struct sockaddr*)&(q->address_d), optarg);
			if (tmp<0) {
				fprintf(stderr, "parse_address is failed\n");
				goto err;
			} else {
				q->prefixlen_d = (uint8_t) tmp;
			}	
			break;
		case OPT_SPORT: /* sadb_address family */
			q->port_s = strtoul(optarg, NULL, 10);
			if (q->port_s > USHRT_MAX) {
				fprintf(stderr, "source port is invalid\n");
				goto err;
			}
			/* do htons() later */
			break;
		case OPT_DPORT:
			q->port_d = strtoul(optarg, NULL, 10);
			if (q->port_d == USHRT_MAX) {
				fprintf(stderr, "destination port is invalid\n");
				goto err;
			}
			/* do htons() later */
			break;
		case OPT_AUTH:
			if (!strcasecmp("hmac-md5", optarg)) {
				q->aalg = SADB_AALG_MD5HMAC;
				q->key_bits_a = 128;
			} else if (!strcasecmp("hmac-sha1", optarg)) {
				q->aalg = SADB_AALG_SHA1HMAC;
				q->key_bits_a = 160;
			} else {
				fprintf(stderr, "algorithm is not supported\n");
			}
			break;
		case OPT_ESP:
			if (!strcasecmp("des-cbc", optarg)) {
				q->ealg = SADB_EALG_DESCBC;
				q->key_bits_e = 64;
			} else if (!strcasecmp("3des-cbc", optarg)) {
				q->ealg = SADB_EALG_3DESCBC;
				q->key_bits_e = 192;
			} else if (!strcasecmp("null", optarg)) {
				q->ealg = SADB_EALG_NULL;
				q->key_bits_e = 0;
			} else if (!strcasecmp("aes-cbc", optarg)) {
				q->ealg = SADB_EALG_AES;
				q->key_bits_e = 128;
			} else {
				fprintf(stderr, "algorithm is not supported\n");
			}
			break;
	
		case OPT_AUTHKEY:
			//memset( q->key_a, 0, MAX_AUTH_KEY_LEN);
			if (!strncasecmp("0x", optarg, 2))
				error = parse_keybits(optarg+2, q->key_a, q->key_bits_a);
			else if (is_allnumber(optarg)) /* decimal numbers */ 
				error = parse_keybits(optarg, q->key_a, q->key_bits_a);
			else { /* generic string */
				if (strlen(optarg) < q->key_bits_a/8)
					error = -EINVAL;
				memcpy(q->key_a, optarg, q->key_bits_a/8);
			}

			if (error) {
				fprintf(stderr, "auth key param parse failed\n");
				goto err;
			}
			for (i=0; i<q->key_bits_a/8; i++) {
				DEBUG_MSG("%x", 0xFF & q->key_a[i]);
			}
			DEBUG_MSG("\n");
			break;
		case OPT_ESPKEY:
			memset( q->key_e, 0, MAX_ESP_KEY_LEN);
			if (!strncasecmp("0x", optarg, 2))
				error = parse_keybits(optarg+2, q->key_e, q->key_bits_e);
			else if (is_allnumber(optarg))
				error = parse_keybits(optarg, q->key_e, q->key_bits_e); 
			else { 
				if (strlen(optarg) < q->key_bits_e/8)
					error = -EINVAL;
				memcpy(q->key_e, optarg, q->key_bits_e/8);
			}

			if (error) {
				fprintf(stderr, "esp key param parse failed\n");
				goto err;
			}
			for (i=0; i<q->key_bits_e/8; i++) {
				DEBUG_MSG("%x", 0xFF & q->key_e[i]);
			}
			DEBUG_MSG("\n");
			break;
		case 'L':
		case OPT_LIST:
			show_status();
			goto err;
		case 'v':
		case OPT_VERSION:
			print_header(stdout);
			print_version(stdout);
			goto err;
		case 'h':
		case OPT_HELP: /* help */
		default:
			print_header(stderr);
			print_usage(stderr);
			goto err;
		}
	}
	return 0;
err:
	return -1;
}

int parse_address(struct sockaddr* address, char* optarg)
{
	char buf[256];
	char *str_addr, *str_prefix;
	uint16_t prefix = 0;

	/* for getaddrinfo */
        int gai;
        struct addrinfo hints, *ai;

	if (!(address && optarg)) {
		fprintf(stderr, "parse_address: address or optarg is null\n");
		return -EINVAL;
	}

	memset(buf, 0, 256);
	str_addr = strtok_r(optarg, ADDRESS_DELIM, (char**)&buf);
	if (!str_addr) {
		fprintf(stderr, "parse_address: str_addr is null\n");
		return -EINVAL;
	}

	str_prefix = strtok_r(NULL, ADDRESS_DELIM, (char**)&buf);

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_UNSPEC;
	hints.ai_flags |= AI_NUMERICHOST;

	gai = getaddrinfo(str_addr, NULL, &hints, &ai);

	if (gai) {
		fprintf(stderr, "parse_address: getaddrinfo failed with %s: %s\n", str_addr, gai_strerror(gai));
		return -EINVAL;
	}

	switch (ai->ai_addr->sa_family) {
	case AF_INET:
		DEBUG_MSG("IPv4:%u.%u.%u.%u\n"
				, (((struct sockaddr_in*)ai)->sin_addr.s_addr >>  0) & 0xFF
				, (((struct sockaddr_in*)ai)->sin_addr.s_addr >>  8) & 0xFF
				, (((struct sockaddr_in*)ai)->sin_addr.s_addr >> 16) & 0xFF
				, (((struct sockaddr_in*)ai)->sin_addr.s_addr >> 24) & 0xFF);
		memcpy(address, ai->ai_addr, sizeof(struct sockaddr_in));			
		break;
	case AF_INET6:
		DEBUG_MSG("IPv6:%x:%x:%x:%x:%x:%x:%x:%x\n",
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[0],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[1],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[2],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[3],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[4],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[5],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[6],
				((struct sockaddr_in6*)ai->ai_addr)->sin6_addr.s6_addr16[7] );
		memcpy(address, ai->ai_addr, sizeof(struct sockaddr_in6));			
		break;
	default:
		fprintf(stderr, "parse_address: address family is not expected\n");
		return -EINVAL;
	}

	if (!str_prefix) { /* host */	
		switch(address->sa_family){
		case AF_INET:
			return 32;
		case AF_INET6:
			return 128;
		default:
			return -EINVAL;
		}
	} else {
		prefix = strtoul(str_prefix, NULL, 10);
		switch(address->sa_family){
		case AF_INET:
			return prefix <=32 ? prefix : -EINVAL;
		case AF_INET6:
			return prefix <= 128 ? prefix : -EINVAL;
		default:
			return -EINVAL;
		}

	}

}

int parse_lifetime(struct lifetime* lifetime, char* optarg)
{
	int i;
	char buf[256];
	char* arg[4];
	uint32_t tmp0;
	uint64_t tmp1, tmp2, tmp3;

	if (!lifetime) {
		fprintf(stderr, "parse_lifetime: lifetime is null\n");
		return -EINVAL;
	}

	memset(buf, 0, 256);
	arg[0] = strtok_r(optarg, LIFETIME_DELIM, (char**)&buf);
	for (i=1; i<4; i++) {
		arg[i] = strtok_r(NULL, LIFETIME_DELIM, (char**)&buf);
		if (!arg) {
			fprintf(stderr, "parse_lifetime: tokenize failed\n");
			return -EINVAL;
		}
	}

	tmp0 = strtoul(arg[0], NULL, 10);  /* allocation */
	tmp1 = strtoul(arg[1], NULL, 10);  /* byte */
	tmp2 = strtoul(arg[2], NULL, 10);  /* add */
	tmp3 = strtoul(arg[3], NULL, 10);  /* use */

	if( tmp0 == ULONG_MAX || tmp1 == ULONG_MAX || 
		tmp2 == ULONG_MAX || tmp3 == ULONG_MAX )
			return -EINVAL;

	lifetime->allocation	= tmp0; 
	lifetime->byte		= tmp1;
	lifetime->add		= tmp2;
	lifetime->use		= tmp3;

	return 0;		
}

int is_allnumber(const char *s)
{
	const char *p;

	for (p = s; *p; p++) { 
		if (!isdigit(*p))  return 0; 
	}
	return -1;
}


int char2hex(char c)
{

	int i=0;
	char tmp[] ="0123456789ABCDEF";
	char tmp2[] ="0123456789abcdef";

	for (i=0; tmp[i] != 0;i++) {
		if (c==tmp[i])
			return i;
		if (c==tmp2[i])
			return i;
	}

	return -EINVAL;
}


int parse_keybits(char* keystr, char* key, int keybits)
{
	int i=0, length=0;
	int j=0, keylen=0;
	int tmp1, tmp2;

	if (!(keystr&&key)) {
		fprintf(stderr, "parse_keybits: keystr or key is null\n");
		return -EINVAL;
	}

	length = strlen(keystr);

	if (!length) {
		fprintf(stderr, "parse_keybits: keystr length is 0\n");
		return -EINVAL;
	}

	keylen = keybits/8;

	for (i=0, j=0; i+1<length && j<keylen; i+=2) {

		tmp1=char2hex(keystr[i]);
		tmp2=char2hex(keystr[i+1]);

		if(tmp1<0 || tmp2<0){
			fprintf(stderr, "parse_keybits: parse failed\n");
			return -EINVAL;
		}

		DEBUG_MSG("i=%d, tmp1=%x, tmp2=%x\n", i, tmp1, tmp2);
		
		key[j] |= (char)( 0xFF & (tmp1<<4) );
		key[j] |= (char)( 0xFF & tmp2 );
		j++;
	}

	return 0;
}

int show_status(void)
{
	FILE *sadb_fp;
	FILE *spd_fp;
	char buf[BUFSIZ];

#define PROC_SADB	"/proc/net/sadb"
#define PROC_SPD	"/proc/net/spd"
	sadb_fp = fopen(PROC_SADB, "r");
	spd_fp = fopen(PROC_SPD, "r");

	fprintf(stdout, "SADB:\n");
	while ( (fgets(buf, sizeof(buf), sadb_fp)) != NULL) {
		fputs(buf, stdout);
	}

	fprintf(stdout, "\n");

	fprintf(stdout, "SPD:\n");
	while ( (fgets(buf, sizeof(buf), spd_fp)) != NULL) {
		fputs(buf, stdout);
	}

	return 0;
}
