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