]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_sock.c
Merge pull request #531 from qlyoung/fix-stack-ref
[mirror_frr.git] / pimd / pim_sock.c
CommitLineData
12e41d03
DL
1/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
12e41d03
DL
19*/
20
7a2fbbf0 21#include <zebra.h>
12e41d03
DL
22
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <netinet/igmp.h>
27#include <arpa/inet.h>
28#include <unistd.h>
29#include <netdb.h>
30#include <errno.h>
31
12e41d03
DL
32#include "log.h"
33#include "privs.h"
9df99407 34#include "if.h"
61ea3951 35#include "vrf.h"
c5bdb09f 36#include "sockopt.h"
12e41d03
DL
37
38#include "pimd.h"
9df99407 39#include "pim_mroute.h"
12e41d03
DL
40#include "pim_sock.h"
41#include "pim_str.h"
42#include "pim_igmp_join.h"
43
44/* GLOBAL VARS */
12e41d03 45
31c680fc
DS
46int
47pim_socket_raw (int protocol)
12e41d03
DL
48{
49 int fd;
50
51 if ( pimd_privs.change (ZPRIVS_RAISE) )
52 zlog_err ("pim_sockek_raw: could not raise privs, %s",
53 safe_strerror (errno) );
54
55 fd = socket(AF_INET, SOCK_RAW, protocol);
56
57 if ( pimd_privs.change (ZPRIVS_LOWER) )
58 zlog_err ("pim_socket_raw: could not lower privs, %s",
59 safe_strerror (errno) );
60
61 if (fd < 0) {
62 zlog_warn("Could not create raw socket: errno=%d: %s",
63 errno, safe_strerror(errno));
64 return PIM_SOCK_ERR_SOCKET;
65 }
66
67 return fd;
68}
69
31c680fc
DS
70int
71pim_socket_ip_hdr (int fd)
72{
73 const int on = 1;
74 int ret;
75
76 if (pimd_privs.change (ZPRIVS_RAISE))
77 zlog_err ("%s: could not raise privs, %s",
78 __PRETTY_FUNCTION__, safe_strerror (errno));
79
80 ret = setsockopt (fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on));
81
82 if (pimd_privs.change (ZPRIVS_LOWER))
83 zlog_err ("%s: could not lower privs, %s",
84 __PRETTY_FUNCTION__, safe_strerror (errno));
85
86 return ret;
87}
88
728cd663
DS
89/*
90 * Given a socket and a interface,
91 * Bind that socket to that interface
92 */
31c680fc
DS
93int
94pim_socket_bind (int fd, struct interface *ifp)
728cd663 95{
f15bf553
DS
96 int ret = 0;
97#ifdef SO_BINDTODEVICE
728cd663
DS
98
99 if (pimd_privs.change (ZPRIVS_RAISE))
100 zlog_err ("%s: could not raise privs, %s",
101 __PRETTY_FUNCTION__, safe_strerror (errno));
102
103 ret = setsockopt (fd, SOL_SOCKET,
104 SO_BINDTODEVICE, ifp->name, strlen (ifp->name));
105
106 if (pimd_privs.change (ZPRIVS_LOWER))
107 zlog_err ("%s: could not lower privs, %s",
108 __PRETTY_FUNCTION__, safe_strerror (errno));
109
f15bf553 110#endif
728cd663
DS
111 return ret;
112}
113
b35702d0 114int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, u_char loop)
12e41d03 115{
55a645a6 116 int rcvbuf = 1024 * 1024 * 8;
c2403b25 117#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
21391ed8 118 struct ip_mreqn mreq;
c2403b25
DS
119#else
120 struct ip_mreq mreq;
121#endif
12e41d03
DL
122 int fd;
123
124 fd = pim_socket_raw(protocol);
125 if (fd < 0) {
126 zlog_warn("Could not create multicast socket: errno=%d: %s",
127 errno, safe_strerror(errno));
128 return PIM_SOCK_ERR_SOCKET;
129 }
130
6bcd6029 131#ifdef SO_BINDTODEVICE
61ea3951
DS
132 if (protocol == IPPROTO_PIM)
133 {
134 int ret;
61ea3951 135
728cd663 136 ret = pim_socket_bind (fd, ifp);
61ea3951
DS
137 if (ret)
138 {
54026320 139 close (fd);
61ea3951
DS
140 zlog_warn("Could not set fd: %d for interface: %s to device",
141 fd, ifp->name);
142 return PIM_SOCK_ERR_BIND;
143 }
144 }
6bcd6029
DL
145#else
146 /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to
147 * only use 1 socket for all interfaces? */
148#endif
61ea3951 149
12e41d03
DL
150 /* Needed to obtain destination address from recvmsg() */
151 {
152#if defined(HAVE_IP_PKTINFO)
7ec30636 153 /* Linux and Solaris IP_PKTINFO */
12e41d03 154 int opt = 1;
7ec30636 155 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
12e41d03
DL
156 zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
157 fd, errno, safe_strerror(errno));
158 }
159#elif defined(HAVE_IP_RECVDSTADDR)
160 /* BSD IP_RECVDSTADDR */
161 int opt = 1;
162 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
163 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
164 fd, errno, safe_strerror(errno));
165 }
166#else
167 zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
168 __FILE__, __PRETTY_FUNCTION__);
169 close(fd);
170 return PIM_SOCK_ERR_DSTADDR;
171#endif
172 }
173
174
175 /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
176 if (protocol == IPPROTO_IGMP) {
6a5e07e0 177 uint8_t ra[4];
12e41d03
DL
178 ra[0] = 148;
179 ra[1] = 4;
180 ra[2] = 0;
181 ra[3] = 0;
182 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
183 zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
184 fd, errno, safe_strerror(errno));
185 close(fd);
186 return PIM_SOCK_ERR_RA;
187 }
188 }
189
190 {
191 int reuse = 1;
192 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
193 (void *) &reuse, sizeof(reuse))) {
194 zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
195 fd, errno, safe_strerror(errno));
196 close(fd);
197 return PIM_SOCK_ERR_REUSE;
198 }
199 }
200
201 {
202 const int MTTL = 1;
203 int ttl = MTTL;
204 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
205 (void *) &ttl, sizeof(ttl))) {
206 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
207 MTTL, fd, errno, safe_strerror(errno));
208 close(fd);
209 return PIM_SOCK_ERR_TTL;
210 }
211 }
212
c5bdb09f 213 if (setsockopt_ipv4_multicast_loop (fd, loop)) {
12e41d03
DL
214 zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
215 loop ? "enable" : "disable",
216 fd, errno, safe_strerror(errno));
217 close(fd);
218 return PIM_SOCK_ERR_LOOP;
219 }
220
21391ed8 221 memset (&mreq, 0, sizeof (mreq));
c2403b25 222#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
b35702d0 223 mreq.imr_ifindex = ifp->ifindex;
c2403b25 224#else
5fe80c1a
DS
225 /*
226 * I am not sure what to do here yet for *BSD
227 */
228 //mreq.imr_interface = ifindex;
c2403b25
DS
229#endif
230
12e41d03 231 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
21391ed8 232 (void *) &mreq, sizeof(mreq))) {
12e41d03
DL
233 zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
234 fd, errno, safe_strerror(errno));
235 close(fd);
236 return PIM_SOCK_ERR_IFACE;
237 }
238
55a645a6
DS
239 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)))
240 zlog_warn("%s: Failure to set buffer size to %d",
241 __PRETTY_FUNCTION__, rcvbuf);
242
7923d317
DS
243 {
244 long flags;
245
246 flags = fcntl(fd, F_GETFL, 0);
247 if (flags < 0) {
248 zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
249 fd, errno, safe_strerror(errno));
250 close(fd);
251 return PIM_SOCK_ERR_NONBLOCK_GETFL;
12e41d03 252 }
12e41d03 253
7923d317
DS
254 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
255 zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
256 fd, errno, safe_strerror(errno));
257 close(fd);
258 return PIM_SOCK_ERR_NONBLOCK_SETFL;
259 }
260 }
261
12e41d03
DL
262 return fd;
263}
264
265int pim_socket_join(int fd, struct in_addr group,
b892f1dd 266 struct in_addr ifaddr, ifindex_t ifindex)
12e41d03
DL
267{
268 int ret;
269
270#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
271 struct ip_mreqn opt;
272#else
273 struct ip_mreq opt;
274#endif
275
276 opt.imr_multiaddr = group;
277
278#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
279 opt.imr_address = ifaddr;
280 opt.imr_ifindex = ifindex;
281#else
282 opt.imr_interface = ifaddr;
283#endif
284
285 ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
286 if (ret) {
eaa54bdb
DW
287 char group_str[INET_ADDRSTRLEN];
288 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
289 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
290 sprintf(group_str, "<group?>");
291 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
292 sprintf(ifaddr_str, "<ifaddr?>");
293
294 zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
295 fd, group_str, ifaddr_str, errno, safe_strerror(errno));
296 return ret;
297 }
298
299 if (PIM_DEBUG_TRACE) {
eaa54bdb
DW
300 char group_str[INET_ADDRSTRLEN];
301 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
302 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
303 sprintf(group_str, "<group?>");
304 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
305 sprintf(ifaddr_str, "<ifaddr?>");
306
307 zlog_debug("Socket fd=%d joined group %s on interface address %s",
308 fd, group_str, ifaddr_str);
309 }
310
311 return ret;
312}
313
b892f1dd 314int pim_socket_join_source(int fd, ifindex_t ifindex,
12e41d03
DL
315 struct in_addr group_addr,
316 struct in_addr source_addr,
317 const char *ifname)
318{
319 if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
eaa54bdb
DW
320 char group_str[INET_ADDRSTRLEN];
321 char source_str[INET_ADDRSTRLEN];
12e41d03
DL
322 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
323 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
324 zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
325 __PRETTY_FUNCTION__,
326 fd, group_str, source_str, ifindex, ifname,
3d7765d7 327 errno, safe_strerror(errno));
12e41d03
DL
328 return -1;
329 }
330
331 return 0;
332}
333
334int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
335 struct sockaddr_in *from, socklen_t *fromlen,
336 struct sockaddr_in *to, socklen_t *tolen,
b892f1dd 337 ifindex_t *ifindex)
12e41d03
DL
338{
339 struct msghdr msgh;
340 struct cmsghdr *cmsg;
341 struct iovec iov;
342 char cbuf[1000];
343 int err;
344
345 /*
346 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
347 * Use getsockname() to get sin_port.
348 */
349 if (to) {
350 struct sockaddr_in si;
351 socklen_t si_len = sizeof(si);
12e41d03 352
fb5d6e2f
DS
353 memset (&si, 0, sizeof (si));
354 to->sin_family = AF_INET;
355
356 pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len);
357
358 to->sin_port = si.sin_port;
359 to->sin_addr = si.sin_addr;
12e41d03
DL
360
361 if (tolen)
362 *tolen = sizeof(si);
363 }
364
365 memset(&msgh, 0, sizeof(struct msghdr));
366 iov.iov_base = buf;
367 iov.iov_len = len;
368 msgh.msg_control = cbuf;
369 msgh.msg_controllen = sizeof(cbuf);
370 msgh.msg_name = from;
371 msgh.msg_namelen = fromlen ? *fromlen : 0;
372 msgh.msg_iov = &iov;
373 msgh.msg_iovlen = 1;
374 msgh.msg_flags = 0;
375
376 err = recvmsg(fd, &msgh, 0);
377 if (err < 0)
378 return err;
379
380 if (fromlen)
381 *fromlen = msgh.msg_namelen;
382
383 for (cmsg = CMSG_FIRSTHDR(&msgh);
384 cmsg != NULL;
385 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
386
387#ifdef HAVE_IP_PKTINFO
7ec30636 388 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
12e41d03
DL
389 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
390 if (to)
391 ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr;
392 if (tolen)
393 *tolen = sizeof(struct sockaddr_in);
394 if (ifindex)
395 *ifindex = i->ipi_ifindex;
396
12e41d03
DL
397 break;
398 }
399#endif
400
401#ifdef HAVE_IP_RECVDSTADDR
402 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
403 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
404 if (to)
405 ((struct sockaddr_in *) to)->sin_addr = *i;
406 if (tolen)
407 *tolen = sizeof(struct sockaddr_in);
408
12e41d03
DL
409 break;
410 }
411#endif
412
413#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
414 if (cmsg->cmsg_type == IP_RECVIF)
415 if (ifindex)
416 *ifindex = CMSG_IFINDEX(cmsg);
417#endif
418
419 } /* for (cmsg) */
420
421 return err; /* len */
422}
423
424int pim_socket_mcastloop_get(int fd)
425{
426 int loop;
427 socklen_t loop_len = sizeof(loop);
428
429 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
430 &loop, &loop_len)) {
431 int e = errno;
432 zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
433 fd, errno, safe_strerror(errno));
434 errno = e;
435 return PIM_SOCK_ERR_LOOP;
436 }
437
438 return loop;
439}
440
441int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
442{
443 if (getsockname(fd, name, namelen)) {
444 int e = errno;
445 zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
446 fd, errno, safe_strerror(errno));
447 errno = e;
448 return PIM_SOCK_ERR_NAME;
449 }
450
451 return PIM_SOCK_ERR_NONE;
452}