]>
Commit | Line | Data |
---|---|---|
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_msg.h" | |
29 | #include "pim_jp_agg.h" | |
30 | #include "pim_join.h" | |
31 | #include "pim_iface.h" | |
32 | ||
33 | void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag) | |
34 | { | |
35 | list_delete(&jag->sources); | |
36 | ||
37 | XFREE(MTYPE_PIM_JP_AGG_GROUP, jag); | |
38 | } | |
39 | ||
40 | static void pim_jp_agg_src_free(struct pim_jp_sources *js) | |
41 | { | |
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); | |
54 | } | |
55 | ||
56 | int pim_jp_agg_group_list_cmp(void *arg1, void *arg2) | |
57 | { | |
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; | |
62 | ||
63 | if (jag1->group.s_addr < jag2->group.s_addr) | |
64 | return -1; | |
65 | ||
66 | if (jag1->group.s_addr > jag2->group.s_addr) | |
67 | return 1; | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
72 | static int pim_jp_agg_src_cmp(void *arg1, void *arg2) | |
73 | { | |
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; | |
76 | ||
77 | if (js1->is_join && !js2->is_join) | |
78 | return -1; | |
79 | ||
80 | if (!js1->is_join && js2->is_join) | |
81 | return 1; | |
82 | ||
83 | if ((uint32_t)js1->up->sg.src.s_addr < (uint32_t)js2->up->sg.src.s_addr) | |
84 | return -1; | |
85 | ||
86 | if ((uint32_t)js1->up->sg.src.s_addr > (uint32_t)js2->up->sg.src.s_addr) | |
87 | return 1; | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
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 | */ | |
98 | void pim_jp_agg_clear_group(struct list *group) | |
99 | { | |
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 | } | |
111 | list_delete(&jag->sources); | |
112 | listnode_delete(group, jag); | |
113 | XFREE(MTYPE_PIM_JP_AGG_GROUP, jag); | |
114 | } | |
115 | } | |
116 | ||
117 | static struct pim_iface_upstream_switch * | |
118 | pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) | |
119 | { | |
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; | |
143 | } | |
144 | ||
145 | void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) | |
146 | { | |
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) { | |
171 | list_delete(&jag->sources); | |
172 | listnode_delete(group, jag); | |
173 | XFREE(MTYPE_PIM_JP_AGG_GROUP, jag); | |
174 | } | |
175 | } | |
176 | ||
177 | int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up) | |
178 | { | |
179 | struct listnode *node, *nnode; | |
180 | struct pim_jp_agg_group *jag = NULL; | |
181 | struct pim_jp_sources *js = NULL; | |
182 | ||
183 | for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) { | |
184 | if (jag->group.s_addr == up->sg.grp.s_addr) | |
185 | break; | |
186 | } | |
187 | ||
188 | if (!jag) | |
189 | return 0; | |
190 | ||
191 | for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) { | |
192 | if (js->up == up) | |
193 | return 1; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
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 | */ | |
212 | void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) | |
213 | { | |
214 | #ifdef PIM_JP_AGG_DEBUG | |
215 | struct interface *ifp; | |
216 | struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info; | |
217 | struct pim_instance *pim = pim_ifp->pim; | |
218 | ||
219 | FOR_ALL_INTERFACES (pim->vrf, ifp) { | |
220 | pim_ifp = ifp->info; | |
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 | } | |
235 | #else | |
236 | return; | |
237 | #endif | |
238 | } | |
239 | ||
240 | void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, | |
241 | bool is_join) | |
242 | { | |
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 | } | |
280 | } | |
281 | ||
282 | void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, | |
283 | struct pim_upstream *up) | |
284 | { | |
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); | |
311 | } | |
312 | ||
313 | ||
314 | void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf, | |
315 | struct pim_upstream *up, bool is_join) | |
316 | { | |
317 | static struct list *groups = NULL; | |
318 | static struct pim_jp_agg_group jag; | |
319 | static struct pim_jp_sources js; | |
320 | ||
321 | static bool first = true; | |
322 | ||
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; | |
329 | ||
330 | if (first) { | |
331 | groups = list_new(); | |
332 | jag.sources = list_new(); | |
333 | ||
334 | listnode_add(groups, &jag); | |
335 | listnode_add(jag.sources, &js); | |
336 | ||
337 | first = false; | |
338 | } | |
339 | ||
340 | jag.group.s_addr = up->sg.grp.s_addr; | |
341 | js.up = up; | |
342 | js.is_join = is_join; | |
343 | ||
344 | pim_joinprune_send(rpf, groups); | |
345 | } |