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(neigh
->source_addr
);
400 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
401 neigh
= pim_neighbor_add(ifp
, src_addr
, hello_options
,
402 hello_option_holdtime
,
403 hello_option_propagation_delay
,
404 hello_option_override_interval
,
405 hello_option_dr_priority
,
406 hello_option_generation_id
,
407 hello_option_addr_list
,
408 PIM_NEIGHBOR_SEND_NOW
);
410 if (PIM_DEBUG_PIM_HELLO
) {
411 char src_str
[INET_ADDRSTRLEN
];
412 pim_inet4_dump("<src?>", src_addr
,
416 "%s: failure re-creating PIM neighbor %s on interface %s",
417 __PRETTY_FUNCTION__
, src_str
,
420 FREE_ADDR_LIST_THEN_RETURN(-9);
422 /* actual addr list is saved under neighbor */
425 } /* GenId mismatch: replace neighbor */
427 } /* GenId received */
430 Update existing neighbor
433 pim_neighbor_update(neigh
, hello_options
, hello_option_holdtime
,
434 hello_option_dr_priority
, hello_option_addr_list
);
435 /* actual addr list is saved under neighbor */
439 int pim_hello_build_tlv(struct interface
*ifp
, uint8_t *tlv_buf
,
440 int tlv_buf_size
, uint16_t holdtime
,
441 uint32_t dr_priority
, uint32_t generation_id
,
442 uint16_t propagation_delay
, uint16_t override_interval
,
443 int can_disable_join_suppression
)
445 uint8_t *curr
= tlv_buf
;
446 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
454 curr
= pim_tlv_append_uint16(curr
, pastend
,
455 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
457 if (PIM_DEBUG_PIM_HELLO
) {
459 "%s: could not set PIM hello Holdtime option for interface %s",
460 __PRETTY_FUNCTION__
, ifp
->name
);
465 /* LAN Prune Delay */
466 tmp
= pim_tlv_append_2uint16(curr
, pastend
,
467 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
468 propagation_delay
, override_interval
);
470 if (PIM_DEBUG_PIM_HELLO
) {
472 "%s: could not set PIM LAN Prune Delay option for interface %s",
473 __PRETTY_FUNCTION__
, ifp
->name
);
477 if (can_disable_join_suppression
) {
478 *((uint8_t *)(curr
) + 4) |= 0x80; /* enable T bit */
483 curr
= pim_tlv_append_uint32(
484 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
486 if (PIM_DEBUG_PIM_HELLO
) {
488 "%s: could not set PIM hello DR Priority option for interface %s",
489 __PRETTY_FUNCTION__
, ifp
->name
);
495 curr
= pim_tlv_append_uint32(curr
, pastend
,
496 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
499 if (PIM_DEBUG_PIM_HELLO
) {
501 "%s: could not set PIM hello Generation ID option for interface %s",
502 __PRETTY_FUNCTION__
, ifp
->name
);
507 /* Secondary Address List */
508 if (ifp
->connected
->count
) {
509 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
,
510 ifp
->connected
, AF_INET
);
512 if (PIM_DEBUG_PIM_HELLO
) {
514 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
515 __PRETTY_FUNCTION__
, ifp
->name
);
519 if (pimg
->send_v6_secondary
) {
520 curr
= pim_tlv_append_addrlist_ucast(
521 curr
, pastend
, ifp
->connected
, AF_INET6
);
523 if (PIM_DEBUG_PIM_HELLO
) {
525 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
526 __PRETTY_FUNCTION__
, ifp
->name
);
533 return curr
- tlv_buf
;
537 RFC 4601: 4.3.1. Sending Hello Messages
539 Thus, if a router needs to send a Join/Prune or Assert message on an
540 interface on which it has not yet sent a Hello message with the
541 currently configured IP address, then it MUST immediately send the
542 relevant Hello message without waiting for the Hello Timer to
543 expire, followed by the Join/Prune or Assert message.
545 void pim_hello_require(struct interface
*ifp
)
547 struct pim_interface
*pim_ifp
;
555 if (pim_ifp
->pim_ifstat_hello_sent
)
558 pim_hello_restart_now(ifp
); /* Send hello and restart timer */