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