Import Upstream version 1.2.2
[quagga-debian.git] / pimd / pim_rpf.c
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
37 static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up);
38
39 int 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_info("%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
111 static 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
123 enum pim_rpf_result pim_rpf_update(struct pim_upstream *up,
124                                    struct pim_rpf *old_rpf)
125 {
126   struct pim_nexthop  save_nexthop;
127   struct pim_rpf          save_rpf;
128   struct pim_rpf     *rpf = &up->rpf;
129
130   save_nexthop  = rpf->source_nexthop; /* detect change in pim_nexthop */
131   save_rpf = up->rpf;
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) && PIM_DEBUG_PIM_EVENTS) {
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_debug("%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.rpf_addr.s_addr != rpf->rpf_addr.s_addr) {
197
198     /* return old rpf to caller ? */
199     if (old_rpf)
200       *old_rpf = save_rpf;
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 */
223 static 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 }