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