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
27 setsockopt_so_recvbuf (int sock
, int size
)
31 if ( (ret
= setsockopt (sock
, SOL_SOCKET
, SO_RCVBUF
, (char *)
32 &size
, sizeof (int))) < 0)
33 zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
34 sock
,size
,safe_strerror(errno
));
40 setsockopt_so_sendbuf (const int sock
, int size
)
42 int ret
= setsockopt (sock
, SOL_SOCKET
, SO_SNDBUF
,
43 (char *)&size
, sizeof (int));
46 zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
47 sock
, size
, safe_strerror (errno
));
53 getsockopt_so_sendbuf (const int sock
)
56 socklen_t optlen
= sizeof (optval
);
57 int ret
= getsockopt (sock
, SOL_SOCKET
, SO_SNDBUF
,
58 (char *)&optval
, &optlen
);
61 zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
62 sock
, errno
, safe_strerror (errno
));
69 getsockopt_cmsg_data (struct msghdr
*msgh
, int level
, int type
)
74 for (cmsg
= ZCMSG_FIRSTHDR(msgh
);
76 cmsg
= CMSG_NXTHDR(msgh
, cmsg
))
77 if (cmsg
->cmsg_level
== level
&& cmsg
->cmsg_type
)
78 return (ptr
= CMSG_DATA(cmsg
));
84 /* Set IPv6 packet info to the socket. */
86 setsockopt_ipv6_pktinfo (int sock
, int val
)
90 #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
91 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &val
, sizeof(val
));
93 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno
));
95 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &val
, sizeof(val
));
97 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno
));
98 #endif /* INIA_IPV6 */
102 /* Set multicast hops val to the socket. */
104 setsockopt_ipv6_checksum (int sock
, int val
)
109 ret
= setsockopt(sock
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
));
111 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_CHECKSUM
, &val
, sizeof(val
));
112 #endif /* GNU_LINUX */
114 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
118 /* Set multicast hops val to the socket. */
120 setsockopt_ipv6_multicast_hops (int sock
, int val
)
124 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
));
126 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
130 /* Set multicast hops val to the socket. */
132 setsockopt_ipv6_unicast_hops (int sock
, int val
)
136 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
, sizeof(val
));
138 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
143 setsockopt_ipv6_hoplimit (int sock
, int val
)
147 #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
148 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
, sizeof(val
));
150 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
152 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, &val
, sizeof(val
));
154 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
159 /* Set multicast loop zero to the socket. */
161 setsockopt_ipv6_multicast_loop (int sock
, int val
)
165 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
,
168 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
173 getsockopt_ipv6_ifindex (struct msghdr
*msgh
)
175 struct in6_pktinfo
*pktinfo
;
177 pktinfo
= getsockopt_cmsg_data (msgh
, IPPROTO_IPV6
, IPV6_PKTINFO
);
179 return pktinfo
->ipi6_ifindex
;
181 #endif /* HAVE_IPV6 */
185 * Process multicast socket options for IPv4 in an OS-dependent manner.
186 * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP.
188 * Many operating systems have a limit on the number of groups that
189 * can be joined per socket (where each group and local address
190 * counts). This impacts OSPF, which joins groups on each interface
191 * using a single socket. The limit is typically 20, derived from the
192 * original BSD multicast implementation. Some systems have
193 * mechanisms for increasing this limit.
195 * In many 4.4BSD-derived systems, multicast group operations are not
196 * allowed on interfaces that are not UP. Thus, a previous attempt to
197 * leave the group may have failed, leaving it still joined, and we
198 * drop/join quietly to recover. This may not be necessary, but aims to
199 * defend against unknown behavior in that we will still return an error
200 * if the second join fails. It is not clear how other systems
201 * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
202 * but this behavior should not be harmful if they behave the same way,
203 * allow leaves, or implicitly leave all groups joined to down interfaces.
206 setsockopt_multicast_ipv4(int sock
,
208 struct in_addr if_addr
/* required */,
209 unsigned int mcast_addr
,
210 unsigned int ifindex
/* optional: if non-zero, may be
211 used instead of if_addr */)
214 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
215 /* This is better because it uses ifindex directly */
216 struct ip_mreqn mreqn
;
221 case IP_MULTICAST_IF
:
222 case IP_ADD_MEMBERSHIP
:
223 case IP_DROP_MEMBERSHIP
:
224 memset (&mreqn
, 0, sizeof(mreqn
));
227 mreqn
.imr_multiaddr
.s_addr
= mcast_addr
;
230 mreqn
.imr_ifindex
= ifindex
;
232 mreqn
.imr_address
= if_addr
;
234 ret
= setsockopt(sock
, IPPROTO_IP
, optname
,
235 (void *)&mreqn
, sizeof(mreqn
));
236 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
238 /* see above: handle possible problem when interface comes back up */
239 char buf
[2][INET_ADDRSTRLEN
];
240 zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
241 "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
243 inet_ntop(AF_INET
, &if_addr
, buf
[0], sizeof(buf
[0])),
244 inet_ntop(AF_INET
, &mreqn
.imr_multiaddr
,
245 buf
[1], sizeof(buf
[1])), ifindex
);
246 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
247 (void *)&mreqn
, sizeof(mreqn
));
248 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
249 (void *)&mreqn
, sizeof(mreqn
));
255 /* Can out and give an understandable error */
261 /* Example defines for another OS, boilerplate off other code in this
262 function, AND handle optname as per other sections for consistency !! */
263 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
264 /* Add your favourite OS here! */
266 #else /* #if OS_TYPE */
267 /* standard BSD API */
273 #ifdef HAVE_BSD_STRUCT_IP_MREQ_HACK
275 m
.s_addr
= htonl(ifindex
);
282 case IP_MULTICAST_IF
:
283 return setsockopt (sock
, IPPROTO_IP
, optname
, (void *)&m
, sizeof(m
));
286 case IP_ADD_MEMBERSHIP
:
287 case IP_DROP_MEMBERSHIP
:
288 memset (&mreq
, 0, sizeof(mreq
));
289 mreq
.imr_multiaddr
.s_addr
= mcast_addr
;
290 mreq
.imr_interface
= m
;
292 ret
= setsockopt (sock
, IPPROTO_IP
, optname
, (void *)&mreq
, sizeof(mreq
));
293 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
295 /* see above: handle possible problem when interface comes back up */
296 char buf
[2][INET_ADDRSTRLEN
];
297 zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
298 "re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
300 inet_ntop(AF_INET
, &if_addr
, buf
[0], sizeof(buf
[0])),
301 inet_ntop(AF_INET
, &mreq
.imr_multiaddr
,
302 buf
[1], sizeof(buf
[1])), ifindex
);
303 setsockopt (sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
304 (void *)&mreq
, sizeof(mreq
));
305 ret
= setsockopt (sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
306 (void *)&mreq
, sizeof(mreq
));
312 /* Can out and give an understandable error */
317 #endif /* #if OS_TYPE */
322 setsockopt_ipv4_ifindex (int sock
, int val
)
326 #if defined (IP_PKTINFO)
327 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof (val
))) < 0)
328 zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
329 sock
,val
,safe_strerror(errno
));
330 #elif defined (IP_RECVIF)
331 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_RECVIF
, &val
, sizeof (val
))) < 0)
332 zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
333 sock
,val
,safe_strerror(errno
));
335 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
336 #warning "Will not be able to receive link info."
337 #warning "Things might be seriously broken.."
338 /* XXX Does this ever happen? Should there be a zlog_warn message here? */
345 setsockopt_ifindex (int af
, int sock
, int val
)
352 ret
= setsockopt_ipv4_ifindex (sock
, val
);
356 ret
= setsockopt_ipv6_pktinfo (sock
, val
);
360 zlog_warn ("setsockopt_ifindex: unknown address family %d", af
);
366 * Requires: msgh is not NULL and points to a valid struct msghdr, which
367 * may or may not have control data about the incoming interface.
369 * Returns the interface index (small integer >= 1) if it can be
370 * determined, or else 0.
373 getsockopt_ipv4_ifindex (struct msghdr
*msgh
)
375 /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
378 #if defined(IP_PKTINFO)
379 /* Linux pktinfo based ifindex retrieval */
380 struct in_pktinfo
*pktinfo
;
383 (struct in_pktinfo
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_PKTINFO
);
384 /* XXX Can pktinfo be NULL? Clean up post 0.98. */
385 ifindex
= pktinfo
->ipi_ifindex
;
387 #elif defined(IP_RECVIF)
389 /* retrieval based on IP_RECVIF */
392 /* BSD systems use a sockaddr_dl as the control message payload. */
393 struct sockaddr_dl
*sdl
;
395 /* SUNOS_5 uses an integer with the index. */
402 (struct sockaddr_dl
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
404 ifindex
= sdl
->sdl_index
;
409 * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
410 * enable it fails with errno=99, and the struct msghdr has
413 ifindex_p
= (uint_t
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
414 if (ifindex_p
!= NULL
)
415 ifindex
= *ifindex_p
;
422 * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
423 * XXX Decide if this is a core service, or if daemons have to cope.
424 * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
425 * daemons have to cope.
427 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
428 #warning "Some daemons may fail to operate correctly!"
431 #endif /* IP_PKTINFO */
436 /* return ifindex, 0 if none found */
438 getsockopt_ifindex (int af
, struct msghdr
*msgh
)
445 return (getsockopt_ipv4_ifindex (msgh
));
449 return (getsockopt_ipv6_ifindex (msgh
));
453 zlog_warn ("getsockopt_ifindex: unknown address family %d", af
);
454 return (ifindex
= 0);
458 /* swab iph between order system uses for IP_HDRINCL and host order */
460 sockopt_iphdrincl_swab_htosys (struct ip
*iph
)
462 /* BSD and derived take iph in network order, except for
465 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
466 iph
->ip_len
= htons(iph
->ip_len
);
467 iph
->ip_off
= htons(iph
->ip_off
);
468 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
470 iph
->ip_id
= htons(iph
->ip_id
);
474 sockopt_iphdrincl_swab_systoh (struct ip
*iph
)
476 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
477 iph
->ip_len
= ntohs(iph
->ip_len
);
478 iph
->ip_off
= ntohs(iph
->ip_off
);
479 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
481 iph
->ip_id
= ntohs(iph
->ip_id
);