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