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