]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - pimd/pim_macro.c
New upstream version 1.2.4
[quagga-debian.git] / pimd / pim_macro.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
27 #include "pim_macro.h"
28 #include "pimd.h"
29 #include "pim_str.h"
30 #include "pim_iface.h"
31 #include "pim_ifchannel.h"
32
33 #define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
34
35 /*
36   DownstreamJPState(S,G,I) is the per-interface state machine for
37   receiving (S,G) Join/Prune messages.
38
39   DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
40 */
41 static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
42 {
43   return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
44 }
45
46 /*
47   The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
48   module or other local membership mechanism has determined that local
49   members on interface I desire to receive traffic sent specifically
50   by S to G.
51 */
52 static int local_receiver_include(const struct pim_ifchannel *ch)
53 {
54   /* local_receiver_include(S,G,I) ? */
55   return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
56 }
57
58 /*
59   RFC 4601: 4.1.6.  State Summarization Macros
60
61    The set "joins(S,G)" is the set of all interfaces on which the
62    router has received (S,G) Joins:
63
64    joins(S,G) =
65        { all interfaces I such that
66          DownstreamJPState(S,G,I) is either Join or Prune-Pending }
67
68   DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
69 */
70 int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
71 {
72   return downstream_jpstate_isjoined(ch);
73 }
74
75 /*
76   RFC 4601: 4.6.5.  Assert State Macros
77
78    The set "lost_assert(S,G)" is the set of all interfaces on which the
79    router has received (S,G) joins but has lost an (S,G) assert.
80
81    lost_assert(S,G) =
82        { all interfaces I such that
83          lost_assert(S,G,I) == TRUE }
84
85      bool lost_assert(S,G,I) {
86        if ( RPF_interface(S) == I ) {
87           return FALSE
88        } else {
89           return ( AssertWinner(S,G,I) != NULL AND
90                    AssertWinner(S,G,I) != me  AND
91                    (AssertWinnerMetric(S,G,I) is better
92                       than spt_assert_metric(S,I) )
93        }
94      }
95
96   AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
97   packet that won an Assert.
98 */
99 int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
100 {
101   struct interface *ifp;
102   struct pim_interface *pim_ifp;
103   struct pim_assert_metric spt_assert_metric;
104
105   ifp = ch->interface;
106   if (!ifp) {
107     char src_str[100];
108     char grp_str[100];
109     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
110     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
111     zlog_warn("%s: (S,G)=(%s,%s): null interface",
112               __PRETTY_FUNCTION__,
113               src_str, grp_str);
114     return 0; /* false */
115   }
116
117   /* RPF_interface(S) == I ? */
118   if (ch->upstream->rpf.source_nexthop.interface == ifp)
119     return 0; /* false */
120
121   pim_ifp = ifp->info;
122   if (!pim_ifp) {
123     char src_str[100];
124     char grp_str[100];
125     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
126     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
127     zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
128               __PRETTY_FUNCTION__,
129               src_str, grp_str, ifp->name);
130     return 0; /* false */
131   }
132
133   if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
134     return 0; /* false */
135
136   /* AssertWinner(S,G,I) == me ? */
137   if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
138     return 0; /* false */
139
140   spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf,
141                                                   pim_ifp->primary_address);
142
143   return pim_assert_metric_better(&ch->ifassert_winner_metric,
144                                   &spt_assert_metric);
145 }
146
147 /*
148   RFC 4601: 4.1.6.  State Summarization Macros
149
150    pim_include(S,G) =
151        { all interfaces I such that:
152          ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
153            OR AssertWinner(S,G,I) == me )
154           AND  local_receiver_include(S,G,I) }
155
156    AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
157    packet that won an Assert.
158 */
159 int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
160 {
161   struct pim_interface *pim_ifp = ch->interface->info;
162
163   if (!pim_ifp) {
164     char src_str[100];
165     char grp_str[100];
166     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
167     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
168     zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
169               __PRETTY_FUNCTION__,
170               src_str, grp_str, ch->interface->name);
171     return 0; /* false */
172   }
173
174   /* local_receiver_include(S,G,I) ? */
175   if (!local_receiver_include(ch))
176     return 0; /* false */
177     
178   /* OR AssertWinner(S,G,I) == me ? */
179   if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
180     return 1; /* true */
181     
182   return (
183           /* I_am_DR( I ) ? */
184           PIM_IFP_I_am_DR(pim_ifp)
185           &&
186           /* lost_assert(S,G,I) == FALSE ? */
187           (!pim_macro_ch_lost_assert(ch))
188           );
189 }
190
191 int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
192 {
193   if (pim_macro_chisin_joins(ch))
194     return 1; /* true */
195
196   return pim_macro_chisin_pim_include(ch);
197 }
198
199 /*
200   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
201
202   CouldAssert(S,G,I) =
203   SPTbit(S,G)==TRUE
204   AND (RPF_interface(S) != I)
205   AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
206                  (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
207                  (-) lost_assert(*,G)
208                  (+) joins(S,G) (+) pim_include(S,G) ) )
209
210   CouldAssert(S,G,I) is true for downstream interfaces that would be in
211   the inherited_olist(S,G) if (S,G) assert information was not taken
212   into account.
213
214   CouldAssert(S,G,I) may be affected by changes in the following:
215
216   pim_ifp->primary_address
217   pim_ifp->pim_dr_addr
218   ch->ifassert_winner_metric
219   ch->ifassert_winner
220   ch->local_ifmembership
221   ch->ifjoin_state
222   ch->upstream->rpf.source_nexthop.mrib_metric_preference
223   ch->upstream->rpf.source_nexthop.mrib_route_metric
224   ch->upstream->rpf.source_nexthop.interface
225 */
226 int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
227 {
228   struct interface *ifp;
229
230   /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
231
232   ifp = ch->interface;
233   if (!ifp) {
234     char src_str[100];
235     char grp_str[100];
236     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
237     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
238     zlog_warn("%s: (S,G)=(%s,%s): null interface",
239               __PRETTY_FUNCTION__,
240               src_str, grp_str);
241     return 0; /* false */
242   }
243
244   /* RPF_interface(S) != I ? */
245   if (ch->upstream->rpf.source_nexthop.interface == ifp)
246     return 0; /* false */
247
248   /* I in joins(S,G) (+) pim_include(S,G) ? */
249   return pim_macro_chisin_joins_or_include(ch);
250 }
251
252 /*
253   RFC 4601: 4.6.3.  Assert Metrics
254
255    spt_assert_metric(S,I) gives the assert metric we use if we're
256    sending an assert based on active (S,G) forwarding state:
257
258     assert_metric
259     spt_assert_metric(S,I) {
260       return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
261     }
262 */
263 struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
264                                                      struct in_addr ifaddr)
265 {
266   struct pim_assert_metric metric;
267
268   metric.rpt_bit_flag      = 0;
269   metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
270   metric.route_metric      = rpf->source_nexthop.mrib_route_metric;
271   metric.ip_address        = ifaddr;
272
273   return metric;
274 }
275
276 /*
277   RFC 4601: 4.6.3.  Assert Metrics
278
279    An assert metric for (S,G) to include in (or compare against) an
280    Assert message sent on interface I should be computed using the
281    following pseudocode:
282
283   assert_metric  my_assert_metric(S,G,I) {
284     if( CouldAssert(S,G,I) == TRUE ) {
285       return spt_assert_metric(S,I)
286     } else if( CouldAssert(*,G,I) == TRUE ) {
287       return rpt_assert_metric(G,I)
288     } else {
289       return infinite_assert_metric()
290     }
291   }
292 */
293 struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
294 {
295   struct pim_interface *pim_ifp;
296
297   pim_ifp = ch->interface->info;
298
299   if (pim_ifp) {
300     if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
301       return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
302     }
303   }
304
305   return qpim_infinite_assert_metric;
306 }
307
308 /*
309   RFC 4601 4.2.  Data Packet Forwarding Rules
310   RFC 4601 4.8.2.  PIM-SSM-Only Routers
311   
312   Macro:
313   inherited_olist(S,G) =
314     joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
315 */
316 static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
317 {
318   if (pim_macro_ch_lost_assert(ch))
319     return 0; /* false */
320
321   return pim_macro_chisin_joins_or_include(ch);
322 }
323
324 /*
325   RFC 4601 4.2.  Data Packet Forwarding Rules
326   RFC 4601 4.8.2.  PIM-SSM-Only Routers
327
328   Additionally, the Packet forwarding rules of Section 4.2 can be
329   simplified in a PIM-SSM-only router:
330   
331   iif is the incoming interface of the packet.
332   oiflist = NULL
333   if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
334     oiflist = inherited_olist(S,G)
335   } else if (iif is in inherited_olist(S,G)) {
336     send Assert(S,G) on iif
337   }
338   oiflist = oiflist (-) iif
339   forward packet on all interfaces in oiflist
340   
341   Macro:
342   inherited_olist(S,G) =
343     joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
344
345   Note:
346   - The following test is performed as response to WRONGVIF kernel
347     upcall:
348     if (iif is in inherited_olist(S,G)) {
349       send Assert(S,G) on iif
350     }
351     See pim_mroute.c mroute_msg().
352 */
353 int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
354 {
355   if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
356     /* oiflist is NULL */
357     return 0; /* false */
358   }
359
360   /* oiflist = oiflist (-) iif */
361   if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
362     return 0; /* false */
363
364   return pim_macro_chisin_inherited_olist(ch);
365 }
366
367 /*
368   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
369
370   AssertTrackingDesired(S,G,I) =
371   (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
372         (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
373         (-) lost_assert(*,G)
374         (+) joins(S,G) ) )
375      OR (local_receiver_include(S,G,I) == TRUE
376          AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
377      OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE))
378      OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE)
379          AND (SPTbit(S,G) == FALSE))
380
381   AssertTrackingDesired(S,G,I) is true on any interface in which an
382   (S,G) assert might affect our behavior.
383 */
384 int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
385 {
386   struct pim_interface *pim_ifp;
387   struct interface *ifp;
388
389   ifp = ch->interface;
390   if (!ifp) {
391     char src_str[100];
392     char grp_str[100];
393     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
394     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
395     zlog_warn("%s: (S,G)=(%s,%s): null interface",
396               __PRETTY_FUNCTION__,
397               src_str, grp_str);
398     return 0; /* false */
399   }
400
401   pim_ifp = ifp->info;
402   if (!pim_ifp) {
403     char src_str[100];
404     char grp_str[100];
405     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
406     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
407     zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
408               __PRETTY_FUNCTION__,
409               src_str, grp_str, ch->interface->name);
410     return 0; /* false */
411   }
412
413   /* I in joins(S,G) ? */
414   if (pim_macro_chisin_joins(ch))
415     return 1; /* true */
416
417   /* local_receiver_include(S,G,I) ? */
418   if (local_receiver_include(ch)) {
419     /* I_am_DR(I) ? */
420     if (PIM_IFP_I_am_DR(pim_ifp))
421       return 1; /* true */
422
423     /* AssertWinner(S,G,I) == me ? */
424     if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
425       return 1; /* true */
426   }
427
428   /* RPF_interface(S) == I ? */
429   if (ch->upstream->rpf.source_nexthop.interface == ifp) {
430     /* JoinDesired(S,G) ? */
431     if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
432       return 1; /* true */
433   }
434
435   return 0; /* false */
436 }
437