/*
 *      MIPL Diagnostics utility
 *
 *      Authors:
 *      Antti Tuominen          <ajtuomin@tml.hut.fi>
 *
 *      $Id: mipdiag.c,v 1.14 2001/07/20 08:47:17 antti Exp $
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      Changes:
 *      Alexandru Petrescu,
 *      Miguel Catalina-Gallego   :    Support for configuration of keys 
 *                                     via /proc/sys/... 
 */


#define EINVDIGIT       1
#define ESRCTOOLONG     2
#define ESRCTOOSHORT    3

#define UID0 0

#include <errno.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <ctype.h>

#include <fcntl.h>
#include <sys/ioctl.h>

#include "mip6.h"

static char base64_digits [] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz- ";

void usage();
void version();

void print_access();
void print_bcache();
void print_bulist();
void print_stats();

void access_do_one(char policy, char *addr, int prefix);
void bcache_do_one(char *haddr, char *caddr, int cbt, int type);
void bulist_do_one(char *cnaddr, char *haddr, char *coaddr, long int exp, 
		   int seq, int sta, int del, int mdl, long int cbs);
void stats_do_one(char *stat, int value);

void set_access(char *rule);
void set_debuglevel(char *level);
void set_tunnel_sitelocal(char *opt);
void sa_file(char *path);
void delsa(char *arg);
void showsa(char *arg);
int set_homeaddress(char *homeaddr);
int set_homeagent(char *homeagent);
int get_mn_info(void);
void acl_file(char *path);
void acl_add_rule(char *policy, struct in6_addr addr, int prefix);

int check_uid(int uid);
int format_addr(char *addr, char *faddr);
int base64_to_bin (uint8_t *dst,char *src,int size);

int main (int argc, char **argv)
{
	int c;
	
	while (1)
	{
		int option_index = 0;
		static struct option long_options[] =
		{
			{"info", 0, 0, 'i'},
			{"access", 0, 0, 'a'},
			{"rule", 1, 0, 'r'},
			{"rulelist", 1, 0, 'R'},
			{"statistics", 0, 0, 's'},
			{"bulist", 0, 0, 'l'},
			{"bcache", 0, 0, 'c'},
			{"help", 0, 0, '?'},
			{"version", 0, 0, 'V'},
			{"debuglevel", 1, 0, 'd'},
			{"tunnel_sitelocal", 1, 0, 't'},
			{"homeagent", 1, 0, 'H'},
			{"homeaddress", 1, 0, 'h'},
			{"safile", 2, 0, 'S'},
			{"showsa", 2, 0, 0},
			{"delsa", 1, 0, 0},
			{0, 0, 0, 0}
		};
		
		c = getopt_long (argc, argv, "S:R:H:iacd:h:lr:t:k:sV?",
				 long_options, &option_index);
		if (c == -1)
			break;
		
		switch (c)
		{
		case 0:

			if (strcmp(long_options[option_index].name, 
				   "showsa") == 0)
				showsa(optarg);
			if (strcmp(long_options[option_index].name,"delsa") 
			    == 0)
				delsa(optarg);
			
			break;
		case 'r':
			set_access(optarg);
			break;
		case 'H':
			set_homeagent(optarg);
			break;
		case 'i':
			get_mn_info();
			break;
		case 'a':
			print_access();
			break;
		case 'c':
			print_bcache();
			break;
		case 'd':
			set_debuglevel(optarg);
			break;
		case 'R':
			acl_file(optarg);
			break;
		case 'S':
			sa_file(optarg);
			break;
		case 'h':
			set_homeaddress(optarg);
			break;
		case 't':
			set_tunnel_sitelocal(optarg);
			break;
		case 's':
			print_stats();
			break;
		case 'l':
			print_bulist();
			break;
		case 'V':
			version();
			break;
		case '?':
			usage();
			break;
			
		default:
			printf ("mipdiag: invalid option -- %c. Try -? for help.\n", c);
		}
	}
	
	if (optind < argc)
	{
		printf ("non-option ARGV-elements: ");
		while (optind < argc)
			printf ("%s ", argv[optind++]);
		printf ("\n");
	}
	
	exit (0);
}

void usage()
{
	fprintf (stderr, "Usage: mipdiag [-acls]           Show Mobile IP information\n");
	fprintf (stderr, "       mipdiag [-hHdrtRMS value] Modify Mobile IP runtime parameters\n\n");
	fprintf (stderr, "       mipdiag [-?|--help]       Detailed usage syntax\n");
	fprintf (stderr, "       mipdiag [-V|--version]    Display version/author and exit\n\n");
	fprintf (stderr, "        -a, --access             show access control list\n");
	fprintf (stderr, "        -c, --bcache             show binding cache\n");
	fprintf (stderr, "        -l, --bulist             show bingding update list\n");
	fprintf (stderr, "        -i, --info               show MN information\n");
	fprintf (stderr, "        -s, --statistics         show statistics\n");
	fprintf (stderr, "        -S, --safile             filename for security associations\n");
	fprintf (stderr, "        -h, --homeaddress        set MN's home address\n");
	fprintf (stderr, "        -H, --homeagent          set MN's home agent's address\n");
	fprintf (stderr, "        -d, --debuglevel         set module's debug level\n");
	fprintf (stderr, "        -r, --rule               set access control rule\n");
	fprintf (stderr, "        -R  --rulelist           read access control list from file\n");
	fprintf (stderr, "        -t, --tunnel_sitelocal   set tunnel sitelocal option\n");
	fprintf (stderr, "        ---showsa                show SA for address or all SAs\n");
	fprintf (stderr, "        ---delsa                 delete SA for address\n");
}
void version()
{
	fprintf (stderr, "MIPL Mobile IPv6 0.9\n");
	fprintf (stderr, "mipdiag 0.20 (2001-05-26) Antti Tuominen\n");
}

void print_stats()
{
	FILE *stats;
	char stat[40];
	int value = 0;

	stats = fopen("/proc/net/mip6_stat", "r");
	if (stats != NULL) {
		printf ("Mobile IPv6 Statistics\n");
		while (!feof(stats)) {
			memset(stat, 0, 40);
			fscanf(stats, "%s = %d\n", stat, &value);
			stats_do_one(stat, value);
		}
		fclose(stats);
	} else {
		fprintf(stderr, "Could not read statistics information.\n");
		exit(ENOENT);
	}
}

void stats_do_one(char *stat, int value)
{
	printf("%-25s : %d\n", stat, value);
}

void print_access()
{
	FILE *access;
	char policy, addr[33];
	int prefix;

	access = fopen("/proc/net/mip6_access", "r");
	if (access != NULL) {
		printf("Mobile IPv6 Mobile Node Access Control List\n");
		printf("Policy   Address range                               Prefix length\n");
		while (!feof(access)) {
			memset(addr, 0, 33);
			fscanf(access, "%c %32s %2x\n", &policy, addr, &prefix);
			access_do_one(policy, addr, prefix);
		}
		fclose(access);
	} else {
		fprintf(stderr, "Could not read access control list.\n");
		exit(ENOENT);
	}
}

void access_do_one(char policy, char *addr, int prefix)
{
	char saddr[44];

	if (strlen(addr)) {
		memset(saddr, 0, 44);
		format_addr(addr, saddr);
		printf("%-8s %-44s %d\n", policy == '+' ? "ALLOW" : "DENY", saddr, prefix);
	}
}

void print_bcache()
{
	FILE *bcache;
	char haddr[33], caddr[33];
	long int callback_time = 0;
	int type = 0;

	bcache = fopen("/proc/net/mip6_bcache", "r");
	if (bcache != NULL) {
		printf("Mobile IPv6 Binding cache\n");
		printf("Home Address                                Care-of Address                             Lifetime  Type\n");
		while (!feof(bcache)) {
			memset(haddr, 0, 33);
			memset(caddr, 0, 33);
			fscanf(bcache, "h=%32s  c=%32s (e=%ld,t=%d)\n",
			       haddr, caddr, &callback_time, &type);
			bcache_do_one(haddr, caddr, callback_time, type);
		}
		fclose(bcache);
	} else {
		fprintf(stderr, "Could not read binding cache information.\n");
		exit(ENOENT);
	}
}

void bcache_do_one(char *haddr, char *caddr, int cbt, int type)
{
	char buf1[44], buf2[44];
	if (strlen(haddr) && strlen(caddr)) {
		memset(buf1,0,44);
		memset(buf2,0,44);

		format_addr(haddr, buf1);
		format_addr(caddr, buf2);
		printf("%-44s%-44s%-10d%-5d\n", buf1, buf2, cbt, type);
	}
}

void print_bulist()
{
	FILE *bulist;
	char cnaddr[33], haddr[33], coaddr[33];
	int seq = 0, sta = 0, del = 0, mdl = 0;
	long int exp = 0, cbs = 0;

	bulist = fopen("/proc/net/mip6_bul", "r");
	if (bulist != NULL) {
		printf ("Mobile IPv6 Binding update list\n");
		while (!feof(bulist)) {
			memset(cnaddr, 0, 33);
			memset(haddr, 0, 33);
			memset(coaddr, 0, 33);
			fscanf(bulist, "cna=%32s ha=%32s coa=%32s\nexp=%ld seq=%d sta=%d del=%d mdl=%d cbs=%ld\n",
			       cnaddr, haddr, coaddr, &exp, &seq, &sta, &del, &mdl, &cbs);
			bulist_do_one(cnaddr, haddr, coaddr, exp, seq, sta, del, mdl, cbs);
		}
		fclose(bulist);
	} else {
		fprintf(stderr, "Could not read binding update list information.\n");
		exit(ENOENT);
	}
}

void bulist_do_one(
	char *cnaddr, char *haddr, char *coaddr, 
	long int exp, int seq, int sta, int del, int mdl, long int cbs)
{

	char buf1[44], buf2[44], buf3[44];
	
	if (strlen(cnaddr) && strlen(haddr) && strlen(coaddr)) {
		memset(buf1,0,44);
		memset(buf2,0,44);
		memset(buf3,0,44);

		format_addr(cnaddr, buf1);
		format_addr(haddr, buf2);
		format_addr(coaddr, buf3);
		printf("Recipient CN: %s\n", buf1);
		printf("BINDING home address: %s care-of address: %s\n", buf2, buf3);
		printf("        expires: %ld sequence: %d state: %d\n", exp, seq, sta);
		printf("        delay: %d max delay %d callback time: %ld\n\n", del, mdl, cbs);
	}
}

void acl_add_rule(char *policy, struct in6_addr addr, int prefix)
{
	FILE *acl;
	char cpolicy;

	if (!strcasecmp(policy, "DENY"))
		cpolicy = '-';
	else if (!strcasecmp(policy, "ALLOW"))
		cpolicy = '+';
	else if (!strcasecmp(policy, "FLUSH"))
		cpolicy = '*';
	else {
		fprintf(stderr, "Access list format error: unknown policy \"%s\".\n",
			policy);
		exit(1);
	}

	acl = fopen("/proc/sys/net/ipv6/mobility/mobile_node_list", "r+");
	if (acl != NULL) {
		if (cpolicy == '*')
			fprintf(acl, "*");
		else
			fprintf(acl, "%c %08x%08x%08x%08x %02x", cpolicy,
				htonl(addr.s6_addr32[0]), 
				htonl(addr.s6_addr32[1]),
				htonl(addr.s6_addr32[2]), 
				htonl(addr.s6_addr32[3]),
				prefix);
		fclose(acl);
	}

}

void acl_file(char *path)
{
	FILE *acl;
	char buffer[128];

	if (!strcmp(path, "-"))
		acl = stdin;
	else
		acl = fopen(path, "r");

	if (acl != NULL) {
		while (!feof(acl)) {
			if (fgets(buffer, 128, acl) != NULL)
				set_access(buffer);
		}
		fclose(acl);
	}
	
}

void set_access(char *rule) 
{
	int plen = 0;
	char *policy, *addr, *n, *p;
	struct in6_addr saddr;

	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}

	if (strchr(rule, '#')) {
		return;
	}
	policy = (char *) malloc(128 * sizeof(char));
	addr = (char *) malloc(128 * sizeof(char));

	sscanf(rule, "%s %s", policy, addr); 
	
	if (strncasecmp(policy, "FLUSH", 5)) {
		n = strchr(addr, '/');
		if (n) {
			p = n + 1;
			*n = '\0';
			plen = strtoul(p, &p, 10);
			if (*p) exit(1);
		} else {
			fprintf(stderr, "Access list format error: no prefix length.\n");
			exit(1);
		}
		
		if(!inet_pton(AF_INET6, addr, &saddr)) {
			fprintf(stderr, "Access list format error: invalid IPv6 address.\n");
			exit(1);
		}
	} 
	acl_add_rule(policy, saddr, plen);
}
void delsa(char *addr_c)
{
	struct in6_addr addr;
	int mipv6_fd = open(CTLFILE, 0);
	if (mipv6_fd < 0) {
		perror("open:");
		return;
	}
	if(addr_c != NULL) {
		if (inet_pton(AF_INET6, addr_c, &addr) < 0) {
			fprintf(stderr, "invalid address for peer\n");
			return;
		}
		ioctl(mipv6_fd, IOCTL_DEL_SA_BUNDLE, &addr);	
	}
	close(mipv6_fd);
}	
void showsa(char *addr_c)
{
	struct in6_addr addr;
	int mipv6_fd = open(CTLFILE, 0);
	if (mipv6_fd < 0) {
		perror("open:");
		return;
	}
	if(addr_c != NULL) {
		if (inet_pton(AF_INET6, addr_c, &addr) < 0) {
			fprintf(stderr, "invalid address for peer\n");
			close(mipv6_fd);
			return;
		}

		ioctl(mipv6_fd, IOCTL_PRINT_SA, &addr);	
	}
	else {
		memset(&addr, 0, sizeof(struct in6_addr));
		ioctl(mipv6_fd, IOCTL_PRINT_SA, &addr);
	}
	close(mipv6_fd);
	
}	


/* Format for SAs in conf. file:  
 * peer address 
 * SPI 
 * Authent. algorithm
 * key for SA
 * hard lifetime
 * soft lifetime
 * direction
 */
int add_sa(int mipv6_fd, FILE *conf_fd)
{
	char buffer[128], *temp_val, *tmp;
	int ret;
	struct sa_ioctl sa;
	temp_val = (char *)malloc(32);
	if (temp_val == NULL) 
		return -1;
	memset(temp_val, 0, 32);
		
	memset(&sa, 0, sizeof(struct sa_ioctl));
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	if (buffer[0] == ' ' || buffer[0] == '\n') 
		return -1;
	strtok(buffer, " ");

	tmp = strtok(NULL,"\n");

	if (inet_pton(AF_INET6, tmp, &sa.addr) < 0) {
		fprintf(stderr, "invalid address for peer %s\n", tmp);
		return -1;
	}
	memset(temp_val, 0, 32);
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	temp_val = strstr(buffer,"spi: ") + 
		strlen("spi: ");	
	sa.spi = strtoul(temp_val,NULL, 0);

	if (!fgets(buffer, 127, conf_fd))
		return -1;
	if (strstr(buffer, "ALG_AUTH_HMAC_MD5") != NULL) {
		sa.auth_alg = ALG_AUTH_HMAC_MD5 ;
		sa.key_auth_len = 128/8;		
	}
	else if (strstr(buffer, "ALG_AUTH_HMAC_SHA1") != NULL) {	  
		sa.auth_alg = ALG_AUTH_HMAC_SHA1 ;
		sa.key_auth_len = 160/8;
	}	
	else {
		fprintf(stderr, "mipdiag: unknown authentication algorithm\n  %s\n", buffer);
		return -1;
	}
	memset(temp_val, 0, 32);
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	temp_val = strstr(buffer,"key: ") + 
		strlen("key: ");

	ret = base64_to_bin (sa.key_auth, temp_val, 
			     sa.key_auth_len );

	if (ret == -EINVDIGIT)
	{
		fprintf(stderr, "An invalid digit was used in the key\n");
		return -EINVDIGIT;
	}
	else if (ret == -ESRCTOOLONG)
	{
		fprintf(stderr, "The key was longer than expected\n");
		return -ESRCTOOLONG;
	}		
	else if (ret == ESRCTOOSHORT)
	{
		fprintf(stderr, "The key was shorter than expected\n");
		return -ESRCTOOSHORT;
	}				
	memset(temp_val, 0, 32);
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	temp_val = strstr(buffer,"lifetime: ") + 
		strlen("lifetime: ");
	sa.lifetime = strtoul(temp_val, NULL, 10);
	memset(temp_val, 0, 32);
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	temp_val = strstr(buffer,"soft_lifetime: ") + 
		strlen("soft_lifetime: ");
	sa.soft_lifetime = strtoul(temp_val, NULL, 10);
	memset(temp_val, 0, 32);
	if (!fgets(buffer, 127, conf_fd))
		return -1;
	if (strstr(buffer, "inbound") != NULL) 
		ret = ioctl(mipv6_fd, IOCTL_ADD_IB_SA, &sa);
	else if (strstr(buffer, "outbound") != NULL) 
		ret = ioctl(mipv6_fd, IOCTL_ADD_OB_SA, &sa);
	else {
		fprintf(stderr, "mipdiag: invalid direction for sa\n");
		return -1;
	}

	return ret;

}
void sa_file(char *path)
{
	FILE *sas;
	int fd;
	if(!path)
		strcpy(path, "/etc/mipv6_sas.conf");
	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}

	fd = open(CTLFILE, 0);
	if (fd < 0) {
		perror("open:");
		return;
	}
       
	if (!strcmp(path, "-"))
		sas = stdin;
	else
		sas = fopen(path, "r");


	if (sas != NULL) {
		while (!feof(sas) && (add_sa(fd, sas) == 0)) {
		}
		fclose(sas);
		
	}	
	close(fd);	
}
void set_debuglevel(char *level)
{
	FILE *debug;
	int ilevel = atoi(level);

	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}
	debug = fopen("/proc/sys/net/ipv6/mobility/debuglevel", "r+");
	if (debug != NULL) {
		if (fprintf(debug, "%d", ilevel) > 0)
			printf("Debug level set to %d\n", ilevel);
		else
			fprintf(stderr, "Unable to set debug level.\n");
		fclose(debug);
	} else {
		fprintf(stderr, "Could not set debug level. Is module loaded?\n");
		exit(ENOENT);
	}
}

void set_tunnel_sitelocal(char *opt)
{
	FILE *tunnel_sitelocal;
	int iopt = -1;

	if (!strcasecmp(opt, "YES") || !strcasecmp(opt, "ON") || 
	    !strcasecmp(opt, "Y") || !strcmp(opt, "1"))
		iopt = 1;
	else if (!strcasecmp(opt, "NO") || !strcasecmp(opt, "OFF") ||
		 !strcasecmp(opt, "N") || !strcmp(opt, "0"))
		iopt = 0;
	else {
		fprintf(stderr, "Invalid value for option: %s\n", opt);
		exit(EINVAL);
	}

	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}

	tunnel_sitelocal = fopen("/proc/sys/net/ipv6/mobility/tunnel_sitelocal", "r+");
	if (tunnel_sitelocal != NULL) {
		if (fprintf(tunnel_sitelocal, "%d", iopt) > 0)
			if (iopt==0)
				printf("Tunnel sitelocal option set to 'no'.\n");
			else
				printf("Tunnel sitelocal option set to 'yes'.\n");
		else
			fprintf(stderr, "Unable to set tunnel sitelocal option.\n");
		fclose(tunnel_sitelocal);
	} else {
		fprintf(stderr, "Could not set tunnel sitelocal option. Is module loaded?\n");
		exit(ENOENT);
	}
}

int set_homeaddress(char *addr)
{
	struct in6_ifreq hinfo;
	int fd, ret;
	char *p, *n;

	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}

	memset(&hinfo, 0, sizeof(hinfo));
	n = strchr(addr, '/');
	if (n) {
		p = n + 1;
		*n = '\0';
		hinfo.ifr6_prefixlen = strtoul(p, &p, 10);
		if (*p) hinfo.ifr6_prefixlen = 128;
	} else {
		fprintf(stderr, "Invalid home address: no prefix length\n");
		exit(-1);
	}

	inet_pton(AF_INET6, addr, &hinfo.ifr6_addr);

	fd = open(CTLFILE, 0);
	if (fd < 0) {
		perror("open");
		return -ENOENT;
	}

	ret = ioctl(fd, IOCTL_SET_HOMEADDR, &hinfo);

	if (ret < 0) {
		fprintf(stderr, "ioctl failed\n");
		return ret;
	}

	close(fd);

	return 0;
}

int set_homeagent(char *addr)
{
	char *p, *n;

	int fd, ret;
	struct ha_info hainfo;

	if (!check_uid(UID0)) {
		fprintf(stderr, "You must be root (uid = 0) to do this.\n");
		exit(EPERM);
	}

	fd = open(CTLFILE, 0);
	if (fd < 0) {
		perror("open:");
		return -ENOENT;
	}

	memset(&hainfo, 0, sizeof(hainfo));
	n = strchr(addr, '/');
	if (n) {
		p = n + 1;
		*n = '\0';
		hainfo.prefixlen = strtoul(p, &p, 10);
		if (*p) hainfo.prefixlen = 128;
	}

	inet_pton(AF_INET6, addr, &hainfo.addr);

	ret = ioctl(fd, IOCTL_SET_HOMEAGENT, &hainfo);

	if (ret < 0) {
		fprintf(stderr, "ioctl failed\n");
		return ret;
	}

	close(fd);

	return 0;
}

int get_mn_info(void)
{
	int fd, ret;
	char address[INET6_ADDRSTRLEN];
	struct ha_info *hainfo;
	struct in6_ifreq *hinfo;
	struct in6_addr *careof;

	fd = open(CTLFILE, 0);
	if (fd < 0) {
		perror("open:");
		return -ENOENT;
	}

	hainfo = malloc(sizeof(struct ha_info));
	hinfo = malloc(sizeof(struct in6_ifreq));
	careof = malloc(sizeof(struct in6_addr));
	memset(hainfo, 0, sizeof(struct ha_info));
	memset(hinfo, 0, sizeof(struct in6_ifreq));
	memset(careof, 0, sizeof(struct in6_addr));
	
	ret = ioctl(fd, IOCTL_GET_HOMEAGENT, hainfo);
	if (ret < 0) {
		perror("ioctl:");
		return ret;
	}

	ret = ioctl(fd, IOCTL_GET_HOMEADDR, hinfo);
	if (ret < 0) {
		perror("ioctl:");
		return ret;
	}

	ret = ioctl(fd, IOCTL_GET_CAREOFADDR, careof);
	if (ret < 0) {
		perror("ioctl:");
		return ret;
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&hainfo->addr)) {
		printf("Home Agent has NOT been set.\n");
	} else {
		inet_ntop(AF_INET6, &hainfo->addr, address, INET6_ADDRSTRLEN);
		printf("Home Agent      : %s/%d\n", address, hainfo->prefixlen);
	}

	if (IN6_IS_ADDR_UNSPECIFIED(&hinfo->ifr6_addr)) {
		printf("Home Address has NOT been set.\n");
	} else {
		inet_ntop(AF_INET6, &hinfo->ifr6_addr, address, INET6_ADDRSTRLEN);
		printf("Home Address    : %s/%d\n", address, hinfo->ifr6_prefixlen);
	}

	if (!IN6_IS_ADDR_UNSPECIFIED(careof)) {
		inet_ntop(AF_INET6, careof, address, INET6_ADDRSTRLEN);
		printf("Care-of Address : %s\n", address);
	}

	close(fd);
	return 0;
}

int check_uid(int uid)
{
	if (geteuid() != uid) return 0;
	else return 1;
}

int format_addr(char *addr, char *faddr)
{
	int taddr[8];
	int i, mz = 0, tz = 0, zl = -1;
	char *p;

	sscanf(addr, "%04x%04x%04x%04x%04x%04x%04x%04x",
	       &taddr[0], &taddr[1], &taddr[2], &taddr[3],
	       &taddr[4], &taddr[5], &taddr[6], &taddr[7]);

	for (i = 0; i < 8; i++) {
		if (taddr[i] == 0) tz++;
		else {
			if ((tz > 0) && (tz > mz)) {
				zl = i - tz;
				mz = tz;
			}
			tz = 0;
		}
	}
	p = faddr;
	for (i = 0; i < 8; i++) {
		if (i == zl) {
			i += mz - 1;
			p += sprintf(p, ":");
			if ((zl + mz) == 8)
				p += sprintf(p, ":");
			continue;
		}
		if (i != 0)
			p += sprintf(p, ":");
		p += sprintf(p, "%x", taddr[i]);
	}
	p = NULL;
	return strlen(faddr);
}

/*
 * dst: pointer to destination buffer
 * src: pointer to base64 string
 * size: size (in BYTES) of destination buffer
 */
int base64_to_bin(uint8_t *dst,char *src,int size)
{
	int i,j;
	char *p;
	int offset,count;
	uint32_t digitval;
	uint8_t buf[4];
	
	offset = 18;
	*(uint32_t *)buf = 0;
	count = size;
	memset(dst,0,size);
  
	for (i = 0; src[i] != '\0'&& src[i] != '\n'; i++) {
		p = strchr(base64_digits,src[i]);
		if (p == NULL)
			return (-EINVDIGIT);
		digitval = p - base64_digits;

		*(uint32_t *)buf |= htonl(digitval << offset);

		if (offset == 0) {
			for (j = 0; (j < 3) && (count > 0); j++,count--)
				dst[j] = buf[j + 1];

			offset = 18;
			dst += 3;
			*(uint32_t *)buf = 0;
		}
		else
			offset -= 6;
	}

	if (offset != 18) 
		for (j = 0; (j < 3) && (count > 0); j++,count--)
			dst[j] = buf[j + 1];

	if (i < ((8*size + 5) / 6)) 
		return (-ESRCTOOSHORT);
	else if (i > ((8*size + 5) / 6)) 
		return (-ESRCTOOLONG);
	
	return (0);
}
