]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_multicast.c
Merge pull request #10770 from chiragshah6/evpn_dev3
[mirror_frr.git] / nhrpd / nhrp_multicast.c
CommitLineData
fa31fcf2
AL
1/* NHRP Multicast Support
2 * Copyright (c) 2020-2021 4RF Limited
3 *
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
14#include <fcntl.h>
15#include <net/if.h>
16#include <net/ethernet.h>
17#include <netinet/if_ether.h>
18#include <linux/netlink.h>
19#include <linux/neighbour.h>
20#include <linux/netfilter/nfnetlink_log.h>
21#include <linux/if_packet.h>
22#include <sys/types.h>
23#include <sys/socket.h>
24
25#include "thread.h"
26#include "nhrpd.h"
27#include "netlink.h"
28#include "znl.h"
29#include "os.h"
30
49463161 31DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast");
fa31fcf2 32
9084e209 33int netlink_mcast_nflog_group;
fa31fcf2
AL
34static int netlink_mcast_log_fd = -1;
35static struct thread *netlink_mcast_log_thread;
36
37struct mcast_ctx {
38 struct interface *ifp;
39 struct zbuf *pkt;
40};
41
42static void nhrp_multicast_send(struct nhrp_peer *p, struct zbuf *zb)
43{
fa31fcf2
AL
44 size_t addrlen;
45 int ret;
46
47 addrlen = sockunion_get_addrlen(&p->vc->remote.nbma);
48 ret = os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
0f8595a9 49 sockunion_get_addr(&p->vc->remote.nbma), addrlen,
9f7f6d3c 50 addrlen == 4 ? ETH_P_IP : ETH_P_IPV6);
0f8595a9
RD
51
52 debugf(NHRP_DEBUG_COMMON,
46d3c185
RD
53 "Multicast Packet: %pSU -> %pSU, ret = %d, size = %zu, addrlen = %zu",
54 &p->vc->local.nbma, &p->vc->remote.nbma, ret, zbuf_used(zb),
55 addrlen);
fa31fcf2
AL
56}
57
0f8595a9
RD
58static void nhrp_multicast_forward_nbma(union sockunion *nbma_addr,
59 struct interface *ifp, struct zbuf *pkt)
fa31fcf2
AL
60{
61 struct nhrp_peer *p = nhrp_peer_get(ifp, nbma_addr);
0f8595a9
RD
62
63 if (p && p->online) {
fa31fcf2
AL
64 /* Send packet */
65 nhrp_multicast_send(p, pkt);
fa31fcf2 66 }
0da7701a 67 nhrp_peer_unref(p);
fa31fcf2
AL
68}
69
70static void nhrp_multicast_forward_cache(struct nhrp_cache *c, void *pctx)
71{
72 struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
73
74 if (c->cur.type == NHRP_CACHE_DYNAMIC && c->cur.peer)
0f8595a9
RD
75 nhrp_multicast_forward_nbma(&c->cur.peer->vc->remote.nbma,
76 ctx->ifp, ctx->pkt);
fa31fcf2
AL
77}
78
79static void nhrp_multicast_forward(struct nhrp_multicast *mcast, void *pctx)
80{
81 struct mcast_ctx *ctx = (struct mcast_ctx *)pctx;
82 struct nhrp_interface *nifp = ctx->ifp->info;
83
84 if (!nifp->enabled)
85 return;
86
87 /* dynamic */
88 if (sockunion_family(&mcast->nbma_addr) == AF_UNSPEC) {
0f8595a9
RD
89 nhrp_cache_foreach(ctx->ifp, nhrp_multicast_forward_cache,
90 pctx);
fa31fcf2
AL
91 return;
92 }
93
94 /* Fixed IP Address */
95 nhrp_multicast_forward_nbma(&mcast->nbma_addr, ctx->ifp, ctx->pkt);
96}
97
98static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb)
99{
100 struct nfgenmsg *nf;
101 struct rtattr *rta;
63c0a735 102 struct zbuf rtapl;
fa31fcf2
AL
103 uint32_t *out_ndx = NULL;
104 afi_t afi;
105 struct mcast_ctx ctx;
106
fa31fcf2
AL
107 nf = znl_pull(zb, sizeof(*nf));
108 if (!nf)
109 return;
110
63c0a735 111 ctx.pkt = NULL;
fa31fcf2
AL
112 while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
113 switch (rta->rta_type) {
114 case NFULA_IFINDEX_OUTDEV:
115 out_ndx = znl_pull(&rtapl, sizeof(*out_ndx));
116 break;
117 case NFULA_PAYLOAD:
63c0a735 118 ctx.pkt = &rtapl;
fa31fcf2
AL
119 break;
120 /* NFULA_HWHDR exists and is supposed to contain source
121 * hardware address. However, for ip_gre it seems to be
122 * the nexthop destination address if the packet matches
0f8595a9
RD
123 * route.
124 */
fa31fcf2
AL
125 }
126 }
127
63c0a735 128 if (!out_ndx || !ctx.pkt)
fa31fcf2
AL
129 return;
130
63c0a735
RD
131 ctx.ifp = if_lookup_by_index(htonl(*out_ndx), VRF_DEFAULT);
132 if (!ctx.ifp)
fa31fcf2
AL
133 return;
134
c2bb9917
RD
135 debugf(NHRP_DEBUG_COMMON,
136 "Intercepted multicast packet leaving %s len %zu",
137 ctx.ifp->name, zbuf_used(ctx.pkt));
fa31fcf2
AL
138
139 for (afi = 0; afi < AFI_MAX; afi++) {
63c0a735 140 nhrp_multicast_foreach(ctx.ifp, afi, nhrp_multicast_forward,
0f8595a9 141 (void *)&ctx);
fa31fcf2
AL
142 }
143}
144
cc9f21da 145static void netlink_mcast_log_recv(struct thread *t)
fa31fcf2
AL
146{
147 uint8_t buf[65535]; /* Max OSPF Packet size */
148 int fd = THREAD_FD(t);
149 struct zbuf payload, zb;
150 struct nlmsghdr *n;
151
fa31fcf2
AL
152
153 zbuf_init(&zb, buf, sizeof(buf), 0);
154 while (zbuf_recv(&zb, fd) > 0) {
155 while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) {
156 debugf(NHRP_DEBUG_COMMON,
157 "Netlink-mcast-log: Received msg_type %u, msg_flags %u",
158 n->nlmsg_type, n->nlmsg_flags);
159 switch (n->nlmsg_type) {
160 case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET:
161 netlink_mcast_log_handler(n, &payload);
162 break;
163 }
164 }
165 }
166
167 thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd,
168 &netlink_mcast_log_thread);
fa31fcf2
AL
169}
170
171static void netlink_mcast_log_register(int fd, int group)
172{
173 struct nlmsghdr *n;
174 struct nfgenmsg *nf;
175 struct nfulnl_msg_config_cmd cmd;
176 struct zbuf *zb = zbuf_alloc(512);
177
178 n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG,
179 NLM_F_REQUEST | NLM_F_ACK);
180 nf = znl_push(zb, sizeof(*nf));
181 *nf = (struct nfgenmsg){
182 .nfgen_family = AF_UNSPEC,
183 .version = NFNETLINK_V0,
184 .res_id = htons(group),
185 };
186 cmd.command = NFULNL_CFG_CMD_BIND;
187 znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
188 znl_nlmsg_complete(zb, n);
189
190 zbuf_send(zb, fd);
191 zbuf_free(zb);
192}
193
9084e209 194void netlink_mcast_set_nflog_group(int nlgroup)
fa31fcf2
AL
195{
196 if (netlink_mcast_log_fd >= 0) {
197 THREAD_OFF(netlink_mcast_log_thread);
198 close(netlink_mcast_log_fd);
199 netlink_mcast_log_fd = -1;
200 debugf(NHRP_DEBUG_COMMON, "De-register nflog group");
201 }
202 netlink_mcast_nflog_group = nlgroup;
203 if (nlgroup) {
204 netlink_mcast_log_fd = znl_open(NETLINK_NETFILTER, 0);
205 if (netlink_mcast_log_fd < 0)
206 return;
207
208 netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup);
0f8595a9
RD
209 thread_add_read(master, netlink_mcast_log_recv, 0,
210 netlink_mcast_log_fd,
fa31fcf2 211 &netlink_mcast_log_thread);
0f8595a9
RD
212 debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d",
213 netlink_mcast_nflog_group);
fa31fcf2
AL
214 }
215}
216
9084e209
AL
217static int nhrp_multicast_free(struct interface *ifp,
218 struct nhrp_multicast *mcast)
219{
db4db2bb
DL
220 struct nhrp_interface *nifp = ifp->info;
221
222 nhrp_mcastlist_del(&nifp->afi[mcast->afi].mcastlist_head, mcast);
9084e209
AL
223 XFREE(MTYPE_NHRP_MULTICAST, mcast);
224 return 0;
225}
226
0f8595a9
RD
227int nhrp_multicast_add(struct interface *ifp, afi_t afi,
228 union sockunion *nbma_addr)
fa31fcf2
AL
229{
230 struct nhrp_interface *nifp = ifp->info;
231 struct nhrp_multicast *mcast;
fa31fcf2 232
db4db2bb 233 frr_each (nhrp_mcastlist, &nifp->afi[afi].mcastlist_head, mcast) {
fa31fcf2
AL
234 if (sockunion_same(&mcast->nbma_addr, nbma_addr))
235 return NHRP_ERR_ENTRY_EXISTS;
236 }
237
238 mcast = XMALLOC(MTYPE_NHRP_MULTICAST, sizeof(struct nhrp_multicast));
239
240 *mcast = (struct nhrp_multicast){
0f8595a9 241 .afi = afi, .ifp = ifp, .nbma_addr = *nbma_addr,
fa31fcf2 242 };
db4db2bb 243 nhrp_mcastlist_add_tail(&nifp->afi[afi].mcastlist_head, mcast);
fa31fcf2 244
46d3c185 245 debugf(NHRP_DEBUG_COMMON, "Adding multicast entry (%pSU)", nbma_addr);
fa31fcf2
AL
246
247 return NHRP_OK;
248}
249
0f8595a9
RD
250int nhrp_multicast_del(struct interface *ifp, afi_t afi,
251 union sockunion *nbma_addr)
fa31fcf2
AL
252{
253 struct nhrp_interface *nifp = ifp->info;
db4db2bb 254 struct nhrp_multicast *mcast;
fa31fcf2 255
db4db2bb 256 frr_each_safe (nhrp_mcastlist, &nifp->afi[afi].mcastlist_head, mcast) {
fa31fcf2
AL
257 if (!sockunion_same(&mcast->nbma_addr, nbma_addr))
258 continue;
259
46d3c185
RD
260 debugf(NHRP_DEBUG_COMMON, "Deleting multicast entry (%pSU)",
261 nbma_addr);
fa31fcf2
AL
262
263 nhrp_multicast_free(ifp, mcast);
264
265 return NHRP_OK;
266 }
267
268 return NHRP_ERR_ENTRY_NOT_FOUND;
269}
270
271void nhrp_multicast_interface_del(struct interface *ifp)
272{
273 struct nhrp_interface *nifp = ifp->info;
db4db2bb 274 struct nhrp_multicast *mcast;
fa31fcf2
AL
275 afi_t afi;
276
277 for (afi = 0; afi < AFI_MAX; afi++) {
db4db2bb
DL
278 debugf(NHRP_DEBUG_COMMON, "Cleaning up multicast entries (%zu)",
279 nhrp_mcastlist_count(&nifp->afi[afi].mcastlist_head));
fa31fcf2 280
db4db2bb
DL
281 frr_each_safe (nhrp_mcastlist, &nifp->afi[afi].mcastlist_head,
282 mcast) {
fa31fcf2
AL
283 nhrp_multicast_free(ifp, mcast);
284 }
285 }
286}
287
288void nhrp_multicast_foreach(struct interface *ifp, afi_t afi,
0f8595a9
RD
289 void (*cb)(struct nhrp_multicast *, void *),
290 void *ctx)
fa31fcf2
AL
291{
292 struct nhrp_interface *nifp = ifp->info;
293 struct nhrp_multicast *mcast;
294
db4db2bb 295 frr_each (nhrp_mcastlist, &nifp->afi[afi].mcastlist_head, mcast) {
0f8595a9 296 cb(mcast, ctx);
fa31fcf2
AL
297 }
298}