Line data Source code
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 0 : static void on_trace(const char *label, struct interface *ifp, pim_addr src)
38 : {
39 0 : if (PIM_DEBUG_PIM_TRACE)
40 0 : zlog_debug("%s: from %pPAs on %s", label, &src, ifp->name);
41 0 : }
42 :
43 0 : 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 0 : if (isset)
48 0 : 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 0 : 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 0 : if (isset)
58 0 : 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 0 : 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 0 : if (isset)
68 0 : 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 0 : 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 0 : if (isset)
78 0 : 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 0 : 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 0 : if (isset)
88 0 : 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 0 : }
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 256 : int pim_hello_recv(struct interface *ifp, pim_addr src_addr, uint8_t *tlv_buf,
107 : int tlv_buf_size)
108 : {
109 256 : struct pim_interface *pim_ifp;
110 256 : struct pim_neighbor *neigh;
111 256 : uint8_t *tlv_curr;
112 256 : uint8_t *tlv_pastend;
113 256 : pim_hello_options hello_options =
114 : 0; /* bit array recording options found */
115 256 : uint16_t hello_option_holdtime = 0;
116 256 : uint16_t hello_option_propagation_delay = 0;
117 256 : uint16_t hello_option_override_interval = 0;
118 256 : uint32_t hello_option_dr_priority = 0;
119 256 : uint32_t hello_option_generation_id = 0;
120 256 : struct list *hello_option_addr_list = 0;
121 :
122 256 : if (PIM_DEBUG_PIM_HELLO)
123 0 : on_trace(__func__, ifp, src_addr);
124 :
125 256 : pim_ifp = ifp->info;
126 256 : assert(pim_ifp);
127 :
128 256 : if (pim_ifp->pim_passive_enable) {
129 0 : if (PIM_DEBUG_PIM_PACKETS)
130 0 : zlog_debug(
131 : "skip receiving PIM message on passive interface %s",
132 : ifp->name);
133 0 : return 0;
134 : }
135 :
136 256 : ++pim_ifp->pim_ifstat_hello_recv;
137 :
138 : /*
139 : Parse PIM hello TLVs
140 : */
141 256 : assert(tlv_buf_size >= 0);
142 256 : tlv_curr = tlv_buf;
143 256 : tlv_pastend = tlv_buf + tlv_buf_size;
144 :
145 1280 : while (tlv_curr < tlv_pastend) {
146 1024 : uint16_t option_type;
147 1024 : uint16_t option_len;
148 1024 : int remain = tlv_pastend - tlv_curr;
149 :
150 1024 : if (remain < PIM_TLV_MIN_SIZE) {
151 0 : if (PIM_DEBUG_PIM_HELLO)
152 0 : 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 0 : FREE_ADDR_LIST_THEN_RETURN(-1);
157 : }
158 :
159 1024 : option_type = PIM_TLV_GET_TYPE(tlv_curr);
160 1024 : tlv_curr += PIM_TLV_TYPE_SIZE;
161 1024 : option_len = PIM_TLV_GET_LENGTH(tlv_curr);
162 1024 : tlv_curr += PIM_TLV_LENGTH_SIZE;
163 :
164 1024 : if ((tlv_curr + option_len) > tlv_pastend) {
165 0 : if (PIM_DEBUG_PIM_HELLO)
166 0 : 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 0 : FREE_ADDR_LIST_THEN_RETURN(-2);
172 : }
173 :
174 1024 : if (PIM_DEBUG_PIM_HELLO)
175 0 : 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 1024 : switch (option_type) {
181 256 : case PIM_MSG_OPTION_TYPE_HOLDTIME:
182 256 : if (pim_tlv_parse_holdtime(ifp->name, src_addr,
183 : &hello_options,
184 : &hello_option_holdtime,
185 : option_len, tlv_curr)) {
186 0 : FREE_ADDR_LIST_THEN_RETURN(-3);
187 : }
188 : break;
189 256 : case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
190 256 : if (pim_tlv_parse_lan_prune_delay(
191 256 : ifp->name, src_addr, &hello_options,
192 : &hello_option_propagation_delay,
193 : &hello_option_override_interval, option_len,
194 : tlv_curr)) {
195 0 : FREE_ADDR_LIST_THEN_RETURN(-4);
196 : }
197 : break;
198 256 : case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
199 256 : if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
200 : &hello_options,
201 : &hello_option_dr_priority,
202 : option_len, tlv_curr)) {
203 0 : FREE_ADDR_LIST_THEN_RETURN(-5);
204 : }
205 : break;
206 256 : case PIM_MSG_OPTION_TYPE_GENERATION_ID:
207 256 : if (pim_tlv_parse_generation_id(
208 256 : ifp->name, src_addr, &hello_options,
209 : &hello_option_generation_id, option_len,
210 : tlv_curr)) {
211 0 : FREE_ADDR_LIST_THEN_RETURN(-6);
212 : }
213 : break;
214 0 : case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
215 0 : 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 0 : case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
223 0 : if (PIM_DEBUG_PIM_HELLO)
224 0 : 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 0 : default:
230 0 : if (PIM_DEBUG_PIM_HELLO)
231 0 : 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 256 : if (PIM_DEBUG_PIM_HELLO) {
245 0 : tlv_trace_uint16(__func__, "holdtime", ifp->name, src_addr,
246 0 : PIM_OPTION_IS_SET(hello_options,
247 : PIM_OPTION_MASK_HOLDTIME),
248 : hello_option_holdtime);
249 0 : tlv_trace_uint16(
250 0 : __func__, "propagation_delay", ifp->name, src_addr,
251 0 : PIM_OPTION_IS_SET(hello_options,
252 : PIM_OPTION_MASK_LAN_PRUNE_DELAY),
253 : hello_option_propagation_delay);
254 0 : tlv_trace_uint16(
255 0 : __func__, "override_interval", ifp->name, src_addr,
256 0 : PIM_OPTION_IS_SET(hello_options,
257 : PIM_OPTION_MASK_LAN_PRUNE_DELAY),
258 : hello_option_override_interval);
259 0 : tlv_trace_bool(
260 0 : __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 0 : PIM_OPTION_IS_SET(
265 : hello_options,
266 : PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
267 0 : tlv_trace_uint32(__func__, "dr_priority", ifp->name, src_addr,
268 0 : PIM_OPTION_IS_SET(hello_options,
269 : PIM_OPTION_MASK_DR_PRIORITY),
270 : hello_option_dr_priority);
271 0 : tlv_trace_uint32_hex(
272 0 : __func__, "generation_id", ifp->name, src_addr,
273 0 : PIM_OPTION_IS_SET(hello_options,
274 : PIM_OPTION_MASK_GENERATION_ID),
275 : hello_option_generation_id);
276 0 : tlv_trace_list(__func__, "address_list", ifp->name, src_addr,
277 0 : PIM_OPTION_IS_SET(hello_options,
278 : PIM_OPTION_MASK_ADDRESS_LIST),
279 : hello_option_addr_list);
280 : }
281 :
282 256 : if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
283 0 : if (PIM_DEBUG_PIM_HELLO)
284 0 : 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 256 : neigh = pim_neighbor_find(ifp, src_addr);
294 256 : if (!neigh) {
295 : /* Add as new neighbor */
296 :
297 6 : 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 6 : if (!neigh) {
304 0 : if (PIM_DEBUG_PIM_HELLO)
305 0 : zlog_warn(
306 : "%s: failure creating PIM neighbor %pPAs on interface %s",
307 : __func__, &src_addr, ifp->name);
308 0 : FREE_ADDR_LIST_THEN_RETURN(-8);
309 : }
310 : /* Forward BSM if required */
311 6 : if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
312 6 : if (PIM_DEBUG_PIM_HELLO)
313 0 : zlog_debug(
314 : "%s: forwarding bsm to new nbr failed",
315 : __func__);
316 : }
317 :
318 : /* actual addr list has been saved under neighbor */
319 6 : return 0;
320 : }
321 :
322 : /*
323 : Received generation ID ?
324 : */
325 :
326 250 : if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
327 : /* GenID mismatch ? */
328 250 : if (!PIM_OPTION_IS_SET(neigh->hello_options,
329 : PIM_OPTION_MASK_GENERATION_ID)
330 250 : || (hello_option_generation_id != neigh->generation_id)) {
331 : /* GenID mismatch, then replace neighbor */
332 :
333 0 : if (PIM_DEBUG_PIM_HELLO)
334 0 : 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 0 : pim_upstream_rpf_genid_changed(pim_ifp->pim,
341 : neigh->source_addr);
342 :
343 0 : pim_neighbor_delete(ifp, neigh, "GenID mismatch");
344 0 : 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 0 : if (!neigh) {
353 0 : if (PIM_DEBUG_PIM_HELLO)
354 0 : zlog_debug(
355 : "%s: failure re-creating PIM neighbor %pPAs on interface %s",
356 : __func__, &src_addr, ifp->name);
357 0 : FREE_ADDR_LIST_THEN_RETURN(-9);
358 : }
359 : /* Forward BSM if required */
360 0 : if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
361 0 : if (PIM_DEBUG_PIM_HELLO)
362 0 : zlog_debug(
363 : "%s: forwarding bsm to new nbr failed",
364 : __func__);
365 : }
366 : /* actual addr list is saved under neighbor */
367 0 : return 0;
368 :
369 : } /* GenId mismatch: replace neighbor */
370 :
371 : } /* GenId received */
372 :
373 : /*
374 : Update existing neighbor
375 : */
376 :
377 250 : 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 250 : return 0;
381 : }
382 :
383 361 : 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 361 : uint8_t *curr = tlv_buf;
390 361 : uint8_t *pastend = tlv_buf + tlv_buf_size;
391 361 : 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 361 : curr = pim_tlv_append_uint16(curr, pastend,
403 : PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
404 361 : if (!curr) {
405 0 : if (PIM_DEBUG_PIM_HELLO) {
406 0 : zlog_debug(
407 : "%s: could not set PIM hello Holdtime option for interface %s",
408 : __func__, ifp->name);
409 : }
410 0 : return -1;
411 : }
412 :
413 : /* LAN Prune Delay */
414 361 : tmp = pim_tlv_append_2uint16(curr, pastend,
415 : PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
416 : propagation_delay, override_interval);
417 361 : if (!tmp) {
418 0 : if (PIM_DEBUG_PIM_HELLO) {
419 0 : zlog_debug(
420 : "%s: could not set PIM LAN Prune Delay option for interface %s",
421 : __func__, ifp->name);
422 : }
423 0 : return -1;
424 : }
425 361 : if (can_disable_join_suppression) {
426 0 : *(curr + 4) |= 0x80; /* enable T bit */
427 : }
428 361 : curr = tmp;
429 :
430 : /* DR Priority */
431 361 : curr = pim_tlv_append_uint32(
432 : curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
433 361 : if (!curr) {
434 0 : if (PIM_DEBUG_PIM_HELLO) {
435 0 : zlog_debug(
436 : "%s: could not set PIM hello DR Priority option for interface %s",
437 : __func__, ifp->name);
438 : }
439 0 : return -2;
440 : }
441 :
442 : /* Generation ID */
443 361 : curr = pim_tlv_append_uint32(curr, pastend,
444 : PIM_MSG_OPTION_TYPE_GENERATION_ID,
445 : generation_id);
446 361 : if (!curr) {
447 0 : if (PIM_DEBUG_PIM_HELLO) {
448 0 : zlog_debug(
449 : "%s: could not set PIM hello Generation ID option for interface %s",
450 : __func__, ifp->name);
451 : }
452 0 : return -3;
453 : }
454 :
455 : /* Secondary Address List */
456 361 : if (ifp->connected->count) {
457 361 : curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp,
458 : PIM_AF);
459 361 : if (!curr) {
460 0 : if (PIM_DEBUG_PIM_HELLO) {
461 0 : 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 0 : 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 361 : 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 6 : void pim_hello_require(struct interface *ifp)
496 : {
497 6 : struct pim_interface *pim_ifp;
498 :
499 6 : assert(ifp);
500 :
501 6 : pim_ifp = ifp->info;
502 :
503 6 : assert(pim_ifp);
504 :
505 6 : if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp->flags))
506 : return;
507 :
508 0 : pim_hello_restart_now(ifp); /* Send hello and restart timer */
509 : }
|