blob: 682b7397b13a0fb34064967775db1a4663baed5f [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"
24
paul4f7baa02004-07-23 15:11:07 +000025static void *
26getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
27{
28 struct cmsghdr *cmsg;
29 void *ptr = NULL;
30
31 for (cmsg = CMSG_FIRSTHDR(msgh);
32 cmsg != NULL;
33 cmsg = CMSG_NXTHDR(msgh, cmsg))
34 if (cmsg->cmsg_level == level && cmsg->cmsg_type)
35 return (ptr = CMSG_DATA(cmsg));
36}
37
paul718e3742002-12-13 20:15:29 +000038#ifdef HAVE_IPV6
39/* Set IPv6 packet info to the socket. */
40int
41setsockopt_ipv6_pktinfo (int sock, int val)
42{
43 int ret;
44
45#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
46 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
47 if (ret < 0)
48 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno));
49#else /*RFC2292*/
50 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
51 if (ret < 0)
52 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
53#endif /* INIA_IPV6 */
54 return ret;
55}
56
57/* Set multicast hops val to the socket. */
58int
59setsockopt_ipv6_checksum (int sock, int val)
60{
61 int ret;
62
63#ifdef GNU_LINUX
64 ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
65#else
66 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
67#endif /* GNU_LINUX */
68 if (ret < 0)
69 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
70 return ret;
71}
72
73/* Set multicast hops val to the socket. */
74int
75setsockopt_ipv6_multicast_hops (int sock, int val)
76{
77 int ret;
78
79 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
80 if (ret < 0)
81 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
82 return ret;
83}
84
85/* Set multicast hops val to the socket. */
86int
87setsockopt_ipv6_unicast_hops (int sock, int val)
88{
89 int ret;
90
91 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
92 if (ret < 0)
93 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
94 return ret;
95}
96
97int
98setsockopt_ipv6_hoplimit (int sock, int val)
99{
100 int ret;
101
102#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
103 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
104 if (ret < 0)
105 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
106#else /*RFC2292*/
107 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
108 if (ret < 0)
109 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
110#endif
111 return ret;
112}
113
114/* Set multicast loop zero to the socket. */
115int
116setsockopt_ipv6_multicast_loop (int sock, int val)
117{
118 int ret;
119
120 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
121 sizeof (val));
122 if (ret < 0)
123 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
124 return ret;
125}
126
paul4f7baa02004-07-23 15:11:07 +0000127static int
128getsockopt_ipv6_pktinfo_ifindex (struct msghdr *msgh)
129{
130 struct in6_pktinfo *pktinfo;
131
132 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
133
134 return pktinfo->ipi6_ifindex;
135}
paul718e3742002-12-13 20:15:29 +0000136#endif /* HAVE_IPV6 */
137
138
139/* Set up a multicast socket options for IPv4
140 This is here so that people only have to do their OS multicast mess
141 in one place rather than all through zebra, ospfd, and ripd
142 NB: This is a hookpoint for specific OS functionality */
143int
144setsockopt_multicast_ipv4(int sock,
145 int optname,
146 struct in_addr if_addr,
147 unsigned int mcast_addr,
148 unsigned int ifindex)
149{
150
151 /* Linux 2.2.0 and up */
152#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
153 /* This is better because it uses ifindex directly */
154 struct ip_mreqn mreqn;
155
156 switch (optname)
157 {
158 case IP_MULTICAST_IF:
159 case IP_ADD_MEMBERSHIP:
160 case IP_DROP_MEMBERSHIP:
161 memset (&mreqn, 0, sizeof(mreqn));
162
163 if (mcast_addr)
164 mreqn.imr_multiaddr.s_addr = mcast_addr;
165
166 if (ifindex)
167 mreqn.imr_ifindex = ifindex;
168 else
169 mreqn.imr_address = if_addr;
170
171 return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
172 break;
173
174 default:
175 /* Can out and give an understandable error */
176 errno = EINVAL;
177 return -1;
178 break;
179 }
180
181 /* Example defines for another OS, boilerplate off other code in this
182 function, AND handle optname as per other sections for consistency !! */
183 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
184 /* Add your favourite OS here! */
185
186#else /* #if OS_TYPE */
187 /* default OS support */
188
189 struct in_addr m;
190 struct ip_mreq mreq;
191
192 switch (optname)
193 {
194 case IP_MULTICAST_IF:
195 m = if_addr;
196
197 return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
198 break;
199
200 case IP_ADD_MEMBERSHIP:
201 case IP_DROP_MEMBERSHIP:
202 memset (&mreq, 0, sizeof(mreq));
203 mreq.imr_multiaddr.s_addr = mcast_addr;
204 mreq.imr_interface = if_addr;
205
206 return setsockopt (sock,
207 IPPROTO_IP,
208 optname,
209 (void *)&mreq,
210 sizeof(mreq));
211 break;
212
213 default:
214 /* Can out and give an understandable error */
215 errno = EINVAL;
216 return -1;
217 break;
218 }
219#endif /* #if OS_TYPE */
220
221}
paul4f7baa02004-07-23 15:11:07 +0000222
223static int
224setsockopt_ipv4_pktinfo (int sock, int val)
225{
226 int ret;
227
228#if defined (IP_PKTINFO)
229 ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val));
230#elif defined (IP_RECVIF)
231 ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val));
232#else
233#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
234#warning "Will not be able to receive link info."
235#warning "Things might be seriously broken.."
236#endif
237
238 if (ret < 0)
239 zlog_warn ("Can't set IP_PKTINFO option");
240 return ret;
241}
242
243/* set appropriate pktinfo socket option
244 * on systems without PKTINFO, sets RECVIF, which only retrieves
245 * interface index.
246 */
247int
248setsockopt_pktinfo (int af, int sock, int val)
249{
250 int ret = -1;
251
252 switch (af)
253 {
254 case AF_INET:
255 ret = setsockopt_ipv4_pktinfo (sock, val);
256 break;
257#ifdef HAVE_IPV6
258 case AF_INET6:
259 ret = setsockopt_ipv6_pktinfo (sock, val);
260 break;
261#endif
262 default:
263 zlog_warn ("setsockopt_pktinfo: unknown address family %d");
264 }
265 return ret;
266}
267
268
269static int
270getsockopt_ipv4_pktinfo_ifindex (struct msghdr *msgh)
271{
272 int ifindex = 0;
273#if defined (IP_PKTINFO)
274 struct in_pktinfo *pktinfo;
gdt33f92322004-07-23 16:14:32 +0000275#elif defined (IP_RECVIF)
paul4f7baa02004-07-23 15:11:07 +0000276#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000277 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000278 struct sockaddr_dl *sdl;
279#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000280 /* SUNOS_5 doesn't need a structure to extract ifindex */
paul4f7baa02004-07-23 15:11:07 +0000281#else /* IP_RECVIF */
gdt33f92322004-07-23 16:14:32 +0000282 /* XXX Neither, so we are going to lose. */
paul4f7baa02004-07-23 15:11:07 +0000283#endif /* IP_PKTINFO */
284
285#ifdef IP_PKTINFO
286 pktinfo =
287 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
288#elif defined (IP_RECVIF)
gdt33f92322004-07-23 16:14:32 +0000289#ifndef SUNOS_5
290 sdl =
paul4f7baa02004-07-23 15:11:07 +0000291 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000292 ifindex = sdl->sdl_index;
293#else
294 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000295#endif /* SUNOS_5 */
296#else
297#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
gdt33f92322004-07-23 16:14:32 +0000298 /* XXX why not -1 - this is a failure condition. */
299 ifindex = 0;
paul4f7baa02004-07-23 15:11:07 +0000300#endif /* IP_PKTINFO */
301
302 return ifindex;
303}
304
305/* return ifindex, 0 if none found */
306int
307getsockopt_pktinfo_ifindex (int af, struct msghdr *msgh)
308{
309 int ifindex = 0;
310
311 switch (af)
312 {
313 case AF_INET:
314 return (getsockopt_ipv4_pktinfo_ifindex (msgh));
315 break;
316#ifdef HAVE_IPV6
317 case AF_INET6:
318 return (getsockopt_ipv6_pktinfo_ifindex (msgh));
319 break;
320#endif
321 default:
322 zlog_warn ("getsockopt_pktinfo_ifindex: unknown address family %d");
323 return (ifindex = 0);
324 }
325}
326