]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-dhcp/dhcp-packet.c
update TODO
[thirdparty/systemd.git] / src / libsystemd-dhcp / 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"
33#include "dhcp-lease.h"
34#include "dhcp-internal.h"
35#include "sd-dhcp-client.h"
36
37#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
38
39int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
40 uint8_t type, uint16_t secs, uint8_t **opt,
41 size_t *optlen) {
42 int err;
43
44 *opt = (uint8_t *)(message + 1);
45
46 if (*optlen < 4)
47 return -ENOBUFS;
48 *optlen -= 4;
49
50 message->op = op;
51 message->htype = ARPHRD_ETHER;
52 message->hlen = ETHER_ADDR_LEN;
53 message->xid = htobe32(xid);
54
55 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
56 refuse to issue an DHCP lease if 'secs' is set to zero */
57 message->secs = htobe16(secs);
58
59 (*opt)[0] = 0x63;
60 (*opt)[1] = 0x82;
61 (*opt)[2] = 0x53;
62 (*opt)[3] = 0x63;
63
64 *opt += 4;
65
66 err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
67 &type);
68 if (err < 0)
69 return err;
70
71 return 0;
72}
73
74static uint16_t dhcp_checksum(void *buf, int len) {
75 uint32_t sum;
76 uint16_t *check;
77 int i;
78 uint8_t *odd;
79
80 sum = 0;
81 check = buf;
82
83 for (i = 0; i < len / 2 ; i++)
84 sum += check[i];
85
86 if (len & 0x01) {
87 odd = buf;
88 sum += odd[len - 1];
89 }
90
91 while (sum >> 16)
92 sum = (sum & 0xffff) + (sum >> 16);
93
94 return ~sum;
95}
96
97void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint8_t op,
98 uint16_t len) {
99 assert(op == BOOTREQUEST || op == BOOTREPLY);
100
101 packet->ip.version = IPVERSION;
102 packet->ip.ihl = DHCP_IP_SIZE / 4;
103 packet->ip.tot_len = htobe16(len);
104
105 packet->ip.protocol = IPPROTO_UDP;
106 packet->ip.saddr = INADDR_ANY;
107 packet->ip.daddr = INADDR_BROADCAST;
108
109 switch (op) {
110 case BOOTREQUEST:
111 packet->udp.source = htobe16(DHCP_PORT_CLIENT);
112 packet->udp.dest = htobe16(DHCP_PORT_SERVER);
113 break;
114 case BOOTREPLY:
115 packet->udp.source = htobe16(DHCP_PORT_SERVER);
116 packet->udp.dest = htobe16(DHCP_PORT_CLIENT);
117 break;
118 }
119
120 packet->udp.len = htobe16(len - DHCP_IP_SIZE);
121
122 packet->ip.check = packet->udp.len;
123 packet->udp.check = dhcp_checksum(&packet->ip.ttl, len - 8);
124
125 packet->ip.ttl = IPDEFTTL;
126 packet->ip.check = 0;
127 packet->ip.check = dhcp_checksum(&packet->ip, DHCP_IP_SIZE);
128}
129
130int dhcp_packet_verify_headers(DHCPPacket *packet, uint8_t op, size_t len) {
131 size_t hdrlen;
132
133 assert(op == BOOTREQUEST || op == BOOTREPLY);
134
ac4f16ab
TG
135 if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE)) {
136 log_dhcp_client(client, "ignoring packet: packet too small");
cf597f65 137 return -EINVAL;
ac4f16ab 138 }
cf597f65
TG
139
140 hdrlen = packet->ip.ihl * 4;
ac4f16ab
TG
141 if (hdrlen < 20 || hdrlen > len) {
142 log_dhcp_client(client, "ignoring packet: header with wrong size");
cf597f65 143 return -EINVAL;
ac4f16ab 144 }
cf597f65 145
ac4f16ab
TG
146 if (dhcp_checksum(&packet->ip, hdrlen)) {
147 log_dhcp_client(client, "ignoring packet: invalid ip checksum");
cf597f65 148 return -EINVAL;
ac4f16ab
TG
149 }
150
151 if (hdrlen + be16toh(packet->udp.len) > len) {
152 log_dhcp_client(client, "ignoring packet: packet too small (udp.len=%u)",
153 be16toh(packet->udp.len));
154 return -EINVAL;
155 }
cf597f65
TG
156
157 if (packet->udp.check) {
158 packet->ip.check = packet->udp.len;
159 packet->ip.ttl = 0;
160
161 if (dhcp_checksum(&packet->ip.ttl,
ac4f16ab
TG
162 be16toh(packet->udp.len) + 12)) {
163 log_dhcp_client(client, "ignoring packet: invalid udp checksum");
cf597f65 164 return -EINVAL;
ac4f16ab 165 }
cf597f65
TG
166 }
167
ac4f16ab
TG
168 if (packet->dhcp.op != op) {
169 log_dhcp_client(client, "ignoring packet: wrong operation");
cf597f65 170 return -EINVAL;
ac4f16ab 171 }
cf597f65
TG
172
173 switch (op) {
174 case BOOTREQUEST:
175 if (be16toh(packet->udp.source) != DHCP_PORT_CLIENT ||
ac4f16ab
TG
176 be16toh(packet->udp.dest) != DHCP_PORT_SERVER) {
177 log_dhcp_client(client, "ignoring packet: wrong ports");
cf597f65 178 return -EINVAL;
ac4f16ab 179 }
cf597f65
TG
180 break;
181 case BOOTREPLY:
182 if (be16toh(packet->udp.source) != DHCP_PORT_SERVER ||
ac4f16ab
TG
183 be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
184 log_dhcp_client(client, "ignoring packet: wrong ports");
cf597f65 185 return -EINVAL;
ac4f16ab 186 }
cf597f65
TG
187 break;
188 }
189
190 return 0;
191}