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