1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2013 Intel Corporation. All rights reserved.
7 #include <net/ethernet.h>
8 #include <net/if_arp.h>
11 #include "dhcp-internal.h"
12 #include "dhcp-protocol.h"
14 #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
16 int dhcp_message_init(DHCPMessage
*message
, uint8_t op
, uint32_t xid
,
17 uint8_t type
, uint16_t arp_type
, size_t optlen
,
22 assert(IN_SET(op
, BOOTREQUEST
, BOOTREPLY
));
23 assert(IN_SET(arp_type
, ARPHRD_ETHER
, ARPHRD_INFINIBAND
));
26 message
->htype
= arp_type
;
27 message
->hlen
= (arp_type
== ARPHRD_ETHER
) ? ETHER_ADDR_LEN
: 0;
28 message
->xid
= htobe32(xid
);
29 message
->magic
= htobe32(DHCP_MAGIC_COOKIE
);
31 r
= dhcp_option_append(message
, optlen
, &offset
, 0,
32 SD_DHCP_OPTION_MESSAGE_TYPE
, 1, &type
);
41 uint16_t dhcp_packet_checksum(uint8_t *buf
, size_t len
) {
42 uint64_t *buf_64
= (uint64_t*)buf
;
43 uint64_t *end_64
= buf_64
+ (len
/ sizeof(uint64_t));
48 while (buf_64
< end_64
) {
51 /* wrap around in one's complement */
57 if (len
% sizeof(uint64_t)) {
58 /* If the buffer is not aligned to 64-bit, we need
59 to zero-pad the last few bytes and add them in */
60 uint64_t buf_tail
= 0;
62 memcpy(&buf_tail
, buf_64
, len
% sizeof(uint64_t));
71 sum
= (sum
& 0xffff) + (sum
>> 16);
76 void dhcp_packet_append_ip_headers(DHCPPacket
*packet
, be32_t source_addr
,
77 uint16_t source_port
, be32_t destination_addr
,
78 uint16_t destination_port
, uint16_t len
) {
79 packet
->ip
.version
= IPVERSION
;
80 packet
->ip
.ihl
= DHCP_IP_SIZE
/ 4;
81 packet
->ip
.tot_len
= htobe16(len
);
83 packet
->ip
.tos
= IPTOS_CLASS_CS6
;
85 packet
->ip
.protocol
= IPPROTO_UDP
;
86 packet
->ip
.saddr
= source_addr
;
87 packet
->ip
.daddr
= destination_addr
;
89 packet
->udp
.source
= htobe16(source_port
);
90 packet
->udp
.dest
= htobe16(destination_port
);
92 packet
->udp
.len
= htobe16(len
- DHCP_IP_SIZE
);
94 packet
->ip
.check
= packet
->udp
.len
;
95 packet
->udp
.check
= dhcp_packet_checksum((uint8_t*)&packet
->ip
.ttl
, len
- 8);
97 packet
->ip
.ttl
= IPDEFTTL
;
99 packet
->ip
.check
= dhcp_packet_checksum((uint8_t*)&packet
->ip
, DHCP_IP_SIZE
);
102 int dhcp_packet_verify_headers(DHCPPacket
*packet
, size_t len
, bool checksum
, uint16_t port
) {
109 if (packet
->ip
.version
!= IPVERSION
)
110 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
111 "ignoring packet: not IPv4");
113 if (packet
->ip
.ihl
< 5)
114 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
115 "ignoring packet: IPv4 IHL (%u words) invalid",
118 hdrlen
= packet
->ip
.ihl
* 4;
120 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
121 "ignoring packet: IPv4 IHL (%zu bytes) "
122 "smaller than minimum (20 bytes)",
126 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
127 "ignoring packet: packet (%zu bytes) "
128 "smaller than expected (%zu) by IP header",
133 if (packet
->ip
.protocol
!= IPPROTO_UDP
)
134 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
135 "ignoring packet: not UDP");
137 if (len
< hdrlen
+ be16toh(packet
->udp
.len
))
138 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
139 "ignoring packet: packet (%zu bytes) "
140 "smaller than expected (%zu) by UDP header",
141 len
, hdrlen
+ be16toh(packet
->udp
.len
));
143 if (be16toh(packet
->udp
.dest
) != port
)
144 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
145 "ignoring packet: to port %u, which "
146 "is not the DHCP client port (%u)",
147 be16toh(packet
->udp
.dest
), port
);
149 /* checksums - computing these is relatively expensive, so only do it
150 if all the other checks have passed
153 if (dhcp_packet_checksum((uint8_t*)&packet
->ip
, hdrlen
))
154 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
155 "ignoring packet: invalid IP checksum");
157 if (checksum
&& packet
->udp
.check
) {
158 packet
->ip
.check
= packet
->udp
.len
;
161 if (dhcp_packet_checksum((uint8_t*)&packet
->ip
.ttl
,
162 be16toh(packet
->udp
.len
) + 12))
163 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
164 "ignoring packet: invalid UDP checksum");