]>
Commit | Line | Data |
---|---|---|
8f5f5e91 DS |
1 | /* |
2 | * PIM for Quagga | |
3 | * Copyright (C) 2015 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 | |
17 | * along with this program; see the file COPYING; if not, write to the | |
18 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
19 | * MA 02110-1301 USA | |
20 | */ | |
21 | #include <zebra.h> | |
22 | ||
9bf3c633 | 23 | #include "lib/json.h" |
54b97c74 | 24 | #include "log.h" |
8f5f5e91 | 25 | #include "network.h" |
744d91b3 | 26 | #include "if.h" |
36d6bd7d DS |
27 | #include "linklist.h" |
28 | #include "prefix.h" | |
29 | #include "memory.h" | |
00d07c6f DS |
30 | #include "vty.h" |
31 | #include "vrf.h" | |
dfe43e25 | 32 | #include "plist.h" |
8f5f5e91 DS |
33 | |
34 | #include "pimd.h" | |
75a26779 | 35 | #include "pim_vty.h" |
54b97c74 | 36 | #include "pim_str.h" |
7176984f | 37 | #include "pim_iface.h" |
8f5f5e91 | 38 | #include "pim_rp.h" |
ed66602c DS |
39 | #include "pim_str.h" |
40 | #include "pim_rpf.h" | |
13afbd05 | 41 | #include "pim_sock.h" |
36d6bd7d | 42 | #include "pim_memory.h" |
00d07c6f | 43 | #include "pim_iface.h" |
7667c556 | 44 | #include "pim_msdp.h" |
1bc98276 | 45 | #include "pim_nht.h" |
8f5f5e91 | 46 | |
36d6bd7d DS |
47 | |
48 | static struct list *qpim_rp_list = NULL; | |
49 | static struct rp_info *tail = NULL; | |
50 | ||
51 | static void | |
52 | pim_rp_info_free (struct rp_info *rp_info) | |
53 | { | |
54 | XFREE (MTYPE_PIM_RP, rp_info); | |
55 | } | |
56 | ||
ff3745c2 | 57 | int |
36d6bd7d DS |
58 | pim_rp_list_cmp (void *v1, void *v2) |
59 | { | |
60 | struct rp_info *rp1 = (struct rp_info *)v1; | |
61 | struct rp_info *rp2 = (struct rp_info *)v2; | |
62 | ||
dfe43e25 DW |
63 | /* |
64 | * Sort by RP IP address | |
65 | */ | |
66 | if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr) | |
67 | return -1; | |
68 | ||
69 | if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr) | |
70 | return 1; | |
71 | ||
72 | /* | |
73 | * Sort by group IP address | |
74 | */ | |
75 | if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr) | |
76 | return -1; | |
77 | ||
78 | if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr) | |
79 | return 1; | |
80 | ||
ff3745c2 | 81 | return 0; |
36d6bd7d DS |
82 | } |
83 | ||
84 | void | |
85 | pim_rp_init (void) | |
86 | { | |
87 | struct rp_info *rp_info; | |
88 | ||
89 | qpim_rp_list = list_new (); | |
90 | qpim_rp_list->del = (void (*)(void *))pim_rp_info_free; | |
91 | qpim_rp_list->cmp = pim_rp_list_cmp; | |
92 | ||
93 | rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); | |
94 | ||
95 | if (!rp_info) | |
96 | return; | |
97 | ||
98 | str2prefix ("224.0.0.0/4", &rp_info->group); | |
fa8da98c | 99 | rp_info->group.family = AF_INET; |
63c59d0c DS |
100 | rp_info->rp.rpf_addr.family = AF_INET; |
101 | rp_info->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; | |
36d6bd7d DS |
102 | tail = rp_info; |
103 | ||
104 | listnode_add (qpim_rp_list, rp_info); | |
105 | } | |
106 | ||
107 | void | |
108 | pim_rp_free (void) | |
109 | { | |
110 | if (qpim_rp_list) | |
0b6817c5 DS |
111 | list_delete (qpim_rp_list); |
112 | qpim_rp_list = NULL; | |
36d6bd7d DS |
113 | } |
114 | ||
dfe43e25 DW |
115 | /* |
116 | * Given an RP's prefix-list, return the RP's rp_info for that prefix-list | |
117 | */ | |
36d6bd7d | 118 | static struct rp_info * |
dfe43e25 | 119 | pim_rp_find_prefix_list (struct in_addr rp, const char *plist) |
36d6bd7d DS |
120 | { |
121 | struct listnode *node; | |
122 | struct rp_info *rp_info; | |
123 | ||
124 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
125 | { | |
63c59d0c | 126 | if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && |
dfe43e25 DW |
127 | rp_info->plist && strcmp(rp_info->plist, plist) == 0) |
128 | { | |
129 | return rp_info; | |
130 | } | |
36d6bd7d DS |
131 | } |
132 | ||
133 | return NULL; | |
134 | } | |
135 | ||
dfe43e25 DW |
136 | /* |
137 | * Return true if plist is used by any rp_info | |
138 | */ | |
139 | static int | |
140 | pim_rp_prefix_list_used (const char *plist) | |
141 | { | |
142 | struct listnode *node; | |
143 | struct rp_info *rp_info; | |
144 | ||
145 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
146 | { | |
147 | if (rp_info->plist && strcmp(rp_info->plist, plist) == 0) | |
148 | { | |
149 | return 1; | |
150 | } | |
151 | } | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | /* | |
157 | * Given an RP's address, return the RP's rp_info that is an exact match for 'group' | |
158 | */ | |
36d6bd7d | 159 | static struct rp_info * |
dfe43e25 | 160 | pim_rp_find_exact (struct in_addr rp, struct prefix *group) |
36d6bd7d DS |
161 | { |
162 | struct listnode *node; | |
163 | struct rp_info *rp_info; | |
164 | ||
165 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
166 | { | |
63c59d0c | 167 | if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && |
dfe43e25 | 168 | prefix_same (&rp_info->group, group)) |
36d6bd7d DS |
169 | return rp_info; |
170 | } | |
54b97c74 | 171 | |
36d6bd7d DS |
172 | return NULL; |
173 | } | |
174 | ||
dfe43e25 DW |
175 | /* |
176 | * Given a group, return the rp_info for that group | |
177 | */ | |
36d6bd7d DS |
178 | static struct rp_info * |
179 | pim_rp_find_match_group (struct prefix *group) | |
180 | { | |
181 | struct listnode *node; | |
182 | struct rp_info *rp_info; | |
dfe43e25 | 183 | struct prefix_list *plist; |
36d6bd7d DS |
184 | |
185 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
186 | { | |
dfe43e25 DW |
187 | if (rp_info->plist) |
188 | { | |
189 | plist = prefix_list_lookup (AFI_IP, rp_info->plist); | |
190 | ||
191 | if (plist && prefix_list_apply (plist, group) == PREFIX_PERMIT) | |
192 | return rp_info; | |
193 | } | |
194 | else | |
195 | { | |
196 | if (prefix_match (&rp_info->group, group)) | |
197 | return rp_info; | |
198 | } | |
36d6bd7d DS |
199 | } |
200 | ||
201 | return NULL; | |
202 | } | |
75a26779 | 203 | |
dfe43e25 DW |
204 | /* |
205 | * When the user makes "ip pim rp" configuration changes or if they change the | |
206 | * prefix-list(s) used by these statements we must tickle the upstream state | |
207 | * for each group to make them re-lookup who their RP should be. | |
208 | * | |
209 | * This is a placeholder function for now. | |
210 | */ | |
211 | static void | |
212 | pim_rp_refresh_group_to_rp_mapping() | |
213 | { | |
7667c556 | 214 | pim_msdp_i_am_rp_changed(); |
dfe43e25 DW |
215 | } |
216 | ||
217 | void | |
218 | pim_rp_prefix_list_update (struct prefix_list *plist) | |
219 | { | |
220 | struct listnode *node; | |
221 | struct rp_info *rp_info; | |
222 | int refresh_needed = 0; | |
223 | ||
224 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
225 | { | |
226 | if (rp_info->plist && strcmp(rp_info->plist, prefix_list_name (plist)) == 0) | |
227 | { | |
228 | refresh_needed = 1; | |
229 | break; | |
230 | } | |
231 | } | |
232 | ||
233 | if (refresh_needed) | |
234 | pim_rp_refresh_group_to_rp_mapping(); | |
235 | } | |
236 | ||
7176984f | 237 | static int |
238 | pim_rp_check_interface_addrs(struct rp_info *rp_info, | |
239 | struct pim_interface *pim_ifp) | |
240 | { | |
241 | struct listnode *node; | |
242 | struct pim_secondary_addr *sec_addr; | |
243 | ||
244 | if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) | |
245 | return 1; | |
246 | ||
247 | if (!pim_ifp->sec_addr_list) { | |
248 | return 0; | |
249 | } | |
250 | ||
251 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { | |
252 | if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) { | |
253 | return 1; | |
254 | } | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
00d07c6f DS |
260 | static void |
261 | pim_rp_check_interfaces (struct rp_info *rp_info) | |
262 | { | |
263 | struct listnode *node; | |
264 | struct interface *ifp; | |
265 | ||
7176984f | 266 | rp_info->i_am_rp = 0; |
00d07c6f DS |
267 | for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) |
268 | { | |
269 | struct pim_interface *pim_ifp = ifp->info; | |
270 | ||
271 | if (!pim_ifp) | |
272 | continue; | |
273 | ||
7176984f | 274 | if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { |
275 | rp_info->i_am_rp = 1; | |
276 | } | |
00d07c6f DS |
277 | } |
278 | } | |
279 | ||
75a26779 | 280 | int |
dfe43e25 | 281 | pim_rp_new (const char *rp, const char *group_range, const char *plist) |
75a26779 DS |
282 | { |
283 | int result; | |
36d6bd7d DS |
284 | struct rp_info *rp_info; |
285 | struct rp_info *rp_all; | |
286 | struct prefix group_all; | |
dfe43e25 DW |
287 | struct listnode *node, *nnode; |
288 | struct rp_info *tmp_rp_info; | |
289 | char buffer[BUFSIZ]; | |
1bc98276 | 290 | struct prefix nht_p; |
36d6bd7d DS |
291 | |
292 | rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); | |
dfe43e25 DW |
293 | if (!rp_info) |
294 | return PIM_MALLOC_FAIL; | |
36d6bd7d DS |
295 | |
296 | if (group_range == NULL) | |
297 | result = str2prefix ("224.0.0.0/4", &rp_info->group); | |
298 | else | |
299 | result = str2prefix (group_range, &rp_info->group); | |
300 | ||
301 | if (!result) | |
dfe43e25 DW |
302 | { |
303 | XFREE (MTYPE_PIM_RP, rp_info); | |
304 | return PIM_GROUP_BAD_ADDRESS; | |
305 | } | |
306 | ||
fa8da98c DS |
307 | rp_info->rp.rpf_addr.family = AF_INET; |
308 | result = inet_pton (rp_info->rp.rpf_addr.family, rp, &rp_info->rp.rpf_addr.u.prefix4); | |
75a26779 | 309 | |
dfe43e25 | 310 | if (result <= 0) |
36d6bd7d | 311 | { |
36d6bd7d | 312 | XFREE (MTYPE_PIM_RP, rp_info); |
dfe43e25 | 313 | return PIM_RP_BAD_ADDRESS; |
36d6bd7d DS |
314 | } |
315 | ||
dfe43e25 | 316 | if (plist) |
36d6bd7d | 317 | { |
dfe43e25 DW |
318 | /* |
319 | * Return if the prefix-list is already configured for this RP | |
320 | */ | |
321 | if (pim_rp_find_prefix_list (rp_info->rp.rpf_addr.u.prefix4, plist)) | |
322 | { | |
323 | XFREE (MTYPE_PIM_RP, rp_info); | |
324 | return PIM_SUCCESS; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Barf if the prefix-list is already configured for an RP | |
329 | */ | |
330 | if (pim_rp_prefix_list_used (plist)) | |
331 | { | |
332 | XFREE (MTYPE_PIM_RP, rp_info); | |
333 | return PIM_RP_PFXLIST_IN_USE; | |
334 | } | |
335 | ||
336 | /* | |
337 | * Free any existing rp_info entries for this RP | |
338 | */ | |
339 | for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) | |
340 | { | |
341 | if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) | |
342 | { | |
343 | if (tmp_rp_info->plist) | |
344 | pim_rp_del (rp, NULL, tmp_rp_info->plist); | |
345 | else | |
346 | pim_rp_del (rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL); | |
347 | } | |
348 | } | |
36d6bd7d | 349 | |
dfe43e25 DW |
350 | rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist); |
351 | } | |
352 | else | |
36d6bd7d | 353 | { |
dfe43e25 DW |
354 | str2prefix ("224.0.0.0/4", &group_all); |
355 | rp_all = pim_rp_find_match_group(&group_all); | |
356 | ||
357 | /* | |
358 | * Barf if group is a non-multicast subnet | |
359 | */ | |
360 | if (! prefix_match (&rp_all->group, &rp_info->group)) | |
36d6bd7d | 361 | { |
dfe43e25 DW |
362 | XFREE (MTYPE_PIM_RP, rp_info); |
363 | return PIM_GROUP_BAD_ADDRESS; | |
36d6bd7d DS |
364 | } |
365 | ||
dfe43e25 DW |
366 | /* |
367 | * Remove any prefix-list rp_info entries for this RP | |
368 | */ | |
369 | for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) | |
370 | { | |
371 | if (tmp_rp_info->plist && | |
372 | rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) | |
373 | { | |
374 | pim_rp_del (rp, NULL, tmp_rp_info->plist); | |
375 | } | |
376 | } | |
377 | ||
378 | /* | |
379 | * Take over the 224.0.0.0/4 group if the rp is INADDR_NONE | |
380 | */ | |
381 | if (prefix_same (&rp_all->group, &rp_info->group) && | |
382 | pim_rpf_addr_is_inaddr_none (&rp_all->rp)) | |
383 | { | |
384 | rp_all->rp.rpf_addr = rp_info->rp.rpf_addr; | |
385 | XFREE (MTYPE_PIM_RP, rp_info); | |
386 | ||
1bc98276 CS |
387 | /* Register addr with Zebra NHT */ |
388 | nht_p.family = AF_INET; | |
389 | nht_p.prefixlen = IPV4_MAX_BITLEN; | |
390 | nht_p.u.prefix4 = rp_all->rp.rpf_addr.u.prefix4; | |
391 | if (PIM_DEBUG_PIM_TRACE) | |
392 | { | |
393 | char buf[PREFIX2STR_BUFFER]; | |
394 | prefix2str (&nht_p, buf, sizeof (buf)); | |
395 | zlog_debug ("%s: NHT Register rp_all addr %s with NHT ", | |
396 | __PRETTY_FUNCTION__, buf); | |
397 | } | |
398 | pim_find_or_track_nexthop (&nht_p, NULL, rp_all); | |
399 | ||
a04da05a | 400 | if (pim_nexthop_lookup (&rp_all->rp.source_nexthop, rp_all->rp.rpf_addr.u.prefix4, 1) != 0) |
dfe43e25 DW |
401 | return PIM_RP_NO_PATH; |
402 | ||
403 | pim_rp_check_interfaces (rp_all); | |
44521e5d | 404 | pim_rp_refresh_group_to_rp_mapping(); |
dfe43e25 DW |
405 | return PIM_SUCCESS; |
406 | } | |
407 | ||
408 | /* | |
409 | * Return if the group is already configured for this RP | |
410 | */ | |
411 | if (pim_rp_find_exact (rp_info->rp.rpf_addr.u.prefix4, &rp_info->group)) | |
412 | { | |
413 | XFREE (MTYPE_PIM_RP, rp_info); | |
414 | return PIM_SUCCESS; | |
415 | } | |
416 | ||
417 | /* | |
418 | * Barf if this group is already covered by some other RP | |
419 | */ | |
420 | tmp_rp_info = pim_rp_find_match_group (&rp_info->group); | |
421 | ||
422 | if (tmp_rp_info) | |
423 | { | |
424 | if (tmp_rp_info->plist) | |
425 | { | |
426 | XFREE (MTYPE_PIM_RP, rp_info); | |
427 | return PIM_GROUP_PFXLIST_OVERLAP; | |
428 | } | |
429 | else | |
430 | { | |
431 | /* | |
432 | * If the only RP that covers this group is an RP configured for | |
433 | * 224.0.0.0/4 that is fine, ignore that one. For all others | |
434 | * though we must return PIM_GROUP_OVERLAP | |
435 | */ | |
436 | if (! prefix_same (&group_all, &tmp_rp_info->group)) | |
437 | { | |
438 | XFREE (MTYPE_PIM_RP, rp_info); | |
439 | return PIM_GROUP_OVERLAP; | |
440 | } | |
441 | } | |
442 | } | |
36d6bd7d DS |
443 | } |
444 | ||
445 | listnode_add_sort (qpim_rp_list, rp_info); | |
446 | ||
1bc98276 CS |
447 | /* Register addr with Zebra NHT */ |
448 | nht_p.family = AF_INET; | |
449 | nht_p.prefixlen = IPV4_MAX_BITLEN; | |
450 | nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; | |
451 | if (PIM_DEBUG_PIM_TRACE) | |
452 | { | |
453 | char buf[PREFIX2STR_BUFFER]; | |
454 | prefix2str (&nht_p, buf, sizeof (buf)); | |
455 | zlog_debug ("%s: NHT Register RP addr %s with Zebra ", __PRETTY_FUNCTION__, buf); | |
456 | } | |
457 | pim_find_or_track_nexthop (&nht_p, NULL, rp_info); | |
458 | ||
a04da05a | 459 | if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) |
dfe43e25 | 460 | return PIM_RP_NO_PATH; |
75a26779 | 461 | |
00d07c6f | 462 | pim_rp_check_interfaces (rp_info); |
1bc98276 | 463 | pim_rp_refresh_group_to_rp_mapping (); |
dfe43e25 | 464 | return PIM_SUCCESS; |
75a26779 DS |
465 | } |
466 | ||
467 | int | |
dfe43e25 | 468 | pim_rp_del (const char *rp, const char *group_range, const char *plist) |
75a26779 | 469 | { |
36d6bd7d DS |
470 | struct prefix group; |
471 | struct in_addr rp_addr; | |
472 | struct prefix g_all; | |
473 | struct rp_info *rp_info; | |
474 | struct rp_info *rp_all; | |
475 | int result; | |
1bc98276 | 476 | struct prefix nht_p; |
36d6bd7d | 477 | |
36d6bd7d DS |
478 | if (group_range == NULL) |
479 | result = str2prefix ("224.0.0.0/4", &group); | |
480 | else | |
481 | result = str2prefix (group_range, &group); | |
482 | ||
483 | if (!result) | |
dfe43e25 | 484 | return PIM_GROUP_BAD_ADDRESS; |
36d6bd7d DS |
485 | |
486 | result = inet_pton (AF_INET, rp, &rp_addr); | |
487 | if (result <= 0) | |
dfe43e25 DW |
488 | return PIM_RP_BAD_ADDRESS; |
489 | ||
490 | if (plist) | |
491 | rp_info = pim_rp_find_prefix_list (rp_addr, plist); | |
492 | else | |
493 | rp_info = pim_rp_find_exact (rp_addr, &group); | |
36d6bd7d | 494 | |
36d6bd7d | 495 | if (!rp_info) |
dfe43e25 DW |
496 | return PIM_RP_NOT_FOUND; |
497 | ||
498 | if (rp_info->plist) | |
499 | { | |
500 | XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist); | |
501 | rp_info->plist = NULL; | |
502 | } | |
503 | ||
1bc98276 CS |
504 | /* Deregister addr with Zebra NHT */ |
505 | nht_p.family = AF_INET; | |
506 | nht_p.prefixlen = IPV4_MAX_BITLEN; | |
507 | nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; | |
508 | if (PIM_DEBUG_PIM_TRACE) | |
509 | { | |
510 | char buf[PREFIX2STR_BUFFER]; | |
511 | prefix2str (&nht_p, buf, sizeof (buf)); | |
512 | zlog_debug ("%s: Deregister RP addr %s with NHT ", __PRETTY_FUNCTION__, buf); | |
513 | } | |
514 | pim_delete_tracked_nexthop (&nht_p, NULL, rp_info); | |
515 | ||
dfe43e25 DW |
516 | str2prefix ("224.0.0.0/4", &g_all); |
517 | rp_all = pim_rp_find_match_group (&g_all); | |
36d6bd7d DS |
518 | |
519 | if (rp_all == rp_info) | |
520 | { | |
63c59d0c DS |
521 | rp_all->rp.rpf_addr.family = AF_INET; |
522 | rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; | |
36d6bd7d | 523 | rp_all->i_am_rp = 0; |
dfe43e25 | 524 | return PIM_SUCCESS; |
36d6bd7d | 525 | } |
75a26779 | 526 | |
36d6bd7d | 527 | listnode_delete (qpim_rp_list, rp_info); |
1bc98276 | 528 | pim_rp_refresh_group_to_rp_mapping (); |
dfe43e25 | 529 | return PIM_SUCCESS; |
75a26779 | 530 | } |
13afbd05 DS |
531 | |
532 | int | |
533 | pim_rp_setup (void) | |
534 | { | |
36d6bd7d DS |
535 | struct listnode *node; |
536 | struct rp_info *rp_info; | |
537 | int ret = 0; | |
538 | ||
539 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) | |
13afbd05 | 540 | { |
e30a4d4a DS |
541 | if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE) |
542 | continue; | |
543 | ||
63b8f7a3 | 544 | if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) |
36d6bd7d | 545 | { |
98906696 DS |
546 | if (PIM_DEBUG_PIM_TRACE) |
547 | zlog_debug ("Unable to lookup nexthop for rp specified"); | |
36d6bd7d DS |
548 | ret++; |
549 | } | |
13afbd05 DS |
550 | } |
551 | ||
36d6bd7d DS |
552 | if (ret) |
553 | return 0; | |
554 | ||
13afbd05 DS |
555 | return 1; |
556 | } | |
557 | ||
54b97c74 | 558 | /* |
7176984f | 559 | * Checks to see if we should elect ourself the actual RP when new if |
560 | * addresses are added against an interface. | |
54b97c74 DS |
561 | */ |
562 | void | |
7176984f | 563 | pim_rp_check_on_if_add(struct pim_interface *pim_ifp) |
54b97c74 | 564 | { |
36d6bd7d DS |
565 | struct listnode *node; |
566 | struct rp_info *rp_info; | |
7667c556 | 567 | bool i_am_rp_changed = false; |
36d6bd7d DS |
568 | |
569 | if (qpim_rp_list == NULL) | |
54b97c74 DS |
570 | return; |
571 | ||
7176984f | 572 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) { |
573 | if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) | |
574 | continue; | |
575 | ||
576 | /* if i_am_rp is already set nothing to be done (adding new addresses | |
577 | * is not going to make a difference). */ | |
578 | if (rp_info->i_am_rp) { | |
579 | continue; | |
580 | } | |
581 | ||
582 | if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { | |
583 | i_am_rp_changed = true; | |
584 | rp_info->i_am_rp = 1; | |
36d6bd7d | 585 | if (PIM_DEBUG_ZEBRA) { |
eaa54bdb | 586 | char rp[PREFIX_STRLEN]; |
63c59d0c | 587 | pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp)); |
7176984f | 588 | zlog_debug("%s: %s: i am rp", __func__, rp); |
36d6bd7d | 589 | } |
7176984f | 590 | } |
591 | } | |
54b97c74 | 592 | |
7176984f | 593 | if (i_am_rp_changed) { |
594 | pim_msdp_i_am_rp_changed(); | |
595 | } | |
596 | } | |
36d6bd7d | 597 | |
7176984f | 598 | /* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses |
599 | * are removed. Removing numbers is an uncommon event in an active network | |
600 | * so I have made no attempt to optimize it. */ | |
601 | void | |
602 | pim_i_am_rp_re_evaluate(void) | |
603 | { | |
604 | struct listnode *node; | |
605 | struct rp_info *rp_info; | |
606 | bool i_am_rp_changed = false; | |
607 | int old_i_am_rp; | |
608 | ||
609 | if (qpim_rp_list == NULL) | |
610 | return; | |
611 | ||
612 | for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) { | |
613 | if (pim_rpf_addr_is_inaddr_none(&rp_info->rp)) | |
614 | continue; | |
615 | ||
616 | old_i_am_rp = rp_info->i_am_rp; | |
617 | pim_rp_check_interfaces(rp_info); | |
618 | ||
619 | if (old_i_am_rp != rp_info->i_am_rp) { | |
620 | i_am_rp_changed = true; | |
621 | if (PIM_DEBUG_ZEBRA) { | |
622 | char rp[PREFIX_STRLEN]; | |
623 | pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp)); | |
624 | if (rp_info->i_am_rp) { | |
625 | zlog_debug("%s: %s: i am rp", __func__, rp); | |
626 | } else { | |
627 | zlog_debug("%s: %s: i am no longer rp", __func__, rp); | |
36d6bd7d | 628 | } |
7176984f | 629 | } |
54b97c74 | 630 | } |
7176984f | 631 | } |
7667c556 | 632 | |
7176984f | 633 | if (i_am_rp_changed) { |
634 | pim_msdp_i_am_rp_changed(); | |
635 | } | |
54b97c74 DS |
636 | } |
637 | ||
638 | /* | |
639 | * I_am_RP(G) is true if the group-to-RP mapping indicates that | |
640 | * this router is the RP for the group. | |
641 | * | |
642 | * Since we only have static RP, all groups are part of this RP | |
643 | */ | |
644 | int | |
645 | pim_rp_i_am_rp (struct in_addr group) | |
646 | { | |
36d6bd7d DS |
647 | struct prefix g; |
648 | struct rp_info *rp_info; | |
649 | ||
650 | memset (&g, 0, sizeof (g)); | |
651 | g.family = AF_INET; | |
652 | g.prefixlen = 32; | |
653 | g.u.prefix4 = group; | |
654 | ||
655 | rp_info = pim_rp_find_match_group (&g); | |
656 | ||
657 | if (rp_info) | |
658 | return rp_info->i_am_rp; | |
659 | ||
660 | return 0; | |
54b97c74 DS |
661 | } |
662 | ||
71694057 DS |
663 | /* |
664 | * RP(G) | |
665 | * | |
666 | * Return the RP that the Group belongs too. | |
667 | */ | |
ed66602c | 668 | struct pim_rpf * |
71694057 DS |
669 | pim_rp_g (struct in_addr group) |
670 | { | |
36d6bd7d DS |
671 | struct prefix g; |
672 | struct rp_info *rp_info; | |
673 | ||
674 | memset (&g, 0, sizeof (g)); | |
675 | g.family = AF_INET; | |
676 | g.prefixlen = 32; | |
677 | g.u.prefix4 = group; | |
678 | ||
679 | rp_info = pim_rp_find_match_group (&g); | |
680 | ||
681 | if (rp_info) | |
682 | { | |
63b8f7a3 | 683 | pim_nexthop_lookup(&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1); |
36d6bd7d DS |
684 | return (&rp_info->rp); |
685 | } | |
686 | ||
687 | // About to Go Down | |
688 | return NULL; | |
71694057 DS |
689 | } |
690 | ||
8f5f5e91 DS |
691 | /* |
692 | * Set the upstream IP address we want to talk to based upon | |
693 | * the rp configured and the source address | |
694 | * | |
695 | * If we have don't have a RP configured and the source address is * | |
696 | * then return failure. | |
697 | * | |
698 | */ | |
699 | int | |
36d6bd7d | 700 | pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group) |
8f5f5e91 | 701 | { |
36d6bd7d DS |
702 | struct rp_info *rp_info; |
703 | struct prefix g; | |
704 | ||
705 | memset (&g, 0, sizeof (g)); | |
706 | g.family = AF_INET; | |
707 | g.prefixlen = 32; | |
708 | g.u.prefix4 = group; | |
709 | ||
710 | rp_info = pim_rp_find_match_group (&g); | |
711 | ||
63c59d0c | 712 | if ((pim_rpf_addr_is_inaddr_none (&rp_info->rp)) && (source.s_addr == INADDR_ANY)) |
8f5f5e91 DS |
713 | { |
714 | if (PIM_DEBUG_PIM_TRACE) | |
715 | zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__); | |
716 | return 0; | |
717 | } | |
718 | ||
63c59d0c | 719 | *up = (source.s_addr == INADDR_ANY) ? rp_info->rp.rpf_addr.u.prefix4 : source; |
8f5f5e91 DS |
720 | |
721 | return 1; | |
722 | } | |
75a26779 DS |
723 | |
724 | int | |
725 | pim_rp_config_write (struct vty *vty) | |
726 | { | |
36d6bd7d DS |
727 | struct listnode *node; |
728 | struct rp_info *rp_info; | |
dfe43e25 DW |
729 | char rp_buffer[32]; |
730 | char group_buffer[32]; | |
36d6bd7d | 731 | int count = 0; |
75a26779 | 732 | |
36d6bd7d | 733 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) |
75a26779 | 734 | { |
63c59d0c | 735 | if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) |
36d6bd7d DS |
736 | continue; |
737 | ||
dfe43e25 DW |
738 | if (rp_info->plist) |
739 | vty_out(vty, "ip pim rp %s prefix-list %s%s", | |
740 | inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), | |
741 | rp_info->plist, VTY_NEWLINE); | |
742 | else | |
743 | vty_out(vty, "ip pim rp %s %s%s", | |
744 | inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), | |
745 | prefix2str(&rp_info->group, group_buffer, 32), VTY_NEWLINE); | |
746 | count++; | |
75a26779 DS |
747 | } |
748 | ||
36d6bd7d | 749 | return count; |
75a26779 DS |
750 | } |
751 | ||
752 | int | |
753 | pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr) | |
754 | { | |
36d6bd7d DS |
755 | struct rp_info *rp_info; |
756 | struct prefix g; | |
757 | ||
758 | memset (&g, 0, sizeof (g)); | |
759 | g.family = AF_INET; | |
760 | g.prefixlen = 32; | |
761 | g.u.prefix4 = group; | |
762 | ||
763 | rp_info = pim_rp_find_match_group (&g); | |
75a26779 DS |
764 | /* |
765 | * See if we can short-cut some? | |
766 | * This might not make sense if we ever leave a static RP | |
767 | * type of configuration. | |
768 | * Note - Premature optimization might bite our patooeys' here. | |
769 | */ | |
770 | if (I_am_RP(group)) | |
771 | { | |
63c59d0c | 772 | if (dest_addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) |
75a26779 DS |
773 | return 1; |
774 | } | |
775 | ||
c5e2cb11 | 776 | if (if_lookup_exact_address (&dest_addr, AF_INET, VRF_DEFAULT)) |
75a26779 | 777 | return 1; |
1bc98276 | 778 | |
75a26779 DS |
779 | return 0; |
780 | } | |
00d07c6f DS |
781 | |
782 | void | |
9bf3c633 | 783 | pim_rp_show_information (struct vty *vty, u_char uj) |
00d07c6f DS |
784 | { |
785 | struct rp_info *rp_info; | |
9bf3c633 | 786 | struct rp_info *prev_rp_info = NULL; |
00d07c6f DS |
787 | struct listnode *node; |
788 | ||
9bf3c633 DW |
789 | json_object *json = NULL; |
790 | json_object *json_rp_rows = NULL; | |
791 | json_object *json_row = NULL; | |
792 | ||
793 | if (uj) | |
794 | json = json_object_new_object(); | |
795 | else | |
796 | vty_out (vty, "RP address group/prefix-list OIF I am RP%s", VTY_NEWLINE); | |
797 | ||
00d07c6f DS |
798 | for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) |
799 | { | |
63c59d0c | 800 | if (!pim_rpf_addr_is_inaddr_none (&rp_info->rp)) |
dce1a92f DS |
801 | { |
802 | char buf[48]; | |
dfe43e25 | 803 | |
9bf3c633 DW |
804 | if (uj) |
805 | { | |
806 | /* | |
807 | * If we have moved on to a new RP then add the entry for the previous RP | |
808 | */ | |
809 | if (prev_rp_info && | |
810 | prev_rp_info->rp.rpf_addr.u.prefix4.s_addr != rp_info->rp.rpf_addr.u.prefix4.s_addr) | |
811 | { | |
812 | json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); | |
813 | json_rp_rows = NULL; | |
814 | } | |
815 | ||
816 | if (!json_rp_rows) | |
817 | json_rp_rows = json_object_new_array(); | |
818 | ||
819 | json_row = json_object_new_object(); | |
3440bd15 DS |
820 | if (rp_info->rp.source_nexthop.interface) |
821 | json_object_string_add(json_row, "outboundInterface", rp_info->rp.source_nexthop.interface->name); | |
9bf3c633 DW |
822 | |
823 | if (rp_info->i_am_rp) | |
824 | json_object_boolean_true_add(json_row, "iAmRP"); | |
dfe43e25 | 825 | |
9bf3c633 DW |
826 | if (rp_info->plist) |
827 | json_object_string_add(json_row, "prefixList", rp_info->plist); | |
828 | else | |
829 | json_object_string_add(json_row, "group", prefix2str(&rp_info->group, buf, 48)); | |
dfe43e25 | 830 | |
9bf3c633 DW |
831 | json_object_array_add(json_rp_rows, json_row); |
832 | } | |
dfe43e25 | 833 | else |
9bf3c633 DW |
834 | { |
835 | vty_out (vty, "%-15s ", inet_ntoa (rp_info->rp.rpf_addr.u.prefix4)); | |
836 | ||
837 | if (rp_info->plist) | |
838 | vty_out (vty, "%-18s ", rp_info->plist); | |
839 | else | |
840 | vty_out (vty, "%-18s ", prefix2str(&rp_info->group, buf, 48)); | |
841 | ||
3440bd15 DS |
842 | if (rp_info->rp.source_nexthop.interface) |
843 | vty_out (vty, "%-10s ", rp_info->rp.source_nexthop.interface->name); | |
844 | else | |
845 | vty_out (vty, "%-10s ", "(Unknown)"); | |
9bf3c633 DW |
846 | |
847 | if (rp_info->i_am_rp) | |
848 | vty_out (vty, "yes%s", VTY_NEWLINE); | |
849 | else | |
850 | vty_out (vty, "no%s", VTY_NEWLINE); | |
851 | } | |
852 | ||
853 | prev_rp_info = rp_info; | |
dce1a92f | 854 | } |
00d07c6f | 855 | } |
9bf3c633 DW |
856 | |
857 | if (uj) { | |
858 | if (prev_rp_info && json_rp_rows) | |
859 | json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); | |
860 | ||
5f9cf1d2 | 861 | vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); |
9bf3c633 DW |
862 | json_object_free(json); |
863 | } | |
00d07c6f | 864 | } |