blob: 1a7524b5dccdb8e23fe7c96c3f281ab6189f63dd [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));
49}
50
paul718e3742002-12-13 20:15:29 +000051#ifdef HAVE_IPV6
52/* Set IPv6 packet info to the socket. */
53int
54setsockopt_ipv6_pktinfo (int sock, int val)
55{
56 int ret;
57
58#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
59 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
60 if (ret < 0)
61 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno));
62#else /*RFC2292*/
63 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
64 if (ret < 0)
65 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
66#endif /* INIA_IPV6 */
67 return ret;
68}
69
70/* Set multicast hops val to the socket. */
71int
72setsockopt_ipv6_checksum (int sock, int val)
73{
74 int ret;
75
76#ifdef GNU_LINUX
77 ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
78#else
79 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
80#endif /* GNU_LINUX */
81 if (ret < 0)
82 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
83 return ret;
84}
85
86/* Set multicast hops val to the socket. */
87int
88setsockopt_ipv6_multicast_hops (int sock, int val)
89{
90 int ret;
91
92 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
93 if (ret < 0)
94 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
95 return ret;
96}
97
98/* Set multicast hops val to the socket. */
99int
100setsockopt_ipv6_unicast_hops (int sock, int val)
101{
102 int ret;
103
104 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
105 if (ret < 0)
106 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
107 return ret;
108}
109
110int
111setsockopt_ipv6_hoplimit (int sock, int val)
112{
113 int ret;
114
115#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
116 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
117 if (ret < 0)
118 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
119#else /*RFC2292*/
120 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
121 if (ret < 0)
122 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
123#endif
124 return ret;
125}
126
127/* Set multicast loop zero to the socket. */
128int
129setsockopt_ipv6_multicast_loop (int sock, int val)
130{
131 int ret;
132
133 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
134 sizeof (val));
135 if (ret < 0)
136 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
137 return ret;
138}
139
paul4f7baa02004-07-23 15:11:07 +0000140static int
paule6822762004-08-19 04:13:29 +0000141getsockopt_ipv6_ifindex (struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000142{
143 struct in6_pktinfo *pktinfo;
144
145 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
146
147 return pktinfo->ipi6_ifindex;
148}
paul718e3742002-12-13 20:15:29 +0000149#endif /* HAVE_IPV6 */
150
151
152/* Set up a multicast socket options for IPv4
153 This is here so that people only have to do their OS multicast mess
154 in one place rather than all through zebra, ospfd, and ripd
155 NB: This is a hookpoint for specific OS functionality */
156int
157setsockopt_multicast_ipv4(int sock,
158 int optname,
159 struct in_addr if_addr,
160 unsigned int mcast_addr,
161 unsigned int ifindex)
162{
163
164 /* Linux 2.2.0 and up */
165#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
166 /* This is better because it uses ifindex directly */
167 struct ip_mreqn mreqn;
168
169 switch (optname)
170 {
171 case IP_MULTICAST_IF:
172 case IP_ADD_MEMBERSHIP:
173 case IP_DROP_MEMBERSHIP:
174 memset (&mreqn, 0, sizeof(mreqn));
175
176 if (mcast_addr)
177 mreqn.imr_multiaddr.s_addr = mcast_addr;
178
179 if (ifindex)
180 mreqn.imr_ifindex = ifindex;
181 else
182 mreqn.imr_address = if_addr;
183
184 return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
185 break;
186
187 default:
188 /* Can out and give an understandable error */
189 errno = EINVAL;
190 return -1;
191 break;
192 }
193
194 /* Example defines for another OS, boilerplate off other code in this
195 function, AND handle optname as per other sections for consistency !! */
196 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
197 /* Add your favourite OS here! */
198
199#else /* #if OS_TYPE */
200 /* default OS support */
201
202 struct in_addr m;
203 struct ip_mreq mreq;
204
205 switch (optname)
206 {
207 case IP_MULTICAST_IF:
208 m = if_addr;
209
210 return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
211 break;
212
213 case IP_ADD_MEMBERSHIP:
214 case IP_DROP_MEMBERSHIP:
215 memset (&mreq, 0, sizeof(mreq));
216 mreq.imr_multiaddr.s_addr = mcast_addr;
217 mreq.imr_interface = if_addr;
218
219 return setsockopt (sock,
220 IPPROTO_IP,
221 optname,
222 (void *)&mreq,
223 sizeof(mreq));
224 break;
225
226 default:
227 /* Can out and give an understandable error */
228 errno = EINVAL;
229 return -1;
230 break;
231 }
232#endif /* #if OS_TYPE */
233
234}
paul4f7baa02004-07-23 15:11:07 +0000235
236static int
paule6822762004-08-19 04:13:29 +0000237setsockopt_ipv4_ifindex (int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000238{
239 int ret;
240
241#if defined (IP_PKTINFO)
242 ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val));
243#elif defined (IP_RECVIF)
244 ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val));
245#else
246#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
247#warning "Will not be able to receive link info."
248#warning "Things might be seriously broken.."
249#endif
250
251 if (ret < 0)
252 zlog_warn ("Can't set IP_PKTINFO option");
253 return ret;
254}
255
256/* set appropriate pktinfo socket option
257 * on systems without PKTINFO, sets RECVIF, which only retrieves
258 * interface index.
paule6822762004-08-19 04:13:29 +0000259 * Not portable for IPv4, use only setsockopt_ifindex for v4.
paul4f7baa02004-07-23 15:11:07 +0000260 */
261int
262setsockopt_pktinfo (int af, int sock, int val)
263{
264 int ret = -1;
265
266 switch (af)
267 {
268 case AF_INET:
paule6822762004-08-19 04:13:29 +0000269 /* _ifindex will use PKTINFO if available.. */
270 ret = setsockopt_ipv4_ifindex (sock, val);
paul4f7baa02004-07-23 15:11:07 +0000271 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_pktinfo: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000279 }
280 return ret;
281}
282
paule6822762004-08-19 04:13:29 +0000283int
284setsockopt_ifindex (int af, int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000285{
paule6822762004-08-19 04:13:29 +0000286 int ret = -1;
287
288 switch (af)
289 {
290 case AF_INET:
291 ret = setsockopt_ipv4_ifindex (sock, val);
292 break;
293#ifdef HAVE_IPV6
294 case AF_INET6:
295 ret = setsockopt_ipv6_pktinfo (sock, val);
296 break;
297#endif
298 default:
hassoe473b032004-09-26 16:08:11 +0000299 zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
paule6822762004-08-19 04:13:29 +0000300 }
301 return ret;
302}
303
304static int
305getsockopt_ipv4_ifindex (struct msghdr *msgh)
306{
307 int ifindex = -1;
308#if defined(IP_PKTINFO)
309/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000310 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000311
312 pktinfo =
313 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
314 ifindex = pktinfo->ipi_ifindex;
315
316#elif defined(IP_RECVIF)
317/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000318#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000319 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000320 struct sockaddr_dl *sdl;
321#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000322 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000323
gdt33f92322004-07-23 16:14:32 +0000324#ifndef SUNOS_5
325 sdl =
paul4f7baa02004-07-23 15:11:07 +0000326 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000327 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000328#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000329 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000330#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000331
332#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
333
paul4f7baa02004-07-23 15:11:07 +0000334#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paule6822762004-08-19 04:13:29 +0000335#warning "things will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000336 /* XXX why not -1 - this is a failure condition. */
paule6822762004-08-19 04:13:29 +0000337 ifindex = -1;
338#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000339 return ifindex;
340}
341
342/* return ifindex, 0 if none found */
343int
paule6822762004-08-19 04:13:29 +0000344getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000345{
346 int ifindex = 0;
347
348 switch (af)
349 {
350 case AF_INET:
paule6822762004-08-19 04:13:29 +0000351 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000352 break;
353#ifdef HAVE_IPV6
354 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000355 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000356 break;
357#endif
358 default:
hassoe473b032004-09-26 16:08:11 +0000359 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000360 return (ifindex = 0);
361 }
362}