chiark / gitweb /
nspawn: make socket(AF_NETLINK, *, NETLINK_AUDIT) fail with EAFNOTSUPPORT in containers
[elogind.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, 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
74 static 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
97 void 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
130 int dhcp_packet_verify_headers(DHCPPacket *packet, uint8_t op, size_t len) {
131         size_t hdrlen;
132
133         assert(op == BOOTREQUEST || op == BOOTREPLY);
134
135         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
136                 return -EINVAL;
137
138         hdrlen = packet->ip.ihl * 4;
139         if (hdrlen < 20 || hdrlen > len || dhcp_checksum(&packet->ip, hdrlen))
140                 return -EINVAL;
141
142         if (hdrlen + be16toh(packet->udp.len) > len)
143                 return -EINVAL;
144
145         if (packet->udp.check) {
146                 packet->ip.check = packet->udp.len;
147                 packet->ip.ttl = 0;
148
149                 if (dhcp_checksum(&packet->ip.ttl,
150                                   be16toh(packet->udp.len) + 12))
151                         return -EINVAL;
152         }
153
154         if (packet->dhcp.op != op)
155                 return -EINVAL;
156
157         switch (op) {
158                 case BOOTREQUEST:
159                         if (be16toh(packet->udp.source) != DHCP_PORT_CLIENT ||
160                             be16toh(packet->udp.dest) != DHCP_PORT_SERVER)
161                                 return -EINVAL;
162                         break;
163                 case BOOTREPLY:
164                         if (be16toh(packet->udp.source) != DHCP_PORT_SERVER ||
165                             be16toh(packet->udp.dest) != DHCP_PORT_CLIENT)
166                                 return -EINVAL;
167                         break;
168         }
169
170         return 0;
171 }