]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_join.c
pimd: Fix join_desired for (*,G) states
[mirror_frr.git] / pimd / pim_join.c
CommitLineData
12e41d03
DL
1/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
12e41d03
DL
20*/
21
22#include <zebra.h>
23
24#include "log.h"
25#include "prefix.h"
744d91b3 26#include "if.h"
12e41d03
DL
27
28#include "pimd.h"
29#include "pim_str.h"
30#include "pim_tlv.h"
31#include "pim_msg.h"
32#include "pim_pim.h"
33#include "pim_join.h"
3667b0bc 34#include "pim_oil.h"
12e41d03
DL
35#include "pim_iface.h"
36#include "pim_hello.h"
37#include "pim_ifchannel.h"
6c629103
DS
38#include "pim_rpf.h"
39#include "pim_rp.h"
12e41d03 40
4950938e
DS
41static void
42on_trace (const char *label,
43 struct interface *ifp, struct in_addr src)
12e41d03
DL
44{
45 if (PIM_DEBUG_PIM_TRACE) {
46 char src_str[100];
47 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
48 zlog_debug("%s: from %s on %s",
49 label, src_str, ifp->name);
50 }
51}
52
53static void recv_join(struct interface *ifp,
54 struct pim_neighbor *neigh,
55 uint16_t holdtime,
56 struct in_addr upstream,
57 struct in_addr group,
58 struct in_addr source,
59 uint8_t source_flags)
60{
4ed0af70 61 struct prefix_sg sg;
ee1a477a 62
4ed0af70
DS
63 memset (&sg, 0, sizeof (struct prefix_sg));
64 sg.src = source;
65 sg.grp = group;
ee1a477a 66
12e41d03
DL
67 if (PIM_DEBUG_PIM_TRACE) {
68 char up_str[100];
12e41d03
DL
69 char neigh_str[100];
70 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
12e41d03 71 pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
ee1a477a 72 zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
12e41d03 73 __PRETTY_FUNCTION__,
ee1a477a 74 pim_str_sg_dump (&sg),
12e41d03
DL
75 source_flags & PIM_RPT_BIT_MASK,
76 source_flags & PIM_WILDCARD_BIT_MASK,
77 up_str, holdtime, neigh_str, ifp->name);
78 }
346cffe3
DS
79
80 /*
81 * If the RPT and WC are set it's a (*,G)
82 * and the source is the RP
83 */
84 if ((source_flags & PIM_RPT_BIT_MASK) &&
85 (source_flags & PIM_WILDCARD_BIT_MASK))
86 {
4ed0af70 87 struct pim_rpf *rp = RP (sg.grp);
346cffe3
DS
88
89 /*
90 * If the RP sent in the message is not
91 * our RP for the group, drop the message
92 */
4ed0af70 93 if (sg.src.s_addr != rp->rpf_addr.s_addr)
346cffe3
DS
94 return;
95
4ed0af70 96 sg.src.s_addr = INADDR_ANY;
346cffe3
DS
97 }
98
12e41d03
DL
99 /* Restart join expiry timer */
100 pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
ee1a477a 101 &sg, source_flags, holdtime);
6c629103 102
4ed0af70 103 if (sg.src.s_addr == INADDR_ANY)
6c629103 104 {
3667b0bc
DS
105 struct pim_upstream *up = pim_upstream_find (&sg);
106 struct pim_upstream *child;
107 struct listnode *up_node;
6c629103 108
3667b0bc 109 for (ALL_LIST_ELEMENTS_RO (qpim_upstream_list, up_node, child))
6c629103 110 {
3667b0bc
DS
111 if (child->parent == up)
112 {
113 char buff[100];
7a3ddda5 114
3667b0bc
DS
115 strcpy (buff, pim_str_sg_dump (&up->sg));
116 zlog_debug("%s %s: Join(S,G)=%s from %s",
117 __FILE__, __PRETTY_FUNCTION__,
118 buff, pim_str_sg_dump (&sg));
119
7a3ddda5
DS
120 if (pim_upstream_evaluate_join_desired (child))
121 {
122 pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
123 pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
124 }
3667b0bc 125 }
6c629103
DS
126 }
127 }
128
12e41d03
DL
129}
130
131static void recv_prune(struct interface *ifp,
132 struct pim_neighbor *neigh,
133 uint16_t holdtime,
134 struct in_addr upstream,
135 struct in_addr group,
136 struct in_addr source,
137 uint8_t source_flags)
138{
4ed0af70 139 struct prefix_sg sg;
7bd2c6fa 140
4ed0af70
DS
141 memset (&sg, 0, sizeof (struct prefix_sg));
142 sg.src = source;
143 sg.grp = group;
7bd2c6fa 144
12e41d03
DL
145 if (PIM_DEBUG_PIM_TRACE) {
146 char up_str[100];
12e41d03
DL
147 char neigh_str[100];
148 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
12e41d03 149 pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
7bd2c6fa 150 zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
12e41d03 151 __PRETTY_FUNCTION__,
7bd2c6fa 152 pim_str_sg_dump (&sg),
12e41d03
DL
153 source_flags & PIM_RPT_BIT_MASK,
154 source_flags & PIM_WILDCARD_BIT_MASK,
155 up_str, holdtime, neigh_str, ifp->name);
156 }
035f28f6
DS
157
158 if ((source_flags & PIM_RPT_BIT_MASK) &&
159 (source_flags & PIM_WILDCARD_BIT_MASK))
160 {
4ed0af70 161 struct pim_rpf *rp = RP (sg.grp);
035f28f6
DS
162
163 // Ignoring Prune *,G's at the moment.
4ed0af70 164 if (sg.src.s_addr != rp->rpf_addr.s_addr)
035f28f6
DS
165 return;
166
4ed0af70 167 sg.src.s_addr = INADDR_ANY;
035f28f6 168 }
12e41d03 169
7bd2c6fa 170 pim_ifchannel_prune(ifp, upstream, &sg, source_flags, holdtime);
6c629103 171
4ed0af70 172 if (sg.src.s_addr == INADDR_ANY)
6c629103 173 {
3667b0bc
DS
174 struct pim_upstream *up = pim_upstream_find (&sg);
175 struct pim_upstream *child;
176 struct listnode *up_node;
6c629103 177
3667b0bc 178 for (ALL_LIST_ELEMENTS_RO (qpim_upstream_list, up_node, child))
6c629103 179 {
3667b0bc
DS
180 if (child->parent == up)
181 {
182 char buff[100];
183 strcpy (buff, pim_str_sg_dump (&up->sg));
184 zlog_debug("%s %s: Prune(S,G)=%s from %s",
185 __FILE__, __PRETTY_FUNCTION__,
186 buff, pim_str_sg_dump (&sg));
7a3ddda5
DS
187
188 if (!pim_upstream_evaluate_join_desired (child))
189 pim_channel_del_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
3667b0bc 190 }
6c629103
DS
191 }
192 }
193
12e41d03
DL
194}
195
196int pim_joinprune_recv(struct interface *ifp,
197 struct pim_neighbor *neigh,
198 struct in_addr src_addr,
199 uint8_t *tlv_buf, int tlv_buf_size)
200{
201 struct prefix msg_upstream_addr;
202 uint8_t msg_num_groups;
203 uint16_t msg_holdtime;
204 int addr_offset;
205 uint8_t *buf;
206 uint8_t *pastend;
207 int remain;
208 int group;
209
210 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
211
212 buf = tlv_buf;
213 pastend = tlv_buf + tlv_buf_size;
214
215 /*
216 Parse ucast addr
217 */
4416b1f6
DS
218 addr_offset = pim_parse_addr_ucast (&msg_upstream_addr,
219 buf, pastend - buf);
12e41d03
DL
220 if (addr_offset < 1) {
221 char src_str[100];
222 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
223 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
224 __PRETTY_FUNCTION__,
225 src_str, ifp->name);
226 return -1;
227 }
228 buf += addr_offset;
229
230 /*
231 Check upstream address family
232 */
233 if (msg_upstream_addr.family != AF_INET) {
4950938e 234 if (PIM_DEBUG_PIM_J_P) {
12e41d03
DL
235 char src_str[100];
236 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
237 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
238 __PRETTY_FUNCTION__,
239 msg_upstream_addr.family, src_str, ifp->name);
240 }
241 return -2;
242 }
243
244 remain = pastend - buf;
245 if (remain < 4) {
246 char src_str[100];
247 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
248 zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
249 __PRETTY_FUNCTION__,
250 remain, 4, src_str, ifp->name);
251 return -4;
252 }
253
254 ++buf; /* skip reserved byte */
255 msg_num_groups = *(const uint8_t *) buf;
256 ++buf;
257 msg_holdtime = ntohs(*(const uint16_t *) buf);
258 ++buf;
259 ++buf;
260
4950938e 261 if (PIM_DEBUG_PIM_J_P) {
12e41d03
DL
262 char src_str[100];
263 char upstream_str[100];
264 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
265 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
266 upstream_str, sizeof(upstream_str));
4950938e
DS
267 zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
268 __PRETTY_FUNCTION__,
269 upstream_str, msg_num_groups, msg_holdtime,
270 src_str, ifp->name);
12e41d03
DL
271 }
272
273 /* Scan groups */
274 for (group = 0; group < msg_num_groups; ++group) {
275 struct prefix msg_group_addr;
276 struct prefix msg_source_addr;
277 uint8_t msg_source_flags;
278 uint16_t msg_num_joined_sources;
279 uint16_t msg_num_pruned_sources;
280 int source;
281
4416b1f6
DS
282 addr_offset = pim_parse_addr_group (&msg_group_addr,
283 buf, pastend - buf);
12e41d03
DL
284 if (addr_offset < 1) {
285 return -5;
286 }
287 buf += addr_offset;
288
289 remain = pastend - buf;
290 if (remain < 4) {
291 char src_str[100];
292 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
293 zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
294 __PRETTY_FUNCTION__,
295 remain, 4, src_str, ifp->name);
296 return -6;
297 }
298
299 msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
300 buf += 2;
301 msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
302 buf += 2;
303
4950938e 304 if (PIM_DEBUG_PIM_J_P) {
12e41d03
DL
305 char src_str[100];
306 char upstream_str[100];
307 char group_str[100];
308 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
309 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
310 upstream_str, sizeof(upstream_str));
311 pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
312 group_str, sizeof(group_str));
313 zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
314 __PRETTY_FUNCTION__,
315 upstream_str, group_str, msg_group_addr.prefixlen,
316 msg_num_joined_sources, msg_num_pruned_sources,
317 src_str, ifp->name);
318 }
319
320 /* Scan joined sources */
321 for (source = 0; source < msg_num_joined_sources; ++source) {
4416b1f6
DS
322 addr_offset = pim_parse_addr_source (&msg_source_addr,
323 &msg_source_flags,
324 buf, pastend - buf);
12e41d03
DL
325 if (addr_offset < 1) {
326 return -7;
327 }
328
329 buf += addr_offset;
330
331 recv_join(ifp, neigh, msg_holdtime,
332 msg_upstream_addr.u.prefix4,
333 msg_group_addr.u.prefix4,
334 msg_source_addr.u.prefix4,
335 msg_source_flags);
336 }
337
338 /* Scan pruned sources */
339 for (source = 0; source < msg_num_pruned_sources; ++source) {
4416b1f6
DS
340 addr_offset = pim_parse_addr_source (&msg_source_addr,
341 &msg_source_flags,
342 buf, pastend - buf);
12e41d03
DL
343 if (addr_offset < 1) {
344 return -8;
345 }
346
347 buf += addr_offset;
348
349 recv_prune(ifp, neigh, msg_holdtime,
350 msg_upstream_addr.u.prefix4,
351 msg_group_addr.u.prefix4,
352 msg_source_addr.u.prefix4,
353 msg_source_flags);
354 }
355
356 } /* scan groups */
357
358 return 0;
359}
360
361int pim_joinprune_send(struct interface *ifp,
362 struct in_addr upstream_addr,
4ed0af70 363 struct prefix_sg *sg,
12e41d03
DL
364 int send_join)
365{
366 struct pim_interface *pim_ifp;
367 uint8_t pim_msg[1000];
12e41d03 368 int pim_msg_size;
12e41d03 369
4950938e
DS
370 on_trace (__PRETTY_FUNCTION__, ifp, upstream_addr);
371
12e41d03
DL
372 zassert(ifp);
373
374 pim_ifp = ifp->info;
375
376 if (!pim_ifp) {
377 zlog_warn("%s: multicast not enabled on interface %s",
378 __PRETTY_FUNCTION__,
379 ifp->name);
380 return -1;
381 }
382
4950938e 383 if (PIM_DEBUG_PIM_J_P) {
12e41d03 384 char dst_str[100];
12e41d03 385 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
05e451f8 386 zlog_debug("%s: sending %s(S,G)=%s to upstream=%s on interface %s",
12e41d03
DL
387 __PRETTY_FUNCTION__,
388 send_join ? "Join" : "Prune",
05e451f8 389 pim_str_sg_dump (sg), dst_str, ifp->name);
12e41d03
DL
390 }
391
392 if (PIM_INADDR_IS_ANY(upstream_addr)) {
4950938e 393 if (PIM_DEBUG_PIM_J_P) {
12e41d03 394 char dst_str[100];
12e41d03 395 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
05e451f8 396 zlog_debug("%s: %s(S,G)=%s: upstream=%s is myself on interface %s",
12e41d03
DL
397 __PRETTY_FUNCTION__,
398 send_join ? "Join" : "Prune",
05e451f8 399 pim_str_sg_dump (sg), dst_str, ifp->name);
12e41d03
DL
400 }
401 return 0;
402 }
403
404 /*
405 RFC 4601: 4.3.1. Sending Hello Messages
406
407 Thus, if a router needs to send a Join/Prune or Assert message on
408 an interface on which it has not yet sent a Hello message with the
409 currently configured IP address, then it MUST immediately send the
410 relevant Hello message without waiting for the Hello Timer to
411 expire, followed by the Join/Prune or Assert message.
412 */
413 pim_hello_require(ifp);
414
415 /*
416 Build PIM message
417 */
346cffe3 418 pim_msg_size = pim_msg_join_prune_encode (pim_msg, 1000, send_join,
4ed0af70 419 sg->src, sg->grp,
346cffe3 420 upstream_addr, PIM_JP_HOLDTIME);
12e41d03 421
346cffe3
DS
422 if (pim_msg_size < 0)
423 return pim_msg_size;
12e41d03
DL
424
425 if (pim_msg_send(pim_ifp->pim_sock_fd,
426 qpim_all_pim_routers_addr,
427 pim_msg,
428 pim_msg_size,
429 ifp->name)) {
430 zlog_warn("%s: could not send PIM message on interface %s",
431 __PRETTY_FUNCTION__, ifp->name);
432 return -8;
433 }
434
435 return 0;
436}