IPVarch/ipvarch.c

809 lines
23 KiB
C

/*
* ipvarch.c - IPVarch: Internet Protocol Version Architect
*
* A small ANSI C (C89) command line tool for inspecting and planning
* IP addresses and IPv4 networks.
*
* Modes:
* ipvarch <address> inspect an IPv4 or IPv6 address
* ipvarch --plan-network <ipv4>/<prefix> print a network plan table
* ipvarch --random-ipv4network <scope> <prefix>
* generate a random valid plan
*
* Targets tcc (Tiny C Compiler); also builds with gcc -std=c89 -pedantic.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
typedef unsigned char byte;
/* UTF-8 encoded box drawing characters (U+2500 block). */
#define BOX_H "\xe2\x94\x80" /* horizontal line */
#define BOX_V "\xe2\x94\x82" /* vertical line */
#define BOX_TL "\xe2\x94\x8c" /* top left ┌ */
#define BOX_TR "\xe2\x94\x90" /* top right ┐ */
#define BOX_BL "\xe2\x94\x94" /* bottom left └ */
#define BOX_BR "\xe2\x94\x98" /* bottom right ┘ */
#define BOX_TJ "\xe2\x94\xac" /* top join ┬ */
#define BOX_BJ "\xe2\x94\xb4" /* bottom join ┴ */
#define BOX_LJ "\xe2\x94\x9c" /* left join ├ */
#define BOX_RJ "\xe2\x94\xa4" /* right join ┤ */
#define BOX_CJ "\xe2\x94\xbc" /* centre join ┼ */
typedef const char *Row[2];
/* ------------------------------------------------------------------ */
/* Helpers */
/* ------------------------------------------------------------------ */
static void die(const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "ipvarch: error: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
exit(1);
}
static void usage(void)
{
printf("IPVarch - Internet Protocol Version Architect\n\n");
printf("Usage:\n");
printf(" ipvarch <address> inspect an IPv4/IPv6 address\n");
printf(" ipvarch --plan-network <ipv4>/<prefix> plan a network (/8 /16 /24 /32)\n");
printf(" ipvarch --random-ipv4network <scope> <prefix>\n");
printf(" random valid network plan\n\n");
printf("Examples:\n");
printf(" ipvarch 192.168.1.1\n");
printf(" ipvarch 2001:db8::1\n");
printf(" ipvarch --plan-network 172.16.0.0/16\n");
printf(" ipvarch --random-ipv4network private /24\n");
return;
}
/* Display width of a UTF-8 string (continuation bytes occupy no column). */
static int dwidth(const char *s)
{
int w = 0;
while (*s != '\0') {
if ((((unsigned char)*s) & 0xC0) != 0x80)
w++;
s++;
}
return w;
}
static void dashes(int n)
{
while (n-- > 0)
fputs(BOX_H, stdout);
return;
}
static void padcell(const char *s, int w)
{
int d = dwidth(s) + 1;
putchar(' ');
fputs(s, stdout);
while (d < w) {
putchar(' ');
d++;
}
return;
}
static void centercell(const char *s, int w)
{
int d = dwidth(s) + 1;
int l = (w - d) / 2;
int r = w - d - l;
while (l-- > 0)
putchar(' ');
putchar(' ');
fputs(s, stdout);
while (r-- > 0)
putchar(' ');
return;
}
static void print_table(const char *title, const char *rows[][2], int nrows)
{
int w1 = 0, w2 = 0, tw, i;
for (i = 0; i < nrows; i++) {
int a = dwidth(rows[i][0]) + 2; /* room for cell padding */
int b = dwidth(rows[i][1]) + 2;
if (a > w1) w1 = a;
if (b > w2) w2 = b;
}
tw = dwidth(title) + 2;
if (tw > w1 + w2 - 1)
w2 = tw - w1 + 1;
fputs(BOX_TL, stdout); dashes(w1); fputs(BOX_TJ, stdout);
dashes(w2); fputs(BOX_TR "\n", stdout);
fputs(BOX_V, stdout); centercell(title, w1 + w2 + 1); fputs(BOX_V "\n", stdout);
fputs(BOX_LJ, stdout); dashes(w1); fputs(BOX_CJ, stdout);
dashes(w2); fputs(BOX_RJ "\n", stdout);
for (i = 0; i < nrows; i++) {
fputs(BOX_V, stdout); padcell(rows[i][0], w1);
fputs(BOX_V, stdout); padcell(rows[i][1], w2);
fputs(BOX_V "\n", stdout);
}
fputs(BOX_BL, stdout); dashes(w1); fputs(BOX_BJ, stdout);
dashes(w2); fputs(BOX_BR "\n", stdout);
return;
}
/* ------------------------------------------------------------------ */
/* Parsing */
/* ------------------------------------------------------------------ */
static int parse_ipv4(const char *s, byte out[4])
{
const char *p = s;
int i;
for (i = 0; i < 4; i++) {
int v = 0, digits = 0;
if (i > 0) {
if (*p != '.')
return 0;
p++;
}
while (*p >= '0' && *p <= '9') {
v = v * 10 + (*p - '0');
digits++;
p++;
if (digits > 3 || v > 255)
return 0;
}
if (digits == 0)
return 0;
out[i] = (byte)v;
}
if (*p != '\0')
return 0;
return 1;
}
static int hexval(int c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
/* Parse a colon separated piece list such as "ffff:0:192.168.1.1".
* The final token may be an embedded IPv4 address (two groups).
* Returns count via *cnt and 1 on success. */
static int parse_piece(const char *piece, unsigned short *gr, int *cnt)
{
char buf[48];
byte v4[4];
const char *p;
int n = 0;
if (strlen(piece) >= sizeof(buf))
return 0;
strcpy(buf, piece);
p = buf;
while (*p != '\0') {
const char *tok = p;
int hasdot = 0;
while (*p != '\0' && *p != ':') {
if (*p == '.')
hasdot = 1;
p++;
}
if (hasdot) {
if (*p != '\0') /* embedded IPv4 must be last */
return 0;
if (!parse_ipv4(tok, v4))
return 0;
if (n > 6)
return 0;
gr[n++] = (unsigned short)((v4[0] << 8) | v4[1]);
gr[n++] = (unsigned short)((v4[2] << 8) | v4[3]);
} else {
int len = (int)(p - tok);
int j, val = 0;
if (len < 1 || len > 4)
return 0;
for (j = 0; j < len; j++) {
int hv = hexval((unsigned char)tok[j]);
if (hv < 0)
return 0;
val = (val << 4) | hv;
}
if (n >= 8)
return 0;
gr[n++] = (unsigned short)val;
}
if (*p == '\0')
break;
p++; /* skip ':' */
if (*p == '\0') /* trailing colon */
return 0;
}
*cnt = n;
return 1;
}
/* Parse an IPv6 address (RFC 4291 text form, compressed "::" allowed,
* embedded IPv4 in the tail allowed) into 16 bytes. */
static int parse_ipv6(const char *s, byte out[16])
{
char buf[80];
char *dd, *head, *tail;
unsigned short gr[16];
int nh = 0, nt = 0, total, i;
if (strlen(s) >= sizeof(buf))
return 0;
strcpy(buf, s);
dd = strstr(buf, "::");
if (dd != NULL) {
if (strstr(dd + 1, "::") != NULL)
return 0;
*dd = '\0';
head = buf;
tail = dd + 2;
/* An embedded IPv4 is only valid in the tail piece. */
if (strchr(head, '.') != NULL && strchr(head, ':') == NULL)
return 0;
} else {
head = buf;
tail = NULL;
}
if (!parse_piece(head, gr, &nh))
return 0;
if (tail != NULL) {
if (!parse_piece(tail, gr + nh, &nt))
return 0;
}
total = nh + nt;
if (dd != NULL) {
if (total > 7) /* "::" must stand for zero groups */
return 0;
memset(out, 0, 16);
for (i = 0; i < nh; i++) {
out[2 * i] = (byte)(gr[i] >> 8);
out[2 * i + 1] = (byte)(gr[i] & 0xFF);
}
for (i = 0; i < nt; i++) {
int k = 8 - nt + i;
out[2 * k] = (byte)(gr[nh + i] >> 8);
out[2 * k + 1] = (byte)(gr[nh + i] & 0xFF);
}
} else {
if (total != 8)
return 0;
for (i = 0; i < 8; i++) {
out[2 * i] = (byte)(gr[i] >> 8);
out[2 * i + 1] = (byte)(gr[i] & 0xFF);
}
}
return 1;
}
/* ------------------------------------------------------------------ */
/* Formatting */
/* ------------------------------------------------------------------ */
static void fmt_v4(char *dst, const byte a[4])
{
sprintf(dst, "%u.%u.%u.%u",
(unsigned)a[0], (unsigned)a[1], (unsigned)a[2], (unsigned)a[3]);
return;
}
/* Compressed RFC 5952 style text form. */
static void fmt_v6(char *dst, const byte a[16])
{
unsigned short g[8];
int i, zs = -1, zl = 0, bs = -1, bl = 0, first = 1, suppress = 0;
char *p = dst;
for (i = 0; i < 8; i++)
g[i] = (unsigned short)((a[2 * i] << 8) | a[2 * i + 1]);
for (i = 0; i < 8; i++) {
if (g[i] == 0) {
if (zl == 0)
zs = i;
zl++;
if (zl > bl) {
bl = zl;
bs = zs;
}
} else {
zl = 0;
}
}
if (bl < 2) {
bs = 0;
bl = 0;
}
if (bl == 8) {
strcpy(dst, "::");
return;
}
i = 0;
while (i < 8) {
if (bl > 0 && i >= bs && i < bs + bl) {
*p++ = ':';
*p++ = ':';
suppress = 1; /* "::" replaces the join */
first = 0;
i = bs + bl;
continue;
}
if (!first && !suppress)
*p++ = ':';
suppress = 0;
p += sprintf(p, "%x", (unsigned)g[i]);
first = 0;
i++;
}
*p = '\0';
return;
}
/* ------------------------------------------------------------------ */
/* Classification (rules per ipaddrinfo.html) */
/* ------------------------------------------------------------------ */
static void classify_v4(const byte a[4], const char **cls, const char **scope)
{
if (a[0] == 0) {
*cls = "0 (reserved)";
*scope = "Reserved (this network)";
} else if (a[0] < 128) {
*cls = "A";
*scope = "Public";
} else if (a[0] < 192) {
*cls = "B";
*scope = "Public";
} else if (a[0] < 224) {
*cls = "C";
*scope = "Public";
} else if (a[0] < 240) {
*cls = "D";
*scope = "Multicast";
} else {
*cls = "E";
*scope = "Reserved";
}
if (a[0] == 127)
*scope = "Loopback";
else if (a[0] == 10)
*scope = "Private (RFC 1918)";
else if (a[0] == 172 && a[1] >= 16 && a[1] <= 31)
*scope = "Private (RFC 1918)";
else if (a[0] == 192 && a[1] == 168)
*scope = "Private (RFC 1918)";
else if (a[0] == 169 && a[1] == 254)
*scope = "Link-local (APIPA)";
else if (a[0] == 192 && a[1] == 0 && a[2] == 2)
*scope = "Documentation (TEST-NET-1)";
else if (a[0] == 198 && a[1] == 51 && a[2] == 100)
*scope = "Documentation (TEST-NET-2)";
else if (a[0] == 203 && a[1] == 0 && a[2] == 113)
*scope = "Documentation (TEST-NET-3)";
else if (a[0] == 100 && a[1] >= 64 && a[1] <= 127)
*scope = "Shared (CGNAT)";
else if (a[0] == 198 && (a[1] == 18 || a[1] == 19))
*scope = "Benchmarking";
return;
}
static void classify_v6(const byte a[16], const char **scope)
{
int i;
for (i = 0; i < 16; i++) {
if (a[i] != 0)
break;
}
if (i == 16) {
*scope = "Unspecified (::)";
return;
}
if (a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 &&
a[5] == 0 && a[6] == 0 && a[7] == 0 && a[8] == 0 && a[9] == 0 &&
a[10] == 0 && a[11] == 0 && a[12] == 0 && a[13] == 0 &&
a[14] == 0 && a[15] == 1) {
*scope = "Loopback (::1)";
return;
}
if (a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 &&
a[5] == 0 && a[6] == 0 && a[7] == 0 && a[8] == 0 && a[9] == 0 &&
a[10] == 0xFF && a[11] == 0xFF) {
*scope = "IPv4-mapped (::ffff:0:0/96)";
return;
}
if (a[0] == 0xFF) {
*scope = "Multicast (ff00::/8)";
return;
}
if (a[0] == 0xFE && (a[1] & 0xC0) == 0x80) {
*scope = "Link-local (fe80::/10)";
return;
}
if ((a[0] & 0xFE) == 0xFC) {
*scope = "Private (unique local, fc00::/7)";
return;
}
if (a[0] == 0x20 && a[1] == 0x01 && a[2] == 0x0D && a[3] == 0xB8) {
*scope = "Documentation (2001:db8::/32)";
return;
}
*scope = "Public (global unicast)";
return;
}
/* ------------------------------------------------------------------ */
/* IPv4 network math */
/* ------------------------------------------------------------------ */
static unsigned long v4_to_u32(const byte a[4])
{
return ((unsigned long)a[0] << 24) | ((unsigned long)a[1] << 16) |
((unsigned long)a[2] << 8) | (unsigned long)a[3];
}
static void u32_to_v4(unsigned long u, byte out[4])
{
out[0] = (byte)((u >> 24) & 0xFF);
out[1] = (byte)((u >> 16) & 0xFF);
out[2] = (byte)((u >> 8) & 0xFF);
out[3] = (byte)(u & 0xFF);
return;
}
static void print_plan(unsigned long net, int prefix)
{
unsigned long mask, bc, cnt, firstu, lastu;
byte a[4], m4[4], b4[4], lo4[4], hi4[4];
char tmp1[24], tmp2[24];
char nb[24], mb[40], bb[24], rb[64], cb[24], gb[24], db[24], pb[64];
char sb[48], tb[80];
const char *cls, *scope;
Row rows[10];
int nr = 0;
mask = (prefix == 8) ? 0xFF000000ul :
(prefix == 16) ? 0xFFFF0000ul :
(prefix == 24) ? 0xFFFFFF00ul : 0xFFFFFFFFul;
bc = net | (~mask & 0xFFFFFFFFul);
u32_to_v4(net, a);
u32_to_v4(mask, m4);
classify_v4(a, &cls, &scope);
fmt_v4(nb, a);
fmt_v4(tmp1, m4);
sprintf(mb, "%s /%d", tmp1, prefix);
if (prefix == 32) {
strcpy(bb, "n/a (single host)");
strcpy(rb, nb); /* the host address itself */
strcpy(cb, "1");
strcpy(gb, "n/a (single host)");
strcpy(db, "n/a (single host)");
strcpy(pb, "disabled (single-host network)");
} else {
firstu = net + 1;
lastu = bc - 1;
cnt = (1ul << (32 - prefix)) - 2;
u32_to_v4(bc, b4);
fmt_v4(bb, b4);
u32_to_v4(firstu, lo4);
fmt_v4(tmp1, lo4);
u32_to_v4(lastu, hi4);
fmt_v4(tmp2, hi4);
sprintf(rb, "%s - %s", tmp1, tmp2);
sprintf(cb, "%lu", cnt);
fmt_v4(gb, lo4); /* gateway: first usable */
u32_to_v4(firstu + 1, hi4);
fmt_v4(db, hi4); /* DNS/DHCP: next usable */
hi4[0] = a[0]; hi4[1] = a[1]; hi4[2] = a[2]; hi4[3] = 254;
lo4[3] = 100;
fmt_v4(tmp1, lo4);
fmt_v4(tmp2, hi4);
sprintf(pb, "%s - %s", tmp1, tmp2);
}
sprintf(sb, "%s", scope);
sprintf(tb, "IPVarch Network Plan - %s /%d", nb, prefix);
rows[nr][0] = "Network address"; rows[nr][1] = nb; nr++;
rows[nr][0] = "Netmask"; rows[nr][1] = mb; nr++;
rows[nr][0] = "Broadcast"; rows[nr][1] = bb; nr++;
rows[nr][0] = "Usable host range"; rows[nr][1] = rb; nr++;
rows[nr][0] = "Usable hosts"; rows[nr][1] = cb; nr++;
rows[nr][0] = "Class"; rows[nr][1] = cls; nr++;
rows[nr][0] = "Scope"; rows[nr][1] = sb; nr++;
rows[nr][0] = "Gateway IP"; rows[nr][1] = gb; nr++;
rows[nr][0] = "DNS/DHCP server"; rows[nr][1] = db; nr++;
rows[nr][0] = "DHCP pool"; rows[nr][1] = pb; nr++;
print_table(tb, rows, nr);
return;
}
/* ------------------------------------------------------------------ */
/* Random network generation */
/* ------------------------------------------------------------------ */
static int is_ok_public(const byte a[4])
{
if (a[0] == 0 || a[0] == 10 || a[0] == 127)
return 0;
if (a[0] == 172 && a[1] >= 16 && a[1] <= 31)
return 0;
if (a[0] == 192 && a[1] == 168)
return 0;
if (a[0] == 169 && a[1] == 254)
return 0;
if (a[0] == 100 && a[1] >= 64 && a[1] <= 127)
return 0;
if (a[0] == 198 && (a[1] == 18 || a[1] == 19))
return 0;
if (a[0] == 192 && a[1] == 0 && a[2] == 2)
return 0;
if (a[0] == 198 && a[1] == 51 && a[2] == 100)
return 0;
if (a[0] == 203 && a[1] == 0 && a[2] == 113)
return 0;
if (a[0] >= 224)
return 0;
return 1;
}
static void random_network(int prefix, int wantprivate, byte out[4])
{
int tries, r;
byte a[4];
for (tries = 0; tries < 10000; tries++) {
r = rand();
if (wantprivate) {
if (prefix == 8) {
/* Only one valid private /8 exists: 10.0.0.0/8 */
a[0] = 10; a[1] = 0; a[2] = 0; a[3] = 0;
} else if (prefix == 16) {
if (r % 2) {
a[0] = 192; a[1] = 168; a[2] = rand() % 256;
} else {
a[0] = 172; a[1] = 16 + rand() % 16; a[2] = rand() % 256;
}
a[3] = 0;
} else {
r = rand() % 8;
if (r < 4) {
a[0] = 10; a[1] = rand() % 256; a[2] = rand() % 256;
} else if (r < 6) {
a[0] = 172; a[1] = 16 + rand() % 16; a[2] = rand() % 256;
} else {
a[0] = 192; a[1] = 168; a[2] = rand() % 256;
}
if (prefix == 24)
a[3] = 0;
else
a[3] = 1 + rand() % 254;
}
} else {
a[0] = (rand() % 223) + 1; /* avoids class D/E and 0 */
if (a[0] == 10 || a[0] == 127)
continue;
a[1] = rand() % 256;
a[2] = rand() % 256;
a[3] = (prefix == 32) ? (1 + rand() % 254) : 0;
}
/* Zero the host bits for the chosen prefix. */
if (prefix == 8) { a[1] = 0; a[2] = 0; a[3] = 0; }
if (prefix == 16) { a[2] = 0; a[3] = 0; }
if (prefix == 24) { a[3] = 0; }
if (!wantprivate && !is_ok_public(a))
continue;
out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
return;
}
die("could not generate a suitable random network; please retry");
return;
}
/* ------------------------------------------------------------------ */
/* Modes */
/* ------------------------------------------------------------------ */
static void mode_info(const char *arg)
{
byte a[16];
const char *cls = "", *scope = "";
char nb[64], gb[96], ob[96];
int i;
unsigned short g[8];
char *q;
if (strchr(arg, '/') != NULL)
die("info mode takes a plain address; for subnets use --plan-network");
if (strchr(arg, ':') != NULL) {
if (!parse_ipv6(arg, a))
die("invalid IPv6 address: %s", arg);
fmt_v6(nb, a);
classify_v6(a, &scope);
for (i = 0; i < 8; i++)
g[i] = (unsigned short)((a[2 * i] << 8) | a[2 * i + 1]);
printf("Address: %s\n", nb);
printf("Type: IPv6\n");
printf("Class: n/a (IPv6 has no address classes)\n");
printf("Scope: %s\n", scope);
q = gb;
for (i = 0; i < 8; i++)
q += sprintf(q, "%s%04x", i > 0 ? " " : "", (unsigned)g[i]);
printf("Groups: %s\n", gb);
q = ob;
for (i = 0; i < 16; i++)
q += sprintf(q, "%s%u", i > 0 ? ", " : "", (unsigned)a[i]);
printf("Octets: %s\n", ob);
} else {
byte a4[4];
if (!parse_ipv4(arg, a4))
die("invalid IPv4 or IPv6 address: %s", arg);
classify_v4(a4, &cls, &scope);
q = ob;
for (i = 0; i < 4; i++)
q += sprintf(q, "%s%u", i > 0 ? ", " : "", (unsigned)a4[i]);
printf("Address: %u.%u.%u.%u\n",
(unsigned)a4[0], (unsigned)a4[1], (unsigned)a4[2], (unsigned)a4[3]);
printf("Type: IPv4\n");
printf("Class: %s\n", cls);
printf("Scope: %s\n", scope);
printf("Octets: %s\n", ob);
}
return;
}
static void mode_plan(const char *arg)
{
char buf[80];
char *slash;
byte a[4];
unsigned long u, mask, net;
int prefix;
if (strlen(arg) >= sizeof(buf) - 1)
die("argument too long: %s", arg);
strcpy(buf, arg);
slash = strchr(buf, '/');
if (slash == NULL)
die("expected a subnet with a mask suffix (e.g. 192.168.1.0/24)");
*slash = '\0';
if (!parse_ipv4(buf, a))
die("invalid IPv4 address in subnet: %s", buf);
prefix = atoi(slash + 1);
if (prefix != 8 && prefix != 16 && prefix != 24 && prefix != 32)
die("unsupported prefix '%s' (supported: /8 /16 /24 /32)", slash + 1);
mask = (prefix == 8) ? 0xFF000000ul :
(prefix == 16) ? 0xFFFF0000ul :
(prefix == 24) ? 0xFFFFFF00ul : 0xFFFFFFFFul;
u = v4_to_u32(a);
net = u & mask;
if (net != u) {
byte c[4];
u32_to_v4(net, c);
die("host bits are set; the network address for that mask is %u.%u.%u.%u/%d",
(unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3], prefix);
}
if (a[0] == 0)
die("0.0.0.0 networks are reserved and cannot be planned");
if (a[0] >= 224)
die("multicast and reserved networks (class D/E) cannot be planned");
print_plan(net, prefix);
return;
}
static void mode_random(const char *scope_s, const char *prefix_s)
{
char lc[16];
const char *p;
byte net[4];
int prefix, priv;
char *q;
if (strlen(scope_s) >= sizeof(lc))
die("invalid scope: %s", scope_s);
q = lc;
for (p = scope_s; *p != '\0'; p++)
*q++ = (char)((*p >= 'A' && *p <= 'Z') ? (*p - 'A' + 'a') : *p);
*q = '\0';
if (strcmp(lc, "private") == 0)
priv = 1;
else if (strcmp(lc, "public") == 0)
priv = 0;
else
die("invalid scope '%s' (expected private or public)", scope_s);
if (prefix_s[0] == '/')
prefix_s++;
prefix = atoi(prefix_s);
if (prefix != 8 && prefix != 16 && prefix != 24 && prefix != 32)
die("unsupported prefix '/%s' (supported: /8 /16 /24 /32)", prefix_s);
srand((unsigned)time(NULL));
random_network(prefix, priv, net);
printf("Random network plan (--random-ipv4network %s /%d)\n\n",
priv ? "private" : "public", prefix);
print_plan(v4_to_u32(net), prefix);
return;
}
int main(int argc, char **argv)
{
if (argc >= 2 &&
(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 ||
strcmp(argv[1], "help") == 0)) {
usage();
return 0;
}
if (argc >= 2 && strcmp(argv[1], "--plan-network") == 0) {
if (argc != 3)
die("--plan-network takes exactly one argument: <ipv4>/<prefix>");
mode_plan(argv[2]);
return 0;
}
if (argc >= 2 && strcmp(argv[1], "--random-ipv4network") == 0) {
if (argc != 4)
die("--random-ipv4network takes two arguments: <private|public> </8|/16|/24|/32>");
mode_random(argv[2], argv[3]);
return 0;
}
if (argc == 2) {
mode_info(argv[1]);
return 0;
}
usage();
return 1;
}