]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* socket.c |
2 | ||
3 | BSD socket interface code... */ | |
4 | ||
5 | /* | |
49a7fb58 | 6 | * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1995-2003 by Internet Software Consortium |
d7837182 | 8 | * |
7512d88b TM |
9 | * This Source Code Form is subject to the terms of the Mozilla Public |
10 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |
11 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
d7837182 | 12 | * |
98311e4b DH |
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
d7837182 | 20 | * |
98311e4b | 21 | * Internet Systems Consortium, Inc. |
429a56d7 TM |
22 | * PO Box 360 |
23 | * Newmarket, NH 03857 USA | |
98311e4b | 24 | * <info@isc.org> |
2c85ac9b | 25 | * https://www.isc.org/ |
49733f31 | 26 | * |
d7837182 TL |
27 | */ |
28 | ||
469cf3a4 TL |
29 | /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). |
30 | * This sockopt allows a socket to be bound to a particular interface, | |
31 | * thus enabling the use of DHCPD on a multihomed host. | |
32 | * If SO_BINDTODEVICE is defined in your system header files, the use of | |
f6b8f48d | 33 | * this sockopt will be automatically enabled. |
469cf3a4 TL |
34 | * I have implemented it under Linux; other systems should be doable also. |
35 | */ | |
36 | ||
d7837182 | 37 | #include "dhcpd.h" |
e39b4193 | 38 | #include <isc/util.h> |
fe5b0fdd DH |
39 | #include <errno.h> |
40 | #include <sys/ioctl.h> | |
a512d11b | 41 | #include <sys/uio.h> |
99fe695e | 42 | #include <sys/uio.h> |
d7837182 | 43 | |
7cfeb916 SR |
44 | #if defined(sun) && defined(USE_V4_PKTINFO) |
45 | #include <sys/sysmacros.h> | |
46 | #include <net/if.h> | |
47 | #include <sys/sockio.h> | |
48 | #include <net/if_dl.h> | |
b047bd38 | 49 | #include <sys/dlpi.h> |
7cfeb916 SR |
50 | #endif |
51 | ||
0a5d6860 | 52 | #ifdef USE_SOCKET_FALLBACK |
a1b705e5 | 53 | # if !defined (USE_SOCKET_SEND) |
0a5d6860 TL |
54 | # define if_register_send if_register_fallback |
55 | # define send_packet send_fallback | |
c3585217 | 56 | # define if_reinitialize_send if_reinitialize_fallback |
a1b705e5 | 57 | # endif |
c3585217 TL |
58 | #endif |
59 | ||
ecddae64 DH |
60 | #if defined(DHCPv6) |
61 | /* | |
62 | * XXX: this is gross. we need to go back and overhaul the API for socket | |
63 | * handling. | |
64 | */ | |
4b8251a0 | 65 | static int no_global_v6_socket = 0; |
ecddae64 DH |
66 | static unsigned int global_v6_socket_references = 0; |
67 | static int global_v6_socket = -1; | |
563f0b8a FD |
68 | #if defined(RELAY_PORT) |
69 | static unsigned int relay_port_v6_socket_references = 0; | |
70 | static int relay_port_v6_socket = -1; | |
71 | #endif | |
ecddae64 DH |
72 | |
73 | static void if_register_multicast(struct interface_info *info); | |
74 | #endif | |
75 | ||
7cfeb916 SR |
76 | /* |
77 | * We can use a single socket for AF_INET (similar to AF_INET6) on all | |
78 | * interfaces configured for DHCP if the system has support for IP_PKTINFO | |
79 | * and IP_RECVPKTINFO (for example Solaris 11). | |
80 | */ | |
81 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
82 | static unsigned int global_v4_socket_references = 0; | |
83 | static int global_v4_socket = -1; | |
84 | #endif | |
85 | ||
28868515 SK |
86 | /* |
87 | * If we can't bind() to a specific interface, then we can only have | |
88 | * a single socket. This variable insures that we don't try to listen | |
89 | * on two sockets. | |
90 | */ | |
91 | #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) | |
c3585217 | 92 | static int once = 0; |
28868515 | 93 | #endif /* !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) */ |
c3585217 TL |
94 | |
95 | /* Reinitializes the specified interface after an address change. This | |
96 | is not required for packet-filter APIs. */ | |
97 | ||
a1b705e5 | 98 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
c3585217 TL |
99 | void if_reinitialize_send (info) |
100 | struct interface_info *info; | |
101 | { | |
102 | #if 0 | |
103 | #ifndef USE_SOCKET_RECEIVE | |
104 | once = 0; | |
105 | close (info -> wfdesc); | |
106 | #endif | |
107 | if_register_send (info); | |
108 | #endif | |
109 | } | |
110 | #endif | |
111 | ||
112 | #ifdef USE_SOCKET_RECEIVE | |
113 | void if_reinitialize_receive (info) | |
114 | struct interface_info *info; | |
115 | { | |
116 | #if 0 | |
117 | once = 0; | |
118 | close (info -> rfdesc); | |
119 | if_register_receive (info); | |
120 | #endif | |
121 | } | |
0a5d6860 TL |
122 | #endif |
123 | ||
a1b705e5 TL |
124 | #if defined (USE_SOCKET_SEND) || \ |
125 | defined (USE_SOCKET_RECEIVE) || \ | |
126 | defined (USE_SOCKET_FALLBACK) | |
e23c9055 | 127 | /* Generic interface registration routine... */ |
98bd7ca0 | 128 | int |
ecddae64 | 129 | if_register_socket(struct interface_info *info, int family, |
4b8251a0 | 130 | int *do_multicast, struct in6_addr *linklocal6) |
ecddae64 | 131 | { |
98bd7ca0 DH |
132 | struct sockaddr_storage name; |
133 | int name_len; | |
d7837182 | 134 | int sock; |
d7837182 | 135 | int flag; |
98bd7ca0 | 136 | int domain; |
98bf1607 SR |
137 | #ifdef DHCPv6 |
138 | struct sockaddr_in6 *addr6; | |
139 | #endif | |
140 | struct sockaddr_in *addr; | |
98bd7ca0 DH |
141 | |
142 | /* INSIST((family == AF_INET) || (family == AF_INET6)); */ | |
469cf3a4 | 143 | |
fe5b0fdd | 144 | #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) |
e23c9055 | 145 | /* Make sure only one interface is registered. */ |
98bd7ca0 | 146 | if (once) { |
8ae2d595 | 147 | log_fatal ("The standard socket API can only support %s", |
13ee152c | 148 | "hosts with a single network interface."); |
98bd7ca0 | 149 | } |
469cf3a4 | 150 | once = 1; |
0a5d6860 | 151 | #endif |
e23c9055 | 152 | |
f6b8f48d | 153 | /* |
98bd7ca0 | 154 | * Set up the address we're going to bind to, depending on the |
f6b8f48d TM |
155 | * address family. |
156 | */ | |
98bd7ca0 | 157 | memset(&name, 0, sizeof(name)); |
98bf1607 | 158 | switch (family) { |
fe5b0fdd | 159 | #ifdef DHCPv6 |
98bf1607 | 160 | case AF_INET6: |
f6b8f48d | 161 | addr6 = (struct sockaddr_in6 *)&name; |
98bf1607 SR |
162 | addr6->sin6_family = AF_INET6; |
163 | addr6->sin6_port = local_port; | |
563f0b8a FD |
164 | #if defined(RELAY_PORT) |
165 | if (relay_port && | |
166 | ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) | |
167 | addr6->sin6_port = relay_port; | |
168 | #endif | |
a2a0f98c FD |
169 | /* A server feature */ |
170 | if (bind_local_address6) { | |
171 | memcpy(&addr6->sin6_addr, | |
172 | &local_address6, | |
173 | sizeof(addr6->sin6_addr)); | |
174 | } | |
175 | /* A client feature */ | |
4b8251a0 SR |
176 | if (linklocal6) { |
177 | memcpy(&addr6->sin6_addr, | |
178 | linklocal6, | |
179 | sizeof(addr6->sin6_addr)); | |
a2a0f98c FD |
180 | } |
181 | if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) { | |
4b8251a0 SR |
182 | addr6->sin6_scope_id = if_nametoindex(info->name); |
183 | } | |
7de20a95 | 184 | #ifdef HAVE_SA_LEN |
98bf1607 | 185 | addr6->sin6_len = sizeof(*addr6); |
7de20a95 | 186 | #endif |
98bf1607 | 187 | name_len = sizeof(*addr6); |
98bd7ca0 | 188 | domain = PF_INET6; |
7de20a95 | 189 | if ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) { |
ecddae64 | 190 | *do_multicast = 0; |
7de20a95 | 191 | } |
98bf1607 | 192 | break; |
fe5b0fdd | 193 | #endif /* DHCPv6 */ |
98bf1607 SR |
194 | |
195 | case AF_INET: | |
196 | default: | |
f6b8f48d | 197 | addr = (struct sockaddr_in *)&name; |
06eb8bab | 198 | addr->sin_family = AF_INET; |
563f0b8a | 199 | addr->sin_port = relay_port ? relay_port : local_port; |
06eb8bab SK |
200 | memcpy(&addr->sin_addr, |
201 | &local_address, | |
202 | sizeof(addr->sin_addr)); | |
7de20a95 EH |
203 | #ifdef HAVE_SA_LEN |
204 | addr->sin_len = sizeof(*addr); | |
205 | #endif | |
06eb8bab SK |
206 | name_len = sizeof(*addr); |
207 | domain = PF_INET; | |
98bf1607 | 208 | break; |
98bd7ca0 | 209 | } |
d7837182 | 210 | |
0a5d6860 | 211 | /* Make a socket... */ |
98bd7ca0 DH |
212 | sock = socket(domain, SOCK_DGRAM, IPPROTO_UDP); |
213 | if (sock < 0) { | |
4ad524bd | 214 | log_fatal("Can't create dhcp socket for %s: %m", info->name); |
98bd7ca0 | 215 | } |
d7837182 | 216 | |
e23c9055 TL |
217 | /* Set the REUSEADDR option so that we don't fail to start if |
218 | we're being restarted. */ | |
d7837182 | 219 | flag = 1; |
98bd7ca0 DH |
220 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, |
221 | (char *)&flag, sizeof(flag)) < 0) { | |
4ad524bd TM |
222 | log_fatal("Can't set SO_REUSEADDR on dhcp socket for" |
223 | " %s: %m", info->name); | |
98bd7ca0 | 224 | } |
d7837182 | 225 | |
5cefe5e5 TL |
226 | /* Set the BROADCAST option so that we can broadcast DHCP responses. |
227 | We shouldn't do this for fallback devices, and we can detect that | |
228 | a device is a fallback because it has no ifp structure. */ | |
98bd7ca0 DH |
229 | if (info->ifp && |
230 | (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, | |
231 | (char *)&flag, sizeof(flag)) < 0)) { | |
4ad524bd TM |
232 | log_fatal("Can't set SO_BROADCAST on dhcp socket for" |
233 | " %s: %m", info->name); | |
98bd7ca0 | 234 | } |
d7837182 | 235 | |
ecddae64 DH |
236 | #if defined(DHCPv6) && defined(SO_REUSEPORT) |
237 | /* | |
238 | * We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple | |
239 | * daemons can bind to their own sockets and get data for their | |
240 | * respective interfaces. This does not (and should not) affect | |
241 | * DHCPv4 sockets; we can't yet support BSD sockets well, much | |
4b8251a0 | 242 | * less multiple sockets. Make sense only with multicast. |
bf0b1863 FD |
243 | * RedHat defines SO_REUSEPORT with a kernel which does not support |
244 | * it and returns ENOPROTOOPT so in this case ignore the error. | |
ecddae64 | 245 | */ |
eff1c7d8 | 246 | if ((local_family == AF_INET6) && *do_multicast) { |
ecddae64 | 247 | flag = 1; |
bf0b1863 FD |
248 | if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, |
249 | (char *)&flag, sizeof(flag)) < 0) && | |
250 | (errno != ENOPROTOOPT)) { | |
4ad524bd TM |
251 | log_fatal("Can't set SO_REUSEPORT on dhcp socket for" |
252 | " %s: %m", info->name); | |
ecddae64 DH |
253 | } |
254 | } | |
255 | #endif | |
256 | ||
e23c9055 | 257 | /* Bind the socket to this interface's IP address. */ |
98bd7ca0 DH |
258 | if (bind(sock, (struct sockaddr *)&name, name_len) < 0) { |
259 | log_error("Can't bind to dhcp address: %m"); | |
260 | log_error("Please make sure there is no other dhcp server"); | |
261 | log_error("running and that there's no entry for dhcp or"); | |
262 | log_error("bootp in /etc/inetd.conf. Also make sure you"); | |
263 | log_error("are not running HP JetAdmin software, which"); | |
264 | log_fatal("includes a bootp server."); | |
7eae478e | 265 | } |
d7837182 | 266 | |
fe5b0fdd | 267 | #if defined(SO_BINDTODEVICE) |
469cf3a4 | 268 | /* Bind this socket to this interface. */ |
ecddae64 | 269 | if ((local_family != AF_INET6) && (info->ifp != NULL) && |
98bd7ca0 | 270 | setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, |
4ad524bd TM |
271 | (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) { |
272 | log_fatal("Can't set SO_BINDTODEVICE on dhcp socket for" | |
273 | " %s : %m", (char *)(info->ifp)); | |
469cf3a4 TL |
274 | } |
275 | #endif | |
276 | ||
41e45067 DH |
277 | /* IP_BROADCAST_IF instructs the kernel which interface to send |
278 | * IP packets whose destination address is 255.255.255.255. These | |
279 | * will be treated as subnet broadcasts on the interface identified | |
280 | * by ip address (info -> primary_address). This is only known to | |
281 | * be defined in SCO system headers, and may not be defined in all | |
282 | * releases. | |
283 | */ | |
284 | #if defined(SCO) && defined(IP_BROADCAST_IF) | |
98bd7ca0 DH |
285 | if (info->address_count && |
286 | setsockopt(sock, IPPROTO_IP, IP_BROADCAST_IF, &info->addresses[0], | |
287 | sizeof(info->addresses[0])) < 0) | |
4ad524bd TM |
288 | log_fatal("Can't set IP_BROADCAST_IF on dhcp socket for" |
289 | " %s: %m", info->name); | |
41e45067 DH |
290 | #endif |
291 | ||
7cfeb916 SR |
292 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) |
293 | /* | |
294 | * If we turn on IP_RECVPKTINFO we will be able to receive | |
295 | * the interface index information of the received packet. | |
296 | */ | |
297 | if (family == AF_INET) { | |
298 | int on = 1; | |
f6b8f48d | 299 | if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, |
7cfeb916 | 300 | &on, sizeof(on)) != 0) { |
4ad524bd TM |
301 | log_fatal("Can't set IP_RECVPTKINFO on dhcp socket for" |
302 | " %s: %m", info->name); | |
7cfeb916 SR |
303 | } |
304 | } | |
305 | #endif | |
306 | ||
fe5b0fdd | 307 | #ifdef DHCPv6 |
98bd7ca0 | 308 | /* |
f6b8f48d | 309 | * If we turn on IPV6_PKTINFO, we will be able to receive |
98bd7ca0 DH |
310 | * additional information, such as the destination IP address. |
311 | * We need this to spot unicast packets. | |
312 | */ | |
313 | if (family == AF_INET6) { | |
314 | int on = 1; | |
315 | #ifdef IPV6_RECVPKTINFO | |
316 | /* RFC3542 */ | |
f6b8f48d | 317 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
98bd7ca0 | 318 | &on, sizeof(on)) != 0) { |
8834cc2e TM |
319 | log_fatal("setsockopt: IPV6_RECVPKTINFO for %s: %m", |
320 | info->name); | |
98bd7ca0 DH |
321 | } |
322 | #else | |
323 | /* RFC2292 */ | |
f6b8f48d | 324 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, |
98bd7ca0 | 325 | &on, sizeof(on)) != 0) { |
8834cc2e TM |
326 | log_fatal("setsockopt: IPV6_PKTINFO for %s: %m", |
327 | info->name); | |
98bd7ca0 DH |
328 | } |
329 | #endif | |
330 | } | |
7de20a95 | 331 | |
fe5b0fdd | 332 | #endif /* DHCPv6 */ |
98bd7ca0 | 333 | |
e23c9055 | 334 | return sock; |
d7837182 | 335 | } |
c2b5b5e8 TM |
336 | |
337 | #ifdef DHCPv6 | |
338 | void set_multicast_hop_limit(struct interface_info* info, int hop_limit) { | |
339 | if (setsockopt(info->wfdesc, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, | |
340 | &hop_limit, sizeof(int)) < 0) { | |
5da634c5 | 341 | log_fatal("setsockopt: IPV6_MULTICAST_HOPS for %s: %m", |
8834cc2e | 342 | info->name); |
c2b5b5e8 TM |
343 | } |
344 | ||
345 | log_debug("Setting hop count limit to %d for interface %s", | |
346 | hop_limit, info->name); | |
347 | ||
348 | } | |
349 | #endif /* DHCPv6 */ | |
350 | ||
a1b705e5 | 351 | #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ |
d7837182 | 352 | |
a1b705e5 | 353 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
c3585217 | 354 | void if_register_send (info) |
c857a7b6 | 355 | struct interface_info *info; |
d7837182 | 356 | { |
e23c9055 | 357 | #ifndef USE_SOCKET_RECEIVE |
4b8251a0 | 358 | info->wfdesc = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
359 | /* If this is a normal IPv4 address, get the hardware address. */ |
360 | if (strcmp(info->name, "fallback") != 0) | |
361 | get_hw_addr(info->name, &info->hw_address); | |
2e96d6e7 TL |
362 | #if defined (USE_SOCKET_FALLBACK) |
363 | /* Fallback only registers for send, but may need to receive as | |
364 | well. */ | |
7cfeb916 | 365 | info->rfdesc = info->wfdesc; |
2e96d6e7 | 366 | #endif |
e23c9055 | 367 | #else |
7cfeb916 | 368 | info->wfdesc = info->rfdesc; |
e23c9055 | 369 | #endif |
3648a2a1 | 370 | if (!quiet_interface_discovery) |
74f45f96 | 371 | log_info ("Sending on Socket/%s%s%s", |
7cfeb916 SR |
372 | info->name, |
373 | (info->shared_network ? "/" : ""), | |
374 | (info->shared_network ? | |
375 | info->shared_network->name : "")); | |
d7837182 | 376 | } |
7203e8ee | 377 | |
3782236d | 378 | #if defined (USE_SOCKET_SEND) |
7203e8ee TL |
379 | void if_deregister_send (info) |
380 | struct interface_info *info; | |
381 | { | |
382 | #ifndef USE_SOCKET_RECEIVE | |
383 | close (info -> wfdesc); | |
384 | #endif | |
385 | info -> wfdesc = -1; | |
386 | ||
387 | if (!quiet_interface_discovery) | |
388 | log_info ("Disabling output on Socket/%s%s%s", | |
389 | info -> name, | |
390 | (info -> shared_network ? "/" : ""), | |
391 | (info -> shared_network ? | |
392 | info -> shared_network -> name : "")); | |
393 | } | |
3782236d | 394 | #endif /* USE_SOCKET_SEND */ |
a1b705e5 | 395 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
d7837182 | 396 | |
e23c9055 | 397 | #ifdef USE_SOCKET_RECEIVE |
c3585217 | 398 | void if_register_receive (info) |
e23c9055 | 399 | struct interface_info *info; |
e23c9055 | 400 | { |
7cfeb916 SR |
401 | |
402 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
403 | if (global_v4_socket_references == 0) { | |
4b8251a0 | 404 | global_v4_socket = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
405 | if (global_v4_socket < 0) { |
406 | /* | |
407 | * if_register_socket() fatally logs if it fails to | |
408 | * create a socket, this is just a sanity check. | |
409 | */ | |
410 | log_fatal("Failed to create AF_INET socket %s:%d", | |
411 | MDL); | |
412 | } | |
413 | } | |
f6b8f48d | 414 | |
7cfeb916 SR |
415 | info->rfdesc = global_v4_socket; |
416 | global_v4_socket_references++; | |
417 | #else | |
e23c9055 TL |
418 | /* If we're using the socket API for sending and receiving, |
419 | we don't need to register this interface twice. */ | |
4b8251a0 | 420 | info->rfdesc = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
421 | #endif /* IP_PKTINFO... */ |
422 | /* If this is a normal IPv4 address, get the hardware address. */ | |
423 | if (strcmp(info->name, "fallback") != 0) | |
424 | get_hw_addr(info->name, &info->hw_address); | |
425 | ||
3648a2a1 | 426 | if (!quiet_interface_discovery) |
74f45f96 | 427 | log_info ("Listening on Socket/%s%s%s", |
7cfeb916 SR |
428 | info->name, |
429 | (info->shared_network ? "/" : ""), | |
430 | (info->shared_network ? | |
431 | info->shared_network->name : "")); | |
e23c9055 | 432 | } |
7203e8ee TL |
433 | |
434 | void if_deregister_receive (info) | |
435 | struct interface_info *info; | |
436 | { | |
7cfeb916 SR |
437 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) |
438 | /* Dereference the global v4 socket. */ | |
439 | if ((info->rfdesc == global_v4_socket) && | |
7cfeb916 SR |
440 | (global_v4_socket_references > 0)) { |
441 | global_v4_socket_references--; | |
442 | info->rfdesc = -1; | |
443 | } else { | |
444 | log_fatal("Impossible condition at %s:%d", MDL); | |
445 | } | |
7203e8ee | 446 | |
7cfeb916 SR |
447 | if (global_v4_socket_references == 0) { |
448 | close(global_v4_socket); | |
449 | global_v4_socket = -1; | |
450 | } | |
451 | #else | |
452 | close(info->rfdesc); | |
453 | info->rfdesc = -1; | |
454 | #endif /* IP_PKTINFO... */ | |
7203e8ee TL |
455 | if (!quiet_interface_discovery) |
456 | log_info ("Disabling input on Socket/%s%s%s", | |
457 | info -> name, | |
458 | (info -> shared_network ? "/" : ""), | |
459 | (info -> shared_network ? | |
460 | info -> shared_network -> name : "")); | |
05af5898 | 461 | } |
e23c9055 TL |
462 | #endif /* USE_SOCKET_RECEIVE */ |
463 | ||
98bd7ca0 | 464 | |
f6b8f48d | 465 | #ifdef DHCPv6 |
ecddae64 DH |
466 | /* |
467 | * This function joins the interface to DHCPv6 multicast groups so we will | |
468 | * receive multicast messages. | |
469 | */ | |
470 | static void | |
471 | if_register_multicast(struct interface_info *info) { | |
472 | int sock = info->rfdesc; | |
473 | struct ipv6_mreq mreq; | |
474 | ||
475 | if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, | |
476 | &mreq.ipv6mr_multiaddr) <= 0) { | |
f6b8f48d | 477 | log_fatal("inet_pton: unable to convert '%s'", |
ecddae64 DH |
478 | All_DHCP_Relay_Agents_and_Servers); |
479 | } | |
480 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
f6b8f48d | 481 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, |
ecddae64 | 482 | &mreq, sizeof(mreq)) < 0) { |
5da634c5 TM |
483 | log_fatal("setsockopt: IPV6_JOIN_GROUP for %s: %m", |
484 | info->name); | |
ecddae64 DH |
485 | } |
486 | ||
487 | /* | |
488 | * The relay agent code sets the streams so you know which way | |
489 | * is up and down. But a relay agent shouldn't join to the | |
490 | * Server address, or else you get fun loops. So up or down | |
491 | * doesn't matter, we're just using that config to sense this is | |
492 | * a relay agent. | |
493 | */ | |
494 | if ((info->flags & INTERFACE_STREAMS) == 0) { | |
495 | if (inet_pton(AF_INET6, All_DHCP_Servers, | |
496 | &mreq.ipv6mr_multiaddr) <= 0) { | |
f6b8f48d | 497 | log_fatal("inet_pton: unable to convert '%s'", |
ecddae64 DH |
498 | All_DHCP_Servers); |
499 | } | |
500 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
f6b8f48d | 501 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, |
ecddae64 | 502 | &mreq, sizeof(mreq)) < 0) { |
5da634c5 TM |
503 | log_fatal("setsockopt: IPV6_JOIN_GROUP for %s: %m", |
504 | info->name); | |
ecddae64 DH |
505 | } |
506 | } | |
507 | } | |
508 | ||
98bd7ca0 DH |
509 | void |
510 | if_register6(struct interface_info *info, int do_multicast) { | |
ecddae64 DH |
511 | /* Bounce do_multicast to a stack variable because we may change it. */ |
512 | int req_multi = do_multicast; | |
513 | ||
4b8251a0 SR |
514 | if (no_global_v6_socket) { |
515 | log_fatal("Impossible condition at %s:%d", MDL); | |
516 | } | |
517 | ||
563f0b8a FD |
518 | #if defined(RELAY_PORT) |
519 | if (!relay_port || | |
520 | ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) { | |
521 | #endif | |
ecddae64 DH |
522 | if (global_v6_socket_references == 0) { |
523 | global_v6_socket = if_register_socket(info, AF_INET6, | |
4b8251a0 | 524 | &req_multi, NULL); |
ecddae64 DH |
525 | if (global_v6_socket < 0) { |
526 | /* | |
527 | * if_register_socket() fatally logs if it fails to | |
528 | * create a socket, this is just a sanity check. | |
529 | */ | |
530 | log_fatal("Impossible condition at %s:%d", MDL); | |
a2a0f98c FD |
531 | } else if (bind_local_address6) { |
532 | char addr6_str[INET6_ADDRSTRLEN]; | |
533 | ||
534 | if (inet_ntop(AF_INET6, | |
535 | &local_address6, | |
536 | addr6_str, | |
537 | sizeof(addr6_str)) == NULL) { | |
538 | log_fatal("inet_ntop: unable to convert " | |
539 | "local-address6"); | |
540 | } | |
541 | log_info("Bound to [%s]:%d", | |
542 | addr6_str, | |
543 | (int) ntohs(local_port)); | |
ecddae64 | 544 | } else { |
a2a0f98c | 545 | log_info("Bound to *:%d", (int) ntohs(local_port)); |
ecddae64 DH |
546 | } |
547 | } | |
f6b8f48d | 548 | |
ecddae64 DH |
549 | info->rfdesc = global_v6_socket; |
550 | info->wfdesc = global_v6_socket; | |
551 | global_v6_socket_references++; | |
552 | ||
563f0b8a FD |
553 | #if defined(RELAY_PORT) |
554 | } else { | |
555 | /* | |
556 | * If relay port is defined, we need to register one | |
557 | * IPv6 UPD socket to handle upstream server or relay agent | |
558 | * with a non-547 UDP local port. | |
559 | */ | |
560 | if ((relay_port_v6_socket_references == 0) && | |
561 | ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) { | |
562 | relay_port_v6_socket = if_register_socket(info, AF_INET6, | |
563 | &req_multi, NULL); | |
564 | if (relay_port_v6_socket < 0) { | |
565 | log_fatal("Impossible condition at %s:%d", MDL); | |
566 | } else { | |
567 | log_info("Bound to relay port *:%d", | |
568 | (int) ntohs(relay_port)); | |
569 | } | |
570 | } | |
571 | info->rfdesc = relay_port_v6_socket; | |
572 | info->wfdesc = relay_port_v6_socket; | |
573 | relay_port_v6_socket_references++; | |
574 | } | |
575 | #endif | |
576 | ||
ecddae64 DH |
577 | if (req_multi) |
578 | if_register_multicast(info); | |
579 | ||
580 | get_hw_addr(info->name, &info->hw_address); | |
581 | ||
98bd7ca0 DH |
582 | if (!quiet_interface_discovery) { |
583 | if (info->shared_network != NULL) { | |
ecddae64 | 584 | log_info("Listening on Socket/%d/%s/%s", |
f6b8f48d | 585 | global_v6_socket, info->name, |
98bd7ca0 | 586 | info->shared_network->name); |
ecddae64 DH |
587 | log_info("Sending on Socket/%d/%s/%s", |
588 | global_v6_socket, info->name, | |
98bd7ca0 DH |
589 | info->shared_network->name); |
590 | } else { | |
591 | log_info("Listening on Socket/%s", info->name); | |
592 | log_info("Sending on Socket/%s", info->name); | |
593 | } | |
594 | } | |
595 | } | |
596 | ||
4b8251a0 SR |
597 | /* |
598 | * Register an IPv6 socket bound to the link-local address of | |
599 | * the argument interface (used by clients on a multiple interface box, | |
600 | * vs. a server or a relay using the global IPv6 socket and running | |
601 | * *only* in a single instance). | |
602 | */ | |
603 | void | |
604 | if_register_linklocal6(struct interface_info *info) { | |
605 | int sock; | |
606 | int count; | |
607 | struct in6_addr *addr6 = NULL; | |
608 | int req_multi = 0; | |
609 | ||
610 | if (global_v6_socket >= 0) { | |
611 | log_fatal("Impossible condition at %s:%d", MDL); | |
612 | } | |
f6b8f48d | 613 | |
4b8251a0 SR |
614 | no_global_v6_socket = 1; |
615 | ||
616 | /* get the (?) link-local address */ | |
617 | for (count = 0; count < info->v6address_count; count++) { | |
618 | addr6 = &info->v6addresses[count]; | |
619 | if (IN6_IS_ADDR_LINKLOCAL(addr6)) | |
620 | break; | |
621 | } | |
622 | ||
623 | if (!addr6) { | |
624 | log_fatal("no link-local IPv6 address for %s", info->name); | |
625 | } | |
626 | ||
627 | sock = if_register_socket(info, AF_INET6, &req_multi, addr6); | |
628 | ||
629 | if (sock < 0) { | |
630 | log_fatal("if_register_socket for %s fails", info->name); | |
631 | } | |
632 | ||
633 | info->rfdesc = sock; | |
634 | info->wfdesc = sock; | |
635 | ||
636 | get_hw_addr(info->name, &info->hw_address); | |
637 | ||
638 | if (!quiet_interface_discovery) { | |
639 | if (info->shared_network != NULL) { | |
640 | log_info("Listening on Socket/%d/%s/%s", | |
f6b8f48d | 641 | global_v6_socket, info->name, |
4b8251a0 SR |
642 | info->shared_network->name); |
643 | log_info("Sending on Socket/%d/%s/%s", | |
644 | global_v6_socket, info->name, | |
645 | info->shared_network->name); | |
646 | } else { | |
647 | log_info("Listening on Socket/%s", info->name); | |
648 | log_info("Sending on Socket/%s", info->name); | |
649 | } | |
650 | } | |
651 | } | |
652 | ||
f6b8f48d | 653 | void |
98bd7ca0 | 654 | if_deregister6(struct interface_info *info) { |
4b8251a0 SR |
655 | /* client case */ |
656 | if (no_global_v6_socket) { | |
657 | close(info->rfdesc); | |
658 | info->rfdesc = -1; | |
659 | info->wfdesc = -1; | |
660 | } else if ((info->rfdesc == global_v6_socket) && | |
661 | (info->wfdesc == global_v6_socket) && | |
662 | (global_v6_socket_references > 0)) { | |
663 | /* Dereference the global v6 socket. */ | |
ecddae64 DH |
664 | global_v6_socket_references--; |
665 | info->rfdesc = -1; | |
666 | info->wfdesc = -1; | |
563f0b8a FD |
667 | #if defined(RELAY_PORT) |
668 | } else if (relay_port && | |
669 | (info->rfdesc == relay_port_v6_socket) && | |
670 | (info->wfdesc == relay_port_v6_socket) && | |
671 | (relay_port_v6_socket_references > 0)) { | |
672 | /* Dereference the relay port v6 socket. */ | |
673 | relay_port_v6_socket_references--; | |
674 | info->rfdesc = -1; | |
675 | info->wfdesc = -1; | |
676 | #endif | |
ecddae64 DH |
677 | } else { |
678 | log_fatal("Impossible condition at %s:%d", MDL); | |
679 | } | |
98bd7ca0 DH |
680 | |
681 | if (!quiet_interface_discovery) { | |
682 | if (info->shared_network != NULL) { | |
683 | log_info("Disabling input on Socket/%s/%s", info->name, | |
684 | info->shared_network->name); | |
685 | log_info("Disabling output on Socket/%s/%s", info->name, | |
686 | info->shared_network->name); | |
687 | } else { | |
688 | log_info("Disabling input on Socket/%s", info->name); | |
689 | log_info("Disabling output on Socket/%s", info->name); | |
690 | } | |
691 | } | |
ecddae64 | 692 | |
563f0b8a FD |
693 | if (!no_global_v6_socket) { |
694 | if (global_v6_socket_references == 0) { | |
695 | close(global_v6_socket); | |
696 | global_v6_socket = -1; | |
ecddae64 | 697 | |
563f0b8a FD |
698 | log_info("Unbound from *:%d", |
699 | (int) ntohs(local_port)); | |
700 | } | |
701 | #if defined(RELAY_PORT) | |
702 | if (relay_port && (relay_port_v6_socket_references == 0)) { | |
703 | close(relay_port_v6_socket); | |
704 | relay_port_v6_socket = -1; | |
705 | ||
706 | log_info("Unbound from relay port *:%d", | |
707 | (int) ntohs(relay_port)); | |
708 | } | |
709 | #endif | |
ecddae64 | 710 | } |
98bd7ca0 | 711 | } |
fe5b0fdd | 712 | #endif /* DHCPv6 */ |
98bd7ca0 | 713 | |
a1b705e5 | 714 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
4595a58c | 715 | ssize_t send_packet (interface, packet, raw, len, from, to, hto) |
e23c9055 | 716 | struct interface_info *interface; |
a8b53b42 TL |
717 | struct packet *packet; |
718 | struct dhcp_packet *raw; | |
719 | size_t len; | |
0a5d6860 | 720 | struct in_addr from; |
c857a7b6 | 721 | struct sockaddr_in *to; |
e23c9055 | 722 | struct hardware *hto; |
a8b53b42 | 723 | { |
5145810c TL |
724 | int result; |
725 | #ifdef IGNORE_HOSTUNREACH | |
726 | int retry = 0; | |
727 | do { | |
7cfeb916 SR |
728 | #endif |
729 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
730 | struct in_pktinfo pktinfo; | |
731 | ||
732 | if (interface->ifp != NULL) { | |
733 | memset(&pktinfo, 0, sizeof (pktinfo)); | |
734 | pktinfo.ipi_ifindex = interface->ifp->ifr_index; | |
735 | if (setsockopt(interface->wfdesc, IPPROTO_IP, | |
736 | IP_PKTINFO, (char *)&pktinfo, | |
f6b8f48d | 737 | sizeof(pktinfo)) < 0) |
5da634c5 TM |
738 | log_fatal("setsockopt: IP_PKTINFO for %s: %m", |
739 | (char*)(interface->ifp)); | |
7cfeb916 | 740 | } |
5145810c TL |
741 | #endif |
742 | result = sendto (interface -> wfdesc, (char *)raw, len, 0, | |
743 | (struct sockaddr *)to, sizeof *to); | |
744 | #ifdef IGNORE_HOSTUNREACH | |
13ee152c | 745 | } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && |
5145810c TL |
746 | result < 0 && |
747 | (errno == EHOSTUNREACH || | |
748 | errno == ECONNREFUSED) && | |
749 | retry++ < 10); | |
750 | #endif | |
74f45f96 | 751 | if (result < 0) { |
c5b0f529 | 752 | log_error ("send_packet: %m"); |
74f45f96 | 753 | if (errno == ENETUNREACH) |
a1b705e5 TL |
754 | log_error ("send_packet: please consult README file%s", |
755 | " regarding broadcast address."); | |
74f45f96 | 756 | } |
af361f2a | 757 | return result; |
a8b53b42 | 758 | } |
98bd7ca0 | 759 | |
a1b705e5 | 760 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
a8b53b42 | 761 | |
cff9b78f SK |
762 | #ifdef DHCPv6 |
763 | /* | |
f6b8f48d | 764 | * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will |
cff9b78f SK |
765 | * synthesize them (based on the BIND 9 technique). |
766 | */ | |
767 | ||
768 | #ifndef CMSG_LEN | |
769 | static size_t CMSG_LEN(size_t len) { | |
770 | size_t hdrlen; | |
771 | /* | |
772 | * Cast NULL so that any pointer arithmetic performed by CMSG_DATA | |
773 | * is correct. | |
774 | */ | |
775 | hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL)); | |
776 | return hdrlen + len; | |
777 | } | |
778 | #endif /* !CMSG_LEN */ | |
779 | ||
780 | #ifndef CMSG_SPACE | |
781 | static size_t CMSG_SPACE(size_t len) { | |
782 | struct msghdr msg; | |
783 | struct cmsghdr *cmsgp; | |
784 | ||
785 | /* | |
786 | * XXX: The buffer length is an ad-hoc value, but should be enough | |
787 | * in a practical sense. | |
788 | */ | |
789 | union { | |
790 | struct cmsghdr cmsg_sizer; | |
791 | u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024]; | |
792 | } dummybuf; | |
793 | ||
794 | memset(&msg, 0, sizeof(msg)); | |
795 | msg.msg_control = &dummybuf; | |
796 | msg.msg_controllen = sizeof(dummybuf); | |
797 | ||
798 | cmsgp = (struct cmsghdr *)&dummybuf; | |
799 | cmsgp->cmsg_len = CMSG_LEN(len); | |
800 | ||
801 | cmsgp = CMSG_NXTHDR(&msg, cmsgp); | |
802 | if (cmsgp != NULL) { | |
803 | return (char *)cmsgp - (char *)msg.msg_control; | |
804 | } else { | |
805 | return 0; | |
806 | } | |
807 | } | |
808 | #endif /* !CMSG_SPACE */ | |
809 | ||
810 | #endif /* DHCPv6 */ | |
811 | ||
7cfeb916 SR |
812 | #if defined(DHCPv6) || \ |
813 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
814 | defined(USE_V4_PKTINFO)) | |
57fbc772 SR |
815 | /* |
816 | * For both send_packet6() and receive_packet6() we need to allocate | |
817 | * space for the cmsg header information. We do this once and reuse | |
7cfeb916 SR |
818 | * the buffer. We also need the control buf for send_packet() and |
819 | * receive_packet() when we use a single socket and IP_PKTINFO to | |
820 | * send the packet out the correct interface. | |
57fbc772 SR |
821 | */ |
822 | static void *control_buf = NULL; | |
823 | static size_t control_buf_len = 0; | |
824 | ||
825 | static void | |
826 | allocate_cmsg_cbuf(void) { | |
827 | control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); | |
828 | control_buf = dmalloc(control_buf_len, MDL); | |
829 | return; | |
830 | } | |
7cfeb916 | 831 | #endif /* DHCPv6, IP_PKTINFO ... */ |
57fbc772 | 832 | |
7cfeb916 | 833 | #ifdef DHCPv6 |
f6b8f48d TM |
834 | /* |
835 | * For both send_packet6() and receive_packet6() we need to use the | |
20ae1aff | 836 | * sendmsg()/recvmsg() functions rather than the simpler send()/recv() |
98bd7ca0 DH |
837 | * functions. |
838 | * | |
839 | * In the case of send_packet6(), we need to do this in order to insure | |
f6b8f48d TM |
840 | * that the reply packet leaves on the same interface that it arrived |
841 | * on. | |
98bd7ca0 | 842 | * |
f6b8f48d | 843 | * In the case of receive_packet6(), we need to do this in order to |
98bd7ca0 DH |
844 | * get the IP address the packet was sent to. This is used to identify |
845 | * whether a packet is multicast or unicast. | |
846 | * | |
847 | * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg. | |
848 | * | |
849 | * Also see the sections in RFC 3542 about IPV6_PKTINFO. | |
850 | */ | |
851 | ||
852 | /* Send an IPv6 packet */ | |
853 | ssize_t send_packet6(struct interface_info *interface, | |
6705543f | 854 | const unsigned char *raw, size_t len, |
98bd7ca0 DH |
855 | struct sockaddr_in6 *to) { |
856 | struct msghdr m; | |
857 | struct iovec v; | |
4b8251a0 | 858 | struct sockaddr_in6 dst; |
98bd7ca0 DH |
859 | int result; |
860 | struct in6_pktinfo *pktinfo; | |
98bd7ca0 | 861 | struct cmsghdr *cmsg; |
4b8251a0 | 862 | unsigned int ifindex; |
57fbc772 SR |
863 | |
864 | /* | |
865 | * If necessary allocate space for the control message header. | |
866 | * The space is common between send and receive. | |
867 | */ | |
868 | ||
869 | if (control_buf == NULL) { | |
870 | allocate_cmsg_cbuf(); | |
871 | if (control_buf == NULL) { | |
872 | log_error("send_packet6: unable to allocate cmsg header"); | |
873 | return(ENOMEM); | |
874 | } | |
875 | } | |
876 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
877 | |
878 | /* | |
879 | * Initialize our message header structure. | |
880 | */ | |
881 | memset(&m, 0, sizeof(m)); | |
882 | ||
883 | /* | |
884 | * Set the target address we're sending to. | |
4b8251a0 | 885 | * Enforce the scope ID for bogus BSDs. |
98bd7ca0 | 886 | */ |
4b8251a0 SR |
887 | memcpy(&dst, to, sizeof(dst)); |
888 | m.msg_name = &dst; | |
889 | m.msg_namelen = sizeof(dst); | |
890 | ifindex = if_nametoindex(interface->name); | |
0cd94b5e TM |
891 | |
892 | // Per OpenBSD patch-common_socket_c,v 1.7 2018/03/06 08:37:39 sthen Exp | |
893 | // always set the scope id. We'll leave the test for no global socket | |
894 | // in place for all others. | |
895 | #ifndef __OpenBSD__ | |
4b8251a0 | 896 | if (no_global_v6_socket) |
0cd94b5e | 897 | #endif |
4b8251a0 | 898 | dst.sin6_scope_id = ifindex; |
98bd7ca0 DH |
899 | |
900 | /* | |
f6b8f48d TM |
901 | * Set the data buffer we're sending. (Using this wacky |
902 | * "scatter-gather" stuff... we only have a single chunk | |
98bd7ca0 DH |
903 | * of data to send, so we declare a single vector entry.) |
904 | */ | |
905 | v.iov_base = (char *)raw; | |
906 | v.iov_len = len; | |
907 | m.msg_iov = &v; | |
908 | m.msg_iovlen = 1; | |
909 | ||
910 | /* | |
911 | * Setting the interface is a bit more involved. | |
f6b8f48d TM |
912 | * |
913 | * We have to create a "control message", and set that to | |
98bd7ca0 DH |
914 | * define the IPv6 packet information. We could set the |
915 | * source address if we wanted, but we can safely let the | |
f6b8f48d | 916 | * kernel decide what that should be. |
98bd7ca0 | 917 | */ |
57fbc772 SR |
918 | m.msg_control = control_buf; |
919 | m.msg_controllen = control_buf_len; | |
98bd7ca0 | 920 | cmsg = CMSG_FIRSTHDR(&m); |
dc9d7b08 | 921 | INSIST(cmsg != NULL); |
98bd7ca0 DH |
922 | cmsg->cmsg_level = IPPROTO_IPV6; |
923 | cmsg->cmsg_type = IPV6_PKTINFO; | |
924 | cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo)); | |
925 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
926 | memset(pktinfo, 0, sizeof(*pktinfo)); | |
a2a0f98c | 927 | pktinfo->ipi6_addr = local_address6; |
4b8251a0 | 928 | pktinfo->ipi6_ifindex = ifindex; |
98bd7ca0 DH |
929 | |
930 | result = sendmsg(interface->wfdesc, &m, 0); | |
931 | if (result < 0) { | |
932 | log_error("send_packet6: %m"); | |
933 | } | |
934 | return result; | |
935 | } | |
fe5b0fdd | 936 | #endif /* DHCPv6 */ |
98bd7ca0 | 937 | |
c857a7b6 | 938 | #ifdef USE_SOCKET_RECEIVE |
4595a58c | 939 | ssize_t receive_packet (interface, buf, len, from, hfrom) |
e23c9055 TL |
940 | struct interface_info *interface; |
941 | unsigned char *buf; | |
942 | size_t len; | |
943 | struct sockaddr_in *from; | |
944 | struct hardware *hfrom; | |
a8b53b42 | 945 | { |
865afd5e | 946 | #if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)) |
d1f313b3 | 947 | SOCKLEN_T flen = sizeof *from; |
7cfeb916 | 948 | #endif |
5145810c | 949 | int result; |
e23c9055 | 950 | |
98bd7ca0 DH |
951 | /* |
952 | * The normal Berkeley socket interface doesn't give us any way | |
953 | * to know what hardware interface we received the message on, | |
954 | * but we should at least make sure the structure is emptied. | |
955 | */ | |
956 | memset(hfrom, 0, sizeof(*hfrom)); | |
957 | ||
5145810c TL |
958 | #ifdef IGNORE_HOSTUNREACH |
959 | int retry = 0; | |
960 | do { | |
961 | #endif | |
7cfeb916 SR |
962 | |
963 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
964 | struct msghdr m; | |
965 | struct iovec v; | |
966 | struct cmsghdr *cmsg; | |
967 | struct in_pktinfo *pktinfo; | |
968 | unsigned int ifindex; | |
7cfeb916 SR |
969 | |
970 | /* | |
971 | * If necessary allocate space for the control message header. | |
972 | * The space is common between send and receive. | |
973 | */ | |
974 | if (control_buf == NULL) { | |
975 | allocate_cmsg_cbuf(); | |
976 | if (control_buf == NULL) { | |
977 | log_error("receive_packet: unable to allocate cmsg " | |
978 | "header"); | |
979 | return(ENOMEM); | |
980 | } | |
981 | } | |
982 | memset(control_buf, 0, control_buf_len); | |
983 | ||
984 | /* | |
985 | * Initialize our message header structure. | |
986 | */ | |
987 | memset(&m, 0, sizeof(m)); | |
988 | ||
989 | /* | |
990 | * Point so we can get the from address. | |
991 | */ | |
992 | m.msg_name = from; | |
993 | m.msg_namelen = sizeof(*from); | |
994 | ||
995 | /* | |
f6b8f48d | 996 | * Set the data buffer we're receiving. (Using this wacky |
7cfeb916 SR |
997 | * "scatter-gather" stuff... but we that doesn't really make |
998 | * sense for us, so we use a single vector entry.) | |
999 | */ | |
1000 | v.iov_base = buf; | |
1001 | v.iov_len = len; | |
1002 | m.msg_iov = &v; | |
1003 | m.msg_iovlen = 1; | |
1004 | ||
1005 | /* | |
1006 | * Getting the interface is a bit more involved. | |
1007 | * | |
f6b8f48d TM |
1008 | * We set up some space for a "control message". We have |
1009 | * previously asked the kernel to give us packet | |
7cfeb916 | 1010 | * information (when we initialized the interface), so we |
865afd5e | 1011 | * should get the interface index from that. |
7cfeb916 SR |
1012 | */ |
1013 | m.msg_control = control_buf; | |
1014 | m.msg_controllen = control_buf_len; | |
1015 | ||
1016 | result = recvmsg(interface->rfdesc, &m, 0); | |
1017 | ||
1018 | if (result >= 0) { | |
1019 | /* | |
1020 | * If we did read successfully, then we need to loop | |
f6b8f48d | 1021 | * through the control messages we received and |
865afd5e | 1022 | * find the one with our inteface index. |
7cfeb916 | 1023 | */ |
7cfeb916 SR |
1024 | cmsg = CMSG_FIRSTHDR(&m); |
1025 | while (cmsg != NULL) { | |
f6b8f48d | 1026 | if ((cmsg->cmsg_level == IPPROTO_IP) && |
7cfeb916 SR |
1027 | (cmsg->cmsg_type == IP_PKTINFO)) { |
1028 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
1029 | ifindex = pktinfo->ipi_ifindex; | |
1030 | /* | |
f6b8f48d | 1031 | * We pass the ifindex back to the caller |
7cfeb916 | 1032 | * using the unused hfrom parameter avoiding |
f6b8f48d | 1033 | * interface changes between sockets and |
7cfeb916 SR |
1034 | * the discover code. |
1035 | */ | |
1036 | memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex)); | |
865afd5e | 1037 | return (result); |
7cfeb916 SR |
1038 | } |
1039 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
1040 | } | |
865afd5e SR |
1041 | |
1042 | /* | |
1043 | * We didn't find the necessary control message | |
1044 | * flag it as an error | |
1045 | */ | |
1046 | result = -1; | |
1047 | errno = EIO; | |
7cfeb916 SR |
1048 | } |
1049 | #else | |
865afd5e SR |
1050 | result = recvfrom(interface -> rfdesc, (char *)buf, len, 0, |
1051 | (struct sockaddr *)from, &flen); | |
7cfeb916 | 1052 | #endif /* IP_PKTINFO ... */ |
5145810c TL |
1053 | #ifdef IGNORE_HOSTUNREACH |
1054 | } while (result < 0 && | |
1055 | (errno == EHOSTUNREACH || | |
1056 | errno == ECONNREFUSED) && | |
1057 | retry++ < 10); | |
1058 | #endif | |
865afd5e | 1059 | return (result); |
c857a7b6 | 1060 | } |
98bd7ca0 | 1061 | |
c857a7b6 | 1062 | #endif /* USE_SOCKET_RECEIVE */ |
c5568eb5 | 1063 | |
fe5b0fdd | 1064 | #ifdef DHCPv6 |
f6b8f48d TM |
1065 | ssize_t |
1066 | receive_packet6(struct interface_info *interface, | |
1067 | unsigned char *buf, size_t len, | |
ecddae64 DH |
1068 | struct sockaddr_in6 *from, struct in6_addr *to_addr, |
1069 | unsigned int *if_idx) | |
1070 | { | |
98bd7ca0 DH |
1071 | struct msghdr m; |
1072 | struct iovec v; | |
98bd7ca0 DH |
1073 | int result; |
1074 | struct cmsghdr *cmsg; | |
1075 | struct in6_pktinfo *pktinfo; | |
57fbc772 SR |
1076 | |
1077 | /* | |
1078 | * If necessary allocate space for the control message header. | |
1079 | * The space is common between send and receive. | |
1080 | */ | |
1081 | if (control_buf == NULL) { | |
1082 | allocate_cmsg_cbuf(); | |
1083 | if (control_buf == NULL) { | |
436e808a SR |
1084 | log_error("receive_packet6: unable to allocate cmsg " |
1085 | "header"); | |
57fbc772 SR |
1086 | return(ENOMEM); |
1087 | } | |
1088 | } | |
1089 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
1090 | |
1091 | /* | |
1092 | * Initialize our message header structure. | |
1093 | */ | |
1094 | memset(&m, 0, sizeof(m)); | |
1095 | ||
1096 | /* | |
1097 | * Point so we can get the from address. | |
1098 | */ | |
1099 | m.msg_name = from; | |
1100 | m.msg_namelen = sizeof(*from); | |
1101 | ||
1102 | /* | |
f6b8f48d | 1103 | * Set the data buffer we're receiving. (Using this wacky |
98bd7ca0 DH |
1104 | * "scatter-gather" stuff... but we that doesn't really make |
1105 | * sense for us, so we use a single vector entry.) | |
1106 | */ | |
1107 | v.iov_base = buf; | |
1108 | v.iov_len = len; | |
1109 | m.msg_iov = &v; | |
1110 | m.msg_iovlen = 1; | |
1111 | ||
1112 | /* | |
1113 | * Getting the interface is a bit more involved. | |
1114 | * | |
f6b8f48d TM |
1115 | * We set up some space for a "control message". We have |
1116 | * previously asked the kernel to give us packet | |
98bd7ca0 DH |
1117 | * information (when we initialized the interface), so we |
1118 | * should get the destination address from that. | |
1119 | */ | |
57fbc772 SR |
1120 | m.msg_control = control_buf; |
1121 | m.msg_controllen = control_buf_len; | |
98bd7ca0 DH |
1122 | |
1123 | result = recvmsg(interface->rfdesc, &m, 0); | |
1124 | ||
1125 | if (result >= 0) { | |
1126 | /* | |
1127 | * If we did read successfully, then we need to loop | |
f6b8f48d | 1128 | * through the control messages we received and |
98bd7ca0 | 1129 | * find the one with our destination address. |
98bd7ca0 | 1130 | */ |
98bd7ca0 DH |
1131 | cmsg = CMSG_FIRSTHDR(&m); |
1132 | while (cmsg != NULL) { | |
f6b8f48d | 1133 | if ((cmsg->cmsg_level == IPPROTO_IPV6) && |
98bd7ca0 DH |
1134 | (cmsg->cmsg_type == IPV6_PKTINFO)) { |
1135 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
1136 | *to_addr = pktinfo->ipi6_addr; | |
ecddae64 | 1137 | *if_idx = pktinfo->ipi6_ifindex; |
865afd5e SR |
1138 | |
1139 | return (result); | |
98bd7ca0 DH |
1140 | } |
1141 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
1142 | } | |
865afd5e SR |
1143 | |
1144 | /* | |
1145 | * We didn't find the necessary control message | |
1146 | * flag is as an error | |
1147 | */ | |
1148 | result = -1; | |
1149 | errno = EIO; | |
98bd7ca0 DH |
1150 | } |
1151 | ||
865afd5e | 1152 | return (result); |
98bd7ca0 | 1153 | } |
fe5b0fdd | 1154 | #endif /* DHCPv6 */ |
98bd7ca0 | 1155 | |
e15381fc | 1156 | #if defined (USE_SOCKET_FALLBACK) |
c5568eb5 TL |
1157 | /* This just reads in a packet and silently discards it. */ |
1158 | ||
acc21512 | 1159 | isc_result_t fallback_discard (object) |
e92653f1 | 1160 | omapi_object_t *object; |
c5568eb5 TL |
1161 | { |
1162 | char buf [1540]; | |
1163 | struct sockaddr_in from; | |
d1f313b3 | 1164 | SOCKLEN_T flen = sizeof from; |
c01f1285 | 1165 | int status; |
acc21512 TL |
1166 | struct interface_info *interface; |
1167 | ||
1168 | if (object -> type != dhcp_type_interface) | |
98bf1607 | 1169 | return DHCP_R_INVALIDARG; |
acc21512 | 1170 | interface = (struct interface_info *)object; |
c5568eb5 | 1171 | |
c01f1285 TL |
1172 | status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, |
1173 | (struct sockaddr *)&from, &flen); | |
4089dd21 M |
1174 | #if defined (DEBUG) |
1175 | /* Only report fallback discard errors if we're debugging. */ | |
acc21512 | 1176 | if (status < 0) { |
8ae2d595 | 1177 | log_error ("fallback_discard: %m"); |
acc21512 TL |
1178 | return ISC_R_UNEXPECTED; |
1179 | } | |
dd9237c3 TM |
1180 | #else |
1181 | /* ignore the fact that status value is never used */ | |
1182 | IGNORE_UNUSED(status); | |
4089dd21 | 1183 | #endif |
acc21512 | 1184 | return ISC_R_SUCCESS; |
c5568eb5 | 1185 | } |
a1b705e5 | 1186 | #endif /* USE_SOCKET_FALLBACK */ |
d2bc90bd | 1187 | |
a1b705e5 | 1188 | #if defined (USE_SOCKET_SEND) |
21d21e91 TL |
1189 | int can_unicast_without_arp (ip) |
1190 | struct interface_info *ip; | |
d2bc90bd TL |
1191 | { |
1192 | return 0; | |
1193 | } | |
1194 | ||
21d21e91 TL |
1195 | int can_receive_unicast_unconfigured (ip) |
1196 | struct interface_info *ip; | |
b547818b | 1197 | { |
a1b705e5 TL |
1198 | #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) |
1199 | return 1; | |
1200 | #else | |
b547818b | 1201 | return 0; |
a1b705e5 | 1202 | #endif |
b547818b TL |
1203 | } |
1204 | ||
5cefe5e5 TL |
1205 | int supports_multiple_interfaces (ip) |
1206 | struct interface_info *ip; | |
1207 | { | |
7cfeb916 SR |
1208 | #if defined(SO_BINDTODEVICE) || \ |
1209 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
1210 | defined(USE_V4_PKTINFO)) | |
1211 | return(1); | |
5cefe5e5 | 1212 | #else |
7cfeb916 | 1213 | return(0); |
5cefe5e5 TL |
1214 | #endif |
1215 | } | |
1216 | ||
d2bc90bd TL |
1217 | /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, |
1218 | do not. */ | |
1219 | ||
1220 | void maybe_setup_fallback () | |
1221 | { | |
a1b705e5 | 1222 | #if defined (USE_SOCKET_FALLBACK) |
acc21512 | 1223 | isc_result_t status; |
950c6a06 TL |
1224 | struct interface_info *fbi = (struct interface_info *)0; |
1225 | if (setup_fallback (&fbi, MDL)) { | |
4b8251a0 | 1226 | fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0, NULL); |
a47105d6 TL |
1227 | fbi -> rfdesc = fbi -> wfdesc; |
1228 | log_info ("Sending on Socket/%s%s%s", | |
1229 | fbi -> name, | |
1230 | (fbi -> shared_network ? "/" : ""), | |
1231 | (fbi -> shared_network ? | |
1232 | fbi -> shared_network -> name : "")); | |
f6b8f48d | 1233 | |
e48c5c5c | 1234 | status = omapi_register_io_object ((omapi_object_t *)fbi, |
acc21512 TL |
1235 | if_readsocket, 0, |
1236 | fallback_discard, 0, 0); | |
1237 | if (status != ISC_R_SUCCESS) | |
1238 | log_fatal ("Can't register I/O handle for %s: %s", | |
1239 | fbi -> name, isc_result_totext (status)); | |
950c6a06 | 1240 | interface_dereference (&fbi, MDL); |
d2bc90bd TL |
1241 | } |
1242 | #endif | |
1243 | } | |
7cfeb916 SR |
1244 | |
1245 | ||
1246 | #if defined(sun) && defined(USE_V4_PKTINFO) | |
1247 | /* This code assumes the existence of SIOCGLIFHWADDR */ | |
1248 | void | |
1249 | get_hw_addr(const char *name, struct hardware *hw) { | |
1250 | struct sockaddr_dl *dladdrp; | |
b047bd38 | 1251 | int sock, i; |
7cfeb916 SR |
1252 | struct lifreq lifr; |
1253 | ||
1254 | memset(&lifr, 0, sizeof (lifr)); | |
1255 | (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); | |
1256 | /* | |
1257 | * Check if the interface is a virtual or IPMP interface - in those | |
1258 | * cases it has no hw address, so generate a random one. | |
1259 | */ | |
1260 | if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || | |
1261 | ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) { | |
1262 | if (sock != -1) | |
1263 | (void) close(sock); | |
1264 | ||
1265 | #ifdef DHCPv6 | |
1266 | /* | |
1267 | * If approrpriate try this with an IPv6 socket | |
1268 | */ | |
1269 | if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 && | |
1270 | ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) { | |
1271 | goto flag_check; | |
1272 | } | |
1273 | if (sock != -1) | |
1274 | (void) close(sock); | |
1275 | #endif | |
1276 | log_fatal("Couldn't get interface flags for %s: %m", name); | |
1277 | ||
1278 | } | |
1279 | ||
1280 | flag_check: | |
1281 | if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) { | |
1282 | hw->hlen = sizeof (hw->hbuf); | |
1283 | srandom((long)gethrtime()); | |
1284 | ||
b047bd38 SR |
1285 | hw->hbuf[0] = HTYPE_IPMP; |
1286 | for (i = 1; i < hw->hlen; ++i) { | |
7cfeb916 SR |
1287 | hw->hbuf[i] = random() % 256; |
1288 | } | |
1289 | ||
1290 | if (sock != -1) | |
1291 | (void) close(sock); | |
1292 | return; | |
1293 | } | |
1294 | ||
1295 | if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0) | |
1296 | log_fatal("Couldn't get interface hardware address for %s: %m", | |
1297 | name); | |
1298 | dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr; | |
b047bd38 SR |
1299 | hw->hlen = dladdrp->sdl_alen+1; |
1300 | switch (dladdrp->sdl_type) { | |
1301 | case DL_CSMACD: /* IEEE 802.3 */ | |
1302 | case DL_ETHER: | |
1303 | hw->hbuf[0] = HTYPE_ETHER; | |
1304 | break; | |
1305 | case DL_TPR: | |
1306 | hw->hbuf[0] = HTYPE_IEEE802; | |
1307 | break; | |
1308 | case DL_FDDI: | |
1309 | hw->hbuf[0] = HTYPE_FDDI; | |
1310 | break; | |
1311 | case DL_IB: | |
1312 | hw->hbuf[0] = HTYPE_INFINIBAND; | |
1313 | break; | |
1314 | default: | |
1315 | log_fatal("%s: unsupported DLPI MAC type %lu", name, | |
1316 | (unsigned long)dladdrp->sdl_type); | |
1317 | } | |
1318 | ||
1319 | memcpy(hw->hbuf+1, LLADDR(dladdrp), hw->hlen-1); | |
7cfeb916 SR |
1320 | |
1321 | if (sock != -1) | |
1322 | (void) close(sock); | |
1323 | } | |
1324 | #endif /* defined(sun) */ | |
1325 | ||
a1b705e5 | 1326 | #endif /* USE_SOCKET_SEND */ |