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 along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include "pim_jp_agg.h"
31 #include "pim_iface.h"
33 void pim_jp_agg_group_list_free(struct pim_jp_agg_group
*jag
)
35 list_delete(&jag
->sources
);
37 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
40 static void pim_jp_agg_src_free(struct pim_jp_sources
*js
)
42 struct pim_upstream
*up
= js
->up
;
45 * When we are being called here, we know
46 * that the neighbor is going away start
47 * the normal j/p timer so that it can
48 * pick this shit back up when the
49 * nbr comes back alive
52 join_timer_start(js
->up
);
53 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
56 int pim_jp_agg_group_list_cmp(void *arg1
, void *arg2
)
58 const struct pim_jp_agg_group
*jag1
=
59 (const struct pim_jp_agg_group
*)arg1
;
60 const struct pim_jp_agg_group
*jag2
=
61 (const struct pim_jp_agg_group
*)arg2
;
63 if (jag1
->group
.s_addr
< jag2
->group
.s_addr
)
66 if (jag1
->group
.s_addr
> jag2
->group
.s_addr
)
72 static int pim_jp_agg_src_cmp(void *arg1
, void *arg2
)
74 const struct pim_jp_sources
*js1
= (const struct pim_jp_sources
*)arg1
;
75 const struct pim_jp_sources
*js2
= (const struct pim_jp_sources
*)arg2
;
77 if (js1
->is_join
&& !js2
->is_join
)
80 if (!js1
->is_join
&& js2
->is_join
)
83 if ((uint32_t)js1
->up
->sg
.src
.s_addr
< (uint32_t)js2
->up
->sg
.src
.s_addr
)
86 if ((uint32_t)js1
->up
->sg
.src
.s_addr
> (uint32_t)js2
->up
->sg
.src
.s_addr
)
93 * This function is used by scan_oil to clear
94 * the created jp_agg_group created when
95 * figuring out where to send prunes
98 void pim_jp_agg_clear_group(struct list
*group
)
100 struct listnode
*gnode
, *gnnode
;
101 struct listnode
*snode
, *snnode
;
102 struct pim_jp_agg_group
*jag
;
103 struct pim_jp_sources
*js
;
105 for (ALL_LIST_ELEMENTS(group
, gnode
, gnnode
, jag
)) {
106 for (ALL_LIST_ELEMENTS(jag
->sources
, snode
, snnode
, js
)) {
107 listnode_delete(jag
->sources
, js
);
109 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
111 list_delete(&jag
->sources
);
112 listnode_delete(group
, jag
);
113 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
117 static struct pim_iface_upstream_switch
*
118 pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf
*rpf
)
120 struct interface
*ifp
= rpf
->source_nexthop
.interface
;
121 struct pim_interface
*pim_ifp
;
122 struct pim_iface_upstream_switch
*pius
;
123 struct listnode
*node
, *nnode
;
130 /* Old interface is pim disabled */
134 for (ALL_LIST_ELEMENTS(pim_ifp
->upstream_switch_list
, node
, nnode
,
136 if (pius
->address
.s_addr
== rpf
->rpf_addr
.u
.prefix4
.s_addr
)
141 pius
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
,
142 sizeof(struct pim_iface_upstream_switch
));
143 pius
->address
.s_addr
= rpf
->rpf_addr
.u
.prefix4
.s_addr
;
144 pius
->us
= list_new();
145 listnode_add_sort(pim_ifp
->upstream_switch_list
, pius
);
151 void pim_jp_agg_remove_group(struct list
*group
, struct pim_upstream
*up
,
152 struct pim_neighbor
*nbr
)
154 struct listnode
*node
, *nnode
;
155 struct pim_jp_agg_group
*jag
= NULL
;
156 struct pim_jp_sources
*js
= NULL
;
158 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
159 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
166 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
172 if (PIM_DEBUG_TRACE
) {
173 char src_str
[INET_ADDRSTRLEN
];
175 pim_inet4_dump("<src?>", nbr
->source_addr
, src_str
,
178 "up %s remove from nbr %s/%s jp-agg-list",
180 nbr
->interface
->name
,
187 listnode_delete(jag
->sources
, js
);
188 XFREE(MTYPE_PIM_JP_AGG_SOURCE
, js
);
191 if (jag
->sources
->count
== 0) {
192 list_delete(&jag
->sources
);
193 listnode_delete(group
, jag
);
194 XFREE(MTYPE_PIM_JP_AGG_GROUP
, jag
);
198 int pim_jp_agg_is_in_list(struct list
*group
, struct pim_upstream
*up
)
200 struct listnode
*node
, *nnode
;
201 struct pim_jp_agg_group
*jag
= NULL
;
202 struct pim_jp_sources
*js
= NULL
;
204 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
205 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
212 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
220 //#define PIM_JP_AGG_DEBUG 1
222 * For the given upstream, check all the neighbor
223 * jp_agg lists and ensure that it is not
226 * *IF* ignore is true we can skip
227 * up->rpf.source_nexthop.interface particular interface for checking
229 * This is a debugging function, Probably
230 * can be safely compiled out in real
233 void pim_jp_agg_upstream_verification(struct pim_upstream
*up
, bool ignore
)
235 #ifdef PIM_JP_AGG_DEBUG
236 struct interface
*ifp
;
237 struct pim_interface
*pim_ifp
;
238 struct pim_instance
*pim
;
240 if (!up
->rpf
.source_nexthop
.interface
) {
241 if (PIM_DEBUG_PIM_TRACE
)
242 zlog_debug("%s: up %s RPF is not present", __func__
,
247 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
250 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
252 struct listnode
*nnode
;
254 if (ignore
&& ifp
== up
->rpf
.source_nexthop
.interface
)
258 struct pim_neighbor
*neigh
;
259 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->pim_neighbor_list
,
261 assert(!pim_jp_agg_is_in_list(
262 neigh
->upstream_jp_agg
, up
));
271 void pim_jp_agg_add_group(struct list
*group
, struct pim_upstream
*up
,
272 bool is_join
, struct pim_neighbor
*nbr
)
274 struct listnode
*node
, *nnode
;
275 struct pim_jp_agg_group
*jag
= NULL
;
276 struct pim_jp_sources
*js
= NULL
;
278 for (ALL_LIST_ELEMENTS(group
, node
, nnode
, jag
)) {
279 if (jag
->group
.s_addr
== up
->sg
.grp
.s_addr
)
284 jag
= XCALLOC(MTYPE_PIM_JP_AGG_GROUP
,
285 sizeof(struct pim_jp_agg_group
));
286 jag
->group
.s_addr
= up
->sg
.grp
.s_addr
;
287 jag
->sources
= list_new();
288 jag
->sources
->cmp
= pim_jp_agg_src_cmp
;
289 jag
->sources
->del
= (void (*)(void *))pim_jp_agg_src_free
;
290 listnode_add_sort(group
, jag
);
293 for (ALL_LIST_ELEMENTS(jag
->sources
, node
, nnode
, js
)) {
299 if (PIM_DEBUG_TRACE
) {
300 char src_str
[INET_ADDRSTRLEN
];
302 pim_inet4_dump("<src?>", nbr
->source_addr
, src_str
,
305 "up %s add to nbr %s/%s jp-agg-list",
307 up
->rpf
.source_nexthop
.interface
->name
,
313 js
= XCALLOC(MTYPE_PIM_JP_AGG_SOURCE
,
314 sizeof(struct pim_jp_sources
));
316 js
->is_join
= is_join
;
317 listnode_add_sort(jag
->sources
, js
);
319 if (js
->is_join
!= is_join
) {
320 listnode_delete(jag
->sources
, js
);
321 js
->is_join
= is_join
;
322 listnode_add_sort(jag
->sources
, js
);
327 void pim_jp_agg_switch_interface(struct pim_rpf
*orpf
, struct pim_rpf
*nrpf
,
328 struct pim_upstream
*up
)
330 struct pim_iface_upstream_switch
*opius
;
331 struct pim_iface_upstream_switch
*npius
;
333 opius
= pim_jp_agg_get_interface_upstream_switch_list(orpf
);
334 npius
= pim_jp_agg_get_interface_upstream_switch_list(nrpf
);
337 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
339 * Transitions from Joined State
341 * RPF'(S,G) changes not due to an Assert
343 * The upstream (S,G) state machine remains in Joined
344 * state. Send Join(S,G) to the new upstream neighbor, which is
345 * the new value of RPF'(S,G). Send Prune(S,G) to the old
346 * upstream neighbor, which is the old value of RPF'(S,G). Set
347 * the Join Timer (JT) to expire after t_periodic seconds.
350 /* send Prune(S,G) to the old upstream neighbor */
352 pim_jp_agg_add_group(opius
->us
, up
, false, NULL
);
354 /* send Join(S,G) to the current upstream neighbor */
356 pim_jp_agg_add_group(npius
->us
, up
, true, NULL
);
360 void pim_jp_agg_single_upstream_send(struct pim_rpf
*rpf
,
361 struct pim_upstream
*up
, bool is_join
)
363 static struct list
*groups
= NULL
;
364 static struct pim_jp_agg_group jag
;
365 static struct pim_jp_sources js
;
367 static bool first
= true;
369 /* skip JP upstream messages if source is directly connected */
370 if (!up
|| !rpf
->source_nexthop
.interface
||
371 pim_if_connected_to_source(rpf
->source_nexthop
.interface
,
373 if_is_loopback_or_vrf(rpf
->source_nexthop
.interface
))
378 jag
.sources
= list_new();
380 listnode_add(groups
, &jag
);
381 listnode_add(jag
.sources
, &js
);
386 jag
.group
.s_addr
= up
->sg
.grp
.s_addr
;
388 js
.is_join
= is_join
;
390 pim_joinprune_send(rpf
, groups
);