summaryrefslogtreecommitdiff
path: root/pimd/pim_sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_sock.c')
-rw-r--r--pimd/pim_sock.c418
1 files changed, 194 insertions, 224 deletions
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
index 8d3fbabd27..92e2d18451 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -34,22 +34,27 @@
#include "vrf.h"
#include "sockopt.h"
#include "lib_errors.h"
+#include "network.h"
#include "pimd.h"
#include "pim_mroute.h"
#include "pim_sock.h"
#include "pim_str.h"
-/* GLOBAL VARS */
+#if PIM_IPV == 4
+#define setsockopt_iptos setsockopt_ipv4_tos
+#define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop
+#else
+#define setsockopt_iptos setsockopt_ipv6_tclass
+#define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop
+#endif
int pim_socket_raw(int protocol)
{
int fd;
frr_with_privs(&pimd_privs) {
-
- fd = socket(AF_INET, SOCK_RAW, protocol);
-
+ fd = socket(PIM_AF, SOCK_RAW, protocol);
}
if (fd < 0) {
@@ -66,10 +71,16 @@ void pim_socket_ip_hdr(int fd)
const int on = 1;
frr_with_privs(&pimd_privs) {
-
+#if PIM_IPV == 4
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));
+ zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
+ __func__);
+#else
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on)))
+ zlog_err(
+ "%s: Could not turn on IPV6_HDRINCL option: %m",
+ __func__);
+#endif
}
}
@@ -80,248 +91,252 @@ void pim_socket_ip_hdr(int fd)
int pim_socket_bind(int fd, struct interface *ifp)
{
int ret = 0;
-#ifdef SO_BINDTODEVICE
+#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)
+#if PIM_IPV == 4
+static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
{
- int rcvbuf = 1024 * 1024 * 8;
-#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
- struct ip_mreqn mreq;
-#else
- struct ip_mreq mreq;
-#endif
- int fd;
+ int one = 1;
+ int ttl = 1;
- 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
- 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));
- }
+ /* Linux and Solaris IP_PKTINFO */
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)))
+ zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd);
#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));
- }
+ /* BSD IP_RECVDSTADDR */
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one)))
+ zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m",
+ fd);
#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;
+ flog_err(
+ EC_LIB_DEVELOPMENT,
+ "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()");
+ close(fd);
+ return PIM_SOCK_ERR_DSTADDR;
#endif
- }
-
- /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4.
- * Message Formats)*/
+ /* 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));
+ "Could not set Router Alert Option on socket fd=%d: %m",
+ fd);
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(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) {
+ zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m",
+ ttl, fd);
+ close(fd);
+ return PIM_SOCK_ERR_TTL;
}
- if (setsockopt_ipv4_multicast_loop(fd, loop)) {
+ if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) {
zlog_warn(
- "Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
- loop ? "enable" : "disable", fd, errno,
- safe_strerror(errno));
+ "Could not set Outgoing Interface Option on socket fd=%d: %m",
+ fd);
close(fd);
- return PIM_SOCK_ERR_LOOP;
+ return PIM_SOCK_ERR_IFACE;
}
- 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
+ return 0;
+}
+#else /* PIM_IPV != 4 */
+static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
+{
+ int ttl = 1;
+ struct ipv6_mreq mreq = {};
+
+ setsockopt_ipv6_pktinfo(fd, 1);
+ setsockopt_ipv6_multicast_hops(fd, ttl);
- if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreq,
+ mreq.ipv6mr_interface = ifp->ifindex;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
sizeof(mreq))) {
zlog_warn(
- "Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
- fd, errno, safe_strerror(errno));
+ "Could not set Outgoing Interface Option on socket fd=%d: %m",
+ fd);
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);
+ return 0;
+}
+#endif
- {
- long flags;
+int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
+ uint8_t loop)
+{
+ int fd;
+ int ret;
- 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;
- }
+ 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;
+ }
- 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;
- }
+ /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
+ * to emulate behaviour? Or change to only use 1 socket for all
+ * interfaces? */
+ 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;
}
- /* 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));
+ set_nonblocking(fd);
+ sockopt_reuseaddr(fd);
+ setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
+
+ ret = pim_setsockopt(protocol, fd, ifp);
+ if (ret) {
+ zlog_warn("pim_setsockopt failed for interface: %s to device ",
+ ifp->name);
+ return ret;
}
+ /* leftover common sockopts */
+ if (setsockopt_multicast_loop(fd, loop)) {
+ zlog_warn(
+ "Could not %s Multicast Loopback Option on socket fd=%d: %m",
+ loop ? "enable" : "disable", fd);
+ close(fd);
+ return PIM_SOCK_ERR_LOOP;
+ }
+
+ /* Set Tx socket DSCP byte */
+ if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
+ zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
+
return fd;
}
-int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr,
- ifindex_t ifindex)
+int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex)
{
int ret;
-#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
- struct ip_mreqn opt;
+#if PIM_IPV == 4
+ ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
+ group.s_addr, ifindex);
#else
- struct ip_mreq opt;
-#endif
-
- opt.imr_multiaddr = group;
+ struct ipv6_mreq opt;
-#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
- opt.imr_address = ifaddr;
- opt.imr_ifindex = ifindex;
-#else
- opt.imr_interface = ifaddr;
+ memcpy(&opt.ipv6mr_multiaddr, &group, 16);
+ opt.ipv6mr_interface = ifindex;
+ ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
#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));
+ "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
+ fd, &group, &ifaddr);
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?>");
-
+ if (PIM_DEBUG_TRACE)
zlog_debug(
- "Socket fd=%d joined group %s on interface address %s",
- fd, group_str, ifaddr_str);
+ "Socket fd=%d joined group %pPAs on interface address %pPAs",
+ fd, &group, &ifaddr);
+ return ret;
+}
+
+#if PIM_IPV == 4
+static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
+ ifindex_t *ifindex)
+{
+ struct cmsghdr *cmsg;
+ struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
+
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(mh, cmsg)) {
+#ifdef HAVE_IP_PKTINFO
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_PKTINFO)) {
+ struct in_pktinfo *i;
+
+ i = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ if (dst4)
+ dst4->sin_addr = i->ipi_addr;
+ 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 (dst4)
+ dst4->sin_addr = *i;
+
+ break;
+ }
+#endif
+
+#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
+ if (cmsg->cmsg_type == IP_RECVIF)
+ if (ifindex)
+ *ifindex = CMSG_IFINDEX(cmsg);
+#endif
}
+}
+#else /* PIM_IPV != 4 */
+static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
+ ifindex_t *ifindex)
+{
+ struct cmsghdr *cmsg;
+ struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
- return ret;
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(mh, cmsg)) {
+ if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
+ (cmsg->cmsg_type == IPV6_PKTINFO)) {
+ struct in6_pktinfo *i;
+
+ i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+
+ if (dst6)
+ dst6->sin6_addr = i->ipi6_addr;
+ if (ifindex)
+ *ifindex = i->ipi6_ifindex;
+ break;
+ }
+ }
}
+#endif /* PIM_IPV != 4 */
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,
+ struct sockaddr_storage *from, socklen_t *fromlen,
+ struct sockaddr_storage *to, socklen_t *tolen,
ifindex_t *ifindex)
{
struct msghdr msgh;
- struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1000];
int err;
@@ -331,19 +346,12 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
* 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);
+ socklen_t to_len = sizeof(*to);
- to->sin_port = si.sin_port;
- to->sin_addr = si.sin_addr;
+ pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
if (tolen)
- *tolen = sizeof(si);
+ *tolen = sizeof(*to);
}
memset(&msgh, 0, sizeof(struct msghdr));
@@ -364,45 +372,7 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
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) */
+ cmsg_getdstaddr(&msgh, to, ifindex);
return err; /* len */
}