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