1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include <net/if_arp.h>
9 #include "alloc-util.h"
10 #include "dhcp-internal.h"
11 #include "dhcp-protocol.h"
13 #include "memory-util.h"
28 static bool verbose
= false;
30 static struct option_desc option_tests
[] = {
31 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
32 { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
33 SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_ACK
}, 12, true, },
34 { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
35 { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
36 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
37 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
38 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
39 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
41 { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_OFFER
,
42 42, 3, 0, 0, 0 }, 8, true, },
43 { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
46 { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_NAK
}, 8,
47 { SD_DHCP_OPTION_OVERLOAD
, 1, DHCP_OVERLOAD_FILE
}, 3, true, },
49 { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE
, 1, DHCP_ACK
}, 9,
50 { 222, 3, 1, 2, 3 }, 5,
51 { SD_DHCP_OPTION_OVERLOAD
, 1,
52 DHCP_OVERLOAD_FILE
|DHCP_OVERLOAD_SNAME
}, 3, true, },
55 static const char *dhcp_type(int type
) {
58 return "DHCPDISCOVER";
76 static void test_invalid_buffer_length(void) {
79 assert_se(dhcp_option_parse(&message
, 0, NULL
, NULL
, NULL
) == -EINVAL
);
80 assert_se(dhcp_option_parse(&message
, sizeof(DHCPMessage
) - 1, NULL
, NULL
, NULL
) == -EINVAL
);
83 static void test_message_init(void) {
84 _cleanup_free_ DHCPMessage
*message
= NULL
;
85 size_t optlen
= 4, optoffset
;
86 size_t len
= sizeof(DHCPMessage
) + optlen
;
89 message
= malloc0(len
);
91 assert_se(dhcp_message_init(message
, BOOTREQUEST
, 0x12345678,
92 DHCP_DISCOVER
, ARPHRD_ETHER
, optlen
, &optoffset
) >= 0);
94 assert_se(message
->xid
== htobe32(0x12345678));
95 assert_se(message
->op
== BOOTREQUEST
);
97 magic
= (uint8_t*)&message
->magic
;
99 assert_se(magic
[0] == 99);
100 assert_se(magic
[1] == 130);
101 assert_se(magic
[2] == 83);
102 assert_se(magic
[3] == 99);
104 assert_se(dhcp_option_parse(message
, len
, NULL
, NULL
, NULL
) >= 0);
107 static DHCPMessage
*create_message(uint8_t *options
, uint16_t optlen
,
108 uint8_t *file
, uint8_t filelen
,
109 uint8_t *sname
, uint8_t snamelen
) {
110 DHCPMessage
*message
;
111 size_t len
= sizeof(DHCPMessage
) + optlen
;
113 message
= malloc0(len
);
116 memcpy_safe(&message
->options
, options
, optlen
);
117 memcpy_safe(&message
->file
, file
, filelen
);
118 memcpy_safe(&message
->sname
, sname
, snamelen
);
123 static void test_ignore_opts(uint8_t *descoption
, int *descpos
, int *desclen
) {
124 assert(*descpos
>= 0);
126 while (*descpos
< *desclen
) {
127 switch(descoption
[*descpos
]) {
128 case SD_DHCP_OPTION_PAD
:
132 case SD_DHCP_OPTION_MESSAGE_TYPE
:
133 case SD_DHCP_OPTION_OVERLOAD
:
143 static int test_options_cb(uint8_t code
, uint8_t len
, const void *option
, void *userdata
) {
144 struct option_desc
*desc
= userdata
;
145 uint8_t *descoption
= NULL
;
146 int *desclen
= NULL
, *descpos
= NULL
;
151 assert_se((!desc
&& !code
&& !len
) || desc
);
156 assert_se(code
!= SD_DHCP_OPTION_PAD
);
157 assert_se(code
!= SD_DHCP_OPTION_END
);
158 assert_se(code
!= SD_DHCP_OPTION_MESSAGE_TYPE
);
159 assert_se(code
!= SD_DHCP_OPTION_OVERLOAD
);
161 while (desc
->pos
>= 0 || desc
->filepos
>= 0 || desc
->snamepos
>= 0) {
163 if (desc
->pos
>= 0) {
164 descoption
= &desc
->options
[0];
165 desclen
= &desc
->len
;
166 descpos
= &desc
->pos
;
167 } else if (desc
->filepos
>= 0) {
168 descoption
= &desc
->file
[0];
169 desclen
= &desc
->filelen
;
170 descpos
= &desc
->filepos
;
171 } else if (desc
->snamepos
>= 0) {
172 descoption
= &desc
->sname
[0];
173 desclen
= &desc
->snamelen
;
174 descpos
= &desc
->snamepos
;
177 assert_se(descoption
&& desclen
&& descpos
);
180 test_ignore_opts(descoption
, descpos
, desclen
);
182 if (*descpos
< *desclen
)
185 if (*descpos
== *desclen
)
190 assert_se(*descpos
!= -1);
192 optcode
= descoption
[*descpos
];
193 optlen
= descoption
[*descpos
+ 1];
196 printf("DHCP code %2d(%2d) len %2d(%2d) ", code
, optcode
,
199 assert_se(code
== optcode
);
200 assert_se(len
== optlen
);
202 for (i
= 0; i
< len
; i
++) {
205 printf("0x%02x(0x%02x) ", ((uint8_t*) option
)[i
],
206 descoption
[*descpos
+ 2 + i
]);
208 assert_se(((uint8_t*) option
)[i
] == descoption
[*descpos
+ 2 + i
]);
214 *descpos
+= optlen
+ 2;
216 test_ignore_opts(descoption
, descpos
, desclen
);
218 if (desc
->pos
!= -1 && desc
->pos
== desc
->len
)
221 if (desc
->filepos
!= -1 && desc
->filepos
== desc
->filelen
)
224 if (desc
->snamepos
!= -1 && desc
->snamepos
== desc
->snamelen
)
230 static void test_options(struct option_desc
*desc
) {
231 uint8_t *options
= NULL
;
232 uint8_t *file
= NULL
;
233 uint8_t *sname
= NULL
;
238 _cleanup_free_ DHCPMessage
*message
= NULL
;
242 file
= &desc
->file
[0];
243 filelen
= desc
->filelen
;
247 sname
= &desc
->sname
[0];
248 snamelen
= desc
->snamelen
;
252 options
= &desc
->options
[0];
256 message
= create_message(options
, optlen
, file
, filelen
,
259 buflen
= sizeof(DHCPMessage
) + optlen
;
262 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, NULL
, NULL
)) == -ENOMSG
);
263 } else if (desc
->success
) {
264 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, desc
, NULL
)) >= 0);
265 assert_se(desc
->pos
== -1 && desc
->filepos
== -1 && desc
->snamepos
== -1);
267 assert_se((res
= dhcp_option_parse(message
, buflen
, test_options_cb
, desc
, NULL
)) < 0);
270 printf("DHCP type %s\n", dhcp_type(res
));
273 static uint8_t options
[64] = {
277 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
279 55, 3, 0x51, 0x52, 0x53,
280 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
284 static void test_option_set(void) {
285 _cleanup_free_ DHCPMessage
*result
= NULL
;
286 size_t offset
= 0, len
, pos
;
289 result
= malloc0(sizeof(DHCPMessage
) + 11);
292 result
->options
[0] = 'A';
293 result
->options
[1] = 'B';
294 result
->options
[2] = 'C';
295 result
->options
[3] = 'D';
297 assert_se(dhcp_option_append(result
, 0, &offset
, 0, SD_DHCP_OPTION_PAD
,
298 0, NULL
) == -ENOBUFS
);
299 assert_se(offset
== 0);
302 assert_se(dhcp_option_append(result
, 5, &offset
, 0, SD_DHCP_OPTION_PAD
,
303 0, NULL
) == -ENOBUFS
);
304 assert_se(offset
== 4);
305 assert_se(dhcp_option_append(result
, 6, &offset
, 0, SD_DHCP_OPTION_PAD
,
307 assert_se(offset
== 5);
311 while (pos
< len
&& options
[pos
] != SD_DHCP_OPTION_END
) {
312 assert_se(dhcp_option_append(result
, len
, &offset
, DHCP_OVERLOAD_SNAME
,
315 &options
[pos
+ 2]) >= 0);
317 if (options
[pos
] == SD_DHCP_OPTION_PAD
)
320 pos
+= 2 + options
[pos
+ 1];
323 assert_se(offset
== pos
);
326 for (i
= 0; i
< 9; i
++) {
328 printf("%2u: 0x%02x(0x%02x) (options)\n", i
, result
->options
[i
],
330 assert_se(result
->options
[i
] == options
[i
]);
334 printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result
->options
[9],
337 assert_se(result
->options
[9] == SD_DHCP_OPTION_END
);
340 printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result
->options
[10],
343 assert_se(result
->options
[10] == SD_DHCP_OPTION_PAD
);
345 for (i
= 0; i
< pos
- 8; i
++) {
347 printf("%2u: 0x%02x(0x%02x) (sname)\n", i
, result
->sname
[i
],
349 assert_se(result
->sname
[i
] == options
[i
+ 9]);
356 int main(int argc
, char *argv
[]) {
359 test_invalid_buffer_length();
364 for (i
= 0; i
< ELEMENTSOF(option_tests
); i
++)
365 test_options(&option_tests
[i
]);