1 /* $NetBSD: relay_unittests.c,v 1.4 2022/04/03 01:10:59 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Internet Systems Consortium, Inc. 19 * PO Box 360 20 * Newmarket, NH 03857 USA 21 * <info@isc.org> 22 * https://www.isc.org/ 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __RCSID("$NetBSD: relay_unittests.c,v 1.4 2022/04/03 01:10:59 christos Exp $"); 28 29 #include "config.h" 30 #include <atf-c.h> 31 #include <omapip/omapip_p.h> 32 #include "dhcpd.h" 33 34 /* @brief Externs for dhcrelay.c functions under test */ 35 extern int add_agent_options; 36 extern int add_relay_agent_options(struct interface_info *, 37 struct dhcp_packet *, unsigned, 38 struct in_addr); 39 40 extern int find_interface_by_agent_option(struct dhcp_packet *, 41 struct interface_info **, 42 u_int8_t *, int); 43 44 extern int strip_relay_agent_options(struct interface_info *, 45 struct interface_info **, 46 struct dhcp_packet *, unsigned); 47 48 /* @brief Add the given option data to a DHCPv4 packet 49 * 50 * It first fills the packet.options buffer with the given pad character. 51 * Next it copies the DHCP magic cookie value into the beginning of the 52 * options buffer. Finally it appends the given data after the cookie. 53 * 54 * @param packet pointer to the packet 55 * @param data pointer to the option data to copy into the packet's options 56 * buffer 57 * @param len length of the option data to copy 58 * @param pad byte value with which to initialize the packet's options buffer 59 * 60 * @return returns the new length of the packet 61 */ 62 unsigned set_packet_options(struct dhcp_packet *packet, 63 unsigned char* data, unsigned len, 64 unsigned char pad) { 65 unsigned new_len; 66 memset(packet->options, pad, DHCP_MAX_OPTION_LEN); 67 68 // Add the COOKIE 69 new_len = 4; 70 memcpy(packet->options, DHCP_OPTIONS_COOKIE, new_len); 71 72 new_len += len; 73 if (new_len > DHCP_MAX_OPTION_LEN) { 74 return(0); 75 } 76 77 memcpy(&packet->options[4], data, len); 78 return(new_len + DHCP_FIXED_NON_UDP); 79 } 80 81 /* @brief Checks two sets of option data for equalit 82 * 83 * It constructs the expected options content by creating an options buffer 84 * filled with the pad value. Next it copies the DHCP magic cookie value 85 * into the beginning of the buffer and then appends the expected data after 86 * the cookie. It the compares this buffer to the actual buffer passed in 87 * for equality and returns the result. 88 * 89 * @param actual_options pointer to the packet::options to be checked 90 * @param expected_data pointer to the expected options data (everything after 91 * the DHCP cookie) 92 * @param data_len length of the expected options data 93 * @param pad byte value with which to initialize the packet's options buffer 94 * 95 * @return zero it the sets of data match, non-zero otherwise 96 */ 97 int check_with_pad(unsigned char* actual_options, 98 unsigned char *expected_data, 99 unsigned data_len, unsigned char pad) { 100 101 unsigned char exp_options[DHCP_MAX_OPTION_LEN]; 102 unsigned new_len; 103 104 memset(exp_options, pad, DHCP_MAX_OPTION_LEN); 105 new_len = 4; 106 memcpy(exp_options, DHCP_OPTIONS_COOKIE, new_len); 107 108 new_len += data_len; 109 if (new_len > DHCP_MAX_OPTION_LEN) { 110 return(-1); 111 } 112 113 memcpy(&exp_options[4], expected_data, data_len); 114 return (memcmp(actual_options, exp_options, DHCP_MAX_OPTION_LEN)); 115 } 116 117 ATF_TC(strip_relay_agent_options_test); 118 119 ATF_TC_HEAD(strip_relay_agent_options_test, tc) { 120 atf_tc_set_md_var(tc, "descr", "tests strip_relay-agent_options"); 121 } 122 123 /* This Test exercises strip_relay_agent_options() function */ 124 ATF_TC_BODY(strip_relay_agent_options_test, tc) { 125 126 struct interface_info ifaces; 127 struct interface_info *matched; 128 struct dhcp_packet packet; 129 unsigned len; 130 int ret; 131 132 memset(&ifaces, 0x0, sizeof(ifaces)); 133 matched = 0; 134 memset(&packet, 0x0, sizeof(packet)); 135 len = 0; 136 137 /* Make sure an empty packet is harmless. We set add_agent_options = 1 138 * to avoid early return when it's 0. */ 139 add_agent_options = 1; 140 ret = strip_relay_agent_options(&ifaces, &matched, &packet, len); 141 if (ret != 0) { 142 atf_tc_fail("empty packet failed"); 143 } 144 145 { 146 /* 147 * Uses valid input option data to verify that: 148 * - When add_agent_options is false, the output option data is the 149 * the same as the input option data (i.e. nothing removed) 150 * - When add_agent_options is true, 0 length relay agent option has 151 * been removed from the output option data 152 * - When add_agent_options is true, a relay agent option has 153 * been removed from the output option data 154 * 155 */ 156 157 unsigned char input_data[] = { 158 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 159 0x52, 0x08, 0x01, 0x06, 0x65, /* Relay Agent Option, len = 8 */ 160 0x6e, 0x70, 0x30, 0x73, 0x4f, 161 162 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 163 0x43, 0x00 /* Opt 0x43, len = 0 */ 164 }; 165 166 unsigned char input_data2[] = { 167 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 168 0x52, 0x00, /* Relay Agent Option, len = 0 */ 169 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 170 0x43, 0x00 /* Opt 0x43, len = 0 */ 171 }; 172 173 unsigned char output_data[] = { 174 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 175 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 176 0x43, 0x00 /* Opt 0x43, len = 0 */ 177 }; 178 179 unsigned char pad = 0x0; 180 len = set_packet_options(&packet, input_data, sizeof(input_data), pad); 181 if (len == 0) { 182 atf_tc_fail("input_data: set_packet_options failed"); 183 } 184 185 /* When add_agent_options = 0, no change should occur */ 186 add_agent_options = 0; 187 ret = strip_relay_agent_options(&ifaces, &matched, &packet, len); 188 if (ret != len) { 189 atf_tc_fail("expected unchanged len %d, returned %d", len, ret); 190 } 191 192 if (check_with_pad(packet.options, input_data, sizeof(input_data), 193 pad) != 0) { 194 atf_tc_fail("expected unchanged data, does not match"); 195 } 196 197 /* When add_agent_options = 1, it should remove the eight byte 198 * relay agent option. */ 199 add_agent_options = 1; 200 201 /* Beause the inbound option data is less than the BOOTP_MIN_LEN, 202 * the output data should get padded out to BOOTP_MIN_LEN 203 * padded out to BOOTP_MIN_LEN */ 204 ret = strip_relay_agent_options(&ifaces, &matched, &packet, len); 205 if (ret != BOOTP_MIN_LEN) { 206 atf_tc_fail("input_data: len of %d, returned %d", 207 BOOTP_MIN_LEN, ret); 208 } 209 210 if (check_with_pad(packet.options, output_data, sizeof(output_data), 211 pad) != 0) { 212 atf_tc_fail("input_data: expected data does not match"); 213 } 214 215 /* Now let's repeat it with a relay agent option 0 bytes in length. */ 216 len = set_packet_options(&packet, input_data2, sizeof(input_data2), pad); 217 if (len == 0) { 218 atf_tc_fail("input_data2 set_packet_options failed"); 219 } 220 221 /* Because the inbound option data is less than the BOOTP_MIN_LEN, 222 * the output data should get padded out to BOOTP_MIN_LEN 223 * padded out to BOOTP_MIN_LEN */ 224 ret = strip_relay_agent_options(&ifaces, &matched, &packet, len); 225 if (ret != BOOTP_MIN_LEN) { 226 atf_tc_fail("input_data2: len of %d, returned %d", 227 BOOTP_MIN_LEN, ret); 228 } 229 230 if (check_with_pad(packet.options, output_data, sizeof(output_data), 231 pad) != 0) { 232 atf_tc_fail("input_data2: expected output does not match"); 233 } 234 } 235 236 { 237 /* Verify that oversized packet filled with long options does not 238 * cause overrun */ 239 240 /* We borrowed this union from discover.c:got_one() */ 241 union { 242 unsigned char packbuf [4095]; /* Packet input buffer. 243 * Must be as large as largest 244 * possible MTU. */ 245 struct dhcp_packet packet; 246 } u; 247 248 unsigned char input_data[] = { 249 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 250 0x52, 0x00, /* Relay Agent Option, len = 0 */ 251 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 252 0x43, 0x00 /* Opt 0x43, len = 0 */ 253 }; 254 255 /* We'll pad it 0xFE, that way wherever we hit for "length" we'll 256 * have length of 254. Increasing the odds we'll push over the end. */ 257 unsigned char pad = 0xFE; 258 memset(u.packbuf, pad, 4095); 259 260 len = set_packet_options(&u.packet, input_data, sizeof(input_data), pad); 261 if (len == 0) { 262 atf_tc_fail("overrun: set_packet_options failed"); 263 } 264 265 /* Enable option stripping by setting add_agent_options = 1 */ 266 add_agent_options = 1; 267 268 /* strip_relay_agent_options() should detect the overrun and return 0 */ 269 ret = strip_relay_agent_options(&ifaces, &matched, &u.packet, 4095); 270 if (ret != 0) { 271 atf_tc_fail("overrun expected return len = 0, we got %d", ret); 272 } 273 } 274 } 275 276 ATF_TC(add_relay_agent_options_test); 277 278 ATF_TC_HEAD(add_relay_agent_options_test, tc) { 279 atf_tc_set_md_var(tc, "descr", "tests agent_relay-agent_options"); 280 } 281 282 /* This Test exercises add_relay_agent_options() function */ 283 ATF_TC_BODY(add_relay_agent_options_test, tc) { 284 285 struct interface_info ifc; 286 struct dhcp_packet packet; 287 unsigned len; 288 int ret; 289 struct in_addr giaddr; 290 291 giaddr.s_addr = inet_addr("192.0.1.1"); 292 293 u_int8_t circuit_id[] = { 0x01,0x02,0x03,0x04,0x05,0x06 }; 294 u_int8_t remote_id[] = { 0x11,0x22,0x33,0x44,0x55,0x66 }; 295 296 memset(&ifc, 0x0, sizeof(ifc)); 297 ifc.circuit_id = circuit_id; 298 ifc.circuit_id_len = sizeof(circuit_id); 299 ifc.remote_id = remote_id; 300 ifc.remote_id_len = sizeof(remote_id); 301 302 memset(&packet, 0x0, sizeof(packet)); 303 len = 0; 304 305 /* Make sure an empty packet is harmless */ 306 ret = add_relay_agent_options(&ifc, &packet, len, giaddr); 307 if (ret != 0) { 308 atf_tc_fail("empty packet failed"); 309 } 310 311 { 312 /* 313 * Uses valid input option data to verify that: 314 * - When add_agent_options is false, the output option data is the 315 * the same as the input option data (i.e. nothing removed) 316 * - When add_agent_options is true, the relay agent option has 317 * been removed from the output option data 318 * - When add_agent_options is true, 0 length relay agent option has 319 * been removed from the output option data 320 * 321 */ 322 323 unsigned char input_data[] = { 324 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 325 0x52, 0x08, 0x01, 0x06, 0x65, /* Relay Agent Option, len = 8 */ 326 0x6e, 0x70, 0x30, 0x73, 0x4f, 327 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 328 0x43, 0x00 /* Opt 0x43, len = 0 */ 329 }; 330 331 unsigned char input_data2[] = { 332 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 333 0x52, 0x00, /* Relay Agent Option, len = 0 */ 334 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 335 0x43, 0x00 /* Opt 0x43, len = 0 */ 336 }; 337 338 unsigned char output_data[] = { 339 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ 340 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */ 341 0x43, 0x00, /* Opt 0x43, len = 0 */ 342 0x52, 0x10, /* Relay Agent, len = 16 */ 343 0x1, 0x6, /* Circuit id, len = 6 */ 344 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 345 0x2, 0x6, /* Remete id, len = 6 */ 346 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xff 347 }; 348 349 unsigned char pad = 0x0; 350 len = set_packet_options(&packet, input_data, sizeof(input_data), pad); 351 if (len == 0) { 352 atf_tc_fail("input_data: set_packet_options failed"); 353 } 354 355 /* When add_agent_options = 0, no change should occur */ 356 add_agent_options = 0; 357 ret = add_relay_agent_options(&ifc, &packet, len, giaddr); 358 if (ret != len) { 359 atf_tc_fail("expected unchanged len %d, returned %d", len, ret); 360 } 361 362 if (check_with_pad(packet.options, input_data, sizeof(input_data), 363 pad) != 0) { 364 atf_tc_fail("expected unchanged data, does not match"); 365 } 366 367 /* When add_agent_options = 1, it should remove the eight byte 368 * relay agent option. */ 369 add_agent_options = 1; 370 371 /* Because the inbound option data is less than the BOOTP_MIN_LEN, 372 * the output data should get padded out to BOOTP_MIN_LEN 373 * padded out to BOOTP_MIN_LEN */ 374 ret = add_relay_agent_options(&ifc, &packet, len, giaddr); 375 if (ret != BOOTP_MIN_LEN) { 376 atf_tc_fail("input_data: len of %d, returned %d", 377 BOOTP_MIN_LEN, ret); 378 } 379 380 if (check_with_pad(packet.options, output_data, sizeof(output_data), 381 pad) != 0) { 382 atf_tc_fail("input_data: expected data does not match"); 383 } 384 385 /* Now let's repeat it with a relay agent option 0 bytes in length. */ 386 len = set_packet_options(&packet, input_data2, sizeof(input_data2), 387 pad); 388 if (len == 0) { 389 atf_tc_fail("input_data2 set_packet_options failed"); 390 } 391 392 /* Because the inbound option data is less than the BOOTP_MIN_LEN, 393 * the output data should get padded out to BOOTP_MIN_LEN 394 * padded out to BOOTP_MIN_LEN */ 395 ret = add_relay_agent_options(&ifc, &packet, len, giaddr); 396 if (ret != BOOTP_MIN_LEN) { 397 atf_tc_fail("input_data2: len of %d, returned %d", 398 BOOTP_MIN_LEN, ret); 399 } 400 401 if (check_with_pad(packet.options, output_data, sizeof(output_data), 402 pad) != 0) { 403 atf_tc_fail("input_data2: expected output does not match"); 404 } 405 } 406 } 407 408 ATF_TC(gwaddr_override_test); 409 410 ATF_TC_HEAD(gwaddr_override_test, tc) { 411 atf_tc_set_md_var(tc, "descr", "tests that gateway addr (giaddr) field can be overridden"); 412 } 413 414 extern isc_boolean_t use_fake_gw; 415 extern struct in_addr gw; 416 417 /* This basic test checks if the new gwaddr override (-g) option is disabled by default */ 418 ATF_TC_BODY(gwaddr_override_test, tc) { 419 420 if (use_fake_gw == ISC_TRUE) { 421 atf_tc_fail("the gwaddr override should be disabled by default"); 422 } 423 char txt[16] = {0}; 424 inet_ntop(AF_INET, &gw, txt, sizeof(txt)); 425 if (strncmp(txt, "0.0.0.0", 8) != 0) { 426 atf_tc_fail("the default gwaddr override value should be 0.0.0.0, but is %s", txt); 427 } 428 } 429 430 ATF_TP_ADD_TCS(tp) { 431 ATF_TP_ADD_TC(tp, strip_relay_agent_options_test); 432 ATF_TP_ADD_TC(tp, add_relay_agent_options_test); 433 ATF_TP_ADD_TC(tp, gwaddr_override_test); 434 435 return (atf_no_error()); 436 } 437