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