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