blob: 8c518f824a486adf4cc7e74710670f992e04a7bd [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* 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>
23#include "log.h"
paule6822762004-08-19 04:13:29 +000024#include "sockopt.h"
paul718e3742002-12-13 20:15:29 +000025
paul0b3acf42004-09-17 08:39:08 +000026int
27setsockopt_so_recvbuf (int sock, int size)
28{
29 int ret;
30
paulb89e60c2004-09-21 15:43:13 +000031 if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
ajsff29bb32004-11-19 18:29:22 +000032 &size, sizeof (int))) < 0)
33 zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
ajsae5e24d2004-11-19 23:43:10 +000034 sock,size,safe_strerror(errno));
paul0b3acf42004-09-17 08:39:08 +000035
36 return ret;
37}
38
paul4f7baa02004-07-23 15:11:07 +000039static void *
40getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
41{
42 struct cmsghdr *cmsg;
43 void *ptr = NULL;
44
45 for (cmsg = CMSG_FIRSTHDR(msgh);
46 cmsg != NULL;
47 cmsg = CMSG_NXTHDR(msgh, cmsg))
48 if (cmsg->cmsg_level == level && cmsg->cmsg_type)
49 return (ptr = CMSG_DATA(cmsg));
paul9035efa2004-10-10 11:56:56 +000050
51 return NULL;
paul4f7baa02004-07-23 15:11:07 +000052}
53
paul718e3742002-12-13 20:15:29 +000054#ifdef HAVE_IPV6
55/* Set IPv6 packet info to the socket. */
56int
57setsockopt_ipv6_pktinfo (int sock, int val)
58{
59 int ret;
60
61#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
62 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
63 if (ret < 0)
ajs6099b3b2004-11-20 02:06:59 +000064 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +000065#else /*RFC2292*/
66 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
67 if (ret < 0)
ajs6099b3b2004-11-20 02:06:59 +000068 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +000069#endif /* INIA_IPV6 */
70 return ret;
71}
72
73/* Set multicast hops val to the socket. */
74int
75setsockopt_ipv6_checksum (int sock, int val)
76{
77 int ret;
78
79#ifdef GNU_LINUX
80 ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
81#else
82 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
83#endif /* GNU_LINUX */
84 if (ret < 0)
85 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
86 return ret;
87}
88
89/* Set multicast hops val to the socket. */
90int
91setsockopt_ipv6_multicast_hops (int sock, int val)
92{
93 int ret;
94
95 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
96 if (ret < 0)
97 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
98 return ret;
99}
100
101/* Set multicast hops val to the socket. */
102int
103setsockopt_ipv6_unicast_hops (int sock, int val)
104{
105 int ret;
106
107 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
108 if (ret < 0)
109 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
110 return ret;
111}
112
113int
114setsockopt_ipv6_hoplimit (int sock, int val)
115{
116 int ret;
117
118#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
119 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
120 if (ret < 0)
121 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
122#else /*RFC2292*/
123 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
124 if (ret < 0)
125 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
126#endif
127 return ret;
128}
129
130/* Set multicast loop zero to the socket. */
131int
132setsockopt_ipv6_multicast_loop (int sock, int val)
133{
134 int ret;
135
136 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
137 sizeof (val));
138 if (ret < 0)
139 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
140 return ret;
141}
142
paul4f7baa02004-07-23 15:11:07 +0000143static int
paule6822762004-08-19 04:13:29 +0000144getsockopt_ipv6_ifindex (struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000145{
146 struct in6_pktinfo *pktinfo;
147
148 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
149
150 return pktinfo->ipi6_ifindex;
151}
paul718e3742002-12-13 20:15:29 +0000152#endif /* HAVE_IPV6 */
153
154
155/* Set up a multicast socket options for IPv4
156 This is here so that people only have to do their OS multicast mess
157 in one place rather than all through zebra, ospfd, and ripd
158 NB: This is a hookpoint for specific OS functionality */
159int
160setsockopt_multicast_ipv4(int sock,
161 int optname,
162 struct in_addr if_addr,
163 unsigned int mcast_addr,
164 unsigned int ifindex)
165{
166
167 /* Linux 2.2.0 and up */
168#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
169 /* This is better because it uses ifindex directly */
170 struct ip_mreqn mreqn;
171
172 switch (optname)
173 {
174 case IP_MULTICAST_IF:
175 case IP_ADD_MEMBERSHIP:
176 case IP_DROP_MEMBERSHIP:
177 memset (&mreqn, 0, sizeof(mreqn));
178
179 if (mcast_addr)
180 mreqn.imr_multiaddr.s_addr = mcast_addr;
181
182 if (ifindex)
183 mreqn.imr_ifindex = ifindex;
184 else
185 mreqn.imr_address = if_addr;
186
187 return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
188 break;
189
190 default:
191 /* Can out and give an understandable error */
192 errno = EINVAL;
193 return -1;
194 break;
195 }
196
197 /* Example defines for another OS, boilerplate off other code in this
198 function, AND handle optname as per other sections for consistency !! */
199 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
200 /* Add your favourite OS here! */
201
202#else /* #if OS_TYPE */
203 /* default OS support */
204
205 struct in_addr m;
206 struct ip_mreq mreq;
207
208 switch (optname)
209 {
210 case IP_MULTICAST_IF:
211 m = if_addr;
212
213 return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
214 break;
215
216 case IP_ADD_MEMBERSHIP:
217 case IP_DROP_MEMBERSHIP:
218 memset (&mreq, 0, sizeof(mreq));
219 mreq.imr_multiaddr.s_addr = mcast_addr;
220 mreq.imr_interface = if_addr;
221
222 return setsockopt (sock,
223 IPPROTO_IP,
224 optname,
225 (void *)&mreq,
226 sizeof(mreq));
227 break;
228
229 default:
230 /* Can out and give an understandable error */
231 errno = EINVAL;
232 return -1;
233 break;
234 }
235#endif /* #if OS_TYPE */
236
237}
paul4f7baa02004-07-23 15:11:07 +0000238
239static int
paule6822762004-08-19 04:13:29 +0000240setsockopt_ipv4_ifindex (int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000241{
242 int ret;
243
244#if defined (IP_PKTINFO)
ajs1d75c8c2004-12-28 21:43:17 +0000245 if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
246 zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
247 sock,val,safe_strerror(errno));
paul4f7baa02004-07-23 15:11:07 +0000248#elif defined (IP_RECVIF)
ajs1d75c8c2004-12-28 21:43:17 +0000249 if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
250 zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
251 sock,val,safe_strerror(errno));
paul4f7baa02004-07-23 15:11:07 +0000252#else
253#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
254#warning "Will not be able to receive link info."
255#warning "Things might be seriously broken.."
ajs1d75c8c2004-12-28 21:43:17 +0000256 /* XXX Does this ever happen? Should there be a zlog_warn message here? */
257 ret = -1;
paul4f7baa02004-07-23 15:11:07 +0000258#endif
paul4f7baa02004-07-23 15:11:07 +0000259 return ret;
260}
261
paule6822762004-08-19 04:13:29 +0000262int
263setsockopt_ifindex (int af, int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000264{
paule6822762004-08-19 04:13:29 +0000265 int ret = -1;
266
267 switch (af)
268 {
269 case AF_INET:
270 ret = setsockopt_ipv4_ifindex (sock, val);
271 break;
272#ifdef HAVE_IPV6
273 case AF_INET6:
274 ret = setsockopt_ipv6_pktinfo (sock, val);
275 break;
276#endif
277 default:
hassoe473b032004-09-26 16:08:11 +0000278 zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
paule6822762004-08-19 04:13:29 +0000279 }
280 return ret;
281}
282
283static int
284getsockopt_ipv4_ifindex (struct msghdr *msgh)
285{
gdt1d69fdf2004-12-29 18:53:30 +0000286 /*
287 * XXX: This routine's semantics are ill-defined, in particular how
288 * the results "can't determine interface due to runtime behavior"
289 * and "OS has no support for how to determine behavior" are
290 * encoded. For now, return 0 for either case; caller must handle
291 * as "don't know".
292 */
paule6822762004-08-19 04:13:29 +0000293 int ifindex = -1;
gdt1d69fdf2004-12-29 18:53:30 +0000294
295 /*
296 * If autoconf found a method to get ifindex, but it didn't work for
297 * this packet, or on this OS, this routine can be entered with a
298 * NULL cmsghdr pointer. Check msgh before using in each case
299 * below, rather than here, to avoid having to ifdef twice, once for
300 * declarations and once for code.
301 */
302
paule6822762004-08-19 04:13:29 +0000303#if defined(IP_PKTINFO)
304/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000305 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000306
gdt1d69fdf2004-12-29 18:53:30 +0000307 if (msgh == NULL)
308 return 0;
309
paule6822762004-08-19 04:13:29 +0000310 pktinfo =
311 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
312 ifindex = pktinfo->ipi_ifindex;
313
314#elif defined(IP_RECVIF)
315/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000316#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000317 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000318 struct sockaddr_dl *sdl;
319#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000320 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000321
gdt1d69fdf2004-12-29 18:53:30 +0000322 if (msgh == NULL)
323 return 0;
324
gdt33f92322004-07-23 16:14:32 +0000325#ifndef SUNOS_5
326 sdl =
paul4f7baa02004-07-23 15:11:07 +0000327 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000328 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000329#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000330 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000331#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000332
333#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
334
paul4f7baa02004-07-23 15:11:07 +0000335#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paul7d9c6e52004-10-22 10:54:39 +0000336#warning "things probably will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000337 /* XXX why not -1 - this is a failure condition. */
paul7d9c6e52004-10-22 10:54:39 +0000338 ifindex = 0;
paule6822762004-08-19 04:13:29 +0000339#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000340 return ifindex;
341}
342
343/* return ifindex, 0 if none found */
344int
paule6822762004-08-19 04:13:29 +0000345getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000346{
347 int ifindex = 0;
348
349 switch (af)
350 {
351 case AF_INET:
paule6822762004-08-19 04:13:29 +0000352 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000353 break;
354#ifdef HAVE_IPV6
355 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000356 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000357 break;
358#endif
359 default:
hassoe473b032004-09-26 16:08:11 +0000360 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000361 return (ifindex = 0);
362 }
363}
paul96e27c92004-10-05 14:33:43 +0000364
365/* swab iph between order system uses for IP_HDRINCL and host order */
366void
367sockopt_iphdrincl_swab_htosys (struct ip *iph)
368{
369 /* BSD and derived take iph in network order, except for
370 * ip_len and ip_off
371 */
372#ifndef HAVE_IP_HDRINCL_BSD_ORDER
373 iph->ip_len = htons(iph->ip_len);
374 iph->ip_off = htons(iph->ip_off);
375#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
376
377 iph->ip_id = htons(iph->ip_id);
378}
379
380void
381sockopt_iphdrincl_swab_systoh (struct ip *iph)
382{
383#ifndef HAVE_IP_HDRINCL_BSD_ORDER
384 iph->ip_len = ntohs(iph->ip_len);
385 iph->ip_off = ntohs(iph->ip_off);
386#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
387
388 iph->ip_id = ntohs(iph->ip_id);
389}