]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* socket.c |
2 | ||
3 | BSD socket interface code... */ | |
4 | ||
5 | /* | |
b047bd38 | 6 | * Copyright (c) 2004-2012 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1995-2003 by Internet Software Consortium |
d7837182 | 8 | * |
98311e4b DH |
9 | * Permission to use, copy, modify, and distribute this software for any |
10 | * purpose with or without fee is hereby granted, provided that the above | |
11 | * copyright notice and this permission notice appear in all copies. | |
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 | * |
98311e4b | 27 | * This software has been written for Internet Systems Consortium |
49733f31 | 28 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. |
98311e4b | 29 | * To learn more about Internet Systems Consortium, see |
2c85ac9b | 30 | * ``https://www.isc.org/''. To learn more about Vixie Enterprises, |
49733f31 TL |
31 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see |
32 | * ``http://www.nominum.com''. | |
d7837182 TL |
33 | */ |
34 | ||
469cf3a4 TL |
35 | /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). |
36 | * This sockopt allows a socket to be bound to a particular interface, | |
37 | * thus enabling the use of DHCPD on a multihomed host. | |
38 | * If SO_BINDTODEVICE is defined in your system header files, the use of | |
39 | * this sockopt will be automatically enabled. | |
40 | * I have implemented it under Linux; other systems should be doable also. | |
41 | */ | |
42 | ||
d7837182 | 43 | #include "dhcpd.h" |
fe5b0fdd DH |
44 | #include <errno.h> |
45 | #include <sys/ioctl.h> | |
a512d11b | 46 | #include <sys/uio.h> |
99fe695e | 47 | #include <sys/uio.h> |
d7837182 | 48 | |
7cfeb916 SR |
49 | #if defined(sun) && defined(USE_V4_PKTINFO) |
50 | #include <sys/sysmacros.h> | |
51 | #include <net/if.h> | |
52 | #include <sys/sockio.h> | |
53 | #include <net/if_dl.h> | |
b047bd38 | 54 | #include <sys/dlpi.h> |
7cfeb916 SR |
55 | #endif |
56 | ||
0a5d6860 | 57 | #ifdef USE_SOCKET_FALLBACK |
a1b705e5 | 58 | # if !defined (USE_SOCKET_SEND) |
0a5d6860 TL |
59 | # define if_register_send if_register_fallback |
60 | # define send_packet send_fallback | |
c3585217 | 61 | # define if_reinitialize_send if_reinitialize_fallback |
a1b705e5 | 62 | # endif |
c3585217 TL |
63 | #endif |
64 | ||
ecddae64 DH |
65 | #if defined(DHCPv6) |
66 | /* | |
67 | * XXX: this is gross. we need to go back and overhaul the API for socket | |
68 | * handling. | |
69 | */ | |
70 | static unsigned int global_v6_socket_references = 0; | |
71 | static int global_v6_socket = -1; | |
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 DH |
129 | if_register_socket(struct interface_info *info, int family, |
130 | int *do_multicast) | |
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; | |
ecddae64 | 164 | /* XXX: What will happen to multicasts if this is nonzero? */ |
98bf1607 | 165 | memcpy(&addr6->sin6_addr, |
98bd7ca0 | 166 | &local_address6, |
98bf1607 | 167 | sizeof(addr6->sin6_addr)); |
7de20a95 | 168 | #ifdef HAVE_SA_LEN |
98bf1607 | 169 | addr6->sin6_len = sizeof(*addr6); |
7de20a95 | 170 | #endif |
98bf1607 | 171 | name_len = sizeof(*addr6); |
98bd7ca0 | 172 | domain = PF_INET6; |
7de20a95 | 173 | if ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) { |
ecddae64 | 174 | *do_multicast = 0; |
7de20a95 | 175 | } |
98bf1607 | 176 | break; |
fe5b0fdd | 177 | #endif /* DHCPv6 */ |
98bf1607 SR |
178 | |
179 | case AF_INET: | |
180 | default: | |
181 | addr = (struct sockaddr_in *)&name; | |
06eb8bab SK |
182 | addr->sin_family = AF_INET; |
183 | addr->sin_port = local_port; | |
184 | memcpy(&addr->sin_addr, | |
185 | &local_address, | |
186 | sizeof(addr->sin_addr)); | |
7de20a95 EH |
187 | #ifdef HAVE_SA_LEN |
188 | addr->sin_len = sizeof(*addr); | |
189 | #endif | |
06eb8bab SK |
190 | name_len = sizeof(*addr); |
191 | domain = PF_INET; | |
98bf1607 | 192 | break; |
98bd7ca0 | 193 | } |
d7837182 | 194 | |
0a5d6860 | 195 | /* Make a socket... */ |
98bd7ca0 DH |
196 | sock = socket(domain, SOCK_DGRAM, IPPROTO_UDP); |
197 | if (sock < 0) { | |
198 | log_fatal("Can't create dhcp socket: %m"); | |
199 | } | |
d7837182 | 200 | |
e23c9055 TL |
201 | /* Set the REUSEADDR option so that we don't fail to start if |
202 | we're being restarted. */ | |
d7837182 | 203 | flag = 1; |
98bd7ca0 DH |
204 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, |
205 | (char *)&flag, sizeof(flag)) < 0) { | |
206 | log_fatal("Can't set SO_REUSEADDR option on dhcp socket: %m"); | |
207 | } | |
d7837182 | 208 | |
5cefe5e5 TL |
209 | /* Set the BROADCAST option so that we can broadcast DHCP responses. |
210 | We shouldn't do this for fallback devices, and we can detect that | |
211 | a device is a fallback because it has no ifp structure. */ | |
98bd7ca0 DH |
212 | if (info->ifp && |
213 | (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, | |
214 | (char *)&flag, sizeof(flag)) < 0)) { | |
215 | log_fatal("Can't set SO_BROADCAST option on dhcp socket: %m"); | |
216 | } | |
d7837182 | 217 | |
ecddae64 DH |
218 | #if defined(DHCPv6) && defined(SO_REUSEPORT) |
219 | /* | |
220 | * We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple | |
221 | * daemons can bind to their own sockets and get data for their | |
222 | * respective interfaces. This does not (and should not) affect | |
223 | * DHCPv4 sockets; we can't yet support BSD sockets well, much | |
224 | * less multiple sockets. | |
225 | */ | |
226 | if (local_family == AF_INET6) { | |
227 | flag = 1; | |
228 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, | |
229 | (char *)&flag, sizeof(flag)) < 0) { | |
230 | log_fatal("Can't set SO_REUSEPORT option on dhcp " | |
231 | "socket: %m"); | |
232 | } | |
233 | } | |
234 | #endif | |
235 | ||
e23c9055 | 236 | /* Bind the socket to this interface's IP address. */ |
98bd7ca0 DH |
237 | if (bind(sock, (struct sockaddr *)&name, name_len) < 0) { |
238 | log_error("Can't bind to dhcp address: %m"); | |
239 | log_error("Please make sure there is no other dhcp server"); | |
240 | log_error("running and that there's no entry for dhcp or"); | |
241 | log_error("bootp in /etc/inetd.conf. Also make sure you"); | |
242 | log_error("are not running HP JetAdmin software, which"); | |
243 | log_fatal("includes a bootp server."); | |
7eae478e | 244 | } |
d7837182 | 245 | |
fe5b0fdd | 246 | #if defined(SO_BINDTODEVICE) |
469cf3a4 | 247 | /* Bind this socket to this interface. */ |
ecddae64 | 248 | if ((local_family != AF_INET6) && (info->ifp != NULL) && |
98bd7ca0 DH |
249 | setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, |
250 | (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) { | |
251 | log_fatal("setsockopt: SO_BINDTODEVICE: %m"); | |
469cf3a4 TL |
252 | } |
253 | #endif | |
254 | ||
41e45067 DH |
255 | /* IP_BROADCAST_IF instructs the kernel which interface to send |
256 | * IP packets whose destination address is 255.255.255.255. These | |
257 | * will be treated as subnet broadcasts on the interface identified | |
258 | * by ip address (info -> primary_address). This is only known to | |
259 | * be defined in SCO system headers, and may not be defined in all | |
260 | * releases. | |
261 | */ | |
262 | #if defined(SCO) && defined(IP_BROADCAST_IF) | |
98bd7ca0 DH |
263 | if (info->address_count && |
264 | setsockopt(sock, IPPROTO_IP, IP_BROADCAST_IF, &info->addresses[0], | |
265 | sizeof(info->addresses[0])) < 0) | |
266 | log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m"); | |
41e45067 DH |
267 | #endif |
268 | ||
7cfeb916 SR |
269 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) |
270 | /* | |
271 | * If we turn on IP_RECVPKTINFO we will be able to receive | |
272 | * the interface index information of the received packet. | |
273 | */ | |
274 | if (family == AF_INET) { | |
275 | int on = 1; | |
276 | if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, | |
277 | &on, sizeof(on)) != 0) { | |
278 | log_fatal("setsockopt: IPV_RECVPKTINFO: %m"); | |
279 | } | |
280 | } | |
281 | #endif | |
282 | ||
fe5b0fdd | 283 | #ifdef DHCPv6 |
98bd7ca0 DH |
284 | /* |
285 | * If we turn on IPV6_PKTINFO, we will be able to receive | |
286 | * additional information, such as the destination IP address. | |
287 | * We need this to spot unicast packets. | |
288 | */ | |
289 | if (family == AF_INET6) { | |
290 | int on = 1; | |
291 | #ifdef IPV6_RECVPKTINFO | |
292 | /* RFC3542 */ | |
293 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, | |
294 | &on, sizeof(on)) != 0) { | |
295 | log_fatal("setsockopt: IPV6_RECVPKTINFO: %m"); | |
296 | } | |
297 | #else | |
298 | /* RFC2292 */ | |
299 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, | |
300 | &on, sizeof(on)) != 0) { | |
301 | log_fatal("setsockopt: IPV6_PKTINFO: %m"); | |
302 | } | |
303 | #endif | |
304 | } | |
7de20a95 EH |
305 | |
306 | if ((family == AF_INET6) && | |
307 | ((info->flags & INTERFACE_UPSTREAM) != 0)) { | |
308 | int hop_limit = 32; | |
309 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, | |
310 | &hop_limit, sizeof(int)) < 0) { | |
311 | log_fatal("setsockopt: IPV6_MULTICAST_HOPS: %m"); | |
312 | } | |
313 | } | |
fe5b0fdd | 314 | #endif /* DHCPv6 */ |
98bd7ca0 | 315 | |
e23c9055 | 316 | return sock; |
d7837182 | 317 | } |
a1b705e5 | 318 | #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ |
d7837182 | 319 | |
a1b705e5 | 320 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
c3585217 | 321 | void if_register_send (info) |
c857a7b6 | 322 | struct interface_info *info; |
d7837182 | 323 | { |
e23c9055 | 324 | #ifndef USE_SOCKET_RECEIVE |
7cfeb916 SR |
325 | info->wfdesc = if_register_socket(info, AF_INET, 0); |
326 | /* If this is a normal IPv4 address, get the hardware address. */ | |
327 | if (strcmp(info->name, "fallback") != 0) | |
328 | get_hw_addr(info->name, &info->hw_address); | |
2e96d6e7 TL |
329 | #if defined (USE_SOCKET_FALLBACK) |
330 | /* Fallback only registers for send, but may need to receive as | |
331 | well. */ | |
7cfeb916 | 332 | info->rfdesc = info->wfdesc; |
2e96d6e7 | 333 | #endif |
e23c9055 | 334 | #else |
7cfeb916 | 335 | info->wfdesc = info->rfdesc; |
e23c9055 | 336 | #endif |
3648a2a1 | 337 | if (!quiet_interface_discovery) |
74f45f96 | 338 | log_info ("Sending on Socket/%s%s%s", |
7cfeb916 SR |
339 | info->name, |
340 | (info->shared_network ? "/" : ""), | |
341 | (info->shared_network ? | |
342 | info->shared_network->name : "")); | |
d7837182 | 343 | } |
7203e8ee | 344 | |
3782236d | 345 | #if defined (USE_SOCKET_SEND) |
7203e8ee TL |
346 | void if_deregister_send (info) |
347 | struct interface_info *info; | |
348 | { | |
349 | #ifndef USE_SOCKET_RECEIVE | |
350 | close (info -> wfdesc); | |
351 | #endif | |
352 | info -> wfdesc = -1; | |
353 | ||
354 | if (!quiet_interface_discovery) | |
355 | log_info ("Disabling output on Socket/%s%s%s", | |
356 | info -> name, | |
357 | (info -> shared_network ? "/" : ""), | |
358 | (info -> shared_network ? | |
359 | info -> shared_network -> name : "")); | |
360 | } | |
3782236d | 361 | #endif /* USE_SOCKET_SEND */ |
a1b705e5 | 362 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
d7837182 | 363 | |
e23c9055 | 364 | #ifdef USE_SOCKET_RECEIVE |
c3585217 | 365 | void if_register_receive (info) |
e23c9055 | 366 | struct interface_info *info; |
e23c9055 | 367 | { |
7cfeb916 SR |
368 | |
369 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
370 | if (global_v4_socket_references == 0) { | |
371 | global_v4_socket = if_register_socket(info, AF_INET, 0); | |
372 | if (global_v4_socket < 0) { | |
373 | /* | |
374 | * if_register_socket() fatally logs if it fails to | |
375 | * create a socket, this is just a sanity check. | |
376 | */ | |
377 | log_fatal("Failed to create AF_INET socket %s:%d", | |
378 | MDL); | |
379 | } | |
380 | } | |
381 | ||
382 | info->rfdesc = global_v4_socket; | |
383 | global_v4_socket_references++; | |
384 | #else | |
e23c9055 TL |
385 | /* If we're using the socket API for sending and receiving, |
386 | we don't need to register this interface twice. */ | |
7cfeb916 SR |
387 | info->rfdesc = if_register_socket(info, AF_INET, 0); |
388 | #endif /* IP_PKTINFO... */ | |
389 | /* If this is a normal IPv4 address, get the hardware address. */ | |
390 | if (strcmp(info->name, "fallback") != 0) | |
391 | get_hw_addr(info->name, &info->hw_address); | |
392 | ||
3648a2a1 | 393 | if (!quiet_interface_discovery) |
74f45f96 | 394 | log_info ("Listening on Socket/%s%s%s", |
7cfeb916 SR |
395 | info->name, |
396 | (info->shared_network ? "/" : ""), | |
397 | (info->shared_network ? | |
398 | info->shared_network->name : "")); | |
e23c9055 | 399 | } |
7203e8ee TL |
400 | |
401 | void if_deregister_receive (info) | |
402 | struct interface_info *info; | |
403 | { | |
7cfeb916 SR |
404 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) |
405 | /* Dereference the global v4 socket. */ | |
406 | if ((info->rfdesc == global_v4_socket) && | |
407 | (info->wfdesc == global_v4_socket) && | |
408 | (global_v4_socket_references > 0)) { | |
409 | global_v4_socket_references--; | |
410 | info->rfdesc = -1; | |
411 | } else { | |
412 | log_fatal("Impossible condition at %s:%d", MDL); | |
413 | } | |
7203e8ee | 414 | |
7cfeb916 SR |
415 | if (global_v4_socket_references == 0) { |
416 | close(global_v4_socket); | |
417 | global_v4_socket = -1; | |
418 | } | |
419 | #else | |
420 | close(info->rfdesc); | |
421 | info->rfdesc = -1; | |
422 | #endif /* IP_PKTINFO... */ | |
7203e8ee TL |
423 | if (!quiet_interface_discovery) |
424 | log_info ("Disabling input on Socket/%s%s%s", | |
425 | info -> name, | |
426 | (info -> shared_network ? "/" : ""), | |
427 | (info -> shared_network ? | |
428 | info -> shared_network -> name : "")); | |
05af5898 | 429 | } |
e23c9055 TL |
430 | #endif /* USE_SOCKET_RECEIVE */ |
431 | ||
98bd7ca0 | 432 | |
fe5b0fdd | 433 | #ifdef DHCPv6 |
ecddae64 DH |
434 | /* |
435 | * This function joins the interface to DHCPv6 multicast groups so we will | |
436 | * receive multicast messages. | |
437 | */ | |
438 | static void | |
439 | if_register_multicast(struct interface_info *info) { | |
440 | int sock = info->rfdesc; | |
441 | struct ipv6_mreq mreq; | |
442 | ||
443 | if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, | |
444 | &mreq.ipv6mr_multiaddr) <= 0) { | |
445 | log_fatal("inet_pton: unable to convert '%s'", | |
446 | All_DHCP_Relay_Agents_and_Servers); | |
447 | } | |
448 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
449 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
450 | &mreq, sizeof(mreq)) < 0) { | |
451 | log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); | |
452 | } | |
453 | ||
454 | /* | |
455 | * The relay agent code sets the streams so you know which way | |
456 | * is up and down. But a relay agent shouldn't join to the | |
457 | * Server address, or else you get fun loops. So up or down | |
458 | * doesn't matter, we're just using that config to sense this is | |
459 | * a relay agent. | |
460 | */ | |
461 | if ((info->flags & INTERFACE_STREAMS) == 0) { | |
462 | if (inet_pton(AF_INET6, All_DHCP_Servers, | |
463 | &mreq.ipv6mr_multiaddr) <= 0) { | |
464 | log_fatal("inet_pton: unable to convert '%s'", | |
465 | All_DHCP_Servers); | |
466 | } | |
467 | mreq.ipv6mr_interface = if_nametoindex(info->name); | |
468 | if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, | |
469 | &mreq, sizeof(mreq)) < 0) { | |
470 | log_fatal("setsockopt: IPV6_JOIN_GROUP: %m"); | |
471 | } | |
472 | } | |
473 | } | |
474 | ||
98bd7ca0 DH |
475 | void |
476 | if_register6(struct interface_info *info, int do_multicast) { | |
ecddae64 DH |
477 | /* Bounce do_multicast to a stack variable because we may change it. */ |
478 | int req_multi = do_multicast; | |
479 | ||
480 | if (global_v6_socket_references == 0) { | |
481 | global_v6_socket = if_register_socket(info, AF_INET6, | |
482 | &req_multi); | |
483 | if (global_v6_socket < 0) { | |
484 | /* | |
485 | * if_register_socket() fatally logs if it fails to | |
486 | * create a socket, this is just a sanity check. | |
487 | */ | |
488 | log_fatal("Impossible condition at %s:%d", MDL); | |
489 | } else { | |
490 | log_info("Bound to *:%d", ntohs(local_port)); | |
491 | } | |
492 | } | |
493 | ||
494 | info->rfdesc = global_v6_socket; | |
495 | info->wfdesc = global_v6_socket; | |
496 | global_v6_socket_references++; | |
497 | ||
498 | if (req_multi) | |
499 | if_register_multicast(info); | |
500 | ||
501 | get_hw_addr(info->name, &info->hw_address); | |
502 | ||
98bd7ca0 DH |
503 | if (!quiet_interface_discovery) { |
504 | if (info->shared_network != NULL) { | |
ecddae64 DH |
505 | log_info("Listening on Socket/%d/%s/%s", |
506 | global_v6_socket, info->name, | |
98bd7ca0 | 507 | info->shared_network->name); |
ecddae64 DH |
508 | log_info("Sending on Socket/%d/%s/%s", |
509 | global_v6_socket, info->name, | |
98bd7ca0 DH |
510 | info->shared_network->name); |
511 | } else { | |
512 | log_info("Listening on Socket/%s", info->name); | |
513 | log_info("Sending on Socket/%s", info->name); | |
514 | } | |
515 | } | |
516 | } | |
517 | ||
518 | void | |
519 | if_deregister6(struct interface_info *info) { | |
ecddae64 DH |
520 | /* Dereference the global v6 socket. */ |
521 | if ((info->rfdesc == global_v6_socket) && | |
522 | (info->wfdesc == global_v6_socket) && | |
523 | (global_v6_socket_references > 0)) { | |
524 | global_v6_socket_references--; | |
525 | info->rfdesc = -1; | |
526 | info->wfdesc = -1; | |
527 | } else { | |
528 | log_fatal("Impossible condition at %s:%d", MDL); | |
529 | } | |
98bd7ca0 DH |
530 | |
531 | if (!quiet_interface_discovery) { | |
532 | if (info->shared_network != NULL) { | |
533 | log_info("Disabling input on Socket/%s/%s", info->name, | |
534 | info->shared_network->name); | |
535 | log_info("Disabling output on Socket/%s/%s", info->name, | |
536 | info->shared_network->name); | |
537 | } else { | |
538 | log_info("Disabling input on Socket/%s", info->name); | |
539 | log_info("Disabling output on Socket/%s", info->name); | |
540 | } | |
541 | } | |
ecddae64 DH |
542 | |
543 | if (global_v6_socket_references == 0) { | |
544 | close(global_v6_socket); | |
545 | global_v6_socket = -1; | |
546 | ||
547 | log_info("Unbound from *:%d", ntohs(local_port)); | |
548 | } | |
98bd7ca0 | 549 | } |
fe5b0fdd | 550 | #endif /* DHCPv6 */ |
98bd7ca0 | 551 | |
a1b705e5 | 552 | #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) |
4595a58c | 553 | ssize_t send_packet (interface, packet, raw, len, from, to, hto) |
e23c9055 | 554 | struct interface_info *interface; |
a8b53b42 TL |
555 | struct packet *packet; |
556 | struct dhcp_packet *raw; | |
557 | size_t len; | |
0a5d6860 | 558 | struct in_addr from; |
c857a7b6 | 559 | struct sockaddr_in *to; |
e23c9055 | 560 | struct hardware *hto; |
a8b53b42 | 561 | { |
5145810c TL |
562 | int result; |
563 | #ifdef IGNORE_HOSTUNREACH | |
564 | int retry = 0; | |
565 | do { | |
7cfeb916 SR |
566 | #endif |
567 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
568 | struct in_pktinfo pktinfo; | |
569 | ||
570 | if (interface->ifp != NULL) { | |
571 | memset(&pktinfo, 0, sizeof (pktinfo)); | |
572 | pktinfo.ipi_ifindex = interface->ifp->ifr_index; | |
573 | if (setsockopt(interface->wfdesc, IPPROTO_IP, | |
574 | IP_PKTINFO, (char *)&pktinfo, | |
575 | sizeof(pktinfo)) < 0) | |
576 | log_fatal("setsockopt: IP_PKTINFO: %m"); | |
577 | } | |
5145810c TL |
578 | #endif |
579 | result = sendto (interface -> wfdesc, (char *)raw, len, 0, | |
580 | (struct sockaddr *)to, sizeof *to); | |
581 | #ifdef IGNORE_HOSTUNREACH | |
13ee152c | 582 | } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && |
5145810c TL |
583 | result < 0 && |
584 | (errno == EHOSTUNREACH || | |
585 | errno == ECONNREFUSED) && | |
586 | retry++ < 10); | |
587 | #endif | |
74f45f96 | 588 | if (result < 0) { |
c5b0f529 | 589 | log_error ("send_packet: %m"); |
74f45f96 | 590 | if (errno == ENETUNREACH) |
a1b705e5 TL |
591 | log_error ("send_packet: please consult README file%s", |
592 | " regarding broadcast address."); | |
74f45f96 | 593 | } |
af361f2a | 594 | return result; |
a8b53b42 | 595 | } |
98bd7ca0 | 596 | |
a1b705e5 | 597 | #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ |
a8b53b42 | 598 | |
cff9b78f SK |
599 | #ifdef DHCPv6 |
600 | /* | |
601 | * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will | |
602 | * synthesize them (based on the BIND 9 technique). | |
603 | */ | |
604 | ||
605 | #ifndef CMSG_LEN | |
606 | static size_t CMSG_LEN(size_t len) { | |
607 | size_t hdrlen; | |
608 | /* | |
609 | * Cast NULL so that any pointer arithmetic performed by CMSG_DATA | |
610 | * is correct. | |
611 | */ | |
612 | hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL)); | |
613 | return hdrlen + len; | |
614 | } | |
615 | #endif /* !CMSG_LEN */ | |
616 | ||
617 | #ifndef CMSG_SPACE | |
618 | static size_t CMSG_SPACE(size_t len) { | |
619 | struct msghdr msg; | |
620 | struct cmsghdr *cmsgp; | |
621 | ||
622 | /* | |
623 | * XXX: The buffer length is an ad-hoc value, but should be enough | |
624 | * in a practical sense. | |
625 | */ | |
626 | union { | |
627 | struct cmsghdr cmsg_sizer; | |
628 | u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024]; | |
629 | } dummybuf; | |
630 | ||
631 | memset(&msg, 0, sizeof(msg)); | |
632 | msg.msg_control = &dummybuf; | |
633 | msg.msg_controllen = sizeof(dummybuf); | |
634 | ||
635 | cmsgp = (struct cmsghdr *)&dummybuf; | |
636 | cmsgp->cmsg_len = CMSG_LEN(len); | |
637 | ||
638 | cmsgp = CMSG_NXTHDR(&msg, cmsgp); | |
639 | if (cmsgp != NULL) { | |
640 | return (char *)cmsgp - (char *)msg.msg_control; | |
641 | } else { | |
642 | return 0; | |
643 | } | |
644 | } | |
645 | #endif /* !CMSG_SPACE */ | |
646 | ||
647 | #endif /* DHCPv6 */ | |
648 | ||
7cfeb916 SR |
649 | #if defined(DHCPv6) || \ |
650 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
651 | defined(USE_V4_PKTINFO)) | |
57fbc772 SR |
652 | /* |
653 | * For both send_packet6() and receive_packet6() we need to allocate | |
654 | * space for the cmsg header information. We do this once and reuse | |
7cfeb916 SR |
655 | * the buffer. We also need the control buf for send_packet() and |
656 | * receive_packet() when we use a single socket and IP_PKTINFO to | |
657 | * send the packet out the correct interface. | |
57fbc772 SR |
658 | */ |
659 | static void *control_buf = NULL; | |
660 | static size_t control_buf_len = 0; | |
661 | ||
662 | static void | |
663 | allocate_cmsg_cbuf(void) { | |
664 | control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); | |
665 | control_buf = dmalloc(control_buf_len, MDL); | |
666 | return; | |
667 | } | |
7cfeb916 | 668 | #endif /* DHCPv6, IP_PKTINFO ... */ |
57fbc772 | 669 | |
7cfeb916 | 670 | #ifdef DHCPv6 |
98bd7ca0 DH |
671 | /* |
672 | * For both send_packet6() and receive_packet6() we need to use the | |
20ae1aff | 673 | * sendmsg()/recvmsg() functions rather than the simpler send()/recv() |
98bd7ca0 DH |
674 | * functions. |
675 | * | |
676 | * In the case of send_packet6(), we need to do this in order to insure | |
677 | * that the reply packet leaves on the same interface that it arrived | |
678 | * on. | |
679 | * | |
680 | * In the case of receive_packet6(), we need to do this in order to | |
681 | * get the IP address the packet was sent to. This is used to identify | |
682 | * whether a packet is multicast or unicast. | |
683 | * | |
684 | * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg. | |
685 | * | |
686 | * Also see the sections in RFC 3542 about IPV6_PKTINFO. | |
687 | */ | |
688 | ||
689 | /* Send an IPv6 packet */ | |
690 | ssize_t send_packet6(struct interface_info *interface, | |
6705543f | 691 | const unsigned char *raw, size_t len, |
98bd7ca0 DH |
692 | struct sockaddr_in6 *to) { |
693 | struct msghdr m; | |
694 | struct iovec v; | |
695 | int result; | |
696 | struct in6_pktinfo *pktinfo; | |
98bd7ca0 | 697 | struct cmsghdr *cmsg; |
57fbc772 SR |
698 | |
699 | /* | |
700 | * If necessary allocate space for the control message header. | |
701 | * The space is common between send and receive. | |
702 | */ | |
703 | ||
704 | if (control_buf == NULL) { | |
705 | allocate_cmsg_cbuf(); | |
706 | if (control_buf == NULL) { | |
707 | log_error("send_packet6: unable to allocate cmsg header"); | |
708 | return(ENOMEM); | |
709 | } | |
710 | } | |
711 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
712 | |
713 | /* | |
714 | * Initialize our message header structure. | |
715 | */ | |
716 | memset(&m, 0, sizeof(m)); | |
717 | ||
718 | /* | |
719 | * Set the target address we're sending to. | |
720 | */ | |
721 | m.msg_name = to; | |
722 | m.msg_namelen = sizeof(*to); | |
723 | ||
724 | /* | |
725 | * Set the data buffer we're sending. (Using this wacky | |
726 | * "scatter-gather" stuff... we only have a single chunk | |
727 | * of data to send, so we declare a single vector entry.) | |
728 | */ | |
729 | v.iov_base = (char *)raw; | |
730 | v.iov_len = len; | |
731 | m.msg_iov = &v; | |
732 | m.msg_iovlen = 1; | |
733 | ||
734 | /* | |
735 | * Setting the interface is a bit more involved. | |
736 | * | |
737 | * We have to create a "control message", and set that to | |
738 | * define the IPv6 packet information. We could set the | |
739 | * source address if we wanted, but we can safely let the | |
740 | * kernel decide what that should be. | |
741 | */ | |
57fbc772 SR |
742 | m.msg_control = control_buf; |
743 | m.msg_controllen = control_buf_len; | |
98bd7ca0 DH |
744 | cmsg = CMSG_FIRSTHDR(&m); |
745 | cmsg->cmsg_level = IPPROTO_IPV6; | |
746 | cmsg->cmsg_type = IPV6_PKTINFO; | |
747 | cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo)); | |
748 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
749 | memset(pktinfo, 0, sizeof(*pktinfo)); | |
750 | pktinfo->ipi6_ifindex = if_nametoindex(interface->name); | |
751 | m.msg_controllen = cmsg->cmsg_len; | |
752 | ||
753 | result = sendmsg(interface->wfdesc, &m, 0); | |
754 | if (result < 0) { | |
755 | log_error("send_packet6: %m"); | |
756 | } | |
757 | return result; | |
758 | } | |
fe5b0fdd | 759 | #endif /* DHCPv6 */ |
98bd7ca0 | 760 | |
c857a7b6 | 761 | #ifdef USE_SOCKET_RECEIVE |
4595a58c | 762 | ssize_t receive_packet (interface, buf, len, from, hfrom) |
e23c9055 TL |
763 | struct interface_info *interface; |
764 | unsigned char *buf; | |
765 | size_t len; | |
766 | struct sockaddr_in *from; | |
767 | struct hardware *hfrom; | |
a8b53b42 | 768 | { |
865afd5e | 769 | #if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)) |
d1f313b3 | 770 | SOCKLEN_T flen = sizeof *from; |
7cfeb916 | 771 | #endif |
5145810c | 772 | int result; |
e23c9055 | 773 | |
98bd7ca0 DH |
774 | /* |
775 | * The normal Berkeley socket interface doesn't give us any way | |
776 | * to know what hardware interface we received the message on, | |
777 | * but we should at least make sure the structure is emptied. | |
778 | */ | |
779 | memset(hfrom, 0, sizeof(*hfrom)); | |
780 | ||
5145810c TL |
781 | #ifdef IGNORE_HOSTUNREACH |
782 | int retry = 0; | |
783 | do { | |
784 | #endif | |
7cfeb916 SR |
785 | |
786 | #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO) | |
787 | struct msghdr m; | |
788 | struct iovec v; | |
789 | struct cmsghdr *cmsg; | |
790 | struct in_pktinfo *pktinfo; | |
791 | unsigned int ifindex; | |
7cfeb916 SR |
792 | |
793 | /* | |
794 | * If necessary allocate space for the control message header. | |
795 | * The space is common between send and receive. | |
796 | */ | |
797 | if (control_buf == NULL) { | |
798 | allocate_cmsg_cbuf(); | |
799 | if (control_buf == NULL) { | |
800 | log_error("receive_packet: unable to allocate cmsg " | |
801 | "header"); | |
802 | return(ENOMEM); | |
803 | } | |
804 | } | |
805 | memset(control_buf, 0, control_buf_len); | |
806 | ||
807 | /* | |
808 | * Initialize our message header structure. | |
809 | */ | |
810 | memset(&m, 0, sizeof(m)); | |
811 | ||
812 | /* | |
813 | * Point so we can get the from address. | |
814 | */ | |
815 | m.msg_name = from; | |
816 | m.msg_namelen = sizeof(*from); | |
817 | ||
818 | /* | |
819 | * Set the data buffer we're receiving. (Using this wacky | |
820 | * "scatter-gather" stuff... but we that doesn't really make | |
821 | * sense for us, so we use a single vector entry.) | |
822 | */ | |
823 | v.iov_base = buf; | |
824 | v.iov_len = len; | |
825 | m.msg_iov = &v; | |
826 | m.msg_iovlen = 1; | |
827 | ||
828 | /* | |
829 | * Getting the interface is a bit more involved. | |
830 | * | |
831 | * We set up some space for a "control message". We have | |
832 | * previously asked the kernel to give us packet | |
833 | * information (when we initialized the interface), so we | |
865afd5e | 834 | * should get the interface index from that. |
7cfeb916 SR |
835 | */ |
836 | m.msg_control = control_buf; | |
837 | m.msg_controllen = control_buf_len; | |
838 | ||
839 | result = recvmsg(interface->rfdesc, &m, 0); | |
840 | ||
841 | if (result >= 0) { | |
842 | /* | |
843 | * If we did read successfully, then we need to loop | |
844 | * through the control messages we received and | |
865afd5e | 845 | * find the one with our inteface index. |
7cfeb916 | 846 | */ |
7cfeb916 SR |
847 | cmsg = CMSG_FIRSTHDR(&m); |
848 | while (cmsg != NULL) { | |
849 | if ((cmsg->cmsg_level == IPPROTO_IP) && | |
850 | (cmsg->cmsg_type == IP_PKTINFO)) { | |
851 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
852 | ifindex = pktinfo->ipi_ifindex; | |
853 | /* | |
854 | * We pass the ifindex back to the caller | |
855 | * using the unused hfrom parameter avoiding | |
856 | * interface changes between sockets and | |
857 | * the discover code. | |
858 | */ | |
859 | memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex)); | |
865afd5e | 860 | return (result); |
7cfeb916 SR |
861 | } |
862 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
863 | } | |
865afd5e SR |
864 | |
865 | /* | |
866 | * We didn't find the necessary control message | |
867 | * flag it as an error | |
868 | */ | |
869 | result = -1; | |
870 | errno = EIO; | |
7cfeb916 SR |
871 | } |
872 | #else | |
865afd5e SR |
873 | result = recvfrom(interface -> rfdesc, (char *)buf, len, 0, |
874 | (struct sockaddr *)from, &flen); | |
7cfeb916 | 875 | #endif /* IP_PKTINFO ... */ |
5145810c TL |
876 | #ifdef IGNORE_HOSTUNREACH |
877 | } while (result < 0 && | |
878 | (errno == EHOSTUNREACH || | |
879 | errno == ECONNREFUSED) && | |
880 | retry++ < 10); | |
881 | #endif | |
865afd5e | 882 | return (result); |
c857a7b6 | 883 | } |
98bd7ca0 | 884 | |
c857a7b6 | 885 | #endif /* USE_SOCKET_RECEIVE */ |
c5568eb5 | 886 | |
fe5b0fdd | 887 | #ifdef DHCPv6 |
98bd7ca0 DH |
888 | ssize_t |
889 | receive_packet6(struct interface_info *interface, | |
890 | unsigned char *buf, size_t len, | |
ecddae64 DH |
891 | struct sockaddr_in6 *from, struct in6_addr *to_addr, |
892 | unsigned int *if_idx) | |
893 | { | |
98bd7ca0 DH |
894 | struct msghdr m; |
895 | struct iovec v; | |
98bd7ca0 DH |
896 | int result; |
897 | struct cmsghdr *cmsg; | |
898 | struct in6_pktinfo *pktinfo; | |
57fbc772 SR |
899 | |
900 | /* | |
901 | * If necessary allocate space for the control message header. | |
902 | * The space is common between send and receive. | |
903 | */ | |
904 | if (control_buf == NULL) { | |
905 | allocate_cmsg_cbuf(); | |
906 | if (control_buf == NULL) { | |
436e808a SR |
907 | log_error("receive_packet6: unable to allocate cmsg " |
908 | "header"); | |
57fbc772 SR |
909 | return(ENOMEM); |
910 | } | |
911 | } | |
912 | memset(control_buf, 0, control_buf_len); | |
98bd7ca0 DH |
913 | |
914 | /* | |
915 | * Initialize our message header structure. | |
916 | */ | |
917 | memset(&m, 0, sizeof(m)); | |
918 | ||
919 | /* | |
920 | * Point so we can get the from address. | |
921 | */ | |
922 | m.msg_name = from; | |
923 | m.msg_namelen = sizeof(*from); | |
924 | ||
925 | /* | |
926 | * Set the data buffer we're receiving. (Using this wacky | |
927 | * "scatter-gather" stuff... but we that doesn't really make | |
928 | * sense for us, so we use a single vector entry.) | |
929 | */ | |
930 | v.iov_base = buf; | |
931 | v.iov_len = len; | |
932 | m.msg_iov = &v; | |
933 | m.msg_iovlen = 1; | |
934 | ||
935 | /* | |
936 | * Getting the interface is a bit more involved. | |
937 | * | |
938 | * We set up some space for a "control message". We have | |
939 | * previously asked the kernel to give us packet | |
940 | * information (when we initialized the interface), so we | |
941 | * should get the destination address from that. | |
942 | */ | |
57fbc772 SR |
943 | m.msg_control = control_buf; |
944 | m.msg_controllen = control_buf_len; | |
98bd7ca0 DH |
945 | |
946 | result = recvmsg(interface->rfdesc, &m, 0); | |
947 | ||
948 | if (result >= 0) { | |
949 | /* | |
950 | * If we did read successfully, then we need to loop | |
951 | * through the control messages we received and | |
952 | * find the one with our destination address. | |
98bd7ca0 | 953 | */ |
98bd7ca0 DH |
954 | cmsg = CMSG_FIRSTHDR(&m); |
955 | while (cmsg != NULL) { | |
956 | if ((cmsg->cmsg_level == IPPROTO_IPV6) && | |
957 | (cmsg->cmsg_type == IPV6_PKTINFO)) { | |
958 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
959 | *to_addr = pktinfo->ipi6_addr; | |
ecddae64 | 960 | *if_idx = pktinfo->ipi6_ifindex; |
865afd5e SR |
961 | |
962 | return (result); | |
98bd7ca0 DH |
963 | } |
964 | cmsg = CMSG_NXTHDR(&m, cmsg); | |
965 | } | |
865afd5e SR |
966 | |
967 | /* | |
968 | * We didn't find the necessary control message | |
969 | * flag is as an error | |
970 | */ | |
971 | result = -1; | |
972 | errno = EIO; | |
98bd7ca0 DH |
973 | } |
974 | ||
865afd5e | 975 | return (result); |
98bd7ca0 | 976 | } |
fe5b0fdd | 977 | #endif /* DHCPv6 */ |
98bd7ca0 | 978 | |
e15381fc | 979 | #if defined (USE_SOCKET_FALLBACK) |
c5568eb5 TL |
980 | /* This just reads in a packet and silently discards it. */ |
981 | ||
acc21512 | 982 | isc_result_t fallback_discard (object) |
e92653f1 | 983 | omapi_object_t *object; |
c5568eb5 TL |
984 | { |
985 | char buf [1540]; | |
986 | struct sockaddr_in from; | |
d1f313b3 | 987 | SOCKLEN_T flen = sizeof from; |
c01f1285 | 988 | int status; |
acc21512 TL |
989 | struct interface_info *interface; |
990 | ||
991 | if (object -> type != dhcp_type_interface) | |
98bf1607 | 992 | return DHCP_R_INVALIDARG; |
acc21512 | 993 | interface = (struct interface_info *)object; |
c5568eb5 | 994 | |
c01f1285 TL |
995 | status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, |
996 | (struct sockaddr *)&from, &flen); | |
4089dd21 M |
997 | #if defined (DEBUG) |
998 | /* Only report fallback discard errors if we're debugging. */ | |
acc21512 | 999 | if (status < 0) { |
8ae2d595 | 1000 | log_error ("fallback_discard: %m"); |
acc21512 TL |
1001 | return ISC_R_UNEXPECTED; |
1002 | } | |
4089dd21 | 1003 | #endif |
acc21512 | 1004 | return ISC_R_SUCCESS; |
c5568eb5 | 1005 | } |
a1b705e5 | 1006 | #endif /* USE_SOCKET_FALLBACK */ |
d2bc90bd | 1007 | |
a1b705e5 | 1008 | #if defined (USE_SOCKET_SEND) |
21d21e91 TL |
1009 | int can_unicast_without_arp (ip) |
1010 | struct interface_info *ip; | |
d2bc90bd TL |
1011 | { |
1012 | return 0; | |
1013 | } | |
1014 | ||
21d21e91 TL |
1015 | int can_receive_unicast_unconfigured (ip) |
1016 | struct interface_info *ip; | |
b547818b | 1017 | { |
a1b705e5 TL |
1018 | #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) |
1019 | return 1; | |
1020 | #else | |
b547818b | 1021 | return 0; |
a1b705e5 | 1022 | #endif |
b547818b TL |
1023 | } |
1024 | ||
5cefe5e5 TL |
1025 | int supports_multiple_interfaces (ip) |
1026 | struct interface_info *ip; | |
1027 | { | |
7cfeb916 SR |
1028 | #if defined(SO_BINDTODEVICE) || \ |
1029 | (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \ | |
1030 | defined(USE_V4_PKTINFO)) | |
1031 | return(1); | |
5cefe5e5 | 1032 | #else |
7cfeb916 | 1033 | return(0); |
5cefe5e5 TL |
1034 | #endif |
1035 | } | |
1036 | ||
d2bc90bd TL |
1037 | /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, |
1038 | do not. */ | |
1039 | ||
1040 | void maybe_setup_fallback () | |
1041 | { | |
a1b705e5 | 1042 | #if defined (USE_SOCKET_FALLBACK) |
acc21512 | 1043 | isc_result_t status; |
950c6a06 TL |
1044 | struct interface_info *fbi = (struct interface_info *)0; |
1045 | if (setup_fallback (&fbi, MDL)) { | |
98bd7ca0 | 1046 | fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0); |
a47105d6 TL |
1047 | fbi -> rfdesc = fbi -> wfdesc; |
1048 | log_info ("Sending on Socket/%s%s%s", | |
1049 | fbi -> name, | |
1050 | (fbi -> shared_network ? "/" : ""), | |
1051 | (fbi -> shared_network ? | |
1052 | fbi -> shared_network -> name : "")); | |
1053 | ||
e48c5c5c | 1054 | status = omapi_register_io_object ((omapi_object_t *)fbi, |
acc21512 TL |
1055 | if_readsocket, 0, |
1056 | fallback_discard, 0, 0); | |
1057 | if (status != ISC_R_SUCCESS) | |
1058 | log_fatal ("Can't register I/O handle for %s: %s", | |
1059 | fbi -> name, isc_result_totext (status)); | |
950c6a06 | 1060 | interface_dereference (&fbi, MDL); |
d2bc90bd TL |
1061 | } |
1062 | #endif | |
1063 | } | |
7cfeb916 SR |
1064 | |
1065 | ||
1066 | #if defined(sun) && defined(USE_V4_PKTINFO) | |
1067 | /* This code assumes the existence of SIOCGLIFHWADDR */ | |
1068 | void | |
1069 | get_hw_addr(const char *name, struct hardware *hw) { | |
1070 | struct sockaddr_dl *dladdrp; | |
b047bd38 | 1071 | int sock, i; |
7cfeb916 SR |
1072 | struct lifreq lifr; |
1073 | ||
1074 | memset(&lifr, 0, sizeof (lifr)); | |
1075 | (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); | |
1076 | /* | |
1077 | * Check if the interface is a virtual or IPMP interface - in those | |
1078 | * cases it has no hw address, so generate a random one. | |
1079 | */ | |
1080 | if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || | |
1081 | ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) { | |
1082 | if (sock != -1) | |
1083 | (void) close(sock); | |
1084 | ||
1085 | #ifdef DHCPv6 | |
1086 | /* | |
1087 | * If approrpriate try this with an IPv6 socket | |
1088 | */ | |
1089 | if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 && | |
1090 | ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) { | |
1091 | goto flag_check; | |
1092 | } | |
1093 | if (sock != -1) | |
1094 | (void) close(sock); | |
1095 | #endif | |
1096 | log_fatal("Couldn't get interface flags for %s: %m", name); | |
1097 | ||
1098 | } | |
1099 | ||
1100 | flag_check: | |
1101 | if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) { | |
1102 | hw->hlen = sizeof (hw->hbuf); | |
1103 | srandom((long)gethrtime()); | |
1104 | ||
b047bd38 SR |
1105 | hw->hbuf[0] = HTYPE_IPMP; |
1106 | for (i = 1; i < hw->hlen; ++i) { | |
7cfeb916 SR |
1107 | hw->hbuf[i] = random() % 256; |
1108 | } | |
1109 | ||
1110 | if (sock != -1) | |
1111 | (void) close(sock); | |
1112 | return; | |
1113 | } | |
1114 | ||
1115 | if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0) | |
1116 | log_fatal("Couldn't get interface hardware address for %s: %m", | |
1117 | name); | |
1118 | dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr; | |
b047bd38 SR |
1119 | hw->hlen = dladdrp->sdl_alen+1; |
1120 | switch (dladdrp->sdl_type) { | |
1121 | case DL_CSMACD: /* IEEE 802.3 */ | |
1122 | case DL_ETHER: | |
1123 | hw->hbuf[0] = HTYPE_ETHER; | |
1124 | break; | |
1125 | case DL_TPR: | |
1126 | hw->hbuf[0] = HTYPE_IEEE802; | |
1127 | break; | |
1128 | case DL_FDDI: | |
1129 | hw->hbuf[0] = HTYPE_FDDI; | |
1130 | break; | |
1131 | case DL_IB: | |
1132 | hw->hbuf[0] = HTYPE_INFINIBAND; | |
1133 | break; | |
1134 | default: | |
1135 | log_fatal("%s: unsupported DLPI MAC type %lu", name, | |
1136 | (unsigned long)dladdrp->sdl_type); | |
1137 | } | |
1138 | ||
1139 | memcpy(hw->hbuf+1, LLADDR(dladdrp), hw->hlen-1); | |
7cfeb916 SR |
1140 | |
1141 | if (sock != -1) | |
1142 | (void) close(sock); | |
1143 | } | |
1144 | #endif /* defined(sun) */ | |
1145 | ||
a1b705e5 | 1146 | #endif /* USE_SOCKET_SEND */ |