]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
8b4a9693 | 2 | /*** |
810adae9 | 3 | Copyright © 2013 Intel Corporation. All rights reserved. |
8b4a9693 PF |
4 | ***/ |
5 | ||
edda10f2 FS |
6 | #include <linux/filter.h> |
7 | #include <linux/if_infiniband.h> | |
8 | #include <linux/if_packet.h> | |
7429b07f | 9 | #include <net/if_arp.h> |
8b4a9693 | 10 | #include <stdio.h> |
3ffd4af2 | 11 | #include <string.h> |
8b4a9693 | 12 | |
9bcbb614 YW |
13 | #include "dhcp-network.h" |
14 | #include "dhcp-protocol.h" | |
5cdf13c7 | 15 | #include "ether-addr-util.h" |
3ffd4af2 | 16 | #include "fd-util.h" |
5cdf13c7 | 17 | #include "socket-util.h" |
f11cba74 | 18 | #include "unaligned.h" |
8b4a9693 | 19 | |
073a1daa YW |
20 | static int _bind_raw_socket( |
21 | int ifindex, | |
22 | union sockaddr_union *link, | |
23 | uint32_t xid, | |
24 | const struct hw_addr_data *hw_addr, | |
25 | const struct hw_addr_data *bcast_addr, | |
26 | uint16_t arp_type, | |
ea577968 | 27 | uint16_t port, |
28 | bool so_priority_set, | |
29 | int so_priority) { | |
073a1daa YW |
30 | |
31 | assert(ifindex > 0); | |
32 | assert(link); | |
33 | assert(hw_addr); | |
34 | assert(bcast_addr); | |
35 | assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND)); | |
36 | ||
37 | switch (arp_type) { | |
38 | case ARPHRD_ETHER: | |
39 | assert(hw_addr->length == ETH_ALEN); | |
40 | assert(bcast_addr->length == ETH_ALEN); | |
41 | break; | |
42 | case ARPHRD_INFINIBAND: | |
43 | assert(hw_addr->length == 0); | |
44 | assert(bcast_addr->length == INFINIBAND_ALEN); | |
45 | break; | |
46 | default: | |
47 | assert_not_reached(); | |
48 | } | |
49 | ||
bc29e507 | 50 | struct sock_filter filter[] = { |
088b6ba2 LP |
51 | BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ |
52 | BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */ | |
53 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
54 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */ | |
55 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ | |
56 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
57 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */ | |
58 | BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20), /* A <- A & 0x20 (More Fragments bit) */ | |
59 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ | |
60 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
61 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */ | |
62 | BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */ | |
63 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ | |
64 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
65 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */ | |
9faed222 | 66 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */ |
088b6ba2 LP |
67 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ |
68 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */ | |
69 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ | |
70 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
71 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ | |
76253e73 | 72 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ |
088b6ba2 | 73 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ |
088b6ba2 LP |
74 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ |
75 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ | |
76 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
13f1fd03 | 77 | BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ |
073a1daa | 78 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (uint8_t) hw_addr->length, 1, 0), /* address length == hw_addr->length ? */ |
13f1fd03 TH |
79 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ |
80 | ||
81 | /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally | |
82 | * compare chaddr for ETH_ALEN bytes. */ | |
073a1daa YW |
83 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */ |
84 | BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(hw_addr->bytes)), /* X <- 4 bytes of client's MAC */ | |
85 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ | |
86 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */ | |
87 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
88 | BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(hw_addr->bytes + 4)), /* X <- remainder of client's MAC */ | |
89 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ | |
90 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */ | |
91 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
13f1fd03 | 92 | |
088b6ba2 LP |
93 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ |
94 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ | |
95 | BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ | |
ea51deb1 | 96 | BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */ |
bc29e507 TG |
97 | }; |
98 | struct sock_fprog fprog = { | |
088b6ba2 LP |
99 | .len = ELEMENTSOF(filter), |
100 | .filter = filter | |
bc29e507 | 101 | }; |
5bb1d7fb | 102 | _cleanup_close_ int s = -EBADF; |
6d5e65f6 | 103 | int r; |
8b4a9693 | 104 | |
66a67eff | 105 | s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); |
8b4a9693 PF |
106 | if (s < 0) |
107 | return -errno; | |
108 | ||
2ff48e98 | 109 | r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true); |
c3d2994b | 110 | if (r < 0) |
2ff48e98 | 111 | return r; |
c3d2994b TG |
112 | |
113 | r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); | |
114 | if (r < 0) | |
115 | return -errno; | |
116 | ||
905d0ea7 YW |
117 | r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true); |
118 | if (r < 0) | |
119 | return r; | |
120 | ||
ea577968 | 121 | if (so_priority_set) { |
122 | r = setsockopt_int(s, SOL_SOCKET, SO_PRIORITY, so_priority); | |
123 | if (r < 0) | |
124 | return r; | |
125 | } | |
126 | ||
b1f24b75 BG |
127 | link->ll = (struct sockaddr_ll) { |
128 | .sll_family = AF_PACKET, | |
129 | .sll_protocol = htobe16(ETH_P_IP), | |
130 | .sll_ifindex = ifindex, | |
131 | .sll_hatype = htobe16(arp_type), | |
073a1daa | 132 | .sll_halen = bcast_addr->length, |
b1f24b75 | 133 | }; |
073a1daa YW |
134 | /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */ |
135 | memcpy(link->ll.sll_addr, bcast_addr->bytes, bcast_addr->length); | |
8b4a9693 | 136 | |
a0233fcd | 137 | r = bind(s, &link->sa, sockaddr_ll_len(&link->ll)); |
c3d2994b | 138 | if (r < 0) |
bc29e507 | 139 | return -errno; |
bc29e507 | 140 | |
c10d6bdb | 141 | return TAKE_FD(s); |
8c00042c PF |
142 | } |
143 | ||
155943b2 YW |
144 | int dhcp_network_bind_raw_socket( |
145 | int ifindex, | |
146 | union sockaddr_union *link, | |
147 | uint32_t xid, | |
073a1daa YW |
148 | const struct hw_addr_data *hw_addr, |
149 | const struct hw_addr_data *bcast_addr, | |
155943b2 | 150 | uint16_t arp_type, |
ea577968 | 151 | uint16_t port, |
152 | bool so_priority_set, | |
153 | int so_priority) { | |
155943b2 | 154 | |
073a1daa YW |
155 | static struct hw_addr_data default_eth_bcast = { |
156 | .length = ETH_ALEN, | |
157 | .ether = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}, | |
158 | }, default_ib_bcast = { | |
159 | .length = INFINIBAND_ALEN, | |
160 | .infiniband = { | |
161 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b, | |
162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
163 | 0xff, 0xff, 0xff, 0xff | |
164 | }, | |
14b66dbc | 165 | }; |
76253e73 | 166 | |
073a1daa YW |
167 | assert(ifindex > 0); |
168 | assert(link); | |
169 | assert(hw_addr); | |
170 | ||
171 | switch (arp_type) { | |
172 | case ARPHRD_ETHER: | |
173 | return _bind_raw_socket(ifindex, link, xid, | |
174 | hw_addr, | |
175 | (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_eth_bcast, | |
ea577968 | 176 | arp_type, port, so_priority_set, so_priority); |
073a1daa YW |
177 | |
178 | case ARPHRD_INFINIBAND: | |
179 | return _bind_raw_socket(ifindex, link, xid, | |
180 | &HW_ADDR_NULL, | |
181 | (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_ib_bcast, | |
ea577968 | 182 | arp_type, port, so_priority_set, so_priority); |
073a1daa YW |
183 | default: |
184 | return -EINVAL; | |
14b66dbc | 185 | } |
76253e73 DW |
186 | } |
187 | ||
afe42aef | 188 | int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) { |
234fc2df PF |
189 | union sockaddr_union src = { |
190 | .in.sin_family = AF_INET, | |
080ab276 TG |
191 | .in.sin_port = htobe16(port), |
192 | .in.sin_addr.s_addr = address, | |
234fc2df | 193 | }; |
5bb1d7fb | 194 | _cleanup_close_ int s = -EBADF; |
9e5b6496 | 195 | int r; |
234fc2df PF |
196 | |
197 | s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); | |
198 | if (s < 0) | |
199 | return -errno; | |
200 | ||
afe42aef SC |
201 | if (ip_service_type >= 0) |
202 | r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type); | |
203 | else | |
204 | r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); | |
c3d2994b | 205 | if (r < 0) |
9e5b6496 | 206 | return r; |
b44cd882 | 207 | |
2ff48e98 | 208 | r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); |
076adf01 | 209 | if (r < 0) |
2ff48e98 | 210 | return r; |
fef0e0f3 | 211 | |
905d0ea7 YW |
212 | r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true); |
213 | if (r < 0) | |
214 | return r; | |
215 | ||
3e7b9f76 | 216 | if (ifindex > 0) { |
953a02d1 | 217 | r = socket_bind_to_ifindex(s, ifindex); |
3e7b9f76 | 218 | if (r < 0) |
953a02d1 | 219 | return r; |
3e7b9f76 MK |
220 | } |
221 | ||
21b6b87e | 222 | if (port == DHCP_PORT_SERVER) { |
2ff48e98 | 223 | r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); |
d6bd972d | 224 | if (r < 0) |
2ff48e98 | 225 | return r; |
21b6b87e YA |
226 | if (address == INADDR_ANY) { |
227 | /* IP_PKTINFO filter should not be applied when packets are | |
228 | allowed to enter/leave through the interface other than | |
229 | DHCP server sits on(BindToInterface option). */ | |
230 | r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); | |
231 | if (r < 0) | |
232 | return r; | |
233 | } | |
076adf01 | 234 | } else { |
2ff48e98 | 235 | r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); |
076adf01 | 236 | if (r < 0) |
2ff48e98 | 237 | return r; |
fef0e0f3 | 238 | } |
85923f79 | 239 | |
d70c7813 | 240 | if (bind(s, &src.sa, sizeof(src.in)) < 0) |
234fc2df | 241 | return -errno; |
234fc2df | 242 | |
c10d6bdb | 243 | return TAKE_FD(s); |
234fc2df PF |
244 | } |
245 | ||
155943b2 YW |
246 | int dhcp_network_send_raw_socket( |
247 | int s, | |
248 | const union sockaddr_union *link, | |
249 | const void *packet, | |
250 | size_t len) { | |
c3d2994b | 251 | |
b8319d74 YW |
252 | /* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this |
253 | * function should fail with negative errno. */ | |
254 | ||
23f30ed3 TG |
255 | assert(link); |
256 | assert(packet); | |
b8319d74 | 257 | assert(len > 0); |
23f30ed3 | 258 | |
a0233fcd | 259 | if (sendto(s, packet, len, 0, &link->sa, sockaddr_ll_len(&link->ll)) < 0) |
1c8035a8 | 260 | return -errno; |
8b4a9693 | 261 | |
1c8035a8 | 262 | return 0; |
8b4a9693 | 263 | } |
234fc2df | 264 | |
155943b2 YW |
265 | int dhcp_network_send_udp_socket( |
266 | int s, | |
267 | be32_t address, | |
268 | uint16_t port, | |
269 | const void *packet, | |
270 | size_t len) { | |
271 | ||
234fc2df PF |
272 | union sockaddr_union dest = { |
273 | .in.sin_family = AF_INET, | |
080ab276 TG |
274 | .in.sin_port = htobe16(port), |
275 | .in.sin_addr.s_addr = address, | |
234fc2df | 276 | }; |
c3d2994b TG |
277 | |
278 | assert(s >= 0); | |
279 | assert(packet); | |
b8319d74 | 280 | assert(len > 0); |
234fc2df | 281 | |
d70c7813 | 282 | if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0) |
234fc2df PF |
283 | return -errno; |
284 | ||
285 | return 0; | |
286 | } |