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 | |
Jafar Al-Gharaibeh | 030674d | 2015-06-11 18:29:02 -0500 | [diff] [blame] | 25 | #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 | |
| 34 | void pim_static_route_free(struct static_route *s_route) |
| 35 | { |
| 36 | XFREE(MTYPE_PIM_STATIC_ROUTE, s_route); |
| 37 | } |
| 38 | |
| 39 | static 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 | |
| 51 | static 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 | |
| 77 | int 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 | |
| 221 | int 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 | } |