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