]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_jp_agg.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / pimd / pim_jp_agg.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for FRR - J/P Aggregation
4 * Copyright (C) 2017 Cumulus Networks, Inc.
5 * Donald Sharp
6 */
7 #include <zebra.h>
8
9 #include "linklist.h"
10 #include "log.h"
11 #include "vrf.h"
12 #include "if.h"
13
14 #include "pimd.h"
15 #include "pim_instance.h"
16 #include "pim_msg.h"
17 #include "pim_jp_agg.h"
18 #include "pim_join.h"
19 #include "pim_iface.h"
20
21 void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag)
22 {
23 list_delete(&jag->sources);
24
25 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
26 }
27
28 static void pim_jp_agg_src_free(struct pim_jp_sources *js)
29 {
30 struct pim_upstream *up = js->up;
31
32 /*
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
38 */
39 if (up)
40 join_timer_start(js->up);
41 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
42 }
43
44 int pim_jp_agg_group_list_cmp(void *arg1, void *arg2)
45 {
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;
50
51 return pim_addr_cmp(jag1->group, jag2->group);
52 }
53
54 static int pim_jp_agg_src_cmp(void *arg1, void *arg2)
55 {
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;
58
59 if (js1->is_join && !js2->is_join)
60 return -1;
61
62 if (!js1->is_join && js2->is_join)
63 return 1;
64
65 return pim_addr_cmp(js1->up->sg.src, js2->up->sg.src);
66 }
67
68 /*
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
72 * and joins.
73 */
74 void pim_jp_agg_clear_group(struct list *group)
75 {
76 struct listnode *gnode, *gnnode;
77 struct listnode *snode, *snnode;
78 struct pim_jp_agg_group *jag;
79 struct pim_jp_sources *js;
80
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);
84 js->up = NULL;
85 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
86 }
87 list_delete(&jag->sources);
88 listnode_delete(group, jag);
89 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
90 }
91 }
92
93 static struct pim_iface_upstream_switch *
94 pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
95 {
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;
100
101 if (!ifp)
102 return NULL;
103
104 pim_ifp = ifp->info;
105
106 /* Old interface is pim disabled */
107 if (!pim_ifp)
108 return NULL;
109
110 for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode,
111 pius)) {
112 if (!pim_addr_cmp(pius->address, rpf->rpf_addr))
113 break;
114 }
115
116 if (!pius) {
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);
122 }
123
124 return pius;
125 }
126
127 void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
128 struct pim_neighbor *nbr)
129 {
130 struct listnode *node, *nnode;
131 struct pim_jp_agg_group *jag = NULL;
132 struct pim_jp_sources *js = NULL;
133
134 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
135 if (!pim_addr_cmp(jag->group, up->sg.grp))
136 break;
137 }
138
139 if (!jag)
140 return;
141
142 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
143 if (js->up == up)
144 break;
145 }
146
147 if (nbr) {
148 if (PIM_DEBUG_TRACE)
149 zlog_debug("up %s remove from nbr %s/%pPAs jp-agg-list",
150 up->sg_str, nbr->interface->name,
151 &nbr->source_addr);
152 }
153
154 if (js) {
155 js->up = NULL;
156 listnode_delete(jag->sources, js);
157 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
158 }
159
160 if (jag->sources->count == 0) {
161 list_delete(&jag->sources);
162 listnode_delete(group, jag);
163 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
164 }
165 }
166
167 int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up)
168 {
169 struct listnode *node, *nnode;
170 struct pim_jp_agg_group *jag = NULL;
171 struct pim_jp_sources *js = NULL;
172
173 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
174 if (!pim_addr_cmp(jag->group, up->sg.grp))
175 break;
176 }
177
178 if (!jag)
179 return 0;
180
181 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
182 if (js->up == up)
183 return 1;
184 }
185
186 return 0;
187 }
188
189 //#define PIM_JP_AGG_DEBUG 1
190 /*
191 * For the given upstream, check all the neighbor
192 * jp_agg lists and ensure that it is not
193 * in another list
194 *
195 * *IF* ignore is true we can skip
196 * up->rpf.source_nexthop.interface particular interface for checking
197 *
198 * This is a debugging function, Probably
199 * can be safely compiled out in real
200 * builds
201 */
202 void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
203 {
204 #ifdef PIM_JP_AGG_DEBUG
205 struct interface *ifp;
206 struct pim_interface *pim_ifp;
207 struct pim_instance *pim;
208
209 if (!up->rpf.source_nexthop.interface) {
210 if (PIM_DEBUG_PIM_TRACE)
211 zlog_debug("%s: up %s RPF is not present", __func__,
212 up->sg_str);
213 return;
214 }
215
216 pim_ifp = up->rpf.source_nexthop.interface->info;
217 pim = pim_ifp->pim;
218
219 FOR_ALL_INTERFACES (pim->vrf, ifp) {
220 pim_ifp = ifp->info;
221 struct listnode *nnode;
222
223 if (ignore && ifp == up->rpf.source_nexthop.interface)
224 continue;
225
226 if (pim_ifp) {
227 struct pim_neighbor *neigh;
228 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
229 nnode, neigh)) {
230 assert(!pim_jp_agg_is_in_list(
231 neigh->upstream_jp_agg, up));
232 }
233 }
234 }
235 #else
236 return;
237 #endif
238 }
239
240 void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
241 bool is_join, struct pim_neighbor *nbr)
242 {
243 struct listnode *node, *nnode;
244 struct pim_jp_agg_group *jag = NULL;
245 struct pim_jp_sources *js = NULL;
246
247 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
248 if (!pim_addr_cmp(jag->group, up->sg.grp))
249 break;
250 }
251
252 if (!jag) {
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);
260 }
261
262 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
263 if (js->up == up)
264 break;
265 }
266
267 if (nbr) {
268 if (PIM_DEBUG_TRACE)
269 zlog_debug("up %s add to nbr %s/%pPAs jp-agg-list",
270 up->sg_str,
271 up->rpf.source_nexthop.interface->name,
272 &nbr->source_addr);
273 }
274
275 if (!js) {
276 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
277 sizeof(struct pim_jp_sources));
278 js->up = up;
279 js->is_join = is_join;
280 listnode_add_sort(jag->sources, js);
281 } else {
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);
286 }
287 }
288 }
289
290 void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
291 struct pim_upstream *up)
292 {
293 struct pim_iface_upstream_switch *opius;
294 struct pim_iface_upstream_switch *npius;
295
296 opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
297 npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
298
299 /*
300 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
301 *
302 * Transitions from Joined State
303 *
304 * RPF'(S,G) changes not due to an Assert
305 *
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.
311 */
312
313 /* send Prune(S,G) to the old upstream neighbor */
314 if (opius)
315 pim_jp_agg_add_group(opius->us, up, false, NULL);
316
317 /* send Join(S,G) to the current upstream neighbor */
318 if (npius)
319 pim_jp_agg_add_group(npius->us, up, true, NULL);
320 }
321
322
323 void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
324 struct pim_upstream *up, bool is_join)
325 {
326 struct list groups, sources;
327 struct pim_jp_agg_group jag;
328 struct pim_jp_sources js;
329
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,
333 up->sg.src) ||
334 if_is_loopback(rpf->source_nexthop.interface))
335 return;
336
337 memset(&groups, 0, sizeof(groups));
338 memset(&sources, 0, sizeof(sources));
339 jag.sources = &sources;
340
341 listnode_add(&groups, &jag);
342 listnode_add(jag.sources, &js);
343
344 jag.group = up->sg.grp;
345 js.up = up;
346 js.is_join = is_join;
347
348 pim_joinprune_send(rpf, &groups);
349
350 list_delete_all_node(jag.sources);
351 list_delete_all_node(&groups);
352 }