]> git.proxmox.com Git - mirror_frr.git/blob - lib/sockopt.c
2004-12-17 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
[mirror_frr.git] / lib / sockopt.c
1 /* 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 #include "sockopt.h"
25
26 int
27 setsockopt_so_recvbuf (int sock, int size)
28 {
29 int ret;
30
31 if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
32 &size, sizeof (int))) < 0)
33 zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
34 sock,size,safe_strerror(errno));
35
36 return ret;
37 }
38
39 static void *
40 getsockopt_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));
50
51 return NULL;
52 }
53
54 #ifdef HAVE_IPV6
55 /* Set IPv6 packet info to the socket. */
56 int
57 setsockopt_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", safe_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", safe_strerror (errno));
69 #endif /* INIA_IPV6 */
70 return ret;
71 }
72
73 /* Set multicast hops val to the socket. */
74 int
75 setsockopt_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. */
90 int
91 setsockopt_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. */
102 int
103 setsockopt_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
113 int
114 setsockopt_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. */
131 int
132 setsockopt_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
143 static int
144 getsockopt_ipv6_ifindex (struct msghdr *msgh)
145 {
146 struct in6_pktinfo *pktinfo;
147
148 pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
149
150 return pktinfo->ipi6_ifindex;
151 }
152 #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 */
159 int
160 setsockopt_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 }
238
239 static int
240 setsockopt_ipv4_ifindex (int sock, int val)
241 {
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
259 int
260 setsockopt_ifindex (int af, int sock, int val)
261 {
262 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:
275 zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
276 }
277 return ret;
278 }
279
280 static int
281 getsockopt_ipv4_ifindex (struct msghdr *msgh)
282 {
283 int ifindex = -1;
284 #if defined(IP_PKTINFO)
285 /* Linux pktinfo based ifindex retrieval */
286 struct in_pktinfo *pktinfo;
287
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 */
294 #ifndef SUNOS_5
295 /* RECVIF, but not SUNOS, so BSD */
296 struct sockaddr_dl *sdl;
297 #endif /* SUNOS_5 */
298 /* SUNOS_5 doesn't need a structure to extract ifindex */
299
300 #ifndef SUNOS_5
301 sdl =
302 (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
303 ifindex = sdl->sdl_index;
304 #else /* !SUNOS_5 */
305 ifindex = *(uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
306 #endif /* SUNOS_5 */
307
308 #else /* neither IP_PKTINFO nor IP_RECVIF, broken */
309
310 #warning "getsockopt_ipv4_pktinfo_ifindex: dont have PKTINFO or RECVIF"
311 #warning "things probably will be broken on this platform!"
312 /* XXX why not -1 - this is a failure condition. */
313 ifindex = 0;
314 #endif /* IP_PKTINFO */
315 return ifindex;
316 }
317
318 /* return ifindex, 0 if none found */
319 int
320 getsockopt_ifindex (int af, struct msghdr *msgh)
321 {
322 int ifindex = 0;
323
324 switch (af)
325 {
326 case AF_INET:
327 return (getsockopt_ipv4_ifindex (msgh));
328 break;
329 #ifdef HAVE_IPV6
330 case AF_INET6:
331 return (getsockopt_ipv6_ifindex (msgh));
332 break;
333 #endif
334 default:
335 zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
336 return (ifindex = 0);
337 }
338 }
339
340 /* swab iph between order system uses for IP_HDRINCL and host order */
341 void
342 sockopt_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
355 void
356 sockopt_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 }