3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
32 #include "pim_hello.h"
33 #include "pim_iface.h"
34 #include "pim_neighbor.h"
35 #include "pim_upstream.h"
37 static void on_trace(const char *label,
38 struct interface *ifp, struct in_addr src)
40 if (PIM_DEBUG_PIM_TRACE) {
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);
48 static void tlv_trace_bool(const char *label, const char *tlv_name,
49 const char *ifname, struct in_addr src_addr,
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",
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)
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",
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)
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",
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)
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",
105 static void tlv_trace(const char *label, const char *tlv_name,
106 const char *ifname, struct in_addr src_addr,
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",
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)
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",
131 addr_list ? ((int) listcount(addr_list)) : -1,
136 #define FREE_ADDR_LIST \
137 if (hello_option_addr_list) { \
138 list_delete(hello_option_addr_list); \
141 #define FREE_ADDR_LIST_THEN_RETURN(code) \
147 int pim_hello_recv(struct interface *ifp,
148 struct in_addr src_addr,
149 uint8_t *tlv_buf, int tlv_buf_size)
151 struct pim_interface *pim_ifp;
152 struct pim_neighbor *neigh;
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;
163 if (PIM_DEBUG_PIM_HELLO)
164 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
169 ++pim_ifp->pim_ifstat_hello_recv;
174 zassert(tlv_buf_size >= 0);
176 tlv_pastend = tlv_buf + tlv_buf_size;
178 while (tlv_curr < tlv_pastend) {
179 uint16_t option_type;
181 int remain = tlv_pastend - tlv_curr;
183 if (remain < PIM_TLV_MIN_SIZE) {
184 if (PIM_DEBUG_PIM_HELLO) {
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",
189 remain, PIM_TLV_MIN_SIZE,
192 FREE_ADDR_LIST_THEN_RETURN(-1);
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;
200 if ((tlv_curr + option_len) > tlv_pastend) {
201 if (PIM_DEBUG_PIM_HELLO) {
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",
206 option_type, option_len, tlv_pastend - tlv_curr,
209 FREE_ADDR_LIST_THEN_RETURN(-2);
212 if (PIM_DEBUG_PIM_HELLO) {
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",
218 option_type, option_len,
222 switch (option_type) {
223 case PIM_MSG_OPTION_TYPE_HOLDTIME:
224 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
226 &hello_option_holdtime,
229 FREE_ADDR_LIST_THEN_RETURN(-3);
232 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
233 if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
235 &hello_option_propagation_delay,
236 &hello_option_override_interval,
239 FREE_ADDR_LIST_THEN_RETURN(-4);
242 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
243 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
245 &hello_option_dr_priority,
248 FREE_ADDR_LIST_THEN_RETURN(-5);
251 case PIM_MSG_OPTION_TYPE_GENERATION_ID:
252 if (pim_tlv_parse_generation_id(ifp->name, src_addr,
254 &hello_option_generation_id,
257 FREE_ADDR_LIST_THEN_RETURN(-6);
260 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
261 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
263 &hello_option_addr_list,
269 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
270 if (PIM_DEBUG_PIM_HELLO) {
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",
275 option_type, option_len,
280 if (PIM_DEBUG_PIM_HELLO) {
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",
285 option_type, option_len,
290 tlv_curr += option_len;
294 Check received PIM hello options
297 if (PIM_DEBUG_PIM_HELLO) {
298 tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
300 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
301 hello_option_holdtime);
302 tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
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",
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",
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",
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",
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",
324 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
325 hello_option_addr_list);
328 if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
329 if (PIM_DEBUG_PIM_HELLO) {
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",
342 neigh = pim_neighbor_find(ifp, src_addr);
344 /* Add as new neighbor */
346 neigh = pim_neighbor_add(ifp, src_addr,
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);
355 if (PIM_DEBUG_PIM_HELLO) {
357 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
358 zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
362 FREE_ADDR_LIST_THEN_RETURN(-8);
365 /* actual addr list has been saved under neighbor */
370 Received generation ID ?
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)) {
380 pim_upstream_rpf_genid_changed(neigh->source_addr);
382 /* GenID mismatch, then replace neighbor */
384 if (PIM_DEBUG_PIM_HELLO) {
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",
389 hello_option_generation_id,
390 neigh->generation_id,
394 pim_upstream_rpf_genid_changed(neigh->source_addr);
396 pim_neighbor_delete(ifp, neigh, "GenID mismatch");
397 neigh = pim_neighbor_add(ifp, src_addr,
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);
406 if (PIM_DEBUG_PIM_HELLO) {
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",
413 FREE_ADDR_LIST_THEN_RETURN(-9);
415 /* actual addr list is saved under neighbor */
418 } /* GenId mismatch: replace neighbor */
420 } /* GenId received */
423 Update existing neighbor
426 pim_neighbor_update(neigh,
428 hello_option_holdtime,
429 hello_option_dr_priority,
430 hello_option_addr_list);
431 /* actual addr list is saved under neighbor */
435 int pim_hello_build_tlv(const char *ifname,
436 uint8_t *tlv_buf, int tlv_buf_size,
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)
445 uint8_t *curr = tlv_buf;
446 uint8_t *pastend = tlv_buf + tlv_buf_size;
454 curr = pim_tlv_append_uint16(curr,
456 PIM_MSG_OPTION_TYPE_HOLDTIME,
459 if (PIM_DEBUG_PIM_HELLO) {
460 zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
461 __PRETTY_FUNCTION__, ifname);
466 /* LAN Prune Delay */
467 tmp = pim_tlv_append_2uint16(curr,
469 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
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);
479 if (can_disable_join_suppression) {
480 *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
485 curr = pim_tlv_append_uint32(curr,
487 PIM_MSG_OPTION_TYPE_DR_PRIORITY,
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);
498 curr = pim_tlv_append_uint32(curr,
500 PIM_MSG_OPTION_TYPE_GENERATION_ID,
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);
510 /* Secondary Address List */
512 curr = pim_tlv_append_addrlist_ucast(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);
524 return curr - tlv_buf;
528 RFC 4601: 4.3.1. Sending Hello Messages
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.
536 void pim_hello_require(struct interface *ifp)
538 struct pim_interface *pim_ifp;
546 if (pim_ifp->pim_ifstat_hello_sent)
549 pim_hello_restart_now(ifp); /* Send hello and restart timer */