ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ip.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ip.h
6 * @brief Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful
7 * text parsing, canonical formatting, and scope classification.
8 *
9 * One representation for both address families so the rest of the stack can carry a peer
10 * address without caring whether it is v4 or v6. The address bytes are stored in network
11 * (big-endian, left-to-right) order: a v4 address uses bytes[0..3], a v6 address bytes[0..15].
12 *
13 * Pure and host-testable - no lwIP, no Arduino, no heap, no stdlib parsing. The parser
14 * implements RFC 4291 §2.2 text forms (dotted-quad v4; v6 with `::` zero-compression and the
15 * embedded-v4 `::ffff:a.b.c.d` tail); the formatter emits the RFC 5952 canonical form
16 * (lower-case, no leading zeros, the longest zero run compressed to `::`, v4-mapped shown as
17 * dotted). ESP32 dual-stack bring-up (enabling IPv6 on the netif) lives in the physical layer
18 * behind PC_ENABLE_IPV6; the TCP/UDP listeners already bind IPADDR_TYPE_ANY, so the server
19 * accepts v6 connections the moment the interface has a v6 address.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef PROTOCORE_IP_H
26#define PROTOCORE_IP_H
27
28#include <stddef.h>
29#include <stdint.h>
30
31/** @brief Address family tag. */
32enum class pc_ip_family : uint8_t
33{
34 PC_IP_NONE = 0, ///< empty / unparsed
35 PC_IP_V4 = 4, ///< IPv4 (bytes[0..3])
36 PC_IP_V6 = 6, ///< IPv6 (bytes[0..15])
37};
38
39/** @brief Address scope, in rough order of reachability (used for allow/deny policy + logging). */
40enum class pc_ip_scope : uint8_t
41{
42 PC_IP_SCOPE_UNSPECIFIED = 0, ///< 0.0.0.0 / ::
43 PC_IP_SCOPE_LOOPBACK, ///< 127.0.0.0/8 / ::1
44 PC_IP_SCOPE_LINK_LOCAL, ///< 169.254.0.0/16 / fe80::/10
45 PC_IP_SCOPE_PRIVATE, ///< RFC1918 (10/8, 172.16/12, 192.168/16) / ULA fc00::/7
46 PC_IP_SCOPE_MULTICAST, ///< 224.0.0.0/4 / ff00::/8
47 PC_IP_SCOPE_GLOBAL, ///< globally routable unicast
48};
49
50/** @brief A v4 or v6 address in network (big-endian) byte order. */
51struct pc_ip
52{
53 pc_ip_family family; ///< address family tag
54 uint8_t bytes[16]; ///< network order; v4 uses the first 4
55};
56
57/** @brief Longest text an ::pc_ip_format can produce, including the NUL (RFC 5952 v4-mapped). */
58#define PC_IP_STR_MAX 46
59
60/**
61 * @brief Parse an IPv4 or IPv6 textual address (RFC 4291 §2.2) into @p out.
62 * @return true on success (@p out->family set to pc_ip_family::PC_IP_V4/V6), false if @p s is malformed.
63 */
64bool pc_ip_parse(const char *s, pc_ip *out);
65
66/**
67 * @brief Format @p ip into @p out as its RFC 5952 canonical text.
68 * @return the length written (excluding the NUL), or 0 if @p ip is empty or @p cap is too small
69 * (need up to ::PC_IP_STR_MAX).
70 */
71size_t pc_ip_format(const pc_ip *ip, char *out, size_t cap);
72
73/** @brief Classify @p ip into a ::pc_ip_scope. */
75
76/** @brief True if @p a and @p b are the same family and address. */
77bool pc_ip_equal(const pc_ip *a, const pc_ip *b);
78
79/** @brief True if @p ip is an IPv4-mapped IPv6 address (::ffff:a.b.c.d, RFC 4291 §2.5.5.2). */
80bool pc_ip_is_v4_mapped(const pc_ip *ip);
81
82/**
83 * @brief Build a v4 ::pc_ip from four octets (a.b.c.d).
84 */
85pc_ip pc_ip_from_v4_octets(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
86
87/**
88 * @brief Build a v6 ::pc_ip from 16 address bytes in network (big-endian) order.
89 */
90pc_ip pc_ip_from_v6_bytes(const uint8_t bytes[16]);
91
92/**
93 * @brief The v4 address as a big-endian (network-order) uint32 (a<<24 | b<<16 | c<<8 | d).
94 * @return 0 if @p ip is not a v4 (or v4-mapped) address.
95 */
96uint32_t pc_ip_to_v4_be(const pc_ip *ip);
97
98/** @brief True if @p ip is empty (pc_ip_family::PC_IP_NONE) or the all-zero unspecified address (0.0.0.0 / ::). */
99bool pc_ip_is_unspecified(const pc_ip *ip);
100
101/**
102 * @brief CIDR containment: is @p addr inside the @p net / @p prefix_len block?
103 *
104 * The two must be the same family. @p prefix_len is 0..32 for v4, 0..128 for v6; the top
105 * @p prefix_len bits of the address bytes must match @p net (a prefix of 0 matches everything).
106 * This is the standard v4/v6 allowlist match.
107 * @return true if @p addr is covered; false on a family mismatch or an out-of-range prefix.
108 */
109bool pc_ip_prefix_match(const pc_ip *addr, const pc_ip *net, uint8_t prefix_len);
110
111#endif // PROTOCORE_IP_H
pc_ip pc_ip_from_v4_octets(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Build a v4 pc_ip from four octets (a.b.c.d).
Definition ip.cpp:607
bool pc_ip_equal(const pc_ip *a, const pc_ip *b)
True if a and b are the same family and address.
Definition ip.cpp:585
pc_ip_scope
Address scope, in rough order of reachability (used for allow/deny policy + logging).
Definition ip.h:41
@ PC_IP_SCOPE_GLOBAL
globally routable unicast
@ PC_IP_SCOPE_LINK_LOCAL
169.254.0.0/16 / fe80::/10
@ PC_IP_SCOPE_LOOPBACK
127.0.0.0/8 / ::1
@ PC_IP_SCOPE_MULTICAST
224.0.0.0/4 / ff00::/8
@ PC_IP_SCOPE_PRIVATE
RFC1918 (10/8, 172.16/12, 192.168/16) / ULA fc00::/7.
@ PC_IP_SCOPE_UNSPECIFIED
0.0.0.0 / ::
pc_ip_family
Address family tag.
Definition ip.h:33
@ PC_IP_NONE
empty / unparsed
@ PC_IP_V6
IPv6 (bytes[0..15])
@ PC_IP_V4
IPv4 (bytes[0..3])
bool pc_ip_prefix_match(const pc_ip *addr, const pc_ip *net, uint8_t prefix_len)
CIDR containment: is addr inside the net / prefix_len block?
Definition ip.cpp:662
size_t pc_ip_format(const pc_ip *ip, char *out, size_t cap)
Format ip into out as its RFC 5952 canonical text.
Definition ip.cpp:488
pc_ip pc_ip_from_v6_bytes(const uint8_t bytes[16])
Build a v6 pc_ip from 16 address bytes in network (big-endian) order.
Definition ip.cpp:619
bool pc_ip_parse(const char *s, pc_ip *out)
Parse an IPv4 or IPv6 textual address (RFC 4291 §2.2) into out.
Definition ip.cpp:436
pc_ip_scope pc_ip_classify(const pc_ip *ip)
Classify ip into a pc_ip_scope.
Definition ip.cpp:568
bool pc_ip_is_unspecified(const pc_ip *ip)
True if ip is empty (pc_ip_family::PC_IP_NONE) or the all-zero unspecified address (0....
Definition ip.cpp:645
uint32_t pc_ip_to_v4_be(const pc_ip *ip)
The v4 address as a big-endian (network-order) uint32 (a<<24 | b<<16 | c<<8 | d).
Definition ip.cpp:627
bool pc_ip_is_v4_mapped(const pc_ip *ip)
True if ip is an IPv4-mapped IPv6 address (::ffff:a.b.c.d, RFC 4291 §2.5.5.2).
Definition ip.cpp:563
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:52
pc_ip_family family
address family tag
Definition ip.h:53
uint8_t bytes[16]
network order; v4 uses the first 4
Definition ip.h:54