The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
inet_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for IP address parsing and formatting
18 *
19 * @file src/lib/util/test/inet_tests.c
20 *
21 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
22 */
23#include "acutest.h"
24#include "acutest_helpers.h"
25
26#include <freeradius-devel/util/inet.h>
27
28/*
29 * Test parsing simple IPv4 addresses.
30 */
31static void test_inet_pton4_basic(void)
32{
33 fr_ipaddr_t addr;
34
35 TEST_CASE("Parse 192.168.1.1");
36 TEST_CHECK(fr_inet_pton4(&addr, "192.168.1.1", -1, false, false, false) == 0);
37 TEST_CHECK(addr.af == AF_INET);
38 TEST_CHECK(addr.prefix == 32);
39 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0xc0a80101);
40
41 TEST_CASE("Parse 10.0.0.1");
42 TEST_CHECK(fr_inet_pton4(&addr, "10.0.0.1", -1, false, false, false) == 0);
43 TEST_CHECK(addr.af == AF_INET);
44 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0x0a000001);
45
46 TEST_CASE("Parse 0.0.0.0");
47 TEST_CHECK(fr_inet_pton4(&addr, "0.0.0.0", -1, false, false, false) == 0);
48 TEST_CHECK(addr.addr.v4.s_addr == 0);
49
50 TEST_CASE("Parse 255.255.255.255");
51 TEST_CHECK(fr_inet_pton4(&addr, "255.255.255.255", -1, false, false, false) == 0);
52 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0xffffffff);
53}
54
55/*
56 * Test parsing IPv4 with prefixes.
57 */
58static void test_inet_pton4_prefix(void)
59{
60 fr_ipaddr_t addr;
61
62 TEST_CASE("Parse 192.168.0.0/16");
63 TEST_CHECK(fr_inet_pton4(&addr, "192.168.0.0/16", -1, false, false, true) == 0);
64 TEST_CHECK(addr.af == AF_INET);
65 TEST_CHECK(addr.prefix == 16);
66
67 TEST_CASE("Parse 10.0.0.0/8");
68 TEST_CHECK(fr_inet_pton4(&addr, "10.0.0.0/8", -1, false, false, true) == 0);
69 TEST_CHECK(addr.prefix == 8);
70
71 TEST_CASE("Parse 0.0.0.0/0");
72 TEST_CHECK(fr_inet_pton4(&addr, "0.0.0.0/0", -1, false, false, true) == 0);
73 TEST_CHECK(addr.prefix == 0);
74}
75
76/*
77 * Test parsing invalid IPv4 addresses.
78 */
79static void test_inet_pton4_invalid(void)
80{
81 fr_ipaddr_t addr;
82
83 TEST_CASE("Invalid: empty string");
84 TEST_CHECK(fr_inet_pton4(&addr, "", -1, false, false, false) < 0);
85
86 TEST_CASE("Invalid: too many octets");
87 TEST_CHECK(fr_inet_pton4(&addr, "1.2.3.4.5", -1, false, false, false) < 0);
88
89 TEST_CASE("Invalid: prefix > 32");
90 TEST_CHECK(fr_inet_pton4(&addr, "10.0.0.0/33", -1, false, false, true) < 0);
91}
92
93/*
94 * Test parsing IPv6 addresses.
95 */
96static void test_inet_pton6_basic(void)
97{
98 fr_ipaddr_t addr;
99
100 TEST_CASE("Parse ::1");
101 TEST_CHECK(fr_inet_pton6(&addr, "::1", -1, false, false, false) == 0);
102 TEST_CHECK(addr.af == AF_INET6);
103 TEST_CHECK(addr.prefix == 128);
104 TEST_CHECK(addr.addr.v6.s6_addr[15] == 1);
105
106 TEST_CASE("Parse ::");
107 TEST_CHECK(fr_inet_pton6(&addr, "::", -1, false, false, false) == 0);
108 TEST_CHECK(addr.af == AF_INET6);
109 {
110 struct in6_addr zero = {};
111 TEST_CHECK(memcmp(&addr.addr.v6, &zero, sizeof(zero)) == 0);
112 }
113
114 TEST_CASE("Parse fe80::1");
115 TEST_CHECK(fr_inet_pton6(&addr, "fe80::1", -1, false, false, false) == 0);
116 TEST_CHECK(addr.af == AF_INET6);
117 TEST_CHECK(addr.addr.v6.s6_addr[0] == 0xfe);
118 TEST_CHECK(addr.addr.v6.s6_addr[1] == 0x80);
119 TEST_CHECK(addr.addr.v6.s6_addr[15] == 1);
120}
121
122/*
123 * Test parsing IPv6 with prefixes.
124 */
125static void test_inet_pton6_prefix(void)
126{
127 fr_ipaddr_t addr;
128
129 TEST_CASE("Parse 2001:db8::/32");
130 TEST_CHECK(fr_inet_pton6(&addr, "2001:db8::/32", -1, false, false, true) == 0);
131 TEST_CHECK(addr.af == AF_INET6);
132 TEST_CHECK(addr.prefix == 32);
133
134 TEST_CASE("Parse ::/0");
135 TEST_CHECK(fr_inet_pton6(&addr, "::/0", -1, false, false, true) == 0);
136 TEST_CHECK(addr.prefix == 0);
137
138 TEST_CASE("Parse fe80::/10");
139 TEST_CHECK(fr_inet_pton6(&addr, "fe80::/10", -1, false, false, true) == 0);
140 TEST_CHECK(addr.prefix == 10);
141}
142
143/*
144 * Test the generic fr_inet_pton which auto-detects v4/v6.
145 */
146static void test_inet_pton_auto(void)
147{
148 fr_ipaddr_t addr;
149
150 TEST_CASE("Auto-detect IPv4");
151 TEST_CHECK(fr_inet_pton(&addr, "192.168.1.1", -1, AF_UNSPEC, false, false) == 0);
152 TEST_CHECK(addr.af == AF_INET);
153
154 TEST_CASE("Auto-detect IPv6");
155 TEST_CHECK(fr_inet_pton(&addr, "::1", -1, AF_UNSPEC, false, false) == 0);
156 TEST_CHECK(addr.af == AF_INET6);
157
158 TEST_CASE("Force AF_INET");
159 TEST_CHECK(fr_inet_pton(&addr, "10.0.0.1", -1, AF_INET, false, false) == 0);
160 TEST_CHECK(addr.af == AF_INET);
161
162 TEST_CASE("Force AF_INET6");
163 TEST_CHECK(fr_inet_pton(&addr, "::1", -1, AF_INET6, false, false) == 0);
164 TEST_CHECK(addr.af == AF_INET6);
165}
166
167/*
168 * Test fr_inet_ntop (address to string).
169 */
170static void test_inet_ntop(void)
171{
172 fr_ipaddr_t addr;
173 char buf[FR_IPADDR_STRLEN];
174
175 TEST_CASE("IPv4 to string");
176 fr_inet_pton4(&addr, "192.168.1.1", -1, false, false, false);
177 TEST_CHECK(fr_inet_ntop(buf, sizeof(buf), &addr) != NULL);
178 TEST_CHECK(strcmp(buf, "192.168.1.1") == 0);
179 TEST_MSG("expected 192.168.1.1, got %s", buf);
180
181 TEST_CASE("IPv6 loopback to string");
182 fr_inet_pton6(&addr, "::1", -1, false, false, false);
183 TEST_CHECK(fr_inet_ntop(buf, sizeof(buf), &addr) != NULL);
184 TEST_CHECK(strcmp(buf, "::1") == 0);
185 TEST_MSG("expected ::1, got %s", buf);
186}
187
188/*
189 * Test fr_inet_ntop_prefix (address with prefix to string).
190 */
191static void test_inet_ntop_prefix(void)
192{
193 fr_ipaddr_t addr;
194 char buf[FR_IPADDR_PREFIX_STRLEN];
195
196 TEST_CASE("IPv4 prefix to string");
197 fr_inet_pton4(&addr, "10.0.0.0/8", -1, false, false, true);
198 TEST_CHECK(fr_inet_ntop_prefix(buf, sizeof(buf), &addr) != NULL);
199 TEST_CHECK(strcmp(buf, "10.0.0.0/8") == 0);
200 TEST_MSG("expected 10.0.0.0/8, got %s", buf);
201}
202
203/*
204 * Test fr_ipaddr_cmp.
205 */
206static void test_ipaddr_cmp(void)
207{
208 fr_ipaddr_t a, b;
209
210 TEST_CASE("Same address compares equal");
211 fr_inet_pton4(&a, "10.0.0.1", -1, false, false, false);
212 fr_inet_pton4(&b, "10.0.0.1", -1, false, false, false);
213 TEST_CHECK(fr_ipaddr_cmp(&a, &b) == 0);
214
215 TEST_CASE("Different addresses compare non-equal");
216 fr_inet_pton4(&b, "10.0.0.2", -1, false, false, false);
217 TEST_CHECK(fr_ipaddr_cmp(&a, &b) != 0);
218
219 TEST_CASE("Less-than comparison");
220 TEST_CHECK(fr_ipaddr_cmp(&a, &b) < 0);
221
222 TEST_CASE("Greater-than comparison");
223 TEST_CHECK(fr_ipaddr_cmp(&b, &a) > 0);
224
225 TEST_CASE("IPv4 vs IPv6 comparison (different af)");
226 fr_inet_pton4(&a, "10.0.0.1", -1, false, false, false);
227 fr_inet_pton6(&b, "::1", -1, false, false, false);
228 TEST_CHECK(fr_ipaddr_cmp(&a, &b) != 0);
229}
230
231/*
232 * Test fr_ipaddr_mask.
233 */
234static void test_ipaddr_mask(void)
235{
236 fr_ipaddr_t addr;
237
238 TEST_CASE("Mask 192.168.1.100 to /24");
239 fr_inet_pton4(&addr, "192.168.1.100", -1, false, false, false);
240 fr_ipaddr_mask(&addr, 24);
241 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0xc0a80100);
242
243 TEST_CASE("Mask 10.1.2.3 to /8");
244 fr_inet_pton4(&addr, "10.1.2.3", -1, false, false, false);
245 fr_ipaddr_mask(&addr, 8);
246 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0x0a000000);
247
248 TEST_CASE("Mask to /0 clears everything");
249 fr_inet_pton4(&addr, "255.255.255.255", -1, false, false, false);
250 fr_ipaddr_mask(&addr, 0);
251 TEST_CHECK(addr.addr.v4.s_addr == 0);
252}
253
254/*
255 * Test fr_ipaddr_is_inaddr_any.
256 */
258{
259 fr_ipaddr_t addr;
260
261 TEST_CASE("0.0.0.0 is INADDR_ANY");
262 fr_inet_pton4(&addr, "0.0.0.0", -1, false, false, false);
264
265 TEST_CASE("10.0.0.1 is not INADDR_ANY");
266 fr_inet_pton4(&addr, "10.0.0.1", -1, false, false, false);
268
269 TEST_CASE(":: is IN6ADDR_ANY");
270 fr_inet_pton6(&addr, "::", -1, false, false, false);
272
273 TEST_CASE("::1 is not IN6ADDR_ANY");
274 fr_inet_pton6(&addr, "::1", -1, false, false, false);
276}
277
278/*
279 * Test fr_ipaddr_is_prefix.
280 */
281static void test_ipaddr_is_prefix(void)
282{
283 fr_ipaddr_t addr;
284
285 TEST_CASE("/32 is not a prefix");
286 fr_inet_pton4(&addr, "10.0.0.1", -1, false, false, false);
287 TEST_CHECK(fr_ipaddr_is_prefix(&addr) == 0);
288
289 TEST_CASE("/24 is a prefix");
290 fr_inet_pton4(&addr, "10.0.0.0/24", -1, false, false, true);
291 TEST_CHECK(fr_ipaddr_is_prefix(&addr) == 1);
292
293 TEST_CASE("/128 is not a prefix (IPv6)");
294 fr_inet_pton6(&addr, "::1", -1, false, false, false);
295 TEST_CHECK(fr_ipaddr_is_prefix(&addr) == 0);
296
297 TEST_CASE("/64 is a prefix (IPv6)");
298 fr_inet_pton6(&addr, "fe80::/64", -1, false, false, true);
299 TEST_CHECK(fr_ipaddr_is_prefix(&addr) == 1);
300}
301
302/*
303 * Test fr_inet_pton_port.
304 */
305static void test_inet_pton_port(void)
306{
307 fr_ipaddr_t addr;
308 uint16_t port = 0;
309
310 TEST_CASE("Parse 192.168.1.1:8080");
311 TEST_CHECK(fr_inet_pton_port(&addr, &port, "192.168.1.1:8080", -1, AF_UNSPEC, false, false) == 0);
312 TEST_CHECK(addr.af == AF_INET);
313 TEST_CHECK(ntohl(addr.addr.v4.s_addr) == 0xc0a80101);
314 TEST_CHECK(port == 8080);
315
316 TEST_CASE("Parse [::1]:443");
317 port = 0;
318 TEST_CHECK(fr_inet_pton_port(&addr, &port, "[::1]:443", -1, AF_UNSPEC, false, false) == 0);
319 TEST_CHECK(addr.af == AF_INET6);
320 TEST_CHECK(port == 443);
321
322 TEST_CASE("Parse address without port");
323 TEST_CHECK(fr_inet_pton_port(&addr, &port, "10.0.0.1", -1, AF_UNSPEC, false, false) == 0);
324 TEST_CHECK(port == 0);
325}
326
327/*
328 * Test sockaddr round-trip conversion.
329 */
330static void test_ipaddr_sockaddr(void)
331{
332 fr_ipaddr_t addr, recovered;
333 struct sockaddr_storage sa;
334 socklen_t salen;
335 uint16_t port, recovered_port;
336
337 TEST_CASE("IPv4 to sockaddr and back");
338 fr_inet_pton4(&addr, "192.168.1.1", -1, false, false, false);
339 port = 1812;
340 TEST_CHECK(fr_ipaddr_to_sockaddr(&sa, &salen, &addr, port) == 0);
341 TEST_CHECK(fr_ipaddr_from_sockaddr(&recovered, &recovered_port, &sa, salen) == 0);
342 TEST_CHECK(fr_ipaddr_cmp(&addr, &recovered) == 0);
343 TEST_CHECK(recovered_port == port);
344
345 TEST_CASE("IPv6 to sockaddr and back");
346 fr_inet_pton6(&addr, "::1", -1, false, false, false);
347 port = 1813;
348 TEST_CHECK(fr_ipaddr_to_sockaddr(&sa, &salen, &addr, port) == 0);
349 TEST_CHECK(fr_ipaddr_from_sockaddr(&recovered, &recovered_port, &sa, salen) == 0);
350 TEST_CHECK(fr_ipaddr_cmp(&addr, &recovered) == 0);
351 TEST_CHECK(recovered_port == port);
352}
353
355 { "inet_pton4_basic", test_inet_pton4_basic },
356 { "inet_pton4_prefix", test_inet_pton4_prefix },
357 { "inet_pton4_invalid", test_inet_pton4_invalid },
358 { "inet_pton6_basic", test_inet_pton6_basic },
359 { "inet_pton6_prefix", test_inet_pton6_prefix },
360 { "inet_pton_auto", test_inet_pton_auto },
361 { "inet_ntop", test_inet_ntop },
362 { "inet_ntop_prefix", test_inet_ntop_prefix },
363 { "ipaddr_cmp", test_ipaddr_cmp },
364 { "ipaddr_mask", test_ipaddr_mask },
365 { "ipaddr_is_inaddr_any", test_ipaddr_is_inaddr_any },
366 { "ipaddr_is_prefix", test_ipaddr_is_prefix },
367 { "inet_pton_port", test_inet_pton_port },
368 { "ipaddr_sockaddr", test_ipaddr_sockaddr },
370};
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_CASE(name)
Definition acutest.h:186
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
int fr_ipaddr_is_prefix(fr_ipaddr_t const *ipaddr)
Determine if an address is a prefix.
Definition inet.c:127
char * fr_inet_ntop_prefix(char out[static FR_IPADDR_PREFIX_STRLEN], size_t outlen, fr_ipaddr_t const *addr)
Print a fr_ipaddr_t as a CIDR style network prefix.
Definition inet.c:1081
int fr_inet_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask)
Parse an IPv6 address or IPv6 prefix in presentation format (and others)
Definition inet.c:633
int fr_ipaddr_from_sockaddr(fr_ipaddr_t *ipaddr, uint16_t *port, struct sockaddr_storage const *sa, socklen_t salen)
Convert sockaddr to our internal ip address representation.
Definition inet.c:1449
int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
Definition inet.c:784
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
Definition inet.c:945
int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
Definition inet.c:63
char * fr_inet_ntop(char out[static FR_IPADDR_STRLEN], size_t outlen, fr_ipaddr_t const *addr)
Print the address portion of a fr_ipaddr_t.
Definition inet.c:1026
int fr_ipaddr_to_sockaddr(struct sockaddr_storage *sa, socklen_t *salen, fr_ipaddr_t const *ipaddr, uint16_t port)
Convert our internal ip address representation to a sockaddr.
Definition inet.c:1400
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1354
void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
Zeroes out the host portion of an fr_ipaddr_t.
Definition inet.c:219
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
Definition inet.h:69
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
int af
Address family.
Definition inet.h:64
union fr_ipaddr_t::@137 addr
#define FR_IPADDR_PREFIX_STRLEN
Like FR_IPADDR_STRLEN but with space for a prefix.
Definition inet.h:93
IPv4/6 prefix.
static void test_ipaddr_mask(void)
Definition inet_tests.c:234
TEST_LIST
Definition inet_tests.c:354
static void test_inet_ntop(void)
Definition inet_tests.c:170
static void test_inet_pton4_prefix(void)
Definition inet_tests.c:58
static void test_ipaddr_cmp(void)
Definition inet_tests.c:206
static void test_inet_pton4_basic(void)
Definition inet_tests.c:31
static void test_inet_pton_auto(void)
Definition inet_tests.c:146
static void test_inet_pton_port(void)
Definition inet_tests.c:305
static void test_inet_pton6_prefix(void)
Definition inet_tests.c:125
static void test_inet_pton6_basic(void)
Definition inet_tests.c:96
static void test_inet_ntop_prefix(void)
Definition inet_tests.c:191
static void test_ipaddr_is_prefix(void)
Definition inet_tests.c:281
static void test_ipaddr_is_inaddr_any(void)
Definition inet_tests.c:257
static void test_inet_pton4_invalid(void)
Definition inet_tests.c:79
static void test_ipaddr_sockaddr(void)
Definition inet_tests.c:330
static const uint8_t * zero
Definition md4.c:357
unsigned short uint16_t
int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask_bits)