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