blob: 1d4063a615e004dab64f7d5c96ad75951d94616e [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 *)
32 &size, sizeof (int))) < 0);
paul0b3acf42004-09-17 08:39:08 +000033 zlog_err ("can't setsockopt SO_RCVBUF");
34
35 return ret;
36}
37
paul4f7baa02004-07-23 15:11:07 +000038static void *
39getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
40{
41 struct cmsghdr *cmsg;
42 void *ptr = NULL;
43
44 for (cmsg = CMSG_FIRSTHDR(msgh);
45 cmsg != NULL;
46 cmsg = CMSG_NXTHDR(msgh, cmsg))
47 if (cmsg->cmsg_level == level && cmsg->cmsg_type)
48 return (ptr = CMSG_DATA(cmsg));
paul9035efa2004-10-10 11:56:56 +000049
50 return NULL;
paul4f7baa02004-07-23 15:11:07 +000051}
52
paul718e3742002-12-13 20:15:29 +000053#ifdef HAVE_IPV6
54/* Set IPv6 packet info to the socket. */
55int
56setsockopt_ipv6_pktinfo (int sock, int val)
57{
58 int ret;
59
60#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
61 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
62 if (ret < 0)
63 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno));
64#else /*RFC2292*/
65 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
66 if (ret < 0)
67 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
68#endif /* INIA_IPV6 */
69 return ret;
70}
71
72/* Set multicast hops val to the socket. */
73int
74setsockopt_ipv6_checksum (int sock, int val)
75{
76 int ret;
77
78#ifdef GNU_LINUX
79 ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
80#else
81 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
82#endif /* GNU_LINUX */
83 if (ret < 0)
84 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
85 return ret;
86}
87
88/* Set multicast hops val to the socket. */
89int
90setsockopt_ipv6_multicast_hops (int sock, int val)
91{
92 int ret;
93
94 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
95 if (ret < 0)
96 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
97 return ret;
98}
99
100/* Set multicast hops val to the socket. */
101int
102setsockopt_ipv6_unicast_hops (int sock, int val)
103{
104 int ret;
105
106 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
107 if (ret < 0)
108 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
109 return ret;
110}
111
112int
113setsockopt_ipv6_hoplimit (int sock, int val)
114{
115 int ret;
116
117#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
118 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
119 if (ret < 0)
120 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
121#else /*RFC2292*/
122 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
123 if (ret < 0)
124 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
125#endif
126 return ret;
127}
128
129/* Set multicast loop zero to the socket. */
130int
131setsockopt_ipv6_multicast_loop (int sock, int val)
132{
133 int ret;
134
135 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
136 sizeof (val));
137 if (ret < 0)
138 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
139 return ret;
140}
141
paul4f7baa02004-07-23 15:11:07 +0000142static int
paule6822762004-08-19 04:13:29 +0000143getsockopt_ipv6_ifindex (struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000144{
145 struct in6_pktinfo *pktinfo;
146
147 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
148
149 return pktinfo->ipi6_ifindex;
150}
paul718e3742002-12-13 20:15:29 +0000151#endif /* HAVE_IPV6 */
152
153
154/* Set up a multicast socket options for IPv4
155 This is here so that people only have to do their OS multicast mess
156 in one place rather than all through zebra, ospfd, and ripd
157 NB: This is a hookpoint for specific OS functionality */
158int
159setsockopt_multicast_ipv4(int sock,
160 int optname,
161 struct in_addr if_addr,
162 unsigned int mcast_addr,
163 unsigned int ifindex)
164{
165
166 /* Linux 2.2.0 and up */
167#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
168 /* This is better because it uses ifindex directly */
169 struct ip_mreqn mreqn;
170
171 switch (optname)
172 {
173 case IP_MULTICAST_IF:
174 case IP_ADD_MEMBERSHIP:
175 case IP_DROP_MEMBERSHIP:
176 memset (&mreqn, 0, sizeof(mreqn));
177
178 if (mcast_addr)
179 mreqn.imr_multiaddr.s_addr = mcast_addr;
180
181 if (ifindex)
182 mreqn.imr_ifindex = ifindex;
183 else
184 mreqn.imr_address = if_addr;
185
186 return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
187 break;
188
189 default:
190 /* Can out and give an understandable error */
191 errno = EINVAL;
192 return -1;
193 break;
194 }
195
196 /* Example defines for another OS, boilerplate off other code in this
197 function, AND handle optname as per other sections for consistency !! */
198 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
199 /* Add your favourite OS here! */
200
201#else /* #if OS_TYPE */
202 /* default OS support */
203
204 struct in_addr m;
205 struct ip_mreq mreq;
206
207 switch (optname)
208 {
209 case IP_MULTICAST_IF:
210 m = if_addr;
211
212 return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
213 break;
214
215 case IP_ADD_MEMBERSHIP:
216 case IP_DROP_MEMBERSHIP:
217 memset (&mreq, 0, sizeof(mreq));
218 mreq.imr_multiaddr.s_addr = mcast_addr;
219 mreq.imr_interface = if_addr;
220
221 return setsockopt (sock,
222 IPPROTO_IP,
223 optname,
224 (void *)&mreq,
225 sizeof(mreq));
226 break;
227
228 default:
229 /* Can out and give an understandable error */
230 errno = EINVAL;
231 return -1;
232 break;
233 }
234#endif /* #if OS_TYPE */
235
236}
paul4f7baa02004-07-23 15:11:07 +0000237
238static int
paule6822762004-08-19 04:13:29 +0000239setsockopt_ipv4_ifindex (int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000240{
241 int ret;
242
243#if defined (IP_PKTINFO)
244 ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val));
245#elif defined (IP_RECVIF)
246 ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val));
247#else
248#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
249#warning "Will not be able to receive link info."
250#warning "Things might be seriously broken.."
251#endif
252
253 if (ret < 0)
254 zlog_warn ("Can't set IP_PKTINFO option");
255 return ret;
256}
257
258/* set appropriate pktinfo socket option
259 * on systems without PKTINFO, sets RECVIF, which only retrieves
260 * interface index.
paule6822762004-08-19 04:13:29 +0000261 * Not portable for IPv4, use only setsockopt_ifindex for v4.
paul4f7baa02004-07-23 15:11:07 +0000262 */
paul23b9c612004-10-22 11:51:57 +0000263static int
paul4f7baa02004-07-23 15:11:07 +0000264setsockopt_pktinfo (int af, int sock, int val)
265{
266 int ret = -1;
267
268 switch (af)
269 {
270 case AF_INET:
paule6822762004-08-19 04:13:29 +0000271 /* _ifindex will use PKTINFO if available.. */
272 ret = setsockopt_ipv4_ifindex (sock, val);
paul4f7baa02004-07-23 15:11:07 +0000273 break;
274#ifdef HAVE_IPV6
275 case AF_INET6:
276 ret = setsockopt_ipv6_pktinfo (sock, val);
277 break;
278#endif
279 default:
hassoe473b032004-09-26 16:08:11 +0000280 zlog_warn ("setsockopt_pktinfo: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000281 }
282 return ret;
283}
284
paule6822762004-08-19 04:13:29 +0000285int
286setsockopt_ifindex (int af, int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000287{
paule6822762004-08-19 04:13:29 +0000288 int ret = -1;
289
290 switch (af)
291 {
292 case AF_INET:
293 ret = setsockopt_ipv4_ifindex (sock, val);
294 break;
295#ifdef HAVE_IPV6
296 case AF_INET6:
297 ret = setsockopt_ipv6_pktinfo (sock, val);
298 break;
299#endif
300 default:
hassoe473b032004-09-26 16:08:11 +0000301 zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
paule6822762004-08-19 04:13:29 +0000302 }
303 return ret;
304}
305
306static int
307getsockopt_ipv4_ifindex (struct msghdr *msgh)
308{
309 int ifindex = -1;
310#if defined(IP_PKTINFO)
311/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000312 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000313
314 pktinfo =
315 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
316 ifindex = pktinfo->ipi_ifindex;
317
318#elif defined(IP_RECVIF)
319/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000320#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000321 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000322 struct sockaddr_dl *sdl;
323#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000324 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000325
gdt33f92322004-07-23 16:14:32 +0000326#ifndef SUNOS_5
327 sdl =
paul4f7baa02004-07-23 15:11:07 +0000328 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000329 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000330#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000331 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000332#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000333
334#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
335
paul4f7baa02004-07-23 15:11:07 +0000336#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paul7d9c6e52004-10-22 10:54:39 +0000337#warning "things probably will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000338 /* XXX why not -1 - this is a failure condition. */
paul7d9c6e52004-10-22 10:54:39 +0000339 ifindex = 0;
paule6822762004-08-19 04:13:29 +0000340#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000341 return ifindex;
342}
343
344/* return ifindex, 0 if none found */
345int
paule6822762004-08-19 04:13:29 +0000346getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000347{
348 int ifindex = 0;
349
350 switch (af)
351 {
352 case AF_INET:
paule6822762004-08-19 04:13:29 +0000353 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000354 break;
355#ifdef HAVE_IPV6
356 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000357 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000358 break;
359#endif
360 default:
hassoe473b032004-09-26 16:08:11 +0000361 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000362 return (ifindex = 0);
363 }
364}
paul96e27c92004-10-05 14:33:43 +0000365
366/* swab iph between order system uses for IP_HDRINCL and host order */
367void
368sockopt_iphdrincl_swab_htosys (struct ip *iph)
369{
370 /* BSD and derived take iph in network order, except for
371 * ip_len and ip_off
372 */
373#ifndef HAVE_IP_HDRINCL_BSD_ORDER
374 iph->ip_len = htons(iph->ip_len);
375 iph->ip_off = htons(iph->ip_off);
376#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
377
378 iph->ip_id = htons(iph->ip_id);
379}
380
381void
382sockopt_iphdrincl_swab_systoh (struct ip *iph)
383{
384#ifndef HAVE_IP_HDRINCL_BSD_ORDER
385 iph->ip_len = ntohs(iph->ip_len);
386 iph->ip_off = ntohs(iph->ip_off);
387#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
388
389 iph->ip_id = ntohs(iph->ip_id);
390}