1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 Copyright (C) 2014 Tom Gundersen
10 #include <net/ethernet.h>
11 #include <net/if_arp.h>
14 #include "dhcp-internal.h"
15 #include "dhcp-protocol.h"
17 #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
19 int dhcp_message_init(DHCPMessage
*message
, uint8_t op
, uint32_t xid
,
20 uint8_t type
, uint16_t arp_type
, size_t optlen
,
25 assert(IN_SET(op
, BOOTREQUEST
, BOOTREPLY
));
26 assert(IN_SET(arp_type
, ARPHRD_ETHER
, ARPHRD_INFINIBAND
));
29 message
->htype
= arp_type
;
30 message
->hlen
= (arp_type
== ARPHRD_ETHER
) ? ETHER_ADDR_LEN
: 0;
31 message
->xid
= htobe32(xid
);
32 message
->magic
= htobe32(DHCP_MAGIC_COOKIE
);
34 r
= dhcp_option_append(message
, optlen
, &offset
, 0,
35 SD_DHCP_OPTION_MESSAGE_TYPE
, 1, &type
);
44 uint16_t dhcp_packet_checksum(uint8_t *buf
, size_t len
) {
45 uint64_t *buf_64
= (uint64_t*)buf
;
46 uint64_t *end_64
= buf_64
+ (len
/ sizeof(uint64_t));
51 while (buf_64
< end_64
) {
54 /* wrap around in one's complement */
60 if (len
% sizeof(uint64_t)) {
61 /* If the buffer is not aligned to 64-bit, we need
62 to zero-pad the last few bytes and add them in */
63 uint64_t buf_tail
= 0;
65 memcpy(&buf_tail
, buf_64
, len
% sizeof(uint64_t));
74 sum
= (sum
& 0xffff) + (sum
>> 16);
79 void dhcp_packet_append_ip_headers(DHCPPacket
*packet
, be32_t source_addr
,
80 uint16_t source_port
, be32_t destination_addr
,
81 uint16_t destination_port
, uint16_t len
) {
82 packet
->ip
.version
= IPVERSION
;
83 packet
->ip
.ihl
= DHCP_IP_SIZE
/ 4;
84 packet
->ip
.tot_len
= htobe16(len
);
86 packet
->ip
.tos
= IPTOS_CLASS_CS6
;
88 packet
->ip
.protocol
= IPPROTO_UDP
;
89 packet
->ip
.saddr
= source_addr
;
90 packet
->ip
.daddr
= destination_addr
;
92 packet
->udp
.source
= htobe16(source_port
);
93 packet
->udp
.dest
= htobe16(destination_port
);
95 packet
->udp
.len
= htobe16(len
- DHCP_IP_SIZE
);
97 packet
->ip
.check
= packet
->udp
.len
;
98 packet
->udp
.check
= dhcp_packet_checksum((uint8_t*)&packet
->ip
.ttl
, len
- 8);
100 packet
->ip
.ttl
= IPDEFTTL
;
101 packet
->ip
.check
= 0;
102 packet
->ip
.check
= dhcp_packet_checksum((uint8_t*)&packet
->ip
, DHCP_IP_SIZE
);
105 int dhcp_packet_verify_headers(DHCPPacket
*packet
, size_t len
, bool checksum
, uint16_t port
) {
112 if (packet
->ip
.version
!= IPVERSION
) {
113 log_debug("ignoring packet: not IPv4");
117 if (packet
->ip
.ihl
< 5) {
118 log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
123 hdrlen
= packet
->ip
.ihl
* 4;
125 log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
126 "smaller than minimum (20 bytes)", hdrlen
);
131 log_debug("ignoring packet: packet (%zu bytes) "
132 "smaller than expected (%zu) by IP header", len
,
139 if (packet
->ip
.protocol
!= IPPROTO_UDP
) {
140 log_debug("ignoring packet: not UDP");
144 if (len
< hdrlen
+ be16toh(packet
->udp
.len
)) {
145 log_debug("ignoring packet: packet (%zu bytes) "
146 "smaller than expected (%zu) by UDP header", len
,
147 hdrlen
+ be16toh(packet
->udp
.len
));
151 if (be16toh(packet
->udp
.dest
) != port
) {
152 log_debug("ignoring packet: to port %u, which "
153 "is not the DHCP client port (%u)",
154 be16toh(packet
->udp
.dest
), port
);
158 /* checksums - computing these is relatively expensive, so only do it
159 if all the other checks have passed
162 if (dhcp_packet_checksum((uint8_t*)&packet
->ip
, hdrlen
)) {
163 log_debug("ignoring packet: invalid IP checksum");
167 if (checksum
&& packet
->udp
.check
) {
168 packet
->ip
.check
= packet
->udp
.len
;
171 if (dhcp_packet_checksum((uint8_t*)&packet
->ip
.ttl
,
172 be16toh(packet
->udp
.len
) + 12)) {
173 log_debug("ignoring packet: invalid UDP checksum");