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