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
,
36 struct interface
*ifp
, struct in_addr src
)
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",
42 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
));
53 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
60 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
61 const char *ifname
, struct in_addr src_addr
,
62 int isset
, uint16_t value
)
65 char src_str
[INET_ADDRSTRLEN
];
66 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
67 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
74 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
75 const char *ifname
, struct in_addr src_addr
,
76 int isset
, uint32_t value
)
79 char src_str
[INET_ADDRSTRLEN
];
80 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
81 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
88 static void tlv_trace_uint32_hex(const char *label
, const char *tlv_name
,
89 const char *ifname
, struct in_addr src_addr
,
90 int isset
, uint32_t value
)
93 char src_str
[INET_ADDRSTRLEN
];
94 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
95 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
103 static void tlv_trace(const char *label
, const char *tlv_name
,
104 const char *ifname
, struct in_addr src_addr
,
108 char src_str
[INET_ADDRSTRLEN
];
109 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
110 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
118 static void tlv_trace_list(const char *label
, const char *tlv_name
,
119 const char *ifname
, struct in_addr src_addr
,
120 int isset
, struct list
*addr_list
)
123 char src_str
[INET_ADDRSTRLEN
];
124 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
125 zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
129 addr_list
? ((int) listcount(addr_list
)) : -1,
134 #define FREE_ADDR_LIST \
135 if (hello_option_addr_list) { \
136 list_delete(hello_option_addr_list); \
139 #define FREE_ADDR_LIST_THEN_RETURN(code) \
145 int pim_hello_recv(struct interface
*ifp
,
146 struct in_addr src_addr
,
147 uint8_t *tlv_buf
, int tlv_buf_size
)
149 struct pim_interface
*pim_ifp
;
150 struct pim_neighbor
*neigh
;
152 uint8_t *tlv_pastend
;
153 pim_hello_options hello_options
= 0; /* bit array recording options found */
154 uint16_t hello_option_holdtime
= 0;
155 uint16_t hello_option_propagation_delay
= 0;
156 uint16_t hello_option_override_interval
= 0;
157 uint32_t hello_option_dr_priority
= 0;
158 uint32_t hello_option_generation_id
= 0;
159 struct list
*hello_option_addr_list
= 0;
161 if (PIM_DEBUG_PIM_HELLO
)
162 on_trace(__PRETTY_FUNCTION__
, ifp
, src_addr
);
167 ++pim_ifp
->pim_ifstat_hello_recv
;
172 zassert(tlv_buf_size
>= 0);
174 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
176 while (tlv_curr
< tlv_pastend
) {
177 uint16_t option_type
;
179 int remain
= tlv_pastend
- tlv_curr
;
181 if (remain
< PIM_TLV_MIN_SIZE
) {
182 if (PIM_DEBUG_PIM_HELLO
) {
183 char src_str
[INET_ADDRSTRLEN
];
184 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
185 zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
187 remain
, PIM_TLV_MIN_SIZE
,
190 FREE_ADDR_LIST_THEN_RETURN(-1);
193 option_type
= PIM_TLV_GET_TYPE(tlv_curr
);
194 tlv_curr
+= PIM_TLV_TYPE_SIZE
;
195 option_len
= PIM_TLV_GET_LENGTH(tlv_curr
);
196 tlv_curr
+= PIM_TLV_LENGTH_SIZE
;
198 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
199 if (PIM_DEBUG_PIM_HELLO
) {
200 char src_str
[INET_ADDRSTRLEN
];
201 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
202 zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
204 option_type
, option_len
, tlv_pastend
- tlv_curr
,
207 FREE_ADDR_LIST_THEN_RETURN(-2);
210 if (PIM_DEBUG_PIM_HELLO
) {
211 char src_str
[INET_ADDRSTRLEN
];
212 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
213 zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
216 option_type
, option_len
,
220 switch (option_type
) {
221 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
222 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
224 &hello_option_holdtime
,
227 FREE_ADDR_LIST_THEN_RETURN(-3);
230 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
:
231 if (pim_tlv_parse_lan_prune_delay(ifp
->name
, src_addr
,
233 &hello_option_propagation_delay
,
234 &hello_option_override_interval
,
237 FREE_ADDR_LIST_THEN_RETURN(-4);
240 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
241 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
243 &hello_option_dr_priority
,
246 FREE_ADDR_LIST_THEN_RETURN(-5);
249 case PIM_MSG_OPTION_TYPE_GENERATION_ID
:
250 if (pim_tlv_parse_generation_id(ifp
->name
, src_addr
,
252 &hello_option_generation_id
,
255 FREE_ADDR_LIST_THEN_RETURN(-6);
258 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
259 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
261 &hello_option_addr_list
,
267 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
268 if (PIM_DEBUG_PIM_HELLO
) {
269 char src_str
[INET_ADDRSTRLEN
];
270 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
271 zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
273 option_type
, option_len
,
278 if (PIM_DEBUG_PIM_HELLO
) {
279 char src_str
[INET_ADDRSTRLEN
];
280 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
281 zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
283 option_type
, option_len
,
288 tlv_curr
+= option_len
;
292 Check received PIM hello options
295 if (PIM_DEBUG_PIM_HELLO
) {
296 tlv_trace_uint16(__PRETTY_FUNCTION__
, "holdtime",
298 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
),
299 hello_option_holdtime
);
300 tlv_trace_uint16(__PRETTY_FUNCTION__
, "propagation_delay",
302 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
303 hello_option_propagation_delay
);
304 tlv_trace_uint16(__PRETTY_FUNCTION__
, "override_interval",
306 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
307 hello_option_override_interval
);
308 tlv_trace_bool(__PRETTY_FUNCTION__
, "can_disable_join_suppression",
310 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
311 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION
));
312 tlv_trace_uint32(__PRETTY_FUNCTION__
, "dr_priority",
314 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_DR_PRIORITY
),
315 hello_option_dr_priority
);
316 tlv_trace_uint32_hex(__PRETTY_FUNCTION__
, "generation_id",
318 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
),
319 hello_option_generation_id
);
320 tlv_trace_list(__PRETTY_FUNCTION__
, "address_list",
322 PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_ADDRESS_LIST
),
323 hello_option_addr_list
);
326 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
327 if (PIM_DEBUG_PIM_HELLO
) {
328 char src_str
[INET_ADDRSTRLEN
];
329 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
330 zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
340 neigh
= pim_neighbor_find(ifp
, src_addr
);
342 /* Add as new neighbor */
344 neigh
= pim_neighbor_add(ifp
, src_addr
,
346 hello_option_holdtime
,
347 hello_option_propagation_delay
,
348 hello_option_override_interval
,
349 hello_option_dr_priority
,
350 hello_option_generation_id
,
351 hello_option_addr_list
,
352 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
, sizeof(src_str
));
357 zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
361 FREE_ADDR_LIST_THEN_RETURN(-8);
364 /* actual addr list has been saved under neighbor */
369 Received generation ID ?
372 if (PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_GENERATION_ID
)) {
373 /* GenID mismatch ? */
374 if (!PIM_OPTION_IS_SET(neigh
->hello_options
, PIM_OPTION_MASK_GENERATION_ID
) ||
375 (hello_option_generation_id
!= neigh
->generation_id
)) {
376 /* GenID mismatch, then replace neighbor */
378 if (PIM_DEBUG_PIM_HELLO
) {
379 char src_str
[INET_ADDRSTRLEN
];
380 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
381 zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
383 hello_option_generation_id
,
384 neigh
->generation_id
,
388 pim_upstream_rpf_genid_changed(neigh
->source_addr
);
390 pim_neighbor_delete(ifp
, neigh
, "GenID mismatch");
391 neigh
= pim_neighbor_add(ifp
, src_addr
,
393 hello_option_holdtime
,
394 hello_option_propagation_delay
,
395 hello_option_override_interval
,
396 hello_option_dr_priority
,
397 hello_option_generation_id
,
398 hello_option_addr_list
,
399 PIM_NEIGHBOR_SEND_NOW
);
401 if (PIM_DEBUG_PIM_HELLO
) {
402 char src_str
[INET_ADDRSTRLEN
];
403 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
404 zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
408 FREE_ADDR_LIST_THEN_RETURN(-9);
410 /* actual addr list is saved under neighbor */
413 } /* GenId mismatch: replace neighbor */
415 } /* GenId received */
418 Update existing neighbor
421 pim_neighbor_update(neigh
,
423 hello_option_holdtime
,
424 hello_option_dr_priority
,
425 hello_option_addr_list
);
426 /* actual addr list is saved under neighbor */
430 int pim_hello_build_tlv(struct interface
*ifp
,
431 uint8_t *tlv_buf
, int tlv_buf_size
,
433 uint32_t dr_priority
,
434 uint32_t generation_id
,
435 uint16_t propagation_delay
,
436 uint16_t override_interval
,
437 int can_disable_join_suppression
)
439 uint8_t *curr
= tlv_buf
;
440 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
448 curr
= pim_tlv_append_uint16(curr
,
450 PIM_MSG_OPTION_TYPE_HOLDTIME
,
453 if (PIM_DEBUG_PIM_HELLO
) {
454 zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
455 __PRETTY_FUNCTION__
, ifp
->name
);
460 /* LAN Prune Delay */
461 tmp
= pim_tlv_append_2uint16(curr
,
463 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY
,
467 if (PIM_DEBUG_PIM_HELLO
) {
468 zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
469 __PRETTY_FUNCTION__
, ifp
->name
);
473 if (can_disable_join_suppression
) {
474 *((uint8_t*)(curr
) + 4) |= 0x80; /* enable T bit */
479 curr
= pim_tlv_append_uint32(curr
,
481 PIM_MSG_OPTION_TYPE_DR_PRIORITY
,
484 if (PIM_DEBUG_PIM_HELLO
) {
485 zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
486 __PRETTY_FUNCTION__
, ifp
->name
);
492 curr
= pim_tlv_append_uint32(curr
,
494 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
497 if (PIM_DEBUG_PIM_HELLO
) {
498 zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
499 __PRETTY_FUNCTION__
, ifp
->name
);
504 /* Secondary Address List */
505 if (ifp
->connected
->count
) {
506 curr
= pim_tlv_append_addrlist_ucast(curr
,
511 if (PIM_DEBUG_PIM_HELLO
) {
512 zlog_debug("%s: could not set PIM hello v4 Secondary Address List option for interface %s",
513 __PRETTY_FUNCTION__
, ifp
->name
);
517 if (pimg
->send_v6_secondary
)
519 curr
= pim_tlv_append_addrlist_ucast(curr
,
524 if (PIM_DEBUG_PIM_HELLO
) {
525 zlog_debug("%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 */