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