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