]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
6250610a | 2 | /* |
896014f4 DL |
3 | * PIM for Quagga: add the ability to configure multicast static routes |
4 | * Copyright (C) 2014 Nathan Bahr, ATCorp | |
896014f4 | 5 | */ |
6250610a | 6 | |
f91f89bc DS |
7 | #include <zebra.h> |
8 | ||
9 | #include "vty.h" | |
744d91b3 | 10 | #include "if.h" |
9867746a DS |
11 | #include "log.h" |
12 | #include "memory.h" | |
13 | #include "linklist.h" | |
f91f89bc | 14 | |
9867746a | 15 | #include "pimd.h" |
993e3d8e | 16 | #include "pim_instance.h" |
9867746a | 17 | #include "pim_oil.h" |
6250610a JAG |
18 | #include "pim_static.h" |
19 | #include "pim_time.h" | |
20 | #include "pim_str.h" | |
6250610a | 21 | #include "pim_iface.h" |
6250610a JAG |
22 | |
23 | void pim_static_route_free(struct static_route *s_route) | |
24 | { | |
d62a17ae | 25 | XFREE(MTYPE_PIM_STATIC_ROUTE, s_route); |
6250610a JAG |
26 | } |
27 | ||
4d762f26 | 28 | static struct static_route *static_route_alloc(void) |
6250610a | 29 | { |
2b57b948 | 30 | return XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(struct static_route)); |
6250610a JAG |
31 | } |
32 | ||
9981117a | 33 | static struct static_route *static_route_new(ifindex_t iif, ifindex_t oif, |
f0d63d90 DL |
34 | pim_addr group, |
35 | pim_addr source) | |
6250610a | 36 | { |
d62a17ae | 37 | struct static_route *s_route; |
38 | s_route = static_route_alloc(); | |
d62a17ae | 39 | |
40 | s_route->group = group; | |
41 | s_route->source = source; | |
42 | s_route->iif = iif; | |
43 | s_route->oif_ttls[oif] = 1; | |
44 | s_route->c_oil.oil_ref_count = 1; | |
f0d63d90 DL |
45 | *oil_origin(&s_route->c_oil) = source; |
46 | *oil_mcastgrp(&s_route->c_oil) = group; | |
47 | *oil_parent(&s_route->c_oil) = iif; | |
48 | oil_if_set(&s_route->c_oil, oif, 1); | |
d62a17ae | 49 | s_route->c_oil.oif_creation[oif] = pim_time_monotonic_sec(); |
50 | ||
51 | return s_route; | |
6250610a JAG |
52 | } |
53 | ||
54 | ||
4e0bc0f0 | 55 | int pim_static_add(struct pim_instance *pim, struct interface *iif, |
f0d63d90 | 56 | struct interface *oif, pim_addr group, pim_addr source) |
6250610a | 57 | { |
d62a17ae | 58 | struct listnode *node = NULL; |
59 | struct static_route *s_route = NULL; | |
60 | struct static_route *original_s_route = NULL; | |
61 | struct pim_interface *pim_iif = iif ? iif->info : NULL; | |
62 | struct pim_interface *pim_oif = oif ? oif->info : NULL; | |
63 | ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; | |
64 | ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; | |
65 | ||
04b6c83d | 66 | if (!iif_index || !oif_index || iif_index == -1 || oif_index == -1) { |
d62a17ae | 67 | zlog_warn( |
68 | "%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)", | |
5e81f5dd | 69 | __FILE__, __func__, iif_index, oif_index); |
d62a17ae | 70 | return -2; |
71 | } | |
6250610a JAG |
72 | |
73 | #ifdef PIM_ENFORCE_LOOPFREE_MFC | |
d62a17ae | 74 | if (iif_index == oif_index) { |
75 | /* looped MFC entry */ | |
76 | zlog_warn( | |
77 | "%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)", | |
5e81f5dd | 78 | __FILE__, __func__, iif_index, oif_index); |
d62a17ae | 79 | return -4; |
80 | } | |
6250610a | 81 | #endif |
096f7609 | 82 | if (iif->vrf->vrf_id != oif->vrf->vrf_id) { |
72ec4672 DS |
83 | return -3; |
84 | } | |
85 | ||
4e0bc0f0 | 86 | for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { |
90937e42 JAG |
87 | if (!pim_addr_cmp(s_route->group, group) && |
88 | !pim_addr_cmp(s_route->source, source) && | |
89 | (s_route->iif == iif_index)) { | |
90 | ||
91 | if (s_route->oif_ttls[oif_index]) { | |
d62a17ae | 92 | zlog_warn( |
f0d63d90 | 93 | "%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
5e81f5dd | 94 | __FILE__, __func__, iif_index, |
f0d63d90 | 95 | oif_index, &group, &source); |
d62a17ae | 96 | return -3; |
97 | } | |
98 | ||
99 | /* Ok, from here on out we will be making changes to the | |
100 | * s_route structure, but if | |
101 | * for some reason we fail to commit these changes to | |
102 | * the kernel, we want to be able | |
103 | * restore the state of the list. So copy the node data | |
104 | * and if need be, we can copy | |
105 | * back if it fails. | |
106 | */ | |
107 | original_s_route = static_route_alloc(); | |
d62a17ae | 108 | memcpy(original_s_route, s_route, |
109 | sizeof(struct static_route)); | |
110 | ||
111 | /* Route exists and has the same input interface, but | |
112 | * adding a new output interface */ | |
90937e42 JAG |
113 | s_route->oif_ttls[oif_index] = 1; |
114 | oil_if_set(&s_route->c_oil, oif_index, 1); | |
115 | s_route->c_oil.oif_creation[oif_index] = | |
116 | pim_time_monotonic_sec(); | |
117 | ++s_route->c_oil.oil_ref_count; | |
d62a17ae | 118 | break; |
119 | } | |
120 | } | |
121 | ||
122 | /* If node is null then we reached the end of the list without finding a | |
123 | * match */ | |
124 | if (!node) { | |
125 | s_route = static_route_new(iif_index, oif_index, group, source); | |
4e0bc0f0 | 126 | listnode_add(pim->static_routes, s_route); |
d62a17ae | 127 | } |
128 | ||
2e4e8571 DS |
129 | s_route->c_oil.pim = pim; |
130 | ||
5e81f5dd | 131 | if (pim_static_mroute_add(&s_route->c_oil, __func__)) { |
d62a17ae | 132 | zlog_warn( |
f0d63d90 DL |
133 | "%s %s: Unable to add static route(iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
134 | __FILE__, __func__, iif_index, oif_index, &group, | |
135 | &source); | |
d62a17ae | 136 | |
137 | /* Need to put s_route back to the way it was */ | |
138 | if (original_s_route) { | |
139 | memcpy(s_route, original_s_route, | |
140 | sizeof(struct static_route)); | |
141 | } else { | |
142 | /* we never stored off a copy, so it must have been a | |
143 | * fresh new route */ | |
4e0bc0f0 | 144 | listnode_delete(pim->static_routes, s_route); |
d62a17ae | 145 | pim_static_route_free(s_route); |
146 | } | |
147 | ||
148 | if (original_s_route) { | |
149 | pim_static_route_free(original_s_route); | |
150 | } | |
151 | ||
152 | return -1; | |
153 | } | |
154 | ||
155 | /* Make sure we free the memory for the route copy if used */ | |
156 | if (original_s_route) { | |
157 | pim_static_route_free(original_s_route); | |
158 | } | |
159 | ||
160 | if (PIM_DEBUG_STATIC) { | |
d62a17ae | 161 | zlog_debug( |
f0d63d90 DL |
162 | "%s: Static route added(iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
163 | __func__, iif_index, oif_index, &group, | |
164 | &source); | |
d62a17ae | 165 | } |
166 | ||
167 | return 0; | |
6250610a JAG |
168 | } |
169 | ||
4e0bc0f0 | 170 | int pim_static_del(struct pim_instance *pim, struct interface *iif, |
f0d63d90 | 171 | struct interface *oif, pim_addr group, pim_addr source) |
6250610a | 172 | { |
d62a17ae | 173 | struct listnode *node = NULL; |
174 | struct listnode *nextnode = NULL; | |
175 | struct static_route *s_route = NULL; | |
176 | struct pim_interface *pim_iif = iif ? iif->info : 0; | |
177 | struct pim_interface *pim_oif = oif ? oif->info : 0; | |
178 | ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; | |
179 | ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; | |
180 | ||
181 | if (!iif_index || !oif_index) { | |
182 | zlog_warn( | |
183 | "%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)", | |
5e81f5dd | 184 | __FILE__, __func__, iif_index, oif_index); |
d62a17ae | 185 | return -2; |
186 | } | |
187 | ||
4e0bc0f0 | 188 | for (ALL_LIST_ELEMENTS(pim->static_routes, node, nextnode, s_route)) { |
d62a17ae | 189 | if (s_route->iif == iif_index |
f0d63d90 DL |
190 | && !pim_addr_cmp(s_route->group, group) |
191 | && !pim_addr_cmp(s_route->source, source) | |
d62a17ae | 192 | && s_route->oif_ttls[oif_index]) { |
193 | s_route->oif_ttls[oif_index] = 0; | |
f0d63d90 | 194 | oil_if_set(&s_route->c_oil, oif_index, 0); |
d62a17ae | 195 | --s_route->c_oil.oil_ref_count; |
196 | ||
197 | /* If there are no more outputs then delete the whole | |
198 | * route, otherwise set the route with the new outputs | |
199 | */ | |
200 | if (s_route->c_oil.oil_ref_count <= 0 | |
5e81f5dd | 201 | ? pim_mroute_del(&s_route->c_oil, __func__) |
69e3538c | 202 | : pim_static_mroute_add(&s_route->c_oil, |
5e81f5dd | 203 | __func__)) { |
d62a17ae | 204 | zlog_warn( |
f0d63d90 | 205 | "%s %s: Unable to remove static route(iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
5e81f5dd | 206 | __FILE__, __func__, iif_index, |
f0d63d90 | 207 | oif_index, &group, &source); |
d62a17ae | 208 | |
209 | s_route->oif_ttls[oif_index] = 1; | |
f0d63d90 | 210 | oil_if_set(&s_route->c_oil, oif_index, 1); |
d62a17ae | 211 | ++s_route->c_oil.oil_ref_count; |
212 | ||
213 | return -1; | |
214 | } | |
215 | ||
216 | s_route->c_oil.oif_creation[oif_index] = 0; | |
217 | ||
218 | if (s_route->c_oil.oil_ref_count <= 0) { | |
4e0bc0f0 | 219 | listnode_delete(pim->static_routes, s_route); |
d62a17ae | 220 | pim_static_route_free(s_route); |
221 | } | |
222 | ||
223 | if (PIM_DEBUG_STATIC) { | |
d62a17ae | 224 | zlog_debug( |
f0d63d90 | 225 | "%s: Static route removed(iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
5e81f5dd | 226 | __func__, iif_index, oif_index, |
f0d63d90 | 227 | &group, &source); |
d62a17ae | 228 | } |
229 | ||
230 | break; | |
231 | } | |
232 | } | |
233 | ||
234 | if (!node) { | |
d62a17ae | 235 | zlog_warn( |
f0d63d90 DL |
236 | "%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%pPAs,source=%pPAs)", |
237 | __FILE__, __func__, iif_index, oif_index, &group, | |
238 | &source); | |
d62a17ae | 239 | return -3; |
240 | } | |
241 | ||
242 | return 0; | |
6250610a | 243 | } |
f91f89bc | 244 | |
64c86530 | 245 | int pim_static_write_mroute(struct pim_instance *pim, struct vty *vty, |
4e0bc0f0 | 246 | struct interface *ifp) |
f91f89bc | 247 | { |
d62a17ae | 248 | struct pim_interface *pim_ifp = ifp->info; |
249 | struct listnode *node; | |
250 | struct static_route *sroute; | |
251 | int count = 0; | |
d62a17ae | 252 | |
253 | if (!pim_ifp) | |
254 | return 0; | |
255 | ||
4e0bc0f0 | 256 | for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sroute)) { |
d62a17ae | 257 | if (sroute->iif == pim_ifp->mroute_vif_index) { |
258 | int i; | |
259 | for (i = 0; i < MAXVIFS; i++) | |
260 | if (sroute->oif_ttls[i]) { | |
261 | struct interface *oifp = | |
7cfc7bcf DS |
262 | pim_if_find_by_vif_index(pim, |
263 | i); | |
f0d63d90 | 264 | if (pim_addr_is_any(sroute->source)) |
d62a17ae | 265 | vty_out(vty, |
d17b6892 | 266 | " " PIM_AF_NAME " mroute %s %pPA\n", |
f0d63d90 | 267 | oifp->name, &sroute->group); |
d62a17ae | 268 | else |
269 | vty_out(vty, | |
d17b6892 | 270 | " " PIM_AF_NAME " mroute %s %pPA %pPA\n", |
f0d63d90 DL |
271 | oifp->name, &sroute->group, |
272 | &sroute->source); | |
d62a17ae | 273 | count++; |
274 | } | |
275 | } | |
276 | } | |
277 | ||
278 | return count; | |
f91f89bc | 279 | } |