3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <netinet/igmp.h>
26 #include <arpa/inet.h>
38 #include "pim_mroute.h"
41 #include "pim_igmp_join.h"
45 int pim_socket_raw(int protocol
)
49 if (pimd_privs
.change(ZPRIVS_RAISE
))
50 zlog_err("pim_sockek_raw: could not raise privs, %s",
51 safe_strerror(errno
));
53 fd
= socket(AF_INET
, SOCK_RAW
, protocol
);
55 if (pimd_privs
.change(ZPRIVS_LOWER
))
56 zlog_err("pim_socket_raw: could not lower privs, %s",
57 safe_strerror(errno
));
60 zlog_warn("Could not create raw socket: errno=%d: %s", errno
,
61 safe_strerror(errno
));
62 return PIM_SOCK_ERR_SOCKET
;
68 int pim_socket_ip_hdr(int fd
)
73 if (pimd_privs
.change(ZPRIVS_RAISE
))
74 zlog_err("%s: could not raise privs, %s", __PRETTY_FUNCTION__
,
75 safe_strerror(errno
));
77 ret
= setsockopt(fd
, IPPROTO_IP
, IP_HDRINCL
, &on
, sizeof(on
));
79 if (pimd_privs
.change(ZPRIVS_LOWER
))
80 zlog_err("%s: could not lower privs, %s", __PRETTY_FUNCTION__
,
81 safe_strerror(errno
));
87 * Given a socket and a interface,
88 * Bind that socket to that interface
90 int pim_socket_bind(int fd
, struct interface
*ifp
)
93 #ifdef SO_BINDTODEVICE
95 if (pimd_privs
.change(ZPRIVS_RAISE
))
96 zlog_err("%s: could not raise privs, %s", __PRETTY_FUNCTION__
,
97 safe_strerror(errno
));
99 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifp
->name
,
102 if (pimd_privs
.change(ZPRIVS_LOWER
))
103 zlog_err("%s: could not lower privs, %s", __PRETTY_FUNCTION__
,
104 safe_strerror(errno
));
110 int pim_socket_mcast(int protocol
, struct in_addr ifaddr
, struct interface
*ifp
,
113 int rcvbuf
= 1024 * 1024 * 8;
114 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
115 struct ip_mreqn mreq
;
121 fd
= pim_socket_raw(protocol
);
123 zlog_warn("Could not create multicast socket: errno=%d: %s",
124 errno
, safe_strerror(errno
));
125 return PIM_SOCK_ERR_SOCKET
;
128 #ifdef SO_BINDTODEVICE
129 if (protocol
== IPPROTO_PIM
) {
132 ret
= pim_socket_bind(fd
, ifp
);
136 "Could not set fd: %d for interface: %s to device",
138 return PIM_SOCK_ERR_BIND
;
142 /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to
143 * only use 1 socket for all interfaces? */
146 /* Needed to obtain destination address from recvmsg() */
148 #if defined(HAVE_IP_PKTINFO)
149 /* Linux and Solaris IP_PKTINFO */
151 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &opt
, sizeof(opt
))) {
153 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
154 fd
, errno
, safe_strerror(errno
));
156 #elif defined(HAVE_IP_RECVDSTADDR)
157 /* BSD IP_RECVDSTADDR */
159 if (setsockopt(fd
, IPPROTO_IP
, IP_RECVDSTADDR
, &opt
,
162 "Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
163 fd
, errno
, safe_strerror(errno
));
167 "%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
168 __FILE__
, __PRETTY_FUNCTION__
);
170 return PIM_SOCK_ERR_DSTADDR
;
175 /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4.
177 if (protocol
== IPPROTO_IGMP
) {
183 if (setsockopt(fd
, IPPROTO_IP
, IP_OPTIONS
, ra
, 4)) {
185 "Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
186 fd
, errno
, safe_strerror(errno
));
188 return PIM_SOCK_ERR_RA
;
194 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&reuse
,
197 "Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
198 fd
, errno
, safe_strerror(errno
));
200 return PIM_SOCK_ERR_REUSE
;
207 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, (void *)&ttl
,
210 "Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
211 MTTL
, fd
, errno
, safe_strerror(errno
));
213 return PIM_SOCK_ERR_TTL
;
217 if (setsockopt_ipv4_multicast_loop(fd
, loop
)) {
219 "Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
220 loop
? "enable" : "disable", fd
, errno
,
221 safe_strerror(errno
));
223 return PIM_SOCK_ERR_LOOP
;
226 memset(&mreq
, 0, sizeof(mreq
));
227 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
228 mreq
.imr_ifindex
= ifp
->ifindex
;
231 * I am not sure what to do here yet for *BSD
233 // mreq.imr_interface = ifindex;
236 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&mreq
,
239 "Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
240 fd
, errno
, safe_strerror(errno
));
242 return PIM_SOCK_ERR_IFACE
;
245 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
, sizeof(rcvbuf
)))
246 zlog_warn("%s: Failure to set buffer size to %d",
247 __PRETTY_FUNCTION__
, rcvbuf
);
252 flags
= fcntl(fd
, F_GETFL
, 0);
255 "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
256 fd
, errno
, safe_strerror(errno
));
258 return PIM_SOCK_ERR_NONBLOCK_GETFL
;
261 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
263 "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
264 fd
, errno
, safe_strerror(errno
));
266 return PIM_SOCK_ERR_NONBLOCK_SETFL
;
273 int pim_socket_join(int fd
, struct in_addr group
, struct in_addr ifaddr
,
278 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
284 opt
.imr_multiaddr
= group
;
286 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
287 opt
.imr_address
= ifaddr
;
288 opt
.imr_ifindex
= ifindex
;
290 opt
.imr_interface
= ifaddr
;
293 ret
= setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &opt
, sizeof(opt
));
295 char group_str
[INET_ADDRSTRLEN
];
296 char ifaddr_str
[INET_ADDRSTRLEN
];
297 if (!inet_ntop(AF_INET
, &group
, group_str
, sizeof(group_str
)))
298 sprintf(group_str
, "<group?>");
299 if (!inet_ntop(AF_INET
, &ifaddr
, ifaddr_str
,
301 sprintf(ifaddr_str
, "<ifaddr?>");
304 "Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
305 fd
, group_str
, ifaddr_str
, errno
, safe_strerror(errno
));
309 if (PIM_DEBUG_TRACE
) {
310 char group_str
[INET_ADDRSTRLEN
];
311 char ifaddr_str
[INET_ADDRSTRLEN
];
312 if (!inet_ntop(AF_INET
, &group
, group_str
, sizeof(group_str
)))
313 sprintf(group_str
, "<group?>");
314 if (!inet_ntop(AF_INET
, &ifaddr
, ifaddr_str
,
316 sprintf(ifaddr_str
, "<ifaddr?>");
319 "Socket fd=%d joined group %s on interface address %s",
320 fd
, group_str
, ifaddr_str
);
326 int pim_socket_join_source(int fd
, ifindex_t ifindex
, struct in_addr group_addr
,
327 struct in_addr source_addr
, const char *ifname
)
329 if (pim_igmp_join_source(fd
, ifindex
, group_addr
, source_addr
)) {
330 char group_str
[INET_ADDRSTRLEN
];
331 char source_str
[INET_ADDRSTRLEN
];
332 pim_inet4_dump("<grp?>", group_addr
, group_str
,
334 pim_inet4_dump("<src?>", source_addr
, source_str
,
337 "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
338 __PRETTY_FUNCTION__
, fd
, group_str
, source_str
, ifindex
,
339 ifname
, errno
, safe_strerror(errno
));
346 int pim_socket_recvfromto(int fd
, uint8_t *buf
, size_t len
,
347 struct sockaddr_in
*from
, socklen_t
*fromlen
,
348 struct sockaddr_in
*to
, socklen_t
*tolen
,
352 struct cmsghdr
*cmsg
;
358 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
359 * Use getsockname() to get sin_port.
362 struct sockaddr_in si
;
363 socklen_t si_len
= sizeof(si
);
365 memset(&si
, 0, sizeof(si
));
366 to
->sin_family
= AF_INET
;
368 pim_socket_getsockname(fd
, (struct sockaddr
*)&si
, &si_len
);
370 to
->sin_port
= si
.sin_port
;
371 to
->sin_addr
= si
.sin_addr
;
377 memset(&msgh
, 0, sizeof(struct msghdr
));
380 msgh
.msg_control
= cbuf
;
381 msgh
.msg_controllen
= sizeof(cbuf
);
382 msgh
.msg_name
= from
;
383 msgh
.msg_namelen
= fromlen
? *fromlen
: 0;
388 err
= recvmsg(fd
, &msgh
, 0);
393 *fromlen
= msgh
.msg_namelen
;
395 for (cmsg
= CMSG_FIRSTHDR(&msgh
); cmsg
!= NULL
;
396 cmsg
= CMSG_NXTHDR(&msgh
, cmsg
)) {
398 #ifdef HAVE_IP_PKTINFO
399 if ((cmsg
->cmsg_level
== IPPROTO_IP
)
400 && (cmsg
->cmsg_type
== IP_PKTINFO
)) {
401 struct in_pktinfo
*i
=
402 (struct in_pktinfo
*)CMSG_DATA(cmsg
);
404 ((struct sockaddr_in
*)to
)->sin_addr
=
407 *tolen
= sizeof(struct sockaddr_in
);
409 *ifindex
= i
->ipi_ifindex
;
415 #ifdef HAVE_IP_RECVDSTADDR
416 if ((cmsg
->cmsg_level
== IPPROTO_IP
)
417 && (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
418 struct in_addr
*i
= (struct in_addr
*)CMSG_DATA(cmsg
);
420 ((struct sockaddr_in
*)to
)->sin_addr
= *i
;
422 *tolen
= sizeof(struct sockaddr_in
);
428 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
429 if (cmsg
->cmsg_type
== IP_RECVIF
)
431 *ifindex
= CMSG_IFINDEX(cmsg
);
436 return err
; /* len */
439 int pim_socket_mcastloop_get(int fd
)
442 socklen_t loop_len
= sizeof(loop
);
444 if (getsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &loop
, &loop_len
)) {
447 "Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
448 fd
, errno
, safe_strerror(errno
));
450 return PIM_SOCK_ERR_LOOP
;
456 int pim_socket_getsockname(int fd
, struct sockaddr
*name
, socklen_t
*namelen
)
458 if (getsockname(fd
, name
, namelen
)) {
461 "Could not get Socket Name for socket fd=%d: errno=%d: %s",
462 fd
, errno
, safe_strerror(errno
));
464 return PIM_SOCK_ERR_NAME
;
467 return PIM_SOCK_ERR_NONE
;