3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
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,
37 #include "pim_iface.h"
38 #include "pim_hello.h"
39 #include "pim_ifchannel.h"
44 on_trace (const char *label
,
45 struct interface
*ifp
, struct in_addr src
)
47 if (PIM_DEBUG_PIM_TRACE
) {
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
);
55 static void recv_join(struct interface
*ifp
,
56 struct pim_neighbor
*neigh
,
58 struct in_addr upstream
,
60 struct in_addr source
,
65 memset (&sg
, 0, sizeof (struct prefix_sg
));
69 if (PIM_DEBUG_PIM_TRACE
) {
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",
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
);
83 * If the RPT and WC are set it's a (*,G)
84 * and the source is the RP
86 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
87 (source_flags
& PIM_WILDCARD_BIT_MASK
))
89 struct pim_rpf
*rp
= RP (sg
.grp
);
92 * If the RP sent in the message is not
93 * our RP for the group, drop the message
95 if (sg
.src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
98 sg
.src
.s_addr
= INADDR_ANY
;
101 /* Restart join expiry timer */
102 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
,
103 &sg
, source_flags
, holdtime
);
105 if (sg
.src
.s_addr
== INADDR_ANY
)
107 struct pim_upstream
*up
= pim_upstream_find (&sg
);
108 struct pim_upstream
*child
;
109 struct listnode
*up_node
;
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
120 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list
, up_node
, child
))
122 if (child
->parent
== up
)
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
));
132 if (pim_upstream_evaluate_join_desired (child
))
134 pim_channel_add_oif (child
->channel_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
135 pim_upstream_switch (child
, PIM_UPSTREAM_JOINED
);
143 static void recv_prune(struct interface
*ifp
,
144 struct pim_neighbor
*neigh
,
146 struct in_addr upstream
,
147 struct in_addr group
,
148 struct in_addr source
,
149 uint8_t source_flags
)
153 memset (&sg
, 0, sizeof (struct prefix_sg
));
157 if (PIM_DEBUG_PIM_TRACE
) {
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",
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
);
170 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
171 (source_flags
& PIM_WILDCARD_BIT_MASK
))
173 struct pim_rpf
*rp
= RP (sg
.grp
);
175 // Ignoring Prune *,G's at the moment.
176 if (sg
.src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
179 sg
.src
.s_addr
= INADDR_ANY
;
182 pim_ifchannel_prune(ifp
, upstream
, &sg
, source_flags
, holdtime
);
184 if (sg
.src
.s_addr
== INADDR_ANY
)
186 struct pim_upstream
*up
= pim_upstream_find (&sg
);
187 struct pim_upstream
*child
;
188 struct listnode
*up_node
;
191 * If up is not found then there is nothing
192 * to do here (see recv_join above)
197 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list
, up_node
, child
))
199 if (child
->parent
== up
)
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
;
205 if (PIM_DEBUG_PIM_TRACE
)
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
));
216 if (!pim_upstream_evaluate_join_desired (child
))
217 pim_channel_del_oif (c_oil
, ifp
, PIM_OIF_FLAG_PROTO_PIM
);
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.
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
);
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
)
236 struct prefix msg_upstream_addr
;
237 uint8_t msg_num_groups
;
238 uint16_t msg_holdtime
;
245 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
248 pastend
= tlv_buf
+ tlv_buf_size
;
253 addr_offset
= pim_parse_addr_ucast (&msg_upstream_addr
,
255 if (addr_offset
< 1) {
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",
266 Check upstream address family
268 if (msg_upstream_addr
.family
!= AF_INET
) {
269 if (PIM_DEBUG_PIM_J_P
) {
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",
274 msg_upstream_addr
.family
, src_str
, ifp
->name
);
279 remain
= pastend
- buf
;
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",
285 remain
, 4, src_str
, ifp
->name
);
289 ++buf
; /* skip reserved byte */
290 msg_num_groups
= *(const uint8_t *) buf
;
292 msg_holdtime
= ntohs(*(const uint16_t *) buf
);
296 if (PIM_DEBUG_PIM_J_P
) {
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",
304 upstream_str
, msg_num_groups
, msg_holdtime
,
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
;
317 addr_offset
= pim_parse_addr_group (&msg_group_addr
,
319 if (addr_offset
< 1) {
324 remain
= pastend
- buf
;
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",
330 remain
, 4, src_str
, ifp
->name
);
334 msg_num_joined_sources
= ntohs(*(const uint16_t *) buf
);
336 msg_num_pruned_sources
= ntohs(*(const uint16_t *) buf
);
339 if (PIM_DEBUG_PIM_J_P
) {
341 char upstream_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",
350 upstream_str
, group_str
, msg_group_addr
.prefixlen
,
351 msg_num_joined_sources
, msg_num_pruned_sources
,
355 /* Scan joined sources */
356 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
357 addr_offset
= pim_parse_addr_source (&msg_source_addr
,
360 if (addr_offset
< 1) {
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
,
373 /* Scan pruned sources */
374 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
375 addr_offset
= pim_parse_addr_source (&msg_source_addr
,
378 if (addr_offset
< 1) {
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
,
396 int pim_joinprune_send(struct interface
*ifp
,
397 struct in_addr upstream_addr
,
398 struct prefix_sg
*sg
,
401 struct pim_interface
*pim_ifp
;
402 uint8_t pim_msg
[1000];
405 on_trace (__PRETTY_FUNCTION__
, ifp
, upstream_addr
);
412 zlog_warn("%s: multicast not enabled on interface %s",
418 if (PIM_DEBUG_PIM_J_P
) {
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",
423 send_join
? "Join" : "Prune",
424 pim_str_sg_dump (sg
), dst_str
, ifp
->name
);
427 if (PIM_INADDR_IS_ANY(upstream_addr
)) {
428 if (PIM_DEBUG_PIM_J_P
) {
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",
433 send_join
? "Join" : "Prune",
434 pim_str_sg_dump (sg
), dst_str
, ifp
->name
);
440 RFC 4601: 4.3.1. Sending Hello Messages
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.
448 pim_hello_require(ifp
);
453 pim_msg_size
= pim_msg_join_prune_encode (pim_msg
, 1000, send_join
,
455 upstream_addr
, PIM_JP_HOLDTIME
);
457 if (pim_msg_size
< 0)
460 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
461 pim_ifp
->primary_address
,
462 qpim_all_pim_routers_addr
,
466 zlog_warn("%s: could not send PIM message on interface %s",
467 __PRETTY_FUNCTION__
, ifp
->name
);