Line data Source code
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_instance.h"
29 : #include "pim_msg.h"
30 : #include "pim_jp_agg.h"
31 : #include "pim_join.h"
32 : #include "pim_iface.h"
33 :
34 1 : void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag)
35 : {
36 1 : list_delete(&jag->sources);
37 :
38 1 : XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
39 1 : }
40 :
41 1 : static void pim_jp_agg_src_free(struct pim_jp_sources *js)
42 : {
43 1 : 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 1 : if (up)
53 1 : join_timer_start(js->up);
54 1 : XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
55 1 : }
56 :
57 0 : int pim_jp_agg_group_list_cmp(void *arg1, void *arg2)
58 : {
59 0 : const struct pim_jp_agg_group *jag1 =
60 : (const struct pim_jp_agg_group *)arg1;
61 0 : const struct pim_jp_agg_group *jag2 =
62 : (const struct pim_jp_agg_group *)arg2;
63 :
64 0 : return pim_addr_cmp(jag1->group, jag2->group);
65 : }
66 :
67 0 : static int pim_jp_agg_src_cmp(void *arg1, void *arg2)
68 : {
69 0 : const struct pim_jp_sources *js1 = (const struct pim_jp_sources *)arg1;
70 0 : const struct pim_jp_sources *js2 = (const struct pim_jp_sources *)arg2;
71 :
72 0 : if (js1->is_join && !js2->is_join)
73 : return -1;
74 :
75 0 : if (!js1->is_join && js2->is_join)
76 : return 1;
77 :
78 0 : return pim_addr_cmp(js1->up->sg.src, js2->up->sg.src);
79 : }
80 :
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 : */
87 0 : void pim_jp_agg_clear_group(struct list *group)
88 : {
89 0 : struct listnode *gnode, *gnnode;
90 0 : struct listnode *snode, *snnode;
91 0 : struct pim_jp_agg_group *jag;
92 0 : struct pim_jp_sources *js;
93 :
94 0 : for (ALL_LIST_ELEMENTS(group, gnode, gnnode, jag)) {
95 0 : for (ALL_LIST_ELEMENTS(jag->sources, snode, snnode, js)) {
96 0 : listnode_delete(jag->sources, js);
97 0 : js->up = NULL;
98 0 : XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
99 : }
100 0 : list_delete(&jag->sources);
101 0 : listnode_delete(group, jag);
102 0 : XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
103 : }
104 0 : }
105 :
106 : static struct pim_iface_upstream_switch *
107 0 : pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
108 : {
109 0 : struct interface *ifp = rpf->source_nexthop.interface;
110 0 : struct pim_interface *pim_ifp;
111 0 : struct pim_iface_upstream_switch *pius;
112 0 : struct listnode *node, *nnode;
113 :
114 0 : if (!ifp)
115 : return NULL;
116 :
117 0 : pim_ifp = ifp->info;
118 :
119 : /* Old interface is pim disabled */
120 0 : if (!pim_ifp)
121 : return NULL;
122 :
123 0 : for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode,
124 : pius)) {
125 0 : if (!pim_addr_cmp(pius->address, rpf->rpf_addr))
126 : break;
127 : }
128 :
129 0 : if (!pius) {
130 0 : pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
131 : sizeof(struct pim_iface_upstream_switch));
132 0 : pius->address = rpf->rpf_addr;
133 0 : pius->us = list_new();
134 0 : listnode_add_sort(pim_ifp->upstream_switch_list, pius);
135 : }
136 :
137 : return pius;
138 : }
139 :
140 0 : void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
141 : struct pim_neighbor *nbr)
142 : {
143 0 : struct listnode *node, *nnode;
144 0 : struct pim_jp_agg_group *jag = NULL;
145 0 : struct pim_jp_sources *js = NULL;
146 :
147 0 : for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
148 0 : if (!pim_addr_cmp(jag->group, up->sg.grp))
149 : break;
150 : }
151 :
152 0 : if (!jag)
153 : return;
154 :
155 0 : for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
156 0 : if (js->up == up)
157 : break;
158 : }
159 :
160 0 : if (nbr) {
161 0 : if (PIM_DEBUG_TRACE)
162 0 : zlog_debug("up %s remove from nbr %s/%pPAs jp-agg-list",
163 : up->sg_str, nbr->interface->name,
164 : &nbr->source_addr);
165 : }
166 :
167 0 : if (js) {
168 0 : js->up = NULL;
169 0 : listnode_delete(jag->sources, js);
170 0 : XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
171 : }
172 :
173 0 : if (jag->sources->count == 0) {
174 0 : list_delete(&jag->sources);
175 0 : listnode_delete(group, jag);
176 0 : XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
177 : }
178 : }
179 :
180 0 : int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up)
181 : {
182 0 : struct listnode *node, *nnode;
183 0 : struct pim_jp_agg_group *jag = NULL;
184 0 : struct pim_jp_sources *js = NULL;
185 :
186 0 : for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
187 0 : if (!pim_addr_cmp(jag->group, up->sg.grp))
188 : break;
189 : }
190 :
191 0 : if (!jag)
192 : return 0;
193 :
194 0 : for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
195 0 : if (js->up == up)
196 : return 1;
197 : }
198 :
199 : return 0;
200 : }
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 : */
215 6 : void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
216 : {
217 : #ifdef PIM_JP_AGG_DEBUG
218 : struct interface *ifp;
219 : struct pim_interface *pim_ifp;
220 : struct pim_instance *pim;
221 :
222 : if (!up->rpf.source_nexthop.interface) {
223 : if (PIM_DEBUG_PIM_TRACE)
224 : zlog_debug("%s: up %s RPF is not present", __func__,
225 : up->sg_str);
226 : return;
227 : }
228 :
229 : pim_ifp = up->rpf.source_nexthop.interface->info;
230 : pim = pim_ifp->pim;
231 :
232 : FOR_ALL_INTERFACES (pim->vrf, ifp) {
233 : 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 : 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 : }
248 : #else
249 6 : return;
250 : #endif
251 : }
252 :
253 1 : void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
254 : bool is_join, struct pim_neighbor *nbr)
255 : {
256 1 : struct listnode *node, *nnode;
257 1 : struct pim_jp_agg_group *jag = NULL;
258 1 : struct pim_jp_sources *js = NULL;
259 :
260 2 : for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
261 0 : if (!pim_addr_cmp(jag->group, up->sg.grp))
262 : break;
263 : }
264 :
265 1 : if (!jag) {
266 1 : jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
267 : sizeof(struct pim_jp_agg_group));
268 1 : jag->group = up->sg.grp;
269 1 : jag->sources = list_new();
270 1 : jag->sources->cmp = pim_jp_agg_src_cmp;
271 1 : jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
272 1 : listnode_add_sort(group, jag);
273 : }
274 :
275 2 : for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
276 0 : if (js->up == up)
277 : break;
278 : }
279 :
280 1 : if (nbr) {
281 1 : if (PIM_DEBUG_TRACE)
282 1 : 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);
286 : }
287 :
288 1 : if (!js) {
289 1 : js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
290 : sizeof(struct pim_jp_sources));
291 1 : js->up = up;
292 1 : js->is_join = is_join;
293 1 : listnode_add_sort(jag->sources, js);
294 : } else {
295 0 : if (js->is_join != is_join) {
296 0 : listnode_delete(jag->sources, js);
297 0 : js->is_join = is_join;
298 0 : listnode_add_sort(jag->sources, js);
299 : }
300 : }
301 1 : }
302 :
303 0 : void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
304 : struct pim_upstream *up)
305 : {
306 0 : struct pim_iface_upstream_switch *opius;
307 0 : struct pim_iface_upstream_switch *npius;
308 :
309 0 : opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
310 0 : 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 0 : if (opius)
328 0 : pim_jp_agg_add_group(opius->us, up, false, NULL);
329 :
330 : /* send Join(S,G) to the current upstream neighbor */
331 0 : if (npius)
332 0 : pim_jp_agg_add_group(npius->us, up, true, NULL);
333 0 : }
334 :
335 :
336 3 : void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
337 : struct pim_upstream *up, bool is_join)
338 : {
339 3 : struct list groups, sources;
340 3 : struct pim_jp_agg_group jag;
341 3 : struct pim_jp_sources js;
342 :
343 : /* skip JP upstream messages if source is directly connected */
344 6 : if (!up || !rpf->source_nexthop.interface ||
345 3 : pim_if_connected_to_source(rpf->source_nexthop.interface,
346 2 : up->sg.src) ||
347 2 : if_is_loopback(rpf->source_nexthop.interface))
348 1 : return;
349 :
350 2 : memset(&groups, 0, sizeof(groups));
351 2 : memset(&sources, 0, sizeof(sources));
352 2 : jag.sources = &sources;
353 :
354 2 : listnode_add(&groups, &jag);
355 2 : listnode_add(jag.sources, &js);
356 :
357 2 : jag.group = up->sg.grp;
358 2 : js.up = up;
359 2 : js.is_join = is_join;
360 :
361 2 : pim_joinprune_send(rpf, &groups);
362 :
363 2 : list_delete_all_node(jag.sources);
364 2 : list_delete_all_node(&groups);
365 : }
|