Import Upstream version 1.2.2
[quagga-debian.git] / pimd / pim_hello.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 "pimd.h"
28 #include "pim_pim.h"
29 #include "pim_str.h"
30 #include "pim_tlv.h"
31 #include "pim_util.h"
32 #include "pim_hello.h"
33 #include "pim_iface.h"
34 #include "pim_neighbor.h"
35 #include "pim_upstream.h"
36
37 static void on_trace(const char *label,
38                      struct interface *ifp, struct in_addr src)
39 {
40   if (PIM_DEBUG_PIM_TRACE) {
41     char src_str[100];
42     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
43     zlog_debug("%s: from %s on %s",
44                label, src_str, ifp->name);
45   }
46 }
47
48 static void tlv_trace_bool(const char *label, const char *tlv_name,
49                            const char *ifname, struct in_addr src_addr,
50                            int isset, int value)
51 {
52   if (isset) {
53     char src_str[100];
54     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
55     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
56                label, 
57                src_str, ifname,
58                tlv_name, value);
59   }
60 }
61
62 static void tlv_trace_uint16(const char *label, const char *tlv_name,
63                              const char *ifname, struct in_addr src_addr,
64                              int isset, uint16_t value)
65 {
66   if (isset) {
67     char src_str[100];
68     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
69     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
70                label, 
71                src_str, ifname,
72                tlv_name, value);
73   }
74 }
75
76 static void tlv_trace_uint32(const char *label, const char *tlv_name,
77                              const char *ifname, struct in_addr src_addr,
78                              int isset, uint32_t value)
79 {
80   if (isset) {
81     char src_str[100];
82     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
83     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
84                label, 
85                src_str, ifname,
86                tlv_name, value);
87   }
88 }
89
90 static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
91                                  const char *ifname, struct in_addr src_addr,
92                                  int isset, uint32_t value)
93 {
94   if (isset) {
95     char src_str[100];
96     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
97     zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
98                label, 
99                src_str, ifname,
100                tlv_name, value);
101   }
102 }
103
104 #if 0
105 static void tlv_trace(const char *label, const char *tlv_name,
106                       const char *ifname, struct in_addr src_addr,
107                       int isset)
108 {
109   if (isset) {
110     char src_str[100];
111     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
112     zlog_debug("%s: PIM hello option from %s on interface %s: %s",
113                label, 
114                src_str, ifname,
115                tlv_name);
116   }
117 }
118 #endif
119
120 static void tlv_trace_list(const char *label, const char *tlv_name,
121                            const char *ifname, struct in_addr src_addr,
122                            int isset, struct list *addr_list)
123 {
124   if (isset) {
125     char src_str[100];
126     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
127     zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
128                label, 
129                src_str, ifname,
130                tlv_name,
131                addr_list ? ((int) listcount(addr_list)) : -1,
132                (void *) addr_list);
133   }
134 }
135
136 #define FREE_ADDR_LIST \
137   if (hello_option_addr_list) { \
138     list_delete(hello_option_addr_list); \
139   }
140
141 #define FREE_ADDR_LIST_THEN_RETURN(code) \
142 { \
143   FREE_ADDR_LIST \
144   return (code); \
145 }
146
147 int pim_hello_recv(struct interface *ifp,
148                    struct in_addr src_addr,
149                    uint8_t *tlv_buf, int tlv_buf_size)
150 {
151   struct pim_interface *pim_ifp;
152   struct pim_neighbor *neigh;
153   uint8_t *tlv_curr;
154   uint8_t *tlv_pastend;
155   pim_hello_options hello_options = 0; /* bit array recording options found */
156   uint16_t hello_option_holdtime = 0;
157   uint16_t hello_option_propagation_delay = 0;
158   uint16_t hello_option_override_interval = 0;
159   uint32_t hello_option_dr_priority = 0;
160   uint32_t hello_option_generation_id = 0;
161   struct list *hello_option_addr_list = 0;
162
163   if (PIM_DEBUG_PIM_HELLO)
164     on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
165
166   pim_ifp = ifp->info;
167   zassert(pim_ifp);
168
169   ++pim_ifp->pim_ifstat_hello_recv;
170
171   /*
172     Parse PIM hello TLVs
173    */
174   zassert(tlv_buf_size >= 0);
175   tlv_curr = tlv_buf;
176   tlv_pastend = tlv_buf + tlv_buf_size;
177
178   while (tlv_curr < tlv_pastend) {
179     uint16_t option_type; 
180     uint16_t option_len;
181     int remain = tlv_pastend - tlv_curr;
182
183     if (remain < PIM_TLV_MIN_SIZE) {
184       if (PIM_DEBUG_PIM_HELLO) {
185         char src_str[100];
186         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
187         zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
188                    __PRETTY_FUNCTION__,
189                    remain, PIM_TLV_MIN_SIZE,
190                    src_str, ifp->name);
191       }
192       FREE_ADDR_LIST_THEN_RETURN(-1);
193     }
194
195     option_type = PIM_TLV_GET_TYPE(tlv_curr);
196     tlv_curr += PIM_TLV_TYPE_SIZE;
197     option_len = PIM_TLV_GET_LENGTH(tlv_curr);
198     tlv_curr += PIM_TLV_LENGTH_SIZE;
199
200     if ((tlv_curr + option_len) > tlv_pastend) {
201       if (PIM_DEBUG_PIM_HELLO) {
202         char src_str[100];
203         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
204         zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
205                    __PRETTY_FUNCTION__,
206                    option_type, option_len, tlv_pastend - tlv_curr,
207                    src_str, ifp->name);
208       }
209       FREE_ADDR_LIST_THEN_RETURN(-2);
210     }
211
212     if (PIM_DEBUG_PIM_HELLO) {
213       char src_str[100];
214       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
215       zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
216                  __PRETTY_FUNCTION__,
217                  remain,
218                  option_type, option_len,
219                  src_str, ifp->name);
220     }
221
222     switch (option_type) {
223     case PIM_MSG_OPTION_TYPE_HOLDTIME:
224       if (pim_tlv_parse_holdtime(ifp->name, src_addr,
225                                  &hello_options,
226                                  &hello_option_holdtime,
227                                  option_len,
228                                  tlv_curr)) {
229         FREE_ADDR_LIST_THEN_RETURN(-3);
230       }
231       break;
232     case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
233       if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
234                                         &hello_options,
235                                         &hello_option_propagation_delay,
236                                         &hello_option_override_interval,
237                                         option_len,
238                                         tlv_curr)) {
239         FREE_ADDR_LIST_THEN_RETURN(-4);
240       }
241       break;
242     case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
243       if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
244                                     &hello_options,
245                                     &hello_option_dr_priority,
246                                     option_len,
247                                     tlv_curr)) {
248         FREE_ADDR_LIST_THEN_RETURN(-5);
249       }
250       break;
251     case PIM_MSG_OPTION_TYPE_GENERATION_ID:
252       if (pim_tlv_parse_generation_id(ifp->name, src_addr,
253                                       &hello_options,
254                                       &hello_option_generation_id,
255                                       option_len,
256                                       tlv_curr)) {
257         FREE_ADDR_LIST_THEN_RETURN(-6);
258       }
259       break;
260     case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
261       if (pim_tlv_parse_addr_list(ifp->name, src_addr,
262                                   &hello_options,
263                                   &hello_option_addr_list,
264                                   option_len,
265                                   tlv_curr)) {
266         return -7;
267       }
268       break;
269     case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
270       if (PIM_DEBUG_PIM_HELLO) {
271         char src_str[100];
272         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
273         zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
274                    __PRETTY_FUNCTION__,
275                    option_type, option_len,
276                    src_str, ifp->name);
277       }
278       break;
279     default:
280       if (PIM_DEBUG_PIM_HELLO) {
281         char src_str[100];
282         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
283         zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
284                    __PRETTY_FUNCTION__,
285                    option_type, option_len,
286                    src_str, ifp->name);
287       }
288     }
289
290     tlv_curr += option_len;
291   }
292
293   /*
294     Check received PIM hello options
295   */
296
297   if (PIM_DEBUG_PIM_HELLO) {
298     tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
299                      ifp->name, src_addr,
300                      PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
301                      hello_option_holdtime);
302     tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
303                      ifp->name, src_addr,
304                      PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
305                      hello_option_propagation_delay);
306     tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
307                      ifp->name, src_addr,
308                      PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
309                      hello_option_override_interval);
310     tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
311                    ifp->name, src_addr,
312                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
313                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
314     tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
315                      ifp->name, src_addr,
316                      PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
317                      hello_option_dr_priority);
318     tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
319                          ifp->name, src_addr,
320                          PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
321                          hello_option_generation_id);
322     tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
323                    ifp->name, src_addr,
324                    PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
325                    hello_option_addr_list);
326   }
327
328   if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
329     if (PIM_DEBUG_PIM_HELLO) {
330       char src_str[100];
331       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
332       zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
333                 __PRETTY_FUNCTION__,
334                 src_str, ifp->name);
335     }
336   }
337
338   /*
339     New neighbor?
340   */
341
342   neigh = pim_neighbor_find(ifp, src_addr);
343   if (!neigh) {
344     /* Add as new neighbor */
345     
346     neigh = pim_neighbor_add(ifp, src_addr,
347                              hello_options,
348                              hello_option_holdtime,
349                              hello_option_propagation_delay,
350                              hello_option_override_interval,
351                              hello_option_dr_priority,
352                              hello_option_generation_id,
353                              hello_option_addr_list);
354     if (!neigh) {
355       if (PIM_DEBUG_PIM_HELLO) {
356         char src_str[100];
357         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
358         zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
359                   __PRETTY_FUNCTION__,
360                   src_str, ifp->name);
361       }
362       FREE_ADDR_LIST_THEN_RETURN(-8);
363     }
364
365     /* actual addr list has been saved under neighbor */
366     return 0;
367   }
368
369   /*
370     Received generation ID ?
371   */
372   
373   if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
374     /* GenID mismatch ? */
375     if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
376         (hello_option_generation_id != neigh->generation_id)) {
377
378       /* GenID changed */
379
380       pim_upstream_rpf_genid_changed(neigh->source_addr);
381
382       /* GenID mismatch, then replace neighbor */
383       
384       if (PIM_DEBUG_PIM_HELLO) {
385         char src_str[100];
386         pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
387         zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
388                    __PRETTY_FUNCTION__,
389                    hello_option_generation_id,
390                    neigh->generation_id,
391                    src_str, ifp->name);
392       }
393
394       pim_upstream_rpf_genid_changed(neigh->source_addr);
395       
396       pim_neighbor_delete(ifp, neigh, "GenID mismatch");
397       neigh = pim_neighbor_add(ifp, src_addr,
398                                hello_options,
399                                hello_option_holdtime,
400                                hello_option_propagation_delay,
401                                hello_option_override_interval,
402                                hello_option_dr_priority,
403                                hello_option_generation_id,
404                                hello_option_addr_list);
405       if (!neigh) {
406         if (PIM_DEBUG_PIM_HELLO) {
407           char src_str[100];
408           pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
409           zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
410                      __PRETTY_FUNCTION__,
411                      src_str, ifp->name);
412         }
413         FREE_ADDR_LIST_THEN_RETURN(-9);
414       }
415       /* actual addr list is saved under neighbor */
416       return 0;
417
418     } /* GenId mismatch: replace neighbor */
419     
420   } /* GenId received */
421
422   /*
423     Update existing neighbor
424   */
425
426   pim_neighbor_update(neigh,
427                       hello_options,
428                       hello_option_holdtime,
429                       hello_option_dr_priority,
430                       hello_option_addr_list);
431   /* actual addr list is saved under neighbor */
432   return 0;
433 }
434
435 int pim_hello_build_tlv(const char *ifname,
436                         uint8_t *tlv_buf, int tlv_buf_size,
437                         uint16_t holdtime,
438                         uint32_t dr_priority,
439                         uint32_t generation_id,
440                         uint16_t propagation_delay,
441                         uint16_t override_interval,
442                         int can_disable_join_suppression,
443                         struct list *ifconnected)
444 {
445   uint8_t *curr = tlv_buf;
446   uint8_t *pastend = tlv_buf + tlv_buf_size;
447   uint8_t *tmp;
448
449   /*
450    * Append options
451    */
452
453   /* Holdtime */
454   curr = pim_tlv_append_uint16(curr,
455                                pastend,
456                                PIM_MSG_OPTION_TYPE_HOLDTIME,
457                                holdtime);
458   if (!curr) {
459     if (PIM_DEBUG_PIM_HELLO) {
460       zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
461                  __PRETTY_FUNCTION__, ifname);
462     }
463     return -1;
464   }
465
466   /* LAN Prune Delay */
467   tmp = pim_tlv_append_2uint16(curr,
468                                pastend,
469                                PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
470                                propagation_delay,
471                                override_interval);
472   if (!tmp) {
473     if (PIM_DEBUG_PIM_HELLO) {
474       zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
475                  __PRETTY_FUNCTION__, ifname);
476     }
477     return -1;
478   }
479   if (can_disable_join_suppression) {
480     *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
481   }
482   curr = tmp;
483
484   /* DR Priority */
485   curr = pim_tlv_append_uint32(curr,
486                                pastend,
487                                PIM_MSG_OPTION_TYPE_DR_PRIORITY,
488                                dr_priority);
489   if (!curr) {
490     if (PIM_DEBUG_PIM_HELLO) {
491       zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
492                  __PRETTY_FUNCTION__, ifname);
493     }
494     return -2;
495   }
496
497   /* Generation ID */
498   curr = pim_tlv_append_uint32(curr,
499                                pastend,
500                                PIM_MSG_OPTION_TYPE_GENERATION_ID,
501                                generation_id);
502   if (!curr) {
503     if (PIM_DEBUG_PIM_HELLO) {
504       zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
505                  __PRETTY_FUNCTION__, ifname);
506     }
507     return -3;
508   }
509
510   /* Secondary Address List */
511   if (ifconnected) {
512     curr = pim_tlv_append_addrlist_ucast(curr,
513                                          pastend,
514                                          ifconnected);
515     if (!curr) {
516       if (PIM_DEBUG_PIM_HELLO) {
517         zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
518                   __PRETTY_FUNCTION__, ifname);
519       }
520       return -4;
521     }
522   }
523
524   return curr - tlv_buf;
525 }
526
527 /*
528   RFC 4601: 4.3.1.  Sending Hello Messages
529
530   Thus, if a router needs to send a Join/Prune or Assert message on an
531   interface on which it has not yet sent a Hello message with the
532   currently configured IP address, then it MUST immediately send the
533   relevant Hello message without waiting for the Hello Timer to
534   expire, followed by the Join/Prune or Assert message.
535 */
536 void pim_hello_require(struct interface *ifp)
537 {
538   struct pim_interface *pim_ifp;
539
540   zassert(ifp);
541
542   pim_ifp = ifp->info;
543
544   zassert(pim_ifp);
545
546   if (pim_ifp->pim_ifstat_hello_sent)
547     return;
548
549   pim_hello_restart_now(ifp); /* Send hello and restart timer */
550 }