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