xref: /illumos-gate/usr/src/test/util-tests/tests/libdhcputil/libdhcputil.c (revision 438283cf397cce47d80cc67b04bbfdfe73b0d142)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2026 Oxide Computer Company
14  */
15 
16 /*
17  * This program is a small test harness for libdhcputil.
18  */
19 
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <err.h>
25 #include <netinet/in.h>
26 #include <sys/sysmacros.h>
27 
28 #include <netinet/dhcp.h>
29 #include <dhcp_inittab.h>
30 
31 typedef struct {
32 	uint8_t *tc_binary;
33 	size_t tc_binlen;
34 	const char *tc_string;
35 } testcase_t;
36 
37 #define	CASE(s, ...)	{					\
38 	.tc_binary = (uint8_t[]) { __VA_ARGS__ },		\
39 	.tc_binlen = sizeof ((uint8_t[]) { __VA_ARGS__ }),	\
40 	.tc_string = s,						\
41 }
42 
43 testcase_t such_cases[] = {
44 	/*
45 	 * A zero-length value should result in the empty string:
46 	 */
47 	CASE(""),
48 
49 	/*
50 	 * Confirm that we can support each possible prefix length:
51 	 */
52 	CASE("128.0.0.0/1:1.2.3.4",
53 	    1,		128,			1, 2, 3, 4),
54 	CASE("128.0.0.0/2:1.2.3.4",
55 	    2,		128,			1, 2, 3, 4),
56 	CASE("128.0.0.0/3:1.2.3.4",
57 	    3,		128,			1, 2, 3, 4),
58 	CASE("128.0.0.0/4:1.2.3.4",
59 	    4,		128,			1, 2, 3, 4),
60 	CASE("10.0.0.0/5:1.2.3.4",
61 	    5,		10,			1, 2, 3, 4),
62 	CASE("10.0.0.0/6:1.2.3.4",
63 	    6,		10,			1, 2, 3, 4),
64 	CASE("10.0.0.0/7:1.2.3.4",
65 	    7,		10,			1, 2, 3, 4),
66 	CASE("10.0.0.0/8:1.2.3.4",
67 	    8,		10,			1, 2, 3, 4),
68 	CASE("10.128.0.0/9:1.2.3.4",
69 	    9,		10, 128,		1, 2, 3, 4),
70 	CASE("10.128.0.0/10:1.2.3.4",
71 	    10,		10, 128,		1, 2, 3, 4),
72 	CASE("10.128.0.0/11:1.2.3.4",
73 	    11,		10, 128,		1, 2, 3, 4),
74 	CASE("10.128.0.0/12:1.2.3.4",
75 	    12,		10, 128,		1, 2, 3, 4),
76 	CASE("10.128.0.0/13:1.2.3.4",
77 	    13,		10, 128,		1, 2, 3, 4),
78 	CASE("10.128.0.0/14:1.2.3.4",
79 	    14,		10, 128,		1, 2, 3, 4),
80 	CASE("10.128.0.0/15:1.2.3.4",
81 	    15,		10, 128,		1, 2, 3, 4),
82 	CASE("1.255.0.0/16:1.2.3.4",
83 	    16,		1, 255,			1, 2, 3, 4),
84 	CASE("1.255.128.0/17:1.2.3.4",
85 	    17,		1, 255, 128,		1, 2, 3, 4),
86 	CASE("1.255.128.0/18:1.2.3.4",
87 	    18,		1, 255, 128,		1, 2, 3, 4),
88 	CASE("1.255.128.0/19:1.2.3.4",
89 	    19,		1, 255, 128,		1, 2, 3, 4),
90 	CASE("1.255.128.0/20:1.2.3.4",
91 	    20,		1, 255, 128,		1, 2, 3, 4),
92 	CASE("1.255.128.0/21:1.2.3.4",
93 	    21,		1, 255, 128,		1, 2, 3, 4),
94 	CASE("1.255.128.0/22:1.2.3.4",
95 	    22,		1, 255, 128,		1, 2, 3, 4),
96 	CASE("1.255.128.0/23:1.2.3.4",
97 	    23,		1, 255, 128,		1, 2, 3, 4),
98 	CASE("1.255.255.0/24:1.2.3.4",
99 	    24,		1, 255, 255,		1, 2, 3, 4),
100 
101 	CASE("1.77.99.128/25:1.2.3.4",
102 	    25,		1, 77, 99, 128,		1, 2, 3, 4),
103 	CASE("1.77.99.128/26:1.2.3.4",
104 	    26,		1, 77, 99, 128,		1, 2, 3, 4),
105 	CASE("1.77.99.128/27:1.2.3.4",
106 	    27,		1, 77, 99, 128,		1, 2, 3, 4),
107 	CASE("1.77.99.128/28:1.2.3.4",
108 	    28,		1, 77, 99, 128,		1, 2, 3, 4),
109 	CASE("1.77.99.128/29:1.2.3.4",
110 	    29,		1, 77, 99, 128,		1, 2, 3, 4),
111 	CASE("1.77.99.128/30:1.2.3.4",
112 	    30,		1, 77, 99, 128,		1, 2, 3, 4),
113 	CASE("1.77.99.128/31:1.2.3.4",
114 	    31,		1, 77, 99, 128,		1, 2, 3, 4),
115 	CASE("1.77.99.128/32:1.2.3.4",
116 	    32,		1, 77, 99, 128,		1, 2, 3, 4),
117 
118 	/*
119 	 * Check that interface routes work:
120 	 */
121 	CASE("1.2.3.0/24:0.0.0.0",
122 	    24,		1, 2, 3,		0, 0, 0, 0),
123 	CASE("1.2.3.0/24:0.0.0.0,4.5.0.0/15:0.0.0.0",
124 	    24,		1, 2, 3,		0, 0, 0, 0,
125 	    15,		4, 5,			0, 0, 0, 0),
126 
127 	/*
128 	 * This is the common pattern used for off-subnet gateways:
129 	 */
130 	CASE("9.10.11.12/32:0.0.0.0,0.0.0.0/0:9.10.11.12",
131 	    32,		9, 10, 11, 12,		0, 0, 0, 0,
132 	    0,					9, 10, 11, 12),
133 
134 	/*
135 	 * Shortest and longest possible single route string:
136 	 */
137 	CASE("255.255.255.255/32:255.255.255.255",
138 	    32,		255, 255, 255, 255,	255, 255, 255, 255),
139 	CASE("0.0.0.0/0:0.0.0.0",
140 	    0,					0, 0, 0, 0),
141 
142 	/*
143 	 * Lots of routes:
144 	 */
145 	CASE("255.255.255.255/32:255.255.255.255,"
146 	    "0.0.0.0/32:0.0.0.0,"
147 	    "255.255.255.255/32:255.255.255.255,"
148 	    "0.0.0.0/0:0.0.0.0,"
149 	    "0.0.0.0/32:255.255.255.255,"
150 	    "255.255.255.255/32:0.0.0.0,"
151 	    "255.255.255.255/32:255.255.255.255,"
152 	    "0.0.0.0/32:0.0.0.0",
153 	    32,		255, 255, 255, 255,	255, 255, 255, 255,
154 	    32,		0, 0, 0, 0,		0, 0, 0, 0,
155 	    32,		255, 255, 255, 255,	255, 255, 255, 255,
156 	    0,					0, 0, 0, 0,
157 	    32,		0, 0, 0, 0,		255, 255, 255, 255,
158 	    32,		255, 255, 255, 255,	0, 0, 0, 0,
159 	    32,		255, 255, 255, 255,	255, 255, 255, 255,
160 	    32,		0, 0, 0, 0,		0, 0, 0, 0),
161 };
162 
163 int
164 main(int argc, char **argv)
165 {
166 	int rc = EXIT_FAILURE;
167 
168 	dhcp_symbol_t *sym = inittab_getbycode(ITAB_CAT_STANDARD,
169 	    ITAB_CONS_INFO, CD_CLASSLESS_ROUTES);
170 	if (sym == NULL) {
171 		errx(1, "could not locate Classless Routes option");
172 	}
173 
174 	/*
175 	 * Confirm that we can decode binary representations into
176 	 * human-readable strings:
177 	 */
178 	for (uint_t n = 0; n < ARRAY_SIZE(such_cases); n++) {
179 		testcase_t *tc = &such_cases[n];
180 
181 		int ierrno = 0;
182 		char *o = inittab_decode_e(sym, tc->tc_binary, tc->tc_binlen,
183 		    B_TRUE, &ierrno);
184 
185 		if (o == NULL) {
186 			errx(1, "test case #%u: decode failure: %s", n,
187 			    inittab_errstr(ierrno));
188 		}
189 
190 		if (strcmp(o, tc->tc_string) != 0) {
191 			errx(1, "test case #%u: wanted \"%s\", got \"%s\"\n",
192 			    n, tc->tc_string, o);
193 		}
194 
195 		printf("test case #%u: ok! (%s)\n", n, o);
196 		free(o);
197 	}
198 
199 	/*
200 	 * Make sure we can also encode strings into binary:
201 	 */
202 	for (uint_t n = 0; n < ARRAY_SIZE(such_cases); n++) {
203 		testcase_t *tc = &such_cases[n];
204 
205 		int ierrno = 0;
206 		uint16_t olen = 0;
207 		uint8_t *o = inittab_encode_e(sym, tc->tc_string, &olen,
208 		    B_TRUE, &ierrno);
209 
210 		if (o == NULL) {
211 			errx(1, "test case #%u (%s): encode failure: %s", n,
212 			    tc->tc_string, inittab_errstr(ierrno));
213 		}
214 
215 		if (olen != tc->tc_binlen) {
216 			errx(1, "test case #%u (%s): encoded len %u != %u", n,
217 			    tc->tc_string, olen, (uint_t)tc->tc_binlen);
218 		}
219 
220 		if (memcmp(o, tc->tc_binary, olen) != 0) {
221 			errx(1, "test case #%u (%s): encoded output is wrong",
222 			    n, tc->tc_string);
223 		}
224 
225 		printf("test case #%u: ok! (%s) [len %u]\n", n, tc->tc_string,
226 		    olen);
227 		free(o);
228 	}
229 
230 	warnx("ok");
231 	rc = EXIT_SUCCESS;
232 
233 	return (rc);
234 }
235