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
26 #include "pim_instance.h"
31 #include "pim_hello.h"
32 #include "pim_iface.h"
33 #include "pim_neighbor.h"
34 #include "pim_upstream.h"
37 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
39 if (PIM_DEBUG_PIM_TRACE
)
40 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
43 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
44 const char *ifname
, pim_addr src_addr
, int isset
,
49 "%s: PIM hello option from %pPAs on interface %s: %s=%d",
50 label
, &src_addr
, ifname
, tlv_name
, value
);
53 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
54 const char *ifname
, pim_addr src_addr
, int isset
,
59 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
60 label
, &src_addr
, ifname
, tlv_name
, value
);
63 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
64 const char *ifname
, pim_addr src_addr
, int isset
,
69 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
70 label
, &src_addr
, ifname
, tlv_name
, value
);
73 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
74 const char *ifname
, pim_addr src_addr
,
75 int isset
, uint32_t value
)
79 "%s: PIM hello option from %pPAs on interface %s: %s=%08x",
80 label
, &src_addr
, ifname
, tlv_name
, value
);
83 static void tlv_trace_list(const char *label
, const char *tlv_name
,
84 const char *ifname
, pim_addr src_addr
, int isset
,
85 struct list
*addr_list
)
89 "%s: PIM hello option from %pPAs on interface %s: %s size=%d list=%p",
90 label
, &src_addr
, ifname
, tlv_name
,
91 addr_list
? ((int)listcount(addr_list
)) : -1,
95 #define FREE_ADDR_LIST \
96 if (hello_option_addr_list) { \
97 list_delete(&hello_option_addr_list); \
100 #define FREE_ADDR_LIST_THEN_RETURN(code) \
106 int pim_hello_recv(struct interface
*ifp
, pim_addr src_addr
, uint8_t *tlv_buf
,
109 struct pim_interface
*pim_ifp
;
110 struct pim_neighbor
*neigh
;
112 uint8_t *tlv_pastend
;
113 pim_hello_options hello_options
=
114 0; /* bit array recording options found */
115 uint16_t hello_option_holdtime
= 0;
116 uint16_t hello_option_propagation_delay
= 0;
117 uint16_t hello_option_override_interval
= 0;
118 uint32_t hello_option_dr_priority
= 0;
119 uint32_t hello_option_generation_id
= 0;
120 struct list
*hello_option_addr_list
= 0;
122 if (PIM_DEBUG_PIM_HELLO
)
123 on_trace(__func__
, ifp
, src_addr
);
128 if (pim_ifp
->pim_passive_enable
) {
129 if (PIM_DEBUG_PIM_PACKETS
)
131 "skip receiving PIM message on passive interface %s",
136 ++pim_ifp
->pim_ifstat_hello_recv
;
141 assert(tlv_buf_size
>= 0);
143 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
145 while (tlv_curr
< tlv_pastend
) {
146 uint16_t option_type
;
148 int remain
= tlv_pastend
- tlv_curr
;
150 if (remain
< PIM_TLV_MIN_SIZE
) {
151 if (PIM_DEBUG_PIM_HELLO
)
153 "%s: short PIM hello TLV size=%d < min=%d from %pPAs on interface %s",
154 __func__
, remain
, PIM_TLV_MIN_SIZE
,
155 &src_addr
, ifp
->name
);
156 FREE_ADDR_LIST_THEN_RETURN(-1);
159 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
160 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
161 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
162 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
164 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
165 if (PIM_DEBUG_PIM_HELLO
)
167 "%s: long PIM hello TLV type=%d length=%d > left=%td from %pPAs on interface %s",
168 __func__
, option_type
, option_len
,
169 tlv_pastend
- tlv_curr
, &src_addr
,
171 FREE_ADDR_LIST_THEN_RETURN(-2);
174 if (PIM_DEBUG_PIM_HELLO
)
176 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %pPAs on %s",
177 __func__
, remain
, option_type
, option_len
,
178 &src_addr
, ifp
->name
);
180 switch (option_type
) {
181 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
182 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
184 &hello_option_holdtime
,
185 option_len
, tlv_curr
)) {
186 FREE_ADDR_LIST_THEN_RETURN(-3);
189 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
190 if (pim_tlv_parse_lan_prune_delay(
191 ifp
->name
, src_addr
, &hello_options
,
192 &hello_option_propagation_delay
,
193 &hello_option_override_interval
, option_len
,
195 FREE_ADDR_LIST_THEN_RETURN(-4);
198 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
199 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
201 &hello_option_dr_priority
,
202 option_len
, tlv_curr
)) {
203 FREE_ADDR_LIST_THEN_RETURN(-5);
206 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
207 if (pim_tlv_parse_generation_id(
208 ifp
->name
, src_addr
, &hello_options
,
209 &hello_option_generation_id
, option_len
,
211 FREE_ADDR_LIST_THEN_RETURN(-6);
214 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
215 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
217 &hello_option_addr_list
,
218 option_len
, tlv_curr
)) {
222 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
223 if (PIM_DEBUG_PIM_HELLO
)
225 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %pPAs on interface %s",
226 __func__
, option_type
, option_len
,
227 &src_addr
, ifp
->name
);
230 if (PIM_DEBUG_PIM_HELLO
)
232 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %pPAs on interface %s",
233 __func__
, option_type
, option_len
,
234 &src_addr
, ifp
->name
);
237 tlv_curr
+= option_len
;
241 Check received PIM hello options
244 if (PIM_DEBUG_PIM_HELLO
) {
245 tlv_trace_uint16(__func__
, "holdtime", ifp
->name
, src_addr
,
246 PIM_OPTION_IS_SET(hello_options
,
247 PIM_OPTION_MASK_HOLDTIME
),
248 hello_option_holdtime
);
250 __func__
, "propagation_delay", ifp
->name
, src_addr
,
251 PIM_OPTION_IS_SET(hello_options
,
252 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
253 hello_option_propagation_delay
);
255 __func__
, "override_interval", ifp
->name
, src_addr
,
256 PIM_OPTION_IS_SET(hello_options
,
257 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
258 hello_option_override_interval
);
260 __func__
, "can_disable_join_suppression", ifp
->name
,
262 PIM_OPTION_IS_SET(hello_options
,
263 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
266 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
267 tlv_trace_uint32(__func__
, "dr_priority", ifp
->name
, src_addr
,
268 PIM_OPTION_IS_SET(hello_options
,
269 PIM_OPTION_MASK_DR_PRIORITY
),
270 hello_option_dr_priority
);
271 tlv_trace_uint32_hex(
272 __func__
, "generation_id", ifp
->name
, src_addr
,
273 PIM_OPTION_IS_SET(hello_options
,
274 PIM_OPTION_MASK_GENERATION_ID
),
275 hello_option_generation_id
);
276 tlv_trace_list(__func__
, "address_list", ifp
->name
, src_addr
,
277 PIM_OPTION_IS_SET(hello_options
,
278 PIM_OPTION_MASK_ADDRESS_LIST
),
279 hello_option_addr_list
);
282 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
283 if (PIM_DEBUG_PIM_HELLO
)
285 "%s: PIM hello missing holdtime from %pPAs on interface %s",
286 __func__
, &src_addr
, ifp
->name
);
293 neigh
= pim_neighbor_find(ifp
, src_addr
);
295 /* Add as new neighbor */
297 neigh
= pim_neighbor_add(
298 ifp
, src_addr
, hello_options
, hello_option_holdtime
,
299 hello_option_propagation_delay
,
300 hello_option_override_interval
,
301 hello_option_dr_priority
, hello_option_generation_id
,
302 hello_option_addr_list
, PIM_NEIGHBOR_SEND_DELAY
);
304 if (PIM_DEBUG_PIM_HELLO
)
306 "%s: failure creating PIM neighbor %pPAs on interface %s",
307 __func__
, &src_addr
, ifp
->name
);
308 FREE_ADDR_LIST_THEN_RETURN(-8);
310 /* Forward BSM if required */
311 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
312 if (PIM_DEBUG_PIM_HELLO
)
314 "%s: forwarding bsm to new nbr failed",
318 /* actual addr list has been saved under neighbor */
323 Received generation ID ?
326 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
327 /* GenID mismatch ? */
328 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
329 PIM_OPTION_MASK_GENERATION_ID
)
330 || (hello_option_generation_id
!= neigh
->generation_id
)) {
331 /* GenID mismatch, then replace neighbor */
333 if (PIM_DEBUG_PIM_HELLO
)
335 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %pPAs on %s",
336 __func__
, hello_option_generation_id
,
337 neigh
->generation_id
, &src_addr
,
340 pim_upstream_rpf_genid_changed(pim_ifp
->pim
,
343 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
344 neigh
= pim_neighbor_add(ifp
, src_addr
, hello_options
,
345 hello_option_holdtime
,
346 hello_option_propagation_delay
,
347 hello_option_override_interval
,
348 hello_option_dr_priority
,
349 hello_option_generation_id
,
350 hello_option_addr_list
,
351 PIM_NEIGHBOR_SEND_NOW
);
353 if (PIM_DEBUG_PIM_HELLO
)
355 "%s: failure re-creating PIM neighbor %pPAs on interface %s",
356 __func__
, &src_addr
, ifp
->name
);
357 FREE_ADDR_LIST_THEN_RETURN(-9);
359 /* Forward BSM if required */
360 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
361 if (PIM_DEBUG_PIM_HELLO
)
363 "%s: forwarding bsm to new nbr failed",
366 /* actual addr list is saved under neighbor */
369 } /* GenId mismatch: replace neighbor */
371 } /* GenId received */
374 Update existing neighbor
377 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
378 hello_option_dr_priority
, hello_option_addr_list
);
379 /* actual addr list is saved under neighbor */
383 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
384 int tlv_buf_size
, uint16_t holdtime
,
385 uint32_t dr_priority
, uint32_t generation_id
,
386 uint16_t propagation_delay
, uint16_t override_interval
,
387 int can_disable_join_suppression
)
389 uint8_t *curr
= tlv_buf
;
390 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
392 struct pim_interface
*pim_ifp
= ifp
->info
;
393 struct pim_instance
*pim
= pim_ifp
->pim
;
400 curr
= pim_tlv_append_uint16(curr
, pastend
,
401 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
403 if (PIM_DEBUG_PIM_HELLO
) {
405 "%s: could not set PIM hello Holdtime option for interface %s",
406 __func__
, ifp
->name
);
411 /* LAN Prune Delay */
412 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
413 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
414 propagation_delay
, override_interval
);
416 if (PIM_DEBUG_PIM_HELLO
) {
418 "%s: could not set PIM LAN Prune Delay option for interface %s",
419 __func__
, ifp
->name
);
423 if (can_disable_join_suppression
) {
424 *(curr
+ 4) |= 0x80; /* enable T bit */
429 curr
= pim_tlv_append_uint32(
430 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
432 if (PIM_DEBUG_PIM_HELLO
) {
434 "%s: could not set PIM hello DR Priority option for interface %s",
435 __func__
, ifp
->name
);
441 curr
= pim_tlv_append_uint32(curr
, pastend
,
442 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
445 if (PIM_DEBUG_PIM_HELLO
) {
447 "%s: could not set PIM hello Generation ID option for interface %s",
448 __func__
, ifp
->name
);
453 /* Secondary Address List */
454 if (ifp
->connected
->count
) {
455 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
456 ifp
->connected
, AF_INET
);
458 if (PIM_DEBUG_PIM_HELLO
) {
460 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
461 __func__
, ifp
->name
);
465 if (pim
->send_v6_secondary
) {
466 curr
= pim_tlv_append_addrlist_ucast(
467 curr
, pastend
, ifp
->connected
, AF_INET6
);
469 if (PIM_DEBUG_PIM_HELLO
) {
471 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
472 __func__
, ifp
->name
);
479 return curr
- tlv_buf
;
483 RFC 4601: 4.3.1. Sending Hello Messages
485 Thus, if a router needs to send a Join/Prune or Assert message on an
486 interface on which it has not yet sent a Hello message with the
487 currently configured IP address, then it MUST immediately send the
488 relevant Hello message without waiting for the Hello Timer to
489 expire, followed by the Join/Prune or Assert message.
491 void pim_hello_require(struct interface
*ifp
)
493 struct pim_interface
*pim_ifp
;
501 if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp
->flags
))
504 pim_hello_restart_now(ifp
); /* Send hello and restart timer */