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>
36 #include "lib_errors.h"
40 #include "pim_instance.h"
41 #include "pim_mroute.h"
42 #include "pim_iface.h"
47 #define setsockopt_iptos setsockopt_ipv4_tos
48 #define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop
50 #define setsockopt_iptos setsockopt_ipv6_tclass
51 #define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop
54 int pim_socket_raw(int protocol
)
58 frr_with_privs(&pimd_privs
) {
59 fd
= socket(PIM_AF
, SOCK_RAW
, protocol
);
63 zlog_warn("Could not create raw socket: errno=%d: %s", errno
,
64 safe_strerror(errno
));
65 return PIM_SOCK_ERR_SOCKET
;
71 void pim_socket_ip_hdr(int fd
)
73 frr_with_privs(&pimd_privs
) {
77 if (setsockopt(fd
, IPPROTO_IP
, IP_HDRINCL
, &on
, sizeof(on
)))
78 zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
85 * Given a socket and a interface,
86 * Bind that socket to that interface
88 int pim_socket_bind(int fd
, struct interface
*ifp
)
92 #ifdef SO_BINDTODEVICE
93 frr_with_privs(&pimd_privs
) {
94 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, ifp
->name
,
102 static inline int pim_setsockopt(int protocol
, int fd
, struct interface
*ifp
)
107 #if defined(HAVE_IP_PKTINFO)
108 /* Linux and Solaris IP_PKTINFO */
109 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
)))
110 zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd
);
111 #elif defined(HAVE_IP_RECVDSTADDR)
112 /* BSD IP_RECVDSTADDR */
113 if (setsockopt(fd
, IPPROTO_IP
, IP_RECVDSTADDR
, &one
, sizeof(one
)))
114 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m",
119 "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()");
121 return PIM_SOCK_ERR_DSTADDR
;
124 /* Set router alert (RFC 2113) for all IGMP messages (RFC
125 * 3376 4. Message Formats)*/
126 if (protocol
== IPPROTO_IGMP
) {
133 if (setsockopt(fd
, IPPROTO_IP
, IP_OPTIONS
, ra
, 4)) {
135 "Could not set Router Alert Option on socket fd=%d: %m",
138 return PIM_SOCK_ERR_RA
;
142 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
))) {
143 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m",
146 return PIM_SOCK_ERR_TTL
;
149 if (setsockopt_ipv4_multicast_if(fd
, PIMADDR_ANY
, ifp
->ifindex
)) {
151 "Could not set Outgoing Interface Option on socket fd=%d: %m",
154 return PIM_SOCK_ERR_IFACE
;
159 #else /* PIM_IPV != 4 */
160 static inline int pim_setsockopt(int protocol
, int fd
, struct interface
*ifp
)
163 struct ipv6_mreq mreq
= {};
165 setsockopt_ipv6_pktinfo(fd
, 1);
166 setsockopt_ipv6_multicast_hops(fd
, ttl
);
168 mreq
.ipv6mr_interface
= ifp
->ifindex
;
169 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, &mreq
,
172 "Could not set Outgoing Interface Option on socket fd=%d: %m",
175 return PIM_SOCK_ERR_IFACE
;
182 int pim_reg_sock(void)
187 frr_with_privs (&pimd_privs
) {
188 fd
= socket(PIM_AF
, SOCK_RAW
, PIM_PROTO_REG
);
192 zlog_warn("Could not create raw socket: errno=%d: %s", errno
,
193 safe_strerror(errno
));
194 return PIM_SOCK_ERR_SOCKET
;
197 if (sockopt_reuseaddr(fd
)) {
199 return PIM_SOCK_ERR_REUSE
;
202 flags
= fcntl(fd
, F_GETFL
, 0);
205 "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
206 fd
, errno
, safe_strerror(errno
));
208 return PIM_SOCK_ERR_NONBLOCK_GETFL
;
211 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
213 "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
214 fd
, errno
, safe_strerror(errno
));
216 return PIM_SOCK_ERR_NONBLOCK_SETFL
;
222 int pim_socket_mcast(int protocol
, pim_addr ifaddr
, struct interface
*ifp
,
228 fd
= pim_socket_raw(protocol
);
230 zlog_warn("Could not create multicast socket: errno=%d: %s",
231 errno
, safe_strerror(errno
));
232 return PIM_SOCK_ERR_SOCKET
;
235 /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
236 * to emulate behaviour? Or change to only use 1 socket for all
238 ret
= pim_socket_bind(fd
, ifp
);
241 zlog_warn("Could not set fd: %d for interface: %s to device",
243 return PIM_SOCK_ERR_BIND
;
247 sockopt_reuseaddr(fd
);
248 setsockopt_so_recvbuf(fd
, 8 * 1024 * 1024);
250 ret
= pim_setsockopt(protocol
, fd
, ifp
);
252 zlog_warn("pim_setsockopt failed for interface: %s to device ",
257 /* leftover common sockopts */
258 if (setsockopt_multicast_loop(fd
, loop
)) {
260 "Could not %s Multicast Loopback Option on socket fd=%d: %m",
261 loop
? "enable" : "disable", fd
);
263 return PIM_SOCK_ERR_LOOP
;
266 /* Set Tx socket DSCP byte */
267 if (setsockopt_iptos(fd
, IPTOS_PREC_INTERNETCONTROL
))
268 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd
);
273 int pim_socket_join(int fd
, pim_addr group
, pim_addr ifaddr
, ifindex_t ifindex
,
274 struct pim_interface
*pim_ifp
)
279 ret
= setsockopt_ipv4_multicast(fd
, IP_ADD_MEMBERSHIP
, ifaddr
,
280 group
.s_addr
, ifindex
);
282 struct ipv6_mreq opt
;
284 memcpy(&opt
.ipv6mr_multiaddr
, &group
, 16);
285 opt
.ipv6mr_interface
= ifindex
;
286 ret
= setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &opt
, sizeof(opt
));
289 pim_ifp
->igmp_ifstat_joins_sent
++;
294 "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
295 fd
, &group
, &ifaddr
);
296 pim_ifp
->igmp_ifstat_joins_failed
++;
302 "Socket fd=%d joined group %pPAs on interface address %pPAs",
303 fd
, &group
, &ifaddr
);
308 static void cmsg_getdstaddr(struct msghdr
*mh
, struct sockaddr_storage
*dst
,
311 struct cmsghdr
*cmsg
;
312 struct sockaddr_in
*dst4
= (struct sockaddr_in
*)dst
;
314 for (cmsg
= CMSG_FIRSTHDR(mh
); cmsg
!= NULL
;
315 cmsg
= CMSG_NXTHDR(mh
, cmsg
)) {
316 #ifdef HAVE_IP_PKTINFO
317 if ((cmsg
->cmsg_level
== IPPROTO_IP
) &&
318 (cmsg
->cmsg_type
== IP_PKTINFO
)) {
319 struct in_pktinfo
*i
;
321 i
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
323 dst4
->sin_addr
= i
->ipi_addr
;
325 *ifindex
= i
->ipi_ifindex
;
331 #ifdef HAVE_IP_RECVDSTADDR
332 if ((cmsg
->cmsg_level
== IPPROTO_IP
) &&
333 (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
334 struct in_addr
*i
= (struct in_addr
*)CMSG_DATA(cmsg
);
343 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
344 if (cmsg
->cmsg_type
== IP_RECVIF
)
346 *ifindex
= CMSG_IFINDEX(cmsg
);
350 #else /* PIM_IPV != 4 */
351 static void cmsg_getdstaddr(struct msghdr
*mh
, struct sockaddr_storage
*dst
,
354 struct cmsghdr
*cmsg
;
355 struct sockaddr_in6
*dst6
= (struct sockaddr_in6
*)dst
;
357 for (cmsg
= CMSG_FIRSTHDR(mh
); cmsg
!= NULL
;
358 cmsg
= CMSG_NXTHDR(mh
, cmsg
)) {
359 if ((cmsg
->cmsg_level
== IPPROTO_IPV6
) &&
360 (cmsg
->cmsg_type
== IPV6_PKTINFO
)) {
361 struct in6_pktinfo
*i
;
363 i
= (struct in6_pktinfo
*)CMSG_DATA(cmsg
);
366 dst6
->sin6_addr
= i
->ipi6_addr
;
368 *ifindex
= i
->ipi6_ifindex
;
373 #endif /* PIM_IPV != 4 */
375 int pim_socket_recvfromto(int fd
, uint8_t *buf
, size_t len
,
376 struct sockaddr_storage
*from
, socklen_t
*fromlen
,
377 struct sockaddr_storage
*to
, socklen_t
*tolen
,
386 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
387 * Use getsockname() to get sin_port.
390 socklen_t to_len
= sizeof(*to
);
392 pim_socket_getsockname(fd
, (struct sockaddr
*)to
, &to_len
);
395 *tolen
= sizeof(*to
);
398 memset(&msgh
, 0, sizeof(msgh
));
401 msgh
.msg_control
= cbuf
;
402 msgh
.msg_controllen
= sizeof(cbuf
);
403 msgh
.msg_name
= from
;
404 msgh
.msg_namelen
= fromlen
? *fromlen
: 0;
409 err
= recvmsg(fd
, &msgh
, 0);
414 *fromlen
= msgh
.msg_namelen
;
416 cmsg_getdstaddr(&msgh
, to
, ifindex
);
418 return err
; /* len */
421 int pim_socket_getsockname(int fd
, struct sockaddr
*name
, socklen_t
*namelen
)
423 if (getsockname(fd
, name
, namelen
)) {
426 "Could not get Socket Name for socket fd=%d: errno=%d: %s",
427 fd
, errno
, safe_strerror(errno
));
429 return PIM_SOCK_ERR_NAME
;
432 return PIM_SOCK_ERR_NONE
;