]>
Commit | Line | Data |
---|---|---|
0a2f9a62 | 1 | /* |
49a7fb58 | 2 | * Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC") |
0a2f9a62 TM |
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. | |
429a56d7 TM |
17 | * PO Box 360 |
18 | * Newmarket, NH 03857 USA | |
0a2f9a62 TM |
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 | ||
afb66401 TM |
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; | |
0a2f9a62 TM |
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) | |
0a2f9a62 TM |
145 | * - When add_agent_options is true, 0 length relay agent option has |
146 | * been removed from the output option data | |
afb66401 TM |
147 | * - When add_agent_options is true, a relay agent option has |
148 | * been removed from the output option data | |
0a2f9a62 TM |
149 | * |
150 | */ | |
151 | ||
152 | unsigned char input_data[] = { | |
153 | 0x35, 0x00, /* DHCP_TYPE = DISCOVER */ | |
0a2f9a62 TM |
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 | ||
afb66401 TM |
192 | /* When add_agent_options = 1, it should remove the eight byte |
193 | * relay agent option. */ | |
0a2f9a62 TM |
194 | add_agent_options = 1; |
195 | ||
afb66401 TM |
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 */ | |
0a2f9a62 TM |
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 | ||
afb66401 | 210 | /* Now let's repeat it with a relay agent option 0 bytes in length. */ |
0a2f9a62 TM |
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 | ||
afb66401 TM |
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 */ | |
0a2f9a62 TM |
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 | { | |
afb66401 TM |
232 | /* Verify that oversized packet filled with long options does not |
233 | * cause overrun */ | |
0a2f9a62 TM |
234 | |
235 | /* We borrowed this union from discover.c:got_one() */ | |
236 | union { | |
237 | unsigned char packbuf [4095]; /* Packet input buffer. | |
afb66401 TM |
238 | * Must be as large as largest |
239 | * possible MTU. */ | |
0a2f9a62 TM |
240 | struct dhcp_packet packet; |
241 | } u; | |
242 | ||
243 | unsigned char input_data[] = { | |
afb66401 TM |
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 */ | |
0a2f9a62 TM |
248 | }; |
249 | ||
afb66401 TM |
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. */ | |
0a2f9a62 TM |
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 | ||
afb66401 | 260 | /* Enable option stripping by setting add_agent_options = 1 */ |
0a2f9a62 TM |
261 | add_agent_options = 1; |
262 | ||
afb66401 | 263 | /* strip_relay_agent_options() should detect the overrun and return 0 */ |
0a2f9a62 TM |
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 | ||
afb66401 | 286 | giaddr.s_addr = inet_addr("192.0.1.1"); |
0a2f9a62 TM |
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 */ | |
0a2f9a62 TM |
320 | 0x52, 0x08, 0x01, 0x06, 0x65, /* Relay Agent Option, len = 8 */ |
321 | 0x6e, 0x70, 0x30, 0x73, 0x4f, | |
0a2f9a62 TM |
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 | ||
afb66401 TM |
362 | /* When add_agent_options = 1, it should remove the eight byte |
363 | * relay agent option. */ | |
0a2f9a62 TM |
364 | add_agent_options = 1; |
365 | ||
afb66401 TM |
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 */ | |
0a2f9a62 TM |
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 | ||
afb66401 | 380 | /* Now let's repeat it with a relay agent option 0 bytes in length. */ |
0a2f9a62 TM |
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 | ||
afb66401 TM |
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 */ | |
0a2f9a62 TM |
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 | ||
acb10388 TM |
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 | ||
2b08209f | 409 | extern isc_boolean_t use_fake_gw; |
acb10388 TM |
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 | ||
2b08209f | 415 | if (use_fake_gw == ISC_TRUE) { |
acb10388 TM |
416 | atf_tc_fail("the gwaddr override should be disabled by default"); |
417 | } | |
418 | char txt[48] = {0}; | |
419 | inet_ntop(AF_INET, &gw, txt, sizeof(txt)); | |
420 | if (strncmp(txt, "0.0.0.0", 9) != 0) { | |
421 | atf_tc_fail("the default gwaddr override value should be 0.0.0.0, but is %s", txt); | |
422 | } | |
423 | } | |
424 | ||
0a2f9a62 TM |
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); | |
acb10388 | 428 | ATF_TP_ADD_TC(tp, gwaddr_override_test); |
0a2f9a62 TM |
429 | |
430 | return (atf_no_error()); | |
431 | } |