|
From f9ff7bf497894b74fd02d54dc0f0a39981f7cc06 Mon Sep 17 00:00:00 2001
|
|
From: Amol Lad <amol.lad@4rf.com>
|
|
Date: Wed, 17 Feb 2021 13:47:32 +1300
|
|
Subject: [PATCH 1/6] nhrpd: Add support for forwarding multicast packets
|
|
|
|
Forwarding multicast is a pre-requisite for allowing multicast based routing
|
|
protocols such as OSPF to work with DMVPN
|
|
|
|
This code relies on externally adding iptables rule. For example:
|
|
iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 224
|
|
|
|
Signed-off-by: Reuben Dowle <reuben.dowle@4rf.com>
|
|
---
|
|
nhrpd/linux.c | 11 +-
|
|
nhrpd/nhrp_interface.c | 2 +
|
|
nhrpd/nhrp_multicast.c | 312 +++++++++++++++++++++++++++++++++++++++++
|
|
nhrpd/nhrp_peer.c | 3 +-
|
|
nhrpd/nhrp_vty.c | 63 +++++++++
|
|
nhrpd/nhrpd.h | 16 +++
|
|
nhrpd/os.h | 2 +-
|
|
nhrpd/subdir.am | 1 +
|
|
8 files changed, 403 insertions(+), 7 deletions(-)
|
|
create mode 100644 nhrpd/nhrp_multicast.c
|
|
|
|
--- a/nhrpd/linux.c
|
|
+++ b/nhrpd/linux.c
|
|
@@ -15,6 +15,7 @@
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
+#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
@@ -42,7 +43,7 @@ int os_socket(void)
|
|
}
|
|
|
|
int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
|
|
- size_t addrlen)
|
|
+ size_t addrlen, uint16_t protocol)
|
|
{
|
|
struct sockaddr_ll lladdr;
|
|
struct iovec iov = {
|
|
@@ -61,16 +62,16 @@ int os_sendmsg(const uint8_t *buf, size_
|
|
|
|
memset(&lladdr, 0, sizeof(lladdr));
|
|
lladdr.sll_family = AF_PACKET;
|
|
- lladdr.sll_protocol = htons(ETH_P_NHRP);
|
|
+ lladdr.sll_protocol = htons(protocol);
|
|
lladdr.sll_ifindex = ifindex;
|
|
lladdr.sll_halen = addrlen;
|
|
memcpy(lladdr.sll_addr, addr, addrlen);
|
|
|
|
- status = sendmsg(nhrp_socket_fd, &msg, 0);
|
|
+ status = sendmsg(os_socket(), &msg, 0);
|
|
if (status < 0)
|
|
- return -1;
|
|
+ return -errno;
|
|
|
|
- return 0;
|
|
+ return status;
|
|
}
|
|
|
|
int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
|
|
--- a/nhrpd/nhrp_interface.c
|
|
+++ b/nhrpd/nhrp_interface.c
|
|
@@ -42,6 +42,7 @@ static int nhrp_if_new_hook(struct inter
|
|
struct nhrp_afi_data *ad = &nifp->afi[afi];
|
|
ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
|
|
list_init(&ad->nhslist_head);
|
|
+ list_init(&ad->mcastlist_head);
|
|
}
|
|
|
|
return 0;
|
|
@@ -55,6 +56,7 @@ static int nhrp_if_delete_hook(struct in
|
|
|
|
nhrp_cache_interface_del(ifp);
|
|
nhrp_nhs_interface_del(ifp);
|
|
+ nhrp_multicast_interface_del(ifp);
|
|
nhrp_peer_interface_del(ifp);
|
|
|
|
if (nifp->ipsec_profile)
|
|
--- /dev/null
|
|
+++ b/nhrpd/nhrp_multicast.c
|
|
@@ -0,0 +1,312 @@
|
|
+/* NHRP Multicast Support
|
|
+ * Copyright (c) 2020-2021 4RF Limited
|
|
+ *
|
|
+ * This file is free software: you may copy, redistribute 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.
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include <fcntl.h>
|
|
+#include <net/if.h>
|
|
+#include <net/ethernet.h>
|
|
+#include <netinet/if_ether.h>
|
|
+#include <linux/netlink.h>
|
|
+#include <linux/neighbour.h>
|
|
+#include <linux/netfilter/nfnetlink_log.h>
|
|
+#include <linux/if_packet.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/socket.h>
|
|
+
|
|
+#include "thread.h"
|
|
+#include "nhrpd.h"
|
|
+#include "netlink.h"
|
|
+#include "znl.h"
|
|
+#include "os.h"
|
|
+
|
|
+DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast")
|
|
+
|
|
+static int netlink_mcast_nflog_group;
|
|
+static int netlink_mcast_log_fd = -1;
|
|
+static struct thread *netlink_mcast_log_thread;
|
|
+static int nhrp_multicast_ip_count;
|
|
+
|
|
+struct mcast_ctx {
|
|
+ struct interface *ifp;
|
|
+ struct zbuf *pkt;
|
|
+};
|
|
+
|
|
+static void nhrp_multicast_send(struct nhrp_peer *p, struct zbuf *zb)
|
|
+{
|
|
+ char buf[2][256];
|
|
+ size_t addrlen;
|
|
+ int ret;
|
|
+
|
|
+ addrlen = sockunion_get_addrlen(&p->vc->remote.nbma);
|
|
+ ret = os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
|
|
+ sockunion_get_addr(&p->vc->remote.nbma),
|
|
+ addrlen, addrlen == 4 ? 0x0800 : 0x86DD);
|
|
+
|
|
+ debugf(NHRP_DEBUG_COMMON, "Multicast Packet: %s -> %s, ret = %d, size = %zu, addrlen = %zu",
|
|
+ sockunion2str(&p->vc->local.nbma, buf[0], sizeof(buf[0])),
|
|
+ sockunion2str(&p->vc->remote.nbma, buf[1], sizeof(buf[1])),
|
|
+ ret, zbuf_used(zb), addrlen);
|
|
+}
|
|
+
|
|
+static void nhrp_multicast_forward_nbma(union sockunion *nbma_addr, struct interface *ifp, struct zbuf *pkt)
|
|
+{
|
|
+ struct nhrp_peer *p = nhrp_peer_get(ifp, nbma_addr);
|
|
+ if(p && p->online) {
|
|
+ /* Send packet */
|
|
+ nhrp_multicast_send(p, pkt);
|
|
+ }
|
|
+ nhrp_peer_unref(p);
|
|
+}
|
|
+
|
|
+static void nhrp_multicast_forward_cache(struct nhrp_cache *c, void *pctx)
|
|
+{
|
|
+ struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
|
|
+
|
|
+ if (c->cur.type == NHRP_CACHE_DYNAMIC && c->cur.peer)
|
|
+ nhrp_multicast_forward_nbma(&c->cur.peer->vc->remote.nbma, ctx->ifp, ctx->pkt);
|
|
+}
|
|
+
|
|
+static void nhrp_multicast_forward(struct nhrp_multicast *mcast, void *pctx)
|
|
+{
|
|
+ struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
|
|
+ struct nhrp_interface *nifp = ctx->ifp->info;
|
|
+
|
|
+ if (!nifp->enabled)
|
|
+ return;
|
|
+
|
|
+ /* dynamic */
|
|
+ if (sockunion_family(&mcast->nbma_addr) == AF_UNSPEC) {
|
|
+ nhrp_cache_foreach(ctx->ifp, nhrp_multicast_forward_cache, pctx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Fixed IP Address */
|
|
+ nhrp_multicast_forward_nbma(&mcast->nbma_addr, ctx->ifp, ctx->pkt);
|
|
+}
|
|
+
|
|
+static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb)
|
|
+{
|
|
+ struct nfgenmsg *nf;
|
|
+ struct rtattr *rta;
|
|
+ struct zbuf rtapl, pktpl;
|
|
+ struct interface *ifp;
|
|
+ uint32_t *out_ndx = NULL;
|
|
+ afi_t afi;
|
|
+ struct mcast_ctx ctx;
|
|
+
|
|
+ debugf(NHRP_DEBUG_COMMON,"Inside %s\n", __func__);
|
|
+
|
|
+ nf = znl_pull(zb, sizeof(*nf));
|
|
+ if (!nf)
|
|
+ return;
|
|
+
|
|
+ memset(&pktpl, 0, sizeof(pktpl));
|
|
+ while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
|
|
+ switch (rta->rta_type) {
|
|
+ case NFULA_IFINDEX_OUTDEV:
|
|
+ out_ndx = znl_pull(&rtapl, sizeof(*out_ndx));
|
|
+ break;
|
|
+ case NFULA_PAYLOAD:
|
|
+ pktpl = rtapl;
|
|
+ break;
|
|
+ /* NFULA_HWHDR exists and is supposed to contain source
|
|
+ * hardware address. However, for ip_gre it seems to be
|
|
+ * the nexthop destination address if the packet matches
|
|
+ * route. */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!out_ndx || !zbuf_used(&pktpl))
|
|
+ return;
|
|
+
|
|
+ ifp = if_lookup_by_index(htonl(*out_ndx), VRF_DEFAULT);
|
|
+ if (!ifp)
|
|
+ return;
|
|
+
|
|
+ debugf(NHRP_DEBUG_COMMON,"Outgoing interface = %s\n", ifp->name);
|
|
+
|
|
+ ctx = (struct mcast_ctx) {
|
|
+ .ifp = ifp,
|
|
+ .pkt = &pktpl,
|
|
+ };
|
|
+
|
|
+ for (afi = 0; afi < AFI_MAX; afi++) {
|
|
+ nhrp_multicast_foreach(ifp, afi, nhrp_multicast_forward, (void *)&ctx);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int netlink_mcast_log_recv(struct thread *t)
|
|
+{
|
|
+ uint8_t buf[65535]; /* Max OSPF Packet size */
|
|
+ int fd = THREAD_FD(t);
|
|
+ struct zbuf payload, zb;
|
|
+ struct nlmsghdr *n;
|
|
+
|
|
+ netlink_mcast_log_thread = NULL;
|
|
+
|
|
+ zbuf_init(&zb, buf, sizeof(buf), 0);
|
|
+ while (zbuf_recv(&zb, fd) > 0) {
|
|
+ while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) {
|
|
+ debugf(NHRP_DEBUG_COMMON,
|
|
+ "Netlink-mcast-log: Received msg_type %u, msg_flags %u",
|
|
+ n->nlmsg_type, n->nlmsg_flags);
|
|
+ switch (n->nlmsg_type) {
|
|
+ case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET:
|
|
+ netlink_mcast_log_handler(n, &payload);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
|
|
+ &netlink_mcast_log_thread);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void netlink_mcast_log_register(int fd, int group)
|
|
+{
|
|
+ struct nlmsghdr *n;
|
|
+ struct nfgenmsg *nf;
|
|
+ struct nfulnl_msg_config_cmd cmd;
|
|
+ struct zbuf *zb = zbuf_alloc(512);
|
|
+
|
|
+ n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG,
|
|
+ NLM_F_REQUEST | NLM_F_ACK);
|
|
+ nf = znl_push(zb, sizeof(*nf));
|
|
+ *nf = (struct nfgenmsg){
|
|
+ .nfgen_family = AF_UNSPEC,
|
|
+ .version = NFNETLINK_V0,
|
|
+ .res_id = htons(group),
|
|
+ };
|
|
+ cmd.command = NFULNL_CFG_CMD_BIND;
|
|
+ znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
|
|
+ znl_nlmsg_complete(zb, n);
|
|
+
|
|
+ zbuf_send(zb, fd);
|
|
+ zbuf_free(zb);
|
|
+}
|
|
+
|
|
+static void netlink_mcast_set_nflog_group(struct interface *ifp, int nlgroup)
|
|
+{
|
|
+ if (netlink_mcast_log_fd >= 0) {
|
|
+ THREAD_OFF(netlink_mcast_log_thread);
|
|
+ close(netlink_mcast_log_fd);
|
|
+ netlink_mcast_log_fd = -1;
|
|
+ debugf(NHRP_DEBUG_COMMON, "De-register nflog group");
|
|
+ }
|
|
+ netlink_mcast_nflog_group = nlgroup;
|
|
+ if (nlgroup) {
|
|
+ netlink_mcast_log_fd = znl_open(NETLINK_NETFILTER, 0);
|
|
+ if (netlink_mcast_log_fd < 0)
|
|
+ return;
|
|
+
|
|
+ netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup);
|
|
+ thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
|
|
+ &netlink_mcast_log_thread);
|
|
+ debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d", netlink_mcast_nflog_group);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int nhrp_multicast_free(struct interface *ifp, struct nhrp_multicast *mcast)
|
|
+{
|
|
+ list_del(&mcast->list_entry);
|
|
+ XFREE(MTYPE_NHRP_MULTICAST, mcast);
|
|
+ if (--nhrp_multicast_ip_count == 0)
|
|
+ netlink_mcast_set_nflog_group(ifp, 0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int nhrp_multicast_add(struct interface *ifp, afi_t afi, union sockunion *nbma_addr)
|
|
+{
|
|
+ struct nhrp_interface *nifp = ifp->info;
|
|
+ struct nhrp_multicast *mcast;
|
|
+ char buf[SU_ADDRSTRLEN];
|
|
+
|
|
+ list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
|
|
+ {
|
|
+ if (sockunion_same(&mcast->nbma_addr, nbma_addr))
|
|
+ return NHRP_ERR_ENTRY_EXISTS;
|
|
+ }
|
|
+
|
|
+ mcast = XMALLOC(MTYPE_NHRP_MULTICAST, sizeof(struct nhrp_multicast));
|
|
+
|
|
+ *mcast = (struct nhrp_multicast){
|
|
+ .afi = afi,
|
|
+ .ifp = ifp,
|
|
+ .nbma_addr = *nbma_addr,
|
|
+ };
|
|
+ list_add_tail(&mcast->list_entry, &nifp->afi[afi].mcastlist_head);
|
|
+
|
|
+ if (netlink_mcast_log_fd == -1)
|
|
+ netlink_mcast_set_nflog_group(ifp, MCAST_NFLOG_GROUP);
|
|
+
|
|
+ nhrp_multicast_ip_count++;
|
|
+
|
|
+ sockunion2str(nbma_addr, buf, sizeof(buf));
|
|
+ debugf(NHRP_DEBUG_COMMON, "Adding multicast entry (%s) [%d]", buf, nhrp_multicast_ip_count);
|
|
+
|
|
+ return NHRP_OK;
|
|
+}
|
|
+
|
|
+int nhrp_multicast_del(struct interface *ifp, afi_t afi, union sockunion *nbma_addr)
|
|
+{
|
|
+ struct nhrp_interface *nifp = ifp->info;
|
|
+ struct nhrp_multicast *mcast, *tmp;
|
|
+ char buf[SU_ADDRSTRLEN];
|
|
+
|
|
+ list_for_each_entry_safe(mcast, tmp, &nifp->afi[afi].mcastlist_head,
|
|
+ list_entry)
|
|
+ {
|
|
+ if (!sockunion_same(&mcast->nbma_addr, nbma_addr))
|
|
+ continue;
|
|
+
|
|
+ sockunion2str(nbma_addr, buf, sizeof(buf));
|
|
+ debugf(NHRP_DEBUG_COMMON, "Deleting multicast entry (%s) [%d]", buf, nhrp_multicast_ip_count);
|
|
+
|
|
+ nhrp_multicast_free(ifp, mcast);
|
|
+
|
|
+ return NHRP_OK;
|
|
+ }
|
|
+
|
|
+ return NHRP_ERR_ENTRY_NOT_FOUND;
|
|
+}
|
|
+
|
|
+void nhrp_multicast_interface_del(struct interface *ifp)
|
|
+{
|
|
+ struct nhrp_interface *nifp = ifp->info;
|
|
+ struct nhrp_multicast *mcast, *tmp;
|
|
+ afi_t afi;
|
|
+
|
|
+ for (afi = 0; afi < AFI_MAX; afi++) {
|
|
+ debugf(NHRP_DEBUG_COMMON, "Cleaning up multicast entries (%d, %d)", !list_empty(&nifp->afi[afi].mcastlist_head), nhrp_multicast_ip_count);
|
|
+
|
|
+ list_for_each_entry_safe(
|
|
+ mcast, tmp, &nifp->afi[afi].mcastlist_head,
|
|
+ list_entry) {
|
|
+ nhrp_multicast_free(ifp, mcast);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
|
|
+ void (*cb)(struct nhrp_multicast *, void *),
|
|
+ void *ctx)
|
|
+{
|
|
+ struct nhrp_interface *nifp = ifp->info;
|
|
+ struct nhrp_multicast *mcast;
|
|
+
|
|
+ list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
|
|
+ {
|
|
+ cb (mcast, ctx);
|
|
+ }
|
|
+}
|
|
--- a/nhrpd/nhrp_peer.c
|
|
+++ b/nhrpd/nhrp_peer.c
|
|
@@ -337,7 +337,8 @@ void nhrp_peer_send(struct nhrp_peer *p,
|
|
|
|
os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
|
|
sockunion_get_addr(&p->vc->remote.nbma),
|
|
- sockunion_get_addrlen(&p->vc->remote.nbma));
|
|
+ sockunion_get_addrlen(&p->vc->remote.nbma),
|
|
+ ETH_P_NHRP);
|
|
zbuf_reset(zb);
|
|
}
|
|
|
|
--- a/nhrpd/nhrp_vty.c
|
|
+++ b/nhrpd/nhrp_vty.c
|
|
@@ -569,6 +569,53 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
+DEFUN(if_nhrp_map_multicast, if_nhrp_map_multicast_cmd,
|
|
+ AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
|
|
+ AFI_STR
|
|
+ NHRP_STR
|
|
+ "Multicast NBMA Configuration\n"
|
|
+ "Use this NBMA mapping for multicasts\n"
|
|
+ "IPv4 NBMA address\n"
|
|
+ "IPv6 NBMA address\n"
|
|
+ "Dynamically learn destinations from client registrations on hub\n")
|
|
+{
|
|
+ VTY_DECLVAR_CONTEXT(interface, ifp);
|
|
+ afi_t afi = cmd_to_afi(argv[0]);
|
|
+ union sockunion nbma_addr;
|
|
+ int ret;
|
|
+
|
|
+ if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
|
|
+ sockunion_family(&nbma_addr) = AF_UNSPEC;
|
|
+
|
|
+ ret = nhrp_multicast_add(ifp, afi, &nbma_addr);
|
|
+
|
|
+ return nhrp_vty_return(vty, ret);
|
|
+}
|
|
+
|
|
+DEFUN(if_no_nhrp_map_multicast, if_no_nhrp_map_multicast_cmd,
|
|
+ "no " AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
|
|
+ NO_STR
|
|
+ AFI_STR
|
|
+ NHRP_STR
|
|
+ "Multicast NBMA Configuration\n"
|
|
+ "Use this NBMA mapping for multicasts\n"
|
|
+ "IPv4 NBMA address\n"
|
|
+ "IPv6 NBMA address\n"
|
|
+ "Dynamically learn destinations from client registrations on hub\n")
|
|
+{
|
|
+ VTY_DECLVAR_CONTEXT(interface, ifp);
|
|
+ afi_t afi = cmd_to_afi(argv[1]);
|
|
+ union sockunion nbma_addr;
|
|
+ int ret;
|
|
+
|
|
+ if (str2sockunion(argv[5]->arg, &nbma_addr) < 0)
|
|
+ sockunion_family(&nbma_addr) = AF_UNSPEC;
|
|
+
|
|
+ ret = nhrp_multicast_del(ifp, afi, &nbma_addr);
|
|
+
|
|
+ return nhrp_vty_return(vty, ret);
|
|
+}
|
|
+
|
|
DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
|
|
AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>",
|
|
AFI_STR
|
|
@@ -1040,6 +1087,7 @@ static int interface_config_write(struct
|
|
struct interface *ifp;
|
|
struct nhrp_interface *nifp;
|
|
struct nhrp_nhs *nhs;
|
|
+ struct nhrp_multicast *mcast;
|
|
const char *aficmd;
|
|
afi_t afi;
|
|
char buf[SU_ADDRSTRLEN];
|
|
@@ -1109,6 +1157,19 @@ static int interface_config_write(struct
|
|
sizeof(buf)),
|
|
nhs->nbma_fqdn);
|
|
}
|
|
+
|
|
+ list_for_each_entry(mcast, &ad->mcastlist_head,
|
|
+ list_entry)
|
|
+ {
|
|
+ vty_out(vty, " %s nhrp map multicast %s\n",
|
|
+ aficmd,
|
|
+ sockunion_family(&mcast->nbma_addr)
|
|
+ == AF_UNSPEC
|
|
+ ? "dynamic"
|
|
+ : sockunion2str(
|
|
+ &mcast->nbma_addr, buf,
|
|
+ sizeof(buf)));
|
|
+ }
|
|
}
|
|
|
|
vty_endframe(vty, "!\n");
|
|
@@ -1163,6 +1224,8 @@ void nhrp_config_init(void)
|
|
install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
|
|
install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
|
|
install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd);
|
|
+ install_element(INTERFACE_NODE, &if_nhrp_map_multicast_cmd);
|
|
+ install_element(INTERFACE_NODE, &if_no_nhrp_map_multicast_cmd);
|
|
install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
|
|
install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
|
|
}
|
|
--- a/nhrpd/nhrpd.h
|
|
+++ b/nhrpd/nhrpd.h
|
|
@@ -24,6 +24,7 @@ DECLARE_MGROUP(NHRPD)
|
|
|
|
#define NHRP_VTY_PORT 2610
|
|
#define NHRP_DEFAULT_CONFIG "nhrpd.conf"
|
|
+#define MCAST_NFLOG_GROUP 224
|
|
|
|
extern struct thread_master *master;
|
|
|
|
@@ -259,6 +260,13 @@ struct nhrp_nhs {
|
|
struct list_head reglist_head;
|
|
};
|
|
|
|
+struct nhrp_multicast {
|
|
+ struct interface *ifp;
|
|
+ struct list_head list_entry;
|
|
+ afi_t afi;
|
|
+ union sockunion nbma_addr; /* IP-address */
|
|
+};
|
|
+
|
|
struct nhrp_registration {
|
|
struct list_head reglist_entry;
|
|
struct thread *t_register;
|
|
@@ -304,6 +312,7 @@ struct nhrp_interface {
|
|
unsigned short mtu;
|
|
unsigned int holdtime;
|
|
struct list_head nhslist_head;
|
|
+ struct list_head mcastlist_head;
|
|
} afi[AFI_MAX];
|
|
};
|
|
|
|
@@ -345,6 +354,13 @@ void nhrp_nhs_foreach(struct interface *
|
|
void *ctx);
|
|
void nhrp_nhs_interface_del(struct interface *ifp);
|
|
|
|
+int nhrp_multicast_add(struct interface *ifp, afi_t afi, union sockunion *nbma_addr);
|
|
+int nhrp_multicast_del(struct interface *ifp, afi_t afi, union sockunion *nbma_addr);
|
|
+void nhrp_multicast_interface_del(struct interface *ifp);
|
|
+void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
|
|
+ void (*cb)(struct nhrp_multicast *, void *),
|
|
+ void *ctx);
|
|
+
|
|
void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
|
|
void nhrp_route_announce(int add, enum nhrp_cache_type type,
|
|
const struct prefix *p, struct interface *ifp,
|
|
--- a/nhrpd/os.h
|
|
+++ b/nhrpd/os.h
|
|
@@ -1,7 +1,7 @@
|
|
|
|
int os_socket(void);
|
|
int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
|
|
- size_t addrlen);
|
|
+ size_t addrlen, uint16_t protocol);
|
|
int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
|
|
size_t *addrlen);
|
|
int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
|
|
--- a/nhrpd/subdir.am
|
|
+++ b/nhrpd/subdir.am
|
|
@@ -21,6 +21,7 @@ nhrpd_nhrpd_SOURCES = \
|
|
nhrpd/nhrp_nhs.c \
|
|
nhrpd/nhrp_packet.c \
|
|
nhrpd/nhrp_peer.c \
|
|
+ nhrpd/nhrp_multicast.c \
|
|
nhrpd/nhrp_route.c \
|
|
nhrpd/nhrp_shortcut.c \
|
|
nhrpd/nhrp_vc.c \
|
|
--- a/ospfd/ospf_interface.c
|
|
+++ b/ospfd/ospf_interface.c
|
|
@@ -534,6 +534,8 @@ static struct ospf_if_params *ospf_new_i
|
|
oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER);
|
|
oip->is_v_wait_set = false;
|
|
|
|
+ oip->ptp_dmvpn = 0;
|
|
+
|
|
return oip;
|
|
}
|
|
|
|
--- a/ospfd/ospf_interface.h
|
|
+++ b/ospfd/ospf_interface.h
|
|
@@ -105,6 +105,9 @@ struct ospf_if_params {
|
|
|
|
/* BFD configuration */
|
|
struct bfd_info *bfd_info;
|
|
+
|
|
+ /* point-to-point DMVPN configuration */
|
|
+ uint8_t ptp_dmvpn;
|
|
};
|
|
|
|
enum { MEMBER_ALLROUTERS = 0,
|
|
@@ -167,6 +170,9 @@ struct ospf_interface {
|
|
/* OSPF Network Type. */
|
|
uint8_t type;
|
|
|
|
+ /* point-to-point DMVPN configuration */
|
|
+ uint8_t ptp_dmvpn;
|
|
+
|
|
/* State of Interface State Machine. */
|
|
uint8_t state;
|
|
|
|
--- a/ospfd/ospf_lsa.c
|
|
+++ b/ospfd/ospf_lsa.c
|
|
@@ -469,6 +469,12 @@ static char link_info_set(struct stream
|
|
}
|
|
|
|
/* Describe Point-to-Point link (Section 12.4.1.1). */
|
|
+
|
|
+/* Note: If the interface is configured as point-to-point dmvpn then the other
|
|
+ * end of link is dmvpn hub with point-to-multipoint ospf network type. The
|
|
+ * hub then expects this router to populate the stub network and also Link Data
|
|
+ * Field set to IP Address and not MIB-II ifIndex
|
|
+ */
|
|
static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi)
|
|
{
|
|
int links = 0;
|
|
@@ -482,7 +488,8 @@ static int lsa_link_ptop_set(struct stre
|
|
if ((nbr = ospf_nbr_lookup_ptop(oi)))
|
|
if (nbr->state == NSM_Full) {
|
|
if (CHECK_FLAG(oi->connected->flags,
|
|
- ZEBRA_IFA_UNNUMBERED)) {
|
|
+ ZEBRA_IFA_UNNUMBERED)
|
|
+ && !oi->ptp_dmvpn) {
|
|
/* For unnumbered point-to-point networks, the
|
|
Link Data field
|
|
should specify the interface's MIB-II ifIndex
|
|
@@ -500,7 +507,8 @@ static int lsa_link_ptop_set(struct stre
|
|
}
|
|
|
|
/* no need for a stub link for unnumbered interfaces */
|
|
- if (!CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
|
|
+ if (oi->ptp_dmvpn
|
|
+ || !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
|
|
/* Regardless of the state of the neighboring router, we must
|
|
add a Type 3 link (stub network).
|
|
N.B. Options 1 & 2 share basically the same logic. */
|
|
--- a/ospfd/ospf_vty.c
|
|
+++ b/ospfd/ospf_vty.c
|
|
@@ -7560,20 +7560,21 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
|
|
return no_ip_ospf_hello_interval(self, vty, argc, argv);
|
|
}
|
|
|
|
-DEFUN (ip_ospf_network,
|
|
- ip_ospf_network_cmd,
|
|
- "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point>",
|
|
- "IP Information\n"
|
|
- "OSPF interface commands\n"
|
|
- "Network type\n"
|
|
- "Specify OSPF broadcast multi-access network\n"
|
|
- "Specify OSPF NBMA network\n"
|
|
- "Specify OSPF point-to-multipoint network\n"
|
|
- "Specify OSPF point-to-point network\n")
|
|
+DEFUN(ip_ospf_network, ip_ospf_network_cmd,
|
|
+ "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>",
|
|
+ "IP Information\n"
|
|
+ "OSPF interface commands\n"
|
|
+ "Network type\n"
|
|
+ "Specify OSPF broadcast multi-access network\n"
|
|
+ "Specify OSPF NBMA network\n"
|
|
+ "Specify OSPF point-to-multipoint network\n"
|
|
+ "Specify OSPF point-to-point network\n"
|
|
+ "Specify OSPF point-to-point DMVPN network\n")
|
|
{
|
|
VTY_DECLVAR_CONTEXT(interface, ifp);
|
|
int idx = 0;
|
|
int old_type = IF_DEF_PARAMS(ifp)->type;
|
|
+ uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
|
|
struct route_node *rn;
|
|
|
|
if (old_type == OSPF_IFTYPE_LOOPBACK) {
|
|
@@ -7582,16 +7583,22 @@ DEFUN (ip_ospf_network,
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
|
|
+ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
|
|
+
|
|
if (argv_find(argv, argc, "broadcast", &idx))
|
|
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST;
|
|
else if (argv_find(argv, argc, "non-broadcast", &idx))
|
|
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
|
|
else if (argv_find(argv, argc, "point-to-multipoint", &idx))
|
|
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
|
|
- else if (argv_find(argv, argc, "point-to-point", &idx))
|
|
+ else if (argv_find(argv, argc, "point-to-point", &idx)) {
|
|
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
|
|
+ if (argv_find(argv, argc, "dmvpn", &idx))
|
|
+ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
|
|
+ }
|
|
|
|
- if (IF_DEF_PARAMS(ifp)->type == old_type)
|
|
+ if (IF_DEF_PARAMS(ifp)->type == old_type
|
|
+ && IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn)
|
|
return CMD_SUCCESS;
|
|
|
|
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
|
|
@@ -7603,6 +7610,7 @@ DEFUN (ip_ospf_network,
|
|
continue;
|
|
|
|
oi->type = IF_DEF_PARAMS(ifp)->type;
|
|
+ oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
|
|
|
|
if (oi->state > ISM_Down) {
|
|
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
|
|
@@ -7643,6 +7651,7 @@ DEFUN (no_ip_ospf_network,
|
|
struct route_node *rn;
|
|
|
|
IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
|
|
+ IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
|
|
|
|
if (IF_DEF_PARAMS(ifp)->type == old_type)
|
|
return CMD_SUCCESS;
|
|
@@ -9888,6 +9897,10 @@ static int config_write_interface_one(st
|
|
vty_out(vty, " ip ospf network %s",
|
|
ospf_int_type_str
|
|
[params->type]);
|
|
+ if (params->type
|
|
+ == OSPF_IFTYPE_POINTOPOINT
|
|
+ && params->ptp_dmvpn)
|
|
+ vty_out(vty, " dmvpn");
|
|
if (params != IF_DEF_PARAMS(ifp) && rn)
|
|
vty_out(vty, " %s",
|
|
inet_ntoa(
|
|
--- a/ospfd/ospfd.c
|
|
+++ b/ospfd/ospfd.c
|
|
@@ -1017,6 +1017,7 @@ static void add_ospf_interface(struct co
|
|
/* If network type is specified previously,
|
|
skip network type setting. */
|
|
oi->type = IF_DEF_PARAMS(co->ifp)->type;
|
|
+ oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn;
|
|
|
|
/* Add pseudo neighbor. */
|
|
ospf_nbr_self_reset(oi, oi->ospf->router_id);
|
|
--- a/doc/user/nhrpd.rst
|
|
+++ b/doc/user/nhrpd.rst
|
|
@@ -189,6 +189,34 @@ and
|
|
https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras
|
|
git repositories for the patches.
|
|
|
|
+.. _multicast-functionality:
|
|
+
|
|
+Multicast Functionality
|
|
+=======================
|
|
+
|
|
+nhrpd can be configured to forward multicast packets, allowing routing
|
|
+protocols that use multicast (such as OSPF) to be supported in the DMVPN
|
|
+network.
|
|
+
|
|
+This support requires an NFLOG redirection rule to work:
|
|
+
|
|
+ .. code-block:: shell
|
|
+
|
|
+ iptables -I OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 2
|
|
+
|
|
+.. index:: nhrp multicast-nflog-group (1-65535)
|
|
+.. clicmd:: nhrp multicast-nflog-group (1-65535)
|
|
+
|
|
+ Sets the nflog group that nhrpd will listen on for multicast packets. This
|
|
+ value must match the nflog-group value set in the iptables rule.
|
|
+
|
|
+.. index:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
|
|
+.. clicmd:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
|
|
+
|
|
+ Sends multicast packets to the specified NBMA address. If dynamic is
|
|
+ specified then destination NBMA address (or addresses) are learnt
|
|
+ dynamically.
|
|
+
|
|
.. _nhrp-events:
|
|
|
|
NHRP Events
|
|
--- a/doc/user/ospfd.rst
|
|
+++ b/doc/user/ospfd.rst
|
|
@@ -687,8 +687,8 @@ Interfaces
|
|
:clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
|
|
specified for the interface.
|
|
|
|
-.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
|
|
-.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
|
|
+.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
|
|
+.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
|
|
|
|
When configuring a point-to-point network on an interface and the interface
|
|
has a /32 address associated with then OSPF will treat the interface
|
|
@@ -870,6 +870,9 @@ Redistribution
|
|
.. index:: no router zebra
|
|
.. clicmd:: no router zebra
|
|
|
|
+ When used in a DMVPN network at a spoke, this OSPF will be configured in
|
|
+ point-to-point, but the HUB will be a point-to-multipoint. To make this
|
|
+ topology work, specify the optional 'dmvpn' parameter at the spoke.
|
|
|
|
.. _showing-ospf-information:
|
|
|