blob: bfc128b97aa25522e7dae3eac1d2170debb0c674 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
20 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "log.h"
26
27#include "pimd.h"
28#include "pim_pim.h"
29#include "pim_str.h"
30#include "pim_tlv.h"
31#include "pim_util.h"
32#include "pim_hello.h"
33#include "pim_iface.h"
34#include "pim_neighbor.h"
35#include "pim_upstream.h"
36
37static void on_trace(const char *label,
38 struct interface *ifp, struct in_addr src)
39{
40 if (PIM_DEBUG_PIM_TRACE) {
41 char src_str[100];
42 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
43 zlog_debug("%s: from %s on %s",
44 label, src_str, ifp->name);
45 }
46}
47
48static void tlv_trace_bool(const char *label, const char *tlv_name,
49 const char *ifname, struct in_addr src_addr,
50 int isset, int value)
51{
52 if (isset) {
53 char src_str[100];
54 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
55 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
56 label,
57 src_str, ifname,
58 tlv_name, value);
59 }
60}
61
62static void tlv_trace_uint16(const char *label, const char *tlv_name,
63 const char *ifname, struct in_addr src_addr,
64 int isset, uint16_t value)
65{
66 if (isset) {
67 char src_str[100];
68 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
69 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
70 label,
71 src_str, ifname,
72 tlv_name, value);
73 }
74}
75
76static void tlv_trace_uint32(const char *label, const char *tlv_name,
77 const char *ifname, struct in_addr src_addr,
78 int isset, uint32_t value)
79{
80 if (isset) {
81 char src_str[100];
82 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
83 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
84 label,
85 src_str, ifname,
86 tlv_name, value);
87 }
88}
89
90static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
91 const char *ifname, struct in_addr src_addr,
92 int isset, uint32_t value)
93{
94 if (isset) {
95 char src_str[100];
96 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
97 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
98 label,
99 src_str, ifname,
100 tlv_name, value);
101 }
102}
103
104#if 0
105static void tlv_trace(const char *label, const char *tlv_name,
106 const char *ifname, struct in_addr src_addr,
107 int isset)
108{
109 if (isset) {
110 char src_str[100];
111 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
112 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
113 label,
114 src_str, ifname,
115 tlv_name);
116 }
117}
118#endif
119
120static void tlv_trace_list(const char *label, const char *tlv_name,
121 const char *ifname, struct in_addr src_addr,
122 int isset, struct list *addr_list)
123{
124 if (isset) {
125 char src_str[100];
126 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
David Lamparter105ad862012-02-16 04:50:35 +0000127 zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
Everton Marques871dbcf2009-08-11 15:43:05 -0300128 label,
129 src_str, ifname,
130 tlv_name,
131 addr_list ? ((int) listcount(addr_list)) : -1,
David Lamparter105ad862012-02-16 04:50:35 +0000132 (void *) addr_list);
Everton Marques871dbcf2009-08-11 15:43:05 -0300133 }
134}
135
136#define FREE_ADDR_LIST \
137 if (hello_option_addr_list) { \
138 list_delete(hello_option_addr_list); \
139 }
140
141#define FREE_ADDR_LIST_THEN_RETURN(code) \
142{ \
143 FREE_ADDR_LIST \
144 return (code); \
145}
146
147int pim_hello_recv(struct interface *ifp,
148 struct in_addr src_addr,
David Lamparterf8cfeb22012-02-16 04:31:08 +0000149 uint8_t *tlv_buf, int tlv_buf_size)
Everton Marques871dbcf2009-08-11 15:43:05 -0300150{
151 struct pim_interface *pim_ifp;
152 struct pim_neighbor *neigh;
David Lamparterf8cfeb22012-02-16 04:31:08 +0000153 uint8_t *tlv_curr;
154 uint8_t *tlv_pastend;
Everton Marques871dbcf2009-08-11 15:43:05 -0300155 pim_hello_options hello_options = 0; /* bit array recording options found */
156 uint16_t hello_option_holdtime = 0;
157 uint16_t hello_option_propagation_delay = 0;
158 uint16_t hello_option_override_interval = 0;
159 uint32_t hello_option_dr_priority = 0;
160 uint32_t hello_option_generation_id = 0;
161 struct list *hello_option_addr_list = 0;
162
Donald Sharp6d853c42015-10-21 16:13:51 -0400163 if (PIM_DEBUG_PIM_HELLO)
164 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300165
166 pim_ifp = ifp->info;
167 zassert(pim_ifp);
168
169 ++pim_ifp->pim_ifstat_hello_recv;
170
171 /*
172 Parse PIM hello TLVs
173 */
174 zassert(tlv_buf_size >= 0);
175 tlv_curr = tlv_buf;
176 tlv_pastend = tlv_buf + tlv_buf_size;
177
178 while (tlv_curr < tlv_pastend) {
179 uint16_t option_type;
180 uint16_t option_len;
181 int remain = tlv_pastend - tlv_curr;
182
183 if (remain < PIM_TLV_MIN_SIZE) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400184 if (PIM_DEBUG_PIM_HELLO) {
185 char src_str[100];
186 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
187 zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
188 __PRETTY_FUNCTION__,
189 remain, PIM_TLV_MIN_SIZE,
190 src_str, ifp->name);
191 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300192 FREE_ADDR_LIST_THEN_RETURN(-1);
193 }
194
195 option_type = PIM_TLV_GET_TYPE(tlv_curr);
196 tlv_curr += PIM_TLV_TYPE_SIZE;
197 option_len = PIM_TLV_GET_LENGTH(tlv_curr);
198 tlv_curr += PIM_TLV_LENGTH_SIZE;
199
200 if ((tlv_curr + option_len) > tlv_pastend) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400201 if (PIM_DEBUG_PIM_HELLO) {
202 char src_str[100];
203 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
204 zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
205 __PRETTY_FUNCTION__,
206 option_type, option_len, tlv_pastend - tlv_curr,
207 src_str, ifp->name);
208 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300209 FREE_ADDR_LIST_THEN_RETURN(-2);
210 }
211
Donald Sharp6d853c42015-10-21 16:13:51 -0400212 if (PIM_DEBUG_PIM_HELLO) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300213 char src_str[100];
214 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
215 zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
216 __PRETTY_FUNCTION__,
217 remain,
218 option_type, option_len,
219 src_str, ifp->name);
220 }
221
222 switch (option_type) {
223 case PIM_MSG_OPTION_TYPE_HOLDTIME:
224 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
225 &hello_options,
226 &hello_option_holdtime,
227 option_len,
228 tlv_curr)) {
229 FREE_ADDR_LIST_THEN_RETURN(-3);
230 }
231 break;
232 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
233 if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
234 &hello_options,
235 &hello_option_propagation_delay,
236 &hello_option_override_interval,
237 option_len,
238 tlv_curr)) {
239 FREE_ADDR_LIST_THEN_RETURN(-4);
240 }
241 break;
242 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
243 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
244 &hello_options,
245 &hello_option_dr_priority,
246 option_len,
247 tlv_curr)) {
248 FREE_ADDR_LIST_THEN_RETURN(-5);
249 }
250 break;
251 case PIM_MSG_OPTION_TYPE_GENERATION_ID:
252 if (pim_tlv_parse_generation_id(ifp->name, src_addr,
253 &hello_options,
254 &hello_option_generation_id,
255 option_len,
256 tlv_curr)) {
257 FREE_ADDR_LIST_THEN_RETURN(-6);
258 }
259 break;
260 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
261 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
262 &hello_options,
263 &hello_option_addr_list,
264 option_len,
265 tlv_curr)) {
266 return -7;
267 }
268 break;
269 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
Donald Sharp6d853c42015-10-21 16:13:51 -0400270 if (PIM_DEBUG_PIM_HELLO) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300271 char src_str[100];
272 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
273 zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
274 __PRETTY_FUNCTION__,
275 option_type, option_len,
276 src_str, ifp->name);
277 }
278 break;
279 default:
Donald Sharp6d853c42015-10-21 16:13:51 -0400280 if (PIM_DEBUG_PIM_HELLO) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300281 char src_str[100];
282 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
Donald Sharp6d853c42015-10-21 16:13:51 -0400283 zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
284 __PRETTY_FUNCTION__,
285 option_type, option_len,
286 src_str, ifp->name);
Everton Marques871dbcf2009-08-11 15:43:05 -0300287 }
288 }
289
290 tlv_curr += option_len;
291 }
292
293 /*
294 Check received PIM hello options
295 */
296
Donald Sharp6d853c42015-10-21 16:13:51 -0400297 if (PIM_DEBUG_PIM_HELLO) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300298 tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
299 ifp->name, src_addr,
300 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
301 hello_option_holdtime);
302 tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
303 ifp->name, src_addr,
304 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
305 hello_option_propagation_delay);
306 tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
307 ifp->name, src_addr,
308 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
309 hello_option_override_interval);
310 tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
311 ifp->name, src_addr,
312 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
313 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
314 tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
315 ifp->name, src_addr,
316 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
317 hello_option_dr_priority);
318 tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
319 ifp->name, src_addr,
320 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
321 hello_option_generation_id);
322 tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
323 ifp->name, src_addr,
324 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
325 hello_option_addr_list);
326 }
327
328 if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400329 if (PIM_DEBUG_PIM_HELLO) {
330 char src_str[100];
331 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
332 zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
333 __PRETTY_FUNCTION__,
334 src_str, ifp->name);
335 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300336 }
337
338 /*
339 New neighbor?
340 */
341
342 neigh = pim_neighbor_find(ifp, src_addr);
343 if (!neigh) {
344 /* Add as new neighbor */
345
346 neigh = pim_neighbor_add(ifp, src_addr,
347 hello_options,
348 hello_option_holdtime,
349 hello_option_propagation_delay,
350 hello_option_override_interval,
351 hello_option_dr_priority,
352 hello_option_generation_id,
353 hello_option_addr_list);
354 if (!neigh) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400355 if (PIM_DEBUG_PIM_HELLO) {
356 char src_str[100];
357 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
358 zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
359 __PRETTY_FUNCTION__,
360 src_str, ifp->name);
361 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300362 FREE_ADDR_LIST_THEN_RETURN(-8);
363 }
364
365 /* actual addr list has been saved under neighbor */
366 return 0;
367 }
368
369 /*
370 Received generation ID ?
371 */
372
373 if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
374 /* GenID mismatch ? */
375 if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
376 (hello_option_generation_id != neigh->generation_id)) {
377
378 /* GenID changed */
379
380 pim_upstream_rpf_genid_changed(neigh->source_addr);
381
382 /* GenID mismatch, then replace neighbor */
383
Donald Sharp6d853c42015-10-21 16:13:51 -0400384 if (PIM_DEBUG_PIM_HELLO) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300385 char src_str[100];
386 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
387 zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
388 __PRETTY_FUNCTION__,
389 hello_option_generation_id,
390 neigh->generation_id,
391 src_str, ifp->name);
392 }
393
394 pim_upstream_rpf_genid_changed(neigh->source_addr);
395
396 pim_neighbor_delete(ifp, neigh, "GenID mismatch");
397 neigh = pim_neighbor_add(ifp, src_addr,
398 hello_options,
399 hello_option_holdtime,
400 hello_option_propagation_delay,
401 hello_option_override_interval,
402 hello_option_dr_priority,
403 hello_option_generation_id,
404 hello_option_addr_list);
405 if (!neigh) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400406 if (PIM_DEBUG_PIM_HELLO) {
407 char src_str[100];
408 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
409 zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
410 __PRETTY_FUNCTION__,
411 src_str, ifp->name);
412 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300413 FREE_ADDR_LIST_THEN_RETURN(-9);
414 }
415 /* actual addr list is saved under neighbor */
416 return 0;
417
418 } /* GenId mismatch: replace neighbor */
419
420 } /* GenId received */
421
422 /*
423 Update existing neighbor
424 */
425
426 pim_neighbor_update(neigh,
427 hello_options,
428 hello_option_holdtime,
429 hello_option_dr_priority,
430 hello_option_addr_list);
431 /* actual addr list is saved under neighbor */
432 return 0;
433}
434
435int pim_hello_build_tlv(const char *ifname,
David Lamparterf8cfeb22012-02-16 04:31:08 +0000436 uint8_t *tlv_buf, int tlv_buf_size,
Everton Marques871dbcf2009-08-11 15:43:05 -0300437 uint16_t holdtime,
438 uint32_t dr_priority,
439 uint32_t generation_id,
440 uint16_t propagation_delay,
441 uint16_t override_interval,
442 int can_disable_join_suppression,
443 struct list *ifconnected)
444{
David Lamparterf8cfeb22012-02-16 04:31:08 +0000445 uint8_t *curr = tlv_buf;
446 uint8_t *pastend = tlv_buf + tlv_buf_size;
447 uint8_t *tmp;
Everton Marques871dbcf2009-08-11 15:43:05 -0300448
449 /*
450 * Append options
451 */
452
453 /* Holdtime */
454 curr = pim_tlv_append_uint16(curr,
455 pastend,
456 PIM_MSG_OPTION_TYPE_HOLDTIME,
457 holdtime);
458 if (!curr) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400459 if (PIM_DEBUG_PIM_HELLO) {
460 zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
461 __PRETTY_FUNCTION__, ifname);
462 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300463 return -1;
464 }
465
466 /* LAN Prune Delay */
467 tmp = pim_tlv_append_2uint16(curr,
468 pastend,
469 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
470 propagation_delay,
471 override_interval);
472 if (!tmp) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400473 if (PIM_DEBUG_PIM_HELLO) {
474 zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
475 __PRETTY_FUNCTION__, ifname);
476 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300477 return -1;
478 }
479 if (can_disable_join_suppression) {
480 *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
481 }
482 curr = tmp;
483
484 /* DR Priority */
485 curr = pim_tlv_append_uint32(curr,
486 pastend,
487 PIM_MSG_OPTION_TYPE_DR_PRIORITY,
488 dr_priority);
489 if (!curr) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400490 if (PIM_DEBUG_PIM_HELLO) {
491 zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
492 __PRETTY_FUNCTION__, ifname);
493 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300494 return -2;
495 }
496
497 /* Generation ID */
498 curr = pim_tlv_append_uint32(curr,
499 pastend,
500 PIM_MSG_OPTION_TYPE_GENERATION_ID,
501 generation_id);
502 if (!curr) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400503 if (PIM_DEBUG_PIM_HELLO) {
504 zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
505 __PRETTY_FUNCTION__, ifname);
506 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300507 return -3;
508 }
509
510 /* Secondary Address List */
511 if (ifconnected) {
512 curr = pim_tlv_append_addrlist_ucast(curr,
513 pastend,
514 ifconnected);
515 if (!curr) {
Donald Sharp6d853c42015-10-21 16:13:51 -0400516 if (PIM_DEBUG_PIM_HELLO) {
517 zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
518 __PRETTY_FUNCTION__, ifname);
519 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300520 return -4;
521 }
522 }
523
524 return curr - tlv_buf;
525}
526
527/*
528 RFC 4601: 4.3.1. Sending Hello Messages
529
530 Thus, if a router needs to send a Join/Prune or Assert message on an
531 interface on which it has not yet sent a Hello message with the
532 currently configured IP address, then it MUST immediately send the
533 relevant Hello message without waiting for the Hello Timer to
534 expire, followed by the Join/Prune or Assert message.
535*/
536void pim_hello_require(struct interface *ifp)
537{
538 struct pim_interface *pim_ifp;
539
540 zassert(ifp);
541
542 pim_ifp = ifp->info;
543
544 zassert(pim_ifp);
545
546 if (pim_ifp->pim_ifstat_hello_sent)
547 return;
548
549 pim_hello_restart_now(ifp); /* Send hello and restart timer */
550}