Networking in C · beginner · ~15 min
Bind, connect, and parse IPv6 alongside IPv4 cleanly.
IPv6 has 128-bit addresses and a slightly different sockaddr. The Berkeley API is otherwise identical. inet_pton(AF_INET6, ...) parses both forms.
Modern internet is dual-stack. Any server that listens on IPv4-only is missing half its addressable clients. Any parser that breaks on IPv6 is a CVE waiting.
Address types. Loopback ::1; link-local fe80::/10; documentation 2001:db8::/32; ULA fc00::/7; multicast ff00::/8.
Scope IDs. fe80::1%eth0 — the %eth0 is the scope (interface name or index) needed for link-local addresses.
Dual-stack. A single AF_INET6 socket can accept v4-mapped (::ffff:1.2.3.4) connections unless you set IPV6_V6ONLY=1.
Pentester mindset. IPv6 introduces new private/internal ranges (ULA, link-local) that allow-lists must consider. Don't only block 10.0.0.0/8 — also block fc00::/7 and fe80::/10.
Defensive coding habit. When binding a server, listen on a specific address (not ::), or set IPV6_V6ONLY explicitly so you know whether v4 traffic is allowed.
struct sockaddr_in6 {
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
IPv6 addresses are 128 bits, written as eight 16-bit hex groups joined
by colons. The struct sockaddr_in6 mirrors sockaddr_in; inet_pton/inet_ntop
handle both families uniformly.
struct sockaddr_in6 a6 = { .sin6_family = AF_INET6, .sin6_port = htons(8080) };
inet_pton(AF_INET6, "::1", &a6.sin6_addr);
int s = socket(AF_INET6, SOCK_STREAM, 0);
int v6only = 1; setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof v6only);
bind(s, (struct sockaddr *)&a6, sizeof a6);
struct sockaddr_in6 a = { .sin6_family = AF_INET6 };
a.sin6_port = htons(8080);
inet_pton(AF_INET6, "::1", &a.sin6_addr); /* parse loopback */
int s = socket(AF_INET6, SOCK_STREAM, 0);
int yes = 1;
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes);
bind(s, (struct sockaddr *)&a, sizeof a);
ip -6 addr lists IPv6 addresses. ping6 ::1 verifies loopback. nc -6 ::1 8080 connects from the shell.
sin6_addr is 16 bytes; use inet_pton/inet_ntop (not inet_aton).
Every modern web server, every cloud provider's load balancer, every operating system since 2010.
nc -6. 2. Parse a string into a discriminated v4/v6 union. 3. Reject any IPv6 result in ULA + link-local + loopback.IPv6 = 128-bit addresses, separate sockaddr, same API. Dual-stack via AF_UNSPEC + V6ONLY toggle. Don't break the parser on it.