]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* socket.c |
2 | ||
3 | BSD socket interface code... */ | |
4 | ||
5 | /* | |
4ad524bd | 6 | * Copyright (c) 2004-2020 by 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 DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
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 | |
33 | * this sockopt will be automatically enabled. | |
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 | |
98bd7ca0 DH |
153 | /* |
154 | * Set up the address we're going to bind to, depending on the | |
155 | * address family. | |
156 | */ | |
157 | memset(&name, 0, sizeof(name)); | |
98bf1607 | 158 | switch (family) { |
fe5b0fdd | 159 | #ifdef DHCPv6 |
98bf1607 SR |
160 | case AF_INET6: |
161 | addr6 = (struct sockaddr_in6 *)&name; | |
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: | |
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; | |
299 | if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, | |
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 DH |
308 | /* |
309 | * If we turn on IPV6_PKTINFO, we will be able to receive | |
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 */ | |
317 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, | |
318 | &on, sizeof(on)) != 0) { | |
319 | log_fatal("setsockopt: IPV6_RECVPKTINFO: %m"); | |
320 | } | |
321 | #else | |
322 | /* RFC2292 */ | |
323 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, | |
324 | &on, sizeof(on)) != 0) { | |
325 | log_fatal("setsockopt: IPV6_PKTINFO: %m"); | |
326 | } | |
327 | #endif | |
328 | } | |
7de20a95 | 329 | |
fe5b0fdd | 330 | #endif /* DHCPv6 */ |
98bd7ca0 | 331 | |
e23c9055 | 332 | return sock; |
d7837182 | 333 | } |
c2b5b5e8 TM |
334 | |
335 | #ifdef DHCPv6 | |
336 | void set_multicast_hop_limit(struct interface_info* info, int hop_limit) { | |
337 | if (setsockopt(info->wfdesc, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, | |
338 | &hop_limit, sizeof(int)) < 0) { | |
339 | log_fatal("setMulticaseHopLimit: IPV6_MULTICAST_HOPS: %m"); | |
340 | } | |
341 | ||
342 | log_debug("Setting hop count limit to %d for interface %s", | |
343 | hop_limit, info->name); | |
344 | ||
345 | } | |
346 | #endif /* DHCPv6 */ | |
347 | ||
a1b705e5 | 348 | #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ |
d7837182 | 349 | |
a1b705e5 | 350 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
c3585217 | 351 | void if_register_send (info) |
c857a7b6 | 352 | struct interface_info *info; |
d7837182 | 353 | { |
e23c9055 | 354 | #ifndef USE_SOCKET_RECEIVE |
4b8251a0 | 355 | info->wfdesc = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
356 | /* If this is a normal IPv4 address, get the hardware address. */ |
357 | if (strcmp(info->name, "fallback") != 0) | |
358 | get_hw_addr(info->name, &info->hw_address); | |
2e96d6e7 TL |
359 | #if defined (USE_SOCKET_FALLBACK) |
360 | /* Fallback only registers for send, but may need to receive as | |
361 | well. */ | |
7cfeb916 | 362 | info->rfdesc = info->wfdesc; |
2e96d6e7 | 363 | #endif |
e23c9055 | 364 | #else |
7cfeb916 | 365 | info->wfdesc = info->rfdesc; |
e23c9055 | 366 | #endif |
3648a2a1 | 367 | if (!quiet_interface_discovery) |
74f45f96 | 368 | log_info ("Sending on Socket/%s%s%s", |
7cfeb916 SR |
369 | info->name, |
370 | (info->shared_network ? "/" : ""), | |
371 | (info->shared_network ? | |
372 | info->shared_network->name : "")); | |
d7837182 | 373 | } |
7203e8ee | 374 | |
3782236d | 375 | #if defined (USE_SOCKET_SEND) |
7203e8ee TL |
376 | void if_deregister_send (info) |
377 | struct interface_info *info; | |
378 | { | |
379 | #ifndef USE_SOCKET_RECEIVE | |
380 | close (info -> wfdesc); | |
381 | #endif | |
382 | info -> wfdesc = -1; | |
383 | ||
384 | if (!quiet_interface_discovery) | |
385 | log_info ("Disabling output on Socket/%s%s%s", | |
386 | info -> name, | |
387 | (info -> shared_network ? "/" : ""), | |
388 | (info -> shared_network ? | |
389 | info -> shared_network -> name : "")); | |
390 | } | |
3782236d | 391 | #endif /* USE_SOCKET_SEND */ |
a1b705e5 | 392 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
d7837182 | 393 | |
e23c9055 | 394 | #ifdef USE_SOCKET_RECEIVE |
c3585217 | 395 | void if_register_receive (info) |
e23c9055 | 396 | struct interface_info *info; |
e23c9055 | 397 | { |
7cfeb916 SR |
398 | |
399 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
400 | if (global_v4_socket_references == 0) { | |
4b8251a0 | 401 | global_v4_socket = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
402 | if (global_v4_socket < 0) { |
403 | /* | |
404 | * if_register_socket() fatally logs if it fails to | |
405 | * create a socket, this is just a sanity check. | |
406 | */ | |
407 | log_fatal("Failed to create AF_INET socket %s:%d", | |
408 | MDL); | |
409 | } | |
410 | } | |
411 | ||
412 | info->rfdesc = global_v4_socket; | |
413 | global_v4_socket_references++; | |
414 | #else | |
e23c9055 TL |
415 | /* If we're using the socket API for sending and receiving, |
416 | we don't need to register this interface twice. */ | |
4b8251a0 | 417 | info->rfdesc = if_register_socket(info, AF_INET, 0, NULL); |
7cfeb916 SR |
418 | #endif /* IP_PKTINFO... */ |
419 | /* If this is a normal IPv4 address, get the hardware address. */ | |
420 | if (strcmp(info->name, "fallback") != 0) | |
421 | get_hw_addr(info->name, &info->hw_address); | |
422 | ||
3648a2a1 | 423 | if (!quiet_interface_discovery) |
74f45f96 | 424 | log_info ("Listening on Socket/%s%s%s", |
7cfeb916 SR |
425 | info->name, |
426 | (info->shared_network ? "/" : ""), | |
427 | (info->shared_network ? | |
428 | info->shared_network->name : "")); | |
e23c9055 | 429 | } |
7203e8ee TL |
430 | |
431 | void if_deregister_receive (info) | |
432 | struct interface_info *info; | |
433 | { | |
7cfeb916 SR |
434 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) |
435 | /* Dereference the global v4 socket. */ | |
436 | if ((info->rfdesc == global_v4_socket) && | |
7cfeb916 SR |
437 | (global_v4_socket_references > 0)) { |
438 | global_v4_socket_references--; | |
439 | info->rfdesc = -1; | |
440 | } else { | |
441 | log_fatal("Impossible condition at %s:%d", MDL); | |
442 | } | |
7203e8ee | 443 | |
7cfeb916 SR |
444 | if (global_v4_socket_references == 0) { |
445 | close(global_v4_socket); | |
446 | global_v4_socket = -1; | |
447 | } | |
448 | #else | |
449 | close(info->rfdesc); | |
450 | info->rfdesc = -1; | |
451 | #endif /* IP_PKTINFO... */ | |
7203e8ee TL |
452 | if (!quiet_interface_discovery) |
453 | log_info ("Disabling input on Socket/%s%s%s", | |
454 | info -> name, | |
455 | (info -> shared_network ? "/" : ""), | |
456 | (info -> shared_network ? | |
457 | info -> shared_network -> name : "")); | |
05af5898 | 458 | } |
e23c9055 TL |
459 | #endif /* USE_SOCKET_RECEIVE */ |
460 | ||
98bd7ca0 | 461 | |
fe5b0fdd | 462 | #ifdef DHCPv6 |
ecddae64 DH |
463 | /* |
464 | * This function joins the interface to DHCPv6 multicast groups so we will | |
465 | * receive multicast messages. | |
466 | */ | |
467 | static void | |
468 | if_register_multicast(struct interface_info *info) { | |
469 | int sock = info->rfdesc; | |
470 | struct ipv6_mreq mreq; | |
471 | ||
472 | if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, | |
473 | &mreq.ipv6mr_multiaddr) <= 0) { | |
474 | log_fatal("inet_pton: unable to convert '%s'", | |
475 | All_DHCP_Relay_Agents_and_Servers); | |
476 | } | |
477 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
478 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
479 | &mreq, sizeof(mreq)) < 0) { | |
480 | log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); | |
481 | } | |
482 | ||
483 | /* | |
484 | * The relay agent code sets the streams so you know which way | |
485 | * is up and down. But a relay agent shouldn't join to the | |
486 | * Server address, or else you get fun loops. So up or down | |
487 | * doesn't matter, we're just using that config to sense this is | |
488 | * a relay agent. | |
489 | */ | |
490 | if ((info->flags & INTERFACE_STREAMS) == 0) { | |
491 | if (inet_pton(AF_INET6, All_DHCP_Servers, | |
492 | &mreq.ipv6mr_multiaddr) <= 0) { | |
493 | log_fatal("inet_pton: unable to convert '%s'", | |
494 | All_DHCP_Servers); | |
495 | } | |
496 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
497 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
498 | &mreq, sizeof(mreq)) < 0) { | |
499 | log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); | |
500 | } | |
501 | } | |
502 | } | |
503 | ||
98bd7ca0 DH |
504 | void |
505 | if_register6(struct interface_info *info, int do_multicast) { | |
ecddae64 DH |
506 | /* Bounce do_multicast to a stack variable because we may change it. */ |
507 | int req_multi = do_multicast; | |
508 | ||
4b8251a0 SR |
509 | if (no_global_v6_socket) { |
510 | log_fatal("Impossible condition at %s:%d", MDL); | |
511 | } | |
512 | ||
563f0b8a FD |
513 | #if defined(RELAY_PORT) |
514 | if (!relay_port || | |
515 | ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) { | |
516 | #endif | |
ecddae64 DH |
517 | if (global_v6_socket_references == 0) { |
518 | global_v6_socket = if_register_socket(info, AF_INET6, | |
4b8251a0 | 519 | &req_multi, NULL); |
ecddae64 DH |
520 | if (global_v6_socket < 0) { |
521 | /* | |
522 | * if_register_socket() fatally logs if it fails to | |
523 | * create a socket, this is just a sanity check. | |
524 | */ | |
525 | log_fatal("Impossible condition at %s:%d", MDL); | |
a2a0f98c FD |
526 | } else if (bind_local_address6) { |
527 | char addr6_str[INET6_ADDRSTRLEN]; | |
528 | ||
529 | if (inet_ntop(AF_INET6, | |
530 | &local_address6, | |
531 | addr6_str, | |
532 | sizeof(addr6_str)) == NULL) { | |
533 | log_fatal("inet_ntop: unable to convert " | |
534 | "local-address6"); | |
535 | } | |
536 | log_info("Bound to [%s]:%d", | |
537 | addr6_str, | |
538 | (int) ntohs(local_port)); | |
ecddae64 | 539 | } else { |
a2a0f98c | 540 | log_info("Bound to *:%d", (int) ntohs(local_port)); |
ecddae64 DH |
541 | } |
542 | } | |
543 | ||
544 | info->rfdesc = global_v6_socket; | |
545 | info->wfdesc = global_v6_socket; | |
546 | global_v6_socket_references++; | |
547 | ||
563f0b8a FD |
548 | #if defined(RELAY_PORT) |
549 | } else { | |
550 | /* | |
551 | * If relay port is defined, we need to register one | |
552 | * IPv6 UPD socket to handle upstream server or relay agent | |
553 | * with a non-547 UDP local port. | |
554 | */ | |
555 | if ((relay_port_v6_socket_references == 0) && | |
556 | ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) { | |
557 | relay_port_v6_socket = if_register_socket(info, AF_INET6, | |
558 | &req_multi, NULL); | |
559 | if (relay_port_v6_socket < 0) { | |
560 | log_fatal("Impossible condition at %s:%d", MDL); | |
561 | } else { | |
562 | log_info("Bound to relay port *:%d", | |
563 | (int) ntohs(relay_port)); | |
564 | } | |
565 | } | |
566 | info->rfdesc = relay_port_v6_socket; | |
567 | info->wfdesc = relay_port_v6_socket; | |
568 | relay_port_v6_socket_references++; | |
569 | } | |
570 | #endif | |
571 | ||
ecddae64 DH |
572 | if (req_multi) |
573 | if_register_multicast(info); | |
574 | ||
575 | get_hw_addr(info->name, &info->hw_address); | |
576 | ||
98bd7ca0 DH |
577 | if (!quiet_interface_discovery) { |
578 | if (info->shared_network != NULL) { | |
ecddae64 DH |
579 | log_info("Listening on Socket/%d/%s/%s", |
580 | global_v6_socket, info->name, | |
98bd7ca0 | 581 | info->shared_network->name); |
ecddae64 DH |
582 | log_info("Sending on Socket/%d/%s/%s", |
583 | global_v6_socket, info->name, | |
98bd7ca0 DH |
584 | info->shared_network->name); |
585 | } else { | |
586 | log_info("Listening on Socket/%s", info->name); | |
587 | log_info("Sending on Socket/%s", info->name); | |
588 | } | |
589 | } | |
590 | } | |
591 | ||
4b8251a0 SR |
592 | /* |
593 | * Register an IPv6 socket bound to the link-local address of | |
594 | * the argument interface (used by clients on a multiple interface box, | |
595 | * vs. a server or a relay using the global IPv6 socket and running | |
596 | * *only* in a single instance). | |
597 | */ | |
598 | void | |
599 | if_register_linklocal6(struct interface_info *info) { | |
600 | int sock; | |
601 | int count; | |
602 | struct in6_addr *addr6 = NULL; | |
603 | int req_multi = 0; | |
604 | ||
605 | if (global_v6_socket >= 0) { | |
606 | log_fatal("Impossible condition at %s:%d", MDL); | |
607 | } | |
608 | ||
609 | no_global_v6_socket = 1; | |
610 | ||
611 | /* get the (?) link-local address */ | |
612 | for (count = 0; count < info->v6address_count; count++) { | |
613 | addr6 = &info->v6addresses[count]; | |
614 | if (IN6_IS_ADDR_LINKLOCAL(addr6)) | |
615 | break; | |
616 | } | |
617 | ||
618 | if (!addr6) { | |
619 | log_fatal("no link-local IPv6 address for %s", info->name); | |
620 | } | |
621 | ||
622 | sock = if_register_socket(info, AF_INET6, &req_multi, addr6); | |
623 | ||
624 | if (sock < 0) { | |
625 | log_fatal("if_register_socket for %s fails", info->name); | |
626 | } | |
627 | ||
628 | info->rfdesc = sock; | |
629 | info->wfdesc = sock; | |
630 | ||
631 | get_hw_addr(info->name, &info->hw_address); | |
632 | ||
633 | if (!quiet_interface_discovery) { | |
634 | if (info->shared_network != NULL) { | |
635 | log_info("Listening on Socket/%d/%s/%s", | |
636 | global_v6_socket, info->name, | |
637 | info->shared_network->name); | |
638 | log_info("Sending on Socket/%d/%s/%s", | |
639 | global_v6_socket, info->name, | |
640 | info->shared_network->name); | |
641 | } else { | |
642 | log_info("Listening on Socket/%s", info->name); | |
643 | log_info("Sending on Socket/%s", info->name); | |
644 | } | |
645 | } | |
646 | } | |
647 | ||
98bd7ca0 DH |
648 | void |
649 | if_deregister6(struct interface_info *info) { | |
4b8251a0 SR |
650 | /* client case */ |
651 | if (no_global_v6_socket) { | |
652 | close(info->rfdesc); | |
653 | info->rfdesc = -1; | |
654 | info->wfdesc = -1; | |
655 | } else if ((info->rfdesc == global_v6_socket) && | |
656 | (info->wfdesc == global_v6_socket) && | |
657 | (global_v6_socket_references > 0)) { | |
658 | /* Dereference the global v6 socket. */ | |
ecddae64 DH |
659 | global_v6_socket_references--; |
660 | info->rfdesc = -1; | |
661 | info->wfdesc = -1; | |
563f0b8a FD |
662 | #if defined(RELAY_PORT) |
663 | } else if (relay_port && | |
664 | (info->rfdesc == relay_port_v6_socket) && | |
665 | (info->wfdesc == relay_port_v6_socket) && | |
666 | (relay_port_v6_socket_references > 0)) { | |
667 | /* Dereference the relay port v6 socket. */ | |
668 | relay_port_v6_socket_references--; | |
669 | info->rfdesc = -1; | |
670 | info->wfdesc = -1; | |
671 | #endif | |
ecddae64 DH |
672 | } else { |
673 | log_fatal("Impossible condition at %s:%d", MDL); | |
674 | } | |
98bd7ca0 DH |
675 | |
676 | if (!quiet_interface_discovery) { | |
677 | if (info->shared_network != NULL) { | |
678 | log_info("Disabling input on Socket/%s/%s", info->name, | |
679 | info->shared_network->name); | |
680 | log_info("Disabling output on Socket/%s/%s", info->name, | |
681 | info->shared_network->name); | |
682 | } else { | |
683 | log_info("Disabling input on Socket/%s", info->name); | |
684 | log_info("Disabling output on Socket/%s", info->name); | |
685 | } | |
686 | } | |
ecddae64 | 687 | |
563f0b8a FD |
688 | if (!no_global_v6_socket) { |
689 | if (global_v6_socket_references == 0) { | |
690 | close(global_v6_socket); | |
691 | global_v6_socket = -1; | |
ecddae64 | 692 | |
563f0b8a FD |
693 | log_info("Unbound from *:%d", |
694 | (int) ntohs(local_port)); | |
695 | } | |
696 | #if defined(RELAY_PORT) | |
697 | if (relay_port && (relay_port_v6_socket_references == 0)) { | |
698 | close(relay_port_v6_socket); | |
699 | relay_port_v6_socket = -1; | |
700 | ||
701 | log_info("Unbound from relay port *:%d", | |
702 | (int) ntohs(relay_port)); | |
703 | } | |
704 | #endif | |
ecddae64 | 705 | } |
98bd7ca0 | 706 | } |
fe5b0fdd | 707 | #endif /* DHCPv6 */ |
98bd7ca0 | 708 | |
a1b705e5 | 709 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
4595a58c | 710 | ssize_t send_packet (interface, packet, raw, len, from, to, hto) |
e23c9055 | 711 | struct interface_info *interface; |
a8b53b42 TL |
712 | struct packet *packet; |
713 | struct dhcp_packet *raw; | |
714 | size_t len; | |
0a5d6860 | 715 | struct in_addr from; |
c857a7b6 | 716 | struct sockaddr_in *to; |
e23c9055 | 717 | struct hardware *hto; |
a8b53b42 | 718 | { |
5145810c TL |
719 | int result; |
720 | #ifdef IGNORE_HOSTUNREACH | |
721 | int retry = 0; | |
722 | do { | |
7cfeb916 SR |
723 | #endif |
724 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
725 | struct in_pktinfo pktinfo; | |
726 | ||
727 | if (interface->ifp != NULL) { | |
728 | memset(&pktinfo, 0, sizeof (pktinfo)); | |
729 | pktinfo.ipi_ifindex = interface->ifp->ifr_index; | |
730 | if (setsockopt(interface->wfdesc, IPPROTO_IP, | |
731 | IP_PKTINFO, (char *)&pktinfo, | |
732 | sizeof(pktinfo)) < 0) | |
733 | log_fatal("setsockopt: IP_PKTINFO: %m"); | |
734 | } | |
5145810c TL |
735 | #endif |
736 | result = sendto (interface -> wfdesc, (char *)raw, len, 0, | |
737 | (struct sockaddr *)to, sizeof *to); | |
738 | #ifdef IGNORE_HOSTUNREACH | |
13ee152c | 739 | } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && |
5145810c TL |
740 | result < 0 && |
741 | (errno == EHOSTUNREACH || | |
742 | errno == ECONNREFUSED) && | |
743 | retry++ < 10); | |
744 | #endif | |
74f45f96 | 745 | if (result < 0) { |
c5b0f529 | 746 | log_error ("send_packet: %m"); |
74f45f96 | 747 | if (errno == ENETUNREACH) |
a1b705e5 TL |
748 | log_error ("send_packet: please consult README file%s", |
749 | " regarding broadcast address."); | |
74f45f96 | 750 | } |
af361f2a | 751 | return result; |
a8b53b42 | 752 | } |
98bd7ca0 | 753 | |
a1b705e5 | 754 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
a8b53b42 | 755 | |
cff9b78f SK |
756 | #ifdef DHCPv6 |
757 | /* | |
758 | * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will | |
759 | * synthesize them (based on the BIND 9 technique). | |
760 | */ | |
761 | ||
762 | #ifndef CMSG_LEN | |
763 | static size_t CMSG_LEN(size_t len) { | |
764 | size_t hdrlen; | |
765 | /* | |
766 | * Cast NULL so that any pointer arithmetic performed by CMSG_DATA | |
767 | * is correct. | |
768 | */ | |
769 | hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL)); | |
770 | return hdrlen + len; | |
771 | } | |
772 | #endif /* !CMSG_LEN */ | |
773 | ||
774 | #ifndef CMSG_SPACE | |
775 | static size_t CMSG_SPACE(size_t len) { | |
776 | struct msghdr msg; | |
777 | struct cmsghdr *cmsgp; | |
778 | ||
779 | /* | |
780 | * XXX: The buffer length is an ad-hoc value, but should be enough | |
781 | * in a practical sense. | |
782 | */ | |
783 | union { | |
784 | struct cmsghdr cmsg_sizer; | |
785 | u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024]; | |
786 | } dummybuf; | |
787 | ||
788 | memset(&msg, 0, sizeof(msg)); | |
789 | msg.msg_control = &dummybuf; | |
790 | msg.msg_controllen = sizeof(dummybuf); | |
791 | ||
792 | cmsgp = (struct cmsghdr *)&dummybuf; | |
793 | cmsgp->cmsg_len = CMSG_LEN(len); | |
794 | ||
795 | cmsgp = CMSG_NXTHDR(&msg, cmsgp); | |
796 | if (cmsgp != NULL) { | |
797 | return (char *)cmsgp - (char *)msg.msg_control; | |
798 | } else { | |
799 | return 0; | |
800 | } | |
801 | } | |
802 | #endif /* !CMSG_SPACE */ | |
803 | ||
804 | #endif /* DHCPv6 */ | |
805 | ||
7cfeb916 SR |
806 | #if defined(DHCPv6) || \ |
807 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
808 | defined(USE_V4_PKTINFO)) | |
57fbc772 SR |
809 | /* |
810 | * For both send_packet6() and receive_packet6() we need to allocate | |
811 | * space for the cmsg header information. We do this once and reuse | |
7cfeb916 SR |
812 | * the buffer. We also need the control buf for send_packet() and |
813 | * receive_packet() when we use a single socket and IP_PKTINFO to | |
814 | * send the packet out the correct interface. | |
57fbc772 SR |
815 | */ |
816 | static void *control_buf = NULL; | |
817 | static size_t control_buf_len = 0; | |
818 | ||
819 | static void | |
820 | allocate_cmsg_cbuf(void) { | |
821 | control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); | |
822 | control_buf = dmalloc(control_buf_len, MDL); | |
823 | return; | |
824 | } | |
7cfeb916 | 825 | #endif /* DHCPv6, IP_PKTINFO ... */ |
57fbc772 | 826 | |
7cfeb916 | 827 | #ifdef DHCPv6 |
98bd7ca0 DH |
828 | /* |
829 | * For both send_packet6() and receive_packet6() we need to use the | |
20ae1aff | 830 | * sendmsg()/recvmsg() functions rather than the simpler send()/recv() |
98bd7ca0 DH |
831 | * functions. |
832 | * | |
833 | * In the case of send_packet6(), we need to do this in order to insure | |
834 | * that the reply packet leaves on the same interface that it arrived | |
835 | * on. | |
836 | * | |
837 | * In the case of receive_packet6(), we need to do this in order to | |
838 | * get the IP address the packet was sent to. This is used to identify | |
839 | * whether a packet is multicast or unicast. | |
840 | * | |
841 | * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg. | |
842 | * | |
843 | * Also see the sections in RFC 3542 about IPV6_PKTINFO. | |
844 | */ | |
845 | ||
846 | /* Send an IPv6 packet */ | |
847 | ssize_t send_packet6(struct interface_info *interface, | |
6705543f | 848 | const unsigned char *raw, size_t len, |
98bd7ca0 DH |
849 | struct sockaddr_in6 *to) { |
850 | struct msghdr m; | |
851 | struct iovec v; | |
4b8251a0 | 852 | struct sockaddr_in6 dst; |
98bd7ca0 DH |
853 | int result; |
854 | struct in6_pktinfo *pktinfo; | |
98bd7ca0 | 855 | struct cmsghdr *cmsg; |
4b8251a0 | 856 | unsigned int ifindex; |
57fbc772 SR |
857 | |
858 | /* | |
859 | * If necessary allocate space for the control message header. | |
860 | * The space is common between send and receive. | |
861 | */ | |
862 | ||
863 | if (control_buf == NULL) { | |
864 | allocate_cmsg_cbuf(); | |
865 | if (control_buf == NULL) { | |
866 | log_error("send_packet6: unable to allocate cmsg header"); | |
867 | return(ENOMEM); | |
868 | } | |
869 | } | |
870 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
871 | |
872 | /* | |
873 | * Initialize our message header structure. | |
874 | */ | |
875 | memset(&m, 0, sizeof(m)); | |
876 | ||
877 | /* | |
878 | * Set the target address we're sending to. | |
4b8251a0 | 879 | * Enforce the scope ID for bogus BSDs. |
98bd7ca0 | 880 | */ |
4b8251a0 SR |
881 | memcpy(&dst, to, sizeof(dst)); |
882 | m.msg_name = &dst; | |
883 | m.msg_namelen = sizeof(dst); | |
884 | ifindex = if_nametoindex(interface->name); | |
0cd94b5e TM |
885 | |
886 | // Per OpenBSD patch-common_socket_c,v 1.7 2018/03/06 08:37:39 sthen Exp | |
887 | // always set the scope id. We'll leave the test for no global socket | |
888 | // in place for all others. | |
889 | #ifndef __OpenBSD__ | |
4b8251a0 | 890 | if (no_global_v6_socket) |
0cd94b5e | 891 | #endif |
4b8251a0 | 892 | dst.sin6_scope_id = ifindex; |
98bd7ca0 DH |
893 | |
894 | /* | |
895 | * Set the data buffer we're sending. (Using this wacky | |
896 | * "scatter-gather" stuff... we only have a single chunk | |
897 | * of data to send, so we declare a single vector entry.) | |
898 | */ | |
899 | v.iov_base = (char *)raw; | |
900 | v.iov_len = len; | |
901 | m.msg_iov = &v; | |
902 | m.msg_iovlen = 1; | |
903 | ||
904 | /* | |
905 | * Setting the interface is a bit more involved. | |
906 | * | |
907 | * We have to create a "control message", and set that to | |
908 | * define the IPv6 packet information. We could set the | |
909 | * source address if we wanted, but we can safely let the | |
910 | * kernel decide what that should be. | |
911 | */ | |
57fbc772 SR |
912 | m.msg_control = control_buf; |
913 | m.msg_controllen = control_buf_len; | |
98bd7ca0 | 914 | cmsg = CMSG_FIRSTHDR(&m); |
dc9d7b08 | 915 | INSIST(cmsg != NULL); |
98bd7ca0 DH |
916 | cmsg->cmsg_level = IPPROTO_IPV6; |
917 | cmsg->cmsg_type = IPV6_PKTINFO; | |
918 | cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo)); | |
919 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
920 | memset(pktinfo, 0, sizeof(*pktinfo)); | |
a2a0f98c | 921 | pktinfo->ipi6_addr = local_address6; |
4b8251a0 | 922 | pktinfo->ipi6_ifindex = ifindex; |
98bd7ca0 DH |
923 | |
924 | result = sendmsg(interface->wfdesc, &m, 0); | |
925 | if (result < 0) { | |
926 | log_error("send_packet6: %m"); | |
927 | } | |
928 | return result; | |
929 | } | |
fe5b0fdd | 930 | #endif /* DHCPv6 */ |
98bd7ca0 | 931 | |
c857a7b6 | 932 | #ifdef USE_SOCKET_RECEIVE |
4595a58c | 933 | ssize_t receive_packet (interface, buf, len, from, hfrom) |
e23c9055 TL |
934 | struct interface_info *interface; |
935 | unsigned char *buf; | |
936 | size_t len; | |
937 | struct sockaddr_in *from; | |
938 | struct hardware *hfrom; | |
a8b53b42 | 939 | { |
865afd5e | 940 | #if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)) |
d1f313b3 | 941 | SOCKLEN_T flen = sizeof *from; |
7cfeb916 | 942 | #endif |
5145810c | 943 | int result; |
e23c9055 | 944 | |
98bd7ca0 DH |
945 | /* |
946 | * The normal Berkeley socket interface doesn't give us any way | |
947 | * to know what hardware interface we received the message on, | |
948 | * but we should at least make sure the structure is emptied. | |
949 | */ | |
950 | memset(hfrom, 0, sizeof(*hfrom)); | |
951 | ||
5145810c TL |
952 | #ifdef IGNORE_HOSTUNREACH |
953 | int retry = 0; | |
954 | do { | |
955 | #endif | |
7cfeb916 SR |
956 | |
957 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
958 | struct msghdr m; | |
959 | struct iovec v; | |
960 | struct cmsghdr *cmsg; | |
961 | struct in_pktinfo *pktinfo; | |
962 | unsigned int ifindex; | |
7cfeb916 SR |
963 | |
964 | /* | |
965 | * If necessary allocate space for the control message header. | |
966 | * The space is common between send and receive. | |
967 | */ | |
968 | if (control_buf == NULL) { | |
969 | allocate_cmsg_cbuf(); | |
970 | if (control_buf == NULL) { | |
971 | log_error("receive_packet: unable to allocate cmsg " | |
972 | "header"); | |
973 | return(ENOMEM); | |
974 | } | |
975 | } | |
976 | memset(control_buf, 0, control_buf_len); | |
977 | ||
978 | /* | |
979 | * Initialize our message header structure. | |
980 | */ | |
981 | memset(&m, 0, sizeof(m)); | |
982 | ||
983 | /* | |
984 | * Point so we can get the from address. | |
985 | */ | |
986 | m.msg_name = from; | |
987 | m.msg_namelen = sizeof(*from); | |
988 | ||
989 | /* | |
990 | * Set the data buffer we're receiving. (Using this wacky | |
991 | * "scatter-gather" stuff... but we that doesn't really make | |
992 | * sense for us, so we use a single vector entry.) | |
993 | */ | |
994 | v.iov_base = buf; | |
995 | v.iov_len = len; | |
996 | m.msg_iov = &v; | |
997 | m.msg_iovlen = 1; | |
998 | ||
999 | /* | |
1000 | * Getting the interface is a bit more involved. | |
1001 | * | |
1002 | * We set up some space for a "control message". We have | |
1003 | * previously asked the kernel to give us packet | |
1004 | * information (when we initialized the interface), so we | |
865afd5e | 1005 | * should get the interface index from that. |
7cfeb916 SR |
1006 | */ |
1007 | m.msg_control = control_buf; | |
1008 | m.msg_controllen = control_buf_len; | |
1009 | ||
1010 | result = recvmsg(interface->rfdesc, &m, 0); | |
1011 | ||
1012 | if (result >= 0) { | |
1013 | /* | |
1014 | * If we did read successfully, then we need to loop | |
1015 | * through the control messages we received and | |
865afd5e | 1016 | * find the one with our inteface index. |
7cfeb916 | 1017 | */ |
7cfeb916 SR |
1018 | cmsg = CMSG_FIRSTHDR(&m); |
1019 | while (cmsg != NULL) { | |
1020 | if ((cmsg->cmsg_level == IPPROTO_IP) && | |
1021 | (cmsg->cmsg_type == IP_PKTINFO)) { | |
1022 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
1023 | ifindex = pktinfo->ipi_ifindex; | |
1024 | /* | |
1025 | * We pass the ifindex back to the caller | |
1026 | * using the unused hfrom parameter avoiding | |
1027 | * interface changes between sockets and | |
1028 | * the discover code. | |
1029 | */ | |
1030 | memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex)); | |
865afd5e | 1031 | return (result); |
7cfeb916 SR |
1032 | } |
1033 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
1034 | } | |
865afd5e SR |
1035 | |
1036 | /* | |
1037 | * We didn't find the necessary control message | |
1038 | * flag it as an error | |
1039 | */ | |
1040 | result = -1; | |
1041 | errno = EIO; | |
7cfeb916 SR |
1042 | } |
1043 | #else | |
865afd5e SR |
1044 | result = recvfrom(interface -> rfdesc, (char *)buf, len, 0, |
1045 | (struct sockaddr *)from, &flen); | |
7cfeb916 | 1046 | #endif /* IP_PKTINFO ... */ |
5145810c TL |
1047 | #ifdef IGNORE_HOSTUNREACH |
1048 | } while (result < 0 && | |
1049 | (errno == EHOSTUNREACH || | |
1050 | errno == ECONNREFUSED) && | |
1051 | retry++ < 10); | |
1052 | #endif | |
865afd5e | 1053 | return (result); |
c857a7b6 | 1054 | } |
98bd7ca0 | 1055 | |
c857a7b6 | 1056 | #endif /* USE_SOCKET_RECEIVE */ |
c5568eb5 | 1057 | |
fe5b0fdd | 1058 | #ifdef DHCPv6 |
98bd7ca0 DH |
1059 | ssize_t |
1060 | receive_packet6(struct interface_info *interface, | |
1061 | unsigned char *buf, size_t len, | |
ecddae64 DH |
1062 | struct sockaddr_in6 *from, struct in6_addr *to_addr, |
1063 | unsigned int *if_idx) | |
1064 | { | |
98bd7ca0 DH |
1065 | struct msghdr m; |
1066 | struct iovec v; | |
98bd7ca0 DH |
1067 | int result; |
1068 | struct cmsghdr *cmsg; | |
1069 | struct in6_pktinfo *pktinfo; | |
57fbc772 SR |
1070 | |
1071 | /* | |
1072 | * If necessary allocate space for the control message header. | |
1073 | * The space is common between send and receive. | |
1074 | */ | |
1075 | if (control_buf == NULL) { | |
1076 | allocate_cmsg_cbuf(); | |
1077 | if (control_buf == NULL) { | |
436e808a SR |
1078 | log_error("receive_packet6: unable to allocate cmsg " |
1079 | "header"); | |
57fbc772 SR |
1080 | return(ENOMEM); |
1081 | } | |
1082 | } | |
1083 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
1084 | |
1085 | /* | |
1086 | * Initialize our message header structure. | |
1087 | */ | |
1088 | memset(&m, 0, sizeof(m)); | |
1089 | ||
1090 | /* | |
1091 | * Point so we can get the from address. | |
1092 | */ | |
1093 | m.msg_name = from; | |
1094 | m.msg_namelen = sizeof(*from); | |
1095 | ||
1096 | /* | |
1097 | * Set the data buffer we're receiving. (Using this wacky | |
1098 | * "scatter-gather" stuff... but we that doesn't really make | |
1099 | * sense for us, so we use a single vector entry.) | |
1100 | */ | |
1101 | v.iov_base = buf; | |
1102 | v.iov_len = len; | |
1103 | m.msg_iov = &v; | |
1104 | m.msg_iovlen = 1; | |
1105 | ||
1106 | /* | |
1107 | * Getting the interface is a bit more involved. | |
1108 | * | |
1109 | * We set up some space for a "control message". We have | |
1110 | * previously asked the kernel to give us packet | |
1111 | * information (when we initialized the interface), so we | |
1112 | * should get the destination address from that. | |
1113 | */ | |
57fbc772 SR |
1114 | m.msg_control = control_buf; |
1115 | m.msg_controllen = control_buf_len; | |
98bd7ca0 DH |
1116 | |
1117 | result = recvmsg(interface->rfdesc, &m, 0); | |
1118 | ||
1119 | if (result >= 0) { | |
1120 | /* | |
1121 | * If we did read successfully, then we need to loop | |
1122 | * through the control messages we received and | |
1123 | * find the one with our destination address. | |
98bd7ca0 | 1124 | */ |
98bd7ca0 DH |
1125 | cmsg = CMSG_FIRSTHDR(&m); |
1126 | while (cmsg != NULL) { | |
1127 | if ((cmsg->cmsg_level == IPPROTO_IPV6) && | |
1128 | (cmsg->cmsg_type == IPV6_PKTINFO)) { | |
1129 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
1130 | *to_addr = pktinfo->ipi6_addr; | |
ecddae64 | 1131 | *if_idx = pktinfo->ipi6_ifindex; |
865afd5e SR |
1132 | |
1133 | return (result); | |
98bd7ca0 DH |
1134 | } |
1135 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
1136 | } | |
865afd5e SR |
1137 | |
1138 | /* | |
1139 | * We didn't find the necessary control message | |
1140 | * flag is as an error | |
1141 | */ | |
1142 | result = -1; | |
1143 | errno = EIO; | |
98bd7ca0 DH |
1144 | } |
1145 | ||
865afd5e | 1146 | return (result); |
98bd7ca0 | 1147 | } |
fe5b0fdd | 1148 | #endif /* DHCPv6 */ |
98bd7ca0 | 1149 | |
e15381fc | 1150 | #if defined (USE_SOCKET_FALLBACK) |
c5568eb5 TL |
1151 | /* This just reads in a packet and silently discards it. */ |
1152 | ||
acc21512 | 1153 | isc_result_t fallback_discard (object) |
e92653f1 | 1154 | omapi_object_t *object; |
c5568eb5 TL |
1155 | { |
1156 | char buf [1540]; | |
1157 | struct sockaddr_in from; | |
d1f313b3 | 1158 | SOCKLEN_T flen = sizeof from; |
c01f1285 | 1159 | int status; |
acc21512 TL |
1160 | struct interface_info *interface; |
1161 | ||
1162 | if (object -> type != dhcp_type_interface) | |
98bf1607 | 1163 | return DHCP_R_INVALIDARG; |
acc21512 | 1164 | interface = (struct interface_info *)object; |
c5568eb5 | 1165 | |
c01f1285 TL |
1166 | status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, |
1167 | (struct sockaddr *)&from, &flen); | |
4089dd21 M |
1168 | #if defined (DEBUG) |
1169 | /* Only report fallback discard errors if we're debugging. */ | |
acc21512 | 1170 | if (status < 0) { |
8ae2d595 | 1171 | log_error ("fallback_discard: %m"); |
acc21512 TL |
1172 | return ISC_R_UNEXPECTED; |
1173 | } | |
dd9237c3 TM |
1174 | #else |
1175 | /* ignore the fact that status value is never used */ | |
1176 | IGNORE_UNUSED(status); | |
4089dd21 | 1177 | #endif |
acc21512 | 1178 | return ISC_R_SUCCESS; |
c5568eb5 | 1179 | } |
a1b705e5 | 1180 | #endif /* USE_SOCKET_FALLBACK */ |
d2bc90bd | 1181 | |
a1b705e5 | 1182 | #if defined (USE_SOCKET_SEND) |
21d21e91 TL |
1183 | int can_unicast_without_arp (ip) |
1184 | struct interface_info *ip; | |
d2bc90bd TL |
1185 | { |
1186 | return 0; | |
1187 | } | |
1188 | ||
21d21e91 TL |
1189 | int can_receive_unicast_unconfigured (ip) |
1190 | struct interface_info *ip; | |
b547818b | 1191 | { |
a1b705e5 TL |
1192 | #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) |
1193 | return 1; | |
1194 | #else | |
b547818b | 1195 | return 0; |
a1b705e5 | 1196 | #endif |
b547818b TL |
1197 | } |
1198 | ||
5cefe5e5 TL |
1199 | int supports_multiple_interfaces (ip) |
1200 | struct interface_info *ip; | |
1201 | { | |
7cfeb916 SR |
1202 | #if defined(SO_BINDTODEVICE) || \ |
1203 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
1204 | defined(USE_V4_PKTINFO)) | |
1205 | return(1); | |
5cefe5e5 | 1206 | #else |
7cfeb916 | 1207 | return(0); |
5cefe5e5 TL |
1208 | #endif |
1209 | } | |
1210 | ||
d2bc90bd TL |
1211 | /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, |
1212 | do not. */ | |
1213 | ||
1214 | void maybe_setup_fallback () | |
1215 | { | |
a1b705e5 | 1216 | #if defined (USE_SOCKET_FALLBACK) |
acc21512 | 1217 | isc_result_t status; |
950c6a06 TL |
1218 | struct interface_info *fbi = (struct interface_info *)0; |
1219 | if (setup_fallback (&fbi, MDL)) { | |
4b8251a0 | 1220 | fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0, NULL); |
a47105d6 TL |
1221 | fbi -> rfdesc = fbi -> wfdesc; |
1222 | log_info ("Sending on Socket/%s%s%s", | |
1223 | fbi -> name, | |
1224 | (fbi -> shared_network ? "/" : ""), | |
1225 | (fbi -> shared_network ? | |
1226 | fbi -> shared_network -> name : "")); | |
1227 | ||
e48c5c5c | 1228 | status = omapi_register_io_object ((omapi_object_t *)fbi, |
acc21512 TL |
1229 | if_readsocket, 0, |
1230 | fallback_discard, 0, 0); | |
1231 | if (status != ISC_R_SUCCESS) | |
1232 | log_fatal ("Can't register I/O handle for %s: %s", | |
1233 | fbi -> name, isc_result_totext (status)); | |
950c6a06 | 1234 | interface_dereference (&fbi, MDL); |
d2bc90bd TL |
1235 | } |
1236 | #endif | |
1237 | } | |
7cfeb916 SR |
1238 | |
1239 | ||
1240 | #if defined(sun) && defined(USE_V4_PKTINFO) | |
1241 | /* This code assumes the existence of SIOCGLIFHWADDR */ | |
1242 | void | |
1243 | get_hw_addr(const char *name, struct hardware *hw) { | |
1244 | struct sockaddr_dl *dladdrp; | |
b047bd38 | 1245 | int sock, i; |
7cfeb916 SR |
1246 | struct lifreq lifr; |
1247 | ||
1248 | memset(&lifr, 0, sizeof (lifr)); | |
1249 | (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); | |
1250 | /* | |
1251 | * Check if the interface is a virtual or IPMP interface - in those | |
1252 | * cases it has no hw address, so generate a random one. | |
1253 | */ | |
1254 | if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || | |
1255 | ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) { | |
1256 | if (sock != -1) | |
1257 | (void) close(sock); | |
1258 | ||
1259 | #ifdef DHCPv6 | |
1260 | /* | |
1261 | * If approrpriate try this with an IPv6 socket | |
1262 | */ | |
1263 | if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 && | |
1264 | ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) { | |
1265 | goto flag_check; | |
1266 | } | |
1267 | if (sock != -1) | |
1268 | (void) close(sock); | |
1269 | #endif | |
1270 | log_fatal("Couldn't get interface flags for %s: %m", name); | |
1271 | ||
1272 | } | |
1273 | ||
1274 | flag_check: | |
1275 | if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) { | |
1276 | hw->hlen = sizeof (hw->hbuf); | |
1277 | srandom((long)gethrtime()); | |
1278 | ||
b047bd38 SR |
1279 | hw->hbuf[0] = HTYPE_IPMP; |
1280 | for (i = 1; i < hw->hlen; ++i) { | |
7cfeb916 SR |
1281 | hw->hbuf[i] = random() % 256; |
1282 | } | |
1283 | ||
1284 | if (sock != -1) | |
1285 | (void) close(sock); | |
1286 | return; | |
1287 | } | |
1288 | ||
1289 | if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0) | |
1290 | log_fatal("Couldn't get interface hardware address for %s: %m", | |
1291 | name); | |
1292 | dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr; | |
b047bd38 SR |
1293 | hw->hlen = dladdrp->sdl_alen+1; |
1294 | switch (dladdrp->sdl_type) { | |
1295 | case DL_CSMACD: /* IEEE 802.3 */ | |
1296 | case DL_ETHER: | |
1297 | hw->hbuf[0] = HTYPE_ETHER; | |
1298 | break; | |
1299 | case DL_TPR: | |
1300 | hw->hbuf[0] = HTYPE_IEEE802; | |
1301 | break; | |
1302 | case DL_FDDI: | |
1303 | hw->hbuf[0] = HTYPE_FDDI; | |
1304 | break; | |
1305 | case DL_IB: | |
1306 | hw->hbuf[0] = HTYPE_INFINIBAND; | |
1307 | break; | |
1308 | default: | |
1309 | log_fatal("%s: unsupported DLPI MAC type %lu", name, | |
1310 | (unsigned long)dladdrp->sdl_type); | |
1311 | } | |
1312 | ||
1313 | memcpy(hw->hbuf+1, LLADDR(dladdrp), hw->hlen-1); | |
7cfeb916 SR |
1314 | |
1315 | if (sock != -1) | |
1316 | (void) close(sock); | |
1317 | } | |
1318 | #endif /* defined(sun) */ | |
1319 | ||
a1b705e5 | 1320 | #endif /* USE_SOCKET_SEND */ |