]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/dhcp-network.c
sleep-config: replace USE() macro with TAKE_PTR() usage
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-network.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
8b4a9693
PF
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>
8b4a9693 22#include <net/ethernet.h>
3e7b9f76 23#include <net/if.h>
7429b07f 24#include <net/if_arp.h>
8b4a9693 25#include <stdio.h>
3ffd4af2
LP
26#include <string.h>
27#include <sys/socket.h>
bc29e507 28#include <linux/filter.h>
3ffd4af2
LP
29#include <linux/if_infiniband.h>
30#include <linux/if_packet.h>
8b4a9693
PF
31
32#include "dhcp-internal.h"
3ffd4af2
LP
33#include "fd-util.h"
34#include "socket-util.h"
f11cba74 35#include "unaligned.h"
8b4a9693 36
76253e73
DW
37static 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,
9faed222
SS
42 uint16_t arp_type, uint8_t dhcp_hlen,
43 uint16_t port) {
bc29e507 44 struct sock_filter filter[] = {
088b6ba2
LP
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 */
9faed222 60 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */
088b6ba2
LP
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 */
76253e73 66 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */
088b6ba2 67 BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
e2acdb6b 68 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */
76253e73 69 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */
088b6ba2
LP
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 */
f11cba74 74 BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
088b6ba2
LP
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 */
f11cba74 80 BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
088b6ba2
LP
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 */
bc29e507
TG
90 };
91 struct sock_fprog fprog = {
088b6ba2
LP
92 .len = ELEMENTSOF(filter),
93 .filter = filter
bc29e507 94 };
c3d2994b 95 _cleanup_close_ int s = -1;
fef0e0f3 96 int r, on = 1;
8b4a9693 97
088b6ba2 98 assert(ifindex > 0);
23f30ed3
TG
99 assert(link);
100
66a67eff 101 s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
8b4a9693
PF
102 if (s < 0)
103 return -errno;
104
088b6ba2 105 r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
c3d2994b
TG
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
b1f24b75
BG
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 };
76253e73 120 memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len);
8b4a9693 121
b1f24b75 122 r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
c3d2994b 123 if (r < 0)
bc29e507 124 return -errno;
bc29e507 125
c3d2994b
TG
126 r = s;
127 s = -1;
8b4a9693 128
c3d2994b 129 return r;
8c00042c
PF
130}
131
76253e73
DW
132int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
133 uint32_t xid, const uint8_t *mac_addr,
9faed222
SS
134 size_t mac_addr_len, uint16_t arp_type,
135 uint16_t port) {
76253e73
DW
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,
9faed222 161 bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
76253e73
DW
162}
163
3e7b9f76 164int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
234fc2df
PF
165 union sockaddr_union src = {
166 .in.sin_family = AF_INET,
080ab276
TG
167 .in.sin_port = htobe16(port),
168 .in.sin_addr.s_addr = address,
234fc2df 169 };
c3d2994b 170 _cleanup_close_ int s = -1;
3e7b9f76 171 char ifname[IF_NAMESIZE] = "";
076adf01 172 int r, on = 1, tos = IPTOS_CLASS_CS6;
234fc2df
PF
173
174 s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
175 if (s < 0)
176 return -errno;
177
c3d2994b
TG
178 r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
179 if (r < 0)
85923f79 180 return -errno;
b44cd882 181
076adf01
TG
182 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
183 if (r < 0)
184 return -errno;
fef0e0f3 185
3e7b9f76
MK
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
076adf01 195 if (address == INADDR_ANY) {
fef0e0f3
TG
196 r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
197 if (r < 0)
198 return -errno;
d6bd972d
TG
199
200 r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
201 if (r < 0)
202 return -errno;
3e7b9f76 203
076adf01
TG
204 } else {
205 r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
206 if (r < 0)
207 return -errno;
fef0e0f3 208 }
85923f79 209
c3d2994b
TG
210 r = bind(s, &src.sa, sizeof(src.in));
211 if (r < 0)
234fc2df 212 return -errno;
234fc2df 213
c3d2994b
TG
214 r = s;
215 s = -1;
216
217 return r;
234fc2df
PF
218}
219
8c00042c 220int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
c3d2994b
TG
221 const void *packet, size_t len) {
222 int r;
223
23f30ed3
TG
224 assert(link);
225 assert(packet);
226 assert(len);
227
b1f24b75 228 r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
c3d2994b 229 if (r < 0)
1c8035a8 230 return -errno;
8b4a9693 231
1c8035a8 232 return 0;
8b4a9693 233}
234fc2df 234
080ab276 235int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
c3d2994b 236 const void *packet, size_t len) {
234fc2df
PF
237 union sockaddr_union dest = {
238 .in.sin_family = AF_INET,
080ab276
TG
239 .in.sin_port = htobe16(port),
240 .in.sin_addr.s_addr = address,
234fc2df 241 };
c3d2994b
TG
242 int r;
243
244 assert(s >= 0);
245 assert(packet);
246 assert(len);
234fc2df 247
c3d2994b
TG
248 r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
249 if (r < 0)
234fc2df
PF
250 return -errno;
251
252 return 0;
253}