]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_jp_agg.c
Merge branch 'frr/pull/550'
[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
34 pim_jp_agg_group_list_free (struct pim_jp_agg_group *jag)
35 {
36 list_delete(jag->sources);
37
38 XFREE (MTYPE_PIM_JP_AGG_GROUP, jag);
39 }
40
41 static void
42 pim_jp_agg_src_free (struct pim_jp_sources *js)
43 {
44 struct pim_upstream *up = js->up;
45
46 /*
47 * When we are being called here, we know
48 * that the neighbor is going away start
49 * the normal j/p timer so that it can
50 * pick this shit back up when the
51 * nbr comes back alive
52 */
53 if (up)
54 join_timer_start(js->up);
55 XFREE (MTYPE_PIM_JP_AGG_SOURCE, js);
56 }
57
58 int
59 pim_jp_agg_group_list_cmp (void *arg1, void *arg2)
60 {
61 const struct pim_jp_agg_group *jag1 = (const struct pim_jp_agg_group *)arg1;
62 const struct pim_jp_agg_group *jag2 = (const struct pim_jp_agg_group *)arg2;
63
64 if (jag1->group.s_addr < jag2->group.s_addr)
65 return -1;
66
67 if (jag1->group.s_addr > jag2->group.s_addr)
68 return 1;
69
70 return 0;
71 }
72
73 static int
74 pim_jp_agg_src_cmp (void *arg1, void *arg2)
75 {
76 const struct pim_jp_sources *js1 = (const struct pim_jp_sources *)arg1;
77 const struct pim_jp_sources *js2 = (const struct pim_jp_sources *)arg2;
78
79 if (js1->is_join && !js2->is_join)
80 return -1;
81
82 if (!js1->is_join && js2->is_join)
83 return 1;
84
85 if ((uint32_t)js1->up->sg.src.s_addr < (uint32_t)js2->up->sg.src.s_addr)
86 return -1;
87
88 if ((uint32_t)js1->up->sg.src.s_addr > (uint32_t)js2->up->sg.src.s_addr)
89 return 1;
90
91 return 0;
92 }
93
94 /*
95 * This function is used by scan_oil to clear
96 * the created jp_agg_group created when
97 * figuring out where to send prunes
98 * and joins.
99 */
100 void
101 pim_jp_agg_clear_group (struct list *group)
102 {
103 struct listnode *gnode, *gnnode;
104 struct listnode *snode, *snnode;
105 struct pim_jp_agg_group *jag;
106 struct pim_jp_sources *js;
107
108 for (ALL_LIST_ELEMENTS(group, gnode, gnnode, jag))
109 {
110 for (ALL_LIST_ELEMENTS(jag->sources, snode, snnode, js))
111 {
112 listnode_delete(jag->sources, js);
113 js->up = NULL;
114 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
115 }
116 jag->sources = NULL;
117 listnode_delete(group, jag);
118 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
119 }
120 }
121
122 static struct pim_iface_upstream_switch *
123 pim_jp_agg_get_interface_upstream_switch_list (struct pim_rpf *rpf)
124 {
125 struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info;
126 struct pim_iface_upstream_switch *pius;
127 struct listnode *node, *nnode;
128
129 /* Old interface is pim disabled */
130 if (!pim_ifp)
131 return NULL;
132
133 for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode, pius))
134 {
135 if (pius->address.s_addr == rpf->rpf_addr.u.prefix4.s_addr)
136 break;
137 }
138
139 if (!pius)
140 {
141 pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_iface_upstream_switch));
142 pius->address.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
143 pius->us = list_new();
144 listnode_add_sort (pim_ifp->upstream_switch_list, pius);
145 }
146
147 return pius;
148 }
149
150 void
151 pim_jp_agg_remove_group (struct list *group, struct pim_upstream *up)
152 {
153 struct listnode *node, *nnode;
154 struct pim_jp_agg_group *jag = NULL;
155 struct pim_jp_sources *js = NULL;
156
157 for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
158 {
159 if (jag->group.s_addr == up->sg.grp.s_addr)
160 break;
161 }
162
163 if (!jag)
164 return;
165
166 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
167 {
168 if (js->up == up)
169 break;
170 }
171
172 if (js)
173 {
174 js->up = NULL;
175 listnode_delete(jag->sources, js);
176 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
177 }
178
179 if (jag->sources->count == 0)
180 {
181 list_delete(jag->sources);
182 jag->sources = NULL;
183 listnode_delete(group, jag);
184 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
185 }
186
187 }
188
189 int
190 pim_jp_agg_is_in_list (struct list *group, struct pim_upstream *up)
191 {
192 struct listnode *node, *nnode;
193 struct pim_jp_agg_group *jag = NULL;
194 struct pim_jp_sources *js = NULL;
195
196 for (ALL_LIST_ELEMENTS (group, node, nnode, jag))
197 {
198 if (jag->group.s_addr == up->sg.grp.s_addr)
199 break;
200 }
201
202 if (!jag)
203 return 0;
204
205 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
206 {
207 if (js->up == up)
208 return 1;
209 }
210
211 return 0;
212 }
213
214 //#define PIM_JP_AGG_DEBUG 1
215 /*
216 * For the given upstream, check all the neighbor
217 * jp_agg lists and ensure that it is not
218 * in another list
219 *
220 * *IF* ignore is true we can skip
221 * up->rpf.source_nexthop.interface particular interface for checking
222 *
223 * This is a debugging function, Probably
224 * can be safely compiled out in real
225 * builds
226 */
227 void
228 pim_jp_agg_upstream_verification (struct pim_upstream *up, bool ignore)
229 {
230 #ifdef PIM_JP_AGG_DEBUG
231 struct listnode *node;
232 struct interface *ifp;
233
234 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
235 {
236 struct pim_interface *pim_ifp = ifp->info;
237 struct listnode *nnode;
238
239 if (ignore && ifp == up->rpf.source_nexthop.interface)
240 continue;
241
242 if (pim_ifp)
243 {
244 struct pim_neighbor *neigh;
245 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, nnode, neigh))
246 {
247 assert (!pim_jp_agg_is_in_list(neigh->upstream_jp_agg, up));
248 }
249 }
250 }
251 #else
252 return;
253 #endif
254 }
255
256 void
257 pim_jp_agg_add_group (struct list *group, struct pim_upstream *up, bool is_join)
258 {
259 struct listnode *node, *nnode;
260 struct pim_jp_agg_group *jag = NULL;
261 struct pim_jp_sources *js = NULL;
262
263 for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
264 {
265 if (jag->group.s_addr == up->sg.grp.s_addr)
266 break;
267 }
268
269 if (!jag)
270 {
271 jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_jp_agg_group));
272 jag->group.s_addr = up->sg.grp.s_addr;
273 jag->sources = list_new();
274 jag->sources->cmp = pim_jp_agg_src_cmp;
275 jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
276 listnode_add_sort (group, jag);
277 }
278
279 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
280 {
281 if (js->up == up)
282 break;
283 }
284
285 if (!js)
286 {
287 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof (struct pim_jp_sources));
288 js->up = up;
289 js->is_join = is_join;
290 listnode_add_sort (jag->sources, js);
291 }
292 else
293 {
294 if (js->is_join != is_join)
295 {
296 listnode_delete(jag->sources, js);
297 js->is_join = is_join;
298 listnode_add_sort (jag->sources, js);
299 }
300 }
301 }
302
303 void
304 pim_jp_agg_switch_interface (struct pim_rpf *orpf,
305 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);
331
332 /* send Join(S,G) to the current upstream neighbor */
333 pim_jp_agg_add_group (npius->us, up, true);
334
335 }
336
337
338 void
339 pim_jp_agg_single_upstream_send (struct pim_rpf *rpf,
340 struct pim_upstream *up,
341 bool is_join)
342 {
343 static struct list *groups = NULL;
344 static struct pim_jp_agg_group jag;
345 static struct pim_jp_sources js;
346
347 static bool first = true;
348
349 /* skip JP upstream messages if source is directly connected */
350 if (!up || !rpf->source_nexthop.interface ||
351 pim_if_connected_to_source (rpf->source_nexthop.interface, up->sg.src))
352 return;
353
354 if (first)
355 {
356 groups = list_new();
357
358 jag.sources = list_new();
359
360 listnode_add(groups, &jag);
361 listnode_add(jag.sources, &js);
362
363 first = false;
364 }
365
366 jag.group.s_addr = up->sg.grp.s_addr;
367 js.up = up;
368 js.is_join = is_join;
369
370 pim_joinprune_send(rpf, groups);
371 }