]>
Commit | Line | Data |
---|---|---|
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 | 33 | void 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 | 40 | static 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 | 56 | int 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 | 72 | static 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 | 98 | void 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 | ||
117 | static struct pim_iface_upstream_switch * | |
d62a17ae | 118 | pim_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 |
151 | void 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 | 198 | int 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 | 233 | void 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 | 271 | void 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 | 327 | void 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 | 360 | void 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 | } |