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$ $
33 #include "pim_hello.h"
34 #include "pim_iface.h"
35 #include "pim_neighbor.h"
36 #include "pim_upstream.h"
38 static void on_trace(const char *label
,
39 struct interface
*ifp
, struct in_addr src
)
41 if (PIM_DEBUG_PIM_TRACE
) {
43 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
44 zlog_debug("%s: from %s on %s",
45 label
, src_str
, ifp
->name
);
49 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
50 const char *ifname
, struct in_addr src_addr
,
55 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
56 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
63 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
64 const char *ifname
, struct in_addr src_addr
,
65 int isset
, uint16_t value
)
69 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
70 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
77 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
78 const char *ifname
, struct in_addr src_addr
,
79 int isset
, uint32_t value
)
83 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
84 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
91 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
92 const char *ifname
, struct in_addr src_addr
,
93 int isset
, uint32_t value
)
97 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
98 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
106 static void tlv_trace(const char *label
, const char *tlv_name
,
107 const char *ifname
, struct in_addr src_addr
,
112 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
113 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
121 static void tlv_trace_list(const char *label
, const char *tlv_name
,
122 const char *ifname
, struct in_addr src_addr
,
123 int isset
, struct list
*addr_list
)
127 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
128 zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
132 addr_list
? ((int) listcount(addr_list
)) : -1,
137 #define FREE_ADDR_LIST \
138 if (hello_option_addr_list) { \
139 list_delete(hello_option_addr_list); \
142 #define FREE_ADDR_LIST_THEN_RETURN(code) \
148 int pim_hello_recv(struct interface
*ifp
,
149 struct in_addr src_addr
,
150 uint8_t *tlv_buf
, int tlv_buf_size
)
152 struct pim_interface
*pim_ifp
;
153 struct pim_neighbor
*neigh
;
155 uint8_t *tlv_pastend
;
156 pim_hello_options hello_options
= 0; /* bit array recording options found */
157 uint16_t hello_option_holdtime
= 0;
158 uint16_t hello_option_propagation_delay
= 0;
159 uint16_t hello_option_override_interval
= 0;
160 uint32_t hello_option_dr_priority
= 0;
161 uint32_t hello_option_generation_id
= 0;
162 struct list
*hello_option_addr_list
= 0;
164 if (PIM_DEBUG_PIM_HELLO
)
165 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
170 ++pim_ifp
->pim_ifstat_hello_recv
;
175 zassert(tlv_buf_size
>= 0);
177 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
179 while (tlv_curr
< tlv_pastend
) {
180 uint16_t option_type
;
182 int remain
= tlv_pastend
- tlv_curr
;
184 if (remain
< PIM_TLV_MIN_SIZE
) {
185 if (PIM_DEBUG_PIM_HELLO
) {
187 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
188 zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
190 remain
, PIM_TLV_MIN_SIZE
,
193 FREE_ADDR_LIST_THEN_RETURN(-1);
196 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
197 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
198 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
199 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
201 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
202 if (PIM_DEBUG_PIM_HELLO
) {
204 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
205 zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
207 option_type
, option_len
, tlv_pastend
- tlv_curr
,
210 FREE_ADDR_LIST_THEN_RETURN(-2);
213 if (PIM_DEBUG_PIM_HELLO
) {
215 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
216 zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
219 option_type
, option_len
,
223 switch (option_type
) {
224 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
225 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
227 &hello_option_holdtime
,
230 FREE_ADDR_LIST_THEN_RETURN(-3);
233 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
234 if (pim_tlv_parse_lan_prune_delay(ifp
->name
, src_addr
,
236 &hello_option_propagation_delay
,
237 &hello_option_override_interval
,
240 FREE_ADDR_LIST_THEN_RETURN(-4);
243 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
244 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
246 &hello_option_dr_priority
,
249 FREE_ADDR_LIST_THEN_RETURN(-5);
252 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
253 if (pim_tlv_parse_generation_id(ifp
->name
, src_addr
,
255 &hello_option_generation_id
,
258 FREE_ADDR_LIST_THEN_RETURN(-6);
261 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
262 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
264 &hello_option_addr_list
,
270 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
271 if (PIM_DEBUG_PIM_HELLO
) {
273 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
274 zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
276 option_type
, option_len
,
281 if (PIM_DEBUG_PIM_HELLO
) {
283 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
284 zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
286 option_type
, option_len
,
291 tlv_curr
+= option_len
;
295 Check received PIM hello options
298 if (PIM_DEBUG_PIM_HELLO
) {
299 tlv_trace_uint16(__PRETTY_FUNCTION__
, "holdtime",
301 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
),
302 hello_option_holdtime
);
303 tlv_trace_uint16(__PRETTY_FUNCTION__
, "propagation_delay",
305 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
306 hello_option_propagation_delay
);
307 tlv_trace_uint16(__PRETTY_FUNCTION__
, "override_interval",
309 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
310 hello_option_override_interval
);
311 tlv_trace_bool(__PRETTY_FUNCTION__
, "can_disable_join_suppression",
313 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
314 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
315 tlv_trace_uint32(__PRETTY_FUNCTION__
, "dr_priority",
317 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_DR_PRIORITY
),
318 hello_option_dr_priority
);
319 tlv_trace_uint32_hex(__PRETTY_FUNCTION__
, "generation_id",
321 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
),
322 hello_option_generation_id
);
323 tlv_trace_list(__PRETTY_FUNCTION__
, "address_list",
325 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_ADDRESS_LIST
),
326 hello_option_addr_list
);
329 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
330 if (PIM_DEBUG_PIM_HELLO
) {
332 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
333 zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
343 neigh
= pim_neighbor_find(ifp
, src_addr
);
345 /* Add as new neighbor */
347 neigh
= pim_neighbor_add(ifp
, src_addr
,
349 hello_option_holdtime
,
350 hello_option_propagation_delay
,
351 hello_option_override_interval
,
352 hello_option_dr_priority
,
353 hello_option_generation_id
,
354 hello_option_addr_list
);
356 if (PIM_DEBUG_PIM_HELLO
) {
358 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
359 zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
363 FREE_ADDR_LIST_THEN_RETURN(-8);
366 /* actual addr list has been saved under neighbor */
371 Received generation ID ?
374 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
375 /* GenID mismatch ? */
376 if (!PIM_OPTION_IS_SET(neigh
->hello_options
, PIM_OPTION_MASK_GENERATION_ID
) ||
377 (hello_option_generation_id
!= neigh
->generation_id
)) {
381 pim_upstream_rpf_genid_changed(neigh
->source_addr
);
383 /* GenID mismatch, then replace neighbor */
385 if (PIM_DEBUG_PIM_HELLO
) {
387 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
388 zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
390 hello_option_generation_id
,
391 neigh
->generation_id
,
395 pim_upstream_rpf_genid_changed(neigh
->source_addr
);
397 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
398 neigh
= pim_neighbor_add(ifp
, src_addr
,
400 hello_option_holdtime
,
401 hello_option_propagation_delay
,
402 hello_option_override_interval
,
403 hello_option_dr_priority
,
404 hello_option_generation_id
,
405 hello_option_addr_list
);
407 if (PIM_DEBUG_PIM_HELLO
) {
409 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
410 zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
414 FREE_ADDR_LIST_THEN_RETURN(-9);
416 /* actual addr list is saved under neighbor */
419 } /* GenId mismatch: replace neighbor */
421 } /* GenId received */
424 Update existing neighbor
427 pim_neighbor_update(neigh
,
429 hello_option_holdtime
,
430 hello_option_dr_priority
,
431 hello_option_addr_list
);
432 /* actual addr list is saved under neighbor */
436 int pim_hello_build_tlv(const char *ifname
,
437 uint8_t *tlv_buf
, int tlv_buf_size
,
439 uint32_t dr_priority
,
440 uint32_t generation_id
,
441 uint16_t propagation_delay
,
442 uint16_t override_interval
,
443 int can_disable_join_suppression
,
444 struct list
*ifconnected
)
446 uint8_t *curr
= tlv_buf
;
447 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
455 curr
= pim_tlv_append_uint16(curr
,
457 PIM_MSG_OPTION_TYPE_HOLDTIME
,
460 if (PIM_DEBUG_PIM_HELLO
) {
461 zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
462 __PRETTY_FUNCTION__
, ifname
);
467 /* LAN Prune Delay */
468 tmp
= pim_tlv_append_2uint16(curr
,
470 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
474 if (PIM_DEBUG_PIM_HELLO
) {
475 zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
476 __PRETTY_FUNCTION__
, ifname
);
480 if (can_disable_join_suppression
) {
481 *((uint8_t*)(curr
) + 4) |= 0x80; /* enable T bit */
486 curr
= pim_tlv_append_uint32(curr
,
488 PIM_MSG_OPTION_TYPE_DR_PRIORITY
,
491 if (PIM_DEBUG_PIM_HELLO
) {
492 zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
493 __PRETTY_FUNCTION__
, ifname
);
499 curr
= pim_tlv_append_uint32(curr
,
501 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
504 if (PIM_DEBUG_PIM_HELLO
) {
505 zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
506 __PRETTY_FUNCTION__
, ifname
);
511 /* Secondary Address List */
513 curr
= pim_tlv_append_addrlist_ucast(curr
,
517 if (PIM_DEBUG_PIM_HELLO
) {
518 zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
519 __PRETTY_FUNCTION__
, ifname
);
525 return curr
- tlv_buf
;
529 RFC 4601: 4.3.1. Sending Hello Messages
531 Thus, if a router needs to send a Join/Prune or Assert message on an
532 interface on which it has not yet sent a Hello message with the
533 currently configured IP address, then it MUST immediately send the
534 relevant Hello message without waiting for the Hello Timer to
535 expire, followed by the Join/Prune or Assert message.
537 void pim_hello_require(struct interface
*ifp
)
539 struct pim_interface
*pim_ifp
;
547 if (pim_ifp
->pim_ifstat_hello_sent
)
550 pim_hello_restart_now(ifp
); /* Send hello and restart timer */