1 /* setsockopt functions
2 * Copyright (C) 1999 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
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
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.
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
25 #include "sockunion.h"
28 setsockopt_so_recvbuf (int sock
, int size
)
32 if ( (ret
= setsockopt (sock
, SOL_SOCKET
, SO_RCVBUF
, (char *)
33 &size
, sizeof (int))) < 0)
34 zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
35 sock
,size
,safe_strerror(errno
));
41 setsockopt_so_sendbuf (const int sock
, int size
)
43 int ret
= setsockopt (sock
, SOL_SOCKET
, SO_SNDBUF
,
44 (char *)&size
, sizeof (int));
47 zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
48 sock
, size
, safe_strerror (errno
));
54 getsockopt_so_sendbuf (const int sock
)
57 socklen_t optlen
= sizeof (optval
);
58 int ret
= getsockopt (sock
, SOL_SOCKET
, SO_SNDBUF
,
59 (char *)&optval
, &optlen
);
62 zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
63 sock
, errno
, safe_strerror (errno
));
70 getsockopt_cmsg_data (struct msghdr
*msgh
, int level
, int type
)
75 for (cmsg
= ZCMSG_FIRSTHDR(msgh
);
77 cmsg
= CMSG_NXTHDR(msgh
, cmsg
))
78 if (cmsg
->cmsg_level
== level
&& cmsg
->cmsg_type
)
79 return (ptr
= CMSG_DATA(cmsg
));
85 /* Set IPv6 packet info to the socket. */
87 setsockopt_ipv6_pktinfo (int sock
, int val
)
91 #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
92 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &val
, sizeof(val
));
94 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno
));
96 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &val
, sizeof(val
));
98 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno
));
99 #endif /* INIA_IPV6 */
103 /* Set multicast hops val to the socket. */
105 setsockopt_ipv6_checksum (int sock
, int val
)
110 ret
= setsockopt(sock
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
));
112 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_CHECKSUM
, &val
, sizeof(val
));
113 #endif /* GNU_LINUX */
115 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
119 /* Set multicast hops val to the socket. */
121 setsockopt_ipv6_multicast_hops (int sock
, int val
)
125 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
));
127 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
131 /* Set multicast hops val to the socket. */
133 setsockopt_ipv6_unicast_hops (int sock
, int val
)
137 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
, sizeof(val
));
139 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
144 setsockopt_ipv6_hoplimit (int sock
, int val
)
148 #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
149 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
, sizeof(val
));
151 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
153 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, &val
, sizeof(val
));
155 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
160 /* Set multicast loop zero to the socket. */
162 setsockopt_ipv6_multicast_loop (int sock
, int val
)
166 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
,
169 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
174 getsockopt_ipv6_ifindex (struct msghdr
*msgh
)
176 struct in6_pktinfo
*pktinfo
;
178 pktinfo
= getsockopt_cmsg_data (msgh
, IPPROTO_IPV6
, IPV6_PKTINFO
);
180 return pktinfo
->ipi6_ifindex
;
182 #endif /* HAVE_IPV6 */
186 * Process multicast socket options for IPv4 in an OS-dependent manner.
187 * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP.
189 * Many operating systems have a limit on the number of groups that
190 * can be joined per socket (where each group and local address
191 * counts). This impacts OSPF, which joins groups on each interface
192 * using a single socket. The limit is typically 20, derived from the
193 * original BSD multicast implementation. Some systems have
194 * mechanisms for increasing this limit.
196 * In many 4.4BSD-derived systems, multicast group operations are not
197 * allowed on interfaces that are not UP. Thus, a previous attempt to
198 * leave the group may have failed, leaving it still joined, and we
199 * drop/join quietly to recover. This may not be necessary, but aims to
200 * defend against unknown behavior in that we will still return an error
201 * if the second join fails. It is not clear how other systems
202 * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
203 * but this behavior should not be harmful if they behave the same way,
204 * allow leaves, or implicitly leave all groups joined to down interfaces.
207 setsockopt_multicast_ipv4(int sock
,
209 struct in_addr if_addr
/* required */,
210 unsigned int mcast_addr
,
211 unsigned int ifindex
/* optional: if non-zero, may be
212 used instead of if_addr */)
215 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
216 /* This is better because it uses ifindex directly */
217 struct ip_mreqn mreqn
;
222 case IP_MULTICAST_IF
:
223 case IP_ADD_MEMBERSHIP
:
224 case IP_DROP_MEMBERSHIP
:
225 memset (&mreqn
, 0, sizeof(mreqn
));
228 mreqn
.imr_multiaddr
.s_addr
= mcast_addr
;
231 mreqn
.imr_ifindex
= ifindex
;
233 mreqn
.imr_address
= if_addr
;
235 ret
= setsockopt(sock
, IPPROTO_IP
, optname
,
236 (void *)&mreqn
, sizeof(mreqn
));
237 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
239 /* see above: handle possible problem when interface comes back up */
240 char buf
[2][INET_ADDRSTRLEN
];
241 zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
242 "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
244 inet_ntop(AF_INET
, &if_addr
, buf
[0], sizeof(buf
[0])),
245 inet_ntop(AF_INET
, &mreqn
.imr_multiaddr
,
246 buf
[1], sizeof(buf
[1])), ifindex
);
247 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
248 (void *)&mreqn
, sizeof(mreqn
));
249 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
250 (void *)&mreqn
, sizeof(mreqn
));
256 /* Can out and give an understandable error */
262 /* Example defines for another OS, boilerplate off other code in this
263 function, AND handle optname as per other sections for consistency !! */
264 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
265 /* Add your favourite OS here! */
267 #else /* #if OS_TYPE */
268 /* standard BSD API */
274 #ifdef HAVE_BSD_STRUCT_IP_MREQ_HACK
276 m
.s_addr
= htonl(ifindex
);
283 case IP_MULTICAST_IF
:
284 return setsockopt (sock
, IPPROTO_IP
, optname
, (void *)&m
, sizeof(m
));
287 case IP_ADD_MEMBERSHIP
:
288 case IP_DROP_MEMBERSHIP
:
289 memset (&mreq
, 0, sizeof(mreq
));
290 mreq
.imr_multiaddr
.s_addr
= mcast_addr
;
291 mreq
.imr_interface
= m
;
293 ret
= setsockopt (sock
, IPPROTO_IP
, optname
, (void *)&mreq
, sizeof(mreq
));
294 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
296 /* see above: handle possible problem when interface comes back up */
297 char buf
[2][INET_ADDRSTRLEN
];
298 zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
299 "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
301 inet_ntop(AF_INET
, &if_addr
, buf
[0], sizeof(buf
[0])),
302 inet_ntop(AF_INET
, &mreq
.imr_multiaddr
,
303 buf
[1], sizeof(buf
[1])), ifindex
);
304 setsockopt (sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
305 (void *)&mreq
, sizeof(mreq
));
306 ret
= setsockopt (sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
307 (void *)&mreq
, sizeof(mreq
));
313 /* Can out and give an understandable error */
318 #endif /* #if OS_TYPE */
323 setsockopt_ipv4_ifindex (int sock
, int val
)
327 #if defined (IP_PKTINFO)
328 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof (val
))) < 0)
329 zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
330 sock
,val
,safe_strerror(errno
));
331 #elif defined (IP_RECVIF)
332 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_RECVIF
, &val
, sizeof (val
))) < 0)
333 zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
334 sock
,val
,safe_strerror(errno
));
336 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
337 #warning "Will not be able to receive link info."
338 #warning "Things might be seriously broken.."
339 /* XXX Does this ever happen? Should there be a zlog_warn message here? */
346 setsockopt_ipv4_tos(int sock
, int tos
)
350 ret
= setsockopt (sock
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof (tos
));
352 zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
353 sock
, tos
, safe_strerror(errno
));
359 setsockopt_ifindex (int af
, int sock
, int val
)
366 ret
= setsockopt_ipv4_ifindex (sock
, val
);
370 ret
= setsockopt_ipv6_pktinfo (sock
, val
);
374 zlog_warn ("setsockopt_ifindex: unknown address family %d", af
);
380 * Requires: msgh is not NULL and points to a valid struct msghdr, which
381 * may or may not have control data about the incoming interface.
383 * Returns the interface index (small integer >= 1) if it can be
384 * determined, or else 0.
387 getsockopt_ipv4_ifindex (struct msghdr
*msgh
)
389 /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
392 #if defined(IP_PKTINFO)
393 /* Linux pktinfo based ifindex retrieval */
394 struct in_pktinfo
*pktinfo
;
397 (struct in_pktinfo
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_PKTINFO
);
398 /* XXX Can pktinfo be NULL? Clean up post 0.98. */
399 ifindex
= pktinfo
->ipi_ifindex
;
401 #elif defined(IP_RECVIF)
403 /* retrieval based on IP_RECVIF */
406 /* BSD systems use a sockaddr_dl as the control message payload. */
407 struct sockaddr_dl
*sdl
;
409 /* SUNOS_5 uses an integer with the index. */
416 (struct sockaddr_dl
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
418 ifindex
= sdl
->sdl_index
;
423 * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
424 * enable it fails with errno=99, and the struct msghdr has
427 ifindex_p
= (uint_t
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
428 if (ifindex_p
!= NULL
)
429 ifindex
= *ifindex_p
;
436 * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
437 * XXX Decide if this is a core service, or if daemons have to cope.
438 * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
439 * daemons have to cope.
441 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
442 #warning "Some daemons may fail to operate correctly!"
445 #endif /* IP_PKTINFO */
450 /* return ifindex, 0 if none found */
452 getsockopt_ifindex (int af
, struct msghdr
*msgh
)
459 return (getsockopt_ipv4_ifindex (msgh
));
463 return (getsockopt_ipv6_ifindex (msgh
));
467 zlog_warn ("getsockopt_ifindex: unknown address family %d", af
);
468 return (ifindex
= 0);
472 /* swab iph between order system uses for IP_HDRINCL and host order */
474 sockopt_iphdrincl_swab_htosys (struct ip
*iph
)
476 /* BSD and derived take iph in network order, except for
479 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
480 iph
->ip_len
= htons(iph
->ip_len
);
481 iph
->ip_off
= htons(iph
->ip_off
);
482 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
484 iph
->ip_id
= htons(iph
->ip_id
);
488 sockopt_iphdrincl_swab_systoh (struct ip
*iph
)
490 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
491 iph
->ip_len
= ntohs(iph
->ip_len
);
492 iph
->ip_off
= ntohs(iph
->ip_off
);
493 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
495 iph
->ip_id
= ntohs(iph
->ip_id
);
499 sockopt_tcp_signature (int sock
, union sockunion
*su
, const char *password
)
501 #if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
502 /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
503 * version of the Quagga patch (based on work by Rick Payne, and Bruce
506 #define TCP_MD5_AUTH 13
507 #define TCP_MD5_AUTH_ADD 1
508 #define TCP_MD5_AUTH_DEL 2
509 struct tcp_rfc2385_cmd
{
510 u_int8_t command
; /* Command - Add/Delete */
511 u_int32_t address
; /* IPV4 address associated */
512 u_int8_t keylen
; /* MD5 Key len (do NOT assume 0 terminated ascii) */
513 void *key
; /* MD5 Key */
515 struct in_addr
*addr
= &su
->sin
.sin_addr
;
517 cmd
.command
= (password
!= NULL
? TCP_MD5_AUTH_ADD
: TCP_MD5_AUTH_DEL
);
518 cmd
.address
= addr
->s_addr
;
519 cmd
.keylen
= (password
!= NULL
? strlen (password
) : 0);
522 return setsockopt (sock
, IPPROTO_TCP
, TCP_MD5_AUTH
, &cmd
, sizeof cmd
);
524 #elif HAVE_DECL_TCP_MD5SIG
528 * XXX Need to do PF_KEY operation here to add/remove an SA entry,
529 * and add/remove an SP entry for this peer's packet flows also.
531 int md5sig
= password
&& *password
? 1 : 0;
533 int keylen
= password
? strlen (password
) : 0;
534 struct tcp_md5sig md5sig
;
535 union sockunion
*su2
, *susock
;
537 /* Figure out whether the socket and the sockunion are the same family..
538 * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
540 if (!(susock
= sockunion_getsockname (sock
)))
543 if (susock
->sa
.sa_family
== su
->sa
.sa_family
)
550 if (su2
->sa
.sa_family
== AF_INET
)
552 sockunion_free (susock
);
557 /* If this does not work, then all users of this sockopt will need to
558 * differentiate between IPv4 and IPv6, and keep seperate sockets for
561 * Sadly, it doesn't seem to work at present. It's unknown whether
562 * this is a bug or not.
564 if (su2
->sa
.sa_family
== AF_INET6
565 && su
->sa
.sa_family
== AF_INET
)
567 su2
->sin6
.sin6_family
= AF_INET6
;
568 /* V4Map the address */
569 memset (&su2
->sin6
.sin6_addr
, 0, sizeof (struct in6_addr
));
570 su2
->sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
571 memcpy (&su2
->sin6
.sin6_addr
.s6_addr32
[3], &su
->sin
.sin_addr
, 4);
576 memset (&md5sig
, 0, sizeof (md5sig
));
577 memcpy (&md5sig
.tcpm_addr
, su2
, sizeof (*su2
));
578 md5sig
.tcpm_keylen
= keylen
;
580 memcpy (md5sig
.tcpm_key
, password
, keylen
);
581 sockunion_free (susock
);
582 #endif /* GNU_LINUX */
583 if ((ret
= setsockopt (sock
, IPPROTO_TCP
, TCP_MD5SIG
, &md5sig
, sizeof md5sig
)) < 0)
585 /* ENOENT is harmless. It is returned when we clear a password for which
586 one was not previously set. */
590 zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
591 sock
, safe_strerror(errno
));
594 #else /* HAVE_TCP_MD5SIG */
596 #endif /* !HAVE_TCP_MD5SIG */