]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/dhcp-packet.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-packet.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
cf597f65
TG
2/***
3 This file is part of systemd.
4
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
6 Copyright (C) 2014 Tom Gundersen
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
cf597f65 22#include <errno.h>
cf597f65
TG
23#include <net/ethernet.h>
24#include <net/if_arp.h>
cf0fbc49 25#include <string.h>
cf597f65 26
cf597f65 27#include "dhcp-internal.h"
cf0fbc49 28#include "dhcp-protocol.h"
cf597f65
TG
29
30#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
31
32int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
76253e73
DW
33 uint8_t type, uint16_t arp_type, size_t optlen,
34 size_t *optoffset) {
20b958bf
TG
35 size_t offset = 0;
36 int r;
cf597f65 37
3742095b
AR
38 assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
39 assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
0a1b6da8 40
cf597f65 41 message->op = op;
76253e73
DW
42 message->htype = arp_type;
43 message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
cf597f65 44 message->xid = htobe32(xid);
3b7ca119 45 message->magic = htobe32(DHCP_MAGIC_COOKIE);
cf597f65 46
04b28be1 47 r = dhcp_option_append(message, optlen, &offset, 0,
22805d92 48 SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
20b958bf
TG
49 if (r < 0)
50 return r;
cf597f65 51
20b958bf 52 *optoffset = offset;
cf597f65
TG
53
54 return 0;
55}
56
0bbc2c1f
TG
57uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
58 uint64_t *buf_64 = (uint64_t*)buf;
59 uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
d5761274
TG
60 uint64_t sum = 0;
61
0bbc2c1f
TG
62 /* See RFC1071 */
63
d5761274
TG
64 while (buf_64 < end_64) {
65 sum += *buf_64;
66 if (sum < *buf_64)
0bbc2c1f 67 /* wrap around in one's complement */
d5761274
TG
68 sum++;
69
313cefa1 70 buf_64++;
d5761274
TG
71 }
72
0bbc2c1f
TG
73 if (len % sizeof(uint64_t)) {
74 /* If the buffer is not aligned to 64-bit, we need
75 to zero-pad the last few bytes and add them in */
76 uint64_t buf_tail = 0;
cf597f65 77
0bbc2c1f 78 memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
cf597f65 79
0bbc2c1f
TG
80 sum += buf_tail;
81 if (sum < buf_tail)
82 /* wrap around */
d5761274 83 sum++;
cf597f65
TG
84 }
85
86 while (sum >> 16)
87 sum = (sum & 0xffff) + (sum >> 16);
88
89 return ~sum;
90}
91
63edaa62
TG
92void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
93 uint16_t source_port, be32_t destination_addr,
94 uint16_t destination_port, uint16_t len) {
cf597f65
TG
95 packet->ip.version = IPVERSION;
96 packet->ip.ihl = DHCP_IP_SIZE / 4;
97 packet->ip.tot_len = htobe16(len);
98
85923f79
TG
99 packet->ip.tos = IPTOS_CLASS_CS6;
100
cf597f65 101 packet->ip.protocol = IPPROTO_UDP;
63edaa62
TG
102 packet->ip.saddr = source_addr;
103 packet->ip.daddr = destination_addr;
cf597f65 104
63edaa62
TG
105 packet->udp.source = htobe16(source_port);
106 packet->udp.dest = htobe16(destination_port);
cf597f65
TG
107
108 packet->udp.len = htobe16(len - DHCP_IP_SIZE);
109
110 packet->ip.check = packet->udp.len;
0bbc2c1f 111 packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
cf597f65
TG
112
113 packet->ip.ttl = IPDEFTTL;
114 packet->ip.check = 0;
0bbc2c1f 115 packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
cf597f65
TG
116}
117
9faed222 118int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
cf597f65
TG
119 size_t hdrlen;
120
5266a81e
TG
121 assert(packet);
122
06b44be7 123 /* IP */
cf597f65 124
6e34949d 125 if (packet->ip.version != IPVERSION) {
aa6fc9b8 126 log_debug("ignoring packet: not IPv4");
6e34949d
TG
127 return -EINVAL;
128 }
129
06b44be7 130 if (packet->ip.ihl < 5) {
aa6fc9b8
TG
131 log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
132 packet->ip.ihl);
cf597f65 133 return -EINVAL;
ac4f16ab 134 }
cf597f65
TG
135
136 hdrlen = packet->ip.ihl * 4;
06b44be7 137 if (hdrlen < 20) {
aa6fc9b8
TG
138 log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
139 "smaller than minimum (20 bytes)", hdrlen);
06b44be7
TG
140 return -EINVAL;
141 }
142
143 if (len < hdrlen) {
aa6fc9b8
TG
144 log_debug("ignoring packet: packet (%zu bytes) "
145 "smaller than expected (%zu) by IP header", len,
146 hdrlen);
cf597f65 147 return -EINVAL;
ac4f16ab 148 }
cf597f65 149
06b44be7
TG
150 /* UDP */
151
d454a674 152 if (packet->ip.protocol != IPPROTO_UDP) {
aa6fc9b8 153 log_debug("ignoring packet: not UDP");
d454a674
UTL
154 return -EINVAL;
155 }
156
8fa2eeac 157 if (len < hdrlen + be16toh(packet->udp.len)) {
aa6fc9b8
TG
158 log_debug("ignoring packet: packet (%zu bytes) "
159 "smaller than expected (%zu) by UDP header", len,
160 hdrlen + be16toh(packet->udp.len));
ac4f16ab
TG
161 return -EINVAL;
162 }
cf597f65 163
9faed222 164 if (be16toh(packet->udp.dest) != port) {
aa6fc9b8
TG
165 log_debug("ignoring packet: to port %u, which "
166 "is not the DHCP client port (%u)",
9faed222 167 be16toh(packet->udp.dest), port);
2ad7561f
TG
168 return -EINVAL;
169 }
170
171 /* checksums - computing these is relatively expensive, so only do it
172 if all the other checks have passed
173 */
174
0bbc2c1f 175 if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
aa6fc9b8 176 log_debug("ignoring packet: invalid IP checksum");
2ad7561f
TG
177 return -EINVAL;
178 }
179
55dab2ed 180 if (checksum && packet->udp.check) {
cf597f65
TG
181 packet->ip.check = packet->udp.len;
182 packet->ip.ttl = 0;
183
0bbc2c1f 184 if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
ac4f16ab 185 be16toh(packet->udp.len) + 12)) {
aa6fc9b8 186 log_debug("ignoring packet: invalid UDP checksum");
cf597f65 187 return -EINVAL;
ac4f16ab 188 }
cf597f65
TG
189 }
190
cf597f65
TG
191 return 0;
192}