blob: 3a8033cc031f1c4f79e502872b3bb052d638b8f7 [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{
286 int ifindex = -1;
287#if defined(IP_PKTINFO)
288/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000289 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000290
291 pktinfo =
292 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
293 ifindex = pktinfo->ipi_ifindex;
294
295#elif defined(IP_RECVIF)
296/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000297#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000298 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000299 struct sockaddr_dl *sdl;
300#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000301 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000302
gdt33f92322004-07-23 16:14:32 +0000303#ifndef SUNOS_5
304 sdl =
paul4f7baa02004-07-23 15:11:07 +0000305 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000306 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000307#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000308 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000309#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000310
311#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
312
paul4f7baa02004-07-23 15:11:07 +0000313#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paul7d9c6e52004-10-22 10:54:39 +0000314#warning "things probably will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000315 /* XXX why not -1 - this is a failure condition. */
paul7d9c6e52004-10-22 10:54:39 +0000316 ifindex = 0;
paule6822762004-08-19 04:13:29 +0000317#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000318 return ifindex;
319}
320
321/* return ifindex, 0 if none found */
322int
paule6822762004-08-19 04:13:29 +0000323getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000324{
325 int ifindex = 0;
326
327 switch (af)
328 {
329 case AF_INET:
paule6822762004-08-19 04:13:29 +0000330 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000331 break;
332#ifdef HAVE_IPV6
333 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000334 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000335 break;
336#endif
337 default:
hassoe473b032004-09-26 16:08:11 +0000338 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000339 return (ifindex = 0);
340 }
341}
paul96e27c92004-10-05 14:33:43 +0000342
343/* swab iph between order system uses for IP_HDRINCL and host order */
344void
345sockopt_iphdrincl_swab_htosys (struct ip *iph)
346{
347 /* BSD and derived take iph in network order, except for
348 * ip_len and ip_off
349 */
350#ifndef HAVE_IP_HDRINCL_BSD_ORDER
351 iph->ip_len = htons(iph->ip_len);
352 iph->ip_off = htons(iph->ip_off);
353#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
354
355 iph->ip_id = htons(iph->ip_id);
356}
357
358void
359sockopt_iphdrincl_swab_systoh (struct ip *iph)
360{
361#ifndef HAVE_IP_HDRINCL_BSD_ORDER
362 iph->ip_len = ntohs(iph->ip_len);
363 iph->ip_off = ntohs(iph->ip_off);
364#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
365
366 iph->ip_id = ntohs(iph->ip_id);
367}