]>
Commit | Line | Data |
---|---|---|
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 | 31 | DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast"); |
fa31fcf2 | 32 | |
9084e209 | 33 | int netlink_mcast_nflog_group; |
fa31fcf2 AL |
34 | static int netlink_mcast_log_fd = -1; |
35 | static struct thread *netlink_mcast_log_thread; | |
36 | ||
37 | struct mcast_ctx { | |
38 | struct interface *ifp; | |
39 | struct zbuf *pkt; | |
40 | }; | |
41 | ||
42 | static 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 |
58 | static 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 | ||
70 | static 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 | ||
79 | static 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 | ||
98 | static 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 | 145 | static 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 | ||
171 | static 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 | 194 | void 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 |
217 | static 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 |
227 | int 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 |
250 | int 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 | ||
271 | void 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 | ||
288 | void 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 | } |