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