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