mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 21:46:33 +00:00 
			
		
		
		
	 772270f3b6
			
		
	
	
		772270f3b6
		
	
	
	
	
		
			
			Replace sprintf with snprintf where straightforward to do so. - sprintf's into local scope buffers of known size are replaced with the equivalent snprintf call - snprintf's into local scope buffers of known size that use the buffer size expression now use sizeof(buffer) - sprintf(buf + strlen(buf), ...) replaced with snprintf() into temp buffer followed by strlcat Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
		
			
				
	
	
		
			442 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * PIM for Quagga
 | |
|  * Copyright (C) 2008  Everton da Silva Marques
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <zebra.h>
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <netinet/igmp.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <unistd.h>
 | |
| #include <netdb.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include "log.h"
 | |
| #include "privs.h"
 | |
| #include "if.h"
 | |
| #include "vrf.h"
 | |
| #include "sockopt.h"
 | |
| #include "lib_errors.h"
 | |
| 
 | |
| #include "pimd.h"
 | |
| #include "pim_mroute.h"
 | |
| #include "pim_sock.h"
 | |
| #include "pim_str.h"
 | |
| 
 | |
| /* GLOBAL VARS */
 | |
| 
 | |
| int pim_socket_raw(int protocol)
 | |
| {
 | |
| 	int fd;
 | |
| 
 | |
| 	frr_with_privs(&pimd_privs) {
 | |
| 
 | |
| 		fd = socket(AF_INET, SOCK_RAW, protocol);
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if (fd < 0) {
 | |
| 		zlog_warn("Could not create raw socket: errno=%d: %s", errno,
 | |
| 			  safe_strerror(errno));
 | |
| 		return PIM_SOCK_ERR_SOCKET;
 | |
| 	}
 | |
| 
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| void pim_socket_ip_hdr(int fd)
 | |
| {
 | |
| 	const int on = 1;
 | |
| 
 | |
| 	frr_with_privs(&pimd_privs) {
 | |
| 
 | |
| 		if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))
 | |
| 			zlog_err("%s: Could not turn on IP_HDRINCL option: %s",
 | |
| 				 __func__, safe_strerror(errno));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given a socket and a interface,
 | |
|  * Bind that socket to that interface
 | |
|  */
 | |
| int pim_socket_bind(int fd, struct interface *ifp)
 | |
| {
 | |
| 	int ret = 0;
 | |
| #ifdef SO_BINDTODEVICE
 | |
| 
 | |
| 	frr_with_privs(&pimd_privs) {
 | |
| 
 | |
| 		ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
 | |
| 				 strlen(ifp->name));
 | |
| 
 | |
| 	}
 | |
| 
 | |
| #endif
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp,
 | |
| 		     uint8_t loop)
 | |
| {
 | |
| 	int rcvbuf = 1024 * 1024 * 8;
 | |
| #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 | |
| 	struct ip_mreqn mreq;
 | |
| #else
 | |
| 	struct ip_mreq mreq;
 | |
| #endif
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = pim_socket_raw(protocol);
 | |
| 	if (fd < 0) {
 | |
| 		zlog_warn("Could not create multicast socket: errno=%d: %s",
 | |
| 			  errno, safe_strerror(errno));
 | |
| 		return PIM_SOCK_ERR_SOCKET;
 | |
| 	}
 | |
| 
 | |
| #ifdef SO_BINDTODEVICE
 | |
| 	if (protocol == IPPROTO_PIM) {
 | |
| 		int ret;
 | |
| 
 | |
| 		ret = pim_socket_bind(fd, ifp);
 | |
| 		if (ret) {
 | |
| 			close(fd);
 | |
| 			zlog_warn(
 | |
| 				"Could not set fd: %d for interface: %s to device",
 | |
| 				fd, ifp->name);
 | |
| 			return PIM_SOCK_ERR_BIND;
 | |
| 		}
 | |
| 	}
 | |
| #else
 | |
| /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour?  Or change to
 | |
|  * only use 1 socket for all interfaces? */
 | |
| #endif
 | |
| 
 | |
| 	/* Needed to obtain destination address from recvmsg() */
 | |
| 	{
 | |
| #if defined(HAVE_IP_PKTINFO)
 | |
| 		/* Linux and Solaris IP_PKTINFO */
 | |
| 		int opt = 1;
 | |
| 		if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 		}
 | |
| #elif defined(HAVE_IP_RECVDSTADDR)
 | |
| 		/* BSD IP_RECVDSTADDR */
 | |
| 		int opt = 1;
 | |
| 		if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt,
 | |
| 			       sizeof(opt))) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 		}
 | |
| #else
 | |
| 		flog_err(
 | |
| 			EC_LIB_DEVELOPMENT,
 | |
| 			"%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
 | |
| 			__FILE__, __func__);
 | |
| 		close(fd);
 | |
| 		return PIM_SOCK_ERR_DSTADDR;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4.
 | |
| 	 * Message Formats)*/
 | |
| 	if (protocol == IPPROTO_IGMP) {
 | |
| 		uint8_t ra[4];
 | |
| 		ra[0] = 148;
 | |
| 		ra[1] = 4;
 | |
| 		ra[2] = 0;
 | |
| 		ra[3] = 0;
 | |
| 		if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 			close(fd);
 | |
| 			return PIM_SOCK_ERR_RA;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		int reuse = 1;
 | |
| 		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse,
 | |
| 			       sizeof(reuse))) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 			close(fd);
 | |
| 			return PIM_SOCK_ERR_REUSE;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		const int MTTL = 1;
 | |
| 		int ttl = MTTL;
 | |
| 		if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl,
 | |
| 			       sizeof(ttl))) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
 | |
| 				MTTL, fd, errno, safe_strerror(errno));
 | |
| 			close(fd);
 | |
| 			return PIM_SOCK_ERR_TTL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (setsockopt_ipv4_multicast_loop(fd, loop)) {
 | |
| 		zlog_warn(
 | |
| 			"Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
 | |
| 			loop ? "enable" : "disable", fd, errno,
 | |
| 			safe_strerror(errno));
 | |
| 		close(fd);
 | |
| 		return PIM_SOCK_ERR_LOOP;
 | |
| 	}
 | |
| 
 | |
| 	memset(&mreq, 0, sizeof(mreq));
 | |
| #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 | |
| 	mreq.imr_ifindex = ifp->ifindex;
 | |
| #else
 | |
| /*
 | |
|  * I am not sure what to do here yet for *BSD
 | |
|  */
 | |
| // mreq.imr_interface = ifindex;
 | |
| #endif
 | |
| 
 | |
| 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreq,
 | |
| 		       sizeof(mreq))) {
 | |
| 		zlog_warn(
 | |
| 			"Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
 | |
| 			fd, errno, safe_strerror(errno));
 | |
| 		close(fd);
 | |
| 		return PIM_SOCK_ERR_IFACE;
 | |
| 	}
 | |
| 
 | |
| 	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)))
 | |
| 		zlog_warn("%s: Failure to set buffer size to %d", __func__,
 | |
| 			  rcvbuf);
 | |
| 
 | |
| 	{
 | |
| 		long flags;
 | |
| 
 | |
| 		flags = fcntl(fd, F_GETFL, 0);
 | |
| 		if (flags < 0) {
 | |
| 			zlog_warn(
 | |
| 				"Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 			close(fd);
 | |
| 			return PIM_SOCK_ERR_NONBLOCK_GETFL;
 | |
| 		}
 | |
| 
 | |
| 		if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
 | |
| 			zlog_warn(
 | |
| 				"Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
 | |
| 				fd, errno, safe_strerror(errno));
 | |
| 			close(fd);
 | |
| 			return PIM_SOCK_ERR_NONBLOCK_SETFL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Set Tx socket DSCP byte */
 | |
| 	if (setsockopt_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL)) {
 | |
| 		zlog_warn("can't set sockopt IP_TOS to PIM/IGMP socket %d: %s",
 | |
| 			  fd, safe_strerror(errno));
 | |
| 	}
 | |
| 
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr,
 | |
| 		    ifindex_t ifindex)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 | |
| 	struct ip_mreqn opt;
 | |
| #else
 | |
| 	struct ip_mreq opt;
 | |
| #endif
 | |
| 
 | |
| 	opt.imr_multiaddr = group;
 | |
| 
 | |
| #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 | |
| 	opt.imr_address = ifaddr;
 | |
| 	opt.imr_ifindex = ifindex;
 | |
| #else
 | |
| 	opt.imr_interface = ifaddr;
 | |
| #endif
 | |
| 
 | |
| 	ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
 | |
| 	if (ret) {
 | |
| 		char group_str[INET_ADDRSTRLEN];
 | |
| 		char ifaddr_str[INET_ADDRSTRLEN];
 | |
| 		if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str)))
 | |
| 			snprintf(group_str, sizeof(group_str), "<group?>");
 | |
| 		if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str,
 | |
| 			       sizeof(ifaddr_str)))
 | |
| 			snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>");
 | |
| 
 | |
| 		flog_err(
 | |
| 			EC_LIB_SOCKET,
 | |
| 			"Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
 | |
| 			fd, group_str, ifaddr_str, errno, safe_strerror(errno));
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	if (PIM_DEBUG_TRACE) {
 | |
| 		char group_str[INET_ADDRSTRLEN];
 | |
| 		char ifaddr_str[INET_ADDRSTRLEN];
 | |
| 		if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str)))
 | |
| 			snprintf(group_str, sizeof(group_str), "<group?>");
 | |
| 		if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str,
 | |
| 			       sizeof(ifaddr_str)))
 | |
| 			snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>");
 | |
| 
 | |
| 		zlog_debug(
 | |
| 			"Socket fd=%d joined group %s on interface address %s",
 | |
| 			fd, group_str, ifaddr_str);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
 | |
| 			  struct sockaddr_in *from, socklen_t *fromlen,
 | |
| 			  struct sockaddr_in *to, socklen_t *tolen,
 | |
| 			  ifindex_t *ifindex)
 | |
| {
 | |
| 	struct msghdr msgh;
 | |
| 	struct cmsghdr *cmsg;
 | |
| 	struct iovec iov;
 | |
| 	char cbuf[1000];
 | |
| 	int err;
 | |
| 
 | |
| 	/*
 | |
| 	 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
 | |
| 	 * Use getsockname() to get sin_port.
 | |
| 	 */
 | |
| 	if (to) {
 | |
| 		struct sockaddr_in si;
 | |
| 		socklen_t si_len = sizeof(si);
 | |
| 
 | |
| 		memset(&si, 0, sizeof(si));
 | |
| 		to->sin_family = AF_INET;
 | |
| 
 | |
| 		pim_socket_getsockname(fd, (struct sockaddr *)&si, &si_len);
 | |
| 
 | |
| 		to->sin_port = si.sin_port;
 | |
| 		to->sin_addr = si.sin_addr;
 | |
| 
 | |
| 		if (tolen)
 | |
| 			*tolen = sizeof(si);
 | |
| 	}
 | |
| 
 | |
| 	memset(&msgh, 0, sizeof(struct msghdr));
 | |
| 	iov.iov_base = buf;
 | |
| 	iov.iov_len = len;
 | |
| 	msgh.msg_control = cbuf;
 | |
| 	msgh.msg_controllen = sizeof(cbuf);
 | |
| 	msgh.msg_name = from;
 | |
| 	msgh.msg_namelen = fromlen ? *fromlen : 0;
 | |
| 	msgh.msg_iov = &iov;
 | |
| 	msgh.msg_iovlen = 1;
 | |
| 	msgh.msg_flags = 0;
 | |
| 
 | |
| 	err = recvmsg(fd, &msgh, 0);
 | |
| 	if (err < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	if (fromlen)
 | |
| 		*fromlen = msgh.msg_namelen;
 | |
| 
 | |
| 	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
 | |
| 	     cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
 | |
| 
 | |
| #ifdef HAVE_IP_PKTINFO
 | |
| 		if ((cmsg->cmsg_level == IPPROTO_IP)
 | |
| 		    && (cmsg->cmsg_type == IP_PKTINFO)) {
 | |
| 			struct in_pktinfo *i =
 | |
| 				(struct in_pktinfo *)CMSG_DATA(cmsg);
 | |
| 			if (to)
 | |
| 				to->sin_addr = i->ipi_addr;
 | |
| 			if (tolen)
 | |
| 				*tolen = sizeof(struct sockaddr_in);
 | |
| 			if (ifindex)
 | |
| 				*ifindex = i->ipi_ifindex;
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| #endif
 | |
| 
 | |
| #ifdef HAVE_IP_RECVDSTADDR
 | |
| 		if ((cmsg->cmsg_level == IPPROTO_IP)
 | |
| 		    && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
 | |
| 			struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
 | |
| 			if (to)
 | |
| 				to->sin_addr = *i;
 | |
| 			if (tolen)
 | |
| 				*tolen = sizeof(struct sockaddr_in);
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| #endif
 | |
| 
 | |
| #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
 | |
| 		if (cmsg->cmsg_type == IP_RECVIF)
 | |
| 			if (ifindex)
 | |
| 				*ifindex = CMSG_IFINDEX(cmsg);
 | |
| #endif
 | |
| 
 | |
| 	} /* for (cmsg) */
 | |
| 
 | |
| 	return err; /* len */
 | |
| }
 | |
| 
 | |
| int pim_socket_mcastloop_get(int fd)
 | |
| {
 | |
| 	int loop;
 | |
| 	socklen_t loop_len = sizeof(loop);
 | |
| 
 | |
| 	if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &loop_len)) {
 | |
| 		int e = errno;
 | |
| 		zlog_warn(
 | |
| 			"Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
 | |
| 			fd, errno, safe_strerror(errno));
 | |
| 		errno = e;
 | |
| 		return PIM_SOCK_ERR_LOOP;
 | |
| 	}
 | |
| 
 | |
| 	return loop;
 | |
| }
 | |
| 
 | |
| int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
 | |
| {
 | |
| 	if (getsockname(fd, name, namelen)) {
 | |
| 		int e = errno;
 | |
| 		zlog_warn(
 | |
| 			"Could not get Socket Name for socket fd=%d: errno=%d: %s",
 | |
| 			fd, errno, safe_strerror(errno));
 | |
| 		errno = e;
 | |
| 		return PIM_SOCK_ERR_NAME;
 | |
| 	}
 | |
| 
 | |
| 	return PIM_SOCK_ERR_NONE;
 | |
| }
 |