]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_join.c
Merge pull request #10565 from lyq140/patch-thread
[mirror_frr.git] / pimd / pim_join.c
1
2
3 /*
4 * PIM for Quagga
5 * Copyright (C) 2008 Everton da Silva Marques
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <zebra.h>
23
24 #include "log.h"
25 #include "prefix.h"
26 #include "if.h"
27 #include "vty.h"
28 #include "plist.h"
29
30 #include "pimd.h"
31 #include "pim_str.h"
32 #include "pim_tlv.h"
33 #include "pim_msg.h"
34 #include "pim_pim.h"
35 #include "pim_join.h"
36 #include "pim_oil.h"
37 #include "pim_iface.h"
38 #include "pim_hello.h"
39 #include "pim_ifchannel.h"
40 #include "pim_rpf.h"
41 #include "pim_rp.h"
42 #include "pim_jp_agg.h"
43 #include "pim_util.h"
44 #include "pim_ssm.h"
45
46 static void on_trace(const char *label, struct interface *ifp, pim_addr src)
47 {
48 if (PIM_DEBUG_PIM_TRACE)
49 zlog_debug("%s: from %pPA on %s", label, &src, ifp->name);
50 }
51
52 static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
53 uint16_t holdtime, pim_addr upstream, pim_sgaddr *sg,
54 uint8_t source_flags)
55 {
56 struct pim_interface *pim_ifp = NULL;
57
58 if (PIM_DEBUG_PIM_TRACE)
59 zlog_debug(
60 "%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
61 __func__, sg, !!(source_flags & PIM_RPT_BIT_MASK),
62 !!(source_flags & PIM_WILDCARD_BIT_MASK), &upstream,
63 holdtime, &neigh->source_addr, ifp->name);
64
65 pim_ifp = ifp->info;
66 assert(pim_ifp);
67
68 ++pim_ifp->pim_ifstat_join_recv;
69
70 /*
71 * If the RPT and WC are set it's a (*,G)
72 * and the source is the RP
73 */
74 if ((source_flags & PIM_RPT_BIT_MASK)
75 && (source_flags & PIM_WILDCARD_BIT_MASK)) {
76 struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp);
77 pim_addr rpf_addr;
78
79 if (!rp) {
80 zlog_warn("%s: Lookup of RP failed for %pSG", __func__,
81 sg);
82 return;
83 }
84 /*
85 * If the RP sent in the message is not
86 * our RP for the group, drop the message
87 */
88 rpf_addr = pim_addr_from_prefix(&rp->rpf_addr);
89 if (pim_addr_cmp(sg->src, rpf_addr)) {
90 zlog_warn(
91 "%s: Specified RP(%pPAs) in join is different than our configured RP(%pPAs)",
92 __func__, &sg->src, &rpf_addr);
93 return;
94 }
95
96 if (pim_is_grp_ssm(pim_ifp->pim, sg->grp)) {
97 zlog_warn(
98 "%s: Specified Group(%pPA) in join is now in SSM, not allowed to create PIM state",
99 __func__, &sg->grp);
100 return;
101 }
102
103 sg->src = PIMADDR_ANY;
104 }
105
106 /* Restart join expiry timer */
107 pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, sg,
108 source_flags, holdtime);
109 }
110
111 static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh,
112 uint16_t holdtime, pim_addr upstream, pim_sgaddr *sg,
113 uint8_t source_flags)
114 {
115 struct pim_interface *pim_ifp = NULL;
116
117 if (PIM_DEBUG_PIM_TRACE)
118 zlog_debug(
119 "%s: prune (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
120 __func__, sg, source_flags & PIM_RPT_BIT_MASK,
121 source_flags & PIM_WILDCARD_BIT_MASK, &upstream,
122 holdtime, &neigh->source_addr, ifp->name);
123
124 pim_ifp = ifp->info;
125 assert(pim_ifp);
126
127 ++pim_ifp->pim_ifstat_prune_recv;
128
129 if ((source_flags & PIM_RPT_BIT_MASK)
130 && (source_flags & PIM_WILDCARD_BIT_MASK)) {
131 /*
132 * RFC 4601 Section 4.5.2:
133 * Received Prune(*,G) messages are processed even if the
134 * RP in the message does not match RP(G).
135 */
136 if (PIM_DEBUG_PIM_TRACE)
137 zlog_debug("%s: Prune received with RP(%pPAs) for %pSG",
138 __func__, &sg->src, sg);
139
140 sg->src = PIMADDR_ANY;
141 }
142
143 pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime);
144 }
145
146 int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
147 pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size)
148 {
149 pim_addr msg_upstream_addr;
150 bool wrong_af = false;
151 struct pim_interface *pim_ifp;
152 uint8_t msg_num_groups;
153 uint16_t msg_holdtime;
154 int addr_offset;
155 uint8_t *buf;
156 uint8_t *pastend;
157 int remain;
158 int group;
159 struct pim_ifchannel *child = NULL;
160 struct listnode *ch_node, *nch_node;
161
162 buf = tlv_buf;
163 pastend = tlv_buf + tlv_buf_size;
164 pim_ifp = ifp->info;
165
166 /*
167 Parse ucast addr
168 */
169 addr_offset = pim_parse_addr_ucast(&msg_upstream_addr, buf,
170 pastend - buf, &wrong_af);
171 if (addr_offset < 1) {
172 zlog_warn("%s: pim_parse_addr_ucast() failure: from %pPA on %s",
173 __func__, &src_addr, ifp->name);
174 return -1;
175 }
176 buf += addr_offset;
177
178 /*
179 Check upstream address family
180 */
181 if (wrong_af) {
182 zlog_warn(
183 "%s: ignoring join/prune directed to unexpected addr family from %pPA on %s",
184 __func__, &src_addr, ifp->name);
185 return -2;
186 }
187
188 remain = pastend - buf;
189 if (remain < 4) {
190 zlog_warn(
191 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %pPA on %s",
192 __func__, remain, 4, &src_addr, ifp->name);
193 return -4;
194 }
195
196 ++buf; /* skip reserved byte */
197 msg_num_groups = *(const uint8_t *)buf;
198 ++buf;
199 msg_holdtime = ntohs(*(const uint16_t *)buf);
200 ++buf;
201 ++buf;
202
203 if (PIM_DEBUG_PIM_J_P)
204 zlog_debug(
205 "%s: join/prune upstream=%pPAs groups=%d holdtime=%d from %pPA on %s",
206 __func__, &msg_upstream_addr, msg_num_groups,
207 msg_holdtime, &src_addr, ifp->name);
208
209 /* Scan groups */
210 for (group = 0; group < msg_num_groups; ++group) {
211 pim_sgaddr sg;
212 uint8_t msg_source_flags;
213 uint16_t msg_num_joined_sources;
214 uint16_t msg_num_pruned_sources;
215 int source;
216 struct pim_ifchannel *starg_ch = NULL, *sg_ch = NULL;
217 bool filtered = false;
218
219 memset(&sg, 0, sizeof(sg));
220 addr_offset = pim_parse_addr_group(&sg, buf, pastend - buf);
221 if (addr_offset < 1) {
222 return -5;
223 }
224 buf += addr_offset;
225
226 remain = pastend - buf;
227 if (remain < 4) {
228 zlog_warn(
229 "%s: short join/prune buffer for source list: size=%d minimum=%d from %pPA on %s",
230 __func__, remain, 4, &src_addr, ifp->name);
231 return -6;
232 }
233
234 msg_num_joined_sources = ntohs(*(const uint16_t *)buf);
235 buf += 2;
236 msg_num_pruned_sources = ntohs(*(const uint16_t *)buf);
237 buf += 2;
238
239 if (PIM_DEBUG_PIM_J_P)
240 zlog_debug(
241 "%s: join/prune upstream=%pPAs group=%pPA/32 join_src=%d prune_src=%d from %pPA on %s",
242 __func__, &msg_upstream_addr, &sg.grp,
243 msg_num_joined_sources, msg_num_pruned_sources,
244 &src_addr, ifp->name);
245
246 /* boundary check */
247 filtered = pim_is_group_filtered(pim_ifp, &sg.grp);
248
249 /* Scan joined sources */
250 for (source = 0; source < msg_num_joined_sources; ++source) {
251 addr_offset = pim_parse_addr_source(
252 &sg, &msg_source_flags, buf, pastend - buf);
253 if (addr_offset < 1) {
254 return -7;
255 }
256
257 buf += addr_offset;
258
259 /* if we are filtering this group, skip the join */
260 if (filtered)
261 continue;
262
263 recv_join(ifp, neigh, msg_holdtime, msg_upstream_addr,
264 &sg, msg_source_flags);
265
266 if (pim_addr_is_any(sg.src)) {
267 starg_ch = pim_ifchannel_find(ifp, &sg);
268 if (starg_ch)
269 pim_ifchannel_set_star_g_join_state(
270 starg_ch, 0, 1);
271 }
272 }
273
274 /* Scan pruned sources */
275 for (source = 0; source < msg_num_pruned_sources; ++source) {
276 addr_offset = pim_parse_addr_source(
277 &sg, &msg_source_flags, buf, pastend - buf);
278 if (addr_offset < 1) {
279 return -8;
280 }
281
282 buf += addr_offset;
283
284 /* if we are filtering this group, skip the prune */
285 if (filtered)
286 continue;
287
288 recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr,
289 &sg, msg_source_flags);
290 /*
291 * So if we are receiving a S,G,RPT prune
292 * before we have any data for that S,G
293 * We need to retrieve the sg_ch after
294 * we parse the prune.
295 */
296 sg_ch = pim_ifchannel_find(ifp, &sg);
297
298 if (!sg_ch)
299 continue;
300
301 /* (*,G) prune received */
302 for (ALL_LIST_ELEMENTS(sg_ch->sources, ch_node,
303 nch_node, child)) {
304 if (PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) {
305 if (child->ifjoin_state
306 == PIM_IFJOIN_PRUNE_PENDING_TMP)
307 THREAD_OFF(
308 child->t_ifjoin_prune_pending_timer);
309 THREAD_OFF(
310 child->t_ifjoin_expiry_timer);
311 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
312 child->ifjoin_state = PIM_IFJOIN_NOINFO;
313 delete_on_noinfo(child);
314 }
315 }
316
317 /* Received SG-RPT Prune delete oif from specific S,G */
318 if (starg_ch && (msg_source_flags & PIM_RPT_BIT_MASK)
319 && !(msg_source_flags & PIM_WILDCARD_BIT_MASK)) {
320 struct pim_upstream *up = sg_ch->upstream;
321 PIM_IF_FLAG_SET_S_G_RPT(sg_ch->flags);
322 if (up) {
323 if (PIM_DEBUG_PIM_TRACE)
324 zlog_debug(
325 "%s: SGRpt flag is set, del inherit oif from up %s",
326 __func__, up->sg_str);
327 pim_channel_del_inherited_oif(
328 up->channel_oil,
329 starg_ch->interface,
330 __func__);
331 }
332 }
333 }
334 if (starg_ch && !filtered)
335 pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0);
336 starg_ch = NULL;
337 } /* scan groups */
338
339 return 0;
340 }
341
342 /*
343 * J/P Message Format
344 *
345 * While the RFC clearly states that this is 32 bits wide, it
346 * is cheating. These fields:
347 * Encoded-Unicast format (6 bytes MIN)
348 * Encoded-Group format (8 bytes MIN)
349 * Encoded-Source format (8 bytes MIN)
350 * are *not* 32 bits wide.
351 *
352 * Nor does the RFC explicitly call out the size for:
353 * Reserved (1 byte)
354 * Num Groups (1 byte)
355 * Holdtime (2 bytes)
356 * Number of Joined Sources (2 bytes)
357 * Number of Pruned Sources (2 bytes)
358 *
359 * This leads to a missleading representation from casual
360 * reading and making assumptions. Be careful!
361 *
362 * 0 1 2 3
363 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
364 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 * |PIM Ver| Type | Reserved | Checksum |
366 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 * | Upstream Neighbor Address (Encoded-Unicast format) |
368 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 * | Reserved | Num groups | Holdtime |
370 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 * | Multicast Group Address 1 (Encoded-Group format) |
372 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373 * | Number of Joined Sources | Number of Pruned Sources |
374 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375 * | Joined Source Address 1 (Encoded-Source format) |
376 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377 * | . |
378 * | . |
379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 * | Joined Source Address n (Encoded-Source format) |
381 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 * | Pruned Source Address 1 (Encoded-Source format) |
383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 * | . |
385 * | . |
386 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387 * | Pruned Source Address n (Encoded-Source format) |
388 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 * | Multicast Group Address m (Encoded-Group format) |
390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 * | Number of Joined Sources | Number of Pruned Sources |
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 * | Joined Source Address 1 (Encoded-Source format) |
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * | . |
396 * | . |
397 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398 * | Joined Source Address n (Encoded-Source format) |
399 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
400 * | Pruned Source Address 1 (Encoded-Source format) |
401 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 * | . |
403 * | . |
404 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 * | Pruned Source Address n (Encoded-Source format) |
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407 */
408 int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
409 {
410 struct pim_jp_agg_group *group;
411 struct pim_interface *pim_ifp = NULL;
412 struct pim_jp_groups *grp = NULL;
413 struct pim_jp *msg = NULL;
414 struct listnode *node, *nnode;
415 uint8_t pim_msg[10000];
416 uint8_t *curr_ptr = pim_msg;
417 bool new_packet = true;
418 size_t packet_left = 0;
419 size_t packet_size = 0;
420 size_t group_size = 0;
421 pim_addr rpf_addr;
422
423 if (rpf->source_nexthop.interface)
424 pim_ifp = rpf->source_nexthop.interface->info;
425 else {
426 zlog_warn("%s: RPF interface is not present", __func__);
427 return -1;
428 }
429
430 rpf_addr = pim_addr_from_prefix(&rpf->rpf_addr);
431
432 on_trace(__func__, rpf->source_nexthop.interface, rpf_addr);
433
434 if (!pim_ifp) {
435 zlog_warn("%s: multicast not enabled on interface %s", __func__,
436 rpf->source_nexthop.interface->name);
437 return -1;
438 }
439
440 if (pim_addr_is_any(rpf_addr)) {
441 if (PIM_DEBUG_PIM_J_P)
442 zlog_debug(
443 "%s: upstream=%pPA is myself on interface %s",
444 __func__, &rpf_addr,
445 rpf->source_nexthop.interface->name);
446 return 0;
447 }
448
449 /*
450 RFC 4601: 4.3.1. Sending Hello Messages
451
452 Thus, if a router needs to send a Join/Prune or Assert message on
453 an interface on which it has not yet sent a Hello message with the
454 currently configured IP address, then it MUST immediately send the
455 relevant Hello message without waiting for the Hello Timer to
456 expire, followed by the Join/Prune or Assert message.
457 */
458 pim_hello_require(rpf->source_nexthop.interface);
459
460 for (ALL_LIST_ELEMENTS(groups, node, nnode, group)) {
461 if (new_packet) {
462 msg = (struct pim_jp *)pim_msg;
463
464 memset(msg, 0, sizeof(*msg));
465
466 pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
467 rpf_addr);
468 msg->reserved = 0;
469 msg->holdtime = htons(PIM_JP_HOLDTIME);
470
471 new_packet = false;
472
473 grp = &msg->groups[0];
474 curr_ptr = (uint8_t *)grp;
475 packet_size = sizeof(struct pim_msg_header);
476 packet_size += sizeof(pim_encoded_unicast);
477 packet_size +=
478 4; // reserved (1) + groups (1) + holdtime (2)
479
480 packet_left = rpf->source_nexthop.interface->mtu - 24;
481 packet_left -= packet_size;
482 }
483 if (PIM_DEBUG_PIM_J_P)
484 zlog_debug(
485 "%s: sending (G)=%pPAs to upstream=%pPA on interface %s",
486 __func__, &group->group, &rpf_addr,
487 rpf->source_nexthop.interface->name);
488
489 group_size = pim_msg_get_jp_group_size(group->sources);
490 if (group_size > packet_left) {
491 pim_msg_build_header(pim_msg, packet_size,
492 PIM_MSG_TYPE_JOIN_PRUNE, false);
493 if (pim_msg_send(pim_ifp->pim_sock_fd,
494 pim_ifp->primary_address,
495 qpim_all_pim_routers_addr, pim_msg,
496 packet_size,
497 rpf->source_nexthop.interface->name)) {
498 zlog_warn(
499 "%s: could not send PIM message on interface %s",
500 __func__,
501 rpf->source_nexthop.interface->name);
502 }
503
504 msg = (struct pim_jp *)pim_msg;
505 memset(msg, 0, sizeof(*msg));
506
507 pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
508 rpf_addr);
509 msg->reserved = 0;
510 msg->holdtime = htons(PIM_JP_HOLDTIME);
511
512 new_packet = false;
513
514 grp = &msg->groups[0];
515 curr_ptr = (uint8_t *)grp;
516 packet_size = sizeof(struct pim_msg_header);
517 packet_size += sizeof(pim_encoded_unicast);
518 packet_size +=
519 4; // reserved (1) + groups (1) + holdtime (2)
520
521 packet_left = rpf->source_nexthop.interface->mtu - 24;
522 packet_left -= packet_size;
523 }
524
525 msg->num_groups++;
526 /*
527 Build PIM message
528 */
529
530 curr_ptr += group_size;
531 packet_left -= group_size;
532 packet_size += group_size;
533 pim_msg_build_jp_groups(grp, group, group_size);
534
535 pim_ifp->pim_ifstat_join_send += ntohs(grp->joins);
536 pim_ifp->pim_ifstat_prune_send += ntohs(grp->prunes);
537
538 if (PIM_DEBUG_PIM_TRACE)
539 zlog_debug(
540 "%s: interface %s num_joins %u num_prunes %u",
541 __func__, rpf->source_nexthop.interface->name,
542 ntohs(grp->joins), ntohs(grp->prunes));
543
544 grp = (struct pim_jp_groups *)curr_ptr;
545 if (packet_left < sizeof(struct pim_jp_groups)
546 || msg->num_groups == 255) {
547 pim_msg_build_header(pim_msg, packet_size,
548 PIM_MSG_TYPE_JOIN_PRUNE, false);
549 if (pim_msg_send(pim_ifp->pim_sock_fd,
550 pim_ifp->primary_address,
551 qpim_all_pim_routers_addr, pim_msg,
552 packet_size,
553 rpf->source_nexthop.interface->name)) {
554 zlog_warn(
555 "%s: could not send PIM message on interface %s",
556 __func__,
557 rpf->source_nexthop.interface->name);
558 }
559
560 new_packet = true;
561 }
562 }
563
564
565 if (!new_packet) {
566 // msg->num_groups = htons (msg->num_groups);
567 pim_msg_build_header(pim_msg, packet_size,
568 PIM_MSG_TYPE_JOIN_PRUNE, false);
569 if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
570 qpim_all_pim_routers_addr, pim_msg,
571 packet_size,
572 rpf->source_nexthop.interface->name)) {
573 zlog_warn(
574 "%s: could not send PIM message on interface %s",
575 __func__, rpf->source_nexthop.interface->name);
576 }
577 }
578 return 0;
579 }