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"
35 static void on_trace(const char *label
, struct interface
*ifp
,
38 if (PIM_DEBUG_PIM_TRACE
) {
39 char src_str
[INET_ADDRSTRLEN
];
40 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
41 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
45 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
46 const char *ifname
, struct in_addr src_addr
,
50 char src_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
53 "%s: PIM hello option from %s on interface %s: %s=%d",
54 label
, src_str
, ifname
, tlv_name
, value
);
58 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
59 const char *ifname
, struct in_addr src_addr
,
60 int isset
, uint16_t value
)
63 char src_str
[INET_ADDRSTRLEN
];
64 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
66 "%s: PIM hello option from %s on interface %s: %s=%u",
67 label
, src_str
, ifname
, tlv_name
, value
);
71 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
72 const char *ifname
, struct in_addr src_addr
,
73 int isset
, uint32_t value
)
76 char src_str
[INET_ADDRSTRLEN
];
77 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
79 "%s: PIM hello option from %s on interface %s: %s=%u",
80 label
, src_str
, ifname
, tlv_name
, value
);
84 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
85 const char *ifname
, struct in_addr src_addr
,
86 int isset
, uint32_t value
)
89 char src_str
[INET_ADDRSTRLEN
];
90 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
92 "%s: PIM hello option from %s on interface %s: %s=%08x",
93 label
, src_str
, ifname
, tlv_name
, value
);
98 static void tlv_trace(const char *label
, const char *tlv_name
,
99 const char *ifname
, struct in_addr src_addr
,
103 char src_str
[INET_ADDRSTRLEN
];
104 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
105 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
113 static void tlv_trace_list(const char *label
, const char *tlv_name
,
114 const char *ifname
, struct in_addr src_addr
,
115 int isset
, struct list
*addr_list
)
118 char src_str
[INET_ADDRSTRLEN
];
119 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
121 "%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
122 label
, src_str
, ifname
, tlv_name
,
123 addr_list
? ((int)listcount(addr_list
)) : -1,
128 #define FREE_ADDR_LIST \
129 if (hello_option_addr_list) { \
130 list_delete(&hello_option_addr_list); \
133 #define FREE_ADDR_LIST_THEN_RETURN(code) \
139 int pim_hello_recv(struct interface
*ifp
, struct in_addr src_addr
,
140 uint8_t *tlv_buf
, int tlv_buf_size
)
142 struct pim_interface
*pim_ifp
;
143 struct pim_neighbor
*neigh
;
145 uint8_t *tlv_pastend
;
146 pim_hello_options hello_options
=
147 0; /* bit array recording options found */
148 uint16_t hello_option_holdtime
= 0;
149 uint16_t hello_option_propagation_delay
= 0;
150 uint16_t hello_option_override_interval
= 0;
151 uint32_t hello_option_dr_priority
= 0;
152 uint32_t hello_option_generation_id
= 0;
153 struct list
*hello_option_addr_list
= 0;
155 if (PIM_DEBUG_PIM_HELLO
)
156 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
161 ++pim_ifp
->pim_ifstat_hello_recv
;
166 zassert(tlv_buf_size
>= 0);
168 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
170 while (tlv_curr
< tlv_pastend
) {
171 uint16_t option_type
;
173 int remain
= tlv_pastend
- tlv_curr
;
175 if (remain
< PIM_TLV_MIN_SIZE
) {
176 if (PIM_DEBUG_PIM_HELLO
) {
177 char src_str
[INET_ADDRSTRLEN
];
178 pim_inet4_dump("<src?>", src_addr
, src_str
,
181 "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
182 __PRETTY_FUNCTION__
, remain
,
183 PIM_TLV_MIN_SIZE
, src_str
, ifp
->name
);
185 FREE_ADDR_LIST_THEN_RETURN(-1);
188 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
189 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
190 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
191 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
193 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
194 if (PIM_DEBUG_PIM_HELLO
) {
195 char src_str
[INET_ADDRSTRLEN
];
196 pim_inet4_dump("<src?>", src_addr
, src_str
,
199 "%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
200 __PRETTY_FUNCTION__
, option_type
,
201 option_len
, tlv_pastend
- tlv_curr
,
204 FREE_ADDR_LIST_THEN_RETURN(-2);
207 if (PIM_DEBUG_PIM_HELLO
) {
208 char src_str
[INET_ADDRSTRLEN
];
209 pim_inet4_dump("<src?>", src_addr
, src_str
,
212 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
213 __PRETTY_FUNCTION__
, remain
, option_type
,
214 option_len
, src_str
, ifp
->name
);
217 switch (option_type
) {
218 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
219 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
221 &hello_option_holdtime
,
222 option_len
, tlv_curr
)) {
223 FREE_ADDR_LIST_THEN_RETURN(-3);
226 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
227 if (pim_tlv_parse_lan_prune_delay(
228 ifp
->name
, src_addr
, &hello_options
,
229 &hello_option_propagation_delay
,
230 &hello_option_override_interval
, option_len
,
232 FREE_ADDR_LIST_THEN_RETURN(-4);
235 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
236 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
238 &hello_option_dr_priority
,
239 option_len
, tlv_curr
)) {
240 FREE_ADDR_LIST_THEN_RETURN(-5);
243 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
244 if (pim_tlv_parse_generation_id(
245 ifp
->name
, src_addr
, &hello_options
,
246 &hello_option_generation_id
, option_len
,
248 FREE_ADDR_LIST_THEN_RETURN(-6);
251 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
252 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
254 &hello_option_addr_list
,
255 option_len
, tlv_curr
)) {
259 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
260 if (PIM_DEBUG_PIM_HELLO
) {
261 char src_str
[INET_ADDRSTRLEN
];
262 pim_inet4_dump("<src?>", src_addr
, src_str
,
265 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
266 __PRETTY_FUNCTION__
, option_type
,
267 option_len
, src_str
, ifp
->name
);
271 if (PIM_DEBUG_PIM_HELLO
) {
272 char src_str
[INET_ADDRSTRLEN
];
273 pim_inet4_dump("<src?>", src_addr
, src_str
,
276 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
277 __PRETTY_FUNCTION__
, option_type
,
278 option_len
, src_str
, ifp
->name
);
282 tlv_curr
+= option_len
;
286 Check received PIM hello options
289 if (PIM_DEBUG_PIM_HELLO
) {
290 tlv_trace_uint16(__PRETTY_FUNCTION__
, "holdtime", ifp
->name
,
292 PIM_OPTION_IS_SET(hello_options
,
293 PIM_OPTION_MASK_HOLDTIME
),
294 hello_option_holdtime
);
296 __PRETTY_FUNCTION__
, "propagation_delay", ifp
->name
,
298 PIM_OPTION_IS_SET(hello_options
,
299 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
300 hello_option_propagation_delay
);
302 __PRETTY_FUNCTION__
, "override_interval", ifp
->name
,
304 PIM_OPTION_IS_SET(hello_options
,
305 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
306 hello_option_override_interval
);
308 __PRETTY_FUNCTION__
, "can_disable_join_suppression",
310 PIM_OPTION_IS_SET(hello_options
,
311 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
314 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
315 tlv_trace_uint32(__PRETTY_FUNCTION__
, "dr_priority", ifp
->name
,
317 PIM_OPTION_IS_SET(hello_options
,
318 PIM_OPTION_MASK_DR_PRIORITY
),
319 hello_option_dr_priority
);
320 tlv_trace_uint32_hex(
321 __PRETTY_FUNCTION__
, "generation_id", ifp
->name
,
323 PIM_OPTION_IS_SET(hello_options
,
324 PIM_OPTION_MASK_GENERATION_ID
),
325 hello_option_generation_id
);
326 tlv_trace_list(__PRETTY_FUNCTION__
, "address_list", ifp
->name
,
328 PIM_OPTION_IS_SET(hello_options
,
329 PIM_OPTION_MASK_ADDRESS_LIST
),
330 hello_option_addr_list
);
333 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
334 if (PIM_DEBUG_PIM_HELLO
) {
335 char src_str
[INET_ADDRSTRLEN
];
336 pim_inet4_dump("<src?>", src_addr
, src_str
,
339 "%s: PIM hello missing holdtime from %s on interface %s",
340 __PRETTY_FUNCTION__
, src_str
, ifp
->name
);
348 neigh
= pim_neighbor_find(ifp
, src_addr
);
350 /* Add as new neighbor */
352 neigh
= pim_neighbor_add(
353 ifp
, src_addr
, hello_options
, hello_option_holdtime
,
354 hello_option_propagation_delay
,
355 hello_option_override_interval
,
356 hello_option_dr_priority
, hello_option_generation_id
,
357 hello_option_addr_list
, PIM_NEIGHBOR_SEND_DELAY
);
359 if (PIM_DEBUG_PIM_HELLO
) {
360 char src_str
[INET_ADDRSTRLEN
];
361 pim_inet4_dump("<src?>", src_addr
, src_str
,
364 "%s: failure creating PIM neighbor %s on interface %s",
365 __PRETTY_FUNCTION__
, src_str
,
368 FREE_ADDR_LIST_THEN_RETURN(-8);
371 /* actual addr list has been saved under neighbor */
376 Received generation ID ?
379 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
380 /* GenID mismatch ? */
381 if (!PIM_OPTION_IS_SET(neigh
->hello_options
,
382 PIM_OPTION_MASK_GENERATION_ID
)
383 || (hello_option_generation_id
!= neigh
->generation_id
)) {
384 /* GenID mismatch, then replace neighbor */
386 if (PIM_DEBUG_PIM_HELLO
) {
387 char src_str
[INET_ADDRSTRLEN
];
388 pim_inet4_dump("<src?>", src_addr
, src_str
,
391 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
393 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 __PRETTY_FUNCTION__
, src_str
,
421 FREE_ADDR_LIST_THEN_RETURN(-9);
423 /* actual addr list is saved under neighbor */
426 } /* GenId mismatch: replace neighbor */
428 } /* GenId received */
431 Update existing neighbor
434 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
435 hello_option_dr_priority
, hello_option_addr_list
);
436 /* actual addr list is saved under neighbor */
440 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
441 int tlv_buf_size
, uint16_t holdtime
,
442 uint32_t dr_priority
, uint32_t generation_id
,
443 uint16_t propagation_delay
, uint16_t override_interval
,
444 int can_disable_join_suppression
)
446 uint8_t *curr
= tlv_buf
;
447 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
449 struct pim_interface
*pim_ifp
= ifp
->info
;
450 struct pim_instance
*pim
= pim_ifp
->pim
;
457 curr
= pim_tlv_append_uint16(curr
, pastend
,
458 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
460 if (PIM_DEBUG_PIM_HELLO
) {
462 "%s: could not set PIM hello Holdtime option for interface %s",
463 __PRETTY_FUNCTION__
, ifp
->name
);
468 /* LAN Prune Delay */
469 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
470 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
471 propagation_delay
, override_interval
);
473 if (PIM_DEBUG_PIM_HELLO
) {
475 "%s: could not set PIM LAN Prune Delay option for interface %s",
476 __PRETTY_FUNCTION__
, ifp
->name
);
480 if (can_disable_join_suppression
) {
481 *((uint8_t *)(curr
) + 4) |= 0x80; /* enable T bit */
486 curr
= pim_tlv_append_uint32(
487 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
489 if (PIM_DEBUG_PIM_HELLO
) {
491 "%s: could not set PIM hello DR Priority option for interface %s",
492 __PRETTY_FUNCTION__
, ifp
->name
);
498 curr
= pim_tlv_append_uint32(curr
, pastend
,
499 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
502 if (PIM_DEBUG_PIM_HELLO
) {
504 "%s: could not set PIM hello Generation ID option for interface %s",
505 __PRETTY_FUNCTION__
, ifp
->name
);
510 /* Secondary Address List */
511 if (ifp
->connected
->count
) {
512 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
513 ifp
->connected
, AF_INET
);
515 if (PIM_DEBUG_PIM_HELLO
) {
517 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
518 __PRETTY_FUNCTION__
, ifp
->name
);
522 if (pim
->send_v6_secondary
) {
523 curr
= pim_tlv_append_addrlist_ucast(
524 curr
, pastend
, ifp
->connected
, AF_INET6
);
526 if (PIM_DEBUG_PIM_HELLO
) {
528 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
529 __PRETTY_FUNCTION__
, ifp
->name
);
536 return curr
- tlv_buf
;
540 RFC 4601: 4.3.1. Sending Hello Messages
542 Thus, if a router needs to send a Join/Prune or Assert message on an
543 interface on which it has not yet sent a Hello message with the
544 currently configured IP address, then it MUST immediately send the
545 relevant Hello message without waiting for the Hello Timer to
546 expire, followed by the Join/Prune or Assert message.
548 void pim_hello_require(struct interface
*ifp
)
550 struct pim_interface
*pim_ifp
;
558 if (pim_ifp
->pim_ifstat_hello_sent
)
561 pim_hello_restart_now(ifp
); /* Send hello and restart timer */