xref: /netbsd-src/external/mpl/dhcp/dist/relay/tests/relay_unittests.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
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 */
set_packet_options(struct dhcp_packet * packet,unsigned char * data,unsigned len,unsigned char pad)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 */
check_with_pad(unsigned char * actual_options,unsigned char * expected_data,unsigned data_len,unsigned char pad)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 
ATF_TC_HEAD(strip_relay_agent_options_test,tc)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 */
ATF_TC_BODY(strip_relay_agent_options_test,tc)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 
ATF_TC_HEAD(add_relay_agent_options_test,tc)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 */
ATF_TC_BODY(add_relay_agent_options_test,tc)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 
ATF_TC_HEAD(gwaddr_override_test,tc)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 */
ATF_TC_BODY(gwaddr_override_test,tc)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 
ATF_TP_ADD_TCS(tp)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