]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_static.c
Merge commit '78986c0' into tmp-3.0-master-merge
[mirror_frr.git] / pimd / pim_static.c
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
35 void pim_static_route_free(struct static_route *s_route)
36 {
37 XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
38 }
39
40 static struct static_route *static_route_alloc()
41 {
42 struct static_route *s_route;
43
44 s_route = XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(*s_route));
45 if (!s_route) {
46 zlog_err("PIM XCALLOC(%zu) failure", sizeof(*s_route));
47 return 0;
48 }
49 return s_route;
50 }
51
52 static struct static_route *static_route_new(unsigned int iif, unsigned int oif,
53 struct in_addr group,
54 struct in_addr source)
55 {
56 struct static_route *s_route;
57 s_route = static_route_alloc();
58 if (!s_route) {
59 return 0;
60 }
61
62 s_route->group = group;
63 s_route->source = source;
64 s_route->iif = iif;
65 s_route->oif_ttls[oif] = 1;
66 s_route->c_oil.oil_ref_count = 1;
67 s_route->c_oil.oil.mfcc_origin = source;
68 s_route->c_oil.oil.mfcc_mcastgrp = group;
69 s_route->c_oil.oil.mfcc_parent = iif;
70 s_route->c_oil.oil.mfcc_ttls[oif] = 1;
71 s_route->c_oil.oif_creation[oif] = pim_time_monotonic_sec();
72
73 return s_route;
74 }
75
76
77 int pim_static_add(struct pim_instance *pim, struct interface *iif,
78 struct interface *oif, struct in_addr group,
79 struct in_addr source)
80 {
81 struct listnode *node = NULL;
82 struct static_route *s_route = NULL;
83 struct static_route *original_s_route = NULL;
84 struct pim_interface *pim_iif = iif ? iif->info : NULL;
85 struct pim_interface *pim_oif = oif ? oif->info : NULL;
86 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
87 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
88
89 if (!iif_index || !oif_index) {
90 zlog_warn(
91 "%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
92 __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index);
93 return -2;
94 }
95
96 #ifdef PIM_ENFORCE_LOOPFREE_MFC
97 if (iif_index == oif_index) {
98 /* looped MFC entry */
99 zlog_warn(
100 "%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
101 __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index);
102 return -4;
103 }
104 #endif
105 if (iif->vrf_id != oif->vrf_id) {
106 return -3;
107 }
108
109 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
110 if (s_route->group.s_addr == group.s_addr
111 && s_route->source.s_addr == source.s_addr) {
112 if (s_route->iif == iif_index
113 && s_route->oif_ttls[oif_index]) {
114 char gifaddr_str[INET_ADDRSTRLEN];
115 char sifaddr_str[INET_ADDRSTRLEN];
116 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
117 sizeof(gifaddr_str));
118 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
119 sizeof(sifaddr_str));
120 zlog_warn(
121 "%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
122 __FILE__, __PRETTY_FUNCTION__,
123 iif_index, oif_index, gifaddr_str,
124 sifaddr_str);
125 return -3;
126 }
127
128 /* Ok, from here on out we will be making changes to the
129 * s_route structure, but if
130 * for some reason we fail to commit these changes to
131 * the kernel, we want to be able
132 * restore the state of the list. So copy the node data
133 * and if need be, we can copy
134 * back if it fails.
135 */
136 original_s_route = static_route_alloc();
137 if (!original_s_route) {
138 return -5;
139 }
140 memcpy(original_s_route, s_route,
141 sizeof(struct static_route));
142
143 /* Route exists and has the same input interface, but
144 * adding a new output interface */
145 if (s_route->iif == iif_index) {
146 s_route->oif_ttls[oif_index] = 1;
147 s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
148 s_route->c_oil.oif_creation[oif_index] =
149 pim_time_monotonic_sec();
150 ++s_route->c_oil.oil_ref_count;
151 } else {
152 /* input interface changed */
153 s_route->iif = iif_index;
154 s_route->c_oil.oil.mfcc_parent = iif_index;
155
156 #ifdef PIM_ENFORCE_LOOPFREE_MFC
157 /* check to make sure the new input was not an
158 * old output */
159 if (s_route->oif_ttls[iif_index]) {
160 s_route->oif_ttls[iif_index] = 0;
161 s_route->c_oil.oif_creation[iif_index] =
162 0;
163 s_route->c_oil.oil
164 .mfcc_ttls[iif_index] = 0;
165 --s_route->c_oil.oil_ref_count;
166 }
167 #endif
168
169 /* now add the new output, if it is new */
170 if (!s_route->oif_ttls[oif_index]) {
171 s_route->oif_ttls[oif_index] = 1;
172 s_route->c_oil.oif_creation[oif_index] =
173 pim_time_monotonic_sec();
174 s_route->c_oil.oil
175 .mfcc_ttls[oif_index] = 1;
176 ++s_route->c_oil.oil_ref_count;
177 }
178 }
179
180 break;
181 }
182 }
183
184 /* If node is null then we reached the end of the list without finding a
185 * match */
186 if (!node) {
187 s_route = static_route_new(iif_index, oif_index, group, source);
188 listnode_add(pim->static_routes, s_route);
189 }
190
191 s_route->c_oil.pim = pim;
192
193 if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
194 char gifaddr_str[INET_ADDRSTRLEN];
195 char sifaddr_str[INET_ADDRSTRLEN];
196 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
197 sizeof(gifaddr_str));
198 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
199 sizeof(sifaddr_str));
200 zlog_warn(
201 "%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
202 __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index,
203 gifaddr_str, sifaddr_str);
204
205 /* Need to put s_route back to the way it was */
206 if (original_s_route) {
207 memcpy(s_route, original_s_route,
208 sizeof(struct static_route));
209 } else {
210 /* we never stored off a copy, so it must have been a
211 * fresh new route */
212 listnode_delete(pim->static_routes, s_route);
213 pim_static_route_free(s_route);
214 }
215
216 if (original_s_route) {
217 pim_static_route_free(original_s_route);
218 }
219
220 return -1;
221 }
222
223 /* Make sure we free the memory for the route copy if used */
224 if (original_s_route) {
225 pim_static_route_free(original_s_route);
226 }
227
228 if (PIM_DEBUG_STATIC) {
229 char gifaddr_str[INET_ADDRSTRLEN];
230 char sifaddr_str[INET_ADDRSTRLEN];
231 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
232 sizeof(gifaddr_str));
233 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
234 sizeof(sifaddr_str));
235 zlog_debug(
236 "%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
237 __PRETTY_FUNCTION__, iif_index, oif_index, gifaddr_str,
238 sifaddr_str);
239 }
240
241 return 0;
242 }
243
244 int pim_static_del(struct pim_instance *pim, struct interface *iif,
245 struct interface *oif, struct in_addr group,
246 struct in_addr source)
247 {
248 struct listnode *node = NULL;
249 struct listnode *nextnode = NULL;
250 struct static_route *s_route = NULL;
251 struct pim_interface *pim_iif = iif ? iif->info : 0;
252 struct pim_interface *pim_oif = oif ? oif->info : 0;
253 ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
254 ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
255
256 if (!iif_index || !oif_index) {
257 zlog_warn(
258 "%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
259 __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index);
260 return -2;
261 }
262
263 for (ALL_LIST_ELEMENTS(pim->static_routes, node, nextnode, s_route)) {
264 if (s_route->iif == iif_index
265 && s_route->group.s_addr == group.s_addr
266 && s_route->source.s_addr == source.s_addr
267 && s_route->oif_ttls[oif_index]) {
268 s_route->oif_ttls[oif_index] = 0;
269 s_route->c_oil.oil.mfcc_ttls[oif_index] = 0;
270 --s_route->c_oil.oil_ref_count;
271
272 /* If there are no more outputs then delete the whole
273 * route, otherwise set the route with the new outputs
274 */
275 if (s_route->c_oil.oil_ref_count <= 0
276 ? pim_mroute_del(&s_route->c_oil,
277 __PRETTY_FUNCTION__)
278 : pim_mroute_add(&s_route->c_oil,
279 __PRETTY_FUNCTION__)) {
280 char gifaddr_str[INET_ADDRSTRLEN];
281 char sifaddr_str[INET_ADDRSTRLEN];
282 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
283 sizeof(gifaddr_str));
284 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
285 sizeof(sifaddr_str));
286 zlog_warn(
287 "%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
288 __FILE__, __PRETTY_FUNCTION__,
289 iif_index, oif_index, gifaddr_str,
290 sifaddr_str);
291
292 s_route->oif_ttls[oif_index] = 1;
293 s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
294 ++s_route->c_oil.oil_ref_count;
295
296 return -1;
297 }
298
299 s_route->c_oil.oif_creation[oif_index] = 0;
300
301 if (s_route->c_oil.oil_ref_count <= 0) {
302 listnode_delete(pim->static_routes, s_route);
303 pim_static_route_free(s_route);
304 }
305
306 if (PIM_DEBUG_STATIC) {
307 char gifaddr_str[INET_ADDRSTRLEN];
308 char sifaddr_str[INET_ADDRSTRLEN];
309 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
310 sizeof(gifaddr_str));
311 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
312 sizeof(sifaddr_str));
313 zlog_debug(
314 "%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
315 __PRETTY_FUNCTION__, iif_index,
316 oif_index, gifaddr_str, sifaddr_str);
317 }
318
319 break;
320 }
321 }
322
323 if (!node) {
324 char gifaddr_str[INET_ADDRSTRLEN];
325 char sifaddr_str[INET_ADDRSTRLEN];
326 pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
327 sizeof(gifaddr_str));
328 pim_inet4_dump("<ifaddr?>", source, sifaddr_str,
329 sizeof(sifaddr_str));
330 zlog_warn(
331 "%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
332 __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index,
333 gifaddr_str, sifaddr_str);
334 return -3;
335 }
336
337 return 0;
338 }
339
340 int pim_static_write_mroute(struct pim_instance *pim, struct vty *vty,
341 struct interface *ifp)
342 {
343 struct pim_interface *pim_ifp = ifp->info;
344 struct listnode *node;
345 struct static_route *sroute;
346 int count = 0;
347 char sbuf[INET_ADDRSTRLEN];
348 char gbuf[INET_ADDRSTRLEN];
349
350 if (!pim_ifp)
351 return 0;
352
353 for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sroute)) {
354 pim_inet4_dump("<ifaddr?>", sroute->group, gbuf, sizeof(gbuf));
355 pim_inet4_dump("<ifaddr?>", sroute->source, sbuf, sizeof(sbuf));
356 if (sroute->iif == pim_ifp->mroute_vif_index) {
357 int i;
358 for (i = 0; i < MAXVIFS; i++)
359 if (sroute->oif_ttls[i]) {
360 struct interface *oifp =
361 pim_if_find_by_vif_index(pim,
362 i);
363 if (sroute->source.s_addr == 0)
364 vty_out(vty,
365 " ip mroute %s %s\n",
366 oifp->name, gbuf);
367 else
368 vty_out(vty,
369 " ip mroute %s %s %s\n",
370 oifp->name, gbuf, sbuf);
371 count++;
372 }
373 }
374 }
375
376 return count;
377 }