]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_jp_agg.c
pimd: handle RPF resolution while in joined state
[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 if (jag1->group.s_addr < jag2->group.s_addr)
64 return -1;
65
66 if (jag1->group.s_addr > jag2->group.s_addr)
67 return 1;
68
69 return 0;
70 }
71
72 static int pim_jp_agg_src_cmp(void *arg1, void *arg2)
73 {
74 const struct pim_jp_sources *js1 = (const struct pim_jp_sources *)arg1;
75 const struct pim_jp_sources *js2 = (const struct pim_jp_sources *)arg2;
76
77 if (js1->is_join && !js2->is_join)
78 return -1;
79
80 if (!js1->is_join && js2->is_join)
81 return 1;
82
83 if ((uint32_t)js1->up->sg.src.s_addr < (uint32_t)js2->up->sg.src.s_addr)
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 return 0;
90 }
91
92 /*
93 * This function is used by scan_oil to clear
94 * the created jp_agg_group created when
95 * figuring out where to send prunes
96 * and joins.
97 */
98 void pim_jp_agg_clear_group(struct list *group)
99 {
100 struct listnode *gnode, *gnnode;
101 struct listnode *snode, *snnode;
102 struct pim_jp_agg_group *jag;
103 struct pim_jp_sources *js;
104
105 for (ALL_LIST_ELEMENTS(group, gnode, gnnode, jag)) {
106 for (ALL_LIST_ELEMENTS(jag->sources, snode, snnode, js)) {
107 listnode_delete(jag->sources, js);
108 js->up = NULL;
109 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
110 }
111 list_delete(&jag->sources);
112 listnode_delete(group, jag);
113 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
114 }
115 }
116
117 static struct pim_iface_upstream_switch *
118 pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
119 {
120 struct interface *ifp = rpf->source_nexthop.interface;
121 struct pim_interface *pim_ifp;
122 struct pim_iface_upstream_switch *pius;
123 struct listnode *node, *nnode;
124
125 if (!ifp)
126 return NULL;
127
128 pim_ifp = ifp->info;
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,
135 pius)) {
136 if (pius->address.s_addr == rpf->rpf_addr.u.prefix4.s_addr)
137 break;
138 }
139
140 if (!pius) {
141 pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
142 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 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 if (jag->group.s_addr == up->sg.grp.s_addr)
159 break;
160 }
161
162 if (!jag)
163 return;
164
165 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
166 if (js->up == up)
167 break;
168 }
169
170 if (js) {
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 list_delete(&jag->sources);
178 listnode_delete(group, jag);
179 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
180 }
181 }
182
183 int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up)
184 {
185 struct listnode *node, *nnode;
186 struct pim_jp_agg_group *jag = NULL;
187 struct pim_jp_sources *js = NULL;
188
189 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
190 if (jag->group.s_addr == up->sg.grp.s_addr)
191 break;
192 }
193
194 if (!jag)
195 return 0;
196
197 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
198 if (js->up == up)
199 return 1;
200 }
201
202 return 0;
203 }
204
205 //#define PIM_JP_AGG_DEBUG 1
206 /*
207 * For the given upstream, check all the neighbor
208 * jp_agg lists and ensure that it is not
209 * in another list
210 *
211 * *IF* ignore is true we can skip
212 * up->rpf.source_nexthop.interface particular interface for checking
213 *
214 * This is a debugging function, Probably
215 * can be safely compiled out in real
216 * builds
217 */
218 void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
219 {
220 #ifdef PIM_JP_AGG_DEBUG
221 struct interface *ifp;
222 struct pim_interface *pim_ifp;
223 struct pim_instance *pim;
224
225 if (!up->rpf.source_nexthop.interface) {
226 if (PIM_DEBUG_PIM_TRACE)
227 zlog_debug("%s: up %s RPF is not present",
228 __PRETTY_FUNCTION__, up->sg_str);
229 return;
230 }
231
232 pim_ifp = up->rpf.source_nexthop.interface->info;
233 pim = pim_ifp->pim;
234
235 FOR_ALL_INTERFACES (pim->vrf, ifp) {
236 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 struct pim_neighbor *neigh;
244 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
245 nnode, neigh)) {
246 assert(!pim_jp_agg_is_in_list(
247 neigh->upstream_jp_agg, up));
248 }
249 }
250 }
251 #else
252 return;
253 #endif
254 }
255
256 void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
257 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 if (jag->group.s_addr == up->sg.grp.s_addr)
265 break;
266 }
267
268 if (!jag) {
269 jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
270 sizeof(struct pim_jp_agg_group));
271 jag->group.s_addr = up->sg.grp.s_addr;
272 jag->sources = list_new();
273 jag->sources->cmp = pim_jp_agg_src_cmp;
274 jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
275 listnode_add_sort(group, jag);
276 }
277
278 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
279 if (js->up == up)
280 break;
281 }
282
283 if (!js) {
284 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
285 sizeof(struct pim_jp_sources));
286 js->up = up;
287 js->is_join = is_join;
288 listnode_add_sort(jag->sources, js);
289 } else {
290 if (js->is_join != is_join) {
291 listnode_delete(jag->sources, js);
292 js->is_join = is_join;
293 listnode_add_sort(jag->sources, js);
294 }
295 }
296 }
297
298 void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
299 struct pim_upstream *up)
300 {
301 struct pim_iface_upstream_switch *opius;
302 struct pim_iface_upstream_switch *npius;
303
304 opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
305 npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
306
307 /*
308 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
309 *
310 * Transitions from Joined State
311 *
312 * RPF'(S,G) changes not due to an Assert
313 *
314 * The upstream (S,G) state machine remains in Joined
315 * state. Send Join(S,G) to the new upstream neighbor, which is
316 * the new value of RPF'(S,G). Send Prune(S,G) to the old
317 * upstream neighbor, which is the old value of RPF'(S,G). Set
318 * the Join Timer (JT) to expire after t_periodic seconds.
319 */
320
321 /* send Prune(S,G) to the old upstream neighbor */
322 if (opius)
323 pim_jp_agg_add_group(opius->us, up, false);
324
325 /* send Join(S,G) to the current upstream neighbor */
326 pim_jp_agg_add_group(npius->us, up, true);
327 }
328
329
330 void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
331 struct pim_upstream *up, bool is_join)
332 {
333 static struct list *groups = NULL;
334 static struct pim_jp_agg_group jag;
335 static struct pim_jp_sources js;
336
337 static bool first = true;
338
339 /* skip JP upstream messages if source is directly connected */
340 if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source(
341 rpf->source_nexthop
342 .interface,
343 up->sg.src))
344 return;
345
346 if (first) {
347 groups = list_new();
348 jag.sources = list_new();
349
350 listnode_add(groups, &jag);
351 listnode_add(jag.sources, &js);
352
353 first = false;
354 }
355
356 jag.group.s_addr = up->sg.grp.s_addr;
357 js.up = up;
358 js.is_join = is_join;
359
360 pim_joinprune_send(rpf, groups);
361 }