/src/frr/pimd/pim_mroute.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * PIM for Quagga |
4 | | * Copyright (C) 2008 Everton da Silva Marques |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | #include "log.h" |
9 | | #include "privs.h" |
10 | | #include "if.h" |
11 | | #include "prefix.h" |
12 | | #include "vty.h" |
13 | | #include "plist.h" |
14 | | #include "sockopt.h" |
15 | | #include "lib_errors.h" |
16 | | #include "lib/network.h" |
17 | | |
18 | | #include "pimd.h" |
19 | | #include "pim_rpf.h" |
20 | | #include "pim_mroute.h" |
21 | | #include "pim_oil.h" |
22 | | #include "pim_str.h" |
23 | | #include "pim_time.h" |
24 | | #include "pim_iface.h" |
25 | | #include "pim_macro.h" |
26 | | #include "pim_rp.h" |
27 | | #include "pim_oil.h" |
28 | | #include "pim_register.h" |
29 | | #include "pim_ifchannel.h" |
30 | | #include "pim_zlookup.h" |
31 | | #include "pim_ssm.h" |
32 | | #include "pim_sock.h" |
33 | | #include "pim_vxlan.h" |
34 | | #include "pim_msg.h" |
35 | | |
36 | | static void mroute_read_on(struct pim_instance *pim); |
37 | | static int pim_upstream_mroute_update(struct channel_oil *c_oil, |
38 | | const char *name); |
39 | | |
40 | | int pim_mroute_set(struct pim_instance *pim, int enable) |
41 | 0 | { |
42 | 0 | int err; |
43 | 0 | int opt, data; |
44 | 0 | socklen_t data_len = sizeof(data); |
45 | | |
46 | | /* |
47 | | * We need to create the VRF table for the pim mroute_socket |
48 | | */ |
49 | 0 | if (enable && pim->vrf->vrf_id != VRF_DEFAULT) { |
50 | 0 | frr_with_privs (&pimd_privs) { |
51 | |
|
52 | 0 | data = pim->vrf->data.l.table_id; |
53 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, |
54 | 0 | MRT_TABLE, &data, data_len); |
55 | 0 | if (err) { |
56 | 0 | zlog_warn( |
57 | 0 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO, MRT_TABLE=%d): errno=%d: %s", |
58 | 0 | __FILE__, __func__, pim->mroute_socket, |
59 | 0 | data, errno, safe_strerror(errno)); |
60 | 0 | return -1; |
61 | 0 | } |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | 0 | frr_with_privs (&pimd_privs) { |
66 | 0 | opt = enable ? MRT_INIT : MRT_DONE; |
67 | | /* |
68 | | * *BSD *cares* about what value we pass down |
69 | | * here |
70 | | */ |
71 | 0 | data = 1; |
72 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data, |
73 | 0 | data_len); |
74 | 0 | if (err) { |
75 | 0 | zlog_warn( |
76 | 0 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,%s=%d): errno=%d: %s", |
77 | 0 | __FILE__, __func__, pim->mroute_socket, |
78 | 0 | enable ? "MRT_INIT" : "MRT_DONE", data, errno, |
79 | 0 | safe_strerror(errno)); |
80 | 0 | return -1; |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | 0 | #if defined(HAVE_IP_PKTINFO) |
85 | 0 | if (enable) { |
86 | | /* Linux and Solaris IP_PKTINFO */ |
87 | 0 | data = 1; |
88 | 0 | if (setsockopt(pim->mroute_socket, PIM_IPPROTO, IP_PKTINFO, |
89 | 0 | &data, data_len)) { |
90 | 0 | zlog_warn( |
91 | 0 | "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", |
92 | 0 | pim->mroute_socket, errno, |
93 | 0 | safe_strerror(errno)); |
94 | 0 | } |
95 | 0 | } |
96 | 0 | #endif |
97 | |
|
98 | | #if PIM_IPV == 6 |
99 | | if (enable) { |
100 | | /* Linux and Solaris IPV6_PKTINFO */ |
101 | | data = 1; |
102 | | if (setsockopt(pim->mroute_socket, PIM_IPPROTO, |
103 | | IPV6_RECVPKTINFO, &data, data_len)) { |
104 | | zlog_warn( |
105 | | "Could not set IPV6_RECVPKTINFO on socket fd=%d: errno=%d: %s", |
106 | | pim->mroute_socket, errno, |
107 | | safe_strerror(errno)); |
108 | | } |
109 | | } |
110 | | #endif |
111 | 0 | setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8); |
112 | |
|
113 | 0 | if (set_nonblocking(pim->mroute_socket) < 0) { |
114 | 0 | zlog_warn( |
115 | 0 | "Could not set non blocking on socket fd=%d: errno=%d: %s", |
116 | 0 | pim->mroute_socket, errno, safe_strerror(errno)); |
117 | 0 | return -1; |
118 | 0 | } |
119 | | |
120 | 0 | if (enable) { |
121 | 0 | #if defined linux |
122 | 0 | int upcalls = GMMSG_WRVIFWHOLE; |
123 | 0 | opt = MRT_PIM; |
124 | |
|
125 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls, |
126 | 0 | sizeof(upcalls)); |
127 | 0 | if (err) { |
128 | 0 | zlog_warn( |
129 | 0 | "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s", |
130 | 0 | errno, safe_strerror(errno)); |
131 | 0 | return -1; |
132 | 0 | } |
133 | | #else |
134 | | zlog_warn( |
135 | | "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall"); |
136 | | #endif |
137 | 0 | } |
138 | | |
139 | 0 | return 0; |
140 | 0 | } |
141 | | |
142 | | static const char *const gmmsgtype2str[GMMSG_WRVIFWHOLE + 1] = { |
143 | | "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"}; |
144 | | |
145 | | |
146 | | int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg) |
147 | 0 | { |
148 | 0 | struct pim_interface *pim_ifp = ifp->info; |
149 | 0 | struct pim_upstream *up; |
150 | 0 | pim_sgaddr sg; |
151 | 0 | bool desync = false; |
152 | |
|
153 | 0 | memset(&sg, 0, sizeof(sg)); |
154 | 0 | sg.src = msg->msg_im_src; |
155 | 0 | sg.grp = msg->msg_im_dst; |
156 | | |
157 | |
|
158 | 0 | if (!pim_ifp || !pim_ifp->pim_enable) { |
159 | 0 | if (PIM_DEBUG_MROUTE) |
160 | 0 | zlog_debug( |
161 | 0 | "%s: %s on interface, dropping packet to %pSG", |
162 | 0 | ifp->name, |
163 | 0 | !pim_ifp ? "Multicast not enabled" |
164 | 0 | : "PIM not enabled", |
165 | 0 | &sg); |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | 0 | if (!pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { |
170 | | /* for ASM, check that we have enough information (i.e. path |
171 | | * to RP) to make a decision on what to do with this packet. |
172 | | * |
173 | | * for SSM, this is meaningless, everything is join-driven, |
174 | | * and for NOCACHE we need to install an empty OIL MFC entry |
175 | | * so the kernel doesn't keep nagging us. |
176 | | */ |
177 | 0 | struct pim_rpf *rpg; |
178 | |
|
179 | 0 | rpg = RP(pim_ifp->pim, msg->msg_im_dst); |
180 | 0 | if (!rpg) { |
181 | 0 | if (PIM_DEBUG_MROUTE) |
182 | 0 | zlog_debug("%s: no RPF for packet to %pSG", |
183 | 0 | ifp->name, &sg); |
184 | 0 | return 0; |
185 | 0 | } |
186 | 0 | if (pim_rpf_addr_is_inaddr_any(rpg)) { |
187 | 0 | if (PIM_DEBUG_MROUTE) |
188 | 0 | zlog_debug("%s: null RPF for packet to %pSG", |
189 | 0 | ifp->name, &sg); |
190 | 0 | return 0; |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | /* |
195 | | * If we've received a multicast packet that isn't connected to |
196 | | * us |
197 | | */ |
198 | 0 | if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) { |
199 | 0 | if (PIM_DEBUG_MROUTE) |
200 | 0 | zlog_debug( |
201 | 0 | "%s: incoming packet to %pSG from non-connected source", |
202 | 0 | ifp->name, &sg); |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | 0 | if (!(PIM_I_am_DR(pim_ifp))) { |
207 | | /* unlike the other debug messages, this one is further in the |
208 | | * "normal operation" category and thus under _DETAIL |
209 | | */ |
210 | 0 | if (PIM_DEBUG_MROUTE_DETAIL) |
211 | 0 | zlog_debug( |
212 | 0 | "%s: not DR on interface, not forwarding traffic for %pSG", |
213 | 0 | ifp->name, &sg); |
214 | | |
215 | | /* |
216 | | * We are not the DR, but we are still receiving packets |
217 | | * Let's blackhole those packets for the moment |
218 | | * As that they will be coming up to the cpu |
219 | | * and causing us to consider them. |
220 | | * |
221 | | * This *will* create a dangling channel_oil |
222 | | * that I see no way to get rid of. Just noting |
223 | | * this for future reference. |
224 | | */ |
225 | 0 | up = pim_upstream_find_or_add( |
226 | 0 | &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __func__); |
227 | 0 | pim_upstream_mroute_add(up->channel_oil, __func__); |
228 | |
|
229 | 0 | return 0; |
230 | 0 | } |
231 | | |
232 | 0 | up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, |
233 | 0 | __func__); |
234 | 0 | if (up->channel_oil->installed) { |
235 | 0 | zlog_warn( |
236 | 0 | "%s: NOCACHE for %pSG, MFC entry disappeared - reinstalling", |
237 | 0 | ifp->name, &sg); |
238 | 0 | desync = true; |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * I moved this debug till after the actual add because |
243 | | * I want to take advantage of the up->sg_str being filled in. |
244 | | */ |
245 | 0 | if (PIM_DEBUG_MROUTE) { |
246 | 0 | zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption", |
247 | 0 | __func__, up->sg_str); |
248 | 0 | } |
249 | |
|
250 | 0 | PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); |
251 | 0 | pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time); |
252 | |
|
253 | 0 | up->channel_oil->cc.pktcnt++; |
254 | | // resolve mfcc_parent prior to mroute_add in channel_add_oif |
255 | 0 | if (up->rpf.source_nexthop.interface && |
256 | 0 | *oil_parent(up->channel_oil) >= MAXVIFS) { |
257 | 0 | pim_upstream_mroute_iif_update(up->channel_oil, __func__); |
258 | 0 | } |
259 | 0 | pim_register_join(up); |
260 | | /* if we have receiver, inherit from parent */ |
261 | 0 | pim_upstream_inherited_olist_decide(pim_ifp->pim, up); |
262 | | |
263 | | /* we just got NOCACHE from the kernel, so... MFC is not in the |
264 | | * kernel for some reason or another. Try installing again. |
265 | | */ |
266 | 0 | if (desync) |
267 | 0 | pim_upstream_mroute_update(up->channel_oil, __func__); |
268 | 0 | return 0; |
269 | 0 | } |
270 | | |
271 | | int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf, |
272 | | size_t len) |
273 | 0 | { |
274 | 0 | struct pim_interface *pim_ifp; |
275 | 0 | pim_sgaddr sg; |
276 | 0 | struct pim_rpf *rpg; |
277 | 0 | const ipv_hdr *ip_hdr; |
278 | 0 | struct pim_upstream *up; |
279 | |
|
280 | 0 | pim_ifp = ifp->info; |
281 | |
|
282 | 0 | ip_hdr = (const ipv_hdr *)buf; |
283 | |
|
284 | 0 | memset(&sg, 0, sizeof(sg)); |
285 | 0 | sg.src = IPV_SRC(ip_hdr); |
286 | 0 | sg.grp = IPV_DST(ip_hdr); |
287 | |
|
288 | 0 | up = pim_upstream_find(pim_ifp->pim, &sg); |
289 | 0 | if (!up) { |
290 | 0 | pim_sgaddr star = sg; |
291 | 0 | star.src = PIMADDR_ANY; |
292 | |
|
293 | 0 | up = pim_upstream_find(pim_ifp->pim, &star); |
294 | |
|
295 | 0 | if (up && PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) { |
296 | 0 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, |
297 | 0 | PIM_UPSTREAM_FLAG_MASK_SRC_LHR, |
298 | 0 | __func__, NULL); |
299 | 0 | if (!up) { |
300 | 0 | if (PIM_DEBUG_MROUTE) |
301 | 0 | zlog_debug( |
302 | 0 | "%s: Unable to create upstream information for %pSG", |
303 | 0 | __func__, &sg); |
304 | 0 | return 0; |
305 | 0 | } |
306 | 0 | pim_upstream_keep_alive_timer_start( |
307 | 0 | up, pim_ifp->pim->keep_alive_time); |
308 | 0 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
309 | 0 | pim_upstream_update_join_desired(pim_ifp->pim, up); |
310 | |
|
311 | 0 | if (PIM_DEBUG_MROUTE) |
312 | 0 | zlog_debug("%s: Creating %s upstream on LHR", |
313 | 0 | __func__, up->sg_str); |
314 | 0 | return 0; |
315 | 0 | } |
316 | 0 | if (PIM_DEBUG_MROUTE_DETAIL) { |
317 | 0 | zlog_debug( |
318 | 0 | "%s: Unable to find upstream channel WHOLEPKT%pSG", |
319 | 0 | __func__, &sg); |
320 | 0 | } |
321 | 0 | return 0; |
322 | 0 | } |
323 | | |
324 | 0 | if (!up->rpf.source_nexthop.interface) { |
325 | 0 | if (PIM_DEBUG_PIM_TRACE) |
326 | 0 | zlog_debug("%s: up %s RPF is not present", __func__, |
327 | 0 | up->sg_str); |
328 | 0 | return 0; |
329 | 0 | } |
330 | | |
331 | 0 | pim_ifp = up->rpf.source_nexthop.interface->info; |
332 | |
|
333 | 0 | rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL; |
334 | |
|
335 | 0 | if ((pim_rpf_addr_is_inaddr_any(rpg)) || (!pim_ifp) || |
336 | 0 | (!(PIM_I_am_DR(pim_ifp)))) { |
337 | 0 | if (PIM_DEBUG_MROUTE) { |
338 | 0 | zlog_debug("%s: Failed Check send packet", __func__); |
339 | 0 | } |
340 | 0 | return 0; |
341 | 0 | } |
342 | | |
343 | | /* |
344 | | * If we've received a register suppress |
345 | | */ |
346 | 0 | if (!up->t_rs_timer) { |
347 | 0 | if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { |
348 | 0 | if (PIM_DEBUG_PIM_REG) |
349 | 0 | zlog_debug( |
350 | 0 | "%pSG register forward skipped as group is SSM", |
351 | 0 | &sg); |
352 | 0 | return 0; |
353 | 0 | } |
354 | | |
355 | 0 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { |
356 | 0 | if (PIM_DEBUG_PIM_REG) |
357 | 0 | zlog_debug( |
358 | 0 | "%s register forward skipped, not FHR", |
359 | 0 | up->sg_str); |
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | 0 | pim_register_send((uint8_t *)buf + sizeof(ipv_hdr), |
364 | 0 | len - sizeof(ipv_hdr), |
365 | 0 | pim_ifp->primary_address, rpg, 0, up); |
366 | 0 | } |
367 | 0 | return 0; |
368 | 0 | } |
369 | | |
370 | | int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg) |
371 | 0 | { |
372 | 0 | struct pim_ifchannel *ch; |
373 | 0 | struct pim_interface *pim_ifp; |
374 | 0 | pim_sgaddr sg; |
375 | |
|
376 | 0 | memset(&sg, 0, sizeof(sg)); |
377 | 0 | sg.src = msg->msg_im_src; |
378 | 0 | sg.grp = msg->msg_im_dst; |
379 | | |
380 | | /* |
381 | | Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. |
382 | | |
383 | | RFC 4601 4.8.2. PIM-SSM-Only Routers |
384 | | |
385 | | iif is the incoming interface of the packet. |
386 | | if (iif is in inherited_olist(S,G)) { |
387 | | send Assert(S,G) on iif |
388 | | } |
389 | | */ |
390 | |
|
391 | 0 | if (!ifp) { |
392 | 0 | if (PIM_DEBUG_MROUTE) |
393 | 0 | zlog_debug( |
394 | 0 | "%s: WRONGVIF (S,G)=%pSG could not find input interface for input_vif_index=%d", |
395 | 0 | __func__, &sg, msg->msg_im_vif); |
396 | 0 | return -1; |
397 | 0 | } |
398 | | |
399 | 0 | pim_ifp = ifp->info; |
400 | 0 | if (!pim_ifp) { |
401 | 0 | if (PIM_DEBUG_MROUTE) |
402 | 0 | zlog_debug( |
403 | 0 | "%s: WRONGVIF (S,G)=%pSG multicast not enabled on interface %s", |
404 | 0 | __func__, &sg, ifp->name); |
405 | 0 | return -2; |
406 | 0 | } |
407 | | |
408 | 0 | ch = pim_ifchannel_find(ifp, &sg); |
409 | 0 | if (!ch) { |
410 | 0 | pim_sgaddr star_g = sg; |
411 | 0 | if (PIM_DEBUG_MROUTE) |
412 | 0 | zlog_debug( |
413 | 0 | "%s: WRONGVIF (S,G)=%pSG could not find channel on interface %s", |
414 | 0 | __func__, &sg, ifp->name); |
415 | |
|
416 | 0 | star_g.src = PIMADDR_ANY; |
417 | 0 | ch = pim_ifchannel_find(ifp, &star_g); |
418 | 0 | if (!ch) { |
419 | 0 | if (PIM_DEBUG_MROUTE) |
420 | 0 | zlog_debug( |
421 | 0 | "%s: WRONGVIF (*,G)=%pSG could not find channel on interface %s", |
422 | 0 | __func__, &star_g, ifp->name); |
423 | 0 | return -3; |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | | /* |
428 | | RFC 4601: 4.6.1. (S,G) Assert Message State Machine |
429 | | |
430 | | Transitions from NoInfo State |
431 | | |
432 | | An (S,G) data packet arrives on interface I, AND |
433 | | CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an |
434 | | downstream interface that is in our (S,G) outgoing interface |
435 | | list. We optimistically assume that we will be the assert |
436 | | winner for this (S,G), and so we transition to the "I am Assert |
437 | | Winner" state and perform Actions A1 (below), which will |
438 | | initiate the assert negotiation for (S,G). |
439 | | */ |
440 | | |
441 | 0 | if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { |
442 | 0 | if (PIM_DEBUG_MROUTE) { |
443 | 0 | zlog_debug( |
444 | 0 | "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s", |
445 | 0 | __func__, ch->sg_str, ifp->name); |
446 | 0 | } |
447 | 0 | return -4; |
448 | 0 | } |
449 | | |
450 | 0 | if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { |
451 | 0 | if (PIM_DEBUG_MROUTE) { |
452 | 0 | zlog_debug( |
453 | 0 | "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel", |
454 | 0 | __func__, ch->sg_str, ifp->name); |
455 | 0 | } |
456 | 0 | return -5; |
457 | 0 | } |
458 | | |
459 | 0 | if (assert_action_a1(ch)) { |
460 | 0 | if (PIM_DEBUG_MROUTE) { |
461 | 0 | zlog_debug( |
462 | 0 | "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s", |
463 | 0 | __func__, ch->sg_str, ifp->name); |
464 | 0 | } |
465 | 0 | return -6; |
466 | 0 | } |
467 | | |
468 | 0 | return 0; |
469 | 0 | } |
470 | | |
471 | | int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf, |
472 | | size_t len) |
473 | 0 | { |
474 | 0 | const ipv_hdr *ip_hdr = (const ipv_hdr *)buf; |
475 | 0 | struct pim_interface *pim_ifp; |
476 | 0 | struct pim_instance *pim; |
477 | 0 | struct pim_ifchannel *ch; |
478 | 0 | struct pim_upstream *up; |
479 | 0 | pim_sgaddr star_g; |
480 | 0 | pim_sgaddr sg; |
481 | |
|
482 | 0 | pim_ifp = ifp->info; |
483 | |
|
484 | 0 | memset(&sg, 0, sizeof(sg)); |
485 | 0 | sg.src = IPV_SRC(ip_hdr); |
486 | 0 | sg.grp = IPV_DST(ip_hdr); |
487 | |
|
488 | 0 | ch = pim_ifchannel_find(ifp, &sg); |
489 | 0 | if (ch) { |
490 | 0 | if (PIM_DEBUG_MROUTE) |
491 | 0 | zlog_debug( |
492 | 0 | "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s", |
493 | 0 | ch->sg_str, ifp->name); |
494 | 0 | return -1; |
495 | 0 | } |
496 | | |
497 | 0 | star_g = sg; |
498 | 0 | star_g.src = PIMADDR_ANY; |
499 | |
|
500 | 0 | pim = pim_ifp->pim; |
501 | | /* |
502 | | * If the incoming interface is the pimreg, then |
503 | | * we know the callback is associated with a pim register |
504 | | * packet and there is nothing to do here as that |
505 | | * normal pim processing will see the packet and allow |
506 | | * us to do the right thing. |
507 | | */ |
508 | 0 | if (ifp == pim->regiface) { |
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | 0 | up = pim_upstream_find(pim_ifp->pim, &sg); |
513 | 0 | if (up) { |
514 | 0 | struct pim_upstream *parent; |
515 | 0 | struct pim_nexthop source; |
516 | 0 | struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp); |
517 | | |
518 | | /* No RPF or No RPF interface or No mcast on RPF interface */ |
519 | 0 | if (!rpf || !rpf->source_nexthop.interface || |
520 | 0 | !rpf->source_nexthop.interface->info) |
521 | 0 | return 0; |
522 | | |
523 | | /* |
524 | | * If we have received a WRVIFWHOLE and are at this |
525 | | * point, we could be receiving the packet on the *,G |
526 | | * tree, let's check and if so we can safely drop |
527 | | * it. |
528 | | */ |
529 | 0 | parent = pim_upstream_find(pim_ifp->pim, &star_g); |
530 | 0 | if (parent && parent->rpf.source_nexthop.interface == ifp) |
531 | 0 | return 0; |
532 | | |
533 | 0 | pim_ifp = rpf->source_nexthop.interface->info; |
534 | |
|
535 | 0 | memset(&source, 0, sizeof(source)); |
536 | | /* |
537 | | * If we are the fhr that means we are getting a callback during |
538 | | * the pimreg period, so I believe we can ignore this packet |
539 | | */ |
540 | 0 | if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { |
541 | | /* |
542 | | * No if channel, but upstream we are at the RP. |
543 | | * |
544 | | * This could be a anycast RP too and we may |
545 | | * not have received a register packet from |
546 | | * the source here at all. So gracefully |
547 | | * bow out of doing a nexthop lookup and |
548 | | * setting the SPTBIT to true |
549 | | */ |
550 | 0 | if (!(pim_addr_is_any(up->upstream_register)) && |
551 | 0 | pim_nexthop_lookup(pim_ifp->pim, &source, |
552 | 0 | up->upstream_register, 0)) { |
553 | 0 | pim_register_stop_send(source.interface, &sg, |
554 | 0 | pim_ifp->primary_address, |
555 | 0 | up->upstream_register); |
556 | 0 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
557 | 0 | } |
558 | |
|
559 | 0 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
560 | 0 | if (!up->channel_oil->installed) |
561 | 0 | pim_upstream_mroute_add(up->channel_oil, |
562 | 0 | __func__); |
563 | 0 | } else { |
564 | 0 | if (I_am_RP(pim_ifp->pim, up->sg.grp)) { |
565 | 0 | if (pim_nexthop_lookup(pim_ifp->pim, &source, |
566 | 0 | up->upstream_register, |
567 | 0 | 0)) |
568 | 0 | pim_register_stop_send( |
569 | 0 | source.interface, &sg, |
570 | 0 | pim_ifp->primary_address, |
571 | 0 | up->upstream_register); |
572 | 0 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
573 | 0 | } else { |
574 | | /* |
575 | | * At this point pimd is connected to |
576 | | * the source, it has a parent, we are not |
577 | | * the RP and the SPTBIT should be set |
578 | | * since we know *the* S,G is on the SPT. |
579 | | * The first time this happens, let's cause |
580 | | * an immediate join to go out so that |
581 | | * the RP can trim this guy immediately |
582 | | * if necessary, instead of waiting |
583 | | * one join/prune send cycle |
584 | | */ |
585 | 0 | if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE && |
586 | 0 | up->parent && |
587 | 0 | up->rpf.source_nexthop.interface != |
588 | 0 | up->parent->rpf.source_nexthop |
589 | 0 | .interface) { |
590 | 0 | up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; |
591 | 0 | pim_jp_agg_single_upstream_send( |
592 | 0 | &up->parent->rpf, up->parent, |
593 | 0 | true); |
594 | 0 | } |
595 | 0 | } |
596 | 0 | pim_upstream_keep_alive_timer_start( |
597 | 0 | up, pim_ifp->pim->keep_alive_time); |
598 | 0 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
599 | 0 | pim_mroute_msg_wholepkt(fd, ifp, buf, len); |
600 | 0 | } |
601 | 0 | return 0; |
602 | 0 | } |
603 | | |
604 | 0 | pim_ifp = ifp->info; |
605 | 0 | if (pim_if_connected_to_source(ifp, sg.src)) { |
606 | 0 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, |
607 | 0 | PIM_UPSTREAM_FLAG_MASK_FHR, __func__, |
608 | 0 | NULL); |
609 | 0 | if (!up) { |
610 | 0 | if (PIM_DEBUG_MROUTE) |
611 | 0 | zlog_debug( |
612 | 0 | "%pSG: WRONGVIF%s unable to create upstream on interface", |
613 | 0 | &sg, ifp->name); |
614 | 0 | return -2; |
615 | 0 | } |
616 | 0 | PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); |
617 | 0 | pim_upstream_keep_alive_timer_start( |
618 | 0 | up, pim_ifp->pim->keep_alive_time); |
619 | 0 | up->channel_oil->cc.pktcnt++; |
620 | 0 | pim_register_join(up); |
621 | 0 | pim_upstream_inherited_olist(pim_ifp->pim, up); |
622 | 0 | if (!up->channel_oil->installed) |
623 | 0 | pim_upstream_mroute_add(up->channel_oil, __func__); |
624 | | |
625 | | // Send the packet to the RP |
626 | 0 | pim_mroute_msg_wholepkt(fd, ifp, buf, len); |
627 | 0 | } else { |
628 | 0 | up = pim_upstream_add(pim_ifp->pim, &sg, ifp, |
629 | 0 | PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, |
630 | 0 | __func__, NULL); |
631 | 0 | if (!up->channel_oil->installed) |
632 | 0 | pim_upstream_mroute_add(up->channel_oil, __func__); |
633 | 0 | } |
634 | | |
635 | 0 | return 0; |
636 | 0 | } |
637 | | |
638 | | #if PIM_IPV == 4 |
639 | | static int process_igmp_packet(struct pim_instance *pim, const char *buf, |
640 | | size_t buf_size, ifindex_t ifindex) |
641 | 0 | { |
642 | 0 | struct interface *ifp; |
643 | 0 | struct pim_interface *pim_ifp; |
644 | 0 | struct in_addr ifaddr; |
645 | 0 | struct gm_sock *igmp; |
646 | 0 | const struct prefix *connected_src; |
647 | 0 | const struct ip *ip_hdr = (const struct ip *)buf; |
648 | | |
649 | | /* We have the IP packet but we do not know which interface this |
650 | | * packet was |
651 | | * received on. Find the interface that is on the same subnet as |
652 | | * the source |
653 | | * of the IP packet. |
654 | | */ |
655 | 0 | ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id); |
656 | |
|
657 | 0 | if (!ifp || !ifp->info) |
658 | 0 | return 0; |
659 | | |
660 | 0 | connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src); |
661 | |
|
662 | 0 | if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) { |
663 | 0 | if (PIM_DEBUG_GM_PACKETS) { |
664 | 0 | zlog_debug( |
665 | 0 | "Recv IGMP packet on interface: %s from a non-connected source: %pI4", |
666 | 0 | ifp->name, &ip_hdr->ip_src); |
667 | 0 | } |
668 | 0 | return 0; |
669 | 0 | } |
670 | | |
671 | 0 | pim_ifp = ifp->info; |
672 | 0 | ifaddr = connected_src ? connected_src->u.prefix4 |
673 | 0 | : pim_ifp->primary_address; |
674 | 0 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); |
675 | |
|
676 | 0 | if (PIM_DEBUG_GM_PACKETS) { |
677 | 0 | zlog_debug( |
678 | 0 | "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4", |
679 | 0 | __func__, pim->vrf->name, ifp->name, igmp, |
680 | 0 | &ip_hdr->ip_src, &ip_hdr->ip_dst); |
681 | 0 | } |
682 | 0 | if (igmp) |
683 | 0 | pim_igmp_packet(igmp, (char *)buf, buf_size); |
684 | 0 | else if (PIM_DEBUG_GM_PACKETS) |
685 | 0 | zlog_debug( |
686 | 0 | "No IGMP socket on interface: %s with connected source: %pI4", |
687 | 0 | ifp->name, &ifaddr); |
688 | |
|
689 | 0 | return 0; |
690 | 0 | } |
691 | | #endif |
692 | | |
693 | | int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size, |
694 | | ifindex_t ifindex) |
695 | 0 | { |
696 | 0 | struct interface *ifp; |
697 | 0 | const ipv_hdr *ip_hdr; |
698 | 0 | const kernmsg *msg; |
699 | |
|
700 | 0 | if (buf_size < (int)sizeof(ipv_hdr)) |
701 | 0 | return 0; |
702 | | |
703 | 0 | ip_hdr = (const ipv_hdr *)buf; |
704 | |
|
705 | 0 | #if PIM_IPV == 4 |
706 | 0 | if (ip_hdr->ip_p == IPPROTO_IGMP) { |
707 | 0 | process_igmp_packet(pim, buf, buf_size, ifindex); |
708 | 0 | } else if (ip_hdr->ip_p) { |
709 | 0 | if (PIM_DEBUG_MROUTE_DETAIL) { |
710 | 0 | zlog_debug( |
711 | 0 | "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld", |
712 | 0 | __func__, ip_hdr->ip_p, &ip_hdr->ip_src, |
713 | 0 | &ip_hdr->ip_dst, (long int)buf_size); |
714 | 0 | } |
715 | |
|
716 | 0 | } else { |
717 | | #else |
718 | | |
719 | | if ((ip_hdr->ip6_vfc & 0xf) == 0) { |
720 | | #endif |
721 | 0 | msg = (const kernmsg *)buf; |
722 | |
|
723 | 0 | ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif); |
724 | |
|
725 | 0 | if (!ifp) |
726 | 0 | return 0; |
727 | 0 | if (PIM_DEBUG_MROUTE) { |
728 | 0 | #if PIM_IPV == 4 |
729 | 0 | zlog_debug( |
730 | 0 | "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld", |
731 | 0 | __func__, gmmsgtype2str[msg->msg_im_msgtype], |
732 | 0 | msg->msg_im_msgtype, ip_hdr->ip_p, |
733 | 0 | pim->mroute_socket, &msg->msg_im_src, |
734 | 0 | &msg->msg_im_dst, ifp->name, msg->msg_im_vif, |
735 | 0 | (long int)buf_size); |
736 | | #else |
737 | | zlog_debug( |
738 | | "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld", |
739 | | __func__, gmmsgtype2str[msg->msg_im_msgtype], |
740 | | msg->msg_im_msgtype, ip_hdr->ip6_nxt, |
741 | | pim->mroute_socket, &msg->msg_im_src, |
742 | | &msg->msg_im_dst, ifp->name, msg->msg_im_vif, |
743 | | (long int)buf_size); |
744 | | #endif |
745 | 0 | } |
746 | |
|
747 | 0 | switch (msg->msg_im_msgtype) { |
748 | 0 | case GMMSG_WRONGVIF: |
749 | 0 | return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp, |
750 | 0 | msg); |
751 | 0 | case GMMSG_NOCACHE: |
752 | 0 | return pim_mroute_msg_nocache(pim->mroute_socket, ifp, |
753 | 0 | msg); |
754 | 0 | case GMMSG_WHOLEPKT: |
755 | 0 | return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp, |
756 | 0 | (const char *)msg, |
757 | 0 | buf_size); |
758 | 0 | case GMMSG_WRVIFWHOLE: |
759 | 0 | return pim_mroute_msg_wrvifwhole(pim->mroute_socket, |
760 | 0 | ifp, (const char *)msg, |
761 | 0 | buf_size); |
762 | 0 | default: |
763 | 0 | break; |
764 | 0 | } |
765 | 0 | } |
766 | | |
767 | 0 | return 0; |
768 | 0 | } |
769 | | |
770 | | static void mroute_read(struct event *t) |
771 | 0 | { |
772 | 0 | struct pim_instance *pim; |
773 | 0 | static long long count; |
774 | 0 | char buf[10000]; |
775 | 0 | int cont = 1; |
776 | 0 | int rd; |
777 | 0 | ifindex_t ifindex; |
778 | 0 | pim = EVENT_ARG(t); |
779 | 0 |
|
780 | 0 | while (cont) { |
781 | 0 | rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf, |
782 | 0 | sizeof(buf), NULL, NULL, NULL, NULL, |
783 | 0 | &ifindex); |
784 | 0 | if (rd <= 0) { |
785 | 0 | if (errno == EINTR) |
786 | 0 | continue; |
787 | 0 | if (errno == EWOULDBLOCK || errno == EAGAIN) |
788 | 0 | break; |
789 | 0 |
|
790 | 0 | zlog_warn( |
791 | 0 | "%s: failure reading rd=%d: fd=%d: errno=%d: %s", |
792 | 0 | __func__, rd, pim->mroute_socket, errno, |
793 | 0 | safe_strerror(errno)); |
794 | 0 | goto done; |
795 | 0 | } |
796 | 0 |
|
797 | 0 | pim_mroute_msg(pim, buf, rd, ifindex); |
798 | 0 |
|
799 | 0 | count++; |
800 | 0 | if (count % router->packet_process == 0) |
801 | 0 | cont = 0; |
802 | 0 | } |
803 | 0 | /* Keep reading */ |
804 | 0 | done: |
805 | 0 | mroute_read_on(pim); |
806 | 0 |
|
807 | 0 | return; |
808 | 0 | } |
809 | | |
810 | | static void mroute_read_on(struct pim_instance *pim) |
811 | 0 | { |
812 | 0 | event_add_read(router->master, mroute_read, pim, pim->mroute_socket, |
813 | 0 | &pim->thread); |
814 | 0 | } |
815 | | |
816 | | static void mroute_read_off(struct pim_instance *pim) |
817 | 0 | { |
818 | 0 | EVENT_OFF(pim->thread); |
819 | 0 | } |
820 | | |
821 | | int pim_mroute_socket_enable(struct pim_instance *pim) |
822 | 1 | { |
823 | 1 | int fd; |
824 | | |
825 | 1 | frr_with_privs(&pimd_privs) { |
826 | | |
827 | | #ifndef FUZZING |
828 | | #if PIM_IPV == 4 |
829 | | fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); |
830 | | #else |
831 | | fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
832 | | #endif |
833 | | if (fd < 0) { |
834 | | zlog_warn("Could not create mroute socket: errno=%d: %s", |
835 | | errno, |
836 | | safe_strerror(errno)); |
837 | | return -2; |
838 | | } |
839 | | #else |
840 | 1 | fd = 69; |
841 | 1 | #endif |
842 | | |
843 | | #ifndef FUZZING |
844 | | #if PIM_IPV == 6 |
845 | | struct icmp6_filter filter[1]; |
846 | | int ret; |
847 | | |
848 | | /* Unlike IPv4, this socket is not used for MLD, so just drop |
849 | | * everything with an empty ICMP6 filter. Otherwise we get |
850 | | * all kinds of garbage here, possibly even non-multicast |
851 | | * related ICMPv6 traffic (e.g. ping) |
852 | | * |
853 | | * (mroute kernel upcall "packets" are injected directly on the |
854 | | * socket, this sockopt -or any other- has no effect on them) |
855 | | */ |
856 | | ICMP6_FILTER_SETBLOCKALL(filter); |
857 | | ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter, |
858 | | sizeof(filter)); |
859 | | if (ret) |
860 | | zlog_err( |
861 | | "(VRF %s) failed to set mroute control filter: %m", |
862 | | pim->vrf->name); |
863 | | #endif |
864 | | #else |
865 | 1 | fd = 69; |
866 | 1 | #endif |
867 | | |
868 | 1 | #ifdef SO_BINDTODEVICE |
869 | 1 | if (pim->vrf->vrf_id != VRF_DEFAULT |
870 | 0 | && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, |
871 | 0 | pim->vrf->name, strlen(pim->vrf->name))) { |
872 | 0 | zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s", |
873 | 0 | safe_strerror(errno)); |
874 | 0 | close(fd); |
875 | 0 | return -3; |
876 | 0 | } |
877 | 1 | #endif |
878 | | |
879 | 1 | } |
880 | | |
881 | 1 | pim->mroute_socket = fd; |
882 | | #ifndef FUZZING |
883 | | if (pim_mroute_set(pim, 1)) { |
884 | | zlog_warn( |
885 | | "Could not enable mroute on socket fd=%d: errno=%d: %s", |
886 | | fd, errno, safe_strerror(errno)); |
887 | | close(fd); |
888 | | pim->mroute_socket = -1; |
889 | | return -3; |
890 | | } |
891 | | |
892 | | pim->mroute_socket_creation = pim_time_monotonic_sec(); |
893 | | |
894 | | mroute_read_on(pim); |
895 | | #endif |
896 | | |
897 | 1 | return 0; |
898 | 1 | } |
899 | | |
900 | | int pim_mroute_socket_disable(struct pim_instance *pim) |
901 | 0 | { |
902 | 0 | if (pim_mroute_set(pim, 0)) { |
903 | 0 | zlog_warn( |
904 | 0 | "Could not disable mroute on socket fd=%d: errno=%d: %s", |
905 | 0 | pim->mroute_socket, errno, safe_strerror(errno)); |
906 | 0 | return -2; |
907 | 0 | } |
908 | | |
909 | 0 | if (close(pim->mroute_socket)) { |
910 | 0 | zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", |
911 | 0 | pim->mroute_socket, errno, safe_strerror(errno)); |
912 | 0 | return -3; |
913 | 0 | } |
914 | | |
915 | 0 | mroute_read_off(pim); |
916 | 0 | pim->mroute_socket = -1; |
917 | |
|
918 | 0 | return 0; |
919 | 0 | } |
920 | | |
921 | | /* |
922 | | For each network interface (e.g., physical or a virtual tunnel) that |
923 | | would be used for multicast forwarding, a corresponding multicast |
924 | | interface must be added to the kernel. |
925 | | */ |
926 | | int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr, |
927 | | unsigned char flags) |
928 | 2 | { |
929 | 2 | struct pim_interface *pim_ifp = ifp->info; |
930 | 2 | pim_vifctl vc; |
931 | 2 | int err; |
932 | | |
933 | 2 | if (PIM_DEBUG_MROUTE) |
934 | 0 | zlog_debug("%s: Add Vif %d (%s[%s])", __func__, |
935 | 2 | pim_ifp->mroute_vif_index, ifp->name, |
936 | 2 | pim_ifp->pim->vrf->name); |
937 | | |
938 | 2 | memset(&vc, 0, sizeof(vc)); |
939 | 2 | vc.vc_vifi = pim_ifp->mroute_vif_index; |
940 | 2 | #if PIM_IPV == 4 |
941 | 2 | #ifdef VIFF_USE_IFINDEX |
942 | 2 | vc.vc_lcl_ifindex = ifp->ifindex; |
943 | | #else |
944 | | if (ifaddr.s_addr == INADDR_ANY) { |
945 | | zlog_warn( |
946 | | "%s: unnumbered interfaces are not supported on this platform", |
947 | | __func__); |
948 | | return -1; |
949 | | } |
950 | | memcpy(&vc.vc_lcl_addr, &ifaddr, sizeof(vc.vc_lcl_addr)); |
951 | | #endif |
952 | | #else |
953 | | vc.vc_pifi = ifp->ifindex; |
954 | | #endif |
955 | 2 | vc.vc_flags = flags; |
956 | 2 | vc.vc_threshold = PIM_MROUTE_MIN_TTL; |
957 | 2 | vc.vc_rate_limit = 0; |
958 | | |
959 | 2 | #if PIM_IPV == 4 |
960 | | #ifdef PIM_DVMRP_TUNNEL |
961 | | if (vc.vc_flags & VIFF_TUNNEL) { |
962 | | memcpy(&vc.vc_rmt_addr, &vif_remote_addr, |
963 | | sizeof(vc.vc_rmt_addr)); |
964 | | } |
965 | | #endif |
966 | 2 | #endif |
967 | | |
968 | | #ifndef FUZZING |
969 | | err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF, |
970 | | (void *)&vc, sizeof(vc)); |
971 | | #else |
972 | 2 | err = 0; |
973 | 2 | #endif |
974 | | |
975 | 2 | if (err) { |
976 | 0 | zlog_warn( |
977 | 0 | "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s", |
978 | 0 | __func__, pim_ifp->pim->mroute_socket, ifp->ifindex, |
979 | 0 | &ifaddr, flags, errno, safe_strerror(errno)); |
980 | 0 | return -2; |
981 | 0 | } |
982 | | |
983 | 2 | return 0; |
984 | 2 | } |
985 | | |
986 | | int pim_mroute_del_vif(struct interface *ifp) |
987 | 0 | { |
988 | 0 | struct pim_interface *pim_ifp = ifp->info; |
989 | 0 | pim_vifctl vc; |
990 | 0 | int err; |
991 | |
|
992 | 0 | if (PIM_DEBUG_MROUTE) |
993 | 0 | zlog_debug("%s: Del Vif %d (%s[%s])", __func__, |
994 | 0 | pim_ifp->mroute_vif_index, ifp->name, |
995 | 0 | pim_ifp->pim->vrf->name); |
996 | |
|
997 | 0 | memset(&vc, 0, sizeof(vc)); |
998 | 0 | vc.vc_vifi = pim_ifp->mroute_vif_index; |
999 | |
|
1000 | 0 | err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF, |
1001 | 0 | (void *)&vc, sizeof(vc)); |
1002 | 0 | if (err) { |
1003 | 0 | zlog_warn( |
1004 | 0 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", |
1005 | 0 | __FILE__, __func__, pim_ifp->pim->mroute_socket, |
1006 | 0 | pim_ifp->mroute_vif_index, errno, safe_strerror(errno)); |
1007 | 0 | return -2; |
1008 | 0 | } |
1009 | | |
1010 | 0 | return 0; |
1011 | 0 | } |
1012 | | |
1013 | | /* |
1014 | | * Prevent creating MFC entry with OIF=IIF. |
1015 | | * |
1016 | | * This is a protection against implementation mistakes. |
1017 | | * |
1018 | | * PIM protocol implicitely ensures loopfree multicast topology. |
1019 | | * |
1020 | | * IGMP must be protected against adding looped MFC entries created |
1021 | | * by both source and receiver attached to the same interface. See |
1022 | | * TODO T22. |
1023 | | * We shall allow igmp to create upstream when it is DR for the intf. |
1024 | | * Assume RP reachable via non DR. |
1025 | | */ |
1026 | | bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, |
1027 | | int oif_index) |
1028 | 0 | { |
1029 | 0 | #ifdef PIM_ENFORCE_LOOPFREE_MFC |
1030 | 0 | struct interface *ifp_out; |
1031 | 0 | struct pim_interface *pim_ifp; |
1032 | |
|
1033 | 0 | if (c_oil->up && |
1034 | 0 | PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags)) |
1035 | 0 | return true; |
1036 | | |
1037 | 0 | ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index); |
1038 | 0 | if (!ifp_out) |
1039 | 0 | return false; |
1040 | 0 | pim_ifp = ifp_out->info; |
1041 | 0 | if (!pim_ifp) |
1042 | 0 | return false; |
1043 | 0 | if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_GM) && |
1044 | 0 | PIM_I_am_DR(pim_ifp)) |
1045 | 0 | return true; |
1046 | | |
1047 | 0 | return false; |
1048 | | #else |
1049 | | return true; |
1050 | | #endif |
1051 | 0 | } |
1052 | | |
1053 | | static inline void pim_mroute_copy(struct channel_oil *out, |
1054 | | struct channel_oil *in) |
1055 | 0 | { |
1056 | 0 | int i; |
1057 | |
|
1058 | 0 | *oil_origin(out) = *oil_origin(in); |
1059 | 0 | *oil_mcastgrp(out) = *oil_mcastgrp(in); |
1060 | 0 | *oil_parent(out) = *oil_parent(in); |
1061 | |
|
1062 | 0 | for (i = 0; i < MAXVIFS; ++i) { |
1063 | 0 | if (*oil_parent(out) == i && |
1064 | 0 | !pim_mroute_allow_iif_in_oil(in, i)) { |
1065 | 0 | oil_if_set(out, i, 0); |
1066 | 0 | continue; |
1067 | 0 | } |
1068 | | |
1069 | 0 | if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE) |
1070 | 0 | oil_if_set(out, i, 0); |
1071 | 0 | else |
1072 | 0 | oil_if_set(out, i, oil_if_has(in, i)); |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | | /* This function must not be called directly 0 |
1077 | | * use pim_upstream_mroute_add or pim_static_mroute_add instead |
1078 | | */ |
1079 | | static int pim_mroute_add(struct channel_oil *c_oil, const char *name) |
1080 | 0 | { |
1081 | 0 | struct pim_instance *pim = c_oil->pim; |
1082 | 0 | struct channel_oil tmp_oil[1] = { }; |
1083 | 0 | int err; |
1084 | |
|
1085 | 0 | pim->mroute_add_last = pim_time_monotonic_sec(); |
1086 | 0 | ++pim->mroute_add_events; |
1087 | | |
1088 | | /* Copy the oil to a temporary structure to fixup (without need to |
1089 | | * later restore) before sending the mroute add to the dataplane |
1090 | | */ |
1091 | 0 | pim_mroute_copy(tmp_oil, c_oil); |
1092 | | |
1093 | | /* The linux kernel *expects* the incoming |
1094 | | * vif to be part of the outgoing list |
1095 | | * in the case of a (*,G). |
1096 | | */ |
1097 | 0 | if (pim_addr_is_any(*oil_origin(c_oil))) { |
1098 | 0 | oil_if_set(tmp_oil, *oil_parent(c_oil), 1); |
1099 | 0 | } |
1100 | | |
1101 | | /* |
1102 | | * If we have an unresolved cache entry for the S,G |
1103 | | * it is owned by the pimreg for the incoming IIF |
1104 | | * So set pimreg as the IIF temporarily to cause |
1105 | | * the packets to be forwarded. Then set it |
1106 | | * to the correct IIF afterwords. |
1107 | | */ |
1108 | 0 | if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil)) |
1109 | 0 | && *oil_parent(c_oil) != 0) { |
1110 | 0 | *oil_parent(tmp_oil) = 0; |
1111 | 0 | } |
1112 | | /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */ |
1113 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC, |
1114 | 0 | &tmp_oil->oil, sizeof(tmp_oil->oil)); |
1115 | |
|
1116 | 0 | if (!err && !c_oil->installed |
1117 | 0 | && !pim_addr_is_any(*oil_origin(c_oil)) |
1118 | 0 | && *oil_parent(c_oil) != 0) { |
1119 | 0 | *oil_parent(tmp_oil) = *oil_parent(c_oil); |
1120 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC, |
1121 | 0 | &tmp_oil->oil, sizeof(tmp_oil->oil)); |
1122 | 0 | } |
1123 | |
|
1124 | 0 | if (err) { |
1125 | 0 | zlog_warn( |
1126 | 0 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s", |
1127 | 0 | __FILE__, __func__, pim->mroute_socket, errno, |
1128 | 0 | safe_strerror(errno)); |
1129 | 0 | return -2; |
1130 | 0 | } |
1131 | | |
1132 | 0 | if (PIM_DEBUG_MROUTE) { |
1133 | 0 | char buf[1000]; |
1134 | 0 | zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name, |
1135 | 0 | pim->vrf->name, |
1136 | 0 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
1137 | 0 | } |
1138 | |
|
1139 | 0 | if (!c_oil->installed) { |
1140 | 0 | c_oil->installed = 1; |
1141 | 0 | c_oil->mroute_creation = pim_time_monotonic_sec(); |
1142 | 0 | } |
1143 | |
|
1144 | 0 | return 0; |
1145 | 0 | } |
1146 | | |
1147 | | static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil, |
1148 | | const char *name) |
1149 | 8.99k | { |
1150 | 8.99k | vifi_t iif = MAXVIFS; |
1151 | 8.99k | struct interface *ifp = NULL; |
1152 | 8.99k | struct pim_interface *pim_ifp; |
1153 | 8.99k | struct pim_upstream *up = c_oil->up; |
1154 | | |
1155 | 8.99k | if (up) { |
1156 | 8.99k | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) { |
1157 | 6.73k | if (up->parent) |
1158 | 3.72k | ifp = up->parent->rpf.source_nexthop.interface; |
1159 | 6.73k | } else { |
1160 | 2.25k | ifp = up->rpf.source_nexthop.interface; |
1161 | 2.25k | } |
1162 | 8.99k | if (ifp) { |
1163 | 0 | pim_ifp = (struct pim_interface *)ifp->info; |
1164 | 0 | if (pim_ifp) |
1165 | 0 | iif = pim_ifp->mroute_vif_index; |
1166 | 0 | } |
1167 | 8.99k | } |
1168 | 8.99k | return iif; |
1169 | 8.99k | } |
1170 | | |
1171 | | static int pim_upstream_mroute_update(struct channel_oil *c_oil, |
1172 | | const char *name) |
1173 | 1.31k | { |
1174 | 1.31k | char buf[1000]; |
1175 | | |
1176 | 1.31k | if (*oil_parent(c_oil) >= MAXVIFS) { |
1177 | | /* the c_oil cannot be installed as a mroute yet */ |
1178 | 1.31k | if (PIM_DEBUG_MROUTE) |
1179 | 0 | zlog_debug( |
1180 | 1.31k | "%s(%s) %s mroute not ready to be installed; %s", |
1181 | 1.31k | __func__, name, |
1182 | 1.31k | pim_channel_oil_dump(c_oil, buf, |
1183 | 1.31k | sizeof(buf)), |
1184 | 1.31k | c_oil->installed ? |
1185 | 1.31k | "uninstall" : "skip"); |
1186 | | /* if already installed flush it out as we are going to stop |
1187 | | * updates to it leaving it in a stale state |
1188 | | */ |
1189 | 1.31k | if (c_oil->installed) |
1190 | 0 | pim_mroute_del(c_oil, name); |
1191 | | /* return success (skipped) */ |
1192 | 1.31k | return 0; |
1193 | 1.31k | } |
1194 | | |
1195 | 0 | return pim_mroute_add(c_oil, name); |
1196 | 1.31k | } |
1197 | | |
1198 | | /* IIF associated with SGrpt entries are re-evaluated when the parent |
1199 | | * (*,G) entries IIF changes |
1200 | | */ |
1201 | | static void pim_upstream_all_sources_iif_update(struct pim_upstream *up) |
1202 | 0 | { |
1203 | 0 | struct listnode *listnode; |
1204 | 0 | struct pim_upstream *child; |
1205 | |
|
1206 | 0 | for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, |
1207 | 0 | child)) { |
1208 | 0 | if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) |
1209 | 0 | pim_upstream_mroute_iif_update(child->channel_oil, |
1210 | 0 | __func__); |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | | /* In the case of "PIM state machine" added mroutes an upstream entry |
1215 | | * must be present to decide on the SPT-forwarding vs. RPT-forwarding. |
1216 | | */ |
1217 | | int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) |
1218 | 1.31k | { |
1219 | 1.31k | vifi_t iif; |
1220 | | |
1221 | 1.31k | iif = pim_upstream_get_mroute_iif(c_oil, name); |
1222 | | |
1223 | 1.31k | if (*oil_parent(c_oil) != iif) { |
1224 | 0 | *oil_parent(c_oil) = iif; |
1225 | 0 | if (pim_addr_is_any(*oil_origin(c_oil)) && |
1226 | 0 | c_oil->up) |
1227 | 0 | pim_upstream_all_sources_iif_update(c_oil->up); |
1228 | 1.31k | } else { |
1229 | 1.31k | *oil_parent(c_oil) = iif; |
1230 | 1.31k | } |
1231 | | |
1232 | 1.31k | return pim_upstream_mroute_update(c_oil, name); |
1233 | 1.31k | } |
1234 | | |
1235 | | /* Look for IIF changes and update the dateplane entry only if the IIF |
1236 | | * has changed. |
1237 | | */ |
1238 | | int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) |
1239 | 7.67k | { |
1240 | 7.67k | vifi_t iif; |
1241 | 7.67k | char buf[1000]; |
1242 | | |
1243 | 7.67k | iif = pim_upstream_get_mroute_iif(c_oil, name); |
1244 | 7.67k | if (*oil_parent(c_oil) == iif) { |
1245 | | /* no change */ |
1246 | 7.67k | return 0; |
1247 | 7.67k | } |
1248 | 0 | *oil_parent(c_oil) = iif; |
1249 | |
|
1250 | 0 | if (pim_addr_is_any(*oil_origin(c_oil)) && |
1251 | 0 | c_oil->up) |
1252 | 0 | pim_upstream_all_sources_iif_update(c_oil->up); |
1253 | |
|
1254 | 0 | if (PIM_DEBUG_MROUTE_DETAIL) |
1255 | 0 | zlog_debug("%s(%s) %s mroute iif update %d", |
1256 | 0 | __func__, name, |
1257 | 0 | pim_channel_oil_dump(c_oil, buf, |
1258 | 0 | sizeof(buf)), iif); |
1259 | | /* XXX: is this hack needed? */ |
1260 | 0 | c_oil->oil_inherited_rescan = 1; |
1261 | 0 | return pim_upstream_mroute_update(c_oil, name); |
1262 | 7.67k | } |
1263 | | |
1264 | | int pim_static_mroute_add(struct channel_oil *c_oil, const char *name) |
1265 | 0 | { |
1266 | 0 | return pim_mroute_add(c_oil, name); |
1267 | 0 | } |
1268 | | |
1269 | | void pim_static_mroute_iif_update(struct channel_oil *c_oil, |
1270 | | int input_vif_index, |
1271 | | const char *name) |
1272 | 0 | { |
1273 | 0 | if (*oil_parent(c_oil) == input_vif_index) |
1274 | 0 | return; |
1275 | | |
1276 | 0 | *oil_parent(c_oil) = input_vif_index; |
1277 | 0 | if (input_vif_index == MAXVIFS) |
1278 | 0 | pim_mroute_del(c_oil, name); |
1279 | 0 | else |
1280 | 0 | pim_static_mroute_add(c_oil, name); |
1281 | 0 | } |
1282 | | |
1283 | | int pim_mroute_del(struct channel_oil *c_oil, const char *name) |
1284 | 84.4k | { |
1285 | 84.4k | struct pim_instance *pim = c_oil->pim; |
1286 | 84.4k | int err; |
1287 | | |
1288 | 84.4k | pim->mroute_del_last = pim_time_monotonic_sec(); |
1289 | 84.4k | ++pim->mroute_del_events; |
1290 | | |
1291 | 84.4k | if (!c_oil->installed) { |
1292 | 84.4k | if (PIM_DEBUG_MROUTE) { |
1293 | 0 | char buf[1000]; |
1294 | 0 | zlog_debug( |
1295 | 0 | "%s %s: vifi %d for route is %s not installed, do not need to send del req. ", |
1296 | 0 | __FILE__, __func__, *oil_parent(c_oil), |
1297 | 0 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
1298 | 0 | } |
1299 | 84.4k | return -2; |
1300 | 84.4k | } |
1301 | | |
1302 | 0 | err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC, |
1303 | 0 | &c_oil->oil, sizeof(c_oil->oil)); |
1304 | 0 | if (err) { |
1305 | 0 | if (PIM_DEBUG_MROUTE) |
1306 | 0 | zlog_warn( |
1307 | 0 | "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s", |
1308 | 0 | __FILE__, __func__, pim->mroute_socket, errno, |
1309 | 0 | safe_strerror(errno)); |
1310 | 0 | return -2; |
1311 | 0 | } |
1312 | | |
1313 | 0 | if (PIM_DEBUG_MROUTE) { |
1314 | 0 | char buf[1000]; |
1315 | 0 | zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name, |
1316 | 0 | pim->vrf->name, |
1317 | 0 | pim_channel_oil_dump(c_oil, buf, sizeof(buf))); |
1318 | 0 | } |
1319 | | |
1320 | | // Reset kernel installed flag |
1321 | 0 | c_oil->installed = 0; |
1322 | |
|
1323 | 0 | return 0; |
1324 | 0 | } |
1325 | | |
1326 | | void pim_mroute_update_counters(struct channel_oil *c_oil) |
1327 | 0 | { |
1328 | 0 | struct pim_instance *pim = c_oil->pim; |
1329 | 0 | pim_sioc_sg_req sgreq; |
1330 | |
|
1331 | 0 | c_oil->cc.oldpktcnt = c_oil->cc.pktcnt; |
1332 | 0 | c_oil->cc.oldbytecnt = c_oil->cc.bytecnt; |
1333 | 0 | c_oil->cc.oldwrong_if = c_oil->cc.wrong_if; |
1334 | |
|
1335 | 0 | if (!c_oil->installed) { |
1336 | 0 | c_oil->cc.lastused = 100 * pim->keep_alive_time; |
1337 | 0 | if (PIM_DEBUG_MROUTE) { |
1338 | 0 | pim_sgaddr sg; |
1339 | |
|
1340 | 0 | sg.src = *oil_origin(c_oil); |
1341 | 0 | sg.grp = *oil_mcastgrp(c_oil); |
1342 | 0 | zlog_debug("Channel%pSG is not installed no need to collect data from kernel", |
1343 | 0 | &sg); |
1344 | 0 | } |
1345 | 0 | return; |
1346 | 0 | } |
1347 | | |
1348 | | |
1349 | 0 | memset(&sgreq, 0, sizeof(sgreq)); |
1350 | |
|
1351 | 0 | pim_zlookup_sg_statistics(c_oil); |
1352 | |
|
1353 | 0 | #if PIM_IPV == 4 |
1354 | 0 | sgreq.src = *oil_origin(c_oil); |
1355 | 0 | sgreq.grp = *oil_mcastgrp(c_oil); |
1356 | | #else |
1357 | | sgreq.src = c_oil->oil.mf6cc_origin; |
1358 | | sgreq.grp = c_oil->oil.mf6cc_mcastgrp; |
1359 | | #endif |
1360 | 0 | if (ioctl(pim->mroute_socket, PIM_SIOCGETSGCNT, &sgreq)) { |
1361 | 0 | pim_sgaddr sg; |
1362 | |
|
1363 | 0 | sg.src = *oil_origin(c_oil); |
1364 | 0 | sg.grp = *oil_mcastgrp(c_oil); |
1365 | |
|
1366 | 0 | zlog_warn( |
1367 | 0 | "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s", |
1368 | 0 | (unsigned long)PIM_SIOCGETSGCNT, &sg, errno, |
1369 | 0 | safe_strerror(errno)); |
1370 | 0 | return; |
1371 | 0 | } |
1372 | | |
1373 | 0 | c_oil->cc.pktcnt = sgreq.pktcnt; |
1374 | 0 | c_oil->cc.bytecnt = sgreq.bytecnt; |
1375 | 0 | c_oil->cc.wrong_if = sgreq.wrong_if; |
1376 | 0 | return; |
1377 | 0 | } |