]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/dhcp-packet.c
timesyncd: save clock to disk everytime we get an NTP fix, and bump clock at boot...
[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,
0a1b6da8 41 uint8_t type, uint8_t **opt, size_t *optlen) {
cf597f65
TG
42 int err;
43
0a1b6da8
TG
44 assert(op == BOOTREQUEST || op == BOOTREPLY);
45
cf597f65
TG
46 message->op = op;
47 message->htype = ARPHRD_ETHER;
48 message->hlen = ETHER_ADDR_LEN;
49 message->xid = htobe32(xid);
3b7ca119 50 message->magic = htobe32(DHCP_MAGIC_COOKIE);
cf597f65 51
3b7ca119 52 *opt = (uint8_t *)(message + 1);
cf597f65
TG
53
54 err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
55 &type);
56 if (err < 0)
57 return err;
58
59 return 0;
60}
61
d5761274
TG
62uint16_t dhcp_packet_checksum(void *buf, size_t len) {
63 uint64_t *buf_64 = buf;
64 uint64_t *end_64 = (uint64_t*)buf + (len / sizeof(uint64_t));
65 uint32_t *buf_32;
66 uint16_t *buf_16;
67 uint8_t *buf_8;
68 uint64_t sum = 0;
69
70 while (buf_64 < end_64) {
71 sum += *buf_64;
72 if (sum < *buf_64)
73 sum++;
74
75 buf_64 ++;
76 }
77
78 buf_32 = (uint32_t*)buf_64;
79
80 if (len & sizeof(uint32_t)) {
81 sum += *buf_32;
82 if (sum < *buf_32)
83 sum++;
84
85 buf_32 ++;
86 }
87
88 buf_16 = (uint16_t*)buf_32;
cf597f65 89
d5761274
TG
90 if (len & sizeof(uint16_t)) {
91 sum += *buf_16;
92 if (sum < *buf_16)
93 sum ++;
94
95 buf_16 ++;
96 }
cf597f65 97
d5761274 98 buf_8 = (uint8_t*)buf_16;
cf597f65 99
d5761274
TG
100 if (len & sizeof(uint8_t)) {
101 sum += *buf_8;
102 if (sum < *buf_8)
103 sum++;
cf597f65
TG
104 }
105
106 while (sum >> 16)
107 sum = (sum & 0xffff) + (sum >> 16);
108
109 return ~sum;
110}
111
63edaa62
TG
112void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
113 uint16_t source_port, be32_t destination_addr,
114 uint16_t destination_port, uint16_t len) {
cf597f65
TG
115 packet->ip.version = IPVERSION;
116 packet->ip.ihl = DHCP_IP_SIZE / 4;
117 packet->ip.tot_len = htobe16(len);
118
85923f79
TG
119 packet->ip.tos = IPTOS_CLASS_CS6;
120
cf597f65 121 packet->ip.protocol = IPPROTO_UDP;
63edaa62
TG
122 packet->ip.saddr = source_addr;
123 packet->ip.daddr = destination_addr;
cf597f65 124
63edaa62
TG
125 packet->udp.source = htobe16(source_port);
126 packet->udp.dest = htobe16(destination_port);
cf597f65
TG
127
128 packet->udp.len = htobe16(len - DHCP_IP_SIZE);
129
130 packet->ip.check = packet->udp.len;
a838c939 131 packet->udp.check = dhcp_packet_checksum(&packet->ip.ttl, len - 8);
cf597f65
TG
132
133 packet->ip.ttl = IPDEFTTL;
134 packet->ip.check = 0;
a838c939 135 packet->ip.check = dhcp_packet_checksum(&packet->ip, DHCP_IP_SIZE);
cf597f65
TG
136}
137
55dab2ed 138int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
cf597f65
TG
139 size_t hdrlen;
140
5266a81e
TG
141 assert(packet);
142
06b44be7 143 /* IP */
cf597f65 144
6e34949d 145 if (packet->ip.version != IPVERSION) {
aa6fc9b8 146 log_debug("ignoring packet: not IPv4");
6e34949d
TG
147 return -EINVAL;
148 }
149
06b44be7 150 if (packet->ip.ihl < 5) {
aa6fc9b8
TG
151 log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
152 packet->ip.ihl);
cf597f65 153 return -EINVAL;
ac4f16ab 154 }
cf597f65
TG
155
156 hdrlen = packet->ip.ihl * 4;
06b44be7 157 if (hdrlen < 20) {
aa6fc9b8
TG
158 log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
159 "smaller than minimum (20 bytes)", hdrlen);
06b44be7
TG
160 return -EINVAL;
161 }
162
163 if (len < hdrlen) {
aa6fc9b8
TG
164 log_debug("ignoring packet: packet (%zu bytes) "
165 "smaller than expected (%zu) by IP header", len,
166 hdrlen);
cf597f65 167 return -EINVAL;
ac4f16ab 168 }
cf597f65 169
06b44be7
TG
170 /* UDP */
171
d454a674 172 if (packet->ip.protocol != IPPROTO_UDP) {
aa6fc9b8 173 log_debug("ignoring packet: not UDP");
d454a674
UTL
174 return -EINVAL;
175 }
176
8fa2eeac 177 if (len < hdrlen + be16toh(packet->udp.len)) {
aa6fc9b8
TG
178 log_debug("ignoring packet: packet (%zu bytes) "
179 "smaller than expected (%zu) by UDP header", len,
180 hdrlen + be16toh(packet->udp.len));
ac4f16ab
TG
181 return -EINVAL;
182 }
cf597f65 183
2ad7561f 184 if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
aa6fc9b8
TG
185 log_debug("ignoring packet: to port %u, which "
186 "is not the DHCP client port (%u)",
187 be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
2ad7561f
TG
188 return -EINVAL;
189 }
190
191 /* checksums - computing these is relatively expensive, so only do it
192 if all the other checks have passed
193 */
194
195 if (dhcp_packet_checksum(&packet->ip, hdrlen)) {
aa6fc9b8 196 log_debug("ignoring packet: invalid IP checksum");
2ad7561f
TG
197 return -EINVAL;
198 }
199
55dab2ed 200 if (checksum && packet->udp.check) {
cf597f65
TG
201 packet->ip.check = packet->udp.len;
202 packet->ip.ttl = 0;
203
a838c939 204 if (dhcp_packet_checksum(&packet->ip.ttl,
ac4f16ab 205 be16toh(packet->udp.len) + 12)) {
aa6fc9b8 206 log_debug("ignoring packet: invalid UDP checksum");
cf597f65 207 return -EINVAL;
ac4f16ab 208 }
cf597f65
TG
209 }
210
cf597f65
TG
211 return 0;
212}