1/* 2 PIM for Quagga 3 Copyright (C) 2008 Everton da Silva Marques 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 "log.h" 26#include "prefix.h" 27#include "memory.h" 28 29#include "pimd.h" 30#include "pim_rpf.h" 31#include "pim_pim.h" 32#include "pim_str.h" 33#include "pim_iface.h" 34#include "pim_zlookup.h" 35#include "pim_ifchannel.h" 36 37static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); 38 39int pim_nexthop_lookup(struct pim_nexthop *nexthop, 40 struct in_addr addr) 41{ 42 struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; 43 int num_ifindex; 44 struct interface *ifp; 45 int first_ifindex; 46 47 num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, 48 PIM_NEXTHOP_IFINDEX_TAB_SIZE, 49 addr, PIM_NEXTHOP_LOOKUP_MAX); 50 if (num_ifindex < 1) { 51 char addr_str[100]; 52 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 53 zlog_warn("%s %s: could not find nexthop ifindex for address %s", 54 __FILE__, __PRETTY_FUNCTION__, 55 addr_str); 56 return -1; 57 } 58 59 first_ifindex = nexthop_tab[0].ifindex; 60 61 if (num_ifindex > 1) { 62 char addr_str[100]; 63 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 64 zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", 65 __FILE__, __PRETTY_FUNCTION__, 66 num_ifindex, addr_str, first_ifindex); 67 /* debug warning only, do not return */ 68 } 69 70 ifp = if_lookup_by_index(first_ifindex); 71 if (!ifp) { 72 char addr_str[100]; 73 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 74 zlog_warn("%s %s: could not find interface for ifindex %d (address %s)", 75 __FILE__, __PRETTY_FUNCTION__, 76 first_ifindex, addr_str); 77 return -2; 78 } 79 80 if (!ifp->info) { 81 char addr_str[100]; 82 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 83 zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", 84 __PRETTY_FUNCTION__, 85 ifp->name, first_ifindex, addr_str); 86 /* debug warning only, do not return */ 87 } 88 89 if (PIM_DEBUG_PIM_TRACE) { 90 char nexthop_str[100]; 91 char addr_str[100]; 92 pim_inet4_dump("<nexthop?>", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); 93 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 94 zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", 95 __FILE__, __PRETTY_FUNCTION__, 96 nexthop_str, addr_str, 97 ifp->name, first_ifindex, 98 nexthop_tab[0].route_metric, 99 nexthop_tab[0].protocol_distance); 100 } 101 102 /* update nextop data */ 103 nexthop->interface = ifp; 104 nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr; 105 nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance; 106 nexthop->mrib_route_metric = nexthop_tab[0].route_metric; 107 108 return 0; 109} 110 111static int nexthop_mismatch(const struct pim_nexthop *nh1, 112 const struct pim_nexthop *nh2) 113{ 114 return (nh1->interface != nh2->interface) 115 || 116 (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr) 117 || 118 (nh1->mrib_metric_preference != nh2->mrib_metric_preference) 119 || 120 (nh1->mrib_route_metric != nh2->mrib_route_metric); 121} 122 123enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, 124 struct in_addr *old_rpf_addr) 125{ 126 struct in_addr save_rpf_addr; 127 struct pim_nexthop save_nexthop; 128 struct pim_rpf *rpf = &up->rpf; 129 130 save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */ 131 save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */ 132 133 if (pim_nexthop_lookup(&rpf->source_nexthop, 134 up->source_addr)) { 135 return PIM_RPF_FAILURE; 136 } 137 138 rpf->rpf_addr = pim_rpf_find_rpf_addr(up); 139 if (PIM_INADDR_IS_ANY(rpf->rpf_addr)) { 140 /* RPF'(S,G) not found */ 141 char src_str[100]; 142 char grp_str[100]; 143 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str)); 144 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str)); 145 zlog_warn("%s %s: RPF'(%s,%s) not found: won't send join upstream", 146 __FILE__, __PRETTY_FUNCTION__, 147 src_str, grp_str); 148 /* warning only */ 149 } 150 151 /* detect change in pim_nexthop */ 152 if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) { 153 154 /* if (PIM_DEBUG_PIM_EVENTS) */ { 155 char src_str[100]; 156 char grp_str[100]; 157 char nhaddr_str[100]; 158 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str)); 159 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str)); 160 pim_inet4_dump("<addr?>", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); 161 zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", 162 __FILE__, __PRETTY_FUNCTION__, 163 src_str, grp_str, 164 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>", 165 nhaddr_str, 166 rpf->source_nexthop.mrib_metric_preference, 167 rpf->source_nexthop.mrib_route_metric); 168 /* warning only */ 169 } 170 171 pim_upstream_update_join_desired(up); 172 pim_upstream_update_could_assert(up); 173 pim_upstream_update_my_assert_metric(up); 174 } 175 176 /* detect change in RPF_interface(S) */ 177 if (save_nexthop.interface != rpf->source_nexthop.interface) { 178 179 /* if (PIM_DEBUG_PIM_EVENTS) */ { 180 char src_str[100]; 181 char grp_str[100]; 182 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str)); 183 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str)); 184 zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", 185 __FILE__, __PRETTY_FUNCTION__, 186 src_str, grp_str, 187 save_nexthop.interface ? save_nexthop.interface->name : "<oldif?>", 188 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>"); 189 /* warning only */ 190 } 191 192 pim_upstream_rpf_interface_changed(up, save_nexthop.interface); 193 } 194 195 /* detect change in RPF'(S,G) */ 196 if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) { 197 198 /* return old rpf to caller ? */ 199 if (old_rpf_addr) 200 *old_rpf_addr = save_rpf_addr; 201 202 return PIM_RPF_CHANGED; 203 } 204 205 return PIM_RPF_OK; 206} 207 208/* 209 RFC 4601: 4.1.6. State Summarization Macros 210 211 neighbor RPF'(S,G) { 212 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) { 213 return AssertWinner(S, G, RPF_interface(S) ) 214 } else { 215 return NBR( RPF_interface(S), MRIB.next_hop( S ) ) 216 } 217 } 218 219 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data 220 packets should be coming and to which joins should be sent on the RP 221 tree and SPT, respectively. 222*/ 223static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) 224{ 225 struct pim_ifchannel *rpf_ch; 226 struct pim_neighbor *neigh; 227 struct in_addr rpf_addr; 228 229 if (!up->rpf.source_nexthop.interface) { 230 char src_str[100]; 231 char grp_str[100]; 232 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str)); 233 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str)); 234 zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)", 235 __PRETTY_FUNCTION__, 236 src_str, grp_str); 237 238 rpf_addr.s_addr = PIM_NET_INADDR_ANY; 239 return rpf_addr; 240 } 241 242 rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface, 243 up->source_addr, up->group_addr); 244 if (rpf_ch) { 245 if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { 246 return rpf_ch->ifassert_winner; 247 } 248 } 249 250 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */ 251 252 neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface, 253 up->rpf.source_nexthop.mrib_nexthop_addr); 254 if (neigh) 255 rpf_addr = neigh->source_addr; 256 else 257 rpf_addr.s_addr = PIM_NET_INADDR_ANY; 258 259 return rpf_addr; 260} 261