1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <net/if_arp.h>
7 #include "alloc-util.h"
8 #include "dhcp-option.h"
9 #include "dhcp-packet.h"
10 #include "memory-util.h"
26 static bool verbose
= false;
28 static struct option_desc option_tests
[] = {
29 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
30 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
31 SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_ACK
}, 12, true, },
32 { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
33 { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
34 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
35 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
36 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
37 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
39 { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_OFFER
,
40 42, 3, 0, 0, 0 }, 8, true, },
41 { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
44 { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_NAK
}, 8,
45 { SD_DHCP_OPTION_OVERLOAD
, 1, DHCP_OVERLOAD_FILE
}, 3, true, },
47 { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_ACK
}, 9,
48 { 222, 3, 1, 2, 3 }, 5,
49 { SD_DHCP_OPTION_OVERLOAD
, 1,
50 DHCP_OVERLOAD_FILE
|DHCP_OVERLOAD_SNAME
}, 3, true, },
53 static const char *dhcp_type(int type
) {
56 return "DHCPDISCOVER";
74 static void test_invalid_buffer_length(void) {
77 assert_se(dhcp_option_parse(&message
, 0, NULL
, NULL
, NULL
) == -EINVAL
);
78 assert_se(dhcp_option_parse(&message
, sizeof(DHCPMessage
) - 1, NULL
, NULL
, NULL
) == -EINVAL
);
81 static void test_message_init(void) {
82 _cleanup_free_ DHCPMessage
*message
= NULL
;
83 size_t optlen
= 4, optoffset
;
84 size_t len
= sizeof(DHCPMessage
) + optlen
;
87 message
= malloc0(len
);
89 assert_se(dhcp_message_init(message
, BOOTREQUEST
, 0x12345678,
90 ARPHRD_ETHER
, ETH_ALEN
, (uint8_t[16]){}, DHCP_DISCOVER
,
91 optlen
, &optoffset
) >= 0);
93 assert_se(message
->xid
== htobe32(0x12345678));
94 assert_se(message
->op
== BOOTREQUEST
);
96 magic
= (uint8_t*)&message
->magic
;
98 assert_se(magic
[0] == 99);
99 assert_se(magic
[1] == 130);
100 assert_se(magic
[2] == 83);
101 assert_se(magic
[3] == 99);
103 assert_se(dhcp_option_parse(message
, len
, NULL
, NULL
, NULL
) >= 0);
106 static DHCPMessage
*create_message(uint8_t *options
, uint16_t optlen
,
107 uint8_t *file
, uint8_t filelen
,
108 uint8_t *sname
, uint8_t snamelen
) {
109 DHCPMessage
*message
;
110 size_t len
= sizeof(DHCPMessage
) + optlen
;
112 message
= malloc0(len
);
115 memcpy_safe(&message
->options
, options
, optlen
);
116 memcpy_safe(&message
->file
, file
, filelen
);
117 memcpy_safe(&message
->sname
, sname
, snamelen
);
122 static void test_ignore_opts(uint8_t *descoption
, int *descpos
, int *desclen
) {
123 assert_se(*descpos
>= 0);
125 while (*descpos
< *desclen
) {
126 switch (descoption
[*descpos
]) {
127 case SD_DHCP_OPTION_PAD
:
131 case SD_DHCP_OPTION_MESSAGE_TYPE
:
132 case SD_DHCP_OPTION_OVERLOAD
:
142 static int test_options_cb(uint8_t code
, uint8_t len
, const void *option
, void *userdata
) {
143 struct option_desc
*desc
= userdata
;
144 uint8_t *descoption
= NULL
;
145 int *desclen
= NULL
, *descpos
= NULL
;
149 assert_se((!desc
&& !code
&& !len
) || desc
);
154 assert_se(code
!= SD_DHCP_OPTION_PAD
);
155 assert_se(code
!= SD_DHCP_OPTION_END
);
156 assert_se(code
!= SD_DHCP_OPTION_MESSAGE_TYPE
);
157 assert_se(code
!= SD_DHCP_OPTION_OVERLOAD
);
159 while (desc
->pos
>= 0 || desc
->filepos
>= 0 || desc
->snamepos
>= 0) {
161 if (desc
->pos
>= 0) {
162 descoption
= &desc
->options
[0];
163 desclen
= &desc
->len
;
164 descpos
= &desc
->pos
;
165 } else if (desc
->filepos
>= 0) {
166 descoption
= &desc
->file
[0];
167 desclen
= &desc
->filelen
;
168 descpos
= &desc
->filepos
;
169 } else if (desc
->snamepos
>= 0) {
170 descoption
= &desc
->sname
[0];
171 desclen
= &desc
->snamelen
;
172 descpos
= &desc
->snamepos
;
175 assert_se(descoption
&& desclen
&& descpos
);
178 test_ignore_opts(descoption
, descpos
, desclen
);
180 if (*descpos
< *desclen
)
183 if (*descpos
== *desclen
)
188 assert_se(*descpos
!= -1);
190 optcode
= descoption
[*descpos
];
191 optlen
= descoption
[*descpos
+ 1];
194 printf("DHCP code %2d(%2d) len %2d(%2d) ", code
, optcode
,
197 assert_se(code
== optcode
);
198 assert_se(len
== optlen
);
200 for (unsigned i
= 0; i
< len
; i
++) {
202 printf("0x%02x(0x%02x) ",
203 ((uint8_t*) option
)[i
],
204 descoption
[*descpos
+ 2 + i
]);
206 assert_se(((uint8_t*) option
)[i
] == descoption
[*descpos
+ 2 + i
]);
212 *descpos
+= optlen
+ 2;
214 test_ignore_opts(descoption
, descpos
, desclen
);
216 if (desc
->pos
!= -1 && desc
->pos
== desc
->len
)
219 if (desc
->filepos
!= -1 && desc
->filepos
== desc
->filelen
)
222 if (desc
->snamepos
!= -1 && desc
->snamepos
== desc
->snamelen
)
228 static void test_options(struct option_desc
*desc
) {
229 uint8_t *options
= NULL
;
230 uint8_t *file
= NULL
;
231 uint8_t *sname
= NULL
;
236 _cleanup_free_ DHCPMessage
*message
= NULL
;
240 file
= &desc
->file
[0];
241 filelen
= desc
->filelen
;
245 sname
= &desc
->sname
[0];
246 snamelen
= desc
->snamelen
;
250 options
= &desc
->options
[0];
254 message
= create_message(options
, optlen
, file
, filelen
,
257 buflen
= sizeof(DHCPMessage
) + optlen
;
260 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, NULL
, NULL
)) == -ENOMSG
);
261 } else if (desc
->success
) {
262 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, desc
, NULL
)) >= 0);
263 assert_se(desc
->pos
== -1 && desc
->filepos
== -1 && desc
->snamepos
== -1);
265 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, desc
, NULL
)) < 0);
268 printf("DHCP type %s\n", dhcp_type(res
));
271 static void test_option_removal(struct option_desc
*desc
) {
272 _cleanup_free_ DHCPMessage
*message
= create_message(&desc
->options
[0], desc
->len
, NULL
, 0, NULL
, 0);
274 assert_se(dhcp_option_parse(message
, sizeof(DHCPMessage
) + desc
->len
, NULL
, NULL
, NULL
) >= 0);
275 assert_se((desc
->len
= dhcp_option_remove_option(message
->options
, desc
->len
, SD_DHCP_OPTION_MESSAGE_TYPE
)) >= 0);
276 assert_se(dhcp_option_parse(message
, sizeof(DHCPMessage
) + desc
->len
, NULL
, NULL
, NULL
) < 0);
279 static uint8_t the_options
[64] = {
283 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
285 55, 3, 0x51, 0x52, 0x53,
286 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
290 static void test_option_set(void) {
291 _cleanup_free_ DHCPMessage
*result
= NULL
;
292 size_t offset
= 0, len
, pos
;
294 result
= malloc0(sizeof(DHCPMessage
) + 11);
297 result
->options
[0] = 'A';
298 result
->options
[1] = 'B';
299 result
->options
[2] = 'C';
300 result
->options
[3] = 'D';
302 assert_se(dhcp_option_append(result
, 0, &offset
, 0, SD_DHCP_OPTION_PAD
,
303 0, NULL
) == -ENOBUFS
);
304 assert_se(offset
== 0);
307 assert_se(dhcp_option_append(result
, 5, &offset
, 0, SD_DHCP_OPTION_PAD
,
308 0, NULL
) == -ENOBUFS
);
309 assert_se(offset
== 4);
310 assert_se(dhcp_option_append(result
, 6, &offset
, 0, SD_DHCP_OPTION_PAD
,
312 assert_se(offset
== 5);
316 while (pos
< len
&& the_options
[pos
] != SD_DHCP_OPTION_END
) {
317 assert_se(dhcp_option_append(result
, len
, &offset
, DHCP_OVERLOAD_SNAME
,
319 the_options
[pos
+ 1],
320 &the_options
[pos
+ 2]) >= 0);
322 if (the_options
[pos
] == SD_DHCP_OPTION_PAD
)
325 pos
+= 2 + the_options
[pos
+ 1];
328 assert_se(offset
== pos
);
331 for (unsigned i
= 0; i
< 9; i
++) {
333 printf("%2u: 0x%02x(0x%02x) (options)\n", i
, result
->options
[i
],
335 assert_se(result
->options
[i
] == the_options
[i
]);
339 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result
->options
[9],
340 (unsigned) SD_DHCP_OPTION_END
);
342 assert_se(result
->options
[9] == SD_DHCP_OPTION_END
);
345 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result
->options
[10],
346 (unsigned) SD_DHCP_OPTION_PAD
);
348 assert_se(result
->options
[10] == SD_DHCP_OPTION_PAD
);
350 for (unsigned i
= 0; i
< pos
- 8; i
++) {
352 printf("%2u: 0x%02x(0x%02x) (sname)\n", i
, result
->sname
[i
],
354 assert_se(result
->sname
[i
] == the_options
[i
+ 9]);
361 int main(int argc
, char *argv
[]) {
362 test_setup_logging(LOG_DEBUG
);
364 test_invalid_buffer_length();
369 FOREACH_ELEMENT(desc
, option_tests
)
374 FOREACH_ELEMENT(desc
, option_tests
) {
375 if (!desc
->success
|| desc
->snamelen
> 0 || desc
->filelen
> 0)
377 test_option_removal(desc
);