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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "pim_hello.h"
31 #include "pim_iface.h"
32 #include "pim_neighbor.h"
33 #include "pim_upstream.h"
36 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
38 if (PIM_DEBUG_PIM_TRACE
)
39 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
42 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
43 const char *ifname
, pim_addr src_addr
, int isset
,
48 "%s: PIM hello option from %pPAs on interface %s: %s=%d",
49 label
, &src_addr
, ifname
, tlv_name
, value
);
52 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
53 const char *ifname
, pim_addr src_addr
, int isset
,
58 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
59 label
, &src_addr
, ifname
, tlv_name
, value
);
62 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
63 const char *ifname
, pim_addr src_addr
, int isset
,
68 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
69 label
, &src_addr
, ifname
, tlv_name
, value
);
72 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
73 const char *ifname
, pim_addr src_addr
,
74 int isset
, uint32_t value
)
78 "%s: PIM hello option from %pPAs on interface %s: %s=%08x",
79 label
, &src_addr
, ifname
, tlv_name
, value
);
82 static void tlv_trace_list(const char *label
, const char *tlv_name
,
83 const char *ifname
, pim_addr src_addr
, int isset
,
84 struct list
*addr_list
)
88 "%s: PIM hello option from %pPAs on interface %s: %s size=%d list=%p",
89 label
, &src_addr
, ifname
, tlv_name
,
90 addr_list
? ((int)listcount(addr_list
)) : -1,
94 #define FREE_ADDR_LIST \
95 if (hello_option_addr_list) { \
96 list_delete(&hello_option_addr_list); \
99 #define FREE_ADDR_LIST_THEN_RETURN(code) \
105 int pim_hello_recv(struct interface
*ifp
, pim_addr src_addr
, uint8_t *tlv_buf
,
108 struct pim_interface
*pim_ifp
;
109 struct pim_neighbor
*neigh
;
111 uint8_t *tlv_pastend
;
112 pim_hello_options hello_options
=
113 0; /* bit array recording options found */
114 uint16_t hello_option_holdtime
= 0;
115 uint16_t hello_option_propagation_delay
= 0;
116 uint16_t hello_option_override_interval
= 0;
117 uint32_t hello_option_dr_priority
= 0;
118 uint32_t hello_option_generation_id
= 0;
119 struct list
*hello_option_addr_list
= 0;
121 if (PIM_DEBUG_PIM_HELLO
)
122 on_trace(__func__
, ifp
, src_addr
);
127 ++pim_ifp
->pim_ifstat_hello_recv
;
132 assert(tlv_buf_size
>= 0);
134 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
136 while (tlv_curr
< tlv_pastend
) {
137 uint16_t option_type
;
139 int remain
= tlv_pastend
- tlv_curr
;
141 if (remain
< PIM_TLV_MIN_SIZE
) {
142 if (PIM_DEBUG_PIM_HELLO
)
144 "%s: short PIM hello TLV size=%d < min=%d from %pPAs on interface %s",
145 __func__
, remain
, PIM_TLV_MIN_SIZE
,
146 &src_addr
, ifp
->name
);
147 FREE_ADDR_LIST_THEN_RETURN(-1);
150 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
151 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
152 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
153 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
155 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
156 if (PIM_DEBUG_PIM_HELLO
)
158 "%s: long PIM hello TLV type=%d length=%d > left=%td from %pPAs on interface %s",
159 __func__
, option_type
, option_len
,
160 tlv_pastend
- tlv_curr
, &src_addr
,
162 FREE_ADDR_LIST_THEN_RETURN(-2);
165 if (PIM_DEBUG_PIM_HELLO
)
167 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %pPAs on %s",
168 __func__
, remain
, option_type
, option_len
,
169 &src_addr
, ifp
->name
);
171 switch (option_type
) {
172 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
173 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
175 &hello_option_holdtime
,
176 option_len
, tlv_curr
)) {
177 FREE_ADDR_LIST_THEN_RETURN(-3);
180 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
181 if (pim_tlv_parse_lan_prune_delay(
182 ifp
->name
, src_addr
, &hello_options
,
183 &hello_option_propagation_delay
,
184 &hello_option_override_interval
, option_len
,
186 FREE_ADDR_LIST_THEN_RETURN(-4);
189 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
190 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
192 &hello_option_dr_priority
,
193 option_len
, tlv_curr
)) {
194 FREE_ADDR_LIST_THEN_RETURN(-5);
197 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
198 if (pim_tlv_parse_generation_id(
199 ifp
->name
, src_addr
, &hello_options
,
200 &hello_option_generation_id
, option_len
,
202 FREE_ADDR_LIST_THEN_RETURN(-6);
205 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
206 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
208 &hello_option_addr_list
,
209 option_len
, tlv_curr
)) {
213 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
214 if (PIM_DEBUG_PIM_HELLO
)
216 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %pPAs on interface %s",
217 __func__
, option_type
, option_len
,
218 &src_addr
, ifp
->name
);
221 if (PIM_DEBUG_PIM_HELLO
)
223 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %pPAs on interface %s",
224 __func__
, option_type
, option_len
,
225 &src_addr
, ifp
->name
);
228 tlv_curr
+= option_len
;
232 Check received PIM hello options
235 if (PIM_DEBUG_PIM_HELLO
) {
236 tlv_trace_uint16(__func__
, "holdtime", ifp
->name
, src_addr
,
237 PIM_OPTION_IS_SET(hello_options
,
238 PIM_OPTION_MASK_HOLDTIME
),
239 hello_option_holdtime
);
241 __func__
, "propagation_delay", ifp
->name
, src_addr
,
242 PIM_OPTION_IS_SET(hello_options
,
243 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
244 hello_option_propagation_delay
);
246 __func__
, "override_interval", ifp
->name
, src_addr
,
247 PIM_OPTION_IS_SET(hello_options
,
248 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
249 hello_option_override_interval
);
251 __func__
, "can_disable_join_suppression", ifp
->name
,
253 PIM_OPTION_IS_SET(hello_options
,
254 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
257 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
258 tlv_trace_uint32(__func__
, "dr_priority", ifp
->name
, src_addr
,
259 PIM_OPTION_IS_SET(hello_options
,
260 PIM_OPTION_MASK_DR_PRIORITY
),
261 hello_option_dr_priority
);
262 tlv_trace_uint32_hex(
263 __func__
, "generation_id", ifp
->name
, src_addr
,
264 PIM_OPTION_IS_SET(hello_options
,
265 PIM_OPTION_MASK_GENERATION_ID
),
266 hello_option_generation_id
);
267 tlv_trace_list(__func__
, "address_list", ifp
->name
, src_addr
,
268 PIM_OPTION_IS_SET(hello_options
,
269 PIM_OPTION_MASK_ADDRESS_LIST
),
270 hello_option_addr_list
);
273 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
274 if (PIM_DEBUG_PIM_HELLO
)
276 "%s: PIM hello missing holdtime from %pPAs on interface %s",
277 __func__
, &src_addr
, ifp
->name
);
284 neigh
= pim_neighbor_find(ifp
, src_addr
);
286 /* Add as new neighbor */
288 neigh
= pim_neighbor_add(
289 ifp
, src_addr
, hello_options
, hello_option_holdtime
,
290 hello_option_propagation_delay
,
291 hello_option_override_interval
,
292 hello_option_dr_priority
, hello_option_generation_id
,
293 hello_option_addr_list
, PIM_NEIGHBOR_SEND_DELAY
);
295 if (PIM_DEBUG_PIM_HELLO
)
297 "%s: failure creating PIM neighbor %pPAs on interface %s",
298 __func__
, &src_addr
, ifp
->name
);
299 FREE_ADDR_LIST_THEN_RETURN(-8);
301 /* Forward BSM if required */
302 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
303 if (PIM_DEBUG_PIM_HELLO
)
305 "%s: forwarding bsm to new nbr failed",
309 /* actual addr list has been saved under neighbor */
314 Received generation ID ?
317 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
318 /* GenID mismatch ? */
319 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
320 PIM_OPTION_MASK_GENERATION_ID
)
321 || (hello_option_generation_id
!= neigh
->generation_id
)) {
322 /* GenID mismatch, then replace neighbor */
324 if (PIM_DEBUG_PIM_HELLO
)
326 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %pPAs on %s",
327 __func__
, hello_option_generation_id
,
328 neigh
->generation_id
, &src_addr
,
331 pim_upstream_rpf_genid_changed(pim_ifp
->pim
,
334 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
335 neigh
= pim_neighbor_add(ifp
, src_addr
, hello_options
,
336 hello_option_holdtime
,
337 hello_option_propagation_delay
,
338 hello_option_override_interval
,
339 hello_option_dr_priority
,
340 hello_option_generation_id
,
341 hello_option_addr_list
,
342 PIM_NEIGHBOR_SEND_NOW
);
344 if (PIM_DEBUG_PIM_HELLO
)
346 "%s: failure re-creating PIM neighbor %pPAs on interface %s",
347 __func__
, &src_addr
, ifp
->name
);
348 FREE_ADDR_LIST_THEN_RETURN(-9);
350 /* Forward BSM if required */
351 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
352 if (PIM_DEBUG_PIM_HELLO
)
354 "%s: forwarding bsm to new nbr failed",
357 /* actual addr list is saved under neighbor */
360 } /* GenId mismatch: replace neighbor */
362 } /* GenId received */
365 Update existing neighbor
368 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
369 hello_option_dr_priority
, hello_option_addr_list
);
370 /* actual addr list is saved under neighbor */
374 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
375 int tlv_buf_size
, uint16_t holdtime
,
376 uint32_t dr_priority
, uint32_t generation_id
,
377 uint16_t propagation_delay
, uint16_t override_interval
,
378 int can_disable_join_suppression
)
380 uint8_t *curr
= tlv_buf
;
381 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
383 struct pim_interface
*pim_ifp
= ifp
->info
;
384 struct pim_instance
*pim
= pim_ifp
->pim
;
391 curr
= pim_tlv_append_uint16(curr
, pastend
,
392 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
394 if (PIM_DEBUG_PIM_HELLO
) {
396 "%s: could not set PIM hello Holdtime option for interface %s",
397 __func__
, ifp
->name
);
402 /* LAN Prune Delay */
403 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
404 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
405 propagation_delay
, override_interval
);
407 if (PIM_DEBUG_PIM_HELLO
) {
409 "%s: could not set PIM LAN Prune Delay option for interface %s",
410 __func__
, ifp
->name
);
414 if (can_disable_join_suppression
) {
415 *(curr
+ 4) |= 0x80; /* enable T bit */
420 curr
= pim_tlv_append_uint32(
421 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
423 if (PIM_DEBUG_PIM_HELLO
) {
425 "%s: could not set PIM hello DR Priority option for interface %s",
426 __func__
, ifp
->name
);
432 curr
= pim_tlv_append_uint32(curr
, pastend
,
433 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
436 if (PIM_DEBUG_PIM_HELLO
) {
438 "%s: could not set PIM hello Generation ID option for interface %s",
439 __func__
, ifp
->name
);
444 /* Secondary Address List */
445 if (ifp
->connected
->count
) {
446 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
447 ifp
->connected
, AF_INET
);
449 if (PIM_DEBUG_PIM_HELLO
) {
451 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
452 __func__
, ifp
->name
);
456 if (pim
->send_v6_secondary
) {
457 curr
= pim_tlv_append_addrlist_ucast(
458 curr
, pastend
, ifp
->connected
, AF_INET6
);
460 if (PIM_DEBUG_PIM_HELLO
) {
462 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
463 __func__
, ifp
->name
);
470 return curr
- tlv_buf
;
474 RFC 4601: 4.3.1. Sending Hello Messages
476 Thus, if a router needs to send a Join/Prune or Assert message on an
477 interface on which it has not yet sent a Hello message with the
478 currently configured IP address, then it MUST immediately send the
479 relevant Hello message without waiting for the Hello Timer to
480 expire, followed by the Join/Prune or Assert message.
482 void pim_hello_require(struct interface
*ifp
)
484 struct pim_interface
*pim_ifp
;
492 if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp
->flags
))
495 pim_hello_restart_now(ifp
); /* Send hello and restart timer */