blob: d0b034f597d802727ee0743af318a8f69fb31d92 [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
paul4f7baa02004-07-23 15:11:07 +000026static void *
27getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
28{
29 struct cmsghdr *cmsg;
30 void *ptr = NULL;
31
32 for (cmsg = CMSG_FIRSTHDR(msgh);
33 cmsg != NULL;
34 cmsg = CMSG_NXTHDR(msgh, cmsg))
35 if (cmsg->cmsg_level == level && cmsg->cmsg_type)
36 return (ptr = CMSG_DATA(cmsg));
37}
38
paul718e3742002-12-13 20:15:29 +000039#ifdef HAVE_IPV6
40/* Set IPv6 packet info to the socket. */
41int
42setsockopt_ipv6_pktinfo (int sock, int val)
43{
44 int ret;
45
46#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
47 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
48 if (ret < 0)
49 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", strerror (errno));
50#else /*RFC2292*/
51 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
52 if (ret < 0)
53 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", strerror (errno));
54#endif /* INIA_IPV6 */
55 return ret;
56}
57
58/* Set multicast hops val to the socket. */
59int
60setsockopt_ipv6_checksum (int sock, int val)
61{
62 int ret;
63
64#ifdef GNU_LINUX
65 ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
66#else
67 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
68#endif /* GNU_LINUX */
69 if (ret < 0)
70 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
71 return ret;
72}
73
74/* Set multicast hops val to the socket. */
75int
76setsockopt_ipv6_multicast_hops (int sock, int val)
77{
78 int ret;
79
80 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
81 if (ret < 0)
82 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
83 return ret;
84}
85
86/* Set multicast hops val to the socket. */
87int
88setsockopt_ipv6_unicast_hops (int sock, int val)
89{
90 int ret;
91
92 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
93 if (ret < 0)
94 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
95 return ret;
96}
97
98int
99setsockopt_ipv6_hoplimit (int sock, int val)
100{
101 int ret;
102
103#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
104 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
105 if (ret < 0)
106 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
107#else /*RFC2292*/
108 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
109 if (ret < 0)
110 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
111#endif
112 return ret;
113}
114
115/* Set multicast loop zero to the socket. */
116int
117setsockopt_ipv6_multicast_loop (int sock, int val)
118{
119 int ret;
120
121 ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
122 sizeof (val));
123 if (ret < 0)
124 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
125 return ret;
126}
127
paul4f7baa02004-07-23 15:11:07 +0000128static int
paule6822762004-08-19 04:13:29 +0000129getsockopt_ipv6_ifindex (struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000130{
131 struct in6_pktinfo *pktinfo;
132
133 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
134
135 return pktinfo->ipi6_ifindex;
136}
paul718e3742002-12-13 20:15:29 +0000137#endif /* HAVE_IPV6 */
138
139
140/* Set up a multicast socket options for IPv4
141 This is here so that people only have to do their OS multicast mess
142 in one place rather than all through zebra, ospfd, and ripd
143 NB: This is a hookpoint for specific OS functionality */
144int
145setsockopt_multicast_ipv4(int sock,
146 int optname,
147 struct in_addr if_addr,
148 unsigned int mcast_addr,
149 unsigned int ifindex)
150{
151
152 /* Linux 2.2.0 and up */
153#if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
154 /* This is better because it uses ifindex directly */
155 struct ip_mreqn mreqn;
156
157 switch (optname)
158 {
159 case IP_MULTICAST_IF:
160 case IP_ADD_MEMBERSHIP:
161 case IP_DROP_MEMBERSHIP:
162 memset (&mreqn, 0, sizeof(mreqn));
163
164 if (mcast_addr)
165 mreqn.imr_multiaddr.s_addr = mcast_addr;
166
167 if (ifindex)
168 mreqn.imr_ifindex = ifindex;
169 else
170 mreqn.imr_address = if_addr;
171
172 return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
173 break;
174
175 default:
176 /* Can out and give an understandable error */
177 errno = EINVAL;
178 return -1;
179 break;
180 }
181
182 /* Example defines for another OS, boilerplate off other code in this
183 function, AND handle optname as per other sections for consistency !! */
184 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
185 /* Add your favourite OS here! */
186
187#else /* #if OS_TYPE */
188 /* default OS support */
189
190 struct in_addr m;
191 struct ip_mreq mreq;
192
193 switch (optname)
194 {
195 case IP_MULTICAST_IF:
196 m = if_addr;
197
198 return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
199 break;
200
201 case IP_ADD_MEMBERSHIP:
202 case IP_DROP_MEMBERSHIP:
203 memset (&mreq, 0, sizeof(mreq));
204 mreq.imr_multiaddr.s_addr = mcast_addr;
205 mreq.imr_interface = if_addr;
206
207 return setsockopt (sock,
208 IPPROTO_IP,
209 optname,
210 (void *)&mreq,
211 sizeof(mreq));
212 break;
213
214 default:
215 /* Can out and give an understandable error */
216 errno = EINVAL;
217 return -1;
218 break;
219 }
220#endif /* #if OS_TYPE */
221
222}
paul4f7baa02004-07-23 15:11:07 +0000223
224static int
paule6822762004-08-19 04:13:29 +0000225setsockopt_ipv4_ifindex (int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000226{
227 int ret;
228
229#if defined (IP_PKTINFO)
230 ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val));
231#elif defined (IP_RECVIF)
232 ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val));
233#else
234#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
235#warning "Will not be able to receive link info."
236#warning "Things might be seriously broken.."
237#endif
238
239 if (ret < 0)
240 zlog_warn ("Can't set IP_PKTINFO option");
241 return ret;
242}
243
244/* set appropriate pktinfo socket option
245 * on systems without PKTINFO, sets RECVIF, which only retrieves
246 * interface index.
paule6822762004-08-19 04:13:29 +0000247 * Not portable for IPv4, use only setsockopt_ifindex for v4.
paul4f7baa02004-07-23 15:11:07 +0000248 */
249int
250setsockopt_pktinfo (int af, int sock, int val)
251{
252 int ret = -1;
253
254 switch (af)
255 {
256 case AF_INET:
paule6822762004-08-19 04:13:29 +0000257 /* _ifindex will use PKTINFO if available.. */
258 ret = setsockopt_ipv4_ifindex (sock, val);
paul4f7baa02004-07-23 15:11:07 +0000259 break;
260#ifdef HAVE_IPV6
261 case AF_INET6:
262 ret = setsockopt_ipv6_pktinfo (sock, val);
263 break;
264#endif
265 default:
266 zlog_warn ("setsockopt_pktinfo: unknown address family %d");
267 }
268 return ret;
269}
270
paule6822762004-08-19 04:13:29 +0000271int
272setsockopt_ifindex (int af, int sock, int val)
paul4f7baa02004-07-23 15:11:07 +0000273{
paule6822762004-08-19 04:13:29 +0000274 int ret = -1;
275
276 switch (af)
277 {
278 case AF_INET:
279 ret = setsockopt_ipv4_ifindex (sock, val);
280 break;
281#ifdef HAVE_IPV6
282 case AF_INET6:
283 ret = setsockopt_ipv6_pktinfo (sock, val);
284 break;
285#endif
286 default:
287 zlog_warn ("setsockopt_ifindex: unknown address family %d");
288 }
289 return ret;
290}
291
292static int
293getsockopt_ipv4_ifindex (struct msghdr *msgh)
294{
295 int ifindex = -1;
296#if defined(IP_PKTINFO)
297/* Linux pktinfo based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000298 struct in_pktinfo *pktinfo;
paule6822762004-08-19 04:13:29 +0000299
300 pktinfo =
301 (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
302 ifindex = pktinfo->ipi_ifindex;
303
304#elif defined(IP_RECVIF)
305/* BSD/other IP_RECVIF based ifindex retrieval */
paul4f7baa02004-07-23 15:11:07 +0000306#ifndef SUNOS_5
gdt33f92322004-07-23 16:14:32 +0000307 /* RECVIF, but not SUNOS, so BSD */
paul4f7baa02004-07-23 15:11:07 +0000308 struct sockaddr_dl *sdl;
309#endif /* SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000310 /* SUNOS_5 doesn't need a structure to extract ifindex */
paule6822762004-08-19 04:13:29 +0000311
gdt33f92322004-07-23 16:14:32 +0000312#ifndef SUNOS_5
313 sdl =
paul4f7baa02004-07-23 15:11:07 +0000314 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
gdt33f92322004-07-23 16:14:32 +0000315 ifindex = sdl->sdl_index;
paule6822762004-08-19 04:13:29 +0000316#else /* !SUNOS_5 */
gdt33f92322004-07-23 16:14:32 +0000317 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
paul4f7baa02004-07-23 15:11:07 +0000318#endif /* SUNOS_5 */
paule6822762004-08-19 04:13:29 +0000319
320#else /* neither IP_PKTINFO nor IP_RECVIF, broken */
321
paul4f7baa02004-07-23 15:11:07 +0000322#warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
paule6822762004-08-19 04:13:29 +0000323#warning "things will be broken on this platform!"
gdt33f92322004-07-23 16:14:32 +0000324 /* XXX why not -1 - this is a failure condition. */
paule6822762004-08-19 04:13:29 +0000325 ifindex = -1;
326#endif /* IP_PKTINFO */
paul4f7baa02004-07-23 15:11:07 +0000327 return ifindex;
328}
329
330/* return ifindex, 0 if none found */
331int
paule6822762004-08-19 04:13:29 +0000332getsockopt_ifindex (int af, struct msghdr *msgh)
paul4f7baa02004-07-23 15:11:07 +0000333{
334 int ifindex = 0;
335
336 switch (af)
337 {
338 case AF_INET:
paule6822762004-08-19 04:13:29 +0000339 return (getsockopt_ipv4_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000340 break;
341#ifdef HAVE_IPV6
342 case AF_INET6:
paule6822762004-08-19 04:13:29 +0000343 return (getsockopt_ipv6_ifindex (msgh));
paul4f7baa02004-07-23 15:11:07 +0000344 break;
345#endif
346 default:
paule6822762004-08-19 04:13:29 +0000347 zlog_warn ("getsockopt_ifindex: unknown address family %d");
paul4f7baa02004-07-23 15:11:07 +0000348 return (ifindex = 0);
349 }
350}