Jafar Al-Gharaibeh | 030674d | 2015-06-11 18:29:02 -0500 | [diff] [blame] | 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 | |
David Lamparter | 821df2c | 2015-09-15 01:53:09 -0700 | [diff] [blame] | 23 | #include <zebra.h> |
| 24 | |
Donald Sharp | 18343ee | 2016-06-02 02:30:46 -0400 | [diff] [blame] | 25 | #include "vty.h" |
| 26 | |
Jafar Al-Gharaibeh | 030674d | 2015-06-11 18:29:02 -0500 | [diff] [blame] | 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) { |
Paul Jakma | ffe7944 | 2015-10-29 14:24:13 +0000 | [diff] [blame] | 47 | zlog_err("PIM XCALLOC(%zu) failure", sizeof(*s_route)); |
Jafar Al-Gharaibeh | 030674d | 2015-06-11 18:29:02 -0500 | [diff] [blame] | 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 | return -1; |
| 200 | } |
| 201 | |
| 202 | /* Make sure we free the memory for the route copy if used */ |
| 203 | if (original_s_route) { |
| 204 | pim_static_route_free(original_s_route); |
| 205 | } |
| 206 | |
| 207 | if (PIM_DEBUG_STATIC) { |
| 208 | char gifaddr_str[100]; |
| 209 | char sifaddr_str[100]; |
| 210 | pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str)); |
| 211 | pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str)); |
| 212 | zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)", |
| 213 | __PRETTY_FUNCTION__, |
| 214 | iif_index, |
| 215 | oif_index, |
| 216 | gifaddr_str, |
| 217 | sifaddr_str); |
| 218 | } |
| 219 | |
| 220 | return 0; |
| 221 | } |
| 222 | |
| 223 | int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source) |
| 224 | { |
| 225 | struct listnode *node = 0; |
| 226 | struct listnode *nextnode = 0; |
| 227 | struct static_route *s_route = 0; |
| 228 | struct pim_interface *pim_iif = iif ? iif->info : 0; |
| 229 | struct pim_interface *pim_oif = oif ? oif->info : 0; |
| 230 | unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; |
| 231 | unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; |
| 232 | |
| 233 | if (!iif_index || !oif_index) { |
| 234 | zlog_warn("%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)", |
| 235 | __FILE__, __PRETTY_FUNCTION__, |
| 236 | iif_index, |
| 237 | oif_index); |
| 238 | return -2; |
| 239 | } |
| 240 | |
| 241 | for (ALL_LIST_ELEMENTS(qpim_static_route_list, node, nextnode, s_route)) { |
| 242 | if (s_route->iif == iif_index && |
| 243 | s_route->group.s_addr == group.s_addr && |
| 244 | s_route->source.s_addr == source.s_addr && |
| 245 | s_route->oif_ttls[oif_index]) { |
| 246 | s_route->oif_ttls[oif_index] = 0; |
| 247 | s_route->mc.mfcc_ttls[oif_index] = 0; |
| 248 | --s_route->oif_count; |
| 249 | |
| 250 | /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */ |
| 251 | if (s_route->oif_count <= 0 ? pim_mroute_del(&s_route->mc) : pim_mroute_add(&s_route->mc)) { |
| 252 | char gifaddr_str[100]; |
| 253 | char sifaddr_str[100]; |
| 254 | pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str)); |
| 255 | pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str)); |
| 256 | zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)", |
| 257 | __FILE__, __PRETTY_FUNCTION__, |
| 258 | iif_index, |
| 259 | oif_index, |
| 260 | gifaddr_str, |
| 261 | sifaddr_str); |
| 262 | |
| 263 | s_route->oif_ttls[oif_index] = 1; |
| 264 | s_route->mc.mfcc_ttls[oif_index] = 1; |
| 265 | ++s_route->oif_count; |
| 266 | |
| 267 | return -1; |
| 268 | } |
| 269 | |
| 270 | s_route->creation[oif_index] = 0; |
| 271 | |
| 272 | if (s_route->oif_count <= 0) { |
| 273 | listnode_delete(qpim_static_route_list, s_route); |
| 274 | pim_static_route_free(s_route); |
| 275 | } |
| 276 | |
| 277 | if (PIM_DEBUG_STATIC) { |
| 278 | char gifaddr_str[100]; |
| 279 | char sifaddr_str[100]; |
| 280 | pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str)); |
| 281 | pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str)); |
| 282 | zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)", |
| 283 | __PRETTY_FUNCTION__, |
| 284 | iif_index, |
| 285 | oif_index, |
| 286 | gifaddr_str, |
| 287 | sifaddr_str); |
| 288 | } |
| 289 | |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | if (!node) { |
| 295 | char gifaddr_str[100]; |
| 296 | char sifaddr_str[100]; |
| 297 | pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str)); |
| 298 | pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str)); |
| 299 | zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)", |
| 300 | __FILE__, __PRETTY_FUNCTION__, |
| 301 | iif_index, |
| 302 | oif_index, |
| 303 | gifaddr_str, |
| 304 | sifaddr_str); |
| 305 | return -3; |
| 306 | } |
| 307 | |
| 308 | return 0; |
| 309 | } |
Donald Sharp | 18343ee | 2016-06-02 02:30:46 -0400 | [diff] [blame] | 310 | |
| 311 | int |
| 312 | pim_static_write_mroute (struct vty *vty, struct interface *ifp) |
| 313 | { |
| 314 | struct listnode *node; |
| 315 | struct static_route *sroute; |
| 316 | int count = 0; |
| 317 | char sbuf[100]; |
| 318 | char gbuf[100]; |
| 319 | |
| 320 | for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute)) |
| 321 | { |
| 322 | pim_inet4_dump ("<ifaddr?>", sroute->group, gbuf, sizeof (gbuf)); |
| 323 | pim_inet4_dump ("<ifaddr?>", sroute->source, sbuf, sizeof (sbuf)); |
| 324 | if (sroute->iif == ifp->ifindex) |
| 325 | { |
| 326 | int i; |
| 327 | for (i = 0; i < MAXVIFS; i++) |
| 328 | if (sroute->oif_ttls[i]) |
| 329 | { |
| 330 | struct interface *oifp = if_lookup_by_index (i); |
| 331 | vty_out (vty, " ip mroute %s %s %s%s", oifp->name, gbuf, sbuf, VTY_NEWLINE); |
| 332 | count ++; |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | return count; |
| 338 | } |