Annotation of embedaddon/pimdd/pim_proto.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1998 by the University of Oregon.
3: * All rights reserved.
4: *
5: * Permission to use, copy, modify, and distribute this software and
6: * its documentation in source and binary forms for lawful
7: * purposes and without fee is hereby granted, provided
8: * that the above copyright notice appear in all copies and that both
9: * the copyright notice and this permission notice appear in supporting
10: * documentation, and that any documentation, advertising materials,
11: * and other materials related to such distribution and use acknowledge
12: * that the software was developed by the University of Oregon.
13: * The name of the University of Oregon may not be used to endorse or
14: * promote products derived from this software without specific prior
15: * written permission.
16: *
17: * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
18: * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
19: * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
20: * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
22: * NON-INFRINGEMENT.
23: *
24: * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
25: * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
26: * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
27: * THE USE OR PERFORMANCE OF THIS SOFTWARE.
28: *
29: * Other copyrights might apply to parts of this software and are so
30: * noted when applicable.
31: */
32: /*
33: * Questions concerning this software should be directed to
34: * Kurt Windisch (kurtw@antc.uoregon.edu)
35: *
36: * $Id: pim_proto.c,v 1.37 1998/12/22 21:50:18 kurtw Exp $
37: */
38: /*
39: * Part of this program has been derived from PIM sparse-mode pimd.
40: * The pimd program is covered by the license in the accompanying file
41: * named "LICENSE.pimd".
42: *
43: * The pimd program is COPYRIGHT 1998 by University of Southern California.
44: *
45: * Part of this program has been derived from mrouted.
46: * The mrouted program is covered by the license in the accompanying file
47: * named "LICENSE.mrouted".
48: *
49: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
50: * Leland Stanford Junior University.
51: *
52: */
53:
54: #include "defs.h"
55:
56: /*
57: * Local functions definitions.
58: */
59: static int parse_pim_hello __P((char *pktPtr,
60: int datalen,
61: u_int32 src,
62: u_int16 *holdtime));
63: static int compare_metrics __P((u_int32 local_preference,
64: u_int32 local_metric,
65: u_int32 local_address,
66: u_int32 remote_preference,
67: u_int32 remote_metric,
68: u_int32 remote_address));
69:
70: vifbitmap_t nbr_vifs; /* Vifs that have one or more neighbors attached */
71:
72: /************************************************************************
73: * PIM_HELLO
74: ************************************************************************/
75: int
76: receive_pim_hello(src, dst, pim_message, datalen)
77: u_int32 src, dst;
78: register char *pim_message;
79: int datalen;
80: {
81: vifi_t vifi;
82: struct uvif *v;
83: register pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr;
84: u_int16 holdtime;
85: u_int8 *data_ptr;
86: int state_change;
87: srcentry_t *srcentry_ptr;
88: srcentry_t *srcentry_ptr_next;
89: mrtentry_t *mrtentry_ptr;
90:
91: /* Checksum */
92: if (inet_cksum((u_int16 *)pim_message, datalen))
93: return(FALSE);
94:
95: if ((vifi = find_vif_direct(src)) == NO_VIF) {
96: /* Either a local vif or somehow received PIM_HELLO from
97: * non-directly connected router. Ignore it.
98: */
99: if (local_address(src) == NO_VIF)
100: log(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
101: inet_fmt(src, s1));
102: return(FALSE);
103: }
104:
105: v = &uvifs[vifi];
106: if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED))
107: return(FALSE); /* Shoudn't come on this interface */
108: data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
109:
110: /* Get the Holdtime (in seconds) from the message. Return if error. */
111: if (parse_pim_hello(pim_message, datalen, src, &holdtime) == FALSE)
112: return(FALSE);
113: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
114: log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
115: inet_fmt(src, s1), holdtime);
116:
117: for (prev_nbr = (pim_nbr_entry_t *)NULL, nbr = v->uv_pim_neighbors;
118: nbr != (pim_nbr_entry_t *)NULL;
119: prev_nbr = nbr, nbr = nbr->next) {
120: /* The PIM neighbors are sorted in decreasing order of the
121: * network addresses (note that to be able to compare them
122: * correctly we must translate the addresses in host order.
123: */
124: if (ntohl(src) < ntohl(nbr->address))
125: continue;
126: if (src == nbr->address) {
127: /* We already have an entry for this host */
128: if (0 == holdtime) {
129: /* Looks like we have a nice neighbor who is going down
130: * and wants to inform us by sending "holdtime=0". Thanks
131: * buddy and see you again!
132: */
133: log(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
134: inet_fmt(src, s1));
135: delete_pim_nbr(nbr);
136: return(TRUE);
137: }
138: SET_TIMER(nbr->timer, holdtime);
139: return(TRUE);
140: }
141: else
142: /*
143: * No entry for this neighbor. Exit the loop and create an
144: * entry for it.
145: */
146: break;
147: }
148:
149: /*
150: * This is a new neighbor. Create a new entry for it.
151: * It must be added right after `prev_nbr`
152: */
153: new_nbr = (pim_nbr_entry_t *)malloc(sizeof(pim_nbr_entry_t));
154: new_nbr->address = src;
155: new_nbr->vifi = vifi;
156: SET_TIMER(new_nbr->timer, holdtime);
157: new_nbr->next = nbr;
158: new_nbr->prev = prev_nbr;
159:
160: if (prev_nbr != (pim_nbr_entry_t *)NULL)
161: prev_nbr->next = new_nbr;
162: else
163: v->uv_pim_neighbors = new_nbr;
164: if (new_nbr->next != (pim_nbr_entry_t *)NULL)
165: new_nbr->next->prev = new_nbr;
166:
167: v->uv_flags &= ~VIFF_NONBRS;
168: v->uv_flags |= VIFF_PIM_NBR;
169: VIFM_SET(vifi, nbr_vifs);
170:
171: /* Elect a new DR */
172: if (ntohl(v->uv_lcl_addr) <
173: ntohl(v->uv_pim_neighbors->address)) {
174: /* The first address is the new potential remote
175: * DR address and it wins (is >) over the local address.
176: */
177: v->uv_flags &= ~VIFF_DR;
178: v->uv_flags &= ~VIFF_QUERIER;
179: }
180:
181: /* Since a new neighbour has come up, let it know your existence */
182: /* XXX: TODO: not in the spec,
183: * but probably should send the message after a short random period?
184: */
185: send_pim_hello(v, PIM_TIMER_HELLO_HOLDTIME);
186:
187: /* Update the source entries */
188: for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
189: srcentry_ptr = srcentry_ptr_next) {
190: srcentry_ptr_next = srcentry_ptr->next;
191:
192: if (srcentry_ptr->incoming == vifi)
193: continue;
194:
195: for (mrtentry_ptr = srcentry_ptr->mrtlink;
196: mrtentry_ptr != (mrtentry_t *)NULL;
197: mrtentry_ptr = mrtentry_ptr->srcnext) {
198:
199: if(!(VIFM_ISSET(vifi, mrtentry_ptr->oifs))) {
200: state_change =
201: change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
202: mrtentry_ptr->pruned_oifs,
203: mrtentry_ptr->leaves);
204: if(state_change == 1)
205: trigger_join_alert(mrtentry_ptr);
206: }
207: }
208: }
209:
210: IF_DEBUG(DEBUG_PIM_HELLO)
211: dump_vifs(stderr); /* Show we got a new neighbor */
212: return(TRUE);
213: }
214:
215:
216: void
217: delete_pim_nbr(nbr_delete)
218: pim_nbr_entry_t *nbr_delete;
219: {
220: srcentry_t *srcentry_ptr;
221: srcentry_t *srcentry_ptr_next;
222: mrtentry_t *mrtentry_ptr;
223: struct uvif *v;
224: int state_change;
225:
226: v = &uvifs[nbr_delete->vifi];
227:
228: /* Delete the entry from the pim_nbrs chain */
229: if (nbr_delete->prev != (pim_nbr_entry_t *)NULL)
230: nbr_delete->prev->next = nbr_delete->next;
231: else
232: v->uv_pim_neighbors = nbr_delete->next;
233: if (nbr_delete->next != (pim_nbr_entry_t *)NULL)
234: nbr_delete->next->prev = nbr_delete->prev;
235:
236: if (v->uv_pim_neighbors == (pim_nbr_entry_t *)NULL) {
237: /* This was our last neighbor. */
238: v->uv_flags &= ~VIFF_PIM_NBR;
239: v->uv_flags |= (VIFF_NONBRS | VIFF_DR | VIFF_QUERIER);
240: VIFM_CLR(nbr_delete->vifi, nbr_vifs);
241: }
242: else {
243: if (ntohl(v->uv_lcl_addr) >
244: ntohl(v->uv_pim_neighbors->address)) {
245: /* The first address is the new potential remote
246: * DR address, but the local address is the winner.
247: */
248: v->uv_flags |= VIFF_DR;
249: v->uv_flags |= VIFF_QUERIER;
250: }
251: }
252:
253: /* Update the source entries:
254: * If the deleted nbr was my upstream, then reset incoming and
255: * update all (S,G) entries for sources reachable through it.
256: * If the deleted nbr was the last on a non-iif vif, then recalcuate
257: * outgoing interfaces.
258: */
259: for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
260: srcentry_ptr = srcentry_ptr_next) {
261: srcentry_ptr_next = srcentry_ptr->next;
262:
263: /* The only time we don't need to scan all mrtentries is when the nbr
264: * was on the iif, but not the upstream nbr!
265: */
266: if (nbr_delete->vifi == srcentry_ptr->incoming &&
267: srcentry_ptr->upstream != nbr_delete)
268: continue;
269:
270: /* Reset the next hop (PIM) router */
271: if(srcentry_ptr->upstream == nbr_delete)
272: if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) {
273: /* Coudn't reset it. Sorry, the hext hop router toward that
274: * source is probably not a PIM router, or cannot find route
275: * at all, hence I cannot handle this source and have to
276: * delete it.
277: */
278: delete_srcentry(srcentry_ptr);
279: free((char *)nbr_delete);
280: return;
281: }
282:
283: for (mrtentry_ptr = srcentry_ptr->mrtlink;
284: mrtentry_ptr != (mrtentry_t *)NULL;
285: mrtentry_ptr = mrtentry_ptr->srcnext) {
286: mrtentry_ptr->incoming = srcentry_ptr->incoming;
287: mrtentry_ptr->upstream = srcentry_ptr->upstream;
288: mrtentry_ptr->metric = srcentry_ptr->metric;
289: mrtentry_ptr->preference = srcentry_ptr->preference;
290: state_change =
291: change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
292: mrtentry_ptr->pruned_oifs,
293: mrtentry_ptr->leaves);
294: if(state_change == -1) {
295: trigger_prune_alert(mrtentry_ptr);
296: } else if(state_change == 1) {
297: trigger_join_alert(mrtentry_ptr);
298: }
299: }
300: }
301:
302: free((char *)nbr_delete);
303: }
304:
305:
306: /* TODO: simplify it! */
307: static int
308: parse_pim_hello(pim_message, datalen, src, holdtime)
309: char *pim_message;
310: int datalen;
311: u_int32 src;
312: u_int16 *holdtime;
313: {
314: u_int8 *pim_hello_message;
315: u_int8 *data_ptr;
316: u_int16 option_type;
317: u_int16 option_length;
318: int holdtime_received_ok = FALSE;
319: int option_total_length;
320:
321: pim_hello_message = (u_int8 *)(pim_message + sizeof(pim_header_t));
322: datalen -= sizeof(pim_header_t);
323: for ( ; datalen >= sizeof(pim_hello_t); ) {
324: /* Ignore any data if shorter than (pim_hello header) */
325: data_ptr = pim_hello_message;
326: GET_HOSTSHORT(option_type, data_ptr);
327: GET_HOSTSHORT(option_length, data_ptr);
328: switch (option_type) {
329: case PIM_MESSAGE_HELLO_HOLDTIME:
330: if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) {
331: IF_DEBUG(DEBUG_PIM_HELLO)
332: log(LOG_DEBUG, 0,
333: "PIM HELLO Holdtime from %s: invalid OptionLength = %u",
334: inet_fmt(src, s1), option_length);
335: return (FALSE);
336: }
337: GET_HOSTSHORT(*holdtime, data_ptr);
338: holdtime_received_ok = TRUE;
339: break;
340: default:
341: /* Ignore any unknown options */
342: break;
343: }
344:
345: /* Move to the next option */
346: /* XXX: TODO: If we are padding to the end of the 32 bit boundary,
347: * use the first method to move to the next option, otherwise
348: * simply (sizeof(pim_hello_t) + option_length).
349: */
350: #ifdef BOUNDARY_32_BIT
351: option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) +
352: ((option_length & 0x3) ? 4 : 0));
353: #else
354: option_total_length = (sizeof(pim_hello_t) + option_length);
355: #endif /* BOUNDARY_32_BIT */
356: datalen -= option_total_length;
357: pim_hello_message += option_total_length;
358: }
359: return (holdtime_received_ok);
360: }
361:
362:
363: int
364: send_pim_hello(v, holdtime)
365: struct uvif *v;
366: u_int16 holdtime;
367: {
368: char *buf;
369: u_int8 *data_ptr;
370:
371: int datalen;
372:
373: buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
374: data_ptr = (u_int8 *)buf;
375: PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr);
376: PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr);
377: PUT_HOSTSHORT(holdtime, data_ptr);
378:
379: datalen = data_ptr - (u_int8 *)buf;
380:
381: send_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO,
382: datalen);
383: SET_TIMER(v->uv_pim_hello_timer, PIM_TIMER_HELLO_PERIOD);
384: return(TRUE);
385: }
386:
387:
388: /************************************************************************
389: * PIM_JOIN_PRUNE
390: ************************************************************************/
391:
392: typedef struct {
393: u_int32 source;
394: u_int32 group;
395: u_int32 target;
396: } join_delay_cbk_t;
397:
398: typedef struct {
399: vifi_t vifi;
400: u_int32 source;
401: u_int32 group;
402: u_int16 holdtime;
403: } prune_delay_cbk_t;
404:
405: static void
406: delayed_join_job(arg)
407: void *arg;
408: {
409: mrtentry_t *mrtentry_ptr;
410:
411: join_delay_cbk_t *cbk = (join_delay_cbk_t *)arg;
412:
413: mrtentry_ptr = find_route(cbk->source, cbk->group, MRTF_SG, DONT_CREATE);
414: if(mrtentry_ptr == (mrtentry_t *)NULL)
415: return;
416:
417: if(mrtentry_ptr->join_delay_timerid)
418: timer_clearTimer(mrtentry_ptr->join_delay_timerid);
419:
420: if(mrtentry_ptr->upstream)
421: send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
422: mrtentry_ptr->upstream->address, 0);
423:
424: free(cbk);
425: }
426:
427: static void
428: schedule_delayed_join(mrtentry_ptr, target)
429: mrtentry_t *mrtentry_ptr;
430: u_int32 target;
431: {
432: u_long random_delay;
433: join_delay_cbk_t *cbk;
434:
435: /* Delete existing timer */
436: if(mrtentry_ptr->join_delay_timerid)
437: timer_clearTimer(mrtentry_ptr->join_delay_timerid);
438:
439: #ifdef SYSV
440: random_delay = lrand48() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
441: #else
442: random_delay = random() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
443: #endif
444:
445: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
446: log(LOG_DEBUG, 0, "Scheduling join for src %s, grp %s, delay %d",
447: inet_fmt(mrtentry_ptr->source->address, s1),
448: inet_fmt(mrtentry_ptr->group->group, s2),
449: random_delay);
450:
451: if(random_delay == 0 && mrtentry_ptr->upstream) {
452: send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
453: mrtentry_ptr->upstream->address, 0);
454: return;
455: }
456:
457: cbk = (join_delay_cbk_t *)malloc(sizeof(join_delay_cbk_t));
458: cbk->source = mrtentry_ptr->source->address;
459: cbk->group = mrtentry_ptr->group->group;
460: cbk->target = target;
461:
462: mrtentry_ptr->join_delay_timerid =
463: timer_setTimer(random_delay, delayed_join_job, cbk);
464: }
465:
466:
467: static void
468: delayed_prune_job(arg)
469: void *arg;
470: {
471: mrtentry_t *mrtentry_ptr;
472: vifbitmap_t new_pruned_oifs;
473: int state_change;
474:
475: prune_delay_cbk_t *cbk = (prune_delay_cbk_t *)arg;
476:
477: mrtentry_ptr = find_route(cbk->source, cbk->group, MRTF_SG, DONT_CREATE);
478: if(mrtentry_ptr == (mrtentry_t *)NULL)
479: return;
480:
481: if(mrtentry_ptr->prune_delay_timerids[cbk->vifi])
482: timer_clearTimer(mrtentry_ptr->prune_delay_timerids[cbk->vifi]);
483:
484: if(VIFM_ISSET(cbk->vifi, mrtentry_ptr->oifs)) {
485: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
486: log(LOG_DEBUG, 0, "Deleting pruned vif %d for src %s, grp %s",
487: cbk->vifi,
488: inet_fmt(cbk->source, s1),
489: inet_fmt(cbk->group, s2));
490:
491: VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
492: VIFM_SET(cbk->vifi, new_pruned_oifs);
493: SET_TIMER(mrtentry_ptr->prune_timers[cbk->vifi], cbk->holdtime);
494:
495: state_change =
496: change_interfaces(mrtentry_ptr,
497: mrtentry_ptr->incoming,
498: new_pruned_oifs,
499: mrtentry_ptr->leaves);
500:
501: /* Handle transition to negative cache */
502: if(state_change == -1)
503: trigger_prune_alert(mrtentry_ptr);
504: }
505:
506: free(cbk);
507: }
508:
509: static void
510: schedule_delayed_prune(mrtentry_ptr, vifi, holdtime)
511: mrtentry_t *mrtentry_ptr;
512: vifi_t vifi;
513: u_int16 holdtime;
514: {
515: prune_delay_cbk_t *cbk;
516:
517: /* Delete existing timer */
518: if(mrtentry_ptr->prune_delay_timerids[vifi])
519: timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
520:
521: cbk = (prune_delay_cbk_t *)malloc(sizeof(prune_delay_cbk_t));
522: cbk->vifi = vifi;
523: cbk->source = mrtentry_ptr->source->address;
524: cbk->group = mrtentry_ptr->group->group;
525: cbk->holdtime = holdtime;
526:
527: mrtentry_ptr->prune_delay_timerids[vifi] =
528: timer_setTimer((u_int16)PIM_RANDOM_DELAY_JOIN_TIMEOUT+1,
529: delayed_prune_job, cbk);
530: }
531:
532:
533: /* TODO: when parsing, check if we go beyong message size */
534: int
535: receive_pim_join_prune(src, dst, pim_message, datalen)
536: u_int32 src, dst;
537: char *pim_message;
538: register int datalen;
539: {
540: vifi_t vifi;
541: struct uvif *v;
542: pim_encod_uni_addr_t uni_target_addr;
543: pim_encod_grp_addr_t encod_group;
544: pim_encod_src_addr_t encod_src;
545: u_int8 *data_ptr;
546: u_int8 num_groups;
547: u_int16 holdtime;
548: u_int16 num_j_srcs;
549: u_int16 num_p_srcs;
550: u_int32 source;
551: u_int32 group;
552: u_int32 s_mask;
553: u_int32 g_mask;
554: u_int8 s_flags;
555: u_int8 reserved;
556: mrtentry_t *mrtentry_ptr;
557: pim_nbr_entry_t *upstream_router;
558: vifbitmap_t new_pruned_oifs;
559: int state_change;
560:
561: if ((vifi = find_vif_direct(src)) == NO_VIF) {
562: /* Either a local vif or somehow received PIM_JOIN_PRUNE from
563: * non-directly connected router. Ignore it.
564: */
565: if (local_address(src) == NO_VIF)
566: log(LOG_INFO, 0,
567: "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
568: inet_fmt(src, s1));
569: return(FALSE);
570: }
571:
572: /* Checksum */
573: if (inet_cksum((u_int16 *)pim_message, datalen))
574: return(FALSE);
575:
576: v = &uvifs[vifi];
577: if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
578: return(FALSE); /* Shoudn't come on this interface */
579: data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
580:
581: /* Get the target address */
582: GET_EUADDR(&uni_target_addr, data_ptr);
583: GET_BYTE(reserved, data_ptr);
584: GET_BYTE(num_groups, data_ptr);
585: if (num_groups == 0)
586: return (FALSE); /* No indication for groups in the message */
587: GET_HOSTSHORT(holdtime, data_ptr);
588:
589: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
590: log(LOG_DEBUG, 0,
591: "PIM Join/Prune received from %s : target %s, holdtime %d",
592: inet_fmt(src, s1), inet_fmt(uni_target_addr.unicast_addr, s2),
593: holdtime);
594:
595: if ((uni_target_addr.unicast_addr != v->uv_lcl_addr) &&
596: (uni_target_addr.unicast_addr != INADDR_ANY_N)) {
597: /* if I am not the target of the join or prune message */
598: /* Join Suppression: when receiving a join not addressed to me,
599: * if I am delaying a join for this (S,G) then cancel the delayed
600: * join.
601: * Prune Soliticiting Joins: when receiving a prune not addressed to
602: * me on a LAN, schedule delayed join if I have downstream receivers.
603: */
604:
605: upstream_router = find_pim_nbr(uni_target_addr.unicast_addr);
606: if (upstream_router == (pim_nbr_entry_t *)NULL)
607: return (FALSE); /* I have no such neighbor */
608: while (num_groups--) {
609: GET_EGADDR(&encod_group, data_ptr);
610: GET_HOSTSHORT(num_j_srcs, data_ptr);
611: GET_HOSTSHORT(num_p_srcs, data_ptr);
612: MASKLEN_TO_MASK(encod_group.masklen, g_mask);
613: group = encod_group.mcast_addr;
614: if (!IN_MULTICAST(ntohl(group))) {
615: data_ptr +=
616: (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
617: continue; /* Ignore this group and jump to the next */
618: }
619:
620: while (num_j_srcs--) {
621: GET_ESADDR(&encod_src, data_ptr);
622: source = encod_src.src_addr;
623: if (!inet_valid_host(source))
624: continue;
625: s_flags = encod_src.flags;
626: MASKLEN_TO_MASK(encod_src.masklen, s_mask);
627:
628: /* (S,G) Join suppresion */
629: mrtentry_ptr = find_route(source, group, MRTF_SG,
630: DONT_CREATE);
631: if(mrtentry_ptr == (mrtentry_t *)NULL)
632: continue;
633:
634: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
635: log(LOG_DEBUG, 0,
636: "\tJOIN src %s, group %s - canceling delayed join",
637: inet_fmt(source, s1), inet_fmt(group, s2));
638:
639: /* Cancel the delayed join */
640: if(mrtentry_ptr->join_delay_timerid) {
641: timer_clearTimer(mrtentry_ptr->join_delay_timerid);
642: mrtentry_ptr->join_delay_timerid = 0;
643: }
644: }
645:
646: while (num_p_srcs--) {
647: GET_ESADDR(&encod_src, data_ptr);
648: source = encod_src.src_addr;
649: if (!inet_valid_host(source))
650: continue;
651: s_flags = encod_src.flags;
652:
653: /* if P2P link (not addressed to me) ignore
654: */
655: if(uvifs[vifi].uv_flags & VIFF_POINT_TO_POINT)
656: continue;
657:
658: /* if non-null oiflist then schedule delayed join
659: */
660: mrtentry_ptr = find_route(source, group, MRTF_SG,
661: DONT_CREATE);
662: if(mrtentry_ptr == (mrtentry_t *)NULL)
663: continue;
664:
665:
666: if(!(VIFM_ISEMPTY(mrtentry_ptr->oifs))) {
667: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
668: log(LOG_DEBUG, 0,
669: "\tPRUNE src %s, group %s - scheduling delayed join",
670: inet_fmt(source, s1), inet_fmt(group, s2));
671:
672: schedule_delayed_join(mrtentry_ptr, uni_target_addr);
673: }
674: }
675:
676: } /* while groups */
677:
678: return(TRUE);
679: } /* if not unicast target */
680:
681: /* I am the target of this join/prune:
682: * For joins, cancel delayed prunes that I have scheduled.
683: * For prunes, echo the prune and schedule delayed prunes on LAN or
684: * prune immediately on point-to-point links.
685: */
686: else {
687: while (num_groups--) {
688: GET_EGADDR(&encod_group, data_ptr);
689: GET_HOSTSHORT(num_j_srcs, data_ptr);
690: GET_HOSTSHORT(num_p_srcs, data_ptr);
691: MASKLEN_TO_MASK(encod_group.masklen, g_mask);
692: group = encod_group.mcast_addr;
693: if (!IN_MULTICAST(ntohl(group))) {
694: data_ptr +=
695: (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
696: continue; /* Ignore this group and jump to the next */
697: }
698:
699: while (num_j_srcs--) {
700: GET_ESADDR(&encod_src, data_ptr);
701: source = encod_src.src_addr;
702: if (!inet_valid_host(source))
703: continue;
704: s_flags = encod_src.flags;
705: MASKLEN_TO_MASK(encod_src.masklen, s_mask);
706:
707: mrtentry_ptr = find_route(source, group, MRTF_SG,
708: DONT_CREATE);
709: if(mrtentry_ptr == (mrtentry_t *)NULL)
710: continue;
711:
712: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
713: log(LOG_DEBUG, 0,
714: "\tJOIN src %s, group %s - canceling delayed prune",
715: inet_fmt(source, s1), inet_fmt(group, s2));
716:
717: /* Cancel the delayed prune */
718: if(mrtentry_ptr->prune_delay_timerids[vifi]) {
719: timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
720: mrtentry_ptr->prune_delay_timerids[vifi] = 0;
721: }
722: }
723:
724: while (num_p_srcs--) {
725: GET_ESADDR(&encod_src, data_ptr);
726: source = encod_src.src_addr;
727: if (!inet_valid_host(source))
728: continue;
729: s_flags = encod_src.flags;
730:
731:
732: mrtentry_ptr = find_route(source, group, MRTF_SG,
733: DONT_CREATE);
734: if(mrtentry_ptr == (mrtentry_t *)NULL)
735: continue;
736:
737: /* if P2P link (addressed to me) prune immediately
738: */
739: if(uvifs[vifi].uv_flags & VIFF_POINT_TO_POINT) {
740: if(VIFM_ISSET(vifi, mrtentry_ptr->pruned_oifs)) {
741:
742: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
743: log(LOG_DEBUG, 0,
744: "\tPRUNE(P2P) src %s, group %s - pruning vif",
745: inet_fmt(source, s1), inet_fmt(group, s2));
746:
747: IF_DEBUG(DEBUG_MRT)
748: log(LOG_DEBUG, 0, "Deleting pruned vif %d for src %s, grp %s",
749: vifi,
750: inet_fmt(source, s1), inet_fmt(group, s2));
751:
752: VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
753: VIFM_SET(vifi, new_pruned_oifs);
754: SET_TIMER(mrtentry_ptr->prune_timers[vifi], holdtime);
755:
756: state_change =
757: change_interfaces(mrtentry_ptr,
758: mrtentry_ptr->incoming,
759: new_pruned_oifs,
760: mrtentry_ptr->leaves);
761:
762: /* Handle transition to negative cache */
763: if(state_change == -1)
764: trigger_prune_alert(mrtentry_ptr);
765: } /* if is pruned */
766: } /* if p2p */
767:
768: /* if LAN link, echo the prune and schedule delayed
769: * oif deletion
770: */
771: else {
772: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
773: log(LOG_DEBUG, 0,
774: "\tPRUNE(LAN) src %s, group %s - scheduling delayed prune",
775: inet_fmt(source, s1), inet_fmt(group, s2));
776:
777: send_pim_jp(mrtentry_ptr, PIM_ACTION_PRUNE, vifi,
778: uni_target_addr.unicast_addr, holdtime);
779: schedule_delayed_prune(mrtentry_ptr, vifi, holdtime);
780: }
781: }
782: } /* while groups */
783: } /* else I am unicast target */
784:
785: return(TRUE);
786: }
787:
788:
789: int
790: send_pim_jp(mrtentry_ptr, action, vifi, target_addr, holdtime)
791: mrtentry_t *mrtentry_ptr;
792: int action; /* PIM_ACTION_JOIN or PIM_ACTION_PRUNE */
793: vifi_t vifi; /* vif to send join/prune on */
794: u_int32 target_addr; /* encoded unicast target neighbor */
795: u_int16 holdtime; /* holdtime */
796: {
797: u_int8 *data_ptr, *data_start_ptr;
798:
799: data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
800: + sizeof(pim_header_t));
801: data_start_ptr = data_ptr;
802:
803: if(mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
804: /* No upstream neighbor - don't send */
805: return(FALSE);
806: }
807:
808: IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
809: log(LOG_DEBUG, 0,
810: "Sending %s: vif %s, src %s, group %s, target %s, holdtime %d",
811: action==PIM_ACTION_JOIN ? "JOIN" : "PRUNE",
812: inet_fmt(uvifs[vifi].uv_lcl_addr, s1),
813: inet_fmt(mrtentry_ptr->source->address, s2),
814: inet_fmt(mrtentry_ptr->group->group, s3),
815: inet_fmt(target_addr, s4), holdtime);
816:
817: PUT_EUADDR(target_addr, data_ptr); /* encoded unicast target addr */
818: PUT_BYTE(0, data_ptr); /* Reserved */
819: *data_ptr++ = (u_int8)1; /* number of groups */
820: PUT_HOSTSHORT(holdtime, data_ptr); /* holdtime */
821:
822: /* data_ptr points at the first, and only encoded mcast group */
823: PUT_EGADDR(mrtentry_ptr->group->group, SINGLE_GRP_MSKLEN, 0, data_ptr);
824:
825: /* set the number of join and prune sources */
826: if(action == PIM_ACTION_JOIN) {
827: PUT_HOSTSHORT(1, data_ptr);
828: PUT_HOSTSHORT(0, data_ptr);
829: } else if(action == PIM_ACTION_PRUNE) {
830: PUT_HOSTSHORT(0, data_ptr);
831: PUT_HOSTSHORT(1, data_ptr);
832: }
833:
834: PUT_ESADDR(mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN,
835: 0, data_ptr);
836:
837: /* Cancel active graft */
838: delete_pim_graft_entry(mrtentry_ptr);
839:
840: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
841: PIM_JOIN_PRUNE, data_ptr - data_start_ptr);
842:
843: return(TRUE);
844: }
845:
846:
847: /************************************************************************
848: * PIM_ASSERT
849: ************************************************************************/
850:
851: /* Notes on assert prefs/metrics
852: * - For downstream routers, compare pref/metric previously received from
853: * winner against those in message.
854: * ==> store assert winner's pref/metric in mrtentry
855: * - For upstream router compare my actualy pref/metric for the source
856: * against those received in message.
857: * ==> store my actual pref/metric in srcentry
858: */
859:
860: int
861: receive_pim_assert(src, dst, pim_message, datalen)
862: u_int32 src, dst;
863: register char *pim_message;
864: int datalen;
865: {
866: vifi_t vifi;
867: pim_encod_uni_addr_t eusaddr;
868: pim_encod_grp_addr_t egaddr;
869: u_int32 source, group;
870: mrtentry_t *mrtentry_ptr;
871: u_int8 *data_ptr;
872: struct uvif *v;
873: u_int32 assert_preference;
874: u_int32 assert_metric;
875: u_int32 local_metric;
876: u_int32 local_preference;
877: u_int8 local_wins;
878: vifbitmap_t new_pruned_oifs;
879: int state_change;
880:
881: if ((vifi = find_vif_direct(src)) == NO_VIF) {
882: /* Either a local vif or somehow received PIM_ASSERT from
883: * non-directly connected router. Ignore it.
884: */
885: if (local_address(src) == NO_VIF)
886: log(LOG_INFO, 0,
887: "Ignoring PIM_ASSERT from non-neighbor router %s",
888: inet_fmt(src, s1));
889: return(FALSE);
890: }
891:
892: /* Checksum */
893: if (inet_cksum((u_int16 *)pim_message, datalen))
894: return(FALSE);
895:
896: v = &uvifs[vifi];
897: if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
898: return(FALSE); /* Shoudn't come on this interface */
899: data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
900:
901: /* Get the group and source addresses */
902: GET_EGADDR(&egaddr, data_ptr);
903: GET_EUADDR(&eusaddr, data_ptr);
904:
905: /* Get the metric related info */
906: GET_HOSTLONG(assert_preference, data_ptr);
907: GET_HOSTLONG(assert_metric, data_ptr);
908:
909: source = eusaddr.unicast_addr;
910: group = egaddr.mcast_addr;
911:
912: IF_DEBUG(DEBUG_PIM_ASSERT)
913: log(LOG_DEBUG, 0,
914: "PIM Assert received from : src %s, grp %s, pref %d, metric %d",
915: inet_fmt(src, s1), inet_fmt(source, s2), inet_fmt(group, s3),
916: assert_preference, assert_metric);
917:
918: mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE);
919: if(mrtentry_ptr->flags & MRTF_NEW) {
920: /* For some reason, it's possible for asserts to be processed
921: * before the data alerts a cache miss. Therefore, when an
922: * assert is received, create (S,G) state and continue, since
923: * we know by the assert that there are upstream forwarders.
924: */
925: IF_DEBUG(DEBUG_PIM_ASSERT)
926: log(LOG_DEBUG, 0, "\tNo MRT entry - creating...");
927:
928: mrtentry_ptr->flags &= ~MRTF_NEW;
929:
930: /* Set oifs */
931: set_leaves(mrtentry_ptr);
932: calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
933:
934: /* Add it to the kernel */
935: k_chg_mfc(igmp_socket, source, group, mrtentry_ptr->incoming,
936: mrtentry_ptr->oifs);
937:
938: #ifdef RSRR
939: rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
940: #endif /* RSRR */
941:
942: /* No need to call change_interfaces, but check for NULL oiflist */
943: if(VIFM_ISEMPTY(mrtentry_ptr->oifs))
944: trigger_prune_alert(mrtentry_ptr);
945: }
946:
947: /* If arrived on iif, I'm downstream of the asserted LAN.
948: * If arrived on oif, I'm upstream of the asserted LAN.
949: */
950: if (vifi == mrtentry_ptr->incoming) {
951: /* assert arrived on iif ==> I'm a downstream router */
952:
953: /* Determine local (really that of upstream nbr!) pref/metric */
954: local_metric = mrtentry_ptr->metric;
955: local_preference = mrtentry_ptr->preference;
956:
957: if(mrtentry_ptr->upstream &&
958: mrtentry_ptr->upstream->address == src &&
959: assert_preference == local_preference &&
960: assert_metric == local_metric)
961:
962: /* if assert from previous winner w/ same pref/metric,
963: * then assert sender wins again */
964: local_wins = FALSE;
965:
966: else
967: /* assert from someone else or something changed */
968: local_wins = compare_metrics(local_preference, local_metric,
969: mrtentry_ptr->upstream->address,
970: assert_preference, assert_metric,
971: src);
972:
973: /* This is between the assert sender and previous winner or rpf
974: * (who is the "local" in this case).
975: */
976: if(local_wins == TRUE) {
977: /* the assert-sender loses, so discard the assert */
978: IF_DEBUG(DEBUG_PIM_ASSERT)
979: log(LOG_DEBUG, 0,
980: "\tAssert sender %s loses", inet_fmt(src, s1));
981: return(TRUE);
982: }
983:
984: /* The assert sender wins: upstream must be changed to the winner */
985: IF_DEBUG(DEBUG_PIM_ASSERT)
986: log(LOG_DEBUG, 0,
987: "\tAssert sender %s wins", inet_fmt(src, s1));
988: if(mrtentry_ptr->upstream->address != src) {
989: IF_DEBUG(DEBUG_PIM_ASSERT)
990: log(LOG_DEBUG, 0,
991: "\tChanging upstream nbr to %s", inet_fmt(src, s1));
992: mrtentry_ptr->preference = assert_preference;
993: mrtentry_ptr->metric = assert_metric;
994: mrtentry_ptr->upstream = find_pim_nbr(src);
995: }
996: SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT);
997: mrtentry_ptr->flags |= MRTF_ASSERTED;
998:
999: /* Send a join for the S,G if oiflist is non-empty */
1000: if(!(VIFM_ISEMPTY(mrtentry_ptr->oifs)))
1001: send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
1002: src, 0);
1003:
1004: } /* if assert on iif */
1005:
1006: /* If the assert arrived on an oif: */
1007: else {
1008: if(!(VIFM_ISSET(vifi, mrtentry_ptr->oifs)))
1009: return(FALSE);
1010: /* assert arrived on oif ==> I'm a upstream router */
1011:
1012: /* Determine local pref/metric */
1013: local_metric = mrtentry_ptr->source->metric;
1014: local_preference = mrtentry_ptr->source->preference;
1015:
1016: local_wins = compare_metrics(local_preference, local_metric,
1017: v->uv_lcl_addr,
1018: assert_preference, assert_metric, src);
1019:
1020: if(local_wins == FALSE) {
1021:
1022: /* Assert sender wins - prune the interface */
1023:
1024: IF_DEBUG(DEBUG_PIM_ASSERT)
1025: log(LOG_DEBUG, 0,
1026: "\tAssert sender %s wins - pruning...", inet_fmt(src, s1));
1027:
1028: VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
1029: VIFM_SET(vifi, new_pruned_oifs);
1030: SET_TIMER(mrtentry_ptr->prune_timers[vifi],
1031: PIM_JOIN_PRUNE_HOLDTIME);
1032:
1033: state_change =
1034: change_interfaces(mrtentry_ptr,
1035: mrtentry_ptr->incoming,
1036: new_pruned_oifs,
1037: mrtentry_ptr->leaves);
1038:
1039: /* Handle transition to negative cache */
1040: if(state_change == -1)
1041: trigger_prune_alert(mrtentry_ptr);
1042:
1043: } /* assert sender wins */
1044:
1045: else {
1046:
1047: /* Local wins (assert sender loses):
1048: * send assert and schedule prune
1049: */
1050:
1051: IF_DEBUG(DEBUG_PIM_ASSERT)
1052: log(LOG_DEBUG, 0,
1053: "\tAssert sender %s loses - sending assert and scheuling prune",
1054: inet_fmt(src, s1));
1055:
1056: if(!(VIFM_ISSET(vifi, mrtentry_ptr->leaves))) {
1057: /* No directly connected receivers - delay prune */
1058: send_pim_jp(mrtentry_ptr, PIM_ACTION_PRUNE, vifi,
1059: v->uv_lcl_addr, PIM_JOIN_PRUNE_HOLDTIME);
1060: schedule_delayed_prune(mrtentry_ptr, vifi,
1061: PIM_JOIN_PRUNE_HOLDTIME);
1062: }
1063: send_pim_assert(source, group, vifi,
1064: mrtentry_ptr->source->preference,
1065: mrtentry_ptr->source->metric);
1066: }
1067:
1068: } /* if assert on oif */
1069:
1070: return(TRUE);
1071: }
1072:
1073:
1074: int
1075: send_pim_assert(source, group, vifi, local_preference, local_metric)
1076: u_int32 source;
1077: u_int32 group;
1078: vifi_t vifi;
1079: u_int32 local_preference;
1080: u_int32 local_metric;
1081: {
1082: u_int8 *data_ptr;
1083: u_int8 *data_start_ptr;
1084:
1085: data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
1086: + sizeof(pim_header_t));
1087: data_start_ptr = data_ptr;
1088: PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data_ptr);
1089: PUT_EUADDR(source, data_ptr);
1090:
1091: PUT_HOSTLONG(local_preference, data_ptr);
1092: PUT_HOSTLONG(local_metric, data_ptr);
1093: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
1094: PIM_ASSERT, data_ptr - data_start_ptr);
1095:
1096: return(TRUE);
1097: }
1098:
1099:
1100: /* Return TRUE if the local win, otherwise FALSE */
1101: static int
1102: compare_metrics(local_preference, local_metric, local_address,
1103: remote_preference, remote_metric, remote_address)
1104: u_int32 local_preference;
1105: u_int32 local_metric;
1106: u_int32 local_address;
1107: u_int32 remote_preference;
1108: u_int32 remote_metric;
1109: u_int32 remote_address;
1110: {
1111: /* Now lets see who has a smaller gun (aka "asserts war") */
1112: /* FYI, the smaller gun...err metric wins, but if the same
1113: * caliber, then the bigger network address wins. The order of
1114: * treatment is: preference, metric, address.
1115: */
1116: /* The RPT bits are already included as the most significant bits
1117: * of the preferences.
1118: */
1119: if (remote_preference > local_preference)
1120: return TRUE;
1121: if (remote_preference < local_preference)
1122: return FALSE;
1123: if (remote_metric > local_metric)
1124: return TRUE;
1125: if (remote_metric < local_metric)
1126: return FALSE;
1127: if (ntohl(local_address) > ntohl(remote_address))
1128: return TRUE;
1129: return FALSE;
1130: }
1131:
1132:
1133:
1134:
1135: /************************************************************************
1136: * PIM_GRAFT
1137: ************************************************************************/
1138:
1139:
1140: u_long graft_retrans_timer; /* Graft retransmission timer */
1141: pim_graft_entry_t *graft_list; /* Active grafting entries */
1142:
1143:
1144: void
1145: delete_pim_graft_entry(mrtentry_ptr)
1146: mrtentry_t *mrtentry_ptr;
1147: {
1148: pim_graft_entry_t *graft_entry;
1149:
1150: if(mrtentry_ptr->graft == (pim_graft_entry_t *)NULL)
1151: return;
1152: graft_entry = mrtentry_ptr->graft;
1153:
1154: if(graft_entry->prev)
1155: graft_entry->prev->next = graft_entry->next;
1156: else
1157: graft_list = graft_entry->next;
1158: if(graft_entry->next)
1159: graft_entry->next->prev = graft_entry->prev;
1160: mrtentry_ptr->graft = (pim_graft_entry_t *)NULL;
1161: free(graft_entry);
1162:
1163: /* Stop the timer if there are no more entries */
1164: if(!graft_list) {
1165: timer_clearTimer(graft_retrans_timer);
1166: RESET_TIMER(graft_retrans_timer);
1167: }
1168: }
1169:
1170:
1171: int retransmit_pim_graft(mrtentry_ptr)
1172: mrtentry_t *mrtentry_ptr;
1173: {
1174: u_int8 *data_ptr, *data_start_ptr;
1175:
1176: data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
1177: + sizeof(pim_header_t));
1178: data_start_ptr = data_ptr;
1179:
1180: if(mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
1181: /* No upstream neighbor - don't send */
1182: return(FALSE);
1183: }
1184:
1185: IF_DEBUG(DEBUG_PIM_GRAFT)
1186: log(LOG_DEBUG, 0,
1187: "Sending GRAFT: vif %s, src %s, grp %s, dst %s",
1188: inet_fmt(uvifs[mrtentry_ptr->incoming].uv_lcl_addr, s1),
1189: inet_fmt(mrtentry_ptr->source->address, s2),
1190: inet_fmt(mrtentry_ptr->group->group, s3),
1191: inet_fmt(mrtentry_ptr->upstream->address, s4));
1192:
1193:
1194: PUT_EUADDR(mrtentry_ptr->upstream->address, data_ptr); /* unicast target */
1195: PUT_BYTE(0, data_ptr); /* Reserved */
1196: *data_ptr++ = (u_int8)1; /* number of groups */
1197: PUT_HOSTSHORT(0, data_ptr); /* no holdtime */
1198:
1199: /* data_ptr points at the first, and only encoded mcast group */
1200: PUT_EGADDR(mrtentry_ptr->group->group, SINGLE_GRP_MSKLEN, 0, data_ptr);
1201:
1202: /* set the number of join(graft) and prune sources */
1203: PUT_HOSTSHORT(1, data_ptr);
1204: PUT_HOSTSHORT(0, data_ptr);
1205:
1206: PUT_ESADDR(mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN,
1207: 0, data_ptr);
1208:
1209: send_pim(pim_send_buf, uvifs[mrtentry_ptr->incoming].uv_lcl_addr,
1210: mrtentry_ptr->upstream->address,
1211: PIM_GRAFT, data_ptr - data_start_ptr);
1212:
1213: return(TRUE);
1214: }
1215:
1216:
1217: static void
1218: retransmit_all_pim_grafts(arg)
1219: void *arg; /* UNUSED */
1220: {
1221: pim_graft_entry_t *graft_ptr;
1222:
1223: IF_DEBUG(DEBUG_PIM_GRAFT)
1224: log(LOG_DEBUG, 0, "Retransmitting all pending PIM-Grafts");
1225:
1226:
1227: for(graft_ptr = graft_list;
1228: graft_ptr != NULL;
1229: graft_ptr = graft_ptr->next) {
1230:
1231: IF_DEBUG(DEBUG_PIM_GRAFT)
1232: log(LOG_DEBUG, 0, "\tGRAFT src %s, grp %s",
1233: inet_fmt(graft_ptr->mrtlink->source->address, s1),
1234: inet_fmt(graft_ptr->mrtlink->group->group, s2));
1235:
1236: retransmit_pim_graft(graft_ptr->mrtlink);
1237: }
1238:
1239: if(graft_list)
1240: timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, retransmit_all_pim_grafts,
1241: (void *)NULL);
1242: }
1243:
1244:
1245: int
1246: receive_pim_graft(src, dst, pim_message, datalen, pimtype)
1247: u_int32 src, dst;
1248: register char *pim_message;
1249: int datalen;
1250: int pimtype;
1251: {
1252: vifi_t vifi;
1253: struct uvif *v;
1254: pim_encod_uni_addr_t uni_target_addr;
1255: pim_encod_grp_addr_t encod_group;
1256: pim_encod_src_addr_t encod_src;
1257: u_int8 *data_ptr;
1258: u_int8 num_groups;
1259: u_int16 holdtime;
1260: u_int16 num_j_srcs;
1261: u_int16 num_p_srcs;
1262: u_int32 source;
1263: u_int32 group;
1264: u_int32 s_mask;
1265: u_int32 g_mask;
1266: u_int8 s_flags;
1267: u_int8 reserved;
1268: mrtentry_t *mrtentry_ptr;
1269: int state_change;
1270:
1271: if ((vifi = find_vif_direct(src)) == NO_VIF) {
1272: /* Either a local vif or somehow received PIM_GRAFT from
1273: * non-directly connected router. Ignore it.
1274: */
1275: if (local_address(src) == NO_VIF)
1276: log(LOG_INFO, 0,
1277: "Ignoring PIM_GRAFT from non-neighbor router %s",
1278: inet_fmt(src, s1));
1279: return(FALSE);
1280: }
1281:
1282: /* Checksum */
1283: if (inet_cksum((u_int16 *)pim_message, datalen))
1284: return(FALSE);
1285:
1286: v = &uvifs[vifi];
1287: if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
1288: return(FALSE); /* Shoudn't come on this interface */
1289: data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
1290:
1291: /* Get the target address */
1292: GET_EUADDR(&uni_target_addr, data_ptr);
1293: GET_BYTE(reserved, data_ptr);
1294: GET_BYTE(num_groups, data_ptr);
1295: if (num_groups == 0)
1296: return (FALSE); /* No indication for groups in the message */
1297: GET_HOSTSHORT(holdtime, data_ptr);
1298:
1299: IF_DEBUG(DEBUG_PIM_GRAFT)
1300: log(LOG_DEBUG, 0,
1301: "PIM %s received from %s on vif %s",
1302: pimtype == PIM_GRAFT ? "GRAFT" : "GRAFT-ACK",
1303: inet_fmt(src, s1), inet_fmt(uvifs[vifi].uv_lcl_addr, s2));
1304:
1305: while (num_groups--) {
1306: GET_EGADDR(&encod_group, data_ptr);
1307: GET_HOSTSHORT(num_j_srcs, data_ptr);
1308: GET_HOSTSHORT(num_p_srcs, data_ptr);
1309: MASKLEN_TO_MASK(encod_group.masklen, g_mask);
1310: group = encod_group.mcast_addr;
1311: if (!IN_MULTICAST(ntohl(group))) {
1312: data_ptr +=
1313: (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1314: continue; /* Ignore this group and jump to the next */
1315: }
1316:
1317: while (num_j_srcs--) {
1318: GET_ESADDR(&encod_src, data_ptr);
1319: source = encod_src.src_addr;
1320: if (!inet_valid_host(source))
1321: continue;
1322: s_flags = encod_src.flags;
1323: MASKLEN_TO_MASK(encod_src.masklen, s_mask);
1324:
1325: mrtentry_ptr = find_route(source, group, MRTF_SG,
1326: DONT_CREATE);
1327: if(mrtentry_ptr == (mrtentry_t *)NULL)
1328: continue;
1329:
1330: if(pimtype == PIM_GRAFT) {
1331: /* Graft */
1332: IF_DEBUG(DEBUG_PIM_GRAFT)
1333: log(LOG_DEBUG, 0,
1334: "\tGRAFT src %s, group %s - forward data on vif %d",
1335: inet_fmt(source, s1), inet_fmt(group, s2), vifi);
1336:
1337: /* Cancel any delayed prune */
1338: if(mrtentry_ptr->prune_delay_timerids[vifi]) {
1339: timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
1340: mrtentry_ptr->prune_delay_timerids[vifi] = 0;
1341: }
1342:
1343: /* Add to oiflist (unprune) */
1344: if (VIFM_ISSET(vifi, mrtentry_ptr->pruned_oifs)) {
1345: VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs);
1346: SET_TIMER(mrtentry_ptr->prune_timers[vifi], 0);
1347: state_change =
1348: change_interfaces(mrtentry_ptr,
1349: mrtentry_ptr->incoming,
1350: mrtentry_ptr->pruned_oifs,
1351: mrtentry_ptr->leaves);
1352: if(state_change == 1)
1353: trigger_join_alert(mrtentry_ptr);
1354: }
1355: } /* Graft */
1356: else {
1357: /* Graft-Ack */
1358: if(mrtentry_ptr->graft)
1359: delete_pim_graft_entry(mrtentry_ptr);
1360: }
1361: }
1362: /* Ignore anything in the prune portion of the message! */
1363: }
1364:
1365: /* Respond to graft with a graft-ack */
1366: if(pimtype == PIM_GRAFT) {
1367: IF_DEBUG(DEBUG_PIM_GRAFT)
1368: log(LOG_DEBUG, 0, "Sending GRAFT-ACK: vif %s, dst %s",
1369: inet_fmt(uvifs[vifi].uv_lcl_addr, s1), inet_fmt(src, s2));
1370: bcopy(pim_message, pim_send_buf + sizeof(struct ip), datalen);
1371: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr,
1372: src, PIM_GRAFT_ACK, datalen - sizeof(pim_header_t));
1373: }
1374:
1375: return(TRUE);
1376: }
1377:
1378: int
1379: send_pim_graft(mrtentry_ptr)
1380: mrtentry_t *mrtentry_ptr;
1381: {
1382: pim_graft_entry_t *new_graft;
1383: int was_sent = 0;
1384:
1385: if(mrtentry_ptr->graft != (pim_graft_entry_t *)NULL)
1386: /* Already sending grafts */
1387: return(FALSE);
1388:
1389: /* Send the first graft */
1390: was_sent = retransmit_pim_graft(mrtentry_ptr);
1391: if(!was_sent)
1392: return(FALSE);
1393:
1394: /* Set up retransmission */
1395: new_graft = (pim_graft_entry_t *)malloc(sizeof(pim_graft_entry_t));
1396: if (new_graft == (pim_graft_entry_t *)NULL) {
1397: log(LOG_WARNING, 0,
1398: "Memory allocation error for graft entry src %s, grp %s",
1399: inet_fmt(mrtentry_ptr->source->address, s1),
1400: inet_fmt(mrtentry_ptr->group->group, s2));
1401: return(FALSE);
1402: }
1403: new_graft->next = graft_list;
1404: new_graft->prev = (pim_graft_entry_t *)NULL;
1405: new_graft->mrtlink = mrtentry_ptr;
1406: if(graft_list == (pim_graft_entry_t *)NULL)
1407: graft_list = new_graft;
1408: mrtentry_ptr->graft = new_graft;
1409:
1410: /* Set up timer if not running */
1411: if(!graft_retrans_timer)
1412: SET_TIMER(graft_retrans_timer,
1413: timer_setTimer(PIM_GRAFT_RETRANS_PERIOD,
1414: retransmit_all_pim_grafts,
1415: (void *)NULL));
1416:
1417: return(TRUE);
1418: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>