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