blob: 3ab5011bc76b9737fc748d78fcba7ae956f6eab8 [file] [log] [blame]
Jafar Al-Gharaibeh030674d2015-06-11 18:29:02 -05001/*
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
David Lamparter821df2c2015-09-15 01:53:09 -070023#include <zebra.h>
24
Jafar Al-Gharaibeh030674d2015-06-11 18:29:02 -050025#include "pim_static.h"
26#include "pim_time.h"
27#include "pim_str.h"
28#include "pimd.h"
29#include "pim_iface.h"
30#include "log.h"
31#include "memory.h"
32#include "linklist.h"
33
34void pim_static_route_free(struct static_route *s_route)
35{
36 XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
37}
38
39static struct static_route * static_route_alloc()
40{
41 struct static_route *s_route;
42
43 s_route = XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(*s_route));
44 if (!s_route) {
45 zlog_err("PIM XCALLOC(%lu) failure", sizeof(*s_route));
46 return 0;
47 }
48 return s_route;
49}
50
51static struct static_route *static_route_new(unsigned int iif,
52 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->oif_count = 1;
67 s_route->mc.mfcc_origin = source;
68 s_route->mc.mfcc_mcastgrp = group;
69 s_route->mc.mfcc_parent = iif;
70 s_route->mc.mfcc_ttls[oif] = 1;
71 s_route->creation[oif] = pim_time_monotonic_sec();
72
73 return s_route;
74}
75
76
77int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
78{
79 struct listnode *node = 0;
80 struct static_route *s_route = 0;
81 struct static_route *original_s_route = 0;
82 struct pim_interface *pim_iif = iif ? iif->info : 0;
83 struct pim_interface *pim_oif = oif ? oif->info : 0;
84 unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
85 unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
86
87 if (!iif_index || !oif_index) {
88 zlog_warn("%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
89 __FILE__, __PRETTY_FUNCTION__,
90 iif_index,
91 oif_index);
92 return -2;
93 }
94
95#ifdef PIM_ENFORCE_LOOPFREE_MFC
96 if (iif_index == oif_index) {
97 /* looped MFC entry */
98 zlog_warn("%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
99 __FILE__, __PRETTY_FUNCTION__,
100 iif_index,
101 oif_index);
102 return -4;
103 }
104#endif
105
106 for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
107 if (s_route->group.s_addr == group.s_addr &&
108 s_route->source.s_addr == source.s_addr) {
109 if (s_route->iif == iif_index &&
110 s_route->oif_ttls[oif_index]) {
111 char gifaddr_str[100];
112 char sifaddr_str[100];
113 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
114 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
115 zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
116 __FILE__, __PRETTY_FUNCTION__,
117 iif_index,
118 oif_index,
119 gifaddr_str,
120 sifaddr_str);
121 return -3;
122 }
123
124 /* Ok, from here on out we will be making changes to the s_route structure, but if
125 * for some reason we fail to commit these changes to the kernel, we want to be able
126 * restore the state of the list. So copy the node data and if need be, we can copy
127 * back if it fails.
128 */
129 original_s_route = static_route_alloc();
130 if (!original_s_route) {
131 return -5;
132 }
133 memcpy(original_s_route, s_route, sizeof(struct static_route));
134
135 /* Route exists and has the same input interface, but adding a new output interface */
136 if (s_route->iif == iif_index) {
137 s_route->oif_ttls[oif_index] = 1;
138 s_route->mc.mfcc_ttls[oif_index] = 1;
139 s_route->creation[oif_index] = pim_time_monotonic_sec();
140 ++s_route->oif_count;
141 } else {
142 /* input interface changed */
143 s_route->iif = iif_index;
144 s_route->mc.mfcc_parent = iif_index;
145
146#ifdef PIM_ENFORCE_LOOPFREE_MFC
147 /* check to make sure the new input was not an old output */
148 if (s_route->oif_ttls[iif_index]) {
149 s_route->oif_ttls[iif_index] = 0;
150 s_route->creation[iif_index] = 0;
151 s_route->mc.mfcc_ttls[iif_index] = 0;
152 --s_route->oif_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->creation[oif_index] = pim_time_monotonic_sec();
160 s_route->mc.mfcc_ttls[oif_index] = 1;
161 ++s_route->oif_count;
162 }
163 }
164
165 break;
166 }
167 }
168
169 /* If node is null then we reached the end of the list without finding a match */
170 if (!node) {
171 s_route = static_route_new(iif_index, oif_index, group, source);
172 listnode_add(qpim_static_route_list, s_route);
173 }
174
175 if (pim_mroute_add(&(s_route->mc)))
176 {
177 char gifaddr_str[100];
178 char sifaddr_str[100];
179 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
180 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
181 zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
182 __FILE__, __PRETTY_FUNCTION__,
183 iif_index,
184 oif_index,
185 gifaddr_str,
186 sifaddr_str);
187
188 /* Need to put s_route back to the way it was */
189 if (original_s_route) {
190 memcpy(s_route, original_s_route, sizeof(struct static_route));
191 } else {
192 /* we never stored off a copy, so it must have been a fresh new route */
193 listnode_delete(qpim_static_route_list, s_route);
194 pim_static_route_free(s_route);
195 }
196
197 return -1;
198 }
199
200 /* Make sure we free the memory for the route copy if used */
201 if (original_s_route) {
202 pim_static_route_free(original_s_route);
203 }
204
205 if (PIM_DEBUG_STATIC) {
206 char gifaddr_str[100];
207 char sifaddr_str[100];
208 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
209 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
210 zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
211 __PRETTY_FUNCTION__,
212 iif_index,
213 oif_index,
214 gifaddr_str,
215 sifaddr_str);
216 }
217
218 return 0;
219}
220
221int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
222{
223 struct listnode *node = 0;
224 struct listnode *nextnode = 0;
225 struct static_route *s_route = 0;
226 struct pim_interface *pim_iif = iif ? iif->info : 0;
227 struct pim_interface *pim_oif = oif ? oif->info : 0;
228 unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
229 unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;
230
231 if (!iif_index || !oif_index) {
232 zlog_warn("%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
233 __FILE__, __PRETTY_FUNCTION__,
234 iif_index,
235 oif_index);
236 return -2;
237 }
238
239 for (ALL_LIST_ELEMENTS(qpim_static_route_list, node, nextnode, s_route)) {
240 if (s_route->iif == iif_index &&
241 s_route->group.s_addr == group.s_addr &&
242 s_route->source.s_addr == source.s_addr &&
243 s_route->oif_ttls[oif_index]) {
244 s_route->oif_ttls[oif_index] = 0;
245 s_route->mc.mfcc_ttls[oif_index] = 0;
246 --s_route->oif_count;
247
248 /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
249 if (s_route->oif_count <= 0 ? pim_mroute_del(&s_route->mc) : pim_mroute_add(&s_route->mc)) {
250 char gifaddr_str[100];
251 char sifaddr_str[100];
252 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
253 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
254 zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
255 __FILE__, __PRETTY_FUNCTION__,
256 iif_index,
257 oif_index,
258 gifaddr_str,
259 sifaddr_str);
260
261 s_route->oif_ttls[oif_index] = 1;
262 s_route->mc.mfcc_ttls[oif_index] = 1;
263 ++s_route->oif_count;
264
265 return -1;
266 }
267
268 s_route->creation[oif_index] = 0;
269
270 if (s_route->oif_count <= 0) {
271 listnode_delete(qpim_static_route_list, s_route);
272 pim_static_route_free(s_route);
273 }
274
275 if (PIM_DEBUG_STATIC) {
276 char gifaddr_str[100];
277 char sifaddr_str[100];
278 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
279 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
280 zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
281 __PRETTY_FUNCTION__,
282 iif_index,
283 oif_index,
284 gifaddr_str,
285 sifaddr_str);
286 }
287
288 break;
289 }
290 }
291
292 if (!node) {
293 char gifaddr_str[100];
294 char sifaddr_str[100];
295 pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
296 pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
297 zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
298 __FILE__, __PRETTY_FUNCTION__,
299 iif_index,
300 oif_index,
301 gifaddr_str,
302 sifaddr_str);
303 return -3;
304 }
305
306 return 0;
307}