blob: f424f03ee123ca668f0995db6c7b9e3ccbc3a46b [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",
34 sock,size,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)
64 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno));
65#else /*RFC2292*/
66 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
67 if (ret < 0)
68 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
69#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)
245 ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val));
246#elif defined (IP_RECVIF)
247 ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val));
248#else
249#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
250#warning "Will not be able to receive link info."
251#warning "Things might be seriously broken.."
252#endif
253
254 if (ret < 0)
255 zlog_warn ("Can't set IP_PKTINFO option");
256 return ret;
257}
258
paule6822762004-08-19 04:13:29 +0000259int
260setsockopt_ifindex (int af, int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000261{
paule6822762004-08-19 04:13:29 +0000262 int ret = -1;
263
264 switch (af)
265 {
266 case AF_INET:
267 ret = setsockopt_ipv4_ifindex (sock, val);
268 break;
269#ifdef HAVE_IPV6
270 case AF_INET6:
271 ret = setsockopt_ipv6_pktinfo (sock, val);
272 break;
273#endif
274 default:
hassoe473b032004-09-26 16:08:11 +0000275 zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
paule6822762004-08-19 04:13:29 +0000276 }
277 return ret;
278}
279
280static int
281getsockopt_ipv4_ifindex (struct msghdr *msgh)
282{
283 int ifindex = -1;
284#if defined(IP_PKTINFO)
285/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000286 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000287
288 pktinfo =
289 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
290 ifindex = pktinfo->ipi_ifindex;
291
292#elif defined(IP_RECVIF)
293/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000294#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000295 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000296 struct sockaddr_dl *sdl;
297#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000298 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000299
gdt33f92322004-07-23 16:14:32 +0000300#ifndef SUNOS_5
301 sdl =
paul4f7baa02004-07-23 15:11:07 +0000302 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000303 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000304#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000305 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000306#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000307
308#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
309
paul4f7baa02004-07-23 15:11:07 +0000310#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paul7d9c6e52004-10-22 10:54:39 +0000311#warning "things probably will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000312 /* XXX why not -1 - this is a failure condition. */
paul7d9c6e52004-10-22 10:54:39 +0000313 ifindex = 0;
paule6822762004-08-19 04:13:29 +0000314#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000315 return ifindex;
316}
317
318/* return ifindex, 0 if none found */
319int
paule6822762004-08-19 04:13:29 +0000320getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000321{
322 int ifindex = 0;
323
324 switch (af)
325 {
326 case AF_INET:
paule6822762004-08-19 04:13:29 +0000327 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000328 break;
329#ifdef HAVE_IPV6
330 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000331 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000332 break;
333#endif
334 default:
hassoe473b032004-09-26 16:08:11 +0000335 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
paul4f7baa02004-07-23 15:11:07 +0000336 return (ifindex = 0);
337 }
338}
paul96e27c92004-10-05 14:33:43 +0000339
340/* swab iph between order system uses for IP_HDRINCL and host order */
341void
342sockopt_iphdrincl_swab_htosys (struct ip *iph)
343{
344 /* BSD and derived take iph in network order, except for
345 * ip_len and ip_off
346 */
347#ifndef HAVE_IP_HDRINCL_BSD_ORDER
348 iph->ip_len = htons(iph->ip_len);
349 iph->ip_off = htons(iph->ip_off);
350#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
351
352 iph->ip_id = htons(iph->ip_id);
353}
354
355void
356sockopt_iphdrincl_swab_systoh (struct ip *iph)
357{
358#ifndef HAVE_IP_HDRINCL_BSD_ORDER
359 iph->ip_len = ntohs(iph->ip_len);
360 iph->ip_off = ntohs(iph->ip_off);
361#endif /* HAVE_IP_HDRINCL_BSD_ORDER */
362
363 iph->ip_id = ntohs(iph->ip_id);
364}