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