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
,
39 if (PIM_DEBUG_PIM_TRACE
) {
40 char src_str
[INET_ADDRSTRLEN
];
41 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
42 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
46 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
47 const char *ifname
, struct in_addr src_addr
,
51 char src_str
[INET_ADDRSTRLEN
];
52 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
54 "%s: PIM hello option from %s on interface %s: %s=%d",
55 label
, src_str
, ifname
, tlv_name
, value
);
59 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
60 const char *ifname
, struct in_addr src_addr
,
61 int isset
, uint16_t value
)
64 char src_str
[INET_ADDRSTRLEN
];
65 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
67 "%s: PIM hello option from %s on interface %s: %s=%u",
68 label
, src_str
, ifname
, tlv_name
, value
);
72 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
73 const char *ifname
, struct in_addr src_addr
,
74 int isset
, uint32_t value
)
77 char src_str
[INET_ADDRSTRLEN
];
78 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
80 "%s: PIM hello option from %s on interface %s: %s=%u",
81 label
, src_str
, ifname
, tlv_name
, value
);
85 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
86 const char *ifname
, struct in_addr src_addr
,
87 int isset
, uint32_t value
)
90 char src_str
[INET_ADDRSTRLEN
];
91 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
93 "%s: PIM hello option from %s on interface %s: %s=%08x",
94 label
, src_str
, ifname
, tlv_name
, value
);
99 static void tlv_trace(const char *label
, const char *tlv_name
,
100 const char *ifname
, struct in_addr src_addr
,
104 char src_str
[INET_ADDRSTRLEN
];
105 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
106 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
114 static void tlv_trace_list(const char *label
, const char *tlv_name
,
115 const char *ifname
, struct in_addr src_addr
,
116 int isset
, struct list
*addr_list
)
119 char src_str
[INET_ADDRSTRLEN
];
120 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
122 "%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
123 label
, src_str
, ifname
, tlv_name
,
124 addr_list
? ((int)listcount(addr_list
)) : -1,
129 #define FREE_ADDR_LIST \
130 if (hello_option_addr_list) { \
131 list_delete(&hello_option_addr_list); \
134 #define FREE_ADDR_LIST_THEN_RETURN(code) \
140 int pim_hello_recv(struct interface
*ifp
, struct in_addr src_addr
,
141 uint8_t *tlv_buf
, int tlv_buf_size
)
143 struct pim_interface
*pim_ifp
;
144 struct pim_neighbor
*neigh
;
146 uint8_t *tlv_pastend
;
147 pim_hello_options hello_options
=
148 0; /* bit array recording options found */
149 uint16_t hello_option_holdtime
= 0;
150 uint16_t hello_option_propagation_delay
= 0;
151 uint16_t hello_option_override_interval
= 0;
152 uint32_t hello_option_dr_priority
= 0;
153 uint32_t hello_option_generation_id
= 0;
154 struct list
*hello_option_addr_list
= 0;
156 if (PIM_DEBUG_PIM_HELLO
)
157 on_trace(__func__
, ifp
, src_addr
);
162 ++pim_ifp
->pim_ifstat_hello_recv
;
167 zassert(tlv_buf_size
>= 0);
169 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
171 while (tlv_curr
< tlv_pastend
) {
172 uint16_t option_type
;
174 int remain
= tlv_pastend
- tlv_curr
;
176 if (remain
< PIM_TLV_MIN_SIZE
) {
177 if (PIM_DEBUG_PIM_HELLO
) {
178 char src_str
[INET_ADDRSTRLEN
];
179 pim_inet4_dump("<src?>", src_addr
, src_str
,
182 "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
183 __func__
, remain
, PIM_TLV_MIN_SIZE
,
186 FREE_ADDR_LIST_THEN_RETURN(-1);
189 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
190 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
191 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
192 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
194 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
195 if (PIM_DEBUG_PIM_HELLO
) {
196 char src_str
[INET_ADDRSTRLEN
];
197 pim_inet4_dump("<src?>", src_addr
, src_str
,
200 "%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
201 __func__
, option_type
, option_len
,
202 tlv_pastend
- tlv_curr
, src_str
,
205 FREE_ADDR_LIST_THEN_RETURN(-2);
208 if (PIM_DEBUG_PIM_HELLO
) {
209 char src_str
[INET_ADDRSTRLEN
];
210 pim_inet4_dump("<src?>", src_addr
, src_str
,
213 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
214 __func__
, remain
, option_type
, option_len
,
218 switch (option_type
) {
219 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
220 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
222 &hello_option_holdtime
,
223 option_len
, tlv_curr
)) {
224 FREE_ADDR_LIST_THEN_RETURN(-3);
227 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
228 if (pim_tlv_parse_lan_prune_delay(
229 ifp
->name
, src_addr
, &hello_options
,
230 &hello_option_propagation_delay
,
231 &hello_option_override_interval
, option_len
,
233 FREE_ADDR_LIST_THEN_RETURN(-4);
236 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
237 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
239 &hello_option_dr_priority
,
240 option_len
, tlv_curr
)) {
241 FREE_ADDR_LIST_THEN_RETURN(-5);
244 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
245 if (pim_tlv_parse_generation_id(
246 ifp
->name
, src_addr
, &hello_options
,
247 &hello_option_generation_id
, option_len
,
249 FREE_ADDR_LIST_THEN_RETURN(-6);
252 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
253 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
255 &hello_option_addr_list
,
256 option_len
, tlv_curr
)) {
260 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
261 if (PIM_DEBUG_PIM_HELLO
) {
262 char src_str
[INET_ADDRSTRLEN
];
263 pim_inet4_dump("<src?>", src_addr
, src_str
,
266 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
267 __func__
, option_type
, option_len
,
272 if (PIM_DEBUG_PIM_HELLO
) {
273 char src_str
[INET_ADDRSTRLEN
];
274 pim_inet4_dump("<src?>", src_addr
, src_str
,
277 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
278 __func__
, option_type
, option_len
,
283 tlv_curr
+= option_len
;
287 Check received PIM hello options
290 if (PIM_DEBUG_PIM_HELLO
) {
291 tlv_trace_uint16(__func__
, "holdtime", ifp
->name
, src_addr
,
292 PIM_OPTION_IS_SET(hello_options
,
293 PIM_OPTION_MASK_HOLDTIME
),
294 hello_option_holdtime
);
296 __func__
, "propagation_delay", ifp
->name
, src_addr
,
297 PIM_OPTION_IS_SET(hello_options
,
298 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
299 hello_option_propagation_delay
);
301 __func__
, "override_interval", ifp
->name
, src_addr
,
302 PIM_OPTION_IS_SET(hello_options
,
303 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
304 hello_option_override_interval
);
306 __func__
, "can_disable_join_suppression", ifp
->name
,
308 PIM_OPTION_IS_SET(hello_options
,
309 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
312 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
313 tlv_trace_uint32(__func__
, "dr_priority", ifp
->name
, src_addr
,
314 PIM_OPTION_IS_SET(hello_options
,
315 PIM_OPTION_MASK_DR_PRIORITY
),
316 hello_option_dr_priority
);
317 tlv_trace_uint32_hex(
318 __func__
, "generation_id", ifp
->name
, src_addr
,
319 PIM_OPTION_IS_SET(hello_options
,
320 PIM_OPTION_MASK_GENERATION_ID
),
321 hello_option_generation_id
);
322 tlv_trace_list(__func__
, "address_list", ifp
->name
, src_addr
,
323 PIM_OPTION_IS_SET(hello_options
,
324 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
) {
330 char src_str
[INET_ADDRSTRLEN
];
331 pim_inet4_dump("<src?>", src_addr
, src_str
,
334 "%s: PIM hello missing holdtime from %s on interface %s",
335 __func__
, src_str
, ifp
->name
);
343 neigh
= pim_neighbor_find(ifp
, src_addr
);
345 /* Add as new neighbor */
347 neigh
= pim_neighbor_add(
348 ifp
, src_addr
, hello_options
, hello_option_holdtime
,
349 hello_option_propagation_delay
,
350 hello_option_override_interval
,
351 hello_option_dr_priority
, hello_option_generation_id
,
352 hello_option_addr_list
, PIM_NEIGHBOR_SEND_DELAY
);
354 if (PIM_DEBUG_PIM_HELLO
) {
355 char src_str
[INET_ADDRSTRLEN
];
356 pim_inet4_dump("<src?>", src_addr
, src_str
,
359 "%s: failure creating PIM neighbor %s on interface %s",
360 __func__
, src_str
, ifp
->name
);
362 FREE_ADDR_LIST_THEN_RETURN(-8);
364 /* Forward BSM if required */
365 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
366 if (PIM_DEBUG_PIM_HELLO
)
368 "%s: forwarding bsm to new nbr failed",
372 /* actual addr list has been saved under neighbor */
377 Received generation ID ?
380 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
381 /* GenID mismatch ? */
382 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
383 PIM_OPTION_MASK_GENERATION_ID
)
384 || (hello_option_generation_id
!= neigh
->generation_id
)) {
385 /* GenID mismatch, then replace neighbor */
387 if (PIM_DEBUG_PIM_HELLO
) {
388 char src_str
[INET_ADDRSTRLEN
];
389 pim_inet4_dump("<src?>", src_addr
, src_str
,
392 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
393 __func__
, hello_option_generation_id
,
394 neigh
->generation_id
, src_str
,
398 pim_upstream_rpf_genid_changed(pim_ifp
->pim
,
401 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
402 neigh
= pim_neighbor_add(ifp
, src_addr
, hello_options
,
403 hello_option_holdtime
,
404 hello_option_propagation_delay
,
405 hello_option_override_interval
,
406 hello_option_dr_priority
,
407 hello_option_generation_id
,
408 hello_option_addr_list
,
409 PIM_NEIGHBOR_SEND_NOW
);
411 if (PIM_DEBUG_PIM_HELLO
) {
412 char src_str
[INET_ADDRSTRLEN
];
413 pim_inet4_dump("<src?>", src_addr
,
417 "%s: failure re-creating PIM neighbor %s on interface %s",
418 __func__
, src_str
, ifp
->name
);
420 FREE_ADDR_LIST_THEN_RETURN(-9);
422 /* Forward BSM if required */
423 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
424 if (PIM_DEBUG_PIM_HELLO
)
426 "%s: forwarding bsm to new nbr failed",
429 /* actual addr list is saved under neighbor */
432 } /* GenId mismatch: replace neighbor */
434 } /* GenId received */
437 Update existing neighbor
440 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
441 hello_option_dr_priority
, hello_option_addr_list
);
442 /* actual addr list is saved under neighbor */
446 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
447 int tlv_buf_size
, uint16_t holdtime
,
448 uint32_t dr_priority
, uint32_t generation_id
,
449 uint16_t propagation_delay
, uint16_t override_interval
,
450 int can_disable_join_suppression
)
452 uint8_t *curr
= tlv_buf
;
453 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
455 struct pim_interface
*pim_ifp
= ifp
->info
;
456 struct pim_instance
*pim
= pim_ifp
->pim
;
463 curr
= pim_tlv_append_uint16(curr
, pastend
,
464 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
466 if (PIM_DEBUG_PIM_HELLO
) {
468 "%s: could not set PIM hello Holdtime option for interface %s",
469 __func__
, ifp
->name
);
474 /* LAN Prune Delay */
475 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
476 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
477 propagation_delay
, override_interval
);
479 if (PIM_DEBUG_PIM_HELLO
) {
481 "%s: could not set PIM LAN Prune Delay option for interface %s",
482 __func__
, ifp
->name
);
486 if (can_disable_join_suppression
) {
487 *(curr
+ 4) |= 0x80; /* enable T bit */
492 curr
= pim_tlv_append_uint32(
493 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
495 if (PIM_DEBUG_PIM_HELLO
) {
497 "%s: could not set PIM hello DR Priority option for interface %s",
498 __func__
, ifp
->name
);
504 curr
= pim_tlv_append_uint32(curr
, pastend
,
505 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
508 if (PIM_DEBUG_PIM_HELLO
) {
510 "%s: could not set PIM hello Generation ID option for interface %s",
511 __func__
, ifp
->name
);
516 /* Secondary Address List */
517 if (ifp
->connected
->count
) {
518 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
519 ifp
->connected
, AF_INET
);
521 if (PIM_DEBUG_PIM_HELLO
) {
523 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
524 __func__
, ifp
->name
);
528 if (pim
->send_v6_secondary
) {
529 curr
= pim_tlv_append_addrlist_ucast(
530 curr
, pastend
, ifp
->connected
, AF_INET6
);
532 if (PIM_DEBUG_PIM_HELLO
) {
534 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
535 __func__
, ifp
->name
);
542 return curr
- tlv_buf
;
546 RFC 4601: 4.3.1. Sending Hello Messages
548 Thus, if a router needs to send a Join/Prune or Assert message on an
549 interface on which it has not yet sent a Hello message with the
550 currently configured IP address, then it MUST immediately send the
551 relevant Hello message without waiting for the Hello Timer to
552 expire, followed by the Join/Prune or Assert message.
554 void pim_hello_require(struct interface
*ifp
)
556 struct pim_interface
*pim_ifp
;
564 if (pim_ifp
->pim_ifstat_hello_sent
)
567 pim_hello_restart_now(ifp
); /* Send hello and restart timer */