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
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:
37: static 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:
48: static 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:
62: static 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:
76: static 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:
90: static 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
105: static 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:
120: static 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=%p",
128: label,
129: src_str, ifname,
130: tlv_name,
131: addr_list ? ((int) listcount(addr_list)) : -1,
132: (void *) 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:
147: int pim_hello_recv(struct interface *ifp,
148: struct in_addr src_addr,
149: uint8_t *tlv_buf, int tlv_buf_size)
150: {
151: struct pim_interface *pim_ifp;
152: struct pim_neighbor *neigh;
153: uint8_t *tlv_curr;
154: uint8_t *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: if (PIM_DEBUG_PIM_HELLO)
164: on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
165:
166: pim_ifp = ifp->info;
167: zassert(pim_ifp);
168:
169: ++pim_ifp->pim_ifstat_hello_recv;
170:
171: /*
172: Parse PIM hello TLVs
173: */
174: zassert(tlv_buf_size >= 0);
175: tlv_curr = tlv_buf;
176: tlv_pastend = tlv_buf + tlv_buf_size;
177:
178: while (tlv_curr < tlv_pastend) {
179: uint16_t option_type;
180: uint16_t option_len;
181: int remain = tlv_pastend - tlv_curr;
182:
183: if (remain < PIM_TLV_MIN_SIZE) {
184: if (PIM_DEBUG_PIM_HELLO) {
185: char src_str[100];
186: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
187: zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
188: __PRETTY_FUNCTION__,
189: remain, PIM_TLV_MIN_SIZE,
190: src_str, ifp->name);
191: }
192: FREE_ADDR_LIST_THEN_RETURN(-1);
193: }
194:
195: option_type = PIM_TLV_GET_TYPE(tlv_curr);
196: tlv_curr += PIM_TLV_TYPE_SIZE;
197: option_len = PIM_TLV_GET_LENGTH(tlv_curr);
198: tlv_curr += PIM_TLV_LENGTH_SIZE;
199:
200: if ((tlv_curr + option_len) > tlv_pastend) {
201: if (PIM_DEBUG_PIM_HELLO) {
202: char src_str[100];
203: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
204: zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
205: __PRETTY_FUNCTION__,
206: option_type, option_len, tlv_pastend - tlv_curr,
207: src_str, ifp->name);
208: }
209: FREE_ADDR_LIST_THEN_RETURN(-2);
210: }
211:
212: if (PIM_DEBUG_PIM_HELLO) {
213: char src_str[100];
214: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
215: zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
216: __PRETTY_FUNCTION__,
217: remain,
218: option_type, option_len,
219: src_str, ifp->name);
220: }
221:
222: switch (option_type) {
223: case PIM_MSG_OPTION_TYPE_HOLDTIME:
224: if (pim_tlv_parse_holdtime(ifp->name, src_addr,
225: &hello_options,
226: &hello_option_holdtime,
227: option_len,
228: tlv_curr)) {
229: FREE_ADDR_LIST_THEN_RETURN(-3);
230: }
231: break;
232: case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
233: if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
234: &hello_options,
235: &hello_option_propagation_delay,
236: &hello_option_override_interval,
237: option_len,
238: tlv_curr)) {
239: FREE_ADDR_LIST_THEN_RETURN(-4);
240: }
241: break;
242: case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
243: if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
244: &hello_options,
245: &hello_option_dr_priority,
246: option_len,
247: tlv_curr)) {
248: FREE_ADDR_LIST_THEN_RETURN(-5);
249: }
250: break;
251: case PIM_MSG_OPTION_TYPE_GENERATION_ID:
252: if (pim_tlv_parse_generation_id(ifp->name, src_addr,
253: &hello_options,
254: &hello_option_generation_id,
255: option_len,
256: tlv_curr)) {
257: FREE_ADDR_LIST_THEN_RETURN(-6);
258: }
259: break;
260: case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
261: if (pim_tlv_parse_addr_list(ifp->name, src_addr,
262: &hello_options,
263: &hello_option_addr_list,
264: option_len,
265: tlv_curr)) {
266: return -7;
267: }
268: break;
269: case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
270: if (PIM_DEBUG_PIM_HELLO) {
271: char src_str[100];
272: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
273: zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
274: __PRETTY_FUNCTION__,
275: option_type, option_len,
276: src_str, ifp->name);
277: }
278: break;
279: default:
280: if (PIM_DEBUG_PIM_HELLO) {
281: char src_str[100];
282: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
283: zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
284: __PRETTY_FUNCTION__,
285: option_type, option_len,
286: src_str, ifp->name);
287: }
288: }
289:
290: tlv_curr += option_len;
291: }
292:
293: /*
294: Check received PIM hello options
295: */
296:
297: if (PIM_DEBUG_PIM_HELLO) {
298: tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
299: ifp->name, src_addr,
300: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
301: hello_option_holdtime);
302: tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
303: ifp->name, src_addr,
304: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
305: hello_option_propagation_delay);
306: tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
307: ifp->name, src_addr,
308: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
309: hello_option_override_interval);
310: tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
311: ifp->name, src_addr,
312: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
313: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
314: tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
315: ifp->name, src_addr,
316: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
317: hello_option_dr_priority);
318: tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
319: ifp->name, src_addr,
320: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
321: hello_option_generation_id);
322: tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
323: ifp->name, src_addr,
324: PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
325: hello_option_addr_list);
326: }
327:
328: if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
329: if (PIM_DEBUG_PIM_HELLO) {
330: char src_str[100];
331: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
332: zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
333: __PRETTY_FUNCTION__,
334: src_str, ifp->name);
335: }
336: }
337:
338: /*
339: New neighbor?
340: */
341:
342: neigh = pim_neighbor_find(ifp, src_addr);
343: if (!neigh) {
344: /* Add as new neighbor */
345:
346: neigh = pim_neighbor_add(ifp, src_addr,
347: hello_options,
348: hello_option_holdtime,
349: hello_option_propagation_delay,
350: hello_option_override_interval,
351: hello_option_dr_priority,
352: hello_option_generation_id,
353: hello_option_addr_list);
354: if (!neigh) {
355: if (PIM_DEBUG_PIM_HELLO) {
356: char src_str[100];
357: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
358: zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
359: __PRETTY_FUNCTION__,
360: src_str, ifp->name);
361: }
362: FREE_ADDR_LIST_THEN_RETURN(-8);
363: }
364:
365: /* actual addr list has been saved under neighbor */
366: return 0;
367: }
368:
369: /*
370: Received generation ID ?
371: */
372:
373: if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
374: /* GenID mismatch ? */
375: if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
376: (hello_option_generation_id != neigh->generation_id)) {
377:
378: /* GenID changed */
379:
380: pim_upstream_rpf_genid_changed(neigh->source_addr);
381:
382: /* GenID mismatch, then replace neighbor */
383:
384: if (PIM_DEBUG_PIM_HELLO) {
385: char src_str[100];
386: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
387: zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
388: __PRETTY_FUNCTION__,
389: hello_option_generation_id,
390: neigh->generation_id,
391: src_str, ifp->name);
392: }
393:
394: pim_upstream_rpf_genid_changed(neigh->source_addr);
395:
396: pim_neighbor_delete(ifp, neigh, "GenID mismatch");
397: neigh = pim_neighbor_add(ifp, src_addr,
398: hello_options,
399: hello_option_holdtime,
400: hello_option_propagation_delay,
401: hello_option_override_interval,
402: hello_option_dr_priority,
403: hello_option_generation_id,
404: hello_option_addr_list);
405: if (!neigh) {
406: if (PIM_DEBUG_PIM_HELLO) {
407: char src_str[100];
408: pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
409: zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
410: __PRETTY_FUNCTION__,
411: src_str, ifp->name);
412: }
413: FREE_ADDR_LIST_THEN_RETURN(-9);
414: }
415: /* actual addr list is saved under neighbor */
416: return 0;
417:
418: } /* GenId mismatch: replace neighbor */
419:
420: } /* GenId received */
421:
422: /*
423: Update existing neighbor
424: */
425:
426: pim_neighbor_update(neigh,
427: hello_options,
428: hello_option_holdtime,
429: hello_option_dr_priority,
430: hello_option_addr_list);
431: /* actual addr list is saved under neighbor */
432: return 0;
433: }
434:
435: int pim_hello_build_tlv(const char *ifname,
436: uint8_t *tlv_buf, int tlv_buf_size,
437: uint16_t holdtime,
438: uint32_t dr_priority,
439: uint32_t generation_id,
440: uint16_t propagation_delay,
441: uint16_t override_interval,
442: int can_disable_join_suppression,
443: struct list *ifconnected)
444: {
445: uint8_t *curr = tlv_buf;
446: uint8_t *pastend = tlv_buf + tlv_buf_size;
447: uint8_t *tmp;
448:
449: /*
450: * Append options
451: */
452:
453: /* Holdtime */
454: curr = pim_tlv_append_uint16(curr,
455: pastend,
456: PIM_MSG_OPTION_TYPE_HOLDTIME,
457: holdtime);
458: if (!curr) {
459: if (PIM_DEBUG_PIM_HELLO) {
460: zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
461: __PRETTY_FUNCTION__, ifname);
462: }
463: return -1;
464: }
465:
466: /* LAN Prune Delay */
467: tmp = pim_tlv_append_2uint16(curr,
468: pastend,
469: PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
470: propagation_delay,
471: override_interval);
472: if (!tmp) {
473: if (PIM_DEBUG_PIM_HELLO) {
474: zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
475: __PRETTY_FUNCTION__, ifname);
476: }
477: return -1;
478: }
479: if (can_disable_join_suppression) {
480: *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
481: }
482: curr = tmp;
483:
484: /* DR Priority */
485: curr = pim_tlv_append_uint32(curr,
486: pastend,
487: PIM_MSG_OPTION_TYPE_DR_PRIORITY,
488: dr_priority);
489: if (!curr) {
490: if (PIM_DEBUG_PIM_HELLO) {
491: zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
492: __PRETTY_FUNCTION__, ifname);
493: }
494: return -2;
495: }
496:
497: /* Generation ID */
498: curr = pim_tlv_append_uint32(curr,
499: pastend,
500: PIM_MSG_OPTION_TYPE_GENERATION_ID,
501: generation_id);
502: if (!curr) {
503: if (PIM_DEBUG_PIM_HELLO) {
504: zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
505: __PRETTY_FUNCTION__, ifname);
506: }
507: return -3;
508: }
509:
510: /* Secondary Address List */
511: if (ifconnected) {
512: curr = pim_tlv_append_addrlist_ucast(curr,
513: pastend,
514: ifconnected);
515: if (!curr) {
516: if (PIM_DEBUG_PIM_HELLO) {
517: zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
518: __PRETTY_FUNCTION__, ifname);
519: }
520: return -4;
521: }
522: }
523:
524: return curr - tlv_buf;
525: }
526:
527: /*
528: RFC 4601: 4.3.1. Sending Hello Messages
529:
530: Thus, if a router needs to send a Join/Prune or Assert message on an
531: interface on which it has not yet sent a Hello message with the
532: currently configured IP address, then it MUST immediately send the
533: relevant Hello message without waiting for the Hello Timer to
534: expire, followed by the Join/Prune or Assert message.
535: */
536: void pim_hello_require(struct interface *ifp)
537: {
538: struct pim_interface *pim_ifp;
539:
540: zassert(ifp);
541:
542: pim_ifp = ifp->info;
543:
544: zassert(pim_ifp);
545:
546: if (pim_ifp->pim_ifstat_hello_sent)
547: return;
548:
549: pim_hello_restart_now(ifp); /* Send hello and restart timer */
550: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>