/* manage the IPv6 anycast list */

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>

#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in6_var.h>
#include <arpa/inet.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct in6_ifreq ifr;
size_t needed;
char *buf, *lim;
int mib[4] = { CTL_NET, AF_INET6, IPPROTO_IP, IP6CTL_ANYCASTS };
struct in6_anycast *ap;

void dump() {
	if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) {
		perror("sysctl");
		exit(1);
	}
	if (needed == 0) {
		printf("empty IPv6 anycast list\n");
		return;
	}
	if ((buf = malloc(needed)) == NULL) {
		fprintf(stderr, "malloc failed\n");
		exit(1);
	}
	if (sysctl(mib, 4, buf, &needed, NULL, 0) < 0) {
		perror("sysctl2");
		exit(1);
	}
	lim = buf + needed;

	while (buf < lim) {
		char v[64];

		ap = (struct in6_anycast *)buf;
		printf("%s cnt=%u flags=%u\n",
		       inet_ntop(AF_INET6, &ap->ina6_addr, v, sizeof(v)),
		       ap->ina6_refcount, ap->ina6_flags);
		buf += sizeof(*ap);
	}
	if (buf != lim) {
		fprintf(stderr, "buffer mismatch\n");
		exit(1);
	}
}

void dealwith(name)
	char *name;
{
	int s;
	u_long req;

	switch (*name++) {
	    case '+':
		req = SIOCADDANY6;
		break;
	    case '-':
		req = SIOCDELANY6;
		break;
	    default:
		fprintf(stderr, "bad argument %s\n", --name);
		return;
	}

	bzero(&ifr, sizeof(ifr));
	strncpy(ifr.ifr_name, "lo0", sizeof(ifr.ifr_name));
	ifr.ifr_Addr.sin6_family = AF_INET6;
	ifr.ifr_Addr.sin6_len = sizeof(ifr.ifr_Addr);
	if (req == SIOCADDANY6)
		ifr.ifr_Addr.sin6_flowinfo = IP6ANY_ROUTER;
	inet_pton(AF_INET6, name, &ifr.ifr_Addr.sin6_addr);

	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}
	if (ioctl(s, req, (caddr_t)&ifr) < 0)
		perror("ioctl");
	(void) close(s);
}

main(argc, argv)
	int argc;
	char *argv[];
{
	argc--, argv++;
	if (argc == 0)
		dump();
	else while (argc) {
		dealwith(argv[0]);
		argc--, argv++;
	}
	exit(0);
}
