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"
46 pim_socket_raw (int protocol
)
50 if ( pimd_privs
.change (ZPRIVS_RAISE
) )
51 zlog_err ("pim_sockek_raw: could not raise privs, %s",
52 safe_strerror (errno
) );
54 fd
= socket(AF_INET
, SOCK_RAW
, protocol
);
56 if ( pimd_privs
.change (ZPRIVS_LOWER
) )
57 zlog_err ("pim_socket_raw: could not lower privs, %s",
58 safe_strerror (errno
) );
61 zlog_warn("Could not create raw socket: errno=%d: %s",
62 errno
, safe_strerror(errno
));
63 return PIM_SOCK_ERR_SOCKET
;
70 pim_socket_ip_hdr (int fd
)
75 if (pimd_privs
.change (ZPRIVS_RAISE
))
76 zlog_err ("%s: could not raise privs, %s",
77 __PRETTY_FUNCTION__
, safe_strerror (errno
));
79 ret
= setsockopt (fd
, IPPROTO_IP
, IP_HDRINCL
, &on
, sizeof (on
));
81 if (pimd_privs
.change (ZPRIVS_LOWER
))
82 zlog_err ("%s: could not lower privs, %s",
83 __PRETTY_FUNCTION__
, safe_strerror (errno
));
89 * Given a socket and a interface,
90 * Bind that socket to that interface
93 pim_socket_bind (int fd
, struct interface
*ifp
)
96 #ifdef SO_BINDTODEVICE
98 if (pimd_privs
.change (ZPRIVS_RAISE
))
99 zlog_err ("%s: could not raise privs, %s",
100 __PRETTY_FUNCTION__
, safe_strerror (errno
));
102 ret
= setsockopt (fd
, SOL_SOCKET
,
103 SO_BINDTODEVICE
, ifp
->name
, strlen (ifp
->name
));
105 if (pimd_privs
.change (ZPRIVS_LOWER
))
106 zlog_err ("%s: could not lower privs, %s",
107 __PRETTY_FUNCTION__
, safe_strerror (errno
));
113 int pim_socket_mcast(int protocol
, struct in_addr ifaddr
, struct interface
*ifp
, u_char loop
)
115 int rcvbuf
= 1024 * 1024 * 8;
116 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
117 struct ip_mreqn mreq
;
123 fd
= pim_socket_raw(protocol
);
125 zlog_warn("Could not create multicast socket: errno=%d: %s",
126 errno
, safe_strerror(errno
));
127 return PIM_SOCK_ERR_SOCKET
;
130 #ifdef SO_BINDTODEVICE
131 if (protocol
== IPPROTO_PIM
)
135 ret
= pim_socket_bind (fd
, ifp
);
139 zlog_warn("Could not set fd: %d for interface: %s to device",
141 return PIM_SOCK_ERR_BIND
;
145 /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to
146 * only use 1 socket for all interfaces? */
149 /* Needed to obtain destination address from recvmsg() */
151 #if defined(HAVE_IP_PKTINFO)
152 /* Linux and Solaris IP_PKTINFO */
154 if (setsockopt(fd
, IPPROTO_IP
, IP_PKTINFO
, &opt
, sizeof(opt
))) {
155 zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
156 fd
, errno
, safe_strerror(errno
));
158 #elif defined(HAVE_IP_RECVDSTADDR)
159 /* BSD IP_RECVDSTADDR */
161 if (setsockopt(fd
, IPPROTO_IP
, IP_RECVDSTADDR
, &opt
, sizeof(opt
))) {
162 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
163 fd
, errno
, safe_strerror(errno
));
166 zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
167 __FILE__
, __PRETTY_FUNCTION__
);
169 return PIM_SOCK_ERR_DSTADDR
;
174 /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
175 if (protocol
== IPPROTO_IGMP
) {
181 if (setsockopt(fd
, IPPROTO_IP
, IP_OPTIONS
, ra
, 4)) {
182 zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
183 fd
, errno
, safe_strerror(errno
));
185 return PIM_SOCK_ERR_RA
;
191 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
,
192 (void *) &reuse
, sizeof(reuse
))) {
193 zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
194 fd
, errno
, safe_strerror(errno
));
196 return PIM_SOCK_ERR_REUSE
;
203 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_TTL
,
204 (void *) &ttl
, sizeof(ttl
))) {
205 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
206 MTTL
, fd
, errno
, safe_strerror(errno
));
208 return PIM_SOCK_ERR_TTL
;
212 if (setsockopt_ipv4_multicast_loop (fd
, loop
)) {
213 zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
214 loop
? "enable" : "disable",
215 fd
, errno
, safe_strerror(errno
));
217 return PIM_SOCK_ERR_LOOP
;
220 memset (&mreq
, 0, sizeof (mreq
));
221 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
222 mreq
.imr_ifindex
= ifp
->ifindex
;
225 * I am not sure what to do here yet for *BSD
227 //mreq.imr_interface = ifindex;
230 if (setsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_IF
,
231 (void *) &mreq
, sizeof(mreq
))) {
232 zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
233 fd
, errno
, safe_strerror(errno
));
235 return PIM_SOCK_ERR_IFACE
;
238 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
, sizeof(rcvbuf
)))
239 zlog_warn("%s: Failure to set buffer size to %d",
240 __PRETTY_FUNCTION__
, rcvbuf
);
245 flags
= fcntl(fd
, F_GETFL
, 0);
247 zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
248 fd
, errno
, safe_strerror(errno
));
250 return PIM_SOCK_ERR_NONBLOCK_GETFL
;
253 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
254 zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
255 fd
, errno
, safe_strerror(errno
));
257 return PIM_SOCK_ERR_NONBLOCK_SETFL
;
264 int pim_socket_join(int fd
, struct in_addr group
,
265 struct in_addr ifaddr
, ifindex_t ifindex
)
269 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
275 opt
.imr_multiaddr
= group
;
277 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
278 opt
.imr_address
= ifaddr
;
279 opt
.imr_ifindex
= ifindex
;
281 opt
.imr_interface
= ifaddr
;
284 ret
= setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &opt
, sizeof(opt
));
286 char group_str
[INET_ADDRSTRLEN
];
287 char ifaddr_str
[INET_ADDRSTRLEN
];
288 if (!inet_ntop(AF_INET
, &group
, group_str
, sizeof(group_str
)))
289 sprintf(group_str
, "<group?>");
290 if (!inet_ntop(AF_INET
, &ifaddr
, ifaddr_str
, sizeof(ifaddr_str
)))
291 sprintf(ifaddr_str
, "<ifaddr?>");
293 zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
294 fd
, group_str
, ifaddr_str
, errno
, safe_strerror(errno
));
298 if (PIM_DEBUG_TRACE
) {
299 char group_str
[INET_ADDRSTRLEN
];
300 char ifaddr_str
[INET_ADDRSTRLEN
];
301 if (!inet_ntop(AF_INET
, &group
, group_str
, sizeof(group_str
)))
302 sprintf(group_str
, "<group?>");
303 if (!inet_ntop(AF_INET
, &ifaddr
, ifaddr_str
, sizeof(ifaddr_str
)))
304 sprintf(ifaddr_str
, "<ifaddr?>");
306 zlog_debug("Socket fd=%d joined group %s on interface address %s",
307 fd
, group_str
, ifaddr_str
);
313 int pim_socket_join_source(int fd
, ifindex_t ifindex
,
314 struct in_addr group_addr
,
315 struct in_addr source_addr
,
318 if (pim_igmp_join_source(fd
, ifindex
, group_addr
, source_addr
)) {
319 char group_str
[INET_ADDRSTRLEN
];
320 char source_str
[INET_ADDRSTRLEN
];
321 pim_inet4_dump("<grp?>", group_addr
, group_str
, sizeof(group_str
));
322 pim_inet4_dump("<src?>", source_addr
, source_str
, sizeof(source_str
));
323 zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
325 fd
, group_str
, source_str
, ifindex
, ifname
,
326 errno
, safe_strerror(errno
));
333 int pim_socket_recvfromto(int fd
, uint8_t *buf
, size_t len
,
334 struct sockaddr_in
*from
, socklen_t
*fromlen
,
335 struct sockaddr_in
*to
, socklen_t
*tolen
,
339 struct cmsghdr
*cmsg
;
345 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
346 * Use getsockname() to get sin_port.
349 struct sockaddr_in si
;
350 socklen_t si_len
= sizeof(si
);
352 memset (&si
, 0, sizeof (si
));
353 to
->sin_family
= AF_INET
;
355 pim_socket_getsockname(fd
, (struct sockaddr
*) &si
, &si_len
);
357 to
->sin_port
= si
.sin_port
;
358 to
->sin_addr
= si
.sin_addr
;
364 memset(&msgh
, 0, sizeof(struct msghdr
));
367 msgh
.msg_control
= cbuf
;
368 msgh
.msg_controllen
= sizeof(cbuf
);
369 msgh
.msg_name
= from
;
370 msgh
.msg_namelen
= fromlen
? *fromlen
: 0;
375 err
= recvmsg(fd
, &msgh
, 0);
380 *fromlen
= msgh
.msg_namelen
;
382 for (cmsg
= CMSG_FIRSTHDR(&msgh
);
384 cmsg
= CMSG_NXTHDR(&msgh
,cmsg
)) {
386 #ifdef HAVE_IP_PKTINFO
387 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_PKTINFO
)) {
388 struct in_pktinfo
*i
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
390 ((struct sockaddr_in
*) to
)->sin_addr
= i
->ipi_addr
;
392 *tolen
= sizeof(struct sockaddr_in
);
394 *ifindex
= i
->ipi_ifindex
;
400 #ifdef HAVE_IP_RECVDSTADDR
401 if ((cmsg
->cmsg_level
== IPPROTO_IP
) && (cmsg
->cmsg_type
== IP_RECVDSTADDR
)) {
402 struct in_addr
*i
= (struct in_addr
*) CMSG_DATA(cmsg
);
404 ((struct sockaddr_in
*) to
)->sin_addr
= *i
;
406 *tolen
= sizeof(struct sockaddr_in
);
412 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
413 if (cmsg
->cmsg_type
== IP_RECVIF
)
415 *ifindex
= CMSG_IFINDEX(cmsg
);
420 return err
; /* len */
423 int pim_socket_mcastloop_get(int fd
)
426 socklen_t loop_len
= sizeof(loop
);
428 if (getsockopt(fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
,
431 zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
432 fd
, errno
, safe_strerror(errno
));
434 return PIM_SOCK_ERR_LOOP
;
440 int pim_socket_getsockname(int fd
, struct sockaddr
*name
, socklen_t
*namelen
)
442 if (getsockname(fd
, name
, namelen
)) {
444 zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
445 fd
, errno
, safe_strerror(errno
));
447 return PIM_SOCK_ERR_NAME
;
450 return PIM_SOCK_ERR_NONE
;