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
);
98 static void tlv_trace_list(const char *label
, const char *tlv_name
,
99 const char *ifname
, struct in_addr src_addr
,
100 int isset
, struct list
*addr_list
)
103 char src_str
[INET_ADDRSTRLEN
];
104 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
106 "%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
107 label
, src_str
, ifname
, tlv_name
,
108 addr_list
? ((int)listcount(addr_list
)) : -1,
113 #define FREE_ADDR_LIST \
114 if (hello_option_addr_list) { \
115 list_delete(&hello_option_addr_list); \
118 #define FREE_ADDR_LIST_THEN_RETURN(code) \
124 int pim_hello_recv(struct interface
*ifp
, struct in_addr src_addr
,
125 uint8_t *tlv_buf
, int tlv_buf_size
)
127 struct pim_interface
*pim_ifp
;
128 struct pim_neighbor
*neigh
;
130 uint8_t *tlv_pastend
;
131 pim_hello_options hello_options
=
132 0; /* bit array recording options found */
133 uint16_t hello_option_holdtime
= 0;
134 uint16_t hello_option_propagation_delay
= 0;
135 uint16_t hello_option_override_interval
= 0;
136 uint32_t hello_option_dr_priority
= 0;
137 uint32_t hello_option_generation_id
= 0;
138 struct list
*hello_option_addr_list
= 0;
140 if (PIM_DEBUG_PIM_HELLO
)
141 on_trace(__func__
, ifp
, src_addr
);
146 ++pim_ifp
->pim_ifstat_hello_recv
;
151 assert(tlv_buf_size
>= 0);
153 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
155 while (tlv_curr
< tlv_pastend
) {
156 uint16_t option_type
;
158 int remain
= tlv_pastend
- tlv_curr
;
160 if (remain
< PIM_TLV_MIN_SIZE
) {
161 if (PIM_DEBUG_PIM_HELLO
) {
162 char src_str
[INET_ADDRSTRLEN
];
163 pim_inet4_dump("<src?>", src_addr
, src_str
,
166 "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
167 __func__
, remain
, PIM_TLV_MIN_SIZE
,
170 FREE_ADDR_LIST_THEN_RETURN(-1);
173 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
174 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
175 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
176 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
178 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
179 if (PIM_DEBUG_PIM_HELLO
) {
180 char src_str
[INET_ADDRSTRLEN
];
181 pim_inet4_dump("<src?>", src_addr
, src_str
,
184 "%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
185 __func__
, option_type
, option_len
,
186 tlv_pastend
- tlv_curr
, src_str
,
189 FREE_ADDR_LIST_THEN_RETURN(-2);
192 if (PIM_DEBUG_PIM_HELLO
) {
193 char src_str
[INET_ADDRSTRLEN
];
194 pim_inet4_dump("<src?>", src_addr
, src_str
,
197 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
198 __func__
, remain
, option_type
, option_len
,
202 switch (option_type
) {
203 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
204 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
206 &hello_option_holdtime
,
207 option_len
, tlv_curr
)) {
208 FREE_ADDR_LIST_THEN_RETURN(-3);
211 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
212 if (pim_tlv_parse_lan_prune_delay(
213 ifp
->name
, src_addr
, &hello_options
,
214 &hello_option_propagation_delay
,
215 &hello_option_override_interval
, option_len
,
217 FREE_ADDR_LIST_THEN_RETURN(-4);
220 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
221 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
223 &hello_option_dr_priority
,
224 option_len
, tlv_curr
)) {
225 FREE_ADDR_LIST_THEN_RETURN(-5);
228 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
229 if (pim_tlv_parse_generation_id(
230 ifp
->name
, src_addr
, &hello_options
,
231 &hello_option_generation_id
, option_len
,
233 FREE_ADDR_LIST_THEN_RETURN(-6);
236 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
237 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
239 &hello_option_addr_list
,
240 option_len
, tlv_curr
)) {
244 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
245 if (PIM_DEBUG_PIM_HELLO
) {
246 char src_str
[INET_ADDRSTRLEN
];
247 pim_inet4_dump("<src?>", src_addr
, src_str
,
250 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
251 __func__
, option_type
, option_len
,
256 if (PIM_DEBUG_PIM_HELLO
) {
257 char src_str
[INET_ADDRSTRLEN
];
258 pim_inet4_dump("<src?>", src_addr
, src_str
,
261 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
262 __func__
, option_type
, option_len
,
267 tlv_curr
+= option_len
;
271 Check received PIM hello options
274 if (PIM_DEBUG_PIM_HELLO
) {
275 tlv_trace_uint16(__func__
, "holdtime", ifp
->name
, src_addr
,
276 PIM_OPTION_IS_SET(hello_options
,
277 PIM_OPTION_MASK_HOLDTIME
),
278 hello_option_holdtime
);
280 __func__
, "propagation_delay", ifp
->name
, src_addr
,
281 PIM_OPTION_IS_SET(hello_options
,
282 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
283 hello_option_propagation_delay
);
285 __func__
, "override_interval", ifp
->name
, src_addr
,
286 PIM_OPTION_IS_SET(hello_options
,
287 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
288 hello_option_override_interval
);
290 __func__
, "can_disable_join_suppression", ifp
->name
,
292 PIM_OPTION_IS_SET(hello_options
,
293 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
296 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
297 tlv_trace_uint32(__func__
, "dr_priority", ifp
->name
, src_addr
,
298 PIM_OPTION_IS_SET(hello_options
,
299 PIM_OPTION_MASK_DR_PRIORITY
),
300 hello_option_dr_priority
);
301 tlv_trace_uint32_hex(
302 __func__
, "generation_id", ifp
->name
, src_addr
,
303 PIM_OPTION_IS_SET(hello_options
,
304 PIM_OPTION_MASK_GENERATION_ID
),
305 hello_option_generation_id
);
306 tlv_trace_list(__func__
, "address_list", ifp
->name
, src_addr
,
307 PIM_OPTION_IS_SET(hello_options
,
308 PIM_OPTION_MASK_ADDRESS_LIST
),
309 hello_option_addr_list
);
312 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
313 if (PIM_DEBUG_PIM_HELLO
) {
314 char src_str
[INET_ADDRSTRLEN
];
315 pim_inet4_dump("<src?>", src_addr
, src_str
,
318 "%s: PIM hello missing holdtime from %s on interface %s",
319 __func__
, src_str
, ifp
->name
);
327 neigh
= pim_neighbor_find(ifp
, src_addr
);
329 /* Add as new neighbor */
331 neigh
= pim_neighbor_add(
332 ifp
, src_addr
, hello_options
, hello_option_holdtime
,
333 hello_option_propagation_delay
,
334 hello_option_override_interval
,
335 hello_option_dr_priority
, hello_option_generation_id
,
336 hello_option_addr_list
, PIM_NEIGHBOR_SEND_DELAY
);
338 if (PIM_DEBUG_PIM_HELLO
) {
339 char src_str
[INET_ADDRSTRLEN
];
340 pim_inet4_dump("<src?>", src_addr
, src_str
,
343 "%s: failure creating PIM neighbor %s on interface %s",
344 __func__
, src_str
, ifp
->name
);
346 FREE_ADDR_LIST_THEN_RETURN(-8);
348 /* Forward BSM if required */
349 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
350 if (PIM_DEBUG_PIM_HELLO
)
352 "%s: forwarding bsm to new nbr failed",
356 /* actual addr list has been saved under neighbor */
361 Received generation ID ?
364 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
365 /* GenID mismatch ? */
366 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
367 PIM_OPTION_MASK_GENERATION_ID
)
368 || (hello_option_generation_id
!= neigh
->generation_id
)) {
369 /* GenID mismatch, then replace neighbor */
371 if (PIM_DEBUG_PIM_HELLO
) {
372 char src_str
[INET_ADDRSTRLEN
];
373 pim_inet4_dump("<src?>", src_addr
, src_str
,
376 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
377 __func__
, hello_option_generation_id
,
378 neigh
->generation_id
, src_str
,
382 pim_upstream_rpf_genid_changed(pim_ifp
->pim
,
385 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
386 neigh
= pim_neighbor_add(ifp
, src_addr
, hello_options
,
387 hello_option_holdtime
,
388 hello_option_propagation_delay
,
389 hello_option_override_interval
,
390 hello_option_dr_priority
,
391 hello_option_generation_id
,
392 hello_option_addr_list
,
393 PIM_NEIGHBOR_SEND_NOW
);
395 if (PIM_DEBUG_PIM_HELLO
) {
396 char src_str
[INET_ADDRSTRLEN
];
397 pim_inet4_dump("<src?>", src_addr
,
401 "%s: failure re-creating PIM neighbor %s on interface %s",
402 __func__
, src_str
, ifp
->name
);
404 FREE_ADDR_LIST_THEN_RETURN(-9);
406 /* Forward BSM if required */
407 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
408 if (PIM_DEBUG_PIM_HELLO
)
410 "%s: forwarding bsm to new nbr failed",
413 /* actual addr list is saved under neighbor */
416 } /* GenId mismatch: replace neighbor */
418 } /* GenId received */
421 Update existing neighbor
424 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
425 hello_option_dr_priority
, hello_option_addr_list
);
426 /* actual addr list is saved under neighbor */
430 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
431 int tlv_buf_size
, uint16_t holdtime
,
432 uint32_t dr_priority
, uint32_t generation_id
,
433 uint16_t propagation_delay
, uint16_t override_interval
,
434 int can_disable_join_suppression
)
436 uint8_t *curr
= tlv_buf
;
437 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
439 struct pim_interface
*pim_ifp
= ifp
->info
;
440 struct pim_instance
*pim
= pim_ifp
->pim
;
447 curr
= pim_tlv_append_uint16(curr
, pastend
,
448 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
450 if (PIM_DEBUG_PIM_HELLO
) {
452 "%s: could not set PIM hello Holdtime option for interface %s",
453 __func__
, ifp
->name
);
458 /* LAN Prune Delay */
459 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
460 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
461 propagation_delay
, override_interval
);
463 if (PIM_DEBUG_PIM_HELLO
) {
465 "%s: could not set PIM LAN Prune Delay option for interface %s",
466 __func__
, ifp
->name
);
470 if (can_disable_join_suppression
) {
471 *(curr
+ 4) |= 0x80; /* enable T bit */
476 curr
= pim_tlv_append_uint32(
477 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
479 if (PIM_DEBUG_PIM_HELLO
) {
481 "%s: could not set PIM hello DR Priority option for interface %s",
482 __func__
, ifp
->name
);
488 curr
= pim_tlv_append_uint32(curr
, pastend
,
489 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
492 if (PIM_DEBUG_PIM_HELLO
) {
494 "%s: could not set PIM hello Generation ID option for interface %s",
495 __func__
, ifp
->name
);
500 /* Secondary Address List */
501 if (ifp
->connected
->count
) {
502 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
503 ifp
->connected
, AF_INET
);
505 if (PIM_DEBUG_PIM_HELLO
) {
507 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
508 __func__
, ifp
->name
);
512 if (pim
->send_v6_secondary
) {
513 curr
= pim_tlv_append_addrlist_ucast(
514 curr
, pastend
, ifp
->connected
, AF_INET6
);
516 if (PIM_DEBUG_PIM_HELLO
) {
518 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
519 __func__
, ifp
->name
);
526 return curr
- tlv_buf
;
530 RFC 4601: 4.3.1. Sending Hello Messages
532 Thus, if a router needs to send a Join/Prune or Assert message on an
533 interface on which it has not yet sent a Hello message with the
534 currently configured IP address, then it MUST immediately send the
535 relevant Hello message without waiting for the Hello Timer to
536 expire, followed by the Join/Prune or Assert message.
538 void pim_hello_require(struct interface
*ifp
)
540 struct pim_interface
*pim_ifp
;
548 if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp
->flags
))
551 pim_hello_restart_now(ifp
); /* Send hello and restart timer */