Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* setsockopt functions |
3 | | * Copyright (C) 1999 Kunihiro Ishiguro |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include "log.h" |
9 | | #include "sockopt.h" |
10 | | #include "sockunion.h" |
11 | | #include "lib_errors.h" |
12 | | |
13 | | #if (defined(__FreeBSD__) \ |
14 | | && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) \ |
15 | | || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) \ |
16 | | || (defined(__NetBSD__) && defined(__NetBSD_Version__) \ |
17 | | && __NetBSD_Version__ >= 106010000) \ |
18 | | || defined(__OpenBSD__) || defined(__APPLE__) \ |
19 | | || defined(__DragonFly__) || defined(__sun) |
20 | | #define HAVE_BSD_STRUCT_IP_MREQ_HACK |
21 | | #endif |
22 | | |
23 | | void setsockopt_so_recvbuf(int sock, int size) |
24 | 0 | { |
25 | 0 | int orig_req = size; |
26 | |
|
27 | 0 | while (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) |
28 | 0 | == -1) |
29 | 0 | size /= 2; |
30 | |
|
31 | 0 | if (size != orig_req) |
32 | 0 | flog_err(EC_LIB_SOCKET, |
33 | 0 | "%s: fd %d: SO_RCVBUF set to %d (requested %d)", |
34 | 0 | __func__, sock, size, orig_req); |
35 | 0 | } |
36 | | |
37 | | void setsockopt_so_sendbuf(const int sock, int size) |
38 | 0 | { |
39 | 0 | int orig_req = size; |
40 | |
|
41 | 0 | while (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) |
42 | 0 | == -1) |
43 | 0 | size /= 2; |
44 | |
|
45 | 0 | if (size != orig_req) |
46 | 0 | flog_err(EC_LIB_SOCKET, |
47 | 0 | "%s: fd %d: SO_SNDBUF set to %d (requested %d)", |
48 | 0 | __func__, sock, size, orig_req); |
49 | 0 | } |
50 | | |
51 | | int getsockopt_so_sendbuf(const int sock) |
52 | 0 | { |
53 | 0 | uint32_t optval; |
54 | 0 | socklen_t optlen = sizeof(optval); |
55 | 0 | int ret = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&optval, |
56 | 0 | &optlen); |
57 | 0 | if (ret < 0) { |
58 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
59 | 0 | "fd %d: can't getsockopt SO_SNDBUF: %d (%s)", sock, |
60 | 0 | errno, safe_strerror(errno)); |
61 | 0 | return ret; |
62 | 0 | } |
63 | 0 | return optval; |
64 | 0 | } |
65 | | |
66 | | int getsockopt_so_recvbuf(const int sock) |
67 | 0 | { |
68 | 0 | uint32_t optval; |
69 | 0 | socklen_t optlen = sizeof(optval); |
70 | 0 | int ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&optval, |
71 | 0 | &optlen); |
72 | 0 | if (ret < 0) { |
73 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
74 | 0 | "fd %d: can't getsockopt SO_RCVBUF: %d (%s)", sock, |
75 | 0 | errno, safe_strerror(errno)); |
76 | 0 | return ret; |
77 | 0 | } |
78 | 0 | return optval; |
79 | 0 | } |
80 | | |
81 | | static void *getsockopt_cmsg_data(struct msghdr *msgh, int level, int type) |
82 | 0 | { |
83 | 0 | struct cmsghdr *cmsg; |
84 | |
|
85 | 0 | for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; |
86 | 0 | cmsg = CMSG_NXTHDR(msgh, cmsg)) |
87 | 0 | if (cmsg->cmsg_level == level && cmsg->cmsg_type == type) |
88 | 0 | return CMSG_DATA(cmsg); |
89 | | |
90 | 0 | return NULL; |
91 | 0 | } |
92 | | |
93 | | /* Set IPv6 packet info to the socket. */ |
94 | | int setsockopt_ipv6_pktinfo(int sock, int val) |
95 | 0 | { |
96 | 0 | int ret; |
97 | |
|
98 | 0 | #ifdef IPV6_RECVPKTINFO /*2292bis-01*/ |
99 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, |
100 | 0 | sizeof(val)); |
101 | 0 | if (ret < 0) |
102 | 0 | flog_err(EC_LIB_SOCKET, |
103 | 0 | "can't setsockopt IPV6_RECVPKTINFO : %s", |
104 | 0 | safe_strerror(errno)); |
105 | | #else /*RFC2292*/ |
106 | | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); |
107 | | if (ret < 0) |
108 | | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_PKTINFO : %s", |
109 | | safe_strerror(errno)); |
110 | | #endif /* IANA_IPV6 */ |
111 | 0 | return ret; |
112 | 0 | } |
113 | | |
114 | | /* Set multicast hops val to the socket. */ |
115 | | int setsockopt_ipv6_multicast_hops(int sock, int val) |
116 | 0 | { |
117 | 0 | int ret; |
118 | |
|
119 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, |
120 | 0 | sizeof(val)); |
121 | 0 | if (ret < 0) |
122 | 0 | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_MULTICAST_HOPS"); |
123 | 0 | return ret; |
124 | 0 | } |
125 | | |
126 | | /* Set multicast hops val to the socket. */ |
127 | | int setsockopt_ipv6_unicast_hops(int sock, int val) |
128 | 0 | { |
129 | 0 | int ret; |
130 | |
|
131 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, |
132 | 0 | sizeof(val)); |
133 | 0 | if (ret < 0) |
134 | 0 | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_UNICAST_HOPS"); |
135 | 0 | return ret; |
136 | 0 | } |
137 | | |
138 | | int setsockopt_ipv6_hoplimit(int sock, int val) |
139 | 0 | { |
140 | 0 | int ret; |
141 | |
|
142 | 0 | #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ |
143 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, |
144 | 0 | sizeof(val)); |
145 | 0 | if (ret < 0) |
146 | 0 | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_RECVHOPLIMIT"); |
147 | | #else /*RFC2292*/ |
148 | | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); |
149 | | if (ret < 0) |
150 | | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_HOPLIMIT"); |
151 | | #endif |
152 | 0 | return ret; |
153 | 0 | } |
154 | | |
155 | | /* Set multicast loop zero to the socket. */ |
156 | | int setsockopt_ipv6_multicast_loop(int sock, int val) |
157 | 0 | { |
158 | 0 | int ret; |
159 | |
|
160 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, |
161 | 0 | sizeof(val)); |
162 | 0 | if (ret < 0) |
163 | 0 | flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_MULTICAST_LOOP"); |
164 | 0 | return ret; |
165 | 0 | } |
166 | | |
167 | | static int getsockopt_ipv6_ifindex(struct msghdr *msgh) |
168 | 0 | { |
169 | 0 | struct in6_pktinfo *pktinfo; |
170 | |
|
171 | 0 | pktinfo = getsockopt_cmsg_data(msgh, IPPROTO_IPV6, IPV6_PKTINFO); |
172 | |
|
173 | 0 | return pktinfo->ipi6_ifindex; |
174 | 0 | } |
175 | | |
176 | | int setsockopt_ipv6_tclass(int sock, int tclass) |
177 | 0 | { |
178 | 0 | int ret = 0; |
179 | |
|
180 | 0 | #ifdef IPV6_TCLASS /* RFC3542 */ |
181 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, |
182 | 0 | sizeof(tclass)); |
183 | 0 | if (ret < 0) |
184 | 0 | flog_err(EC_LIB_SOCKET, |
185 | 0 | "Can't set IPV6_TCLASS option for fd %d to %#x: %s", |
186 | 0 | sock, tclass, safe_strerror(errno)); |
187 | 0 | #endif |
188 | 0 | return ret; |
189 | 0 | } |
190 | | |
191 | | /* |
192 | | * Process multicast socket options for IPv4 in an OS-dependent manner. |
193 | | * Supported options are IP_{ADD,DROP}_MEMBERSHIP. |
194 | | * |
195 | | * Many operating systems have a limit on the number of groups that |
196 | | * can be joined per socket (where each group and local address |
197 | | * counts). This impacts OSPF, which joins groups on each interface |
198 | | * using a single socket. The limit is typically 20, derived from the |
199 | | * original BSD multicast implementation. Some systems have |
200 | | * mechanisms for increasing this limit. |
201 | | * |
202 | | * In many 4.4BSD-derived systems, multicast group operations are not |
203 | | * allowed on interfaces that are not UP. Thus, a previous attempt to |
204 | | * leave the group may have failed, leaving it still joined, and we |
205 | | * drop/join quietly to recover. This may not be necessary, but aims to |
206 | | * defend against unknown behavior in that we will still return an error |
207 | | * if the second join fails. It is not clear how other systems |
208 | | * (e.g. Linux, Solaris) behave when leaving groups on down interfaces, |
209 | | * but this behavior should not be harmful if they behave the same way, |
210 | | * allow leaves, or implicitly leave all groups joined to down interfaces. |
211 | | */ |
212 | | int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr, |
213 | | unsigned int mcast_addr, ifindex_t ifindex) |
214 | 0 | { |
215 | 0 | #ifdef HAVE_RFC3678 |
216 | 0 | struct group_req gr; |
217 | 0 | struct sockaddr_in *si; |
218 | 0 | int ret; |
219 | 0 | memset(&gr, 0, sizeof(gr)); |
220 | 0 | si = (struct sockaddr_in *)&gr.gr_group; |
221 | 0 | gr.gr_interface = ifindex; |
222 | 0 | si->sin_family = AF_INET; |
223 | | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN |
224 | | si->sin_len = sizeof(struct sockaddr_in); |
225 | | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ |
226 | 0 | si->sin_addr.s_addr = mcast_addr; |
227 | 0 | ret = setsockopt(sock, IPPROTO_IP, |
228 | 0 | (optname == IP_ADD_MEMBERSHIP) ? MCAST_JOIN_GROUP |
229 | 0 | : MCAST_LEAVE_GROUP, |
230 | 0 | (void *)&gr, sizeof(gr)); |
231 | 0 | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) |
232 | 0 | && (errno == EADDRINUSE)) { |
233 | 0 | setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, |
234 | 0 | sizeof(gr)); |
235 | 0 | ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, |
236 | 0 | (void *)&gr, sizeof(gr)); |
237 | 0 | } |
238 | 0 | return ret; |
239 | |
|
240 | | #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__) |
241 | | struct ip_mreqn mreqn; |
242 | | int ret; |
243 | | |
244 | | assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); |
245 | | memset(&mreqn, 0, sizeof(mreqn)); |
246 | | |
247 | | mreqn.imr_multiaddr.s_addr = mcast_addr; |
248 | | mreqn.imr_ifindex = ifindex; |
249 | | |
250 | | ret = setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, |
251 | | sizeof(mreqn)); |
252 | | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) |
253 | | && (errno == EADDRINUSE)) { |
254 | | /* see above: handle possible problem when interface comes back |
255 | | * up */ |
256 | | zlog_info( |
257 | | "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %pI4, ifindex %u)", |
258 | | sock, &mreqn.imr_multiaddr, ifindex); |
259 | | setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void *)&mreqn, |
260 | | sizeof(mreqn)); |
261 | | ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
262 | | (void *)&mreqn, sizeof(mreqn)); |
263 | | } |
264 | | return ret; |
265 | | |
266 | | /* Example defines for another OS, boilerplate off other code in this |
267 | | function, AND handle optname as per other sections for consistency !! */ |
268 | | /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ |
269 | | /* Add your favourite OS here! */ |
270 | | |
271 | | #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ |
272 | | /* standard BSD API */ |
273 | | |
274 | | struct ip_mreq mreq; |
275 | | int ret; |
276 | | |
277 | | assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); |
278 | | |
279 | | |
280 | | memset(&mreq, 0, sizeof(mreq)); |
281 | | mreq.imr_multiaddr.s_addr = mcast_addr; |
282 | | #if !defined __OpenBSD__ |
283 | | mreq.imr_interface.s_addr = htonl(ifindex); |
284 | | #else |
285 | | mreq.imr_interface.s_addr = if_addr.s_addr; |
286 | | #endif |
287 | | |
288 | | ret = setsockopt(sock, IPPROTO_IP, optname, (void *)&mreq, |
289 | | sizeof(mreq)); |
290 | | if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) |
291 | | && (errno == EADDRINUSE)) { |
292 | | /* see above: handle possible problem when interface comes back |
293 | | * up */ |
294 | | zlog_info( |
295 | | "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %pI4, ifindex %u)", |
296 | | sock, &mreq.imr_multiaddr, ifindex); |
297 | | setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void *)&mreq, |
298 | | sizeof(mreq)); |
299 | | ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
300 | | (void *)&mreq, sizeof(mreq)); |
301 | | } |
302 | | return ret; |
303 | | |
304 | | #else |
305 | | #error "Unsupported multicast API" |
306 | | #endif /* #if OS_TYPE */ |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | * Set IP_MULTICAST_IF socket option in an OS-dependent manner. |
311 | | */ |
312 | | int setsockopt_ipv4_multicast_if(int sock, struct in_addr if_addr, |
313 | | ifindex_t ifindex) |
314 | 0 | { |
315 | |
|
316 | 0 | #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX |
317 | 0 | struct ip_mreqn mreqn; |
318 | 0 | memset(&mreqn, 0, sizeof(mreqn)); |
319 | |
|
320 | 0 | mreqn.imr_ifindex = ifindex; |
321 | 0 | return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, |
322 | 0 | sizeof(mreqn)); |
323 | | |
324 | | /* Example defines for another OS, boilerplate off other code in this |
325 | | function */ |
326 | | /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ |
327 | | /* Add your favourite OS here! */ |
328 | | #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) |
329 | | struct in_addr m; |
330 | | |
331 | | #if !defined __OpenBSD__ |
332 | | m.s_addr = htonl(ifindex); |
333 | | #else |
334 | | m.s_addr = if_addr.s_addr; |
335 | | #endif |
336 | | |
337 | | return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, |
338 | | sizeof(m)); |
339 | | #else |
340 | | #error "Unsupported multicast API" |
341 | | #endif |
342 | 0 | } |
343 | | |
344 | | int setsockopt_ipv4_multicast_loop(int sock, uint8_t val) |
345 | 0 | { |
346 | 0 | int ret; |
347 | |
|
348 | 0 | ret = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, |
349 | 0 | sizeof(val)); |
350 | 0 | if (ret < 0) |
351 | 0 | flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_LOOP"); |
352 | |
|
353 | 0 | return ret; |
354 | 0 | } |
355 | | |
356 | | static int setsockopt_ipv4_ifindex(int sock, ifindex_t val) |
357 | 0 | { |
358 | 0 | int ret; |
359 | |
|
360 | 0 | #if defined(IP_PKTINFO) |
361 | 0 | ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)); |
362 | 0 | if (ret < 0) |
363 | 0 | flog_err(EC_LIB_SOCKET, |
364 | 0 | "Can't set IP_PKTINFO option for fd %d to %d: %s", |
365 | 0 | sock, val, safe_strerror(errno)); |
366 | | #elif defined(IP_RECVIF) |
367 | | ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val)); |
368 | | if (ret < 0) |
369 | | flog_err(EC_LIB_SOCKET, |
370 | | "Can't set IP_RECVIF option for fd %d to %d: %s", sock, |
371 | | val, safe_strerror(errno)); |
372 | | #else |
373 | | #warning "Neither IP_PKTINFO nor IP_RECVIF is available." |
374 | | #warning "Will not be able to receive link info." |
375 | | #warning "Things might be seriously broken.." |
376 | | /* XXX Does this ever happen? Should there be a zlog_warn message here? |
377 | | */ |
378 | | ret = -1; |
379 | | #endif |
380 | 0 | return ret; |
381 | 0 | } |
382 | | |
383 | | int setsockopt_ipv4_tos(int sock, int tos) |
384 | 0 | { |
385 | 0 | int ret; |
386 | |
|
387 | 0 | ret = setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); |
388 | 0 | if (ret < 0) |
389 | 0 | flog_err(EC_LIB_SOCKET, |
390 | 0 | "Can't set IP_TOS option for fd %d to %#x: %s", sock, |
391 | 0 | tos, safe_strerror(errno)); |
392 | 0 | return ret; |
393 | 0 | } |
394 | | |
395 | | |
396 | | int setsockopt_ifindex(int af, int sock, ifindex_t val) |
397 | 0 | { |
398 | 0 | int ret = -1; |
399 | |
|
400 | 0 | switch (af) { |
401 | 0 | case AF_INET: |
402 | 0 | ret = setsockopt_ipv4_ifindex(sock, val); |
403 | 0 | break; |
404 | 0 | case AF_INET6: |
405 | 0 | ret = setsockopt_ipv6_pktinfo(sock, val); |
406 | 0 | break; |
407 | 0 | default: |
408 | 0 | flog_err(EC_LIB_DEVELOPMENT, |
409 | 0 | "setsockopt_ifindex: unknown address family %d", af); |
410 | 0 | } |
411 | 0 | return ret; |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * Requires: msgh is not NULL and points to a valid struct msghdr, which |
416 | | * may or may not have control data about the incoming interface. |
417 | | * |
418 | | * Returns the interface index (small integer >= 1) if it can be |
419 | | * determined, or else 0. |
420 | | */ |
421 | | static ifindex_t getsockopt_ipv4_ifindex(struct msghdr *msgh) |
422 | 0 | { |
423 | 0 | ifindex_t ifindex; |
424 | |
|
425 | 0 | #if defined(IP_PKTINFO) |
426 | | /* Linux pktinfo based ifindex retrieval */ |
427 | 0 | struct in_pktinfo *pktinfo; |
428 | |
|
429 | 0 | pktinfo = (struct in_pktinfo *)getsockopt_cmsg_data(msgh, IPPROTO_IP, |
430 | 0 | IP_PKTINFO); |
431 | | |
432 | | /* getsockopt_ifindex() will forward this, being 0 "not found" */ |
433 | 0 | if (pktinfo == NULL) |
434 | 0 | return 0; |
435 | | |
436 | 0 | ifindex = pktinfo->ipi_ifindex; |
437 | |
|
438 | | #elif defined(IP_RECVIF) |
439 | | |
440 | | /* retrieval based on IP_RECVIF */ |
441 | | |
442 | | /* BSD systems use a sockaddr_dl as the control message payload. */ |
443 | | struct sockaddr_dl *sdl; |
444 | | |
445 | | /* BSD */ |
446 | | sdl = (struct sockaddr_dl *)getsockopt_cmsg_data(msgh, IPPROTO_IP, |
447 | | IP_RECVIF); |
448 | | if (sdl != NULL) |
449 | | ifindex = sdl->sdl_index; |
450 | | else |
451 | | ifindex = 0; |
452 | | |
453 | | #else |
454 | | /* |
455 | | * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time. |
456 | | * XXX Decide if this is a core service, or if daemons have to cope. |
457 | | * Since Solaris 8 and OpenBSD seem not to provide it, it seems that |
458 | | * daemons have to cope. |
459 | | */ |
460 | | #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined." |
461 | | #warning "Some daemons may fail to operate correctly!" |
462 | | ifindex = 0; |
463 | | |
464 | | #endif /* IP_PKTINFO */ |
465 | |
|
466 | 0 | return ifindex; |
467 | 0 | } |
468 | | |
469 | | /* return ifindex, 0 if none found */ |
470 | | ifindex_t getsockopt_ifindex(int af, struct msghdr *msgh) |
471 | 0 | { |
472 | 0 | switch (af) { |
473 | 0 | case AF_INET: |
474 | 0 | return (getsockopt_ipv4_ifindex(msgh)); |
475 | 0 | case AF_INET6: |
476 | 0 | return (getsockopt_ipv6_ifindex(msgh)); |
477 | 0 | default: |
478 | 0 | flog_err(EC_LIB_DEVELOPMENT, |
479 | 0 | "getsockopt_ifindex: unknown address family %d", af); |
480 | 0 | return 0; |
481 | 0 | } |
482 | 0 | } |
483 | | |
484 | | /* swab iph between order system uses for IP_HDRINCL and host order */ |
485 | | void sockopt_iphdrincl_swab_htosys(struct ip *iph) |
486 | 0 | { |
487 | | /* BSD and derived take iph in network order, except for |
488 | | * ip_len and ip_off |
489 | | */ |
490 | 0 | #ifndef HAVE_IP_HDRINCL_BSD_ORDER |
491 | 0 | iph->ip_len = htons(iph->ip_len); |
492 | 0 | iph->ip_off = htons(iph->ip_off); |
493 | 0 | #endif /* HAVE_IP_HDRINCL_BSD_ORDER */ |
494 | |
|
495 | 0 | iph->ip_id = htons(iph->ip_id); |
496 | 0 | } |
497 | | |
498 | | void sockopt_iphdrincl_swab_systoh(struct ip *iph) |
499 | 3.40k | { |
500 | 3.40k | #ifndef HAVE_IP_HDRINCL_BSD_ORDER |
501 | 3.40k | iph->ip_len = ntohs(iph->ip_len); |
502 | 3.40k | iph->ip_off = ntohs(iph->ip_off); |
503 | 3.40k | #endif /* HAVE_IP_HDRINCL_BSD_ORDER */ |
504 | | |
505 | 3.40k | iph->ip_id = ntohs(iph->ip_id); |
506 | 3.40k | } |
507 | | |
508 | | int sockopt_tcp_rtt(int sock) |
509 | 1 | { |
510 | 1 | #ifdef TCP_INFO |
511 | 1 | struct tcp_info ti; |
512 | 1 | socklen_t len = sizeof(ti); |
513 | | |
514 | 1 | if (getsockopt(sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0) |
515 | 1 | return 0; |
516 | | |
517 | 0 | return ti.tcpi_rtt / 1000; |
518 | | #else |
519 | | return 0; |
520 | | #endif |
521 | 1 | } |
522 | | |
523 | | int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, |
524 | | const char *password) |
525 | 0 | { |
526 | | #ifndef HAVE_DECL_TCP_MD5SIG |
527 | | /* |
528 | | * We have been asked to enable MD5 auth for an address, but our |
529 | | * platform doesn't support that |
530 | | */ |
531 | | return -2; |
532 | | #endif |
533 | |
|
534 | | #ifndef TCP_MD5SIG_EXT |
535 | | /* |
536 | | * We have been asked to enable MD5 auth for a prefix, but our platform |
537 | | * doesn't support that |
538 | | */ |
539 | | if (prefixlen > 0) |
540 | | return -2; |
541 | | #endif |
542 | |
|
543 | 0 | #if HAVE_DECL_TCP_MD5SIG |
544 | 0 | int ret; |
545 | |
|
546 | 0 | int optname = TCP_MD5SIG; |
547 | | #ifndef GNU_LINUX |
548 | | /* |
549 | | * XXX Need to do PF_KEY operation here to add/remove an SA entry, |
550 | | * and add/remove an SP entry for this peer's packet flows also. |
551 | | */ |
552 | | int md5sig = password && *password ? 1 : 0; |
553 | | #else |
554 | 0 | int keylen = password ? strlen(password) : 0; |
555 | 0 | struct tcp_md5sig md5sig; |
556 | 0 | union sockunion *su2, *susock; |
557 | | |
558 | | /* Figure out whether the socket and the sockunion are the same family.. |
559 | | * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. |
560 | | */ |
561 | 0 | if (!(susock = sockunion_getsockname(sock))) |
562 | 0 | return -1; |
563 | | |
564 | 0 | if (susock->sa.sa_family == su->sa.sa_family) |
565 | 0 | su2 = su; |
566 | 0 | else { |
567 | | /* oops.. */ |
568 | 0 | su2 = susock; |
569 | |
|
570 | 0 | if (su2->sa.sa_family == AF_INET) { |
571 | 0 | sockunion_free(susock); |
572 | 0 | return 0; |
573 | 0 | } |
574 | | |
575 | | /* If this does not work, then all users of this sockopt will |
576 | | * need to |
577 | | * differentiate between IPv4 and IPv6, and keep separate |
578 | | * sockets for |
579 | | * each. |
580 | | * |
581 | | * Sadly, it doesn't seem to work at present. It's unknown |
582 | | * whether |
583 | | * this is a bug or not. |
584 | | */ |
585 | 0 | if (su2->sa.sa_family == AF_INET6 |
586 | 0 | && su->sa.sa_family == AF_INET) { |
587 | 0 | su2->sin6.sin6_family = AF_INET6; |
588 | | /* V4Map the address */ |
589 | 0 | memset(&su2->sin6.sin6_addr, 0, |
590 | 0 | sizeof(struct in6_addr)); |
591 | 0 | su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); |
592 | 0 | memcpy(&su2->sin6.sin6_addr.s6_addr32[3], |
593 | 0 | &su->sin.sin_addr, 4); |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | 0 | memset(&md5sig, 0, sizeof(md5sig)); |
598 | 0 | memcpy(&md5sig.tcpm_addr, su2, sizeof(*su2)); |
599 | |
|
600 | 0 | md5sig.tcpm_keylen = keylen; |
601 | 0 | if (keylen) |
602 | 0 | memcpy(md5sig.tcpm_key, password, keylen); |
603 | 0 | sockunion_free(susock); |
604 | | |
605 | | /* |
606 | | * Handle support for MD5 signatures on prefixes, if available and |
607 | | * requested. Technically the #ifdef check below is not needed because |
608 | | * if prefixlen > 0 and we don't have support for this feature we would |
609 | | * have already returned by now, but leaving it there to be explicit. |
610 | | */ |
611 | 0 | #ifdef TCP_MD5SIG_EXT |
612 | 0 | if (prefixlen > 0) { |
613 | 0 | md5sig.tcpm_prefixlen = prefixlen; |
614 | 0 | md5sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX; |
615 | 0 | optname = TCP_MD5SIG_EXT; |
616 | 0 | } |
617 | 0 | #endif /* TCP_MD5SIG_EXT */ |
618 | |
|
619 | 0 | #endif /* GNU_LINUX */ |
620 | |
|
621 | 0 | ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, sizeof(md5sig)); |
622 | 0 | if (ret < 0) { |
623 | 0 | if (ENOENT == errno) |
624 | 0 | ret = 0; |
625 | 0 | else |
626 | 0 | flog_err_sys( |
627 | 0 | EC_LIB_SYSTEM_CALL, |
628 | 0 | "sockopt_tcp_signature: setsockopt(%d): %s", |
629 | 0 | sock, safe_strerror(errno)); |
630 | 0 | } |
631 | 0 | return ret; |
632 | 0 | #endif /* HAVE_TCP_MD5SIG */ |
633 | | |
634 | | /* |
635 | | * Making compiler happy. If we get to this point we probably |
636 | | * have done something really really wrong. |
637 | | */ |
638 | 0 | return -2; |
639 | 0 | } |
640 | | |
641 | | int sockopt_tcp_signature(int sock, union sockunion *su, const char *password) |
642 | 0 | { |
643 | 0 | return sockopt_tcp_signature_ext(sock, su, 0, password); |
644 | 0 | } |
645 | | |
646 | | /* set TCP mss value to socket */ |
647 | | int sockopt_tcp_mss_set(int sock, int tcp_maxseg) |
648 | 0 | { |
649 | 0 | int ret = 0; |
650 | 0 | socklen_t tcp_maxseg_len = sizeof(tcp_maxseg); |
651 | |
|
652 | 0 | ret = setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, |
653 | 0 | tcp_maxseg_len); |
654 | 0 | if (ret != 0) { |
655 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
656 | 0 | "%s failed: setsockopt(%d): %s", __func__, sock, |
657 | 0 | safe_strerror(errno)); |
658 | 0 | } |
659 | |
|
660 | 0 | return ret; |
661 | 0 | } |
662 | | |
663 | | /* get TCP mss value synced by socket */ |
664 | | int sockopt_tcp_mss_get(int sock) |
665 | 0 | { |
666 | 0 | int ret = 0; |
667 | 0 | int tcp_maxseg = 0; |
668 | 0 | socklen_t tcp_maxseg_len = sizeof(tcp_maxseg); |
669 | |
|
670 | 0 | ret = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, |
671 | 0 | &tcp_maxseg_len); |
672 | 0 | if (ret != 0) { |
673 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
674 | 0 | "%s failed: getsockopt(%d): %s", __func__, sock, |
675 | 0 | safe_strerror(errno)); |
676 | 0 | return 0; |
677 | 0 | } |
678 | | |
679 | 0 | return tcp_maxseg; |
680 | 0 | } |
681 | | |
682 | | int setsockopt_tcp_keepalive(int sock, uint16_t keepalive_idle, |
683 | | uint16_t keepalive_intvl, |
684 | | uint16_t keepalive_probes) |
685 | 0 | { |
686 | 0 | int val = 1; |
687 | |
|
688 | 0 | if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) < 0) { |
689 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
690 | 0 | "%s failed: setsockopt SO_KEEPALIVE (%d): %s", |
691 | 0 | __func__, sock, safe_strerror(errno)); |
692 | 0 | return -1; |
693 | 0 | } |
694 | | |
695 | | #if defined __OpenBSD__ |
696 | | return 0; |
697 | | #else |
698 | | /* Send first probe after keepalive_idle seconds */ |
699 | 0 | val = keepalive_idle; |
700 | 0 | if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < |
701 | 0 | 0) { |
702 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
703 | 0 | "%s failed: setsockopt TCP_KEEPIDLE (%d): %s", |
704 | 0 | __func__, sock, safe_strerror(errno)); |
705 | 0 | return -1; |
706 | 0 | } |
707 | | |
708 | | /* Set interval between two probes */ |
709 | 0 | val = keepalive_intvl; |
710 | 0 | if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < |
711 | 0 | 0) { |
712 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
713 | 0 | "%s failed: setsockopt TCP_KEEPINTVL (%d): %s", |
714 | 0 | __func__, sock, safe_strerror(errno)); |
715 | 0 | return -1; |
716 | 0 | } |
717 | | |
718 | | /* Set maximum probes */ |
719 | 0 | val = keepalive_probes; |
720 | 0 | if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { |
721 | 0 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
722 | 0 | "%s failed: setsockopt TCP_KEEPCNT (%d): %s", |
723 | 0 | __func__, sock, safe_strerror(errno)); |
724 | 0 | return -1; |
725 | 0 | } |
726 | | |
727 | 0 | return 0; |
728 | 0 | #endif |
729 | 0 | } |