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