You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

964 lines
29 KiB

  1. From 6ea5d99456b14db5e82abc2461228bb37aa7556d Mon Sep 17 00:00:00 2001
  2. From: Amol Lad <amol.lad@4rf.com>
  3. Date: Wed, 17 Feb 2021 13:47:32 +1300
  4. Subject: [PATCH 01/14] nhrpd: Add support for forwarding multicast packets
  5. Forwarding multicast is a pre-requisite for allowing multicast based routing
  6. protocols such as OSPF to work with DMVPN
  7. This code relies on externally adding iptables rule. For example:
  8. iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 224
  9. Signed-off-by: Reuben Dowle <reuben.dowle@4rf.com>
  10. ---
  11. nhrpd/linux.c | 11 +-
  12. nhrpd/nhrp_interface.c | 2 +
  13. nhrpd/nhrp_multicast.c | 307 +++++++++++++++++++++++++++++++++++++++++
  14. nhrpd/nhrp_peer.c | 3 +-
  15. nhrpd/nhrp_vty.c | 63 +++++++++
  16. nhrpd/nhrpd.h | 16 +++
  17. nhrpd/os.h | 2 +-
  18. nhrpd/subdir.am | 1 +
  19. 8 files changed, 398 insertions(+), 7 deletions(-)
  20. create mode 100755 nhrpd/nhrp_multicast.c
  21. --- a/nhrpd/linux.c
  22. +++ b/nhrpd/linux.c
  23. @@ -15,6 +15,7 @@
  24. #include <stdio.h>
  25. #include <unistd.h>
  26. #include <string.h>
  27. +#include <errno.h>
  28. #include <sys/ioctl.h>
  29. #include <sys/socket.h>
  30. #include <sys/types.h>
  31. @@ -31,6 +32,11 @@
  32. #include "os.h"
  33. #include "netlink.h"
  34. +#ifndef HAVE_STRLCPY
  35. +size_t strlcpy(char *__restrict dest,
  36. + const char *__restrict src, size_t destsize);
  37. +#endif
  38. +
  39. static int nhrp_socket_fd = -1;
  40. int os_socket(void)
  41. @@ -42,7 +48,7 @@ int os_socket(void)
  42. }
  43. int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
  44. - size_t addrlen)
  45. + size_t addrlen, uint16_t protocol)
  46. {
  47. struct sockaddr_ll lladdr;
  48. struct iovec iov = {
  49. @@ -61,16 +67,16 @@ int os_sendmsg(const uint8_t *buf, size_
  50. memset(&lladdr, 0, sizeof(lladdr));
  51. lladdr.sll_family = AF_PACKET;
  52. - lladdr.sll_protocol = htons(ETH_P_NHRP);
  53. + lladdr.sll_protocol = htons(protocol);
  54. lladdr.sll_ifindex = ifindex;
  55. lladdr.sll_halen = addrlen;
  56. memcpy(lladdr.sll_addr, addr, addrlen);
  57. - status = sendmsg(nhrp_socket_fd, &msg, 0);
  58. + status = sendmsg(os_socket(), &msg, 0);
  59. if (status < 0)
  60. - return -1;
  61. + return -errno;
  62. - return 0;
  63. + return status;
  64. }
  65. int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
  66. @@ -111,7 +117,7 @@ static int linux_configure_arp(const cha
  67. {
  68. struct ifreq ifr;
  69. - strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1);
  70. + strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
  71. if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr))
  72. return -1;
  73. --- a/nhrpd/nhrp_interface.c
  74. +++ b/nhrpd/nhrp_interface.c
  75. @@ -42,6 +42,7 @@ static int nhrp_if_new_hook(struct inter
  76. struct nhrp_afi_data *ad = &nifp->afi[afi];
  77. ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
  78. list_init(&ad->nhslist_head);
  79. + list_init(&ad->mcastlist_head);
  80. }
  81. return 0;
  82. @@ -55,6 +56,7 @@ static int nhrp_if_delete_hook(struct in
  83. nhrp_cache_interface_del(ifp);
  84. nhrp_nhs_interface_del(ifp);
  85. + nhrp_multicast_interface_del(ifp);
  86. nhrp_peer_interface_del(ifp);
  87. if (nifp->ipsec_profile)
  88. --- /dev/null
  89. +++ b/nhrpd/nhrp_multicast.c
  90. @@ -0,0 +1,309 @@
  91. +/* NHRP Multicast Support
  92. + * Copyright (c) 2020-2021 4RF Limited
  93. + *
  94. + * This file is free software: you may copy, redistribute and/or modify
  95. + * it under the terms of the GNU General Public License as published by
  96. + * the Free Software Foundation, either version 2 of the License, or
  97. + * (at your option) any later version.
  98. + */
  99. +
  100. +#ifdef HAVE_CONFIG_H
  101. +#include "config.h"
  102. +#endif
  103. +
  104. +#include <fcntl.h>
  105. +#include <net/if.h>
  106. +#include <net/ethernet.h>
  107. +#include <netinet/if_ether.h>
  108. +#include <linux/netlink.h>
  109. +#include <linux/neighbour.h>
  110. +#include <linux/netfilter/nfnetlink_log.h>
  111. +#include <linux/if_packet.h>
  112. +#include <sys/types.h>
  113. +#include <sys/socket.h>
  114. +
  115. +#include "thread.h"
  116. +#include "nhrpd.h"
  117. +#include "netlink.h"
  118. +#include "znl.h"
  119. +#include "os.h"
  120. +
  121. +DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast")
  122. +
  123. +int netlink_mcast_nflog_group;
  124. +static int netlink_mcast_log_fd = -1;
  125. +static struct thread *netlink_mcast_log_thread;
  126. +
  127. +struct mcast_ctx {
  128. + struct interface *ifp;
  129. + struct zbuf *pkt;
  130. +};
  131. +
  132. +static void nhrp_multicast_send(struct nhrp_peer *p, struct zbuf *zb)
  133. +{
  134. + char buf[2][256];
  135. + size_t addrlen;
  136. + int ret;
  137. +
  138. + addrlen = sockunion_get_addrlen(&p->vc->remote.nbma);
  139. + ret = os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
  140. + sockunion_get_addr(&p->vc->remote.nbma), addrlen,
  141. + addrlen == 4 ? ETH_P_IP : ETH_P_IPV6);
  142. +
  143. + debugf(NHRP_DEBUG_COMMON,
  144. + "Multicast Packet: %s -> %s, ret = %d, size = %zu, addrlen = %zu",
  145. + sockunion2str(&p->vc->local.nbma, buf[0], sizeof(buf[0])),
  146. + sockunion2str(&p->vc->remote.nbma, buf[1], sizeof(buf[1])), ret,
  147. + zbuf_used(zb), addrlen);
  148. +}
  149. +
  150. +static void nhrp_multicast_forward_nbma(union sockunion *nbma_addr,
  151. + struct interface *ifp, struct zbuf *pkt)
  152. +{
  153. + struct nhrp_peer *p = nhrp_peer_get(ifp, nbma_addr);
  154. +
  155. + if (p && p->online) {
  156. + /* Send packet */
  157. + nhrp_multicast_send(p, pkt);
  158. + }
  159. + nhrp_peer_unref(p);
  160. +}
  161. +
  162. +static void nhrp_multicast_forward_cache(struct nhrp_cache *c, void *pctx)
  163. +{
  164. + struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
  165. +
  166. + if (c->cur.type == NHRP_CACHE_DYNAMIC && c->cur.peer)
  167. + nhrp_multicast_forward_nbma(&c->cur.peer->vc->remote.nbma,
  168. + ctx->ifp, ctx->pkt);
  169. +}
  170. +
  171. +static void nhrp_multicast_forward(struct nhrp_multicast *mcast, void *pctx)
  172. +{
  173. + struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
  174. + struct nhrp_interface *nifp = ctx->ifp->info;
  175. +
  176. + if (!nifp->enabled)
  177. + return;
  178. +
  179. + /* dynamic */
  180. + if (sockunion_family(&mcast->nbma_addr) == AF_UNSPEC) {
  181. + nhrp_cache_foreach(ctx->ifp, nhrp_multicast_forward_cache,
  182. + pctx);
  183. + return;
  184. + }
  185. +
  186. + /* Fixed IP Address */
  187. + nhrp_multicast_forward_nbma(&mcast->nbma_addr, ctx->ifp, ctx->pkt);
  188. +}
  189. +
  190. +static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb)
  191. +{
  192. + struct nfgenmsg *nf;
  193. + struct rtattr *rta;
  194. + struct zbuf rtapl;
  195. + uint32_t *out_ndx = NULL;
  196. + afi_t afi;
  197. + struct mcast_ctx ctx;
  198. +
  199. + nf = znl_pull(zb, sizeof(*nf));
  200. + if (!nf)
  201. + return;
  202. +
  203. + ctx.pkt = NULL;
  204. + while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
  205. + switch (rta->rta_type) {
  206. + case NFULA_IFINDEX_OUTDEV:
  207. + out_ndx = znl_pull(&rtapl, sizeof(*out_ndx));
  208. + break;
  209. + case NFULA_PAYLOAD:
  210. + ctx.pkt = &rtapl;
  211. + break;
  212. + /* NFULA_HWHDR exists and is supposed to contain source
  213. + * hardware address. However, for ip_gre it seems to be
  214. + * the nexthop destination address if the packet matches
  215. + * route.
  216. + */
  217. + }
  218. + }
  219. +
  220. + if (!out_ndx || !ctx.pkt)
  221. + return;
  222. +
  223. + ctx.ifp = if_lookup_by_index(htonl(*out_ndx), VRF_DEFAULT);
  224. + if (!ctx.ifp)
  225. + return;
  226. +
  227. + debugf(NHRP_DEBUG_COMMON, "Received multicast packet on %s len %zu\n",
  228. + ctx.ifp->name, zbuf_used(ctx.pkt));
  229. +
  230. + for (afi = 0; afi < AFI_MAX; afi++) {
  231. + nhrp_multicast_foreach(ctx.ifp, afi, nhrp_multicast_forward,
  232. + (void *)&ctx);
  233. + }
  234. +}
  235. +
  236. +static int netlink_mcast_log_recv(struct thread *t)
  237. +{
  238. + uint8_t buf[65535]; /* Max OSPF Packet size */
  239. + int fd = THREAD_FD(t);
  240. + struct zbuf payload, zb;
  241. + struct nlmsghdr *n;
  242. +
  243. + netlink_mcast_log_thread = NULL;
  244. +
  245. + zbuf_init(&zb, buf, sizeof(buf), 0);
  246. + while (zbuf_recv(&zb, fd) > 0) {
  247. + while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) {
  248. + debugf(NHRP_DEBUG_COMMON,
  249. + "Netlink-mcast-log: Received msg_type %u, msg_flags %u",
  250. + n->nlmsg_type, n->nlmsg_flags);
  251. + switch (n->nlmsg_type) {
  252. + case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET:
  253. + netlink_mcast_log_handler(n, &payload);
  254. + break;
  255. + }
  256. + }
  257. + }
  258. +
  259. + thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
  260. + &netlink_mcast_log_thread);
  261. +
  262. + return 0;
  263. +}
  264. +
  265. +static void netlink_mcast_log_register(int fd, int group)
  266. +{
  267. + struct nlmsghdr *n;
  268. + struct nfgenmsg *nf;
  269. + struct nfulnl_msg_config_cmd cmd;
  270. + struct zbuf *zb = zbuf_alloc(512);
  271. +
  272. + n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG,
  273. + NLM_F_REQUEST | NLM_F_ACK);
  274. + nf = znl_push(zb, sizeof(*nf));
  275. + *nf = (struct nfgenmsg){
  276. + .nfgen_family = AF_UNSPEC,
  277. + .version = NFNETLINK_V0,
  278. + .res_id = htons(group),
  279. + };
  280. + cmd.command = NFULNL_CFG_CMD_BIND;
  281. + znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
  282. + znl_nlmsg_complete(zb, n);
  283. +
  284. + zbuf_send(zb, fd);
  285. + zbuf_free(zb);
  286. +}
  287. +
  288. +void netlink_mcast_set_nflog_group(int nlgroup)
  289. +{
  290. + if (netlink_mcast_log_fd >= 0) {
  291. + THREAD_OFF(netlink_mcast_log_thread);
  292. + close(netlink_mcast_log_fd);
  293. + netlink_mcast_log_fd = -1;
  294. + debugf(NHRP_DEBUG_COMMON, "De-register nflog group");
  295. + }
  296. + netlink_mcast_nflog_group = nlgroup;
  297. + if (nlgroup) {
  298. + netlink_mcast_log_fd = znl_open(NETLINK_NETFILTER, 0);
  299. + if (netlink_mcast_log_fd < 0)
  300. + return;
  301. +
  302. + netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup);
  303. + thread_add_read(master, netlink_mcast_log_recv, 0,
  304. + netlink_mcast_log_fd,
  305. + &netlink_mcast_log_thread);
  306. + debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d",
  307. + netlink_mcast_nflog_group);
  308. + }
  309. +}
  310. +
  311. +static int nhrp_multicast_free(struct interface *ifp,
  312. + struct nhrp_multicast *mcast)
  313. +{
  314. + list_del(&mcast->list_entry);
  315. + XFREE(MTYPE_NHRP_MULTICAST, mcast);
  316. + return 0;
  317. +}
  318. +
  319. +int nhrp_multicast_add(struct interface *ifp, afi_t afi,
  320. + union sockunion *nbma_addr)
  321. +{
  322. + struct nhrp_interface *nifp = ifp->info;
  323. + struct nhrp_multicast *mcast;
  324. + char buf[SU_ADDRSTRLEN];
  325. +
  326. + list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
  327. + {
  328. + if (sockunion_same(&mcast->nbma_addr, nbma_addr))
  329. + return NHRP_ERR_ENTRY_EXISTS;
  330. + }
  331. +
  332. + mcast = XMALLOC(MTYPE_NHRP_MULTICAST, sizeof(struct nhrp_multicast));
  333. +
  334. + *mcast = (struct nhrp_multicast){
  335. + .afi = afi, .ifp = ifp, .nbma_addr = *nbma_addr,
  336. + };
  337. + list_add_tail(&mcast->list_entry, &nifp->afi[afi].mcastlist_head);
  338. +
  339. + sockunion2str(nbma_addr, buf, sizeof(buf));
  340. + debugf(NHRP_DEBUG_COMMON, "Adding multicast entry (%s)", buf);
  341. +
  342. + return NHRP_OK;
  343. +}
  344. +
  345. +int nhrp_multicast_del(struct interface *ifp, afi_t afi,
  346. + union sockunion *nbma_addr)
  347. +{
  348. + struct nhrp_interface *nifp = ifp->info;
  349. + struct nhrp_multicast *mcast, *tmp;
  350. + char buf[SU_ADDRSTRLEN];
  351. +
  352. + list_for_each_entry_safe(mcast, tmp, &nifp->afi[afi].mcastlist_head,
  353. + list_entry)
  354. + {
  355. + if (!sockunion_same(&mcast->nbma_addr, nbma_addr))
  356. + continue;
  357. +
  358. + sockunion2str(nbma_addr, buf, sizeof(buf));
  359. + debugf(NHRP_DEBUG_COMMON, "Deleting multicast entry (%s)", buf);
  360. +
  361. + nhrp_multicast_free(ifp, mcast);
  362. +
  363. + return NHRP_OK;
  364. + }
  365. +
  366. + return NHRP_ERR_ENTRY_NOT_FOUND;
  367. +}
  368. +
  369. +void nhrp_multicast_interface_del(struct interface *ifp)
  370. +{
  371. + struct nhrp_interface *nifp = ifp->info;
  372. + struct nhrp_multicast *mcast, *tmp;
  373. + afi_t afi;
  374. +
  375. + for (afi = 0; afi < AFI_MAX; afi++) {
  376. + debugf(NHRP_DEBUG_COMMON,
  377. + "Cleaning up multicast entries (%d)",
  378. + !list_empty(&nifp->afi[afi].mcastlist_head));
  379. +
  380. + list_for_each_entry_safe(
  381. + mcast, tmp, &nifp->afi[afi].mcastlist_head, list_entry)
  382. + {
  383. + nhrp_multicast_free(ifp, mcast);
  384. + }
  385. + }
  386. +}
  387. +
  388. +void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
  389. + void (*cb)(struct nhrp_multicast *, void *),
  390. + void *ctx)
  391. +{
  392. + struct nhrp_interface *nifp = ifp->info;
  393. + struct nhrp_multicast *mcast;
  394. +
  395. + list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry)
  396. + {
  397. + cb(mcast, ctx);
  398. + }
  399. +}
  400. --- a/nhrpd/nhrp_peer.c
  401. +++ b/nhrpd/nhrp_peer.c
  402. @@ -337,7 +337,7 @@ void nhrp_peer_send(struct nhrp_peer *p,
  403. os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
  404. sockunion_get_addr(&p->vc->remote.nbma),
  405. - sockunion_get_addrlen(&p->vc->remote.nbma));
  406. + sockunion_get_addrlen(&p->vc->remote.nbma), ETH_P_NHRP);
  407. zbuf_reset(zb);
  408. }
  409. --- a/nhrpd/nhrp_vty.c
  410. +++ b/nhrpd/nhrp_vty.c
  411. @@ -187,6 +187,9 @@ static int nhrp_config_write(struct vty
  412. if (netlink_nflog_group) {
  413. vty_out(vty, "nhrp nflog-group %d\n", netlink_nflog_group);
  414. }
  415. + if (netlink_mcast_nflog_group)
  416. + vty_out(vty, "nhrp multicast-nflog-group %d\n",
  417. + netlink_mcast_nflog_group);
  418. return 0;
  419. }
  420. @@ -257,6 +260,31 @@ DEFUN(no_nhrp_nflog_group, no_nhrp_nflog
  421. return CMD_SUCCESS;
  422. }
  423. +DEFUN(nhrp_multicast_nflog_group, nhrp_multicast_nflog_group_cmd,
  424. + "nhrp multicast-nflog-group (1-65535)",
  425. + NHRP_STR
  426. + "Specify NFLOG group number for Multicast Packets\n"
  427. + "NFLOG group number\n")
  428. +{
  429. + uint32_t nfgroup;
  430. +
  431. + nfgroup = strtoul(argv[2]->arg, NULL, 10);
  432. + netlink_mcast_set_nflog_group(nfgroup);
  433. +
  434. + return CMD_SUCCESS;
  435. +}
  436. +
  437. +DEFUN(no_nhrp_multicast_nflog_group, no_nhrp_multicast_nflog_group_cmd,
  438. + "no nhrp multicast-nflog-group [(1-65535)]",
  439. + NO_STR
  440. + NHRP_STR
  441. + "Specify NFLOG group number\n"
  442. + "NFLOG group number\n")
  443. +{
  444. + netlink_mcast_set_nflog_group(0);
  445. + return CMD_SUCCESS;
  446. +}
  447. +
  448. DEFUN(tunnel_protection, tunnel_protection_cmd,
  449. "tunnel protection vici profile PROFILE [fallback-profile FALLBACK]",
  450. "NHRP/GRE integration\n"
  451. @@ -569,6 +597,53 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd
  452. return CMD_SUCCESS;
  453. }
  454. +DEFUN(if_nhrp_map_multicast, if_nhrp_map_multicast_cmd,
  455. + AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
  456. + AFI_STR
  457. + NHRP_STR
  458. + "Multicast NBMA Configuration\n"
  459. + "Use this NBMA mapping for multicasts\n"
  460. + "IPv4 NBMA address\n"
  461. + "IPv6 NBMA address\n"
  462. + "Dynamically learn destinations from client registrations on hub\n")
  463. +{
  464. + VTY_DECLVAR_CONTEXT(interface, ifp);
  465. + afi_t afi = cmd_to_afi(argv[0]);
  466. + union sockunion nbma_addr;
  467. + int ret;
  468. +
  469. + if (str2sockunion(argv[4]->arg, &nbma_addr) < 0)
  470. + sockunion_family(&nbma_addr) = AF_UNSPEC;
  471. +
  472. + ret = nhrp_multicast_add(ifp, afi, &nbma_addr);
  473. +
  474. + return nhrp_vty_return(vty, ret);
  475. +}
  476. +
  477. +DEFUN(if_no_nhrp_map_multicast, if_no_nhrp_map_multicast_cmd,
  478. + "no " AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>",
  479. + NO_STR
  480. + AFI_STR
  481. + NHRP_STR
  482. + "Multicast NBMA Configuration\n"
  483. + "Use this NBMA mapping for multicasts\n"
  484. + "IPv4 NBMA address\n"
  485. + "IPv6 NBMA address\n"
  486. + "Dynamically learn destinations from client registrations on hub\n")
  487. +{
  488. + VTY_DECLVAR_CONTEXT(interface, ifp);
  489. + afi_t afi = cmd_to_afi(argv[1]);
  490. + union sockunion nbma_addr;
  491. + int ret;
  492. +
  493. + if (str2sockunion(argv[5]->arg, &nbma_addr) < 0)
  494. + sockunion_family(&nbma_addr) = AF_UNSPEC;
  495. +
  496. + ret = nhrp_multicast_del(ifp, afi, &nbma_addr);
  497. +
  498. + return nhrp_vty_return(vty, ret);
  499. +}
  500. +
  501. DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
  502. AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>",
  503. AFI_STR
  504. @@ -644,8 +719,8 @@ static void show_ip_nhrp_cache(struct nh
  505. sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0]));
  506. if (c->cur.peer)
  507. - sockunion2str(&c->cur.peer->vc->remote.nbma,
  508. - buf[1], sizeof(buf[1]));
  509. + sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1],
  510. + sizeof(buf[1]));
  511. else
  512. snprintf(buf[1], sizeof(buf[1]), "-");
  513. @@ -704,8 +779,8 @@ static void show_ip_nhrp_nhs(struct nhrp
  514. ctx->count++;
  515. if (reg && reg->peer)
  516. - sockunion2str(&reg->peer->vc->remote.nbma,
  517. - buf[0], sizeof(buf[0]));
  518. + sockunion2str(&reg->peer->vc->remote.nbma, buf[0],
  519. + sizeof(buf[0]));
  520. else
  521. snprintf(buf[0], sizeof(buf[0]), "-");
  522. sockunion2str(reg ? &reg->proto_addr : &n->proto_addr, buf[1],
  523. @@ -1018,7 +1093,8 @@ struct write_map_ctx {
  524. const char *aficmd;
  525. };
  526. -static void interface_config_write_nhrp_map(struct nhrp_cache_config *c, void *data)
  527. +static void interface_config_write_nhrp_map(struct nhrp_cache_config *c,
  528. + void *data)
  529. {
  530. struct write_map_ctx *ctx = data;
  531. struct vty *vty = ctx->vty;
  532. @@ -1030,7 +1106,8 @@ static void interface_config_write_nhrp_
  533. vty_out(vty, " %s nhrp map %s %s\n", ctx->aficmd,
  534. sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])),
  535. c->type == NHRP_CACHE_LOCAL
  536. - ? "local" : sockunion2str(&c->nbma, buf[1], sizeof(buf[1])));
  537. + ? "local"
  538. + : sockunion2str(&c->nbma, buf[1], sizeof(buf[1])));
  539. }
  540. static int interface_config_write(struct vty *vty)
  541. @@ -1040,6 +1117,7 @@ static int interface_config_write(struct
  542. struct interface *ifp;
  543. struct nhrp_interface *nifp;
  544. struct nhrp_nhs *nhs;
  545. + struct nhrp_multicast *mcast;
  546. const char *aficmd;
  547. afi_t afi;
  548. char buf[SU_ADDRSTRLEN];
  549. @@ -1093,8 +1171,8 @@ static int interface_config_write(struct
  550. .family = afi2family(afi),
  551. .aficmd = aficmd,
  552. };
  553. - nhrp_cache_config_foreach(ifp, interface_config_write_nhrp_map,
  554. - &mapctx);
  555. + nhrp_cache_config_foreach(
  556. + ifp, interface_config_write_nhrp_map, &mapctx);
  557. list_for_each_entry(nhs, &ad->nhslist_head,
  558. nhslist_entry)
  559. @@ -1109,6 +1187,19 @@ static int interface_config_write(struct
  560. sizeof(buf)),
  561. nhs->nbma_fqdn);
  562. }
  563. +
  564. + list_for_each_entry(mcast, &ad->mcastlist_head,
  565. + list_entry)
  566. + {
  567. + vty_out(vty, " %s nhrp map multicast %s\n",
  568. + aficmd,
  569. + sockunion_family(&mcast->nbma_addr)
  570. + == AF_UNSPEC
  571. + ? "dynamic"
  572. + : sockunion2str(
  573. + &mcast->nbma_addr,
  574. + buf, sizeof(buf)));
  575. + }
  576. }
  577. vty_endframe(vty, "!\n");
  578. @@ -1142,6 +1233,8 @@ void nhrp_config_init(void)
  579. install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd);
  580. install_element(CONFIG_NODE, &nhrp_nflog_group_cmd);
  581. install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd);
  582. + install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd);
  583. + install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd);
  584. /* interface specific commands */
  585. install_node(&nhrp_interface_node);
  586. @@ -1163,6 +1256,8 @@ void nhrp_config_init(void)
  587. install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
  588. install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
  589. install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd);
  590. + install_element(INTERFACE_NODE, &if_nhrp_map_multicast_cmd);
  591. + install_element(INTERFACE_NODE, &if_no_nhrp_map_multicast_cmd);
  592. install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
  593. install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
  594. }
  595. --- a/nhrpd/nhrpd.h
  596. +++ b/nhrpd/nhrpd.h
  597. @@ -259,6 +259,13 @@ struct nhrp_nhs {
  598. struct list_head reglist_head;
  599. };
  600. +struct nhrp_multicast {
  601. + struct interface *ifp;
  602. + struct list_head list_entry;
  603. + afi_t afi;
  604. + union sockunion nbma_addr; /* IP-address */
  605. +};
  606. +
  607. struct nhrp_registration {
  608. struct list_head reglist_entry;
  609. struct thread *t_register;
  610. @@ -304,6 +311,7 @@ struct nhrp_interface {
  611. unsigned short mtu;
  612. unsigned int holdtime;
  613. struct list_head nhslist_head;
  614. + struct list_head mcastlist_head;
  615. } afi[AFI_MAX];
  616. };
  617. @@ -345,6 +353,16 @@ void nhrp_nhs_foreach(struct interface *
  618. void *ctx);
  619. void nhrp_nhs_interface_del(struct interface *ifp);
  620. +int nhrp_multicast_add(struct interface *ifp, afi_t afi,
  621. + union sockunion *nbma_addr);
  622. +int nhrp_multicast_del(struct interface *ifp, afi_t afi,
  623. + union sockunion *nbma_addr);
  624. +void nhrp_multicast_interface_del(struct interface *ifp);
  625. +void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
  626. + void (*cb)(struct nhrp_multicast *, void *),
  627. + void *ctx);
  628. +void netlink_mcast_set_nflog_group(int nlgroup);
  629. +
  630. void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
  631. void nhrp_route_announce(int add, enum nhrp_cache_type type,
  632. const struct prefix *p, struct interface *ifp,
  633. --- a/nhrpd/os.h
  634. +++ b/nhrpd/os.h
  635. @@ -1,7 +1,7 @@
  636. int os_socket(void);
  637. int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
  638. - size_t addrlen);
  639. + size_t addrlen, uint16_t protocol);
  640. int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr,
  641. size_t *addrlen);
  642. int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
  643. --- a/nhrpd/subdir.am
  644. +++ b/nhrpd/subdir.am
  645. @@ -21,6 +21,7 @@ nhrpd_nhrpd_SOURCES = \
  646. nhrpd/nhrp_nhs.c \
  647. nhrpd/nhrp_packet.c \
  648. nhrpd/nhrp_peer.c \
  649. + nhrpd/nhrp_multicast.c \
  650. nhrpd/nhrp_route.c \
  651. nhrpd/nhrp_shortcut.c \
  652. nhrpd/nhrp_vc.c \
  653. --- a/ospfd/ospf_interface.c
  654. +++ b/ospfd/ospf_interface.c
  655. @@ -534,6 +534,8 @@ static struct ospf_if_params *ospf_new_i
  656. oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER);
  657. oip->is_v_wait_set = false;
  658. + oip->ptp_dmvpn = 0;
  659. +
  660. return oip;
  661. }
  662. --- a/ospfd/ospf_interface.h
  663. +++ b/ospfd/ospf_interface.h
  664. @@ -105,6 +105,9 @@ struct ospf_if_params {
  665. /* BFD configuration */
  666. struct bfd_info *bfd_info;
  667. +
  668. + /* point-to-point DMVPN configuration */
  669. + uint8_t ptp_dmvpn;
  670. };
  671. enum { MEMBER_ALLROUTERS = 0,
  672. @@ -167,6 +170,9 @@ struct ospf_interface {
  673. /* OSPF Network Type. */
  674. uint8_t type;
  675. + /* point-to-point DMVPN configuration */
  676. + uint8_t ptp_dmvpn;
  677. +
  678. /* State of Interface State Machine. */
  679. uint8_t state;
  680. --- a/ospfd/ospf_lsa.c
  681. +++ b/ospfd/ospf_lsa.c
  682. @@ -469,6 +469,12 @@ static char link_info_set(struct stream
  683. }
  684. /* Describe Point-to-Point link (Section 12.4.1.1). */
  685. +
  686. +/* Note: If the interface is configured as point-to-point dmvpn then the other
  687. + * end of link is dmvpn hub with point-to-multipoint ospf network type. The
  688. + * hub then expects this router to populate the stub network and also Link Data
  689. + * Field set to IP Address and not MIB-II ifIndex
  690. + */
  691. static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi)
  692. {
  693. int links = 0;
  694. @@ -482,7 +488,8 @@ static int lsa_link_ptop_set(struct stre
  695. if ((nbr = ospf_nbr_lookup_ptop(oi)))
  696. if (nbr->state == NSM_Full) {
  697. if (CHECK_FLAG(oi->connected->flags,
  698. - ZEBRA_IFA_UNNUMBERED)) {
  699. + ZEBRA_IFA_UNNUMBERED)
  700. + && !oi->ptp_dmvpn) {
  701. /* For unnumbered point-to-point networks, the
  702. Link Data field
  703. should specify the interface's MIB-II ifIndex
  704. @@ -500,7 +507,8 @@ static int lsa_link_ptop_set(struct stre
  705. }
  706. /* no need for a stub link for unnumbered interfaces */
  707. - if (!CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
  708. + if (oi->ptp_dmvpn
  709. + || !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) {
  710. /* Regardless of the state of the neighboring router, we must
  711. add a Type 3 link (stub network).
  712. N.B. Options 1 & 2 share basically the same logic. */
  713. --- a/ospfd/ospf_vty.c
  714. +++ b/ospfd/ospf_vty.c
  715. @@ -7562,20 +7562,21 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
  716. return no_ip_ospf_hello_interval(self, vty, argc, argv);
  717. }
  718. -DEFUN (ip_ospf_network,
  719. - ip_ospf_network_cmd,
  720. - "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point>",
  721. - "IP Information\n"
  722. - "OSPF interface commands\n"
  723. - "Network type\n"
  724. - "Specify OSPF broadcast multi-access network\n"
  725. - "Specify OSPF NBMA network\n"
  726. - "Specify OSPF point-to-multipoint network\n"
  727. - "Specify OSPF point-to-point network\n")
  728. +DEFUN(ip_ospf_network, ip_ospf_network_cmd,
  729. + "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>",
  730. + "IP Information\n"
  731. + "OSPF interface commands\n"
  732. + "Network type\n"
  733. + "Specify OSPF broadcast multi-access network\n"
  734. + "Specify OSPF NBMA network\n"
  735. + "Specify OSPF point-to-multipoint network\n"
  736. + "Specify OSPF point-to-point network\n"
  737. + "Specify OSPF point-to-point DMVPN network\n")
  738. {
  739. VTY_DECLVAR_CONTEXT(interface, ifp);
  740. int idx = 0;
  741. int old_type = IF_DEF_PARAMS(ifp)->type;
  742. + uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
  743. struct route_node *rn;
  744. if (old_type == OSPF_IFTYPE_LOOPBACK) {
  745. @@ -7584,16 +7585,22 @@ DEFUN (ip_ospf_network,
  746. return CMD_WARNING_CONFIG_FAILED;
  747. }
  748. + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
  749. +
  750. if (argv_find(argv, argc, "broadcast", &idx))
  751. IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST;
  752. else if (argv_find(argv, argc, "non-broadcast", &idx))
  753. IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
  754. else if (argv_find(argv, argc, "point-to-multipoint", &idx))
  755. IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
  756. - else if (argv_find(argv, argc, "point-to-point", &idx))
  757. + else if (argv_find(argv, argc, "point-to-point", &idx)) {
  758. IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
  759. + if (argv_find(argv, argc, "dmvpn", &idx))
  760. + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
  761. + }
  762. - if (IF_DEF_PARAMS(ifp)->type == old_type)
  763. + if (IF_DEF_PARAMS(ifp)->type == old_type
  764. + && IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn)
  765. return CMD_SUCCESS;
  766. SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
  767. @@ -7605,6 +7612,7 @@ DEFUN (ip_ospf_network,
  768. continue;
  769. oi->type = IF_DEF_PARAMS(ifp)->type;
  770. + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
  771. if (oi->state > ISM_Down) {
  772. OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
  773. @@ -7645,6 +7653,7 @@ DEFUN (no_ip_ospf_network,
  774. struct route_node *rn;
  775. IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
  776. + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
  777. if (IF_DEF_PARAMS(ifp)->type == old_type)
  778. return CMD_SUCCESS;
  779. @@ -9888,6 +9897,10 @@ static int config_write_interface_one(st
  780. vty_out(vty, " ip ospf network %s",
  781. ospf_int_type_str
  782. [params->type]);
  783. + if (params->type
  784. + == OSPF_IFTYPE_POINTOPOINT
  785. + && params->ptp_dmvpn)
  786. + vty_out(vty, " dmvpn");
  787. if (params != IF_DEF_PARAMS(ifp) && rn)
  788. vty_out(vty, " %s",
  789. inet_ntoa(
  790. --- a/ospfd/ospfd.c
  791. +++ b/ospfd/ospfd.c
  792. @@ -1004,6 +1004,7 @@ static void add_ospf_interface(struct co
  793. /* If network type is specified previously,
  794. skip network type setting. */
  795. oi->type = IF_DEF_PARAMS(co->ifp)->type;
  796. + oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn;
  797. /* Add pseudo neighbor. */
  798. ospf_nbr_self_reset(oi, oi->ospf->router_id);
  799. --- a/doc/user/nhrpd.rst
  800. +++ b/doc/user/nhrpd.rst
  801. @@ -189,6 +189,37 @@ and
  802. https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras
  803. git repositories for the patches.
  804. +.. _multicast-functionality:
  805. +
  806. +Multicast Functionality
  807. +=======================
  808. +
  809. +nhrpd can be configured to forward multicast packets, allowing routing
  810. +protocols that use multicast (such as OSPF) to be supported in the DMVPN
  811. +network.
  812. +
  813. +This support requires an iptables NFLOG rule to allow nhrpd to intercept
  814. +multicast packets. A second iptables rule is also usually used to drop the
  815. +original multicast packet.
  816. +
  817. + .. code-block:: shell
  818. +
  819. + iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 2
  820. + iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j DROP
  821. +
  822. +.. index:: nhrp multicast-nflog-group (1-65535)
  823. +.. clicmd:: nhrp multicast-nflog-group (1-65535)
  824. +
  825. + Sets the nflog group that nhrpd will listen on for multicast packets. This
  826. + value must match the nflog-group value set in the iptables rule.
  827. +
  828. +.. index:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
  829. +.. clicmd:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic
  830. +
  831. + Sends multicast packets to the specified NBMA address. If dynamic is
  832. + specified then destination NBMA address (or addresses) are learnt
  833. + dynamically.
  834. +
  835. .. _nhrp-events:
  836. NHRP Events
  837. --- a/doc/user/ospfd.rst
  838. +++ b/doc/user/ospfd.rst
  839. @@ -687,8 +687,8 @@ Interfaces
  840. :clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
  841. specified for the interface.
  842. -.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
  843. -.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)
  844. +.. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
  845. +.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
  846. When configuring a point-to-point network on an interface and the interface
  847. has a /32 address associated with then OSPF will treat the interface
  848. @@ -870,6 +870,9 @@ Redistribution
  849. .. index:: no router zebra
  850. .. clicmd:: no router zebra
  851. + When used in a DMVPN network at a spoke, this OSPF will be configured in
  852. + point-to-point, but the HUB will be a point-to-multipoint. To make this
  853. + topology work, specify the optional 'dmvpn' parameter at the spoke.
  854. .. _showing-ospf-information:
  855. --- a/nhrpd/netlink.h
  856. +++ b/nhrpd/netlink.h
  857. @@ -13,6 +13,7 @@ union sockunion;
  858. struct interface;
  859. extern int netlink_nflog_group;
  860. +extern int netlink_mcast_nflog_group;
  861. extern int netlink_req_fd;
  862. void netlink_init(void);
  863. --- a/ospfd/ospf_packet.c
  864. +++ b/ospfd/ospf_packet.c
  865. @@ -802,7 +802,13 @@ static int ospf_write(struct thread *thr
  866. inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off,
  867. iph.ip_len, oi->ifp->name, oi->ifp->mtu);
  868. - if (ret < 0)
  869. + /* sendmsg will return EPERM if firewall is blocking sending.
  870. + * This is a normal situation when 'ip nhrp map multicast xxx'
  871. + * is being used to send multicast packets to DMVPN peers. In
  872. + * that case the original message is blocked with iptables rule
  873. + * causing the EPERM result
  874. + */
  875. + if (ret < 0 && errno != EPERM)
  876. flog_err(
  877. EC_LIB_SOCKET,
  878. "*** sendmsg in ospf_write failed to %s, id %d, off %d, len %d, interface %s, mtu %u: %s",
  879. @@ -910,8 +916,11 @@ static void ospf_hello(struct ip *iph, s
  880. /* Compare network mask. */
  881. /* Checking is ignored for Point-to-Point and Virtual link. */
  882. + /* Checking is also ignored for Point-to-Multipoint with /32 prefix */
  883. if (oi->type != OSPF_IFTYPE_POINTOPOINT
  884. - && oi->type != OSPF_IFTYPE_VIRTUALLINK)
  885. + && oi->type != OSPF_IFTYPE_VIRTUALLINK
  886. + && !(oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
  887. + && oi->address->prefixlen == IPV4_MAX_BITLEN))
  888. if (oi->address->prefixlen != p.prefixlen) {
  889. flog_warn(
  890. EC_OSPF_PACKET,
  891. @@ -2439,6 +2448,11 @@ static int ospf_check_network_mask(struc
  892. || oi->type == OSPF_IFTYPE_VIRTUALLINK)
  893. return 1;
  894. + /* Ignore mask check for max prefix length (32) */
  895. + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
  896. + && oi->address->prefixlen == IPV4_MAX_BITLEN)
  897. + return 1;
  898. +
  899. masklen2ip(oi->address->prefixlen, &mask);
  900. me.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr;