]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-network.c
libsystemd-network: fix unaligned loads (issue #7654)
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
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 <errno.h>
22 #include <net/ethernet.h>
23 #include <net/if.h>
24 #include <net/if_arp.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <linux/filter.h>
29 #include <linux/if_infiniband.h>
30 #include <linux/if_packet.h>
31
32 #include "dhcp-internal.h"
33 #include "fd-util.h"
34 #include "socket-util.h"
35 #include "unaligned.h"
36
37 static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
38 uint32_t xid, const uint8_t *mac_addr,
39 size_t mac_addr_len,
40 const uint8_t *bcast_addr,
41 const struct ether_addr *eth_mac,
42 uint16_t arp_type, uint8_t dhcp_hlen,
43 uint16_t port) {
44 struct sock_filter filter[] = {
45 BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
46 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
47 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
48 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */
49 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */
50 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
51 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */
52 BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20), /* A <- A & 0x20 (More Fragments bit) */
53 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
54 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
55 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */
56 BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */
57 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
58 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
59 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */
60 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */
61 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
62 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */
63 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */
64 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
65 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */
66 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */
67 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
68 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */
69 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */
70 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
71 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
72 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
73 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
74 BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
75 BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
76 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
77 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
78 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
79 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
80 BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
81 BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
82 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
83 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
84 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
85 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
86 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
87 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
88 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
89 BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
90 };
91 struct sock_fprog fprog = {
92 .len = ELEMENTSOF(filter),
93 .filter = filter
94 };
95 _cleanup_close_ int s = -1;
96 int r, on = 1;
97
98 assert(ifindex > 0);
99 assert(link);
100
101 s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
102 if (s < 0)
103 return -errno;
104
105 r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
106 if (r < 0)
107 return -errno;
108
109 r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
110 if (r < 0)
111 return -errno;
112
113 link->ll = (struct sockaddr_ll) {
114 .sll_family = AF_PACKET,
115 .sll_protocol = htobe16(ETH_P_IP),
116 .sll_ifindex = ifindex,
117 .sll_hatype = htobe16(arp_type),
118 .sll_halen = mac_addr_len,
119 };
120 memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len);
121
122 r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
123 if (r < 0)
124 return -errno;
125
126 r = s;
127 s = -1;
128
129 return r;
130 }
131
132 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
133 uint32_t xid, const uint8_t *mac_addr,
134 size_t mac_addr_len, uint16_t arp_type,
135 uint16_t port) {
136 static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
137 /* Default broadcast address for IPoIB */
138 static const uint8_t ib_bcast[] = {
139 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0xff, 0xff, 0xff, 0xff
142 };
143 struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
144 const uint8_t *bcast_addr = NULL;
145 uint8_t dhcp_hlen = 0;
146
147 assert_return(mac_addr_len > 0, -EINVAL);
148
149 if (arp_type == ARPHRD_ETHER) {
150 assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
151 memcpy(&eth_mac, mac_addr, ETH_ALEN);
152 bcast_addr = eth_bcast;
153 dhcp_hlen = ETH_ALEN;
154 } else if (arp_type == ARPHRD_INFINIBAND) {
155 assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL);
156 bcast_addr = ib_bcast;
157 } else
158 return -EINVAL;
159
160 return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len,
161 bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
162 }
163
164 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
165 union sockaddr_union src = {
166 .in.sin_family = AF_INET,
167 .in.sin_port = htobe16(port),
168 .in.sin_addr.s_addr = address,
169 };
170 _cleanup_close_ int s = -1;
171 char ifname[IF_NAMESIZE] = "";
172 int r, on = 1, tos = IPTOS_CLASS_CS6;
173
174 s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
175 if (s < 0)
176 return -errno;
177
178 r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
179 if (r < 0)
180 return -errno;
181
182 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
183 if (r < 0)
184 return -errno;
185
186 if (ifindex > 0) {
187 if (if_indextoname(ifindex, ifname) == 0)
188 return -errno;
189
190 r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
191 if (r < 0)
192 return -errno;
193 }
194
195 if (address == INADDR_ANY) {
196 r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
197 if (r < 0)
198 return -errno;
199
200 r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
201 if (r < 0)
202 return -errno;
203
204 } else {
205 r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
206 if (r < 0)
207 return -errno;
208 }
209
210 r = bind(s, &src.sa, sizeof(src.in));
211 if (r < 0)
212 return -errno;
213
214 r = s;
215 s = -1;
216
217 return r;
218 }
219
220 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
221 const void *packet, size_t len) {
222 int r;
223
224 assert(link);
225 assert(packet);
226 assert(len);
227
228 r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
229 if (r < 0)
230 return -errno;
231
232 return 0;
233 }
234
235 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
236 const void *packet, size_t len) {
237 union sockaddr_union dest = {
238 .in.sin_family = AF_INET,
239 .in.sin_port = htobe16(port),
240 .in.sin_addr.s_addr = address,
241 };
242 int r;
243
244 assert(s >= 0);
245 assert(packet);
246 assert(len);
247
248 r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
249 if (r < 0)
250 return -errno;
251
252 return 0;
253 }