Skip to content

Subnet / CIDR Calculator — C source

Compute IPv4 subnet details from a CIDR block - network, broadcast, host range, netmask, wildcard, host count, IP class, and private-range detection. Split a block into smaller subnets, all in your browser.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/* subnet-cidr — IPv4 subnet/CIDR math: dotted-quad ⇄ uint32, full subnet details (network, broadcast, hosts, class, private), CIDR splitting. Language: C (C11, stdlib only). Port of src/lib/subnet-cidr.ts — same logic as this dir's python.py / go.go; invalid input reports failure instead of throwing. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct { char network[16], broadcast[16], first_host[16], last_host[16]; uint64_t hosts; int prefix; char ip_class; bool is_private; } Details;

static bool all_digits(const char *s, size_t n) /* the TS /^\d+$/ test */
{
    for (size_t i = 0; i < n; i++) if (s[i] < '0' || s[i] > '9') return false;
    return n > 0;
}
static bool ip_to_int(const char *ip, uint32_t *out) /* "a.b.c.d" → uint32 */
{
    uint32_t result = 0; int octets = 0; const char *p = ip;
    while (true) { /* split on '.', consuming exactly four digit groups */
        const char *dot = strchr(p, '.');
        size_t len = dot ? (size_t)(dot - p) : strlen(p);
        if (!all_digits(p, len)) return false;
        long v = 0; /* decimal on purpose — no octal autodetect */
        for (size_t i = 0; i < len; i++) v = v * 10 + (p[i] - '0');
        if (v > 255) return false;
        result = result * 256 + (uint32_t)v;
        octets++;
        if (!dot) break;
        p = dot + 1;
    }
    return (*out = octets == 4 ? result : 0), octets == 4;
}
static void int_to_ip(uint32_t v, char buf[16]) { sprintf(buf, "%u.%u.%u.%u", (v >> 24) & 255, (v >> 16) & 255, (v >> 8) & 255, v & 255); }
static uint32_t mask_from_prefix(int p) { return p == 0 ? 0 : 0xFFFFFFFFu << (32 - p); }
static bool subnet_details(const char *cidr, Details *d) /* "ip/prefix" → full block info */
{
    const char *slash = strchr(cidr, '/');
    if (!slash || strchr(slash + 1, '/')) return false; /* exactly one slash */
    if (!all_digits(slash + 1, strlen(slash + 1))) return false;
    long prefix = 0;
    for (const char *q = slash + 1; *q; q++) prefix = prefix * 10 + (*q - '0');
    size_t ip_len = (size_t)(slash - cidr);
    char ip[32];
    if (prefix > 32 || ip_len >= sizeof ip) return false;
    memcpy(ip, cidr, ip_len); ip[ip_len] = '\0';
    uint32_t ip_int;
    if (!ip_to_int(ip, &ip_int)) return false;
    uint32_t mask = mask_from_prefix((int)prefix), wildcard = ~mask;
    uint32_t network = ip_int & mask, broadcast = network | wildcard;
    uint32_t first = prefix >= 31 ? network : network + 1;   /* /31,/32: no host split */
    uint32_t last = prefix >= 31 ? broadcast : broadcast - 1;
    uint64_t hosts = prefix >= 32 ? 1 : prefix == 31 ? 2 : ((uint64_t)1 << (32 - prefix)) - 2;
    int o1 = (network >> 24) & 255, o2 = (network >> 16) & 255;
    d->ip_class = o1 < 128 ? 'A' : o1 < 192 ? 'B' : o1 < 224 ? 'C' : '-'; /* D/E → '-' */
    d->is_private = o1 == 10 || (o1 == 172 && o2 >= 16 && o2 <= 31) || (o1 == 192 && o2 == 168);
    int_to_ip(network, d->network); int_to_ip(broadcast, d->broadcast);
    int_to_ip(first, d->first_host); int_to_ip(last, d->last_host);
    d->hosts = hosts; d->prefix = (int)prefix;
    return true;
}
static int split_subnet(const char *cidr, int new_prefix, char out[][20], int max) /* 2^(np-p) children */
{
    Details d;
    if (!subnet_details(cidr, &d) || new_prefix < d.prefix || new_prefix > 32) return 0;
    uint32_t network, block = 1u << (32 - new_prefix);
    int count = 1 << (new_prefix - d.prefix);
    ip_to_int(d.network, &network);
    for (int i = 0; i < count && i < max; i++) {
        char ip[16]; int_to_ip(network + (uint32_t)i * block, ip);
        snprintf(out[i], 20, "%s/%d", ip, new_prefix);
    }
    return count;
}
int main(void)
{
    Details d;
    if (subnet_details("192.168.1.100/24", &d))
        printf("%s/24  net=%s bcast=%s hosts=%llu class=%c private=%s\n", d.network, d.network, d.broadcast, (unsigned long long)d.hosts, d.ip_class, d.is_private ? "yes" : "no");
    char kids[8][20];
    int n = split_subnet("192.168.1.0/24", 26, kids, 8);
    for (int i = 0; i < n; i++) printf("  split /26 → %s\n", kids[i]);
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →