]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_hello.c
Merge pull request #9649 from proelbtn/add-support-for-end-dt4
[mirror_frr.git] / pimd / pim_hello.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
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.
9 *
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.
14 *
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
18 */
19
20 #include <zebra.h>
21
22 #include "log.h"
23 #include "if.h"
24
25 #include "pimd.h"
26 #include "pim_pim.h"
27 #include "pim_str.h"
28 #include "pim_tlv.h"
29 #include "pim_util.h"
30 #include "pim_hello.h"
31 #include "pim_iface.h"
32 #include "pim_neighbor.h"
33 #include "pim_upstream.h"
34 #include "pim_bsm.h"
35
36 static void on_trace(const char *label, struct interface *ifp,
37 struct in_addr src)
38 {
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);
43 }
44 }
45
46 static void tlv_trace_bool(const char *label, const char *tlv_name,
47 const char *ifname, struct in_addr src_addr,
48 int isset, int value)
49 {
50 if (isset) {
51 char src_str[INET_ADDRSTRLEN];
52 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
53 zlog_debug(
54 "%s: PIM hello option from %s on interface %s: %s=%d",
55 label, src_str, ifname, tlv_name, value);
56 }
57 }
58
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)
62 {
63 if (isset) {
64 char src_str[INET_ADDRSTRLEN];
65 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
66 zlog_debug(
67 "%s: PIM hello option from %s on interface %s: %s=%u",
68 label, src_str, ifname, tlv_name, value);
69 }
70 }
71
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)
75 {
76 if (isset) {
77 char src_str[INET_ADDRSTRLEN];
78 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
79 zlog_debug(
80 "%s: PIM hello option from %s on interface %s: %s=%u",
81 label, src_str, ifname, tlv_name, value);
82 }
83 }
84
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)
88 {
89 if (isset) {
90 char src_str[INET_ADDRSTRLEN];
91 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
92 zlog_debug(
93 "%s: PIM hello option from %s on interface %s: %s=%08x",
94 label, src_str, ifname, tlv_name, value);
95 }
96 }
97
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)
101 {
102 if (isset) {
103 char src_str[INET_ADDRSTRLEN];
104 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
105 zlog_debug(
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,
109 (void *)addr_list);
110 }
111 }
112
113 #define FREE_ADDR_LIST \
114 if (hello_option_addr_list) { \
115 list_delete(&hello_option_addr_list); \
116 }
117
118 #define FREE_ADDR_LIST_THEN_RETURN(code) \
119 { \
120 FREE_ADDR_LIST \
121 return (code); \
122 }
123
124 int pim_hello_recv(struct interface *ifp, struct in_addr src_addr,
125 uint8_t *tlv_buf, int tlv_buf_size)
126 {
127 struct pim_interface *pim_ifp;
128 struct pim_neighbor *neigh;
129 uint8_t *tlv_curr;
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;
139
140 if (PIM_DEBUG_PIM_HELLO)
141 on_trace(__func__, ifp, src_addr);
142
143 pim_ifp = ifp->info;
144 assert(pim_ifp);
145
146 ++pim_ifp->pim_ifstat_hello_recv;
147
148 /*
149 Parse PIM hello TLVs
150 */
151 assert(tlv_buf_size >= 0);
152 tlv_curr = tlv_buf;
153 tlv_pastend = tlv_buf + tlv_buf_size;
154
155 while (tlv_curr < tlv_pastend) {
156 uint16_t option_type;
157 uint16_t option_len;
158 int remain = tlv_pastend - tlv_curr;
159
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,
164 sizeof(src_str));
165 zlog_debug(
166 "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
167 __func__, remain, PIM_TLV_MIN_SIZE,
168 src_str, ifp->name);
169 }
170 FREE_ADDR_LIST_THEN_RETURN(-1);
171 }
172
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;
177
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,
182 sizeof(src_str));
183 zlog_debug(
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,
187 ifp->name);
188 }
189 FREE_ADDR_LIST_THEN_RETURN(-2);
190 }
191
192 if (PIM_DEBUG_PIM_HELLO) {
193 char src_str[INET_ADDRSTRLEN];
194 pim_inet4_dump("<src?>", src_addr, src_str,
195 sizeof(src_str));
196 zlog_debug(
197 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
198 __func__, remain, option_type, option_len,
199 src_str, ifp->name);
200 }
201
202 switch (option_type) {
203 case PIM_MSG_OPTION_TYPE_HOLDTIME:
204 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
205 &hello_options,
206 &hello_option_holdtime,
207 option_len, tlv_curr)) {
208 FREE_ADDR_LIST_THEN_RETURN(-3);
209 }
210 break;
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,
216 tlv_curr)) {
217 FREE_ADDR_LIST_THEN_RETURN(-4);
218 }
219 break;
220 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
221 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
222 &hello_options,
223 &hello_option_dr_priority,
224 option_len, tlv_curr)) {
225 FREE_ADDR_LIST_THEN_RETURN(-5);
226 }
227 break;
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,
232 tlv_curr)) {
233 FREE_ADDR_LIST_THEN_RETURN(-6);
234 }
235 break;
236 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
237 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
238 &hello_options,
239 &hello_option_addr_list,
240 option_len, tlv_curr)) {
241 return -7;
242 }
243 break;
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,
248 sizeof(src_str));
249 zlog_debug(
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,
252 src_str, ifp->name);
253 }
254 break;
255 default:
256 if (PIM_DEBUG_PIM_HELLO) {
257 char src_str[INET_ADDRSTRLEN];
258 pim_inet4_dump("<src?>", src_addr, src_str,
259 sizeof(src_str));
260 zlog_debug(
261 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
262 __func__, option_type, option_len,
263 src_str, ifp->name);
264 }
265 }
266
267 tlv_curr += option_len;
268 }
269
270 /*
271 Check received PIM hello options
272 */
273
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);
279 tlv_trace_uint16(
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);
284 tlv_trace_uint16(
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);
289 tlv_trace_bool(
290 __func__, "can_disable_join_suppression", ifp->name,
291 src_addr,
292 PIM_OPTION_IS_SET(hello_options,
293 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
294 PIM_OPTION_IS_SET(
295 hello_options,
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);
310 }
311
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,
316 sizeof(src_str));
317 zlog_debug(
318 "%s: PIM hello missing holdtime from %s on interface %s",
319 __func__, src_str, ifp->name);
320 }
321 }
322
323 /*
324 New neighbor?
325 */
326
327 neigh = pim_neighbor_find(ifp, src_addr);
328 if (!neigh) {
329 /* Add as new neighbor */
330
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);
337 if (!neigh) {
338 if (PIM_DEBUG_PIM_HELLO) {
339 char src_str[INET_ADDRSTRLEN];
340 pim_inet4_dump("<src?>", src_addr, src_str,
341 sizeof(src_str));
342 zlog_warn(
343 "%s: failure creating PIM neighbor %s on interface %s",
344 __func__, src_str, ifp->name);
345 }
346 FREE_ADDR_LIST_THEN_RETURN(-8);
347 }
348 /* Forward BSM if required */
349 if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
350 if (PIM_DEBUG_PIM_HELLO)
351 zlog_debug(
352 "%s: forwarding bsm to new nbr failed",
353 __func__);
354 }
355
356 /* actual addr list has been saved under neighbor */
357 return 0;
358 }
359
360 /*
361 Received generation ID ?
362 */
363
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 */
370
371 if (PIM_DEBUG_PIM_HELLO) {
372 char src_str[INET_ADDRSTRLEN];
373 pim_inet4_dump("<src?>", src_addr, src_str,
374 sizeof(src_str));
375 zlog_debug(
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,
379 ifp->name);
380 }
381
382 pim_upstream_rpf_genid_changed(pim_ifp->pim,
383 neigh->source_addr);
384
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);
394 if (!neigh) {
395 if (PIM_DEBUG_PIM_HELLO) {
396 char src_str[INET_ADDRSTRLEN];
397 pim_inet4_dump("<src?>", src_addr,
398 src_str,
399 sizeof(src_str));
400 zlog_debug(
401 "%s: failure re-creating PIM neighbor %s on interface %s",
402 __func__, src_str, ifp->name);
403 }
404 FREE_ADDR_LIST_THEN_RETURN(-9);
405 }
406 /* Forward BSM if required */
407 if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
408 if (PIM_DEBUG_PIM_HELLO)
409 zlog_debug(
410 "%s: forwarding bsm to new nbr failed",
411 __func__);
412 }
413 /* actual addr list is saved under neighbor */
414 return 0;
415
416 } /* GenId mismatch: replace neighbor */
417
418 } /* GenId received */
419
420 /*
421 Update existing neighbor
422 */
423
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 */
427 return 0;
428 }
429
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)
435 {
436 uint8_t *curr = tlv_buf;
437 uint8_t *pastend = tlv_buf + tlv_buf_size;
438 uint8_t *tmp;
439 struct pim_interface *pim_ifp = ifp->info;
440 struct pim_instance *pim = pim_ifp->pim;
441
442 /*
443 * Append options
444 */
445
446 /* Holdtime */
447 curr = pim_tlv_append_uint16(curr, pastend,
448 PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
449 if (!curr) {
450 if (PIM_DEBUG_PIM_HELLO) {
451 zlog_debug(
452 "%s: could not set PIM hello Holdtime option for interface %s",
453 __func__, ifp->name);
454 }
455 return -1;
456 }
457
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);
462 if (!tmp) {
463 if (PIM_DEBUG_PIM_HELLO) {
464 zlog_debug(
465 "%s: could not set PIM LAN Prune Delay option for interface %s",
466 __func__, ifp->name);
467 }
468 return -1;
469 }
470 if (can_disable_join_suppression) {
471 *(curr + 4) |= 0x80; /* enable T bit */
472 }
473 curr = tmp;
474
475 /* DR Priority */
476 curr = pim_tlv_append_uint32(
477 curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
478 if (!curr) {
479 if (PIM_DEBUG_PIM_HELLO) {
480 zlog_debug(
481 "%s: could not set PIM hello DR Priority option for interface %s",
482 __func__, ifp->name);
483 }
484 return -2;
485 }
486
487 /* Generation ID */
488 curr = pim_tlv_append_uint32(curr, pastend,
489 PIM_MSG_OPTION_TYPE_GENERATION_ID,
490 generation_id);
491 if (!curr) {
492 if (PIM_DEBUG_PIM_HELLO) {
493 zlog_debug(
494 "%s: could not set PIM hello Generation ID option for interface %s",
495 __func__, ifp->name);
496 }
497 return -3;
498 }
499
500 /* Secondary Address List */
501 if (ifp->connected->count) {
502 curr = pim_tlv_append_addrlist_ucast(curr, pastend,
503 ifp->connected, AF_INET);
504 if (!curr) {
505 if (PIM_DEBUG_PIM_HELLO) {
506 zlog_debug(
507 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
508 __func__, ifp->name);
509 }
510 return -4;
511 }
512 if (pim->send_v6_secondary) {
513 curr = pim_tlv_append_addrlist_ucast(
514 curr, pastend, ifp->connected, AF_INET6);
515 if (!curr) {
516 if (PIM_DEBUG_PIM_HELLO) {
517 zlog_debug(
518 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
519 __func__, ifp->name);
520 }
521 return -4;
522 }
523 }
524 }
525
526 return curr - tlv_buf;
527 }
528
529 /*
530 RFC 4601: 4.3.1. Sending Hello Messages
531
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.
537 */
538 void pim_hello_require(struct interface *ifp)
539 {
540 struct pim_interface *pim_ifp;
541
542 assert(ifp);
543
544 pim_ifp = ifp->info;
545
546 assert(pim_ifp);
547
548 if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp->flags))
549 return;
550
551 pim_hello_restart_now(ifp); /* Send hello and restart timer */
552 }