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