]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_jp_agg.c
Merge remote-tracking branch 'origin/stable/2.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 for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode, pius))
131 {
132 if (pius->address.s_addr == rpf->rpf_addr.u.prefix4.s_addr)
133 break;
134 }
135
136 if (!pius)
137 {
138 pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_iface_upstream_switch));
139 pius->address.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
140 pius->us = list_new();
141 listnode_add_sort (pim_ifp->upstream_switch_list, pius);
142 }
143
144 return pius;
145 }
146
147 void
148 pim_jp_agg_remove_group (struct list *group, struct pim_upstream *up)
149 {
150 struct listnode *node, *nnode;
151 struct pim_jp_agg_group *jag = NULL;
152 struct pim_jp_sources *js = NULL;
153
154 for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
155 {
156 if (jag->group.s_addr == up->sg.grp.s_addr)
157 break;
158 }
159
160 if (!jag)
161 return;
162
163 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
164 {
165 if (js->up == up)
166 break;
167 }
168
169 if (js)
170 {
171 js->up = NULL;
172 listnode_delete(jag->sources, js);
173 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
174 }
175
176 if (jag->sources->count == 0)
177 {
178 list_delete(jag->sources);
179 jag->sources = NULL;
180 listnode_delete(group, jag);
181 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
182 }
183
184 }
185
186 int
187 pim_jp_agg_is_in_list (struct list *group, struct pim_upstream *up)
188 {
189 struct listnode *node, *nnode;
190 struct pim_jp_agg_group *jag = NULL;
191 struct pim_jp_sources *js = NULL;
192
193 for (ALL_LIST_ELEMENTS (group, node, nnode, jag))
194 {
195 if (jag->group.s_addr == up->sg.grp.s_addr)
196 break;
197 }
198
199 if (!jag)
200 return 0;
201
202 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
203 {
204 if (js->up == up)
205 return 1;
206 }
207
208 return 0;
209 }
210
211 //#define PIM_JP_AGG_DEBUG 1
212 /*
213 * For the given upstream, check all the neighbor
214 * jp_agg lists and ensure that it is not
215 * in another list
216 *
217 * *IF* ignore is true we can skip
218 * up->rpf.source_nexthop.interface particular interface for checking
219 *
220 * This is a debugging function, Probably
221 * can be safely compiled out in real
222 * builds
223 */
224 void
225 pim_jp_agg_upstream_verification (struct pim_upstream *up, bool ignore)
226 {
227 #ifdef PIM_JP_AGG_DEBUG
228 struct listnode *node;
229 struct interface *ifp;
230
231 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
232 {
233 struct pim_interface *pim_ifp = ifp->info;
234 struct listnode *nnode;
235
236 if (ignore && ifp == up->rpf.source_nexthop.interface)
237 continue;
238
239 if (pim_ifp)
240 {
241 struct pim_neighbor *neigh;
242 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, nnode, neigh))
243 {
244 assert (!pim_jp_agg_is_in_list(neigh->upstream_jp_agg, up));
245 }
246 }
247 }
248 #else
249 return;
250 #endif
251 }
252
253 void
254 pim_jp_agg_add_group (struct list *group, struct pim_upstream *up, bool is_join)
255 {
256 struct listnode *node, *nnode;
257 struct pim_jp_agg_group *jag = NULL;
258 struct pim_jp_sources *js = NULL;
259
260 for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
261 {
262 if (jag->group.s_addr == up->sg.grp.s_addr)
263 break;
264 }
265
266 if (!jag)
267 {
268 jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_jp_agg_group));
269 jag->group.s_addr = up->sg.grp.s_addr;
270 jag->sources = list_new();
271 jag->sources->cmp = pim_jp_agg_src_cmp;
272 jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
273 listnode_add_sort (group, jag);
274 }
275
276 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
277 {
278 if (js->up == up)
279 break;
280 }
281
282 if (!js)
283 {
284 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof (struct pim_jp_sources));
285 js->up = up;
286 js->is_join = is_join;
287 listnode_add_sort (jag->sources, js);
288 }
289 else
290 {
291 if (js->is_join != is_join)
292 {
293 listnode_delete(jag->sources, js);
294 js->is_join = is_join;
295 listnode_add_sort (jag->sources, js);
296 }
297 }
298 }
299
300 void
301 pim_jp_agg_switch_interface (struct pim_rpf *orpf,
302 struct pim_rpf *nrpf,
303 struct pim_upstream *up)
304 {
305 struct pim_iface_upstream_switch *opius;
306 struct pim_iface_upstream_switch *npius;
307
308 opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
309 npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
310
311 /*
312 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
313 *
314 * Transitions from Joined State
315 *
316 * RPF'(S,G) changes not due to an Assert
317 *
318 * The upstream (S,G) state machine remains in Joined
319 * state. Send Join(S,G) to the new upstream neighbor, which is
320 * the new value of RPF'(S,G). Send Prune(S,G) to the old
321 * upstream neighbor, which is the old value of RPF'(S,G). Set
322 * the Join Timer (JT) to expire after t_periodic seconds.
323 */
324
325 /* send Prune(S,G) to the old upstream neighbor */
326 pim_jp_agg_add_group (opius->us, up, false);
327
328 /* send Join(S,G) to the current upstream neighbor */
329 pim_jp_agg_add_group (npius->us, up, true);
330
331 }
332
333
334 void
335 pim_jp_agg_single_upstream_send (struct pim_rpf *rpf,
336 struct pim_upstream *up,
337 bool is_join)
338 {
339 static struct list *groups = NULL;
340 static struct pim_jp_agg_group jag;
341 static struct pim_jp_sources js;
342
343 static bool first = true;
344
345 /* skip JP upstream messages if source is directly connected */
346 if (pim_if_connected_to_source (rpf->source_nexthop.interface, up->sg.src))
347 return;
348
349 if (first)
350 {
351 groups = list_new();
352
353 jag.sources = list_new();
354
355 listnode_add(groups, &jag);
356 listnode_add(jag.sources, &js);
357
358 first = false;
359 }
360
361 jag.group.s_addr = up->sg.grp.s_addr;
362 js.up = up;
363 js.is_join = is_join;
364
365 pim_joinprune_send(rpf, groups);
366 }