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