Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2013 Intel Corporation. All rights reserved.
4 : : ***/
5 : :
6 : : #include <errno.h>
7 : : #include <net/ethernet.h>
8 : : #include <net/if_arp.h>
9 : : #include <string.h>
10 : :
11 : : #include "dhcp-internal.h"
12 : : #include "dhcp-protocol.h"
13 : :
14 : : #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
15 : :
16 : 12 : int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
17 : : uint8_t type, uint16_t arp_type, size_t optlen,
18 : : size_t *optoffset) {
19 : 12 : size_t offset = 0;
20 : : int r;
21 : :
22 [ + - - + ]: 12 : assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
23 [ + - - + ]: 12 : assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
24 : :
25 : 12 : message->op = op;
26 : 12 : message->htype = arp_type;
27 [ + - ]: 12 : message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
28 : 12 : message->xid = htobe32(xid);
29 : 12 : message->magic = htobe32(DHCP_MAGIC_COOKIE);
30 : :
31 : 12 : r = dhcp_option_append(message, optlen, &offset, 0,
32 : : SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
33 [ - + ]: 12 : if (r < 0)
34 : 0 : return r;
35 : :
36 : 12 : *optoffset = offset;
37 : :
38 : 12 : return 0;
39 : : }
40 : :
41 : 40 : uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
42 : 40 : uint64_t *buf_64 = (uint64_t*)buf;
43 : 40 : uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
44 : 40 : uint64_t sum = 0;
45 : :
46 : : /* See RFC1071 */
47 : :
48 [ + + ]: 680 : while (buf_64 < end_64) {
49 : 640 : sum += *buf_64;
50 [ + + ]: 640 : if (sum < *buf_64)
51 : : /* wrap around in one's complement */
52 : 24 : sum++;
53 : :
54 : 640 : buf_64++;
55 : : }
56 : :
57 [ + + ]: 40 : if (len % sizeof(uint64_t)) {
58 : : /* If the buffer is not aligned to 64-bit, we need
59 : : to zero-pad the last few bytes and add them in */
60 : 32 : uint64_t buf_tail = 0;
61 : :
62 : 32 : memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
63 : :
64 : 32 : sum += buf_tail;
65 [ - + ]: 32 : if (sum < buf_tail)
66 : : /* wrap around */
67 : 0 : sum++;
68 : : }
69 : :
70 [ + + ]: 145 : while (sum >> 16)
71 : 105 : sum = (sum & 0xffff) + (sum >> 16);
72 : :
73 : 40 : return ~sum;
74 : : }
75 : :
76 : 8 : void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
77 : : uint16_t source_port, be32_t destination_addr,
78 : : uint16_t destination_port, uint16_t len) {
79 : 8 : packet->ip.version = IPVERSION;
80 : 8 : packet->ip.ihl = DHCP_IP_SIZE / 4;
81 : 8 : packet->ip.tot_len = htobe16(len);
82 : :
83 : 8 : packet->ip.tos = IPTOS_CLASS_CS6;
84 : :
85 : 8 : packet->ip.protocol = IPPROTO_UDP;
86 : 8 : packet->ip.saddr = source_addr;
87 : 8 : packet->ip.daddr = destination_addr;
88 : :
89 : 8 : packet->udp.source = htobe16(source_port);
90 : 8 : packet->udp.dest = htobe16(destination_port);
91 : :
92 : 8 : packet->udp.len = htobe16(len - DHCP_IP_SIZE);
93 : :
94 : 8 : packet->ip.check = packet->udp.len;
95 : 8 : packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
96 : :
97 : 8 : packet->ip.ttl = IPDEFTTL;
98 : 8 : packet->ip.check = 0;
99 : 8 : packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
100 : 8 : }
101 : :
102 : 4 : int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
103 : : size_t hdrlen;
104 : :
105 [ - + ]: 4 : assert(packet);
106 : :
107 : : /* IP */
108 : :
109 [ - + ]: 4 : if (packet->ip.version != IPVERSION)
110 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
111 : : "ignoring packet: not IPv4");
112 : :
113 [ - + ]: 4 : if (packet->ip.ihl < 5)
114 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
115 : : "ignoring packet: IPv4 IHL (%u words) invalid",
116 : : packet->ip.ihl);
117 : :
118 : 4 : hdrlen = packet->ip.ihl * 4;
119 [ - + ]: 4 : if (hdrlen < 20)
120 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
121 : : "ignoring packet: IPv4 IHL (%zu bytes) "
122 : : "smaller than minimum (20 bytes)",
123 : : hdrlen);
124 : :
125 [ - + ]: 4 : if (len < hdrlen)
126 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
127 : : "ignoring packet: packet (%zu bytes) "
128 : : "smaller than expected (%zu) by IP header",
129 : : len, hdrlen);
130 : :
131 : : /* UDP */
132 : :
133 [ - + ]: 4 : if (packet->ip.protocol != IPPROTO_UDP)
134 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
135 : : "ignoring packet: not UDP");
136 : :
137 [ - + ]: 4 : if (len < hdrlen + be16toh(packet->udp.len))
138 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
139 : : "ignoring packet: packet (%zu bytes) "
140 : : "smaller than expected (%zu) by UDP header",
141 : : len, hdrlen + be16toh(packet->udp.len));
142 : :
143 [ - + ]: 4 : if (be16toh(packet->udp.dest) != port)
144 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
145 : : "ignoring packet: to port %u, which "
146 : : "is not the DHCP client port (%u)",
147 : : be16toh(packet->udp.dest), port);
148 : :
149 : : /* checksums - computing these is relatively expensive, so only do it
150 : : if all the other checks have passed
151 : : */
152 : :
153 [ - + ]: 4 : if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen))
154 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
155 : : "ignoring packet: invalid IP checksum");
156 : :
157 [ + - - + ]: 4 : if (checksum && packet->udp.check) {
158 : 0 : packet->ip.check = packet->udp.len;
159 : 0 : packet->ip.ttl = 0;
160 : :
161 [ # # ]: 0 : if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
162 : 0 : be16toh(packet->udp.len) + 12))
163 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
164 : : "ignoring packet: invalid UDP checksum");
165 : : }
166 : :
167 : 4 : return 0;
168 : : }
|