+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 "lib_errors.h"
#include "pimd.h"
+#include "pim_instance.h"
#include "pim_ssmpingd.h"
#include "pim_time.h"
#include "pim_sock.h"
+#include "network.h"
+#if PIM_IPV == 4
static const char *const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
+#else
+static const char *const PIM_SSMPINGD_REPLY_GROUP = "ff3e::4321:1234";
+#endif
enum { PIM_SSMPINGD_REQUEST = 'Q', PIM_SSMPINGD_REPLY = 'A' };
assert(!pim->ssmpingd_list);
- result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP,
+ result = inet_pton(PIM_AF, PIM_SSMPINGD_REPLY_GROUP,
&pim->ssmpingd_group_addr);
assert(result > 0);
}
static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
- struct in_addr source_addr)
+ pim_addr source_addr)
{
struct listnode *node;
struct ssmpingd_sock *ss;
return 0;
for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss))
- if (source_addr.s_addr == ss->source_addr.s_addr)
+ if (!pim_addr_cmp(source_addr, ss->source_addr))
return ss;
return 0;
XFREE(MTYPE_PIM_SSMPINGD, ss);
}
-static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
+#if PIM_IPV == 4
+static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
{
- struct sockaddr_in sockaddr;
- int fd;
-
- fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (fd < 0) {
- flog_err_sys(EC_LIB_SOCKET,
- "%s: could not create socket: errno=%d: %s",
- __func__, errno, safe_strerror(errno));
- return -1;
+ /* 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(
+ "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+ __func__, 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(
+ "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+ __func__, 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 -1;
+#endif
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_addr = addr;
- sockaddr.sin_port = htons(port);
-
- if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
- char addr_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ if (setsockopt_ipv4_multicast_loop(fd, 0)) {
zlog_warn(
- "%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
- __func__, fd, addr_str, port, sizeof(sockaddr), errno,
- safe_strerror(errno));
+ "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
close(fd);
- return -1;
+ return PIM_SOCK_ERR_LOOP;
}
- /* 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(
- "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
- __func__, 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(
- "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
- __func__, 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__);
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
+ sizeof(addr))) {
+ zlog_warn(
+ "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
-#endif
- }
-
- {
- int reuse = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse,
- sizeof(reuse))) {
- zlog_warn(
- "%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&mttl,
return -1;
}
- if (setsockopt_ipv4_multicast_loop(fd, 0)) {
+ return 0;
+}
+#else
+static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
+{
+ setsockopt_ipv6_pktinfo(fd, 1);
+ setsockopt_ipv6_multicast_hops(fd, mttl);
+
+ if (setsockopt_ipv6_multicast_loop(fd, 0)) {
zlog_warn(
"%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
__func__, fd, errno, safe_strerror(errno));
return PIM_SOCK_ERR_LOOP;
}
- if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&addr,
sizeof(addr))) {
zlog_warn(
"%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
close(fd);
return -1;
}
+ return 0;
+}
+#endif
- {
- long flags;
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0) {
- zlog_warn(
- "%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
+static int ssmpingd_socket(pim_addr addr, int port, int mttl)
+{
+ struct sockaddr_storage sockaddr;
+ int fd;
+ int ret;
+ socklen_t len = sizeof(sockaddr);
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
- zlog_warn(
- "%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
+ fd = socket(PIM_AF, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "%s: could not create socket: errno=%d: %s",
+ __func__, errno, safe_strerror(errno));
+ return -1;
+ }
+
+ pim_socket_getsockname(fd, (struct sockaddr *)&sockaddr, &len);
+
+ if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+ zlog_warn(
+ "%s: bind(fd=%d,addr=%pSUp,port=%d,len=%zu) failure: errno=%d: %s",
+ __func__, fd, &sockaddr, port, sizeof(sockaddr), errno,
+ safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ set_nonblocking(fd);
+ sockopt_reuseaddr(fd);
+
+ ret = ssmpingd_setsockopt(fd, addr, mttl);
+ if (ret) {
+ zlog_warn("ssmpingd_setsockopt failed");
+ close(fd);
+ return -1;
}
return fd;
THREAD_OFF(ss->t_sock_read);
if (close(ss->sock_fd)) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", ss->source_addr, source_str,
- sizeof(source_str));
zlog_warn(
- "%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
- __func__, ss->sock_fd, source_str, errno,
+ "%s: failure closing ssmpingd sock_fd=%d for source %pPA: errno=%d: %s",
+ __func__, ss->sock_fd, &ss->source_addr, errno,
safe_strerror(errno));
/* warning only */
}
}
static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
- int len, struct sockaddr_in to)
+ int len, struct sockaddr_storage to)
{
socklen_t tolen = sizeof(to);
int sent;
sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != len) {
- char to_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
if (sent < 0) {
zlog_warn(
- "%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
- __func__, to_str, ntohs(to.sin_port),
- ss->sock_fd, len, errno, safe_strerror(errno));
+ "%s: sendto() failure to %pSUp,fd=%d len=%d: errno=%d: %s",
+ __func__, &to, ss->sock_fd, len, errno,
+ safe_strerror(errno));
} else {
zlog_warn(
- "%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
- __func__, to_str, ntohs(to.sin_port),
- ss->sock_fd, len, sent);
+ "%s: sendto() partial to %pSUp, fd=%d len=%d: sent=%d",
+ __func__, &to, ss->sock_fd, len, sent);
}
}
}
if (len < 0) {
zlog_warn(
- "%s: failure receiving ssmping for source %pI4 on fd=%d: errno=%d: %s",
+ "%s: failure receiving ssmping for source %pPA on fd=%d: errno=%d: %s",
__func__, &ss->source_addr, ss->sock_fd, errno,
safe_strerror(errno));
return -1;
if (buf[0] != PIM_SSMPINGD_REQUEST) {
zlog_warn(
- "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pI4",
+ "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pPA",
__func__, buf[0], &from, &to,
ifp ? ifp->name : "<iface?>", ifindex, ss->sock_fd,
&ss->source_addr);
if (PIM_DEBUG_SSMPINGD) {
zlog_debug(
- "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pI4",
+ "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pPA",
__func__, &from, &to, ifp ? ifp->name : "<iface?>",
ifindex, ss->sock_fd, &ss->source_addr);
}
buf[0] = PIM_SSMPINGD_REPLY;
- struct sockaddr_in *from_addr = (struct sockaddr_in *)&from;
-
/* unicast reply */
- ssmpingd_sendto(ss, buf, len, *from_addr);
+ ssmpingd_sendto(ss, buf, len, from);
/* multicast reply */
- from_addr->sin_addr = ss->pim->ssmpingd_group_addr;
- ssmpingd_sendto(ss, buf, len, *from_addr);
+ memcpy(&from, &ss->pim->ssmpingd_group_addr, sizeof(pim_addr));
+ ssmpingd_sendto(ss, buf, len, from);
return 0;
}
}
static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
- struct in_addr source_addr)
+ pim_addr source_addr)
{
struct ssmpingd_sock *ss;
int sock_fd;
sock_fd =
ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
if (sock_fd < 0) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: ssmpingd_socket() failure for source %s",
- __func__, source_str);
+ zlog_warn("%s: ssmpingd_socket() failure for source %pPA",
+ __func__, &source_addr);
return 0;
}
return ss;
}
-int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr)
+int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr)
{
struct ssmpingd_sock *ss;
return 0;
}
- {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_info("%s: starting ssmpingd for source %s", __func__,
- source_str);
- }
+ zlog_info("%s: starting ssmpingd for source %pPAs", __func__,
+ &source_addr);
ss = ssmpingd_new(pim, source_addr);
if (!ss) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: ssmpingd_new() failure for source %s", __func__,
- source_str);
+ zlog_warn("%s: ssmpingd_new() failure for source %pPAs",
+ __func__, &source_addr);
return -1;
}
return 0;
}
-int pim_ssmpingd_stop(struct pim_instance *pim, struct in_addr source_addr)
+int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr)
{
struct ssmpingd_sock *ss;
ss = ssmpingd_find(pim, source_addr);
if (!ss) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: could not find ssmpingd for source %s", __func__,
- source_str);
+ zlog_warn("%s: could not find ssmpingd for source %pPAs",
+ __func__, &source_addr);
return -1;
}
- {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_info("%s: stopping ssmpingd for source %s", __func__,
- source_str);
- }
+ zlog_info("%s: stopping ssmpingd for source %pPAs", __func__,
+ &source_addr);
ssmpingd_delete(ss);