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_socket_mcast(int protocol
, pim_addr ifaddr
, struct interface
*ifp
,
188 fd
= pim_socket_raw(protocol
);
190 zlog_warn("Could not create multicast socket: errno=%d: %s",
191 errno
, safe_strerror(errno
));
192 return PIM_SOCK_ERR_SOCKET
;
195 /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
196 * to emulate behaviour? Or change to only use 1 socket for all
198 ret
= pim_socket_bind(fd
, ifp
);
201 zlog_warn("Could not set fd: %d for interface: %s to device",
203 return PIM_SOCK_ERR_BIND
;
207 sockopt_reuseaddr(fd
);
208 setsockopt_so_recvbuf(fd
, 8 * 1024 * 1024);
210 ret
= pim_setsockopt(protocol
, fd
, ifp
);
212 zlog_warn("pim_setsockopt failed for interface: %s to device ",
217 /* leftover common sockopts */
218 if (setsockopt_multicast_loop(fd
, loop
)) {
220 "Could not %s Multicast Loopback Option on socket fd=%d: %m",
221 loop
? "enable" : "disable", fd
);
223 return PIM_SOCK_ERR_LOOP
;
226 /* Set Tx socket DSCP byte */
227 if (setsockopt_iptos(fd
, IPTOS_PREC_INTERNETCONTROL
))
228 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd
);
233 int pim_socket_join(int fd
, pim_addr group
, pim_addr ifaddr
, ifindex_t ifindex
,
234 struct pim_interface
*pim_ifp
)
239 ret
= setsockopt_ipv4_multicast(fd
, IP_ADD_MEMBERSHIP
, ifaddr
,
240 group
.s_addr
, ifindex
);
242 struct ipv6_mreq opt
;
244 memcpy(&opt
.ipv6mr_multiaddr
, &group
, 16);
245 opt
.ipv6mr_interface
= ifindex
;
246 ret
= setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &opt
, sizeof(opt
));
249 pim_ifp
->igmp_ifstat_joins_sent
++;
254 "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
255 fd
, &group
, &ifaddr
);
256 pim_ifp
->igmp_ifstat_joins_failed
++;
262 "Socket fd=%d joined group %pPAs on interface address %pPAs",
263 fd
, &group
, &ifaddr
);
268 static void cmsg_getdstaddr(struct msghdr
*mh
, struct sockaddr_storage
*dst
,
271 struct cmsghdr
*cmsg
;
272 struct sockaddr_in
*dst4
= (struct sockaddr_in
*)dst
;
274 for (cmsg
= CMSG_FIRSTHDR(mh
); cmsg
!= NULL
;
275 cmsg
= CMSG_NXTHDR(mh
, cmsg
)) {
276 #ifdef HAVE_IP_PKTINFO
277 if ((cmsg
->cmsg_level
== IPPROTO_IP
) &&
278 (cmsg
->cmsg_type
== IP_PKTINFO
)) {
279 struct in_pktinfo
*i
;
281 i
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
283 dst4
->sin_addr
= i
->ipi_addr
;
285 *ifindex
= i
->ipi_ifindex
;
291 #ifdef HAVE_IP_RECVDSTADDR
292 if ((cmsg
->cmsg_level
== IPPROTO_IP
) &&
293 (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
294 struct in_addr
*i
= (struct in_addr
*)CMSG_DATA(cmsg
);
303 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
304 if (cmsg
->cmsg_type
== IP_RECVIF
)
306 *ifindex
= CMSG_IFINDEX(cmsg
);
310 #else /* PIM_IPV != 4 */
311 static void cmsg_getdstaddr(struct msghdr
*mh
, struct sockaddr_storage
*dst
,
314 struct cmsghdr
*cmsg
;
315 struct sockaddr_in6
*dst6
= (struct sockaddr_in6
*)dst
;
317 for (cmsg
= CMSG_FIRSTHDR(mh
); cmsg
!= NULL
;
318 cmsg
= CMSG_NXTHDR(mh
, cmsg
)) {
319 if ((cmsg
->cmsg_level
== IPPROTO_IPV6
) &&
320 (cmsg
->cmsg_type
== IPV6_PKTINFO
)) {
321 struct in6_pktinfo
*i
;
323 i
= (struct in6_pktinfo
*)CMSG_DATA(cmsg
);
326 dst6
->sin6_addr
= i
->ipi6_addr
;
328 *ifindex
= i
->ipi6_ifindex
;
333 #endif /* PIM_IPV != 4 */
335 int pim_socket_recvfromto(int fd
, uint8_t *buf
, size_t len
,
336 struct sockaddr_storage
*from
, socklen_t
*fromlen
,
337 struct sockaddr_storage
*to
, socklen_t
*tolen
,
346 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
347 * Use getsockname() to get sin_port.
350 socklen_t to_len
= sizeof(*to
);
352 pim_socket_getsockname(fd
, (struct sockaddr
*)to
, &to_len
);
355 *tolen
= sizeof(*to
);
358 memset(&msgh
, 0, sizeof(msgh
));
361 msgh
.msg_control
= cbuf
;
362 msgh
.msg_controllen
= sizeof(cbuf
);
363 msgh
.msg_name
= from
;
364 msgh
.msg_namelen
= fromlen
? *fromlen
: 0;
369 err
= recvmsg(fd
, &msgh
, 0);
374 *fromlen
= msgh
.msg_namelen
;
376 cmsg_getdstaddr(&msgh
, to
, ifindex
);
378 return err
; /* len */
381 int pim_socket_getsockname(int fd
, struct sockaddr
*name
, socklen_t
*namelen
)
383 if (getsockname(fd
, name
, namelen
)) {
386 "Could not get Socket Name for socket fd=%d: errno=%d: %s",
387 fd
, errno
, safe_strerror(errno
));
389 return PIM_SOCK_ERR_NAME
;
392 return PIM_SOCK_ERR_NONE
;