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