]>
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" | |
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 | 34 | void 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 | 41 | static 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 | 57 | int 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 | 67 | static 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 | 87 | void 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 | ||
106 | static struct pim_iface_upstream_switch * | |
d62a17ae | 107 | pim_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 |
140 | void 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 | 180 | int 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 | 215 | void 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 | 253 | void 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 | 303 | void 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 | 336 | void 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 | } |