#include <stdio.h>	/* printf, etc */
#include <stdlib.h>	/* atoi, etc */
#include <string.h>	/* strlcpy, etc */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * arithmetic macros for array (subset)
 *
 * References
 * [1] Taiji Yamada, ``arr_ar.h - arithmetic macros for array,''
 * http://www.aihara.co.jp/~taiji/abmail/
 */
#define arr_init(n, a) do {\
  memset(&(a)[0], 0, sizeof((a)[0])*n);	\
} while (0)

#define arr_not(n, a, b) do {\
  int i;\
\
  for (i=0; i<n; i++)\
    (b)[i] = ~(a)[i];\
} while (0)

#define arr_and(n, a, b, c) do {\
  int i;\
\
  for (i=0; i<n; i++)\
    (c)[i] = (a)[i] & (b)[i];\
} while (0)

#define arr_or(n, a, b, c) do {\
  int i;\
\
  for (i=0; i<n; i++)\
    (c)[i] = (a)[i] | (b)[i];\
} while (0)

#define arr_lsl(n, a, s, b) do {\
  int i;\
\
  for (i=0; i<n; i++) {\
    int k, l, m;\
\
    k = i + s /(8*sizeof((a)[0]));\
    l = k + 1;\
    m = s % (8*sizeof((a)[0]));\
    (b)[i] =\
      ((0<=k && k<n) ? (a)[k]<<m : 0) |\
      ((0<=l && l<n) ? (a)[l]>>(8*sizeof((a)[0])-m) : 0);\
  }\
} while (0)

#define arr_tr(n, a, tr, b) do {\
  int i;\
\
  for (i=0; i<n; i++)\
    (b)[i] = tr((a)[i]);\
} while (0)

/*
 * arithmetic functions for in6_addr (subset)
 *
 * References
 * [1] Taiji Yamada, ``abcidr.c - utilities for CIDR in abmail,''
 * http://www.aihara.co.jp/~taiji/abmail/
 */
struct in6_addr in6_addr_zero(void)
{
  struct in6_addr a;

  arr_init(16, a.s6_addr);
  return a;
}
struct in6_addr in6_addr_not(struct in6_addr a)
{
  struct in6_addr b;

  arr_not(16, a.s6_addr, b.s6_addr);
  return b;
}
struct in6_addr in6_addr_or(struct in6_addr a, struct in6_addr b)
{
  struct in6_addr c;

  arr_or(16, a.s6_addr, b.s6_addr, c.s6_addr);
  return c;
}
struct in6_addr in6_addr_and(struct in6_addr a, struct in6_addr b)
{
  struct in6_addr c;

  arr_and(16, a.s6_addr, b.s6_addr, c.s6_addr);
  return c;
}
struct in6_addr in6_addr_lsl(struct in6_addr a, unsigned int s)
{
  struct in6_addr b;

  arr_tr(4, (uint32_t *)a.s6_addr, ntohl, (uint32_t *)a.s6_addr);
  arr_lsl(4, (uint32_t *)a.s6_addr, s, (uint32_t *)b.s6_addr);
  arr_tr(4, (uint32_t *)b.s6_addr, htonl, (uint32_t *)b.s6_addr);
  return b;
}
int in6_addr_cmp(struct in6_addr a, struct in6_addr b)
{
#if 1
  return memcmp(&a.s6_addr[0], &b.s6_addr[0], sizeof(struct in6_addr));
#else
  int i, cmp;

  arr_tr(4, (uint32_t *)a.s6_addr, ntohl, (uint32_t *)a.s6_addr);
  arr_tr(4, (uint32_t *)b.s6_addr, ntohl, (uint32_t *)b.s6_addr);
  arr_is(4, (cmp=((uint32_t *)a.s6_addr)[i]>((uint32_t *)b.s6_addr)[i]?1:
	     ((uint32_t *)a.s6_addr)[i]<((uint32_t *)b.s6_addr)[i]?-1:0) != 0);
  return cmp;
#endif
}
#define in6_addr_eq(a, b) (in6_addr_cmp(a, b) == 0)
#define in6_addr_le(a, b) (in6_addr_cmp(a, b) <= 0)

int main(int argc, char *argv[])
{
  int a;
  char net_addrs[64] = "::ffff:210.154.62.64", addrs[64] = "::ffff:210.154.62.67";
  int net_mask_len = 32*4-(32-29)/* = 125 */;
  struct in6_addr net_addr, net_mask, end_addr, addr;
  char b[64], *p;

  for (a=1; a<argc; a++)
    if (strcmp(argv[a], "-h") == 0) {
      fprintf(stderr, "usage:\n"
	      "\t%s [-n net_address/mask_len] [-a address]\n", argv[0]);
      return 1;
    }
    else if (strcmp(argv[a], "-n") == 0 && a+1 < argc) {
      strlcpy(net_addrs, argv[++a], sizeof(net_addrs));
      if ((p = strchr(net_addrs, '/'))) *p = '\0';
      if ((p = strchr(argv[a], '/'))) {
	net_mask_len = atoi(p+1);
      }
    }
    else if (strcmp(argv[a], "-a") == 0 && a+1 < argc) {
      strlcpy(addrs, argv[++a], sizeof(addrs));
    }

  /* マスク長lからネットマスク(network byte order)を作成 */
  net_mask = in6_addr_lsl(in6_addr_not(in6_addr_zero()), (32*4 - net_mask_len));
  printf("net_mask_len:\t%d\n", net_mask_len);
  printf("net_mask:\t%s\n", inet_ntop(AF_INET6, &net_mask, b, sizeof(b)));

#if 0
  /* マスク長lのアドレス空間のサイズ */
  printf("space size:\t%llu\n", 1ULL<<(32 - net_mask_len));
#endif

  /* 指定されたネットワークアドレス文字列をバイナリ(network byte order)へ変換 */
  if (inet_pton(AF_INET6, net_addrs, &net_addr) != 1)
    perror("inet_pton(..,net_addrs,..)");
  printf("net_addr:\t%s\n", inet_ntop(AF_INET6, &net_addr, b, sizeof(b)));

  /* ネットマスクからネットワークアドレスを補正 */
  net_addr = in6_addr_and(net_addr, net_mask);
  printf("net_addr:\t%s\n", inet_ntop(AF_INET6, &net_addr, b, sizeof(b)));

  /* ネットマスクとネットワークアドレスからネットワーク終端アドレスを生成 */
  end_addr = in6_addr_or(net_addr, in6_addr_not(net_mask));
  printf("end_addr:\t%s\n", inet_ntop(AF_INET6, &end_addr, b, sizeof(b)));

#if 0
  /* アドレス空間のサイズ=ネットワーク終端アドレス-ネットワークアドレス+1 */
  printf("range size:\t%llu\n",
	 (unsigned long long)ntohl(end_addr.s_addr)-ntohl(net_addr.s_addr)+1);
#endif

  /* 指定されたアドレス文字列をバイナリ(network byte order)へ変換 */
  if (inet_pton(AF_INET6, addrs, &addr) != 1)
    perror("inet_pton(..,addrs,..)");
  printf("addr:\t\t%s\n", inet_ntop(AF_INET6, &addr, b, sizeof(b)));

  /* 指定されたアドレスがネットワーク空間に属するか、の判定 */
  printf("addr in net_addr:\t%s\n",
	 (/*in6_addr_le(net_addr, addr) &&
	    in6_addr_le(addr, end_addr)*/
	  in6_addr_eq(in6_addr_and(addr, net_mask),
		      in6_addr_and(net_addr, net_mask))
	  ) ? "yes" : "no");

  return 0;
}
