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