2 * PIM for FRR - J/P Aggregation
3 * Copyright (C) 2017 Cumulus Networks, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
30 #include "pim_jp_agg.h"
32 #include "pim_iface.h"
35 pim_jp_agg_group_list_free (struct pim_jp_agg_group
*jag
)
37 list_delete(jag
->sources
);
39 XFREE (MTYPE_PIM_JP_AGG_GROUP
, jag
);
43 pim_jp_agg_src_free (struct pim_jp_sources
*js
)
45 struct pim_upstream
*up
= js
->up
;
48 * When we are being called here, we know
49 * that the neighbor is going away start
50 * the normal j/p timer so that it can
51 * pick this shit back up when the
52 * nbr comes back alive
55 join_timer_start(js
->up
);
56 XFREE (MTYPE_PIM_JP_AGG_SOURCE
, js
);
60 pim_jp_agg_group_list_cmp (void *arg1
, void *arg2
)
62 const struct pim_jp_agg_group
*jag1
= (const struct pim_jp_agg_group
*)arg1
;
63 const struct pim_jp_agg_group
*jag2
= (const struct pim_jp_agg_group
*)arg2
;
65 if (jag1
->group
.s_addr
< jag2
->group
.s_addr
)
68 if (jag1
->group
.s_addr
> jag2
->group
.s_addr
)
75 pim_jp_agg_src_cmp (void *arg1
, void *arg2
)
77 const struct pim_jp_sources
*js1
= (const struct pim_jp_sources
*)arg1
;
78 const struct pim_jp_sources
*js2
= (const struct pim_jp_sources
*)arg2
;
80 if (js1
->is_join
&& !js2
->is_join
)
83 if (!js1
->is_join
&& js2
->is_join
)
86 if ((uint32_t)js1
->up
->sg
.src
.s_addr
< (uint32_t)js2
->up
->sg
.src
.s_addr
)
89 if ((uint32_t)js1
->up
->sg
.src
.s_addr
> (uint32_t)js2
->up
->sg
.src
.s_addr
)
96 * This function is used by scan_oil to clear
97 * the created jp_agg_group created when
98 * figuring out where to send prunes
102 pim_jp_agg_clear_group (struct list
*group
)
104 struct listnode
*gnode
, *gnnode
;
105 struct listnode
*snode
, *snnode
;
106 struct pim_jp_agg_group
*jag
;
107 struct pim_jp_sources
*js
;
109 for (ALL_LIST_ELEMENTS(group
, gnode
, gnnode
, jag
))
111 for (ALL_LIST_ELEMENTS(jag
->sources
, snode
, snnode
, js
))
113 listnode_delete(jag
->sources
, js
);
115 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
118 listnode_delete(group
, jag
);
119 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
123 static struct pim_iface_upstream_switch
*
124 pim_jp_agg_get_interface_upstream_switch_list (struct pim_rpf
*rpf
)
126 struct pim_interface
*pim_ifp
= rpf
->source_nexthop
.interface
->info
;
127 struct pim_iface_upstream_switch
*pius
;
128 struct listnode
*node
, *nnode
;
130 for (ALL_LIST_ELEMENTS(pim_ifp
->upstream_switch_list
, node
, nnode
, pius
))
132 if (pius
->address
.s_addr
== rpf
->rpf_addr
.u
.prefix4
.s_addr
)
138 pius
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
, sizeof (struct pim_iface_upstream_switch
));
139 pius
->address
.s_addr
= rpf
->rpf_addr
.u
.prefix4
.s_addr
;
140 pius
->us
= list_new();
141 listnode_add_sort (pim_ifp
->upstream_switch_list
, pius
);
148 pim_jp_agg_remove_group (struct list
*group
, struct pim_upstream
*up
)
150 struct listnode
*node
, *nnode
;
151 struct pim_jp_agg_group
*jag
= NULL
;
152 struct pim_jp_sources
*js
= NULL
;
154 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
))
156 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
163 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
))
172 listnode_delete(jag
->sources
, js
);
173 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
176 if (jag
->sources
->count
== 0)
178 list_delete(jag
->sources
);
180 listnode_delete(group
, jag
);
181 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
187 pim_jp_agg_is_in_list (struct list
*group
, struct pim_upstream
*up
)
189 struct listnode
*node
, *nnode
;
190 struct pim_jp_agg_group
*jag
= NULL
;
191 struct pim_jp_sources
*js
= NULL
;
193 for (ALL_LIST_ELEMENTS (group
, node
, nnode
, jag
))
195 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
202 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
))
211 //#define PIM_JP_AGG_DEBUG 1
213 * For the given upstream, check all the neighbor
214 * jp_agg lists and ensure that it is not
217 * *IF* ignore is true we can skip
218 * up->rpf.source_nexthop.interface particular interface for checking
220 * This is a debugging function, Probably
221 * can be safely compiled out in real
225 pim_jp_agg_upstream_verification (struct pim_upstream
*up
, bool ignore
)
227 #ifdef PIM_JP_AGG_DEBUG
228 struct listnode
*node
;
229 struct interface
*ifp
;
231 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT
), node
, ifp
))
233 struct pim_interface
*pim_ifp
= ifp
->info
;
234 struct listnode
*nnode
;
236 if (ignore
&& ifp
== up
->rpf
.source_nexthop
.interface
)
241 struct pim_neighbor
*neigh
;
242 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
, nnode
, neigh
))
244 assert (!pim_jp_agg_is_in_list(neigh
->upstream_jp_agg
, up
));
254 pim_jp_agg_add_group (struct list
*group
, struct pim_upstream
*up
, bool is_join
)
256 struct listnode
*node
, *nnode
;
257 struct pim_jp_agg_group
*jag
= NULL
;
258 struct pim_jp_sources
*js
= NULL
;
260 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
))
262 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
268 jag
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
, sizeof (struct pim_jp_agg_group
));
269 jag
->group
.s_addr
= up
->sg
.grp
.s_addr
;
270 jag
->sources
= list_new();
271 jag
->sources
->cmp
= pim_jp_agg_src_cmp
;
272 jag
->sources
->del
= (void (*)(void *))pim_jp_agg_src_free
;
273 listnode_add_sort (group
, jag
);
276 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
))
284 js
= XCALLOC(MTYPE_PIM_JP_AGG_SOURCE
, sizeof (struct pim_jp_sources
));
286 js
->is_join
= is_join
;
287 listnode_add_sort (jag
->sources
, js
);
291 if (js
->is_join
!= is_join
)
293 listnode_delete(jag
->sources
, js
);
294 js
->is_join
= is_join
;
295 listnode_add_sort (jag
->sources
, js
);
301 pim_jp_agg_switch_interface (struct pim_rpf
*orpf
,
302 struct pim_rpf
*nrpf
,
303 struct pim_upstream
*up
)
305 struct pim_iface_upstream_switch
*opius
;
306 struct pim_iface_upstream_switch
*npius
;
308 opius
= pim_jp_agg_get_interface_upstream_switch_list(orpf
);
309 npius
= pim_jp_agg_get_interface_upstream_switch_list(nrpf
);
312 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
314 * Transitions from Joined State
316 * RPF'(S,G) changes not due to an Assert
318 * The upstream (S,G) state machine remains in Joined
319 * state. Send Join(S,G) to the new upstream neighbor, which is
320 * the new value of RPF'(S,G). Send Prune(S,G) to the old
321 * upstream neighbor, which is the old value of RPF'(S,G). Set
322 * the Join Timer (JT) to expire after t_periodic seconds.
325 /* send Prune(S,G) to the old upstream neighbor */
326 pim_jp_agg_add_group (opius
->us
, up
, false);
328 /* send Join(S,G) to the current upstream neighbor */
329 pim_jp_agg_add_group (npius
->us
, up
, true);
335 pim_jp_agg_single_upstream_send (struct pim_rpf
*rpf
,
336 struct pim_upstream
*up
,
339 static struct list
*groups
= NULL
;
340 static struct pim_jp_agg_group jag
;
341 static struct pim_jp_sources js
;
343 static bool first
= true;
345 /* skip JP upstream messages if source is directly connected */
346 if (pim_if_connected_to_source (rpf
->source_nexthop
.interface
, up
->sg
.src
))
353 jag
.sources
= list_new();
355 listnode_add(groups
, &jag
);
356 listnode_add(jag
.sources
, &js
);
361 jag
.group
.s_addr
= up
->sg
.grp
.s_addr
;
363 js
.is_join
= is_join
;
365 pim_joinprune_send(rpf
, groups
);