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