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