1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * PIM for FRR - J/P Aggregation
4 * Copyright (C) 2017 Cumulus Networks, Inc.
15 #include "pim_instance.h"
17 #include "pim_jp_agg.h"
19 #include "pim_iface.h"
21 void pim_jp_agg_group_list_free(struct pim_jp_agg_group
*jag
)
23 list_delete(&jag
->sources
);
25 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
28 static void pim_jp_agg_src_free(struct pim_jp_sources
*js
)
30 struct pim_upstream
*up
= js
->up
;
33 * When we are being called here, we know
34 * that the neighbor is going away start
35 * the normal j/p timer so that it can
36 * pick this shit back up when the
37 * nbr comes back alive
40 join_timer_start(js
->up
);
41 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
44 int pim_jp_agg_group_list_cmp(void *arg1
, void *arg2
)
46 const struct pim_jp_agg_group
*jag1
=
47 (const struct pim_jp_agg_group
*)arg1
;
48 const struct pim_jp_agg_group
*jag2
=
49 (const struct pim_jp_agg_group
*)arg2
;
51 return pim_addr_cmp(jag1
->group
, jag2
->group
);
54 static int pim_jp_agg_src_cmp(void *arg1
, void *arg2
)
56 const struct pim_jp_sources
*js1
= (const struct pim_jp_sources
*)arg1
;
57 const struct pim_jp_sources
*js2
= (const struct pim_jp_sources
*)arg2
;
59 if (js1
->is_join
&& !js2
->is_join
)
62 if (!js1
->is_join
&& js2
->is_join
)
65 return pim_addr_cmp(js1
->up
->sg
.src
, js2
->up
->sg
.src
);
69 * This function is used by scan_oil to clear
70 * the created jp_agg_group created when
71 * figuring out where to send prunes
74 void pim_jp_agg_clear_group(struct list
*group
)
76 struct listnode
*gnode
, *gnnode
;
77 struct listnode
*snode
, *snnode
;
78 struct pim_jp_agg_group
*jag
;
79 struct pim_jp_sources
*js
;
81 for (ALL_LIST_ELEMENTS(group
, gnode
, gnnode
, jag
)) {
82 for (ALL_LIST_ELEMENTS(jag
->sources
, snode
, snnode
, js
)) {
83 listnode_delete(jag
->sources
, js
);
85 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
87 list_delete(&jag
->sources
);
88 listnode_delete(group
, jag
);
89 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
93 static struct pim_iface_upstream_switch
*
94 pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf
*rpf
)
96 struct interface
*ifp
= rpf
->source_nexthop
.interface
;
97 struct pim_interface
*pim_ifp
;
98 struct pim_iface_upstream_switch
*pius
;
99 struct listnode
*node
, *nnode
;
106 /* Old interface is pim disabled */
110 for (ALL_LIST_ELEMENTS(pim_ifp
->upstream_switch_list
, node
, nnode
,
112 if (!pim_addr_cmp(pius
->address
, rpf
->rpf_addr
))
117 pius
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
,
118 sizeof(struct pim_iface_upstream_switch
));
119 pius
->address
= rpf
->rpf_addr
;
120 pius
->us
= list_new();
121 listnode_add_sort(pim_ifp
->upstream_switch_list
, pius
);
127 void pim_jp_agg_remove_group(struct list
*group
, struct pim_upstream
*up
,
128 struct pim_neighbor
*nbr
)
130 struct listnode
*node
, *nnode
;
131 struct pim_jp_agg_group
*jag
= NULL
;
132 struct pim_jp_sources
*js
= NULL
;
134 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
135 if (!pim_addr_cmp(jag
->group
, up
->sg
.grp
))
142 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
149 zlog_debug("up %s remove from nbr %s/%pPAs jp-agg-list",
150 up
->sg_str
, nbr
->interface
->name
,
156 listnode_delete(jag
->sources
, js
);
157 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
160 if (jag
->sources
->count
== 0) {
161 list_delete(&jag
->sources
);
162 listnode_delete(group
, jag
);
163 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
167 int pim_jp_agg_is_in_list(struct list
*group
, struct pim_upstream
*up
)
169 struct listnode
*node
, *nnode
;
170 struct pim_jp_agg_group
*jag
= NULL
;
171 struct pim_jp_sources
*js
= NULL
;
173 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
174 if (!pim_addr_cmp(jag
->group
, up
->sg
.grp
))
181 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
189 //#define PIM_JP_AGG_DEBUG 1
191 * For the given upstream, check all the neighbor
192 * jp_agg lists and ensure that it is not
195 * *IF* ignore is true we can skip
196 * up->rpf.source_nexthop.interface particular interface for checking
198 * This is a debugging function, Probably
199 * can be safely compiled out in real
202 void pim_jp_agg_upstream_verification(struct pim_upstream
*up
, bool ignore
)
204 #ifdef PIM_JP_AGG_DEBUG
205 struct interface
*ifp
;
206 struct pim_interface
*pim_ifp
;
207 struct pim_instance
*pim
;
209 if (!up
->rpf
.source_nexthop
.interface
) {
210 if (PIM_DEBUG_PIM_TRACE
)
211 zlog_debug("%s: up %s RPF is not present", __func__
,
216 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
219 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
221 struct listnode
*nnode
;
223 if (ignore
&& ifp
== up
->rpf
.source_nexthop
.interface
)
227 struct pim_neighbor
*neigh
;
228 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
,
230 assert(!pim_jp_agg_is_in_list(
231 neigh
->upstream_jp_agg
, up
));
240 void pim_jp_agg_add_group(struct list
*group
, struct pim_upstream
*up
,
241 bool is_join
, struct pim_neighbor
*nbr
)
243 struct listnode
*node
, *nnode
;
244 struct pim_jp_agg_group
*jag
= NULL
;
245 struct pim_jp_sources
*js
= NULL
;
247 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
248 if (!pim_addr_cmp(jag
->group
, up
->sg
.grp
))
253 jag
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
,
254 sizeof(struct pim_jp_agg_group
));
255 jag
->group
= up
->sg
.grp
;
256 jag
->sources
= list_new();
257 jag
->sources
->cmp
= pim_jp_agg_src_cmp
;
258 jag
->sources
->del
= (void (*)(void *))pim_jp_agg_src_free
;
259 listnode_add_sort(group
, jag
);
262 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
269 zlog_debug("up %s add to nbr %s/%pPAs jp-agg-list",
271 up
->rpf
.source_nexthop
.interface
->name
,
276 js
= XCALLOC(MTYPE_PIM_JP_AGG_SOURCE
,
277 sizeof(struct pim_jp_sources
));
279 js
->is_join
= is_join
;
280 listnode_add_sort(jag
->sources
, js
);
282 if (js
->is_join
!= is_join
) {
283 listnode_delete(jag
->sources
, js
);
284 js
->is_join
= is_join
;
285 listnode_add_sort(jag
->sources
, js
);
290 void pim_jp_agg_switch_interface(struct pim_rpf
*orpf
, struct pim_rpf
*nrpf
,
291 struct pim_upstream
*up
)
293 struct pim_iface_upstream_switch
*opius
;
294 struct pim_iface_upstream_switch
*npius
;
296 opius
= pim_jp_agg_get_interface_upstream_switch_list(orpf
);
297 npius
= pim_jp_agg_get_interface_upstream_switch_list(nrpf
);
300 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
302 * Transitions from Joined State
304 * RPF'(S,G) changes not due to an Assert
306 * The upstream (S,G) state machine remains in Joined
307 * state. Send Join(S,G) to the new upstream neighbor, which is
308 * the new value of RPF'(S,G). Send Prune(S,G) to the old
309 * upstream neighbor, which is the old value of RPF'(S,G). Set
310 * the Join Timer (JT) to expire after t_periodic seconds.
313 /* send Prune(S,G) to the old upstream neighbor */
315 pim_jp_agg_add_group(opius
->us
, up
, false, NULL
);
317 /* send Join(S,G) to the current upstream neighbor */
319 pim_jp_agg_add_group(npius
->us
, up
, true, NULL
);
323 void pim_jp_agg_single_upstream_send(struct pim_rpf
*rpf
,
324 struct pim_upstream
*up
, bool is_join
)
326 struct list groups
, sources
;
327 struct pim_jp_agg_group jag
;
328 struct pim_jp_sources js
;
330 /* skip JP upstream messages if source is directly connected */
331 if (!up
|| !rpf
->source_nexthop
.interface
||
332 pim_if_connected_to_source(rpf
->source_nexthop
.interface
,
334 if_is_loopback(rpf
->source_nexthop
.interface
))
337 memset(&groups
, 0, sizeof(groups
));
338 memset(&sources
, 0, sizeof(sources
));
339 jag
.sources
= &sources
;
341 listnode_add(&groups
, &jag
);
342 listnode_add(jag
.sources
, &js
);
344 jag
.group
= up
->sg
.grp
;
346 js
.is_join
= is_join
;
348 pim_joinprune_send(rpf
, &groups
);
350 list_delete_all_node(jag
.sources
);
351 list_delete_all_node(&groups
);