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