]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_hello.c
*: reindent
[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
35 static void on_trace(const char *label, struct interface *ifp,
36 struct in_addr src)
37 {
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);
42 }
43 }
44
45 static void tlv_trace_bool(const char *label, const char *tlv_name,
46 const char *ifname, struct in_addr src_addr,
47 int isset, int value)
48 {
49 if (isset) {
50 char src_str[INET_ADDRSTRLEN];
51 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
52 zlog_debug(
53 "%s: PIM hello option from %s on interface %s: %s=%d",
54 label, src_str, ifname, tlv_name, value);
55 }
56 }
57
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)
61 {
62 if (isset) {
63 char src_str[INET_ADDRSTRLEN];
64 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
65 zlog_debug(
66 "%s: PIM hello option from %s on interface %s: %s=%u",
67 label, src_str, ifname, tlv_name, value);
68 }
69 }
70
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)
74 {
75 if (isset) {
76 char src_str[INET_ADDRSTRLEN];
77 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
78 zlog_debug(
79 "%s: PIM hello option from %s on interface %s: %s=%u",
80 label, src_str, ifname, tlv_name, value);
81 }
82 }
83
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)
87 {
88 if (isset) {
89 char src_str[INET_ADDRSTRLEN];
90 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
91 zlog_debug(
92 "%s: PIM hello option from %s on interface %s: %s=%08x",
93 label, src_str, ifname, tlv_name, value);
94 }
95 }
96
97 #if 0
98 static void tlv_trace(const char *label, const char *tlv_name,
99 const char *ifname, struct in_addr src_addr,
100 int isset)
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("%s: PIM hello option from %s on interface %s: %s",
106 label,
107 src_str, ifname,
108 tlv_name);
109 }
110 }
111 #endif
112
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)
116 {
117 if (isset) {
118 char src_str[INET_ADDRSTRLEN];
119 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
120 zlog_debug(
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,
124 (void *)addr_list);
125 }
126 }
127
128 #define FREE_ADDR_LIST \
129 if (hello_option_addr_list) { \
130 list_delete(hello_option_addr_list); \
131 }
132
133 #define FREE_ADDR_LIST_THEN_RETURN(code) \
134 { \
135 FREE_ADDR_LIST \
136 return (code); \
137 }
138
139 int pim_hello_recv(struct interface *ifp, struct in_addr src_addr,
140 uint8_t *tlv_buf, int tlv_buf_size)
141 {
142 struct pim_interface *pim_ifp;
143 struct pim_neighbor *neigh;
144 uint8_t *tlv_curr;
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;
154
155 if (PIM_DEBUG_PIM_HELLO)
156 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
157
158 pim_ifp = ifp->info;
159 zassert(pim_ifp);
160
161 ++pim_ifp->pim_ifstat_hello_recv;
162
163 /*
164 Parse PIM hello TLVs
165 */
166 zassert(tlv_buf_size >= 0);
167 tlv_curr = tlv_buf;
168 tlv_pastend = tlv_buf + tlv_buf_size;
169
170 while (tlv_curr < tlv_pastend) {
171 uint16_t option_type;
172 uint16_t option_len;
173 int remain = tlv_pastend - tlv_curr;
174
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,
179 sizeof(src_str));
180 zlog_debug(
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);
184 }
185 FREE_ADDR_LIST_THEN_RETURN(-1);
186 }
187
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;
192
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,
197 sizeof(src_str));
198 zlog_debug(
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,
202 src_str, ifp->name);
203 }
204 FREE_ADDR_LIST_THEN_RETURN(-2);
205 }
206
207 if (PIM_DEBUG_PIM_HELLO) {
208 char src_str[INET_ADDRSTRLEN];
209 pim_inet4_dump("<src?>", src_addr, src_str,
210 sizeof(src_str));
211 zlog_debug(
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);
215 }
216
217 switch (option_type) {
218 case PIM_MSG_OPTION_TYPE_HOLDTIME:
219 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
220 &hello_options,
221 &hello_option_holdtime,
222 option_len, tlv_curr)) {
223 FREE_ADDR_LIST_THEN_RETURN(-3);
224 }
225 break;
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,
231 tlv_curr)) {
232 FREE_ADDR_LIST_THEN_RETURN(-4);
233 }
234 break;
235 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
236 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
237 &hello_options,
238 &hello_option_dr_priority,
239 option_len, tlv_curr)) {
240 FREE_ADDR_LIST_THEN_RETURN(-5);
241 }
242 break;
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,
247 tlv_curr)) {
248 FREE_ADDR_LIST_THEN_RETURN(-6);
249 }
250 break;
251 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
252 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
253 &hello_options,
254 &hello_option_addr_list,
255 option_len, tlv_curr)) {
256 return -7;
257 }
258 break;
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,
263 sizeof(src_str));
264 zlog_debug(
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);
268 }
269 break;
270 default:
271 if (PIM_DEBUG_PIM_HELLO) {
272 char src_str[INET_ADDRSTRLEN];
273 pim_inet4_dump("<src?>", src_addr, src_str,
274 sizeof(src_str));
275 zlog_debug(
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);
279 }
280 }
281
282 tlv_curr += option_len;
283 }
284
285 /*
286 Check received PIM hello options
287 */
288
289 if (PIM_DEBUG_PIM_HELLO) {
290 tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime", ifp->name,
291 src_addr,
292 PIM_OPTION_IS_SET(hello_options,
293 PIM_OPTION_MASK_HOLDTIME),
294 hello_option_holdtime);
295 tlv_trace_uint16(
296 __PRETTY_FUNCTION__, "propagation_delay", ifp->name,
297 src_addr,
298 PIM_OPTION_IS_SET(hello_options,
299 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
300 hello_option_propagation_delay);
301 tlv_trace_uint16(
302 __PRETTY_FUNCTION__, "override_interval", ifp->name,
303 src_addr,
304 PIM_OPTION_IS_SET(hello_options,
305 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
306 hello_option_override_interval);
307 tlv_trace_bool(
308 __PRETTY_FUNCTION__, "can_disable_join_suppression",
309 ifp->name, src_addr,
310 PIM_OPTION_IS_SET(hello_options,
311 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
312 PIM_OPTION_IS_SET(
313 hello_options,
314 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
315 tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority", ifp->name,
316 src_addr,
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,
322 src_addr,
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,
327 src_addr,
328 PIM_OPTION_IS_SET(hello_options,
329 PIM_OPTION_MASK_ADDRESS_LIST),
330 hello_option_addr_list);
331 }
332
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,
337 sizeof(src_str));
338 zlog_debug(
339 "%s: PIM hello missing holdtime from %s on interface %s",
340 __PRETTY_FUNCTION__, src_str, ifp->name);
341 }
342 }
343
344 /*
345 New neighbor?
346 */
347
348 neigh = pim_neighbor_find(ifp, src_addr);
349 if (!neigh) {
350 /* Add as new neighbor */
351
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);
358 if (!neigh) {
359 if (PIM_DEBUG_PIM_HELLO) {
360 char src_str[INET_ADDRSTRLEN];
361 pim_inet4_dump("<src?>", src_addr, src_str,
362 sizeof(src_str));
363 zlog_warn(
364 "%s: failure creating PIM neighbor %s on interface %s",
365 __PRETTY_FUNCTION__, src_str,
366 ifp->name);
367 }
368 FREE_ADDR_LIST_THEN_RETURN(-8);
369 }
370
371 /* actual addr list has been saved under neighbor */
372 return 0;
373 }
374
375 /*
376 Received generation ID ?
377 */
378
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 */
385
386 if (PIM_DEBUG_PIM_HELLO) {
387 char src_str[INET_ADDRSTRLEN];
388 pim_inet4_dump("<src?>", src_addr, src_str,
389 sizeof(src_str));
390 zlog_debug(
391 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
392 __PRETTY_FUNCTION__,
393 hello_option_generation_id,
394 neigh->generation_id, src_str,
395 ifp->name);
396 }
397
398 pim_upstream_rpf_genid_changed(neigh->source_addr);
399
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);
409 if (!neigh) {
410 if (PIM_DEBUG_PIM_HELLO) {
411 char src_str[INET_ADDRSTRLEN];
412 pim_inet4_dump("<src?>", src_addr,
413 src_str,
414 sizeof(src_str));
415 zlog_debug(
416 "%s: failure re-creating PIM neighbor %s on interface %s",
417 __PRETTY_FUNCTION__, src_str,
418 ifp->name);
419 }
420 FREE_ADDR_LIST_THEN_RETURN(-9);
421 }
422 /* actual addr list is saved under neighbor */
423 return 0;
424
425 } /* GenId mismatch: replace neighbor */
426
427 } /* GenId received */
428
429 /*
430 Update existing neighbor
431 */
432
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 */
436 return 0;
437 }
438
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)
444 {
445 uint8_t *curr = tlv_buf;
446 uint8_t *pastend = tlv_buf + tlv_buf_size;
447 uint8_t *tmp;
448
449 /*
450 * Append options
451 */
452
453 /* Holdtime */
454 curr = pim_tlv_append_uint16(curr, pastend,
455 PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
456 if (!curr) {
457 if (PIM_DEBUG_PIM_HELLO) {
458 zlog_debug(
459 "%s: could not set PIM hello Holdtime option for interface %s",
460 __PRETTY_FUNCTION__, ifp->name);
461 }
462 return -1;
463 }
464
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);
469 if (!tmp) {
470 if (PIM_DEBUG_PIM_HELLO) {
471 zlog_debug(
472 "%s: could not set PIM LAN Prune Delay option for interface %s",
473 __PRETTY_FUNCTION__, ifp->name);
474 }
475 return -1;
476 }
477 if (can_disable_join_suppression) {
478 *((uint8_t *)(curr) + 4) |= 0x80; /* enable T bit */
479 }
480 curr = tmp;
481
482 /* DR Priority */
483 curr = pim_tlv_append_uint32(
484 curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
485 if (!curr) {
486 if (PIM_DEBUG_PIM_HELLO) {
487 zlog_debug(
488 "%s: could not set PIM hello DR Priority option for interface %s",
489 __PRETTY_FUNCTION__, ifp->name);
490 }
491 return -2;
492 }
493
494 /* Generation ID */
495 curr = pim_tlv_append_uint32(curr, pastend,
496 PIM_MSG_OPTION_TYPE_GENERATION_ID,
497 generation_id);
498 if (!curr) {
499 if (PIM_DEBUG_PIM_HELLO) {
500 zlog_debug(
501 "%s: could not set PIM hello Generation ID option for interface %s",
502 __PRETTY_FUNCTION__, ifp->name);
503 }
504 return -3;
505 }
506
507 /* Secondary Address List */
508 if (ifp->connected->count) {
509 curr = pim_tlv_append_addrlist_ucast(curr, pastend,
510 ifp->connected, AF_INET);
511 if (!curr) {
512 if (PIM_DEBUG_PIM_HELLO) {
513 zlog_debug(
514 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
515 __PRETTY_FUNCTION__, ifp->name);
516 }
517 return -4;
518 }
519 if (pimg->send_v6_secondary) {
520 curr = pim_tlv_append_addrlist_ucast(
521 curr, pastend, ifp->connected, AF_INET6);
522 if (!curr) {
523 if (PIM_DEBUG_PIM_HELLO) {
524 zlog_debug(
525 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
526 __PRETTY_FUNCTION__, ifp->name);
527 }
528 return -4;
529 }
530 }
531 }
532
533 return curr - tlv_buf;
534 }
535
536 /*
537 RFC 4601: 4.3.1. Sending Hello Messages
538
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.
544 */
545 void pim_hello_require(struct interface *ifp)
546 {
547 struct pim_interface *pim_ifp;
548
549 zassert(ifp);
550
551 pim_ifp = ifp->info;
552
553 zassert(pim_ifp);
554
555 if (pim_ifp->pim_ifstat_hello_sent)
556 return;
557
558 pim_hello_restart_now(ifp); /* Send hello and restart timer */
559 }