]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* setsockopt functions |
2 | * Copyright (C) 1999 Kunihiro Ishiguro | |
3 | * | |
4 | * This file is part of GNU Zebra. | |
5 | * | |
6 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * GNU Zebra is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
18 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | * 02111-1307, USA. | |
20 | */ | |
21 | ||
22 | #include <zebra.h> | |
23 | #include "log.h" | |
e6822768 | 24 | #include "sockopt.h" |
0df7c91f | 25 | #include "sockunion.h" |
718e3744 | 26 | |
0b3acf4f | 27 | int |
28 | setsockopt_so_recvbuf (int sock, int size) | |
29 | { | |
30 | int ret; | |
31 | ||
b89e60c7 | 32 | if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *) |
ff29bb31 | 33 | &size, sizeof (int))) < 0) |
34 | zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s", | |
ae5e24d8 | 35 | sock,size,safe_strerror(errno)); |
0b3acf4f | 36 | |
37 | return ret; | |
38 | } | |
39 | ||
b7fe4141 DO |
40 | int |
41 | setsockopt_so_sendbuf (const int sock, int size) | |
42 | { | |
43 | int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF, | |
44 | (char *)&size, sizeof (int)); | |
45 | ||
46 | if (ret < 0) | |
47 | zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s", | |
48 | sock, size, safe_strerror (errno)); | |
49 | ||
50 | return ret; | |
51 | } | |
52 | ||
53 | int | |
54 | getsockopt_so_sendbuf (const int sock) | |
55 | { | |
56 | u_int32_t optval; | |
57 | socklen_t optlen = sizeof (optval); | |
58 | int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, | |
59 | (char *)&optval, &optlen); | |
60 | if (ret < 0) | |
61 | { | |
62 | zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)", | |
63 | sock, errno, safe_strerror (errno)); | |
64 | return ret; | |
65 | } | |
66 | return optval; | |
67 | } | |
68 | ||
4f7baa0e | 69 | static void * |
70 | getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) | |
71 | { | |
72 | struct cmsghdr *cmsg; | |
73 | void *ptr = NULL; | |
74 | ||
b99760ab | 75 | for (cmsg = ZCMSG_FIRSTHDR(msgh); |
4f7baa0e | 76 | cmsg != NULL; |
77 | cmsg = CMSG_NXTHDR(msgh, cmsg)) | |
78 | if (cmsg->cmsg_level == level && cmsg->cmsg_type) | |
79 | return (ptr = CMSG_DATA(cmsg)); | |
9035efaa | 80 | |
81 | return NULL; | |
4f7baa0e | 82 | } |
83 | ||
718e3744 | 84 | #ifdef HAVE_IPV6 |
85 | /* Set IPv6 packet info to the socket. */ | |
86 | int | |
87 | setsockopt_ipv6_pktinfo (int sock, int val) | |
88 | { | |
89 | int ret; | |
90 | ||
91 | #ifdef IPV6_RECVPKTINFO /*2292bis-01*/ | |
92 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); | |
93 | if (ret < 0) | |
6099b3b5 | 94 | zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno)); |
718e3744 | 95 | #else /*RFC2292*/ |
96 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); | |
97 | if (ret < 0) | |
6099b3b5 | 98 | zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno)); |
718e3744 | 99 | #endif /* INIA_IPV6 */ |
100 | return ret; | |
101 | } | |
102 | ||
103 | /* Set multicast hops val to the socket. */ | |
104 | int | |
105 | setsockopt_ipv6_checksum (int sock, int val) | |
106 | { | |
107 | int ret; | |
108 | ||
109 | #ifdef GNU_LINUX | |
110 | ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); | |
111 | #else | |
112 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); | |
113 | #endif /* GNU_LINUX */ | |
114 | if (ret < 0) | |
115 | zlog_warn ("can't setsockopt IPV6_CHECKSUM"); | |
116 | return ret; | |
117 | } | |
118 | ||
119 | /* Set multicast hops val to the socket. */ | |
120 | int | |
121 | setsockopt_ipv6_multicast_hops (int sock, int val) | |
122 | { | |
123 | int ret; | |
124 | ||
125 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)); | |
126 | if (ret < 0) | |
127 | zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS"); | |
128 | return ret; | |
129 | } | |
130 | ||
131 | /* Set multicast hops val to the socket. */ | |
132 | int | |
133 | setsockopt_ipv6_unicast_hops (int sock, int val) | |
134 | { | |
135 | int ret; | |
136 | ||
137 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); | |
138 | if (ret < 0) | |
139 | zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS"); | |
140 | return ret; | |
141 | } | |
142 | ||
143 | int | |
144 | setsockopt_ipv6_hoplimit (int sock, int val) | |
145 | { | |
146 | int ret; | |
147 | ||
148 | #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ | |
149 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)); | |
150 | if (ret < 0) | |
151 | zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT"); | |
152 | #else /*RFC2292*/ | |
153 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); | |
154 | if (ret < 0) | |
155 | zlog_warn ("can't setsockopt IPV6_HOPLIMIT"); | |
156 | #endif | |
157 | return ret; | |
158 | } | |
159 | ||
160 | /* Set multicast loop zero to the socket. */ | |
161 | int | |
162 | setsockopt_ipv6_multicast_loop (int sock, int val) | |
163 | { | |
164 | int ret; | |
165 | ||
166 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, | |
167 | sizeof (val)); | |
168 | if (ret < 0) | |
169 | zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP"); | |
170 | return ret; | |
171 | } | |
172 | ||
4f7baa0e | 173 | static int |
e6822768 | 174 | getsockopt_ipv6_ifindex (struct msghdr *msgh) |
4f7baa0e | 175 | { |
176 | struct in6_pktinfo *pktinfo; | |
177 | ||
178 | pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); | |
179 | ||
180 | return pktinfo->ipi6_ifindex; | |
181 | } | |
718e3744 | 182 | |
6d0732c8 SH |
183 | int |
184 | setsockopt_ipv6_tclass(int sock, int tclass) | |
185 | { | |
ad61af67 | 186 | int ret = 0; |
6d0732c8 | 187 | |
ad61af67 | 188 | #ifdef IPV6_TCLASS /* RFC3542 */ |
6d0732c8 SH |
189 | ret = setsockopt (sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof (tclass)); |
190 | if (ret < 0) | |
191 | zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s", | |
192 | sock, tclass, safe_strerror(errno)); | |
ad61af67 | 193 | #endif |
6d0732c8 SH |
194 | return ret; |
195 | } | |
196 | #endif /* HAVE_IPV6 */ | |
718e3744 | 197 | |
cc49eb5a | 198 | /* |
199 | * Process multicast socket options for IPv4 in an OS-dependent manner. | |
69bf3a39 | 200 | * Supported options are IP_{ADD,DROP}_MEMBERSHIP. |
cc49eb5a | 201 | * |
202 | * Many operating systems have a limit on the number of groups that | |
203 | * can be joined per socket (where each group and local address | |
204 | * counts). This impacts OSPF, which joins groups on each interface | |
205 | * using a single socket. The limit is typically 20, derived from the | |
206 | * original BSD multicast implementation. Some systems have | |
207 | * mechanisms for increasing this limit. | |
c188c37c | 208 | * |
209 | * In many 4.4BSD-derived systems, multicast group operations are not | |
210 | * allowed on interfaces that are not UP. Thus, a previous attempt to | |
211 | * leave the group may have failed, leaving it still joined, and we | |
212 | * drop/join quietly to recover. This may not be necessary, but aims to | |
213 | * defend against unknown behavior in that we will still return an error | |
214 | * if the second join fails. It is not clear how other systems | |
215 | * (e.g. Linux, Solaris) behave when leaving groups on down interfaces, | |
216 | * but this behavior should not be harmful if they behave the same way, | |
217 | * allow leaves, or implicitly leave all groups joined to down interfaces. | |
cc49eb5a | 218 | */ |
718e3744 | 219 | int |
69bf3a39 | 220 | setsockopt_ipv4_multicast(int sock, |
718e3744 | 221 | int optname, |
718e3744 | 222 | unsigned int mcast_addr, |
69bf3a39 | 223 | unsigned int ifindex) |
718e3744 | 224 | { |
10d04cdb DT |
225 | #ifdef HAVE_RFC3678 |
226 | struct group_req gr; | |
227 | struct sockaddr_in *si; | |
228 | int ret; | |
229 | memset (&gr, 0, sizeof(gr)); | |
230 | si = (struct sockaddr_in *)&gr.gr_group; | |
231 | gr.gr_interface = ifindex; | |
232 | si->sin_family = AF_INET; | |
233 | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN | |
234 | si->sin_len = sizeof(struct sockaddr_in); | |
235 | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ | |
236 | si->sin_addr.s_addr = mcast_addr; | |
237 | ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? | |
238 | MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr)); | |
239 | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) | |
240 | { | |
241 | setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr)); | |
242 | ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr)); | |
243 | } | |
244 | return ret; | |
718e3744 | 245 | |
10d04cdb | 246 | #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__) |
718e3744 | 247 | struct ip_mreqn mreqn; |
c188c37c | 248 | int ret; |
718e3744 | 249 | |
ee7e75d3 DT |
250 | assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); |
251 | memset (&mreqn, 0, sizeof(mreqn)); | |
252 | ||
10d04cdb | 253 | mreqn.imr_multiaddr.s_addr = mcast_addr; |
ee7e75d3 DT |
254 | mreqn.imr_ifindex = ifindex; |
255 | ||
256 | ret = setsockopt(sock, IPPROTO_IP, optname, | |
257 | (void *)&mreqn, sizeof(mreqn)); | |
258 | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) | |
718e3744 | 259 | { |
ee7e75d3 DT |
260 | /* see above: handle possible problem when interface comes back up */ |
261 | char buf[1][INET_ADDRSTRLEN]; | |
262 | zlog_info("setsockopt_ipv4_multicast attempting to drop and " | |
263 | "re-add (fd %d, mcast %s, ifindex %u)", | |
264 | sock, | |
265 | inet_ntop(AF_INET, &mreqn.imr_multiaddr, | |
266 | buf[0], sizeof(buf[0])), ifindex); | |
267 | setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, | |
268 | (void *)&mreqn, sizeof(mreqn)); | |
269 | ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, | |
270 | (void *)&mreqn, sizeof(mreqn)); | |
718e3744 | 271 | } |
ee7e75d3 | 272 | return ret; |
718e3744 | 273 | |
274 | /* Example defines for another OS, boilerplate off other code in this | |
275 | function, AND handle optname as per other sections for consistency !! */ | |
276 | /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ | |
277 | /* Add your favourite OS here! */ | |
278 | ||
69bf3a39 | 279 | #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ |
cc49eb5a | 280 | /* standard BSD API */ |
718e3744 | 281 | |
282 | struct in_addr m; | |
283 | struct ip_mreq mreq; | |
c188c37c | 284 | int ret; |
718e3744 | 285 | |
ee7e75d3 DT |
286 | assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); |
287 | ||
69bf3a39 | 288 | m.s_addr = htonl(ifindex); |
42c98199 | 289 | |
ee7e75d3 DT |
290 | memset (&mreq, 0, sizeof(mreq)); |
291 | mreq.imr_multiaddr.s_addr = mcast_addr; | |
292 | mreq.imr_interface = m; | |
293 | ||
294 | ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq)); | |
295 | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) | |
718e3744 | 296 | { |
ee7e75d3 DT |
297 | /* see above: handle possible problem when interface comes back up */ |
298 | char buf[1][INET_ADDRSTRLEN]; | |
299 | zlog_info("setsockopt_ipv4_multicast attempting to drop and " | |
300 | "re-add (fd %d, mcast %s, ifindex %u)", | |
301 | sock, | |
302 | inet_ntop(AF_INET, &mreq.imr_multiaddr, | |
303 | buf[0], sizeof(buf[0])), ifindex); | |
304 | setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, | |
305 | (void *)&mreq, sizeof(mreq)); | |
306 | ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, | |
307 | (void *)&mreq, sizeof(mreq)); | |
718e3744 | 308 | } |
ee7e75d3 DT |
309 | return ret; |
310 | ||
69bf3a39 DT |
311 | #else |
312 | #error "Unsupported multicast API" | |
718e3744 | 313 | #endif /* #if OS_TYPE */ |
314 | ||
315 | } | |
4f7baa0e | 316 | |
69bf3a39 DT |
317 | /* |
318 | * Set IP_MULTICAST_IF socket option in an OS-dependent manner. | |
319 | */ | |
320 | int | |
321 | setsockopt_ipv4_multicast_if(int sock, | |
322 | unsigned int ifindex) | |
323 | { | |
324 | ||
325 | #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX | |
326 | struct ip_mreqn mreqn; | |
e0afa6f4 | 327 | memset (&mreqn, 0, sizeof(mreqn)); |
69bf3a39 DT |
328 | |
329 | mreqn.imr_ifindex = ifindex; | |
330 | return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn)); | |
331 | ||
332 | /* Example defines for another OS, boilerplate off other code in this | |
333 | function */ | |
334 | /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ | |
335 | /* Add your favourite OS here! */ | |
336 | #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) | |
337 | struct in_addr m; | |
338 | ||
339 | m.s_addr = htonl(ifindex); | |
340 | ||
341 | return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m)); | |
342 | #else | |
343 | #error "Unsupported multicast API" | |
344 | #endif | |
345 | } | |
346 | ||
4f7baa0e | 347 | static int |
e6822768 | 348 | setsockopt_ipv4_ifindex (int sock, int val) |
4f7baa0e | 349 | { |
350 | int ret; | |
351 | ||
352 | #if defined (IP_PKTINFO) | |
1d75c8c3 | 353 | if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0) |
354 | zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s", | |
355 | sock,val,safe_strerror(errno)); | |
4f7baa0e | 356 | #elif defined (IP_RECVIF) |
1d75c8c3 | 357 | if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0) |
358 | zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s", | |
359 | sock,val,safe_strerror(errno)); | |
4f7baa0e | 360 | #else |
361 | #warning "Neither IP_PKTINFO nor IP_RECVIF is available." | |
362 | #warning "Will not be able to receive link info." | |
363 | #warning "Things might be seriously broken.." | |
1d75c8c3 | 364 | /* XXX Does this ever happen? Should there be a zlog_warn message here? */ |
365 | ret = -1; | |
4f7baa0e | 366 | #endif |
4f7baa0e | 367 | return ret; |
368 | } | |
369 | ||
1423c809 SH |
370 | int |
371 | setsockopt_ipv4_tos(int sock, int tos) | |
372 | { | |
373 | int ret; | |
374 | ||
375 | ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); | |
376 | if (ret < 0) | |
377 | zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s", | |
378 | sock, tos, safe_strerror(errno)); | |
379 | return ret; | |
380 | } | |
381 | ||
382 | ||
e6822768 | 383 | int |
384 | setsockopt_ifindex (int af, int sock, int val) | |
385 | { | |
386 | int ret = -1; | |
387 | ||
388 | switch (af) | |
389 | { | |
390 | case AF_INET: | |
391 | ret = setsockopt_ipv4_ifindex (sock, val); | |
392 | break; | |
393 | #ifdef HAVE_IPV6 | |
394 | case AF_INET6: | |
395 | ret = setsockopt_ipv6_pktinfo (sock, val); | |
396 | break; | |
397 | #endif | |
398 | default: | |
e473b032 | 399 | zlog_warn ("setsockopt_ifindex: unknown address family %d", af); |
e6822768 | 400 | } |
401 | return ret; | |
402 | } | |
403 | ||
d44debed | 404 | /* |
405 | * Requires: msgh is not NULL and points to a valid struct msghdr, which | |
406 | * may or may not have control data about the incoming interface. | |
407 | * | |
408 | * Returns the interface index (small integer >= 1) if it can be | |
409 | * determined, or else 0. | |
410 | */ | |
4f7baa0e | 411 | static int |
e6822768 | 412 | getsockopt_ipv4_ifindex (struct msghdr *msgh) |
4f7baa0e | 413 | { |
d44debed | 414 | /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ |
e6822768 | 415 | int ifindex = -1; |
1d69fdf6 | 416 | |
e6822768 | 417 | #if defined(IP_PKTINFO) |
418 | /* Linux pktinfo based ifindex retrieval */ | |
4f7baa0e | 419 | struct in_pktinfo *pktinfo; |
e6822768 | 420 | |
421 | pktinfo = | |
422 | (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); | |
d44debed | 423 | /* XXX Can pktinfo be NULL? Clean up post 0.98. */ |
e6822768 | 424 | ifindex = pktinfo->ipi_ifindex; |
425 | ||
426 | #elif defined(IP_RECVIF) | |
d44debed | 427 | |
428 | /* retrieval based on IP_RECVIF */ | |
429 | ||
4f7baa0e | 430 | #ifndef SUNOS_5 |
d44debed | 431 | /* BSD systems use a sockaddr_dl as the control message payload. */ |
4f7baa0e | 432 | struct sockaddr_dl *sdl; |
d44debed | 433 | #else |
434 | /* SUNOS_5 uses an integer with the index. */ | |
435 | int *ifindex_p; | |
4f7baa0e | 436 | #endif /* SUNOS_5 */ |
1d69fdf6 | 437 | |
33f92320 | 438 | #ifndef SUNOS_5 |
d44debed | 439 | /* BSD */ |
33f92320 | 440 | sdl = |
4f7baa0e | 441 | (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); |
d44debed | 442 | if (sdl != NULL) |
443 | ifindex = sdl->sdl_index; | |
444 | else | |
445 | ifindex = 0; | |
446 | #else | |
447 | /* | |
448 | * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to | |
449 | * enable it fails with errno=99, and the struct msghdr has | |
450 | * controllen 0. | |
451 | */ | |
452 | ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); | |
453 | if (ifindex_p != NULL) | |
454 | ifindex = *ifindex_p; | |
455 | else | |
456 | ifindex = 0; | |
4f7baa0e | 457 | #endif /* SUNOS_5 */ |
e6822768 | 458 | |
d44debed | 459 | #else |
460 | /* | |
461 | * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time. | |
462 | * XXX Decide if this is a core service, or if daemons have to cope. | |
463 | * Since Solaris 8 and OpenBSD seem not to provide it, it seems that | |
464 | * daemons have to cope. | |
465 | */ | |
466 | #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined." | |
467 | #warning "Some daemons may fail to operate correctly!" | |
7d9c6e51 | 468 | ifindex = 0; |
d44debed | 469 | |
e6822768 | 470 | #endif /* IP_PKTINFO */ |
d44debed | 471 | |
4f7baa0e | 472 | return ifindex; |
473 | } | |
474 | ||
475 | /* return ifindex, 0 if none found */ | |
476 | int | |
e6822768 | 477 | getsockopt_ifindex (int af, struct msghdr *msgh) |
4f7baa0e | 478 | { |
4f7baa0e | 479 | switch (af) |
480 | { | |
481 | case AF_INET: | |
e6822768 | 482 | return (getsockopt_ipv4_ifindex (msgh)); |
4f7baa0e | 483 | break; |
484 | #ifdef HAVE_IPV6 | |
485 | case AF_INET6: | |
e6822768 | 486 | return (getsockopt_ipv6_ifindex (msgh)); |
4f7baa0e | 487 | break; |
488 | #endif | |
489 | default: | |
e473b032 | 490 | zlog_warn ("getsockopt_ifindex: unknown address family %d", af); |
3a8c3dd7 | 491 | return 0; |
4f7baa0e | 492 | } |
493 | } | |
96e27c99 | 494 | |
495 | /* swab iph between order system uses for IP_HDRINCL and host order */ | |
496 | void | |
497 | sockopt_iphdrincl_swab_htosys (struct ip *iph) | |
498 | { | |
499 | /* BSD and derived take iph in network order, except for | |
500 | * ip_len and ip_off | |
501 | */ | |
502 | #ifndef HAVE_IP_HDRINCL_BSD_ORDER | |
503 | iph->ip_len = htons(iph->ip_len); | |
504 | iph->ip_off = htons(iph->ip_off); | |
505 | #endif /* HAVE_IP_HDRINCL_BSD_ORDER */ | |
506 | ||
507 | iph->ip_id = htons(iph->ip_id); | |
508 | } | |
509 | ||
510 | void | |
511 | sockopt_iphdrincl_swab_systoh (struct ip *iph) | |
512 | { | |
513 | #ifndef HAVE_IP_HDRINCL_BSD_ORDER | |
514 | iph->ip_len = ntohs(iph->ip_len); | |
515 | iph->ip_off = ntohs(iph->ip_off); | |
516 | #endif /* HAVE_IP_HDRINCL_BSD_ORDER */ | |
517 | ||
518 | iph->ip_id = ntohs(iph->ip_id); | |
519 | } | |
0df7c91f | 520 | |
cf279b3a TT |
521 | int |
522 | sockopt_tcp_rtt (int sock) | |
523 | { | |
524 | #ifdef TCP_INFO | |
525 | struct tcp_info ti; | |
526 | socklen_t len = sizeof(ti); | |
527 | ||
528 | if (getsockopt (sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0) | |
529 | return 0; | |
530 | ||
531 | return ti.tcpi_rtt / 1000; | |
532 | #else | |
533 | return 0; | |
534 | #endif | |
535 | } | |
536 | ||
0df7c91f PJ |
537 | int |
538 | sockopt_tcp_signature (int sock, union sockunion *su, const char *password) | |
539 | { | |
3453a712 PJ |
540 | #if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) |
541 | /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's | |
542 | * version of the Quagga patch (based on work by Rick Payne, and Bruce | |
543 | * Simpson) | |
544 | */ | |
545 | #define TCP_MD5_AUTH 13 | |
546 | #define TCP_MD5_AUTH_ADD 1 | |
547 | #define TCP_MD5_AUTH_DEL 2 | |
548 | struct tcp_rfc2385_cmd { | |
549 | u_int8_t command; /* Command - Add/Delete */ | |
550 | u_int32_t address; /* IPV4 address associated */ | |
551 | u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ | |
552 | void *key; /* MD5 Key */ | |
553 | } cmd; | |
554 | struct in_addr *addr = &su->sin.sin_addr; | |
555 | ||
556 | cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); | |
557 | cmd.address = addr->s_addr; | |
558 | cmd.keylen = (password != NULL ? strlen (password) : 0); | |
559 | cmd.key = password; | |
560 | ||
561 | return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd); | |
562 | ||
563 | #elif HAVE_DECL_TCP_MD5SIG | |
f5612dd3 | 564 | int ret; |
0df7c91f PJ |
565 | #ifndef GNU_LINUX |
566 | /* | |
567 | * XXX Need to do PF_KEY operation here to add/remove an SA entry, | |
568 | * and add/remove an SP entry for this peer's packet flows also. | |
569 | */ | |
570 | int md5sig = password && *password ? 1 : 0; | |
571 | #else | |
572 | int keylen = password ? strlen (password) : 0; | |
573 | struct tcp_md5sig md5sig; | |
574 | union sockunion *su2, *susock; | |
0df7c91f PJ |
575 | |
576 | /* Figure out whether the socket and the sockunion are the same family.. | |
577 | * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. | |
578 | */ | |
579 | if (!(susock = sockunion_getsockname (sock))) | |
580 | return -1; | |
581 | ||
582 | if (susock->sa.sa_family == su->sa.sa_family) | |
583 | su2 = su; | |
584 | else | |
585 | { | |
586 | /* oops.. */ | |
587 | su2 = susock; | |
588 | ||
589 | if (su2->sa.sa_family == AF_INET) | |
590 | { | |
591 | sockunion_free (susock); | |
2b35ae41 CC |
592 | return 0; |
593 | } | |
0df7c91f | 594 | |
e5862a2d | 595 | #ifdef HAVE_IPV6 |
0df7c91f PJ |
596 | /* If this does not work, then all users of this sockopt will need to |
597 | * differentiate between IPv4 and IPv6, and keep seperate sockets for | |
598 | * each. | |
599 | * | |
600 | * Sadly, it doesn't seem to work at present. It's unknown whether | |
601 | * this is a bug or not. | |
602 | */ | |
603 | if (su2->sa.sa_family == AF_INET6 | |
604 | && su->sa.sa_family == AF_INET) | |
605 | { | |
606 | su2->sin6.sin6_family = AF_INET6; | |
607 | /* V4Map the address */ | |
608 | memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); | |
609 | su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); | |
610 | memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4); | |
611 | } | |
e5862a2d | 612 | #endif |
0df7c91f PJ |
613 | } |
614 | ||
615 | memset (&md5sig, 0, sizeof (md5sig)); | |
616 | memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); | |
617 | md5sig.tcpm_keylen = keylen; | |
618 | if (keylen) | |
619 | memcpy (md5sig.tcpm_key, password, keylen); | |
f5612dd3 | 620 | sockunion_free (susock); |
0df7c91f | 621 | #endif /* GNU_LINUX */ |
2b35ae41 CC |
622 | if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0) |
623 | { | |
624 | /* ENOENT is harmless. It is returned when we clear a password for which | |
625 | one was not previously set. */ | |
626 | if (ENOENT == errno) | |
627 | ret = 0; | |
628 | else | |
629 | zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s", | |
630 | sock, safe_strerror(errno)); | |
631 | } | |
0df7c91f PJ |
632 | return ret; |
633 | #else /* HAVE_TCP_MD5SIG */ | |
634 | return -2; | |
3453a712 | 635 | #endif /* !HAVE_TCP_MD5SIG */ |
0df7c91f | 636 | } |