]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_jp_agg.c
Merge pull request #5498 from mjstapp/sharp_with_labels
[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"
28#include "pim_msg.h"
29#include "pim_jp_agg.h"
30#include "pim_join.h"
31#include "pim_iface.h"
32
d62a17ae 33void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag)
982bff89 34{
6a154c88 35 list_delete(&jag->sources);
982bff89 36
d62a17ae 37 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
982bff89
DS
38}
39
d62a17ae 40static void pim_jp_agg_src_free(struct pim_jp_sources *js)
982bff89 41{
d62a17ae 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);
982bff89
DS
54}
55
d62a17ae 56int pim_jp_agg_group_list_cmp(void *arg1, void *arg2)
982bff89 57{
d62a17ae 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;
982bff89 62
d62a17ae 63 if (jag1->group.s_addr < jag2->group.s_addr)
64 return -1;
982bff89 65
d62a17ae 66 if (jag1->group.s_addr > jag2->group.s_addr)
67 return 1;
982bff89 68
d62a17ae 69 return 0;
982bff89
DS
70}
71
d62a17ae 72static int pim_jp_agg_src_cmp(void *arg1, void *arg2)
982bff89 73{
d62a17ae 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;
982bff89 76
d62a17ae 77 if (js1->is_join && !js2->is_join)
78 return -1;
d0db90bf 79
d62a17ae 80 if (!js1->is_join && js2->is_join)
81 return 1;
d0db90bf 82
d62a17ae 83 if ((uint32_t)js1->up->sg.src.s_addr < (uint32_t)js2->up->sg.src.s_addr)
84 return -1;
982bff89 85
d62a17ae 86 if ((uint32_t)js1->up->sg.src.s_addr > (uint32_t)js2->up->sg.src.s_addr)
87 return 1;
982bff89 88
d62a17ae 89 return 0;
982bff89
DS
90}
91
fc9d070d
DS
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 */
d62a17ae 98void pim_jp_agg_clear_group(struct list *group)
982bff89 99{
d62a17ae 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 }
6a154c88 111 list_delete(&jag->sources);
d62a17ae 112 listnode_delete(group, jag);
113 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
114 }
982bff89
DS
115}
116
117static struct pim_iface_upstream_switch *
d62a17ae 118pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
982bff89 119{
db431af2
AK
120 struct interface *ifp = rpf->source_nexthop.interface;
121 struct pim_interface *pim_ifp;
d62a17ae 122 struct pim_iface_upstream_switch *pius;
123 struct listnode *node, *nnode;
124
db431af2
AK
125 if (!ifp)
126 return NULL;
127
128 pim_ifp = ifp->info;
129
d62a17ae 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;
982bff89
DS
149}
150
c5cdf069
AK
151void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
152 struct pim_neighbor *nbr)
982bff89 153{
d62a17ae 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 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 if (js->up == up)
168 break;
169 }
170
c5cdf069
AK
171 if (nbr) {
172 if (PIM_DEBUG_TRACE) {
173 char src_str[INET_ADDRSTRLEN];
174
175 pim_inet4_dump("<src?>", nbr->source_addr, src_str,
176 sizeof(src_str));
177 zlog_debug(
178 "up %s remove from nbr %s/%s jp-agg-list",
179 up->sg_str,
180 nbr->interface->name,
181 src_str);
182 }
183 }
184
d62a17ae 185 if (js) {
186 js->up = NULL;
187 listnode_delete(jag->sources, js);
188 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
189 }
190
191 if (jag->sources->count == 0) {
6a154c88 192 list_delete(&jag->sources);
d62a17ae 193 listnode_delete(group, jag);
194 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
195 }
982bff89
DS
196}
197
d62a17ae 198int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up)
06e12762 199{
d62a17ae 200 struct listnode *node, *nnode;
201 struct pim_jp_agg_group *jag = NULL;
202 struct pim_jp_sources *js = NULL;
06e12762 203
d62a17ae 204 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
205 if (jag->group.s_addr == up->sg.grp.s_addr)
206 break;
207 }
06e12762 208
d62a17ae 209 if (!jag)
210 return 0;
06e12762 211
d62a17ae 212 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
213 if (js->up == up)
214 return 1;
215 }
06e12762 216
d62a17ae 217 return 0;
218}
06e12762
DS
219
220//#define PIM_JP_AGG_DEBUG 1
221/*
222 * For the given upstream, check all the neighbor
223 * jp_agg lists and ensure that it is not
224 * in another list
225 *
226 * *IF* ignore is true we can skip
227 * up->rpf.source_nexthop.interface particular interface for checking
228 *
229 * This is a debugging function, Probably
230 * can be safely compiled out in real
231 * builds
232 */
d62a17ae 233void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
06e12762
DS
234{
235#ifdef PIM_JP_AGG_DEBUG
d62a17ae 236 struct interface *ifp;
957d93ea
SP
237 struct pim_interface *pim_ifp;
238 struct pim_instance *pim;
239
240 if (!up->rpf.source_nexthop.interface) {
23fc858a 241 if (PIM_DEBUG_PIM_TRACE)
957d93ea
SP
242 zlog_debug("%s: up %s RPF is not present",
243 __PRETTY_FUNCTION__, up->sg_str);
244 return;
245 }
246
247 pim_ifp = up->rpf.source_nexthop.interface->info;
248 pim = pim_ifp->pim;
d62a17ae 249
451fda4f 250 FOR_ALL_INTERFACES (pim->vrf, ifp) {
b2365558 251 pim_ifp = ifp->info;
d62a17ae 252 struct listnode *nnode;
253
254 if (ignore && ifp == up->rpf.source_nexthop.interface)
255 continue;
256
257 if (pim_ifp) {
258 struct pim_neighbor *neigh;
259 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
260 nnode, neigh)) {
261 assert(!pim_jp_agg_is_in_list(
262 neigh->upstream_jp_agg, up));
263 }
264 }
265 }
06e12762 266#else
d62a17ae 267 return;
06e12762
DS
268#endif
269}
270
d62a17ae 271void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
c5cdf069 272 bool is_join, struct pim_neighbor *nbr)
982bff89 273{
d62a17ae 274 struct listnode *node, *nnode;
275 struct pim_jp_agg_group *jag = NULL;
276 struct pim_jp_sources *js = NULL;
277
278 for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
279 if (jag->group.s_addr == up->sg.grp.s_addr)
280 break;
281 }
282
283 if (!jag) {
284 jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
285 sizeof(struct pim_jp_agg_group));
286 jag->group.s_addr = up->sg.grp.s_addr;
287 jag->sources = list_new();
288 jag->sources->cmp = pim_jp_agg_src_cmp;
289 jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
290 listnode_add_sort(group, jag);
291 }
292
293 for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
294 if (js->up == up)
295 break;
296 }
297
c5cdf069
AK
298 if (nbr) {
299 if (PIM_DEBUG_TRACE) {
300 char src_str[INET_ADDRSTRLEN];
301
302 pim_inet4_dump("<src?>", nbr->source_addr, src_str,
303 sizeof(src_str));
304 zlog_debug(
305 "up %s add to nbr %s/%s jp-agg-list",
306 up->sg_str,
307 up->rpf.source_nexthop.interface->name,
308 src_str);
309 }
310 }
311
d62a17ae 312 if (!js) {
313 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
314 sizeof(struct pim_jp_sources));
315 js->up = up;
316 js->is_join = is_join;
317 listnode_add_sort(jag->sources, js);
318 } else {
319 if (js->is_join != is_join) {
320 listnode_delete(jag->sources, js);
321 js->is_join = is_join;
322 listnode_add_sort(jag->sources, js);
323 }
324 }
982bff89
DS
325}
326
d62a17ae 327void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
328 struct pim_upstream *up)
982bff89 329{
d62a17ae 330 struct pim_iface_upstream_switch *opius;
331 struct pim_iface_upstream_switch *npius;
332
333 opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
334 npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
335
336 /*
337 * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
338 *
339 * Transitions from Joined State
340 *
341 * RPF'(S,G) changes not due to an Assert
342 *
343 * The upstream (S,G) state machine remains in Joined
344 * state. Send Join(S,G) to the new upstream neighbor, which is
345 * the new value of RPF'(S,G). Send Prune(S,G) to the old
346 * upstream neighbor, which is the old value of RPF'(S,G). Set
347 * the Join Timer (JT) to expire after t_periodic seconds.
348 */
349
350 /* send Prune(S,G) to the old upstream neighbor */
351 if (opius)
c5cdf069 352 pim_jp_agg_add_group(opius->us, up, false, NULL);
d62a17ae 353
354 /* send Join(S,G) to the current upstream neighbor */
b36576e4 355 if (npius)
c5cdf069 356 pim_jp_agg_add_group(npius->us, up, true, NULL);
982bff89
DS
357}
358
359
d62a17ae 360void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
361 struct pim_upstream *up, bool is_join)
982bff89 362{
d62a17ae 363 static struct list *groups = NULL;
364 static struct pim_jp_agg_group jag;
365 static struct pim_jp_sources js;
982bff89 366
d62a17ae 367 static bool first = true;
982bff89 368
d62a17ae 369 /* skip JP upstream messages if source is directly connected */
c692bd2a
AK
370 if (!up || !rpf->source_nexthop.interface ||
371 pim_if_connected_to_source(rpf->source_nexthop.interface,
372 up->sg.src) ||
373 if_is_loopback_or_vrf(rpf->source_nexthop.interface))
d62a17ae 374 return;
e0e127b0 375
d62a17ae 376 if (first) {
377 groups = list_new();
d62a17ae 378 jag.sources = list_new();
982bff89 379
d62a17ae 380 listnode_add(groups, &jag);
381 listnode_add(jag.sources, &js);
982bff89 382
d62a17ae 383 first = false;
384 }
982bff89 385
d62a17ae 386 jag.group.s_addr = up->sg.grp.s_addr;
387 js.up = up;
388 js.is_join = is_join;
982bff89 389
d62a17ae 390 pim_joinprune_send(rpf, groups);
982bff89 391}