]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_hello.c
Merge pull request #254 from donaldsharp/shutdown3
[mirror_frr.git] / pimd / pim_hello.c
CommitLineData
12e41d03
DL
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
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
12e41d03
DL
19*/
20
21#include <zebra.h>
22
23#include "log.h"
744d91b3 24#include "if.h"
12e41d03
DL
25
26#include "pimd.h"
27#include "pim_pim.h"
28#include "pim_str.h"
29#include "pim_tlv.h"
30#include "pim_util.h"
31#include "pim_hello.h"
32#include "pim_iface.h"
33#include "pim_neighbor.h"
34#include "pim_upstream.h"
35
36static void on_trace(const char *label,
37 struct interface *ifp, struct in_addr src)
38{
39 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb 40 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
41 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
42 zlog_debug("%s: from %s on %s",
43 label, src_str, ifp->name);
44 }
45}
46
47static void tlv_trace_bool(const char *label, const char *tlv_name,
48 const char *ifname, struct in_addr src_addr,
49 int isset, int value)
50{
51 if (isset) {
eaa54bdb 52 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
53 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
54 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
55 label,
56 src_str, ifname,
57 tlv_name, value);
58 }
59}
60
61static void tlv_trace_uint16(const char *label, const char *tlv_name,
62 const char *ifname, struct in_addr src_addr,
63 int isset, uint16_t value)
64{
65 if (isset) {
eaa54bdb 66 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
67 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
68 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
69 label,
70 src_str, ifname,
71 tlv_name, value);
72 }
73}
74
75static void tlv_trace_uint32(const char *label, const char *tlv_name,
76 const char *ifname, struct in_addr src_addr,
77 int isset, uint32_t value)
78{
79 if (isset) {
eaa54bdb 80 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
81 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
82 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
83 label,
84 src_str, ifname,
85 tlv_name, value);
86 }
87}
88
89static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
90 const char *ifname, struct in_addr src_addr,
91 int isset, uint32_t value)
92{
93 if (isset) {
eaa54bdb 94 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
95 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
96 zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
97 label,
98 src_str, ifname,
99 tlv_name, value);
100 }
101}
102
103#if 0
104static void tlv_trace(const char *label, const char *tlv_name,
105 const char *ifname, struct in_addr src_addr,
106 int isset)
107{
108 if (isset) {
eaa54bdb 109 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
110 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
111 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
112 label,
113 src_str, ifname,
114 tlv_name);
115 }
116}
117#endif
118
119static void tlv_trace_list(const char *label, const char *tlv_name,
120 const char *ifname, struct in_addr src_addr,
121 int isset, struct list *addr_list)
122{
123 if (isset) {
eaa54bdb 124 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
125 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
126 zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
127 label,
128 src_str, ifname,
129 tlv_name,
130 addr_list ? ((int) listcount(addr_list)) : -1,
131 (void *) addr_list);
132 }
133}
134
135#define FREE_ADDR_LIST \
136 if (hello_option_addr_list) { \
137 list_delete(hello_option_addr_list); \
138 }
139
140#define FREE_ADDR_LIST_THEN_RETURN(code) \
141{ \
142 FREE_ADDR_LIST \
143 return (code); \
144}
145
146int pim_hello_recv(struct interface *ifp,
147 struct in_addr src_addr,
148 uint8_t *tlv_buf, int tlv_buf_size)
149{
150 struct pim_interface *pim_ifp;
151 struct pim_neighbor *neigh;
152 uint8_t *tlv_curr;
153 uint8_t *tlv_pastend;
154 pim_hello_options hello_options = 0; /* bit array recording options found */
155 uint16_t hello_option_holdtime = 0;
156 uint16_t hello_option_propagation_delay = 0;
157 uint16_t hello_option_override_interval = 0;
158 uint32_t hello_option_dr_priority = 0;
159 uint32_t hello_option_generation_id = 0;
160 struct list *hello_option_addr_list = 0;
161
aea6cb94
DS
162 if (PIM_DEBUG_PIM_HELLO)
163 on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
12e41d03
DL
164
165 pim_ifp = ifp->info;
166 zassert(pim_ifp);
167
168 ++pim_ifp->pim_ifstat_hello_recv;
169
170 /*
171 Parse PIM hello TLVs
172 */
173 zassert(tlv_buf_size >= 0);
174 tlv_curr = tlv_buf;
175 tlv_pastend = tlv_buf + tlv_buf_size;
176
177 while (tlv_curr < tlv_pastend) {
178 uint16_t option_type;
179 uint16_t option_len;
180 int remain = tlv_pastend - tlv_curr;
181
182 if (remain < PIM_TLV_MIN_SIZE) {
aea6cb94 183 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 184 char src_str[INET_ADDRSTRLEN];
aea6cb94
DS
185 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
186 zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
187 __PRETTY_FUNCTION__,
188 remain, PIM_TLV_MIN_SIZE,
189 src_str, ifp->name);
190 }
12e41d03
DL
191 FREE_ADDR_LIST_THEN_RETURN(-1);
192 }
193
194 option_type = PIM_TLV_GET_TYPE(tlv_curr);
195 tlv_curr += PIM_TLV_TYPE_SIZE;
196 option_len = PIM_TLV_GET_LENGTH(tlv_curr);
197 tlv_curr += PIM_TLV_LENGTH_SIZE;
198
199 if ((tlv_curr + option_len) > tlv_pastend) {
aea6cb94 200 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 201 char src_str[INET_ADDRSTRLEN];
aea6cb94
DS
202 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
203 zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
204 __PRETTY_FUNCTION__,
205 option_type, option_len, tlv_pastend - tlv_curr,
206 src_str, ifp->name);
207 }
12e41d03
DL
208 FREE_ADDR_LIST_THEN_RETURN(-2);
209 }
210
aea6cb94 211 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 212 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
213 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
214 zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
215 __PRETTY_FUNCTION__,
216 remain,
217 option_type, option_len,
218 src_str, ifp->name);
219 }
220
221 switch (option_type) {
222 case PIM_MSG_OPTION_TYPE_HOLDTIME:
223 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
224 &hello_options,
225 &hello_option_holdtime,
226 option_len,
227 tlv_curr)) {
228 FREE_ADDR_LIST_THEN_RETURN(-3);
229 }
230 break;
231 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
232 if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
233 &hello_options,
234 &hello_option_propagation_delay,
235 &hello_option_override_interval,
236 option_len,
237 tlv_curr)) {
238 FREE_ADDR_LIST_THEN_RETURN(-4);
239 }
240 break;
241 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
242 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
243 &hello_options,
244 &hello_option_dr_priority,
245 option_len,
246 tlv_curr)) {
247 FREE_ADDR_LIST_THEN_RETURN(-5);
248 }
249 break;
250 case PIM_MSG_OPTION_TYPE_GENERATION_ID:
251 if (pim_tlv_parse_generation_id(ifp->name, src_addr,
252 &hello_options,
253 &hello_option_generation_id,
254 option_len,
255 tlv_curr)) {
256 FREE_ADDR_LIST_THEN_RETURN(-6);
257 }
258 break;
259 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
260 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
261 &hello_options,
262 &hello_option_addr_list,
263 option_len,
264 tlv_curr)) {
265 return -7;
266 }
267 break;
268 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
aea6cb94 269 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 270 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
271 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
272 zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
273 __PRETTY_FUNCTION__,
274 option_type, option_len,
275 src_str, ifp->name);
276 }
277 break;
278 default:
aea6cb94 279 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 280 char src_str[INET_ADDRSTRLEN];
12e41d03 281 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
aea6cb94
DS
282 zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
283 __PRETTY_FUNCTION__,
284 option_type, option_len,
285 src_str, ifp->name);
12e41d03
DL
286 }
287 }
288
289 tlv_curr += option_len;
290 }
291
292 /*
293 Check received PIM hello options
294 */
295
aea6cb94 296 if (PIM_DEBUG_PIM_HELLO) {
12e41d03
DL
297 tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
298 ifp->name, src_addr,
299 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
300 hello_option_holdtime);
301 tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
302 ifp->name, src_addr,
303 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
304 hello_option_propagation_delay);
305 tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
306 ifp->name, src_addr,
307 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
308 hello_option_override_interval);
309 tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
310 ifp->name, src_addr,
311 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
312 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
313 tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
314 ifp->name, src_addr,
315 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
316 hello_option_dr_priority);
317 tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
318 ifp->name, src_addr,
319 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
320 hello_option_generation_id);
321 tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
322 ifp->name, src_addr,
323 PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
324 hello_option_addr_list);
325 }
326
327 if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
aea6cb94 328 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 329 char src_str[INET_ADDRSTRLEN];
aea6cb94
DS
330 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
331 zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
332 __PRETTY_FUNCTION__,
333 src_str, ifp->name);
334 }
12e41d03
DL
335 }
336
337 /*
338 New neighbor?
339 */
340
341 neigh = pim_neighbor_find(ifp, src_addr);
342 if (!neigh) {
343 /* Add as new neighbor */
344
345 neigh = pim_neighbor_add(ifp, src_addr,
346 hello_options,
347 hello_option_holdtime,
348 hello_option_propagation_delay,
349 hello_option_override_interval,
350 hello_option_dr_priority,
351 hello_option_generation_id,
1148de0a
DS
352 hello_option_addr_list,
353 PIM_NEIGHBOR_SEND_DELAY);
12e41d03 354 if (!neigh) {
aea6cb94 355 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 356 char src_str[INET_ADDRSTRLEN];
aea6cb94
DS
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 }
12e41d03
DL
362 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)) {
12e41d03
DL
377 /* GenID mismatch, then replace neighbor */
378
aea6cb94 379 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 380 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
381 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
382 zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
383 __PRETTY_FUNCTION__,
384 hello_option_generation_id,
385 neigh->generation_id,
386 src_str, ifp->name);
387 }
388
389 pim_upstream_rpf_genid_changed(neigh->source_addr);
390
391 pim_neighbor_delete(ifp, neigh, "GenID mismatch");
392 neigh = pim_neighbor_add(ifp, src_addr,
393 hello_options,
394 hello_option_holdtime,
395 hello_option_propagation_delay,
396 hello_option_override_interval,
397 hello_option_dr_priority,
398 hello_option_generation_id,
1148de0a
DS
399 hello_option_addr_list,
400 PIM_NEIGHBOR_SEND_NOW);
12e41d03 401 if (!neigh) {
aea6cb94 402 if (PIM_DEBUG_PIM_HELLO) {
eaa54bdb 403 char src_str[INET_ADDRSTRLEN];
aea6cb94
DS
404 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
405 zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
406 __PRETTY_FUNCTION__,
407 src_str, ifp->name);
408 }
12e41d03
DL
409 FREE_ADDR_LIST_THEN_RETURN(-9);
410 }
411 /* actual addr list is saved under neighbor */
412 return 0;
413
414 } /* GenId mismatch: replace neighbor */
415
416 } /* GenId received */
417
418 /*
419 Update existing neighbor
420 */
421
422 pim_neighbor_update(neigh,
423 hello_options,
424 hello_option_holdtime,
425 hello_option_dr_priority,
426 hello_option_addr_list);
427 /* actual addr list is saved under neighbor */
428 return 0;
429}
430
431int pim_hello_build_tlv(const char *ifname,
432 uint8_t *tlv_buf, int tlv_buf_size,
433 uint16_t holdtime,
434 uint32_t dr_priority,
435 uint32_t generation_id,
436 uint16_t propagation_delay,
437 uint16_t override_interval,
438 int can_disable_join_suppression,
439 struct list *ifconnected)
440{
441 uint8_t *curr = tlv_buf;
442 uint8_t *pastend = tlv_buf + tlv_buf_size;
443 uint8_t *tmp;
444
445 /*
446 * Append options
447 */
448
449 /* Holdtime */
450 curr = pim_tlv_append_uint16(curr,
451 pastend,
452 PIM_MSG_OPTION_TYPE_HOLDTIME,
453 holdtime);
454 if (!curr) {
aea6cb94
DS
455 if (PIM_DEBUG_PIM_HELLO) {
456 zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
457 __PRETTY_FUNCTION__, ifname);
458 }
12e41d03
DL
459 return -1;
460 }
461
462 /* LAN Prune Delay */
463 tmp = pim_tlv_append_2uint16(curr,
464 pastend,
465 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
466 propagation_delay,
467 override_interval);
468 if (!tmp) {
aea6cb94
DS
469 if (PIM_DEBUG_PIM_HELLO) {
470 zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
471 __PRETTY_FUNCTION__, ifname);
472 }
12e41d03
DL
473 return -1;
474 }
475 if (can_disable_join_suppression) {
476 *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
477 }
478 curr = tmp;
479
480 /* DR Priority */
481 curr = pim_tlv_append_uint32(curr,
482 pastend,
483 PIM_MSG_OPTION_TYPE_DR_PRIORITY,
484 dr_priority);
485 if (!curr) {
aea6cb94
DS
486 if (PIM_DEBUG_PIM_HELLO) {
487 zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
488 __PRETTY_FUNCTION__, ifname);
489 }
12e41d03
DL
490 return -2;
491 }
492
493 /* Generation ID */
494 curr = pim_tlv_append_uint32(curr,
495 pastend,
496 PIM_MSG_OPTION_TYPE_GENERATION_ID,
497 generation_id);
498 if (!curr) {
aea6cb94
DS
499 if (PIM_DEBUG_PIM_HELLO) {
500 zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
501 __PRETTY_FUNCTION__, ifname);
502 }
12e41d03
DL
503 return -3;
504 }
505
506 /* Secondary Address List */
507 if (ifconnected) {
508 curr = pim_tlv_append_addrlist_ucast(curr,
509 pastend,
510 ifconnected);
511 if (!curr) {
aea6cb94
DS
512 if (PIM_DEBUG_PIM_HELLO) {
513 zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
514 __PRETTY_FUNCTION__, ifname);
515 }
12e41d03
DL
516 return -4;
517 }
518 }
519
520 return curr - tlv_buf;
521}
522
523/*
524 RFC 4601: 4.3.1. Sending Hello Messages
525
526 Thus, if a router needs to send a Join/Prune or Assert message on an
527 interface on which it has not yet sent a Hello message with the
528 currently configured IP address, then it MUST immediately send the
529 relevant Hello message without waiting for the Hello Timer to
530 expire, followed by the Join/Prune or Assert message.
531*/
532void pim_hello_require(struct interface *ifp)
533{
534 struct pim_interface *pim_ifp;
535
536 zassert(ifp);
537
538 pim_ifp = ifp->info;
539
540 zassert(pim_ifp);
541
542 if (pim_ifp->pim_ifstat_hello_sent)
543 return;
544
545 pim_hello_restart_now(ifp); /* Send hello and restart timer */
546}