]>
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 | { |
affe9e99 | 35 | list_delete_and_null(&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 | } | |
affe9e99 | 111 | list_delete_and_null(&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 | { |
d62a17ae | 120 | struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info; |
121 | struct pim_iface_upstream_switch *pius; | |
122 | struct listnode *node, *nnode; | |
123 | ||
124 | /* Old interface is pim disabled */ | |
125 | if (!pim_ifp) | |
126 | return NULL; | |
127 | ||
128 | for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode, | |
129 | pius)) { | |
130 | if (pius->address.s_addr == rpf->rpf_addr.u.prefix4.s_addr) | |
131 | break; | |
132 | } | |
133 | ||
134 | if (!pius) { | |
135 | pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, | |
136 | sizeof(struct pim_iface_upstream_switch)); | |
137 | pius->address.s_addr = rpf->rpf_addr.u.prefix4.s_addr; | |
138 | pius->us = list_new(); | |
139 | listnode_add_sort(pim_ifp->upstream_switch_list, pius); | |
140 | } | |
141 | ||
142 | return pius; | |
982bff89 DS |
143 | } |
144 | ||
d62a17ae | 145 | void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) |
982bff89 | 146 | { |
d62a17ae | 147 | struct listnode *node, *nnode; |
148 | struct pim_jp_agg_group *jag = NULL; | |
149 | struct pim_jp_sources *js = NULL; | |
150 | ||
151 | for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) { | |
152 | if (jag->group.s_addr == up->sg.grp.s_addr) | |
153 | break; | |
154 | } | |
155 | ||
156 | if (!jag) | |
157 | return; | |
158 | ||
159 | for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) { | |
160 | if (js->up == up) | |
161 | break; | |
162 | } | |
163 | ||
164 | if (js) { | |
165 | js->up = NULL; | |
166 | listnode_delete(jag->sources, js); | |
167 | XFREE(MTYPE_PIM_JP_AGG_SOURCE, js); | |
168 | } | |
169 | ||
170 | if (jag->sources->count == 0) { | |
affe9e99 | 171 | list_delete_and_null(&jag->sources); |
d62a17ae | 172 | listnode_delete(group, jag); |
173 | XFREE(MTYPE_PIM_JP_AGG_GROUP, jag); | |
174 | } | |
982bff89 DS |
175 | } |
176 | ||
d62a17ae | 177 | int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up) |
06e12762 | 178 | { |
d62a17ae | 179 | struct listnode *node, *nnode; |
180 | struct pim_jp_agg_group *jag = NULL; | |
181 | struct pim_jp_sources *js = NULL; | |
06e12762 | 182 | |
d62a17ae | 183 | for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) { |
184 | if (jag->group.s_addr == up->sg.grp.s_addr) | |
185 | break; | |
186 | } | |
06e12762 | 187 | |
d62a17ae | 188 | if (!jag) |
189 | return 0; | |
06e12762 | 190 | |
d62a17ae | 191 | for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) { |
192 | if (js->up == up) | |
193 | return 1; | |
194 | } | |
06e12762 | 195 | |
d62a17ae | 196 | return 0; |
197 | } | |
06e12762 DS |
198 | |
199 | //#define PIM_JP_AGG_DEBUG 1 | |
200 | /* | |
201 | * For the given upstream, check all the neighbor | |
202 | * jp_agg lists and ensure that it is not | |
203 | * in another list | |
204 | * | |
205 | * *IF* ignore is true we can skip | |
206 | * up->rpf.source_nexthop.interface particular interface for checking | |
207 | * | |
208 | * This is a debugging function, Probably | |
209 | * can be safely compiled out in real | |
210 | * builds | |
211 | */ | |
d62a17ae | 212 | void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) |
06e12762 DS |
213 | { |
214 | #ifdef PIM_JP_AGG_DEBUG | |
d62a17ae | 215 | struct interface *ifp; |
b2365558 DS |
216 | struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info; |
217 | struct pim_instance *pim = pim_ifp->pim; | |
d62a17ae | 218 | |
451fda4f | 219 | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
b2365558 | 220 | pim_ifp = ifp->info; |
d62a17ae | 221 | struct listnode *nnode; |
222 | ||
223 | if (ignore && ifp == up->rpf.source_nexthop.interface) | |
224 | continue; | |
225 | ||
226 | if (pim_ifp) { | |
227 | struct pim_neighbor *neigh; | |
228 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, | |
229 | nnode, neigh)) { | |
230 | assert(!pim_jp_agg_is_in_list( | |
231 | neigh->upstream_jp_agg, up)); | |
232 | } | |
233 | } | |
234 | } | |
06e12762 | 235 | #else |
d62a17ae | 236 | return; |
06e12762 DS |
237 | #endif |
238 | } | |
239 | ||
d62a17ae | 240 | void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, |
241 | bool is_join) | |
982bff89 | 242 | { |
d62a17ae | 243 | struct listnode *node, *nnode; |
244 | struct pim_jp_agg_group *jag = NULL; | |
245 | struct pim_jp_sources *js = NULL; | |
246 | ||
247 | for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) { | |
248 | if (jag->group.s_addr == up->sg.grp.s_addr) | |
249 | break; | |
250 | } | |
251 | ||
252 | if (!jag) { | |
253 | jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, | |
254 | sizeof(struct pim_jp_agg_group)); | |
255 | jag->group.s_addr = up->sg.grp.s_addr; | |
256 | jag->sources = list_new(); | |
257 | jag->sources->cmp = pim_jp_agg_src_cmp; | |
258 | jag->sources->del = (void (*)(void *))pim_jp_agg_src_free; | |
259 | listnode_add_sort(group, jag); | |
260 | } | |
261 | ||
262 | for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) { | |
263 | if (js->up == up) | |
264 | break; | |
265 | } | |
266 | ||
267 | if (!js) { | |
268 | js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, | |
269 | sizeof(struct pim_jp_sources)); | |
270 | js->up = up; | |
271 | js->is_join = is_join; | |
272 | listnode_add_sort(jag->sources, js); | |
273 | } else { | |
274 | if (js->is_join != is_join) { | |
275 | listnode_delete(jag->sources, js); | |
276 | js->is_join = is_join; | |
277 | listnode_add_sort(jag->sources, js); | |
278 | } | |
279 | } | |
982bff89 DS |
280 | } |
281 | ||
d62a17ae | 282 | void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, |
283 | struct pim_upstream *up) | |
982bff89 | 284 | { |
d62a17ae | 285 | struct pim_iface_upstream_switch *opius; |
286 | struct pim_iface_upstream_switch *npius; | |
287 | ||
288 | opius = pim_jp_agg_get_interface_upstream_switch_list(orpf); | |
289 | npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf); | |
290 | ||
291 | /* | |
292 | * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages | |
293 | * | |
294 | * Transitions from Joined State | |
295 | * | |
296 | * RPF'(S,G) changes not due to an Assert | |
297 | * | |
298 | * The upstream (S,G) state machine remains in Joined | |
299 | * state. Send Join(S,G) to the new upstream neighbor, which is | |
300 | * the new value of RPF'(S,G). Send Prune(S,G) to the old | |
301 | * upstream neighbor, which is the old value of RPF'(S,G). Set | |
302 | * the Join Timer (JT) to expire after t_periodic seconds. | |
303 | */ | |
304 | ||
305 | /* send Prune(S,G) to the old upstream neighbor */ | |
306 | if (opius) | |
307 | pim_jp_agg_add_group(opius->us, up, false); | |
308 | ||
309 | /* send Join(S,G) to the current upstream neighbor */ | |
310 | pim_jp_agg_add_group(npius->us, up, true); | |
982bff89 DS |
311 | } |
312 | ||
313 | ||
d62a17ae | 314 | void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf, |
315 | struct pim_upstream *up, bool is_join) | |
982bff89 | 316 | { |
d62a17ae | 317 | static struct list *groups = NULL; |
318 | static struct pim_jp_agg_group jag; | |
319 | static struct pim_jp_sources js; | |
982bff89 | 320 | |
d62a17ae | 321 | static bool first = true; |
982bff89 | 322 | |
d62a17ae | 323 | /* skip JP upstream messages if source is directly connected */ |
324 | if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source( | |
325 | rpf->source_nexthop | |
326 | .interface, | |
327 | up->sg.src)) | |
328 | return; | |
e0e127b0 | 329 | |
d62a17ae | 330 | if (first) { |
331 | groups = list_new(); | |
982bff89 | 332 | |
d62a17ae | 333 | jag.sources = list_new(); |
982bff89 | 334 | |
d62a17ae | 335 | listnode_add(groups, &jag); |
336 | listnode_add(jag.sources, &js); | |
982bff89 | 337 | |
d62a17ae | 338 | first = false; |
339 | } | |
982bff89 | 340 | |
d62a17ae | 341 | jag.group.s_addr = up->sg.grp.s_addr; |
342 | js.up = up; | |
343 | js.is_join = is_join; | |
982bff89 | 344 | |
d62a17ae | 345 | pim_joinprune_send(rpf, groups); |
982bff89 | 346 | } |