1 /*
2 * PIM for Quagga: add the ability to configure multicast static routes
3 * Copyright (C) 2014 Nathan Bahr, ATCorp
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21
22 #include "vty.h"
23 #include "if.h"
24 #include "log.h"
25 #include "memory.h"
26 #include "linklist.h"
27
28 #include "pimd.h"
29 #include "pim_oil.h"
30 #include "pim_static.h"
31 #include "pim_time.h"
32 #include "pim_str.h"
33 #include "pim_iface.h"
34
pim_static_route_free(struct static_route * s_route)35 void pim_static_route_free(struct static_route *s_route)
36 {
37 XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
38 }
39
static_route_alloc(void)40 static struct static_route *static_route_alloc(void)
41 {
42 return XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(struct static_route));
43 }
44
static_route_new(ifindex_t iif,ifindex_t oif,struct in_addr group,struct in_addr source)45 static struct static_route *static_route_new(ifindex_t iif, ifindex_t oif,
46 struct in_addr group,
47 struct in_addr source)
48 {
49 struct static_route *s_route;
50 s_route = static_route_alloc();
51
52 s_route->group = group;
53 s_route->source = source;
54 s_route->iif = iif;
55 s_route->oif_ttls[oif] = 1;
56 s_route->c_oil.oil_ref_count = 1;
57 s_route->c_oil.oil.mfcc_origin = source;
58 s_route->c_oil.oil.mfcc_mcastgrp = group;
59 s_route->c_oil.oil.mfcc_parent = iif;
60 s_route->c_oil.oil.mfcc_ttls[oif] = 1;
61 s_route->c_oil.oif_creation[oif] = pim_time_monotonic_sec();
62
63 return s_route;
64 }
65
66
pim_static_add(struct pim_instance * pim,struct interface * iif,struct interface * oif,struct in_addr group,struct in_addr source)67 int pim_static_add(struct pim_instance *pim, struct interface *iif,
68 struct interface *oif, struct in_addr group,
69 struct in_addr source)
70 {
71 struct listnode *node = NULL;
72 struct static_route *s_route = NULL;
73 struct static_route *original_s_route = NULL;
74 struct pim_interface *pim_iif = iif ? iif->info : NULL;
75 struct pim_interface *pim_oif = oif ? oif->info : NULL;
76 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
77 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
78
79 if (!iif_index || !oif_index || iif_index == -1 || oif_index == -1) {
80 zlog_warn(
81 "%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
82 __FILE__, __func__, iif_index, oif_index);
83 return -2;
84 }
85
86 #ifdef PIM_ENFORCE_LOOPFREE_MFC
87 if (iif_index == oif_index) {
88 /* looped MFC entry */
89 zlog_warn(
90 "%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
91 __FILE__, __func__, iif_index, oif_index);
92 return -4;
93 }
94 #endif
95 if (iif->vrf_id != oif->vrf_id) {
96 return -3;
97 }
98
99 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
100 if (s_route->group.s_addr == group.s_addr
101 && s_route->source.s_addr == source.s_addr) {
102 if (s_route->iif == iif_index
103 && s_route->oif_ttls[oif_index]) {
104 char gifaddr_str[INET_ADDRSTRLEN];
105 char sifaddr_str[INET_ADDRSTRLEN];
106 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
107 sizeof(gifaddr_str));
108 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
109 sizeof(sifaddr_str));
110 zlog_warn(
111 "%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
112 __FILE__, __func__, iif_index,
113 oif_index, gifaddr_str, sifaddr_str);
114 return -3;
115 }
116
117 /* Ok, from here on out we will be making changes to the
118 * s_route structure, but if
119 * for some reason we fail to commit these changes to
120 * the kernel, we want to be able
121 * restore the state of the list. So copy the node data
122 * and if need be, we can copy
123 * back if it fails.
124 */
125 original_s_route = static_route_alloc();
126 memcpy(original_s_route, s_route,
127 sizeof(struct static_route));
128
129 /* Route exists and has the same input interface, but
130 * adding a new output interface */
131 if (s_route->iif == iif_index) {
132 s_route->oif_ttls[oif_index] = 1;
133 s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
134 s_route->c_oil.oif_creation[oif_index] =
135 pim_time_monotonic_sec();
136 ++s_route->c_oil.oil_ref_count;
137 } else {
138 /* input interface changed */
139 s_route->iif = iif_index;
140 pim_static_mroute_iif_update(
141 &s_route->c_oil, iif_index, __func__);
142
143 #ifdef PIM_ENFORCE_LOOPFREE_MFC
144 /* check to make sure the new input was not an
145 * old output */
146 if (s_route->oif_ttls[iif_index]) {
147 s_route->oif_ttls[iif_index] = 0;
148 s_route->c_oil.oif_creation[iif_index] =
149 0;
150 s_route->c_oil.oil
151 .mfcc_ttls[iif_index] = 0;
152 --s_route->c_oil.oil_ref_count;
153 }
154 #endif
155
156 /* now add the new output, if it is new */
157 if (!s_route->oif_ttls[oif_index]) {
158 s_route->oif_ttls[oif_index] = 1;
159 s_route->c_oil.oif_creation[oif_index] =
160 pim_time_monotonic_sec();
161 s_route->c_oil.oil
162 .mfcc_ttls[oif_index] = 1;
163 ++s_route->c_oil.oil_ref_count;
164 }
165 }
166
167 break;
168 }
169 }
170
171 /* If node is null then we reached the end of the list without finding a
172 * match */
173 if (!node) {
174 s_route = static_route_new(iif_index, oif_index, group, source);
175 listnode_add(pim->static_routes, s_route);
176 }
177
178 s_route->c_oil.pim = pim;
179
180 if (pim_static_mroute_add(&s_route->c_oil, __func__)) {
181 char gifaddr_str[INET_ADDRSTRLEN];
182 char sifaddr_str[INET_ADDRSTRLEN];
183 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
184 sizeof(gifaddr_str));
185 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
186 sizeof(sifaddr_str));
187 zlog_warn(
188 "%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
189 __FILE__, __func__, iif_index, oif_index, gifaddr_str,
190 sifaddr_str);
191
192 /* Need to put s_route back to the way it was */
193 if (original_s_route) {
194 memcpy(s_route, original_s_route,
195 sizeof(struct static_route));
196 } else {
197 /* we never stored off a copy, so it must have been a
198 * fresh new route */
199 listnode_delete(pim->static_routes, s_route);
200 pim_static_route_free(s_route);
201 }
202
203 if (original_s_route) {
204 pim_static_route_free(original_s_route);
205 }
206
207 return -1;
208 }
209
210 /* Make sure we free the memory for the route copy if used */
211 if (original_s_route) {
212 pim_static_route_free(original_s_route);
213 }
214
215 if (PIM_DEBUG_STATIC) {
216 char gifaddr_str[INET_ADDRSTRLEN];
217 char sifaddr_str[INET_ADDRSTRLEN];
218 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
219 sizeof(gifaddr_str));
220 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
221 sizeof(sifaddr_str));
222 zlog_debug(
223 "%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
224 __func__, iif_index, oif_index, gifaddr_str,
225 sifaddr_str);
226 }
227
228 return 0;
229 }
230
pim_static_del(struct pim_instance * pim,struct interface * iif,struct interface * oif,struct in_addr group,struct in_addr source)231 int pim_static_del(struct pim_instance *pim, struct interface *iif,
232 struct interface *oif, struct in_addr group,
233 struct in_addr source)
234 {
235 struct listnode *node = NULL;
236 struct listnode *nextnode = NULL;
237 struct static_route *s_route = NULL;
238 struct pim_interface *pim_iif = iif ? iif->info : 0;
239 struct pim_interface *pim_oif = oif ? oif->info : 0;
240 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
241 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
242
243 if (!iif_index || !oif_index) {
244 zlog_warn(
245 "%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
246 __FILE__, __func__, iif_index, oif_index);
247 return -2;
248 }
249
250 for (ALL_LIST_ELEMENTS(pim->static_routes, node, nextnode, s_route)) {
251 if (s_route->iif == iif_index
252 && s_route->group.s_addr == group.s_addr
253 && s_route->source.s_addr == source.s_addr
254 && s_route->oif_ttls[oif_index]) {
255 s_route->oif_ttls[oif_index] = 0;
256 s_route->c_oil.oil.mfcc_ttls[oif_index] = 0;
257 --s_route->c_oil.oil_ref_count;
258
259 /* If there are no more outputs then delete the whole
260 * route, otherwise set the route with the new outputs
261 */
262 if (s_route->c_oil.oil_ref_count <= 0
263 ? pim_mroute_del(&s_route->c_oil, __func__)
264 : pim_static_mroute_add(&s_route->c_oil,
265 __func__)) {
266 char gifaddr_str[INET_ADDRSTRLEN];
267 char sifaddr_str[INET_ADDRSTRLEN];
268 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
269 sizeof(gifaddr_str));
270 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
271 sizeof(sifaddr_str));
272 zlog_warn(
273 "%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
274 __FILE__, __func__, iif_index,
275 oif_index, gifaddr_str, sifaddr_str);
276
277 s_route->oif_ttls[oif_index] = 1;
278 s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
279 ++s_route->c_oil.oil_ref_count;
280
281 return -1;
282 }
283
284 s_route->c_oil.oif_creation[oif_index] = 0;
285
286 if (s_route->c_oil.oil_ref_count <= 0) {
287 listnode_delete(pim->static_routes, s_route);
288 pim_static_route_free(s_route);
289 }
290
291 if (PIM_DEBUG_STATIC) {
292 char gifaddr_str[INET_ADDRSTRLEN];
293 char sifaddr_str[INET_ADDRSTRLEN];
294 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
295 sizeof(gifaddr_str));
296 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
297 sizeof(sifaddr_str));
298 zlog_debug(
299 "%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
300 __func__, iif_index, oif_index,
301 gifaddr_str, sifaddr_str);
302 }
303
304 break;
305 }
306 }
307
308 if (!node) {
309 char gifaddr_str[INET_ADDRSTRLEN];
310 char sifaddr_str[INET_ADDRSTRLEN];
311 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
312 sizeof(gifaddr_str));
313 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
314 sizeof(sifaddr_str));
315 zlog_warn(
316 "%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
317 __FILE__, __func__, iif_index, oif_index, gifaddr_str,
318 sifaddr_str);
319 return -3;
320 }
321
322 return 0;
323 }
324
pim_static_write_mroute(struct pim_instance * pim,struct vty * vty,struct interface * ifp)325 int pim_static_write_mroute(struct pim_instance *pim, struct vty *vty,
326 struct interface *ifp)
327 {
328 struct pim_interface *pim_ifp = ifp->info;
329 struct listnode *node;
330 struct static_route *sroute;
331 int count = 0;
332 char sbuf[INET_ADDRSTRLEN];
333 char gbuf[INET_ADDRSTRLEN];
334
335 if (!pim_ifp)
336 return 0;
337
338 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sroute)) {
339 pim_inet4_dump("<ifaddr?>", sroute->group, gbuf, sizeof(gbuf));
340 pim_inet4_dump("<ifaddr?>", sroute->source, sbuf, sizeof(sbuf));
341 if (sroute->iif == pim_ifp->mroute_vif_index) {
342 int i;
343 for (i = 0; i < MAXVIFS; i++)
344 if (sroute->oif_ttls[i]) {
345 struct interface *oifp =
346 pim_if_find_by_vif_index(pim,
347 i);
348 if (sroute->source.s_addr == 0)
349 vty_out(vty,
350 " ip mroute %s %s\n",
351 oifp->name, gbuf);
352 else
353 vty_out(vty,
354 " ip mroute %s %s %s\n",
355 oifp->name, gbuf, sbuf);
356 count++;
357 }
358 }
359 }
360
361 return count;
362 }
363