]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-network.c
Merge pull request #8562 from keszybz/docs
[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 return TAKE_FD(s);
127 }
128
129 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
130 uint32_t xid, const uint8_t *mac_addr,
131 size_t mac_addr_len, uint16_t arp_type,
132 uint16_t port) {
133 static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
134 /* Default broadcast address for IPoIB */
135 static const uint8_t ib_bcast[] = {
136 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0xff, 0xff, 0xff, 0xff
139 };
140 struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
141 const uint8_t *bcast_addr = NULL;
142 uint8_t dhcp_hlen = 0;
143
144 assert_return(mac_addr_len > 0, -EINVAL);
145
146 if (arp_type == ARPHRD_ETHER) {
147 assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
148 memcpy(&eth_mac, mac_addr, ETH_ALEN);
149 bcast_addr = eth_bcast;
150 dhcp_hlen = ETH_ALEN;
151 } else if (arp_type == ARPHRD_INFINIBAND) {
152 assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL);
153 bcast_addr = ib_bcast;
154 } else
155 return -EINVAL;
156
157 return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len,
158 bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
159 }
160
161 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
162 union sockaddr_union src = {
163 .in.sin_family = AF_INET,
164 .in.sin_port = htobe16(port),
165 .in.sin_addr.s_addr = address,
166 };
167 _cleanup_close_ int s = -1;
168 char ifname[IF_NAMESIZE] = "";
169 int r, on = 1, tos = IPTOS_CLASS_CS6;
170
171 s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
172 if (s < 0)
173 return -errno;
174
175 r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
176 if (r < 0)
177 return -errno;
178
179 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
180 if (r < 0)
181 return -errno;
182
183 if (ifindex > 0) {
184 if (if_indextoname(ifindex, ifname) == 0)
185 return -errno;
186
187 r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
188 if (r < 0)
189 return -errno;
190 }
191
192 if (address == INADDR_ANY) {
193 r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
194 if (r < 0)
195 return -errno;
196
197 r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
198 if (r < 0)
199 return -errno;
200
201 } else {
202 r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
203 if (r < 0)
204 return -errno;
205 }
206
207 r = bind(s, &src.sa, sizeof(src.in));
208 if (r < 0)
209 return -errno;
210
211 return TAKE_FD(s);
212 }
213
214 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
215 const void *packet, size_t len) {
216 int r;
217
218 assert(link);
219 assert(packet);
220 assert(len);
221
222 r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
223 if (r < 0)
224 return -errno;
225
226 return 0;
227 }
228
229 int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
230 const void *packet, size_t len) {
231 union sockaddr_union dest = {
232 .in.sin_family = AF_INET,
233 .in.sin_port = htobe16(port),
234 .in.sin_addr.s_addr = address,
235 };
236 int r;
237
238 assert(s >= 0);
239 assert(packet);
240 assert(len);
241
242 r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
243 if (r < 0)
244 return -errno;
245
246 return 0;
247 }