Annotation of embedaddon/pimd/pim_proto.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1998-2001
3: * University of Southern California/Information Sciences Institute.
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: * 3. Neither the name of the project nor the names of its contributors
15: * may be used to endorse or promote products derived from this software
16: * without specific prior written permission.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28: * SUCH DAMAGE.
29: */
30: /*
31: * $Id: pim_proto.c,v 1.47 2003/05/28 22:57:16 pavlin Exp $
32: */
33:
34: #include <arpa/inet.h>
35: #include "defs.h"
36:
37: typedef struct {
38: uint16_t holdtime;
39: uint32_t dr_prio;
40: int8_t dr_prio_present;
41: uint32_t genid;
42: } pim_hello_opts_t;
43:
44: /*
45: * Local functions definitions.
46: */
47: static int restart_dr_election (struct uvif *v);
48: static int parse_pim_hello (char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts);
49: static void cache_nbr_settings (pim_nbr_entry_t *nbr, pim_hello_opts_t *opts);
50: static int send_pim_register_stop (uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_source);
51: static build_jp_message_t *get_jp_working_buff (void);
52: static void return_jp_working_buff (pim_nbr_entry_t *pim_nbr);
53: static void pack_jp_message (pim_nbr_entry_t *pim_nbr);
54: static void send_jp_message (pim_nbr_entry_t *pim_nbr);
55: static int compare_metrics (uint32_t local_preference,
56: uint32_t local_metric,
57: uint32_t local_address,
58: uint32_t remote_preference,
59: uint32_t remote_metric,
60: uint32_t remote_address);
61:
62: build_jp_message_t *build_jp_message_pool;
63: int build_jp_message_pool_counter;
64:
65: /************************************************************************
66: * PIM_HELLO
67: ************************************************************************/
68: int receive_pim_hello(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
69: {
70: vifi_t vifi;
71: struct uvif *v;
72: size_t bsr_length;
73: pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr;
74: pim_hello_opts_t opts;
75: srcentry_t *srcentry;
76: mrtentry_t *mrtentry;
77:
78: if (inet_cksum((uint16_t *)msg, len))
79: return FALSE;
80:
81: vifi = find_vif_direct(src);
82: if (vifi == NO_VIF) {
83: /* Either a local vif or somehow received PIM_HELLO from
84: * non-directly connected router. Ignore it. */
85: if (local_address(src) == NO_VIF)
86: logit(LOG_DEBUG, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
87: inet_fmt(src, s1, sizeof(s1)));
88:
89: return FALSE;
90: }
91:
92: v = &uvifs[vifi];
93: if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER))
94: return FALSE; /* Shoudn't come on this interface */
95:
96: /* Get the Holdtime (in seconds) and any DR priority from the message. Return if error. */
97: if (parse_pim_hello(msg, len, src, &opts) == FALSE)
98: return FALSE;
99:
100: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
101: logit(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
102: inet_fmt(src, s1, sizeof(s1)), opts.holdtime);
103:
104: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
105: logit(LOG_DEBUG, 0, "PIM DR PRIORITY from %s is %u",
106: inet_fmt(src, s1, sizeof(s1)), opts.dr_prio);
107:
108: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
109: logit(LOG_DEBUG, 0, "PIM GenID from %s is %u",
110: inet_fmt(src, s1, sizeof(s1)), opts.genid);
111:
112: for (prev_nbr = NULL, nbr = v->uv_pim_neighbors; nbr; prev_nbr = nbr, nbr = nbr->next) {
113: /* The PIM neighbors are sorted in decreasing order of the
114: * network addresses (note that to be able to compare them
115: * correctly we must translate the addresses in host order.
116: */
117: if (ntohl(src) < ntohl(nbr->address))
118: continue;
119:
120: if (src == nbr->address) {
121: /* We already have an entry for this host */
122: if (0 == opts.holdtime) {
123: /* Looks like we have a nice neighbor who is going down
124: * and wants to inform us by sending "holdtime=0". Thanks
125: * buddy and see you again!
126: */
127: logit(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
128: inet_fmt(src, s1, sizeof(s1)));
129: delete_pim_nbr(nbr);
130:
131: return TRUE;
132: }
133:
134: /* https://tools.ietf.org/html/draft-ietf-pim-hello-genid-01 */
135: if (nbr->genid != opts.genid) {
136: /* Known neighbor rebooted, update info and resend RP-Set */
137: cache_nbr_settings(nbr, &opts);
138: goto rebooted;
139: }
140:
141: if (nbr->dr_prio != opts.dr_prio) {
142: /* New DR priority for neighbor, restart DR election */
143: cache_nbr_settings(nbr, &opts);
144: goto election;
145: }
146:
147: cache_nbr_settings(nbr, &opts);
148: return TRUE;
149: }
150:
151: /* No entry for this neighbor. Exit loop to create an entry for it. */
152: break;
153: }
154:
155: /*
156: * This is a new neighbor. Create a new entry for it.
157: * It must be added right after `prev_nbr`
158: */
159: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
160: logit(LOG_INFO, 0, "Received PIM HELLO from new neighbor %s", inet_fmt(src, s1, sizeof(s1)));
161:
162: new_nbr = calloc(1, sizeof(pim_nbr_entry_t));
163: if (!new_nbr)
164: logit(LOG_ERR, 0, "Ran out of memory in receive_pim_hello()");
165:
166: new_nbr->address = src;
167: new_nbr->vifi = vifi;
168: new_nbr->build_jp_message = NULL;
169: new_nbr->next = nbr;
170: new_nbr->prev = prev_nbr;
171:
172: /* Add PIM Hello options */
173: cache_nbr_settings(new_nbr, &opts);
174:
175: /* Add to linked list of neighbors */
176: if (prev_nbr)
177: prev_nbr->next = new_nbr;
178: else
179: v->uv_pim_neighbors = new_nbr;
180:
181: if (new_nbr->next)
182: new_nbr->next->prev = new_nbr;
183:
184: v->uv_flags &= ~VIFF_NONBRS;
185: v->uv_flags |= VIFF_PIM_NBR;
186:
187: /* Since a new neighbour has come up, let it know your existence */
188: /* XXX: TODO: not in the spec,
189: * but probably should send the message after a short random period?
190: */
191: send_pim_hello(v, pim_timer_hello_holdtime);
192:
193: rebooted:
194: if (v->uv_flags & VIFF_DR) {
195: /*
196: * If I am the current DR on that interface, so
197: * send an RP-Set message to the new neighbor.
198: */
199: if ((bsr_length = create_pim_bootstrap_message(pim_send_buf)))
200: send_pim_unicast(pim_send_buf, v->uv_mtu, v->uv_lcl_addr, src, PIM_BOOTSTRAP, bsr_length);
201: }
202:
203: election:
204: if (restart_dr_election(v)) {
205: /* I was the DR, but not anymore. Remove all register_vif from
206: * oif list for all directly connected sources (for vifi). */
207:
208: /* TODO: XXX: first entry is not used! */
209: for (srcentry = srclist->next; srcentry; srcentry = srcentry->next) {
210: /* If not directly connected source for vifi */
211: if ((srcentry->incoming != vifi) || srcentry->upstream)
212: continue;
213:
214: for (mrtentry = srcentry->mrtlink; mrtentry; mrtentry = mrtentry->srcnext) {
215:
216: if (!(mrtentry->flags & MRTF_SG))
217: continue; /* This is not (S,G) entry */
218:
219: /* Remove the register oif */
220: VIFM_CLR(reg_vif_num, mrtentry->joined_oifs);
221: change_interfaces(mrtentry,
222: mrtentry->incoming,
223: mrtentry->joined_oifs,
224: mrtentry->pruned_oifs,
225: mrtentry->leaves,
226: mrtentry->asserted_oifs, 0);
227: }
228: }
229: }
230:
231: /*
232: * TODO: XXX: does a new neighbor change any routing entries info?
233: * Need to trigger joins?
234: */
235:
236: IF_DEBUG(DEBUG_PIM_HELLO)
237: dump_vifs(stderr); /* Show we got a new neighbor */
238:
239: return TRUE;
240: }
241:
242:
243: void delete_pim_nbr(pim_nbr_entry_t *nbr_delete)
244: {
245: srcentry_t *src;
246: srcentry_t *src_next;
247: mrtentry_t *mrt;
248: mrtentry_t *mrt_srcs;
249: grpentry_t *grp;
250: cand_rp_t *cand_rp;
251: rp_grp_entry_t *rp_grp;
252: rpentry_t *rp;
253: struct uvif *v;
254:
255: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
256: logit(LOG_INFO, 0, "Deleting PIM neighbor %s", inet_fmt(nbr_delete->address, s1, sizeof(s1)));
257:
258: v = &uvifs[nbr_delete->vifi];
259:
260: /* Delete the entry from the pim_nbrs chain */
261: if (nbr_delete->prev)
262: nbr_delete->prev->next = nbr_delete->next;
263: else
264: v->uv_pim_neighbors = nbr_delete->next;
265:
266: if (nbr_delete->next)
267: nbr_delete->next->prev = nbr_delete->prev;
268:
269: return_jp_working_buff(nbr_delete);
270:
271: /* That neighbor could've been the DR */
272: restart_dr_election(v);
273:
274: /* Update the source entries */
275: for (src = srclist; src; src = src_next) {
276: src_next = src->next;
277:
278: if (src->upstream != nbr_delete)
279: continue;
280:
281: /* Reset the next hop (PIM) router */
282: if (set_incoming(src, PIM_IIF_SOURCE) == FALSE) {
283: /* Coudn't reset it. Sorry, the hext hop router toward that
284: * source is probably not a PIM router, or cannot find route
285: * at all, hence I cannot handle this source and have to
286: * delete it.
287: */
288: logit(LOG_WARNING, 0, "Delete source entry for source %s", inet_fmt(src->address, s1, sizeof(s1)));
289: delete_srcentry(src);
290: } else if (src->upstream) {
291: /* Ignore the local or directly connected sources */
292: /* Browse all MRT entries for this source and reset the
293: * upstream router. Note that the upstream router is not always
294: * toward the source: it could be toward the RP for example.
295: */
296: for (mrt = src->mrtlink; mrt; mrt = mrt->srcnext) {
297: if (!(mrt->flags & MRTF_RP)) {
298: mrt->upstream = src->upstream;
299: mrt->metric = src->metric;
300: mrt->preference = src->preference;
301: change_interfaces(mrt, src->incoming,
302: mrt->joined_oifs,
303: mrt->pruned_oifs,
304: mrt->leaves,
305: mrt->asserted_oifs, 0);
306: }
307: }
308: }
309: }
310:
311: /* Update the RP entries */
312: for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
313: if (cand_rp->rpentry->upstream != nbr_delete)
314: continue;
315:
316: rp = cand_rp->rpentry;
317:
318: /* Reset the RP entry iif
319: * TODO: check if error setting the iif! */
320: if (local_address(rp->address) == NO_VIF) {
321: set_incoming(rp, PIM_IIF_RP);
322: } else {
323: rp->incoming = reg_vif_num;
324: rp->upstream = NULL;
325: }
326:
327: mrt = rp->mrtlink;
328: if (mrt) {
329: mrt->upstream = rp->upstream;
330: mrt->metric = rp->metric;
331: mrt->preference = rp->preference;
332: change_interfaces(mrt,
333: rp->incoming,
334: mrt->joined_oifs,
335: mrt->pruned_oifs,
336: mrt->leaves,
337: mrt->asserted_oifs, 0);
338: }
339:
340: /* Update the group entries for this RP */
341: for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
342: for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
343:
344: mrt = grp->grp_route;
345: if (mrt) {
346: mrt->upstream = rp->upstream;
347: mrt->metric = rp->metric;
348: mrt->preference = rp->preference;
349: change_interfaces(mrt,
350: rp->incoming,
351: mrt->joined_oifs,
352: mrt->pruned_oifs,
353: mrt->leaves,
354: mrt->asserted_oifs, 0);
355: }
356:
357: /* Update only the (S,G)RPbit entries for this group */
358: for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
359: if (mrt_srcs->flags & MRTF_RP) {
360: mrt_srcs->upstream = rp->upstream;
361: mrt_srcs->metric = rp->metric;
362: mrt_srcs->preference = rp->preference;
363: change_interfaces(mrt_srcs,
364: rp->incoming,
365: mrt_srcs->joined_oifs,
366: mrt_srcs->pruned_oifs,
367: mrt_srcs->leaves,
368: mrt_srcs->asserted_oifs, 0);
369: }
370: }
371: }
372: }
373: }
374:
375: /* Fix GitHub issue #22: Crash in (S,G) state when neighbor is lost */
376: for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
377: for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
378: for (grp = rp_grp->grplink; grp; grp = grp->next) {
379: mrt = grp->grp_route;
380: if (mrt && mrt->upstream) {
381: if (mrt->upstream == nbr_delete)
382: mrt->upstream = NULL;
383: }
384: }
385: }
386: }
387:
388: free(nbr_delete);
389: }
390:
391: /*
392: * If all PIM routers on a network segment support DR-Priority we use
393: * that to elect the DR, and use the highest IP address as the tie
394: * breaker. If any routers does *not* support DR-Priority all routers
395: * must use the IP address to elect the DR, this for backwards compat.
396: *
397: * Returns TRUE if we lost the DR role, elected another router.
398: */
399: static int restart_dr_election(struct uvif *v)
400: {
401: int was_dr = 0, use_dr_prio = 1;
402: uint32_t best_dr_prio = 0;
403: pim_nbr_entry_t *nbr;
404:
405: if (v->uv_flags & VIFF_DR)
406: was_dr = 1;
407:
408: if (!v->uv_pim_neighbors) {
409: /* This was our last neighbor, now we're it. */
410: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
411: logit(LOG_DEBUG, 0, "All neighbor PIM routers on %s lost, we are the DR now.",
412: inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
413:
414: v->uv_flags &= ~VIFF_PIM_NBR;
415: v->uv_flags |= (VIFF_NONBRS | VIFF_DR);
416:
417: return FALSE;
418: }
419:
420: /* Check if all routers on segment advertise DR Priority option
421: * in their PIM Hello messages. Figure out highest prio. */
422: for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) {
423: if (!nbr->dr_prio_present) {
424: use_dr_prio = 0;
425: break;
426: }
427:
428: if (nbr->dr_prio > best_dr_prio)
429: best_dr_prio = nbr->dr_prio;
430: }
431:
432: /*
433: * RFC4601 sec. 4.3.2
434: */
435: if (use_dr_prio) {
436: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
437: logit(LOG_DEBUG, 0, "All routers in %s segment support DR Priority based DR election.",
438: inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
439:
440: if (best_dr_prio < v->uv_dr_prio) {
441: v->uv_flags |= VIFF_DR;
442: return FALSE;
443: }
444:
445: if (best_dr_prio == v->uv_dr_prio)
446: goto tiebreak;
447: } else {
448: tiebreak:
449: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
450: logit(LOG_DEBUG, 0, "Using fallback DR election on %s.",
451: inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
452:
453: if (ntohl(v->uv_lcl_addr) > ntohl(v->uv_pim_neighbors->address)) {
454: /* The first address is the new potential remote
455: * DR address, but the local address is the winner. */
456: v->uv_flags |= VIFF_DR;
457: return FALSE;
458: }
459: }
460:
461: if (was_dr) {
462: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
463: logit(LOG_INFO, 0, "We lost DR role on %s in election.",
464: inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
465:
466: v->uv_flags &= ~VIFF_DR;
467: return TRUE; /* Lost election, clean up. */
468: }
469:
470: return FALSE;
471: }
472:
473: static int validate_pim_opt(uint32_t src, char *str, uint16_t len, uint16_t opt_len)
474: {
475: if (len != opt_len) {
476: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
477: logit(LOG_DEBUG, 0, "PIM HELLO %s from %s: invalid OptionLength = %u",
478: str, inet_fmt(src, s1, sizeof(s1)), opt_len);
479:
480: return FALSE;
481: }
482:
483: return TRUE;
484: }
485:
486: static int parse_pim_hello(char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts)
487: {
488: int result = FALSE;
489: size_t rec_len;
490: uint8_t *data;
491: uint16_t opt_type;
492: uint16_t opt_len;
493:
494: /* Assume no opts. */
495: memset(opts, 0, sizeof(*opts));
496:
497: /* Body of PIM message */
498: msg += sizeof(pim_header_t);
499:
500: /* Ignore any data if shorter than (pim_hello header) */
501: for (len -= sizeof(pim_header_t); len >= sizeof(pim_hello_t); len -= rec_len) {
502: data = (uint8_t *)msg;
503: GET_HOSTSHORT(opt_type, data);
504: GET_HOSTSHORT(opt_len, data);
505:
506: switch (opt_type) {
507: case PIM_HELLO_HOLDTIME:
508: result = validate_pim_opt(src, "Holdtime", PIM_HELLO_HOLDTIME_LEN, opt_len);
509: if (TRUE == result)
510: GET_HOSTSHORT(opts->holdtime, data);
511: break;
512:
513: case PIM_HELLO_DR_PRIO:
514: result = validate_pim_opt(src, "DR Priority", PIM_HELLO_DR_PRIO_LEN, opt_len);
515: if (TRUE == result) {
516: opts->dr_prio_present = 1;
517: GET_HOSTLONG(opts->dr_prio, data);
518: }
519: break;
520:
521: case PIM_HELLO_GENID:
522: result = validate_pim_opt(src, "GenID", PIM_HELLO_GENID_LEN, opt_len);
523: if (TRUE == result)
524: GET_HOSTLONG(opts->genid, data);
525: break;
526:
527: default:
528: break; /* Ignore any unknown options */
529: }
530:
531: /* Move to the next option */
532: rec_len = (sizeof(pim_hello_t) + opt_len);
533: if (len < rec_len || result == FALSE)
534: return FALSE;
535:
536: msg += rec_len;
537: }
538:
539: return result;
540: }
541:
542: static void cache_nbr_settings(pim_nbr_entry_t *nbr, pim_hello_opts_t *opts)
543: {
544: SET_TIMER(nbr->timer, opts->holdtime);
545: nbr->genid = opts->genid;
546: nbr->dr_prio = opts->dr_prio;
547: nbr->dr_prio_present = opts->dr_prio_present;
548: }
549:
550: int send_pim_hello(struct uvif *v, uint16_t holdtime)
551: {
552: char *buf;
553: uint8_t *data;
554: size_t len;
555:
556: buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
557: data = (uint8_t *)buf;
558: PUT_HOSTSHORT(PIM_HELLO_HOLDTIME, data);
559: PUT_HOSTSHORT(PIM_HELLO_HOLDTIME_LEN, data);
560: PUT_HOSTSHORT(holdtime, data);
561:
562: PUT_HOSTSHORT(PIM_HELLO_DR_PRIO, data);
563: PUT_HOSTSHORT(PIM_HELLO_DR_PRIO_LEN, data);
564: PUT_HOSTLONG(v->uv_dr_prio, data);
565:
566: #ifdef ENABLE_PIM_HELLO_GENID
567: PUT_HOSTSHORT(PIM_HELLO_GENID, data);
568: PUT_HOSTSHORT(PIM_HELLO_GENID_LEN, data);
569: PUT_HOSTLONG(v->uv_genid, data);
570: #endif
571:
572: len = data - (uint8_t *)buf;
573: send_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO, len);
574: SET_TIMER(v->uv_hello_timer, pim_timer_hello_interval);
575:
576: return TRUE;
577: }
578:
579:
580: /************************************************************************
581: * PIM_REGISTER
582: ************************************************************************/
583: /* TODO: XXX: IF THE BORDER BIT IS SET, THEN
584: * FORWARD THE WHOLE PACKET FROM USER SPACE
585: * AND AT THE SAME TIME IGNORE ANY CACHE_MISS
586: * SIGNALS FROM THE KERNEL.
587: */
588: int receive_pim_register(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len)
589: {
590: uint32_t inner_src, inner_grp;
591: pim_register_t *reg;
592: struct ip *ip;
593: uint32_t is_border, is_null;
594: mrtentry_t *mrtentry;
595: mrtentry_t *mrtentry2;
596: vifbitmap_t oifs;
597:
598: /*
599: * If instance specific multicast routing table is in use, check
600: * that we are the target of the register packet. Otherwise we
601: * might end up responding to register packet belonging to another
602: * pimd instance. If we are not an RP candidate, we shouldn't have
603: * pimreg interface and shouldn't receive register packets, but we'll
604: * check the cand_rp flag anyway, just to be on the safe side.
605: */
606: if (mrt_table_id != 0) {
607: if (!cand_rp_flag || my_cand_rp_address != reg_dst) {
608: IF_DEBUG(DEBUG_PIM_REGISTER)
609: logit(LOG_DEBUG, 0, "PIM register: packet from %s to %s is not destined for us",
610: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)));
611:
612: return FALSE;
613: }
614: }
615:
616: IF_DEBUG(DEBUG_PIM_REGISTER)
617: logit(LOG_INFO, 0, "Received PIM register: len = %d from %s",
618: len, inet_fmt(reg_src, s1, sizeof(s1)));
619:
620: /*
621: * Message length validation.
622: * This is suppose to be done in the kernel, but some older kernel
623: * versions do not pefrorm the check for the NULL register messages.
624: */
625: if (len < sizeof(pim_header_t) + sizeof(pim_register_t) + sizeof(struct ip)) {
626: IF_DEBUG(DEBUG_PIM_REGISTER)
627: logit(LOG_INFO, 0, "PIM register: short packet (len = %d) from %s",
628: len, inet_fmt(reg_src, s1, sizeof(s1)));
629:
630: return FALSE;
631: }
632:
633: /*
634: * XXX: For PIM_REGISTER the checksum does not include
635: * the inner IP packet. However, some older routers might
636: * create the checksum over the whole packet. Hence,
637: * verify the checksum over the first 8 bytes, and if fails,
638: * then over the whole Register
639: */
640: if ((inet_cksum((uint16_t *)msg, sizeof(pim_header_t) + sizeof(pim_register_t)))
641: && (inet_cksum((uint16_t *)msg, len))) {
642: IF_DEBUG(DEBUG_PIM_REGISTER)
643: logit(LOG_DEBUG, 0, "PIM REGISTER from DR %s: invalid PIM header checksum",
644: inet_fmt(reg_src, s1, sizeof(s1)));
645:
646: return FALSE;
647: }
648:
649: /* Lookup register message flags */
650: reg = (pim_register_t *)(msg + sizeof(pim_header_t));
651: is_border = ntohl(reg->reg_flags) & PIM_REGISTER_BORDER_BIT;
652: is_null = ntohl(reg->reg_flags) & PIM_REGISTER_NULL_REGISTER_BIT;
653:
654: /* initialize the pointer to the encapsulated packet */
655: ip = (struct ip *)(msg + sizeof(pim_header_t) + sizeof(pim_register_t));
656:
657: /* check the IP version (especially for the NULL register...see above) */
658: if (ip->ip_v != IPVERSION && (! is_null)) {
659: IF_DEBUG(DEBUG_PIM_REGISTER)
660: logit(LOG_INFO, 0, "PIM register: incorrect IP version (%d) of the inner packet from %s",
661: ip->ip_v, inet_fmt(reg_src, s1, sizeof(s1)));
662:
663: return FALSE;
664: }
665:
666: /* We are keeping all addresses in network order, so no need for ntohl()*/
667: inner_src = ip->ip_src.s_addr;
668: inner_grp = ip->ip_dst.s_addr;
669:
670: /*
671: * inner_src and inner_grp must be valid IP unicast and multicast address
672: * respectively. XXX: not in the spec.
673: * PIM-SSM support: inner_grp must not be in PIM-SSM range
674: */
675: if ((!inet_valid_host(inner_src)) || (!IN_MULTICAST(ntohl(inner_grp))) || IN_PIM_SSM_RANGE(inner_grp)) {
676: if (!inet_valid_host(inner_src)) {
677: logit(LOG_WARNING, 0, "Inner source address of register message by %s is invalid: %s",
678: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
679: }
680: if (!IN_MULTICAST(ntohl(inner_grp))) {
681: logit(LOG_WARNING, 0, "Inner group address of register message by %s is invalid: %s",
682: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
683: }
684: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
685:
686: return FALSE;
687: }
688:
689: mrtentry = find_route(inner_src, inner_grp, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
690: if (!mrtentry) {
691: /* No routing entry. Send REGISTER_STOP and return. */
692: IF_DEBUG(DEBUG_PIM_REGISTER)
693: logit(LOG_DEBUG, 0, "No routing entry for source %s and/or group %s" ,
694: inet_fmt(inner_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
695:
696: /* TODO: XXX: shouldn't it be inner_src=INADDR_ANY? Not in the spec. */
697: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
698:
699: return TRUE;
700: }
701:
702: /* Check if I am the RP for that group */
703: if ((local_address(reg_dst) == NO_VIF) || !check_mrtentry_rp(mrtentry, reg_dst)) {
704: IF_DEBUG(DEBUG_PIM_REGISTER)
705: logit(LOG_DEBUG, 0, "Not RP in address %s", inet_fmt(reg_dst, s1, sizeof(s1)));
706:
707: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
708:
709: return TRUE;
710: }
711:
712: /* I am the RP */
713:
714: if (mrtentry->flags & MRTF_SG) {
715: /* (S,G) found */
716: /* TODO: check the timer again */
717: SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT); /* restart timer */
718: if (!(mrtentry->flags & MRTF_SPT)) { /* The SPT bit is not set */
719: if (!is_null) {
720: calc_oifs(mrtentry, &oifs);
721: if (VIFM_ISEMPTY(oifs) && (mrtentry->incoming == reg_vif_num)) {
722: IF_DEBUG(DEBUG_PIM_REGISTER)
723: logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s",
724: inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
725:
726: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
727:
728: return TRUE;
729: }
730:
731: /*
732: * TODO: XXX: BUG!!!
733: * The data will be forwarded by the kernel MFC!!!
734: * Need to set a special flag for this routing entry so after
735: * a cache miss occur, the multicast packet will be forwarded
736: * from user space and won't install entry in the kernel MFC.
737: * The problem is that the kernel MFC doesn't know the
738: * PMBR address and simply sets the multicast forwarding
739: * cache to accept/forward all data coming from the
740: * register_vif.
741: */
742: if (is_border) {
743: if (mrtentry->pmbr_addr != reg_src) {
744: IF_DEBUG(DEBUG_PIM_REGISTER)
745: logit(LOG_DEBUG, 0, "pmbr_addr (%s) != reg_src (%s)",
746: inet_fmt(mrtentry->pmbr_addr, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2)));
747:
748: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
749:
750: return TRUE;
751: }
752: }
753:
754: return TRUE;
755: }
756:
757: /* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..?*/
758: return TRUE;
759: }
760: else {
761: /* The SPT bit is set */
762: IF_DEBUG(DEBUG_PIM_REGISTER)
763: logit(LOG_DEBUG, 0, "SPT bit is set for group %s source %s",
764: inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
765:
766: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
767:
768: return TRUE;
769: }
770: }
771: if (mrtentry->flags & (MRTF_WC | MRTF_PMBR)) {
772: if (is_border) {
773: /* Create (S,G) state. The oifs will be the copied from the
774: * existing (*,G) or (*,*,RP) entry. */
775: mrtentry2 = find_route(inner_src, inner_grp, MRTF_SG, CREATE);
776: if (mrtentry2) {
777: mrtentry2->pmbr_addr = reg_src;
778: /* Clear the SPT flag */
779: mrtentry2->flags &= ~(MRTF_SPT | MRTF_NEW);
780: SET_TIMER(mrtentry2->timer, PIM_DATA_TIMEOUT);
781: /* TODO: explicitly call the Join/Prune send function? */
782: FIRE_TIMER(mrtentry2->jp_timer); /* Send the Join immediately */
783: /* TODO: explicitly call this function?
784: send_pim_join_prune(mrtentry2->upstream->vifi,
785: mrtentry2->upstream,
786: PIM_JOIN_PRUNE_HOLDTIME);
787: */
788: }
789: }
790: }
791:
792: if (mrtentry->flags & MRTF_WC) {
793:
794: /* First PIM Register for this routing entry, log it */
795: logit(LOG_INFO, 0, "Received PIM REGISTER: src %s, group %s",
796: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
797:
798: /* (*,G) entry */
799: calc_oifs(mrtentry, &oifs);
800: if (VIFM_ISEMPTY(oifs)) {
801: IF_DEBUG(DEBUG_PIM_REGISTER)
802: logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s (*,G)",
803: inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
804:
805: send_pim_register_stop(reg_dst, reg_src, inner_grp, INADDR_ANY_N);
806:
807: return FALSE;
808: } else { /* XXX: TODO: check with the spec again */
809: if (!is_null) {
810: uint32_t mfc_source = inner_src;
811:
812: /* Install cache entry in the kernel */
813: /* TODO: XXX: probably redundant here, because the
814: * decapsulated mcast packet in the kernel will
815: * result in CACHE_MISS
816: */
817: #ifdef KERNEL_MFC_WC_G
818: if (!(mrtentry->flags & MRTF_MFC_CLONE_SG))
819: mfc_source = INADDR_ANY_N;
820: #endif /* KERNEL_MFC_WC_G */
821: add_kernel_cache(mrtentry, mfc_source, inner_grp, 0);
822: k_chg_mfc(igmp_socket, mfc_source, inner_grp,
823: mrtentry->incoming, mrtentry->oifs,
824: mrtentry->group->rpaddr);
825:
826: return TRUE;
827: }
828: }
829:
830: return TRUE;
831: }
832:
833: if (mrtentry->flags & MRTF_PMBR) {
834: /* (*,*,RP) entry */
835: if (!is_null) {
836: uint32_t mfc_source = inner_src;
837:
838: /* XXX: have to create either (S,G) or (*,G).
839: * The choice below is (*,G)
840: */
841: mrtentry2 = find_route(INADDR_ANY_N, inner_grp, MRTF_WC, CREATE);
842: if (!mrtentry2)
843: return FALSE;
844:
845: if (mrtentry2->flags & MRTF_NEW) {
846: /* TODO: something else? Have the feeling sth is missing */
847: mrtentry2->flags &= ~MRTF_NEW;
848: /* TODO: XXX: copy the timer from the (*,*,RP) entry? */
849: COPY_TIMER(mrtentry->timer, mrtentry2->timer);
850: }
851:
852: /* Install cache entry in the kernel */
853: #ifdef KERNEL_MFC_WC_G
854: if (!(mrtentry->flags & MRTF_MFC_CLONE_SG))
855: mfc_source = INADDR_ANY_N;
856: #endif /* KERNEL_MFC_WC_G */
857: add_kernel_cache(mrtentry, mfc_source, inner_grp, 0);
858: k_chg_mfc(igmp_socket, mfc_source, inner_grp,
859: mrtentry->incoming, mrtentry->oifs,
860: mrtentry2->group->rpaddr);
861:
862: return TRUE;
863: }
864: }
865:
866: /* Shoudn't happen: invalid routing entry? */
867: /* XXX: TODO: shoudn't be inner_src=INADDR_ANY? Not in the spec. */
868: IF_DEBUG(DEBUG_PIM_REGISTER)
869: logit(LOG_DEBUG, 0, "Shoudn't happen: invalid routing entry? (%s, %s, %s, %s)",
870: inet_fmt(reg_dst, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2)),
871: inet_fmt(inner_grp, s3, sizeof(s3)), inet_fmt(inner_src, s4, sizeof(s4)));
872:
873: send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
874:
875: return TRUE;
876: }
877:
878:
879: int send_pim_register(char *packet)
880: {
881: struct ip *ip;
882: uint32_t source, group;
883: vifi_t vifi;
884: rpentry_t *rpentry;
885: mrtentry_t *mrtentry;
886: mrtentry_t *mrtentry2;
887: uint32_t reg_src, reg_dst;
888: int reg_mtu, pktlen = 0;
889: char *buf;
890:
891: ip = (struct ip *)packet;
892: source = ip->ip_src.s_addr;
893: group = ip->ip_dst.s_addr;
894:
895: if (IN_PIM_SSM_RANGE(group))
896: return FALSE; /* Group is in PIM-SSM range, don't send register. */
897:
898: if ((vifi = find_vif_direct_local(source)) == NO_VIF)
899: return FALSE;
900:
901: if (!(uvifs[vifi].uv_flags & VIFF_DR))
902: return FALSE; /* I am not the DR for that subnet */
903:
904: rpentry = rp_match(group);
905: if (!rpentry)
906: return FALSE; /* No RP for this group */
907:
908: if (local_address(rpentry->address) != NO_VIF) {
909: /* TODO: XXX: not sure it is working! */
910: return FALSE; /* I am the RP for this group */
911: }
912:
913: mrtentry = find_route(source, group, MRTF_SG, CREATE);
914: if (!mrtentry)
915: return FALSE; /* Cannot create (S,G) state */
916:
917: if (mrtentry->flags & MRTF_NEW) {
918: /* A new entry, log it */
919: reg_src = uvifs[vifi].uv_lcl_addr;
920: reg_dst = mrtentry->group->rpaddr;
921:
922: logit(LOG_INFO, 0, "Send PIM REGISTER: src %s dst %s, group %s",
923: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
924: inet_fmt(group, s3, sizeof(s3)));
925:
926: mrtentry->flags &= ~MRTF_NEW;
927: RESET_TIMER(mrtentry->rs_timer); /* Reset the Register-Suppression timer */
928: mrtentry2 = mrtentry->group->grp_route;
929: if (!mrtentry2)
930: mrtentry2 = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink;
931: if (mrtentry2) {
932: FIRE_TIMER(mrtentry2->jp_timer); /* Timeout the Join/Prune timer */
933: /* TODO: explicitly call this function?
934: send_pim_join_prune(mrtentry2->upstream->vifi,
935: mrtentry2->upstream,
936: PIM_JOIN_PRUNE_HOLDTIME);
937: */
938: }
939: }
940: /* Restart the (S,G) Entry-timer */
941: SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT);
942:
943: IF_TIMER_NOT_SET(mrtentry->rs_timer) {
944: /* The Register-Suppression Timer is not running.
945: * Encapsulate the data and send to the RP.
946: */
947: buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
948: memset(buf, 0, sizeof(pim_register_t)); /* No flags set */
949: buf += sizeof(pim_register_t);
950:
951: /* Copy the data packet at the back of the register packet */
952: pktlen = ntohs(ip->ip_len);
953: memcpy(buf, ip, pktlen);
954:
955: pktlen += sizeof(pim_register_t); /* 'sizeof(struct ip) + sizeof(pim_header_t)' added by send_pim() */
956: reg_mtu = uvifs[vifi].uv_mtu; /* XXX: Use PMTU to RP instead! */
957: reg_src = uvifs[vifi].uv_lcl_addr;
958: reg_dst = mrtentry->group->rpaddr;
959:
960: send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen);
961:
962: return TRUE;
963: }
964:
965: return TRUE;
966: }
967:
968:
969: int send_pim_null_register(mrtentry_t *mrtentry)
970: {
971: struct ip *ip;
972: pim_register_t *pim_register;
973: int reg_mtu, pktlen;
974: vifi_t vifi;
975: uint32_t reg_src, reg_dst;
976:
977: /* No directly connected source; no local address */
978: if ((vifi = find_vif_direct_local(mrtentry->source->address))== NO_VIF)
979: return FALSE;
980:
981: pim_register = (pim_register_t *)(pim_send_buf + sizeof(struct ip) +
982: sizeof(pim_header_t));
983: memset(pim_register, 0, sizeof(pim_register_t));
984: pim_register->reg_flags = htonl(pim_register->reg_flags
985: | PIM_REGISTER_NULL_REGISTER_BIT);
986:
987: ip = (struct ip *)(pim_register + 1);
988: /* set src/dst in dummy hdr */
989: ip->ip_v = IPVERSION;
990: ip->ip_hl = (sizeof(struct ip) >> 2);
991: ip->ip_tos = 0;
992: ip->ip_id = 0;
993: ip->ip_off = 0;
994: ip->ip_p = IPPROTO_UDP; /* XXX: bogus */
995: ip->ip_len = htons(sizeof(struct ip));
996: ip->ip_ttl = MINTTL; /* TODO: XXX: check whether need to setup the ttl */
997: ip->ip_src.s_addr = mrtentry->source->address;
998: ip->ip_dst.s_addr = mrtentry->group->group;
999: ip->ip_sum = 0;
1000: ip->ip_sum = inet_cksum((uint16_t *)ip, sizeof(struct ip));
1001:
1002: /* include the dummy ip header */
1003: pktlen = sizeof(pim_register_t) + sizeof(struct ip);
1004:
1005: reg_mtu = uvifs[vifi].uv_mtu;
1006: reg_dst = mrtentry->group->rpaddr;
1007: reg_src = uvifs[vifi].uv_lcl_addr;
1008:
1009: send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen);
1010:
1011: return TRUE;
1012: }
1013:
1014:
1015: /************************************************************************
1016: * PIM_REGISTER_STOP
1017: ************************************************************************/
1018: int receive_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len)
1019: {
1020: pim_encod_grp_addr_t egaddr;
1021: pim_encod_uni_addr_t eusaddr;
1022: uint8_t *data;
1023: mrtentry_t *mrtentry;
1024: vifbitmap_t pruned_oifs;
1025:
1026: /* Checksum */
1027: if (inet_cksum((uint16_t *)msg, len))
1028: return FALSE;
1029:
1030: data = (uint8_t *)(msg + sizeof(pim_header_t));
1031: GET_EGADDR(&egaddr, data);
1032: GET_EUADDR(&eusaddr, data);
1033:
1034: logit(LOG_INFO, 0, "Received PIM_REGISTER_STOP from RP %s to %s for src = %s and group = %s",
1035: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
1036: inet_fmt(eusaddr.unicast_addr, s3, sizeof(s3)),
1037: inet_fmt(egaddr.mcast_addr, s4, sizeof(s4)));
1038:
1039: /* TODO: apply the group mask and do register_stop for all grp addresses */
1040: /* TODO: check for SourceAddress == 0 */
1041: mrtentry = find_route(eusaddr.unicast_addr, egaddr.mcast_addr, MRTF_SG, DONT_CREATE);
1042: if (!mrtentry)
1043: return FALSE;
1044:
1045: /* XXX: not in the spec: check if the PIM_REGISTER_STOP originator is
1046: * really the RP
1047: */
1048: if (check_mrtentry_rp(mrtentry, reg_src) == FALSE)
1049: return FALSE;
1050:
1051: /* restart the Register-Suppression timer */
1052: SET_TIMER(mrtentry->rs_timer, (0.5 * PIM_REGISTER_SUPPRESSION_TIMEOUT)
1053: + (RANDOM() % (PIM_REGISTER_SUPPRESSION_TIMEOUT + 1)));
1054: /* Prune the register_vif from the outgoing list */
1055: VIFM_COPY(mrtentry->pruned_oifs, pruned_oifs);
1056: VIFM_SET(reg_vif_num, pruned_oifs);
1057: change_interfaces(mrtentry, mrtentry->incoming,
1058: mrtentry->joined_oifs, pruned_oifs,
1059: mrtentry->leaves,
1060: mrtentry->asserted_oifs, 0);
1061:
1062: return TRUE;
1063: }
1064:
1065:
1066: /* TODO: optional rate limiting is not implemented yet */
1067: /* Unicasts a REGISTER_STOP message to the DR */
1068: static int
1069: send_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_src)
1070: {
1071: char *buf;
1072: uint8_t *data;
1073:
1074: if (IN_PIM_SSM_RANGE(inner_grp))
1075: return TRUE;
1076:
1077: logit(LOG_INFO, 0, "Send PIM REGISTER STOP from %s to router %s for src = %s and group = %s",
1078: inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
1079: inet_fmt(inner_src, s3, sizeof(s3)), inet_fmt(inner_grp, s4, sizeof(s4)));
1080:
1081: buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
1082: data = (uint8_t *)buf;
1083: PUT_EGADDR(inner_grp, SINGLE_GRP_MSKLEN, 0, data);
1084: PUT_EUADDR(inner_src, data);
1085: send_pim_unicast(pim_send_buf, 0, reg_src, reg_dst, PIM_REGISTER_STOP, data - (uint8_t *)buf);
1086:
1087: return TRUE;
1088: }
1089:
1090:
1091: /************************************************************************
1092: * PIM_JOIN_PRUNE
1093: ************************************************************************/
1094: int join_or_prune(mrtentry_t *mrtentry, pim_nbr_entry_t *upstream_router)
1095: {
1096: vifbitmap_t entry_oifs;
1097: mrtentry_t *mrtentry_grp;
1098:
1099: if (!mrtentry || !upstream_router)
1100: return PIM_ACTION_NOTHING;
1101:
1102: calc_oifs(mrtentry, &entry_oifs);
1103: if (mrtentry->flags & (MRTF_PMBR | MRTF_WC)) {
1104: if (IN_PIM_SSM_RANGE(mrtentry->group->group)) {
1105: logit(LOG_DEBUG, 0, "No action for SSM (PMBR|WC)");
1106: return PIM_ACTION_NOTHING;
1107: }
1108: /* (*,*,RP) or (*,G) entry */
1109: /* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */
1110: if (upstream_router != mrtentry->upstream)
1111: return PIM_ACTION_NOTHING;
1112:
1113: /* TODO: XXX: Can we have (*,*,RP) prune? */
1114: if (VIFM_ISEMPTY(entry_oifs)) {
1115: /* NULL oifs */
1116:
1117: if (!(uvifs[mrtentry->incoming].uv_flags & VIFF_DR))
1118: /* I am not the DR for that subnet. */
1119: return PIM_ACTION_PRUNE;
1120:
1121: if (VIFM_ISSET(mrtentry->incoming, mrtentry->leaves))
1122: /* I am the DR and have local leaves */
1123: return PIM_ACTION_JOIN;
1124:
1125: /* Probably the last local member hast timeout */
1126: return PIM_ACTION_PRUNE;
1127: }
1128:
1129: return PIM_ACTION_JOIN;
1130: }
1131:
1132: if (mrtentry->flags & MRTF_SG) {
1133: /* (S,G) entry */
1134: /* TODO: check again */
1135: if (mrtentry->upstream == upstream_router) {
1136: if (!(mrtentry->flags & MRTF_RP)) {
1137: /* Upstream router toward S */
1138: if (VIFM_ISEMPTY(entry_oifs)) {
1139: if (mrtentry->group->active_rp_grp &&
1140: mrtentry->group->rpaddr == my_cand_rp_address) {
1141: /* (S,G) at the RP. Don't send Join/Prune
1142: * (see the end of Section 3.3.2)
1143: */
1144: return PIM_ACTION_NOTHING;
1145: }
1146:
1147: return PIM_ACTION_PRUNE;
1148: }
1149: else {
1150: return PIM_ACTION_JOIN;
1151: }
1152: }
1153: else {
1154: if (IN_PIM_SSM_RANGE(mrtentry->group->group)) {
1155: logit(LOG_DEBUG, 0, "No action for SSM (RP)");
1156: return PIM_ACTION_NOTHING;
1157: }
1158: /* Upstream router toward RP */
1159: if (VIFM_ISEMPTY(entry_oifs))
1160: return PIM_ACTION_PRUNE;
1161: }
1162: }
1163:
1164: /* Looks like the case when the upstream router toward S is
1165: * different from the upstream router toward RP
1166: */
1167: if (!mrtentry->group->active_rp_grp)
1168: return PIM_ACTION_NOTHING;
1169:
1170: mrtentry_grp = mrtentry->group->grp_route;
1171: if (!mrtentry_grp) {
1172: mrtentry_grp = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink;
1173: if (!mrtentry_grp)
1174: return PIM_ACTION_NOTHING;
1175: }
1176:
1177: if (mrtentry_grp->upstream != upstream_router)
1178: return PIM_ACTION_NOTHING; /* XXX: shoudn't happen */
1179:
1180: if (!(mrtentry->flags & MRTF_RP) && (mrtentry->flags & MRTF_SPT))
1181: return PIM_ACTION_PRUNE;
1182: }
1183:
1184: return PIM_ACTION_NOTHING;
1185: }
1186:
1187: /*
1188: * Log PIM Join/Prune message. Send log event for every join and prune separately.
1189: * Format of the Join/Prune message starting from dataptr is following:
1190: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1191: | Multicast Group Address 1 (Encoded-Group format) |
1192: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1193: | Number of Joined Sources | Number of Pruned Sources |
1194: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1195: | Joined Source Address 1 (Encoded-Source format) |
1196: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1197: | . |
1198: | . |
1199: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1200: | Joined Source Address n (Encoded-Source format) |
1201: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1202: | Pruned Source Address 1 (Encoded-Source format) |
1203: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1204: | . |
1205: | . |
1206: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1207: | Pruned Source Address n (Encoded-Source format) |
1208: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1209: | Multicast Group Address m (Encoded-Group format) |
1210: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1211: | Number of Joined Sources | Number of Pruned Sources |
1212: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1213: | Joined Source Address 1 (Encoded-Source format) |
1214: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1215: | . |
1216: | . |
1217: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1218: | Joined Source Address n (Encoded-Source format) |
1219: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1220: | Pruned Source Address 1 (Encoded-Source format) |
1221: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1222: | . |
1223: | . |
1224: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1225: | Pruned Source Address n (Encoded-Source format) |
1226: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1227: */
1228: void log_pim_join_prune(uint32_t src, uint8_t *data_ptr, int num_groups, char* ifname)
1229: {
1230: pim_encod_grp_addr_t encod_group;
1231: pim_encod_src_addr_t encod_src;
1232: uint32_t group, source;
1233: uint16_t num_j_srcs;
1234: uint16_t num_p_srcs;
1235:
1236: /* Message validity check is done by caller */
1237: while (num_groups--) {
1238:
1239: GET_EGADDR(&encod_group, data_ptr);
1240: GET_HOSTSHORT(num_j_srcs, data_ptr);
1241: GET_HOSTSHORT(num_p_srcs, data_ptr);
1242: group = encod_group.mcast_addr;
1243:
1244: while(num_j_srcs--) {
1245: GET_ESADDR(&encod_src, data_ptr);
1246: source = encod_src.src_addr;
1247: logit(LOG_INFO, 0, "Received PIM JOIN from %s to group %s for multicast source %s on %s",
1248: inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname);
1249: }
1250:
1251: while (num_p_srcs--) {
1252: GET_ESADDR(&encod_src, data_ptr);
1253: source = encod_src.src_addr;
1254: logit(LOG_INFO, 0, "Received PIM PRUNE from %s to group %s for multicast source %s on %s",
1255: inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname);
1256: }
1257: }
1258: }
1259:
1260: /* TODO: when parsing, check if we go beyond message size */
1261: /* TODO: too long, simplify it! */
1262: #define PIM_JOIN_PRUNE_MINLEN (4 + PIM_ENCODE_UNI_ADDR_LEN + 4)
1263: int receive_pim_join_prune(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
1264: {
1265: vifi_t vifi;
1266: struct uvif *v;
1267: pim_encod_uni_addr_t eutaddr;
1268: pim_encod_grp_addr_t egaddr;
1269: pim_encod_src_addr_t esaddr;
1270: uint8_t *data;
1271: uint8_t *data_start;
1272: uint8_t *data_group_end;
1273: uint8_t num_groups;
1274: uint8_t num_groups_tmp;
1275: int star_star_rp_found;
1276: uint16_t holdtime;
1277: uint16_t num_j_srcs;
1278: uint16_t num_j_srcs_tmp;
1279: uint16_t num_p_srcs;
1280: uint32_t source;
1281: uint32_t group;
1282: uint32_t s_mask;
1283: uint32_t g_mask;
1284: uint8_t s_flags;
1285: uint8_t reserved __attribute__((unused));
1286: rpentry_t *rpentry;
1287: mrtentry_t *mrt;
1288: mrtentry_t *mrt_srcs;
1289: mrtentry_t *mrt_rp;
1290: grpentry_t *grp;
1291: uint16_t jp_value;
1292: pim_nbr_entry_t *upstream_router;
1293: int my_action;
1294: int ignore_group;
1295: rp_grp_entry_t *rp_grp;
1296: uint8_t *data_group_j_start;
1297: uint8_t *data_group_p_start;
1298:
1299: if ((vifi = find_vif_direct(src)) == NO_VIF) {
1300: /* Either a local vif or somehow received PIM_JOIN_PRUNE from
1301: * non-directly connected router. Ignore it.
1302: */
1303: if (local_address(src) == NO_VIF) {
1304: logit(LOG_DEBUG, 0, "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
1305: inet_fmt(src, s1, sizeof(s1)));
1306: }
1307:
1308: return FALSE;
1309: }
1310:
1311: /* Checksum */
1312: if (inet_cksum((uint16_t *)msg, len))
1313: return FALSE;
1314:
1315: v = &uvifs[vifi];
1316: if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER))
1317: return FALSE; /* Shoudn't come on this interface */
1318:
1319: /* sanity check for the minimum length */
1320: if (len < PIM_JOIN_PRUNE_MINLEN) {
1321: logit(LOG_NOTICE, 0, "%s(): Too short Join/Prune message (%u bytes) from %s on %s",
1322: __func__, len, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
1323:
1324: return FALSE;
1325: }
1326:
1327: len -= PIM_JOIN_PRUNE_MINLEN;
1328: data = (uint8_t *)(msg + sizeof(pim_header_t));
1329:
1330: /* Get the target address */
1331: GET_EUADDR(&eutaddr, data);
1332: GET_BYTE(reserved, data);
1333: GET_BYTE(num_groups, data);
1334: GET_HOSTSHORT(holdtime, data);
1335:
1336: if (num_groups == 0) {
1337: /* No indication for groups in the message */
1338: logit(LOG_NOTICE, 0, "%s(): No groups in Join/Prune message from %s on %s!",
1339: __func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
1340: return FALSE;
1341: }
1342:
1343: logit(LOG_INFO, 0, "Received PIM JOIN/PRUNE from %s on %s",
1344: inet_fmt(src, s1, sizeof(s1)), v->uv_name);
1345:
1346: /* Sanity check for the message length through all the groups */
1347: num_groups_tmp = num_groups;
1348: data_start = data;
1349: while (num_groups_tmp--) {
1350: size_t srclen;
1351:
1352: /* group addr + #join + #src */
1353: if (len < PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t)) {
1354: logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is"
1355: " too short to contain enough data",
1356: __func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
1357: return FALSE;
1358: }
1359:
1360: len -= (PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t));
1361: data += PIM_ENCODE_GRP_ADDR_LEN;
1362:
1363: /* joined source addresses and pruned source addresses */
1364: GET_HOSTSHORT(num_j_srcs, data);
1365: GET_HOSTSHORT(num_p_srcs, data);
1366: srclen = (num_j_srcs + num_p_srcs) * PIM_ENCODE_SRC_ADDR_LEN;
1367: if (len < srclen) {
1368: logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is"
1369: " too short to contain enough data", __func__,
1370: inet_fmt(src, s1, sizeof(s1)), v->uv_name);
1371: return FALSE;
1372: }
1373: len -= srclen;
1374: data += srclen;
1375: }
1376: data = data_start;
1377: num_groups_tmp = num_groups;
1378:
1379: /* Sanity check is done. Log the message */
1380: log_pim_join_prune(src, data, num_groups, v->uv_name);
1381:
1382: if (eutaddr.unicast_addr != v->uv_lcl_addr) {
1383: /* if I am not the target of the join message */
1384: /* Join/Prune suppression code. This either modifies the J/P timers
1385: * or triggers an overriding Join.
1386: */
1387: /* Note that if we have (S,G) prune and (*,G) Join, we must send
1388: * them in the same message. We don't bother to modify both timers
1389: * here. The Join/Prune sending function will take care of that.
1390: */
1391: upstream_router = find_pim_nbr(eutaddr.unicast_addr);
1392: if (!upstream_router)
1393: return FALSE; /* I have no such neighbor */
1394:
1395: while (num_groups--) {
1396: GET_EGADDR(&egaddr, data);
1397: GET_HOSTSHORT(num_j_srcs, data);
1398: GET_HOSTSHORT(num_p_srcs, data);
1399: MASKLEN_TO_MASK(egaddr.masklen, g_mask);
1400: group = egaddr.mcast_addr;
1401: if (!IN_MULTICAST(ntohl(group))) {
1402: data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1403: continue; /* Ignore this group and jump to the next */
1404: }
1405:
1406: if ((ntohl(group) == CLASSD_PREFIX) && (egaddr.masklen == STAR_STAR_RP_MSKLEN)) {
1407: /* (*,*,RP) Join suppression */
1408:
1409: while (num_j_srcs--) {
1410: GET_ESADDR(&esaddr, data);
1411: source = esaddr.src_addr;
1412: if (!inet_valid_host(source))
1413: continue;
1414:
1415: s_flags = esaddr.flags;
1416: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
1417: if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
1418: /* This is the RP address. */
1419: rpentry = rp_find(source);
1420: if (!rpentry)
1421: continue; /* Don't have such RP. Ignore */
1422:
1423: mrt_rp = rpentry->mrtlink;
1424: my_action = join_or_prune(mrt_rp, upstream_router);
1425: if (my_action != PIM_ACTION_JOIN)
1426: continue;
1427:
1428: /* Check the holdtime */
1429: /* TODO: XXX: TIMER implem. dependency! */
1430: if (mrt_rp->jp_timer > holdtime)
1431: continue;
1432:
1433: if ((mrt_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
1434: continue;
1435:
1436: /* Set the Join/Prune suppression timer for this
1437: * routing entry by increasing the current
1438: * Join/Prune timer.
1439: */
1440: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1441: /* TODO: XXX: TIMER implem. dependency! */
1442: if (mrt_rp->jp_timer < jp_value)
1443: SET_TIMER(mrt_rp->jp_timer, jp_value);
1444: }
1445: } /* num_j_srcs */
1446:
1447: while (num_p_srcs--) {
1448: /* TODO: XXX: Can we have (*,*,RP) prune message?
1449: * Not in the spec, but anyway, the code below
1450: * can handle them: either suppress
1451: * the local (*,*,RP) prunes or override the prunes by
1452: * sending (*,*,RP) and/or (*,G) and/or (S,G) Join.
1453: */
1454: GET_ESADDR(&esaddr, data);
1455: source = esaddr.src_addr;
1456: if (!inet_valid_host(source))
1457: continue;
1458:
1459: s_flags = esaddr.flags;
1460: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
1461: if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
1462: /* This is the RP address. */
1463: rpentry = rp_find(source);
1464: if (!rpentry)
1465: continue; /* Don't have such RP. Ignore */
1466:
1467: mrt_rp = rpentry->mrtlink;
1468: my_action = join_or_prune(mrt_rp, upstream_router);
1469: if (my_action == PIM_ACTION_PRUNE) {
1470: /* TODO: XXX: TIMER implem. dependency! */
1471: if ((mrt_rp->jp_timer < holdtime)
1472: || ((mrt_rp->jp_timer == holdtime) &&
1473: (ntohl(src) > ntohl(v->uv_lcl_addr)))) {
1474: /* Suppress the Prune */
1475: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1476: if (mrt_rp->jp_timer < jp_value)
1477: SET_TIMER(mrt_rp->jp_timer, jp_value);
1478: }
1479: } else if (my_action == PIM_ACTION_JOIN) {
1480: /* Override the Prune by scheduling a Join */
1481: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1482: /* TODO: XXX: TIMER implem. dependency! */
1483: if (mrt_rp->jp_timer > jp_value)
1484: SET_TIMER(mrt_rp->jp_timer, jp_value);
1485: }
1486:
1487: /* Check all (*,G) and (S,G) matching to this RP.
1488: * If my_action == JOIN, then send a Join and override
1489: * the (*,*,RP) Prune.
1490: */
1491: for (grp = rpentry->cand_rp->rp_grp_next->grplink; grp; grp = grp->rpnext) {
1492: my_action = join_or_prune(grp->grp_route, upstream_router);
1493: if (my_action == PIM_ACTION_JOIN) {
1494: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1495: /* TODO: XXX: TIMER implem. dependency! */
1496: if (grp->grp_route->jp_timer > jp_value)
1497: SET_TIMER(grp->grp_route->jp_timer, jp_value);
1498: }
1499: for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
1500: my_action = join_or_prune(mrt_srcs, upstream_router);
1501: if (my_action == PIM_ACTION_JOIN) {
1502: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1503: /* TODO: XXX: TIMER implem. dependency! */
1504: if (mrt_srcs->jp_timer > jp_value)
1505: SET_TIMER(mrt_srcs->jp_timer, jp_value);
1506: }
1507: } /* For all (S,G) */
1508: } /* For all (*,G) */
1509: }
1510: } /* num_p_srcs */
1511: continue; /* This was (*,*,RP) suppression */
1512: }
1513:
1514: /* (*,G) or (S,G) suppression */
1515: /* TODO: XXX: currently, accumulated groups
1516: * (i.e. group_masklen < egaddress_lengt) are not
1517: * implemented. Just need to create a loop and apply the
1518: * procedure below for all groups matching the prefix.
1519: */
1520: while (num_j_srcs--) {
1521: GET_ESADDR(&esaddr, data);
1522: source = esaddr.src_addr;
1523: if (!inet_valid_host(source))
1524: continue;
1525:
1526: s_flags = esaddr.flags;
1527: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
1528:
1529: if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
1530: /* (*,G) JOIN_REQUEST (toward the RP) */
1531: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
1532: if (!mrt)
1533: continue;
1534:
1535: my_action = join_or_prune(mrt, upstream_router);
1536: if (my_action != PIM_ACTION_JOIN)
1537: continue;
1538:
1539: /* (*,G) Join suppresion */
1540: if (source != mrt->group->active_rp_grp->rp->rpentry->address)
1541: continue; /* The RP address doesn't match. Ignore. */
1542:
1543: /* Check the holdtime */
1544: /* TODO: XXX: TIMER implem. dependency! */
1545: if (mrt->jp_timer > holdtime)
1546: continue;
1547:
1548: if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
1549: continue;
1550:
1551: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1552: if (mrt->jp_timer < jp_value)
1553: SET_TIMER(mrt->jp_timer, jp_value);
1554: continue;
1555: } /* End of (*,G) Join suppression */
1556:
1557: /* (S,G) Join suppresion */
1558: mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
1559: if (!mrt)
1560: continue;
1561:
1562: my_action = join_or_prune(mrt, upstream_router);
1563: if (my_action != PIM_ACTION_JOIN)
1564: continue;
1565:
1566: /* Check the holdtime */
1567: /* TODO: XXX: TIMER implem. dependency! */
1568: if (mrt->jp_timer > holdtime)
1569: continue;
1570:
1571: if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
1572: continue;
1573:
1574: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1575: if (mrt->jp_timer < jp_value)
1576: SET_TIMER(mrt->jp_timer, jp_value);
1577: continue;
1578: }
1579:
1580: /* Prunes suppression */
1581: while (num_p_srcs--) {
1582: GET_ESADDR(&esaddr, data);
1583: source = esaddr.src_addr;
1584: if (!inet_valid_host(source))
1585: continue;
1586:
1587: s_flags = esaddr.flags;
1588: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
1589: if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
1590: /* (*,G) prune suppression */
1591: rpentry = rp_match(group);
1592: if (!rpentry || (rpentry->address != source))
1593: continue; /* No such RP or it is different. Ignore */
1594:
1595: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
1596: if (!mrt)
1597: continue;
1598:
1599: my_action = join_or_prune(mrt, upstream_router);
1600: if (my_action == PIM_ACTION_PRUNE) {
1601: /* TODO: XXX: TIMER implem. dependency! */
1602: if ((mrt->jp_timer < holdtime)
1603: || ((mrt->jp_timer == holdtime)
1604: && (ntohl(src) > ntohl(v->uv_lcl_addr)))) {
1605: /* Suppress the Prune */
1606: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1607: if (mrt->jp_timer < jp_value)
1608: SET_TIMER(mrt->jp_timer, jp_value);
1609: }
1610: }
1611: else if (my_action == PIM_ACTION_JOIN) {
1612: /* Override the Prune by scheduling a Join */
1613: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1614: /* TODO: XXX: TIMER implem. dependency! */
1615: if (mrt->jp_timer > jp_value)
1616: SET_TIMER(mrt->jp_timer, jp_value);
1617: }
1618:
1619: /* Check all (S,G) entries for this group.
1620: * If my_action == JOIN, then send the Join and override
1621: * the (*,G) Prune.
1622: */
1623: for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
1624: my_action = join_or_prune(mrt_srcs, upstream_router);
1625: if (my_action == PIM_ACTION_JOIN) {
1626: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1627: /* TODO: XXX: TIMER implem. dependency! */
1628: if (mrt->jp_timer > jp_value)
1629: SET_TIMER(mrt->jp_timer, jp_value);
1630: }
1631: } /* For all (S,G) */
1632: continue; /* End of (*,G) prune suppression */
1633: }
1634:
1635: /* (S,G) prune suppression */
1636: mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
1637: if (!mrt)
1638: continue;
1639:
1640: my_action = join_or_prune(mrt, upstream_router);
1641: if (my_action == PIM_ACTION_PRUNE) {
1642: /* Suppress the (S,G) Prune */
1643: /* TODO: XXX: TIMER implem. dependency! */
1644: if ((mrt->jp_timer < holdtime)
1645: || ((mrt->jp_timer == holdtime)
1646: && (ntohl(src) > ntohl(v->uv_lcl_addr)))) {
1647: jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
1648: if (mrt->jp_timer < jp_value)
1649: SET_TIMER(mrt->jp_timer, jp_value);
1650: }
1651: }
1652: else if (my_action == PIM_ACTION_JOIN) {
1653: /* Override the Prune by scheduling a Join */
1654: jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
1655: /* TODO: XXX: TIMER implem. dependency! */
1656: if (mrt->jp_timer > jp_value)
1657: SET_TIMER(mrt->jp_timer, jp_value);
1658: }
1659: } /* while (num_p_srcs--) */
1660: } /* while (num_groups--) */
1661: return TRUE;
1662: } /* End of Join/Prune suppression code */
1663:
1664: /* I am the target of this join, so process the message */
1665:
1666: /* The spec says that if there is (*,G) Join, it has priority over
1667: * old existing ~(S,G) prunes in the routing table.
1668: * However, if the (*,G) Join and the ~(S,G) prune are in
1669: * the same message, ~(S,G) has the priority. The spec doesn't say it,
1670: * but I think the same is true for (*,*,RP) and ~(S,G) prunes.
1671: *
1672: * The code below do:
1673: * (1) Check the whole message for (*,*,RP) Joins.
1674: * (1.1) If found, clean all pruned_oifs for all (*,G) and all (S,G)
1675: * for each RP in the list, but do not update the kernel cache.
1676: * Then go back to the beginning of the message and start
1677: * processing for each group:
1678: * (2) Check for Prunes. If no prunes, process the Joins.
1679: * (3) If there are Prunes:
1680: * (3.1) Scan the Join part for existing (*,G) Join.
1681: * (3.1.1) If there is (*,G) Join, clear join interface from
1682: * the pruned_oifs for all (S,G), but DO NOT flush the
1683: * change to the kernel (by using change_interfaces()
1684: * for example)
1685: * (3.2) After the pruned_oifs are eventually cleared in (3.1.1),
1686: * process the Prune part of the message normally
1687: * (setting the prune_oifs and flashing the changes to the (kernel).
1688: * (3.3) After the Prune part is processed, process the Join part
1689: * normally (by applying any changes to the kernel)
1690: * (4) If there were (*,*,RP) Join/Prune, process them.
1691: *
1692: * If the Join/Prune list is too long, it may result in long processing
1693: * overhead. The idea above is not to place any wrong info in the
1694: * kernel, because it may result in short-time existing traffic
1695: * forwarding on wrong interface.
1696: * Hopefully, in the future will find a better way to implement it.
1697: */
1698: num_groups_tmp = num_groups;
1699: data_start = data;
1700: star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */
1701: while (num_groups_tmp--) {
1702: /* Search for (*,*,RP) Join */
1703: GET_EGADDR(&egaddr, data);
1704: GET_HOSTSHORT(num_j_srcs, data);
1705: GET_HOSTSHORT(num_p_srcs, data);
1706: group = egaddr.mcast_addr;
1707: if ((ntohl(group) != CLASSD_PREFIX) || (egaddr.masklen != STAR_STAR_RP_MSKLEN)) {
1708: /* This is not (*,*,RP). Jump to the next group. */
1709: data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1710: continue;
1711: }
1712:
1713: /* (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear
1714: * the pruned oif, but do not update the kernel.
1715: */
1716: star_star_rp_found = TRUE;
1717: while (num_j_srcs--) {
1718: GET_ESADDR(&esaddr, data);
1719: rpentry = rp_find(esaddr.src_addr);
1720: if (!rpentry)
1721: continue;
1722:
1723: for (rp_grp = rpentry->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
1724: for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
1725: if (grp->grp_route)
1726: VIFM_CLR(vifi, grp->grp_route->pruned_oifs);
1727: for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext)
1728: VIFM_CLR(vifi, mrt->pruned_oifs);
1729: }
1730: }
1731: }
1732: data += (num_p_srcs) * sizeof(pim_encod_src_addr_t);
1733: }
1734:
1735: /*
1736: * Start processing the groups. If this is (*,*,RP), skip it, but process
1737: * it at the end.
1738: */
1739: data = data_start;
1740: num_groups_tmp = num_groups;
1741: while (num_groups_tmp--) {
1742: GET_EGADDR(&egaddr, data);
1743: GET_HOSTSHORT(num_j_srcs, data);
1744: GET_HOSTSHORT(num_p_srcs, data);
1745: group = egaddr.mcast_addr;
1746: if (!IN_MULTICAST(ntohl(group))) {
1747: data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1748: continue; /* Ignore this group and jump to the next one */
1749: }
1750:
1751: if ((ntohl(group) == CLASSD_PREFIX)
1752: && (egaddr.masklen == STAR_STAR_RP_MSKLEN)) {
1753: /* This is (*,*,RP). Jump to the next group. */
1754: data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1755: continue;
1756: }
1757:
1758: rpentry = rp_match(group);
1759: if (!rpentry) {
1760: data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1761: continue;
1762: }
1763:
1764: data_group_j_start = data;
1765: data_group_p_start = data + num_j_srcs * sizeof(pim_encod_src_addr_t);
1766: data_group_end = data + (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1767:
1768: /* Scan the Join part for (*,G) Join and then clear the
1769: * particular interface from pruned_oifs for all (S,G).
1770: * If the RP address in the Join message is different from
1771: * the local match, ignore the whole group.
1772: */
1773: num_j_srcs_tmp = num_j_srcs;
1774: ignore_group = FALSE;
1775: while (num_j_srcs_tmp--) {
1776: GET_ESADDR(&esaddr, data);
1777: if ((esaddr.flags & USADDR_RP_BIT) && (esaddr.flags & USADDR_WC_BIT)) {
1778: /* This is the RP address, i.e. (*,G) Join.
1779: * Check if the RP-mapping is consistent and if "yes",
1780: * then Reset the pruned_oifs for all (S,G) entries.
1781: */
1782: if (rpentry->address != esaddr.src_addr) {
1783: ignore_group = TRUE;
1784: break;
1785: }
1786:
1787: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
1788: if (mrt) {
1789: for (mrt_srcs = mrt->group->mrtlink;
1790: mrt_srcs;
1791: mrt_srcs = mrt_srcs->grpnext)
1792: VIFM_CLR(vifi, mrt_srcs->pruned_oifs);
1793: }
1794: break;
1795: }
1796: }
1797:
1798: if (ignore_group == TRUE) {
1799: data += (num_j_srcs_tmp + num_p_srcs) * sizeof(pim_encod_src_addr_t);
1800: continue;
1801: }
1802:
1803: data = data_group_p_start;
1804: /* Process the Prune part first */
1805: while (num_p_srcs--) {
1806: GET_ESADDR(&esaddr, data);
1807: source = esaddr.src_addr;
1808: if (!inet_valid_host(source))
1809: continue;
1810:
1811: s_flags = esaddr.flags;
1812: if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) {
1813: /* (S,G) prune sent toward S */
1814: mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
1815: if (!mrt)
1816: continue; /* I don't have (S,G) to prune. Ignore. */
1817:
1818: /* If the link is point-to-point, timeout the oif
1819: * immediately, otherwise decrease the timer to allow
1820: * other downstream routers to override the prune.
1821: */
1822: /* TODO: XXX: increase the entry timer? */
1823: if (v->uv_flags & VIFF_POINT_TO_POINT) {
1824: FIRE_TIMER(mrt->vif_timers[vifi]);
1825: } else {
1826: /* TODO: XXX: TIMER implem. dependency! */
1827: if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
1828: SET_TIMER(mrt->vif_timers[vifi],
1829: mrt->vif_deletion_delay[vifi]);
1830: }
1831: IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
1832: VIFM_CLR(vifi, mrt->joined_oifs);
1833: VIFM_SET(vifi, mrt->pruned_oifs);
1834: change_interfaces(mrt,
1835: mrt->incoming,
1836: mrt->joined_oifs,
1837: mrt->pruned_oifs,
1838: mrt->leaves,
1839: mrt->asserted_oifs, 0);
1840: }
1841: continue;
1842: }
1843:
1844: if ((s_flags & USADDR_RP_BIT) && (!(s_flags & USADDR_WC_BIT))) {
1845: /* ~(S,G)RPbit prune sent toward the RP */
1846: mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
1847: if (mrt) {
1848: SET_TIMER(mrt->timer, holdtime);
1849: if (v->uv_flags & VIFF_POINT_TO_POINT) {
1850: FIRE_TIMER(mrt->vif_timers[vifi]);
1851: } else {
1852: /* TODO: XXX: TIMER implem. dependency! */
1853: if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
1854: SET_TIMER(mrt->vif_timers[vifi],
1855: mrt->vif_deletion_delay[vifi]);
1856: }
1857: IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
1858: VIFM_CLR(vifi, mrt->joined_oifs);
1859: VIFM_SET(vifi, mrt->pruned_oifs);
1860: change_interfaces(mrt,
1861: mrt->incoming,
1862: mrt->joined_oifs,
1863: mrt->pruned_oifs,
1864: mrt->leaves,
1865: mrt->asserted_oifs, 0);
1866: }
1867: continue;
1868: }
1869:
1870: /* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */
1871: mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
1872: if (mrt) {
1873: mrt = find_route(source, group, MRTF_SG | MRTF_RP, CREATE);
1874: if (!mrt)
1875: continue;
1876:
1877: mrt->flags &= ~MRTF_NEW;
1878: RESET_TIMER(mrt->vif_timers[vifi]);
1879: /* TODO: XXX: The spec doens't say what value to use for
1880: * the entry time. Use the J/P holdtime.
1881: */
1882: SET_TIMER(mrt->timer, holdtime);
1883: /* TODO: XXX: The spec says to delete the oif. However,
1884: * its timer only should be lowered, so the prune can be
1885: * overwritten on multiaccess LAN. Spec BUG.
1886: */
1887: VIFM_CLR(vifi, mrt->joined_oifs);
1888: VIFM_SET(vifi, mrt->pruned_oifs);
1889: change_interfaces(mrt,
1890: mrt->incoming,
1891: mrt->joined_oifs,
1892: mrt->pruned_oifs,
1893: mrt->leaves,
1894: mrt->asserted_oifs, 0);
1895: }
1896: continue;
1897: }
1898:
1899: if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
1900: /* (*,G) Prune */
1901: mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
1902: if (mrt) {
1903: if (mrt->flags & MRTF_WC) {
1904: /* TODO: XXX: Should check the whole Prune list in
1905: * advance for (*,G) prune and if the RP address
1906: * does not match the local RP-map, then ignore the
1907: * whole group, not only this particular (*,G) prune.
1908: */
1909: if (mrt->group->active_rp_grp->rp->rpentry->address != source)
1910: continue; /* The RP address doesn't match. */
1911:
1912: if (v->uv_flags & VIFF_POINT_TO_POINT) {
1913: FIRE_TIMER(mrt->vif_timers[vifi]);
1914: } else {
1915: /* TODO: XXX: TIMER implem. dependency! */
1916: if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
1917: SET_TIMER(mrt->vif_timers[vifi],
1918: mrt->vif_deletion_delay[vifi]);
1919: }
1920: IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
1921: VIFM_CLR(vifi, mrt->joined_oifs);
1922: VIFM_SET(vifi, mrt->pruned_oifs);
1923: change_interfaces(mrt,
1924: mrt->incoming,
1925: mrt->joined_oifs,
1926: mrt->pruned_oifs,
1927: mrt->leaves,
1928: mrt->asserted_oifs, 0);
1929: }
1930: continue;
1931: }
1932:
1933: /* No (*,G) entry, but found (*,*,RP). Create (*,G) */
1934: if (mrt->source->address != source)
1935: continue; /* The RP address doesn't match. */
1936:
1937: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
1938: if (!mrt)
1939: continue;
1940:
1941: mrt->flags &= ~MRTF_NEW;
1942: RESET_TIMER(mrt->vif_timers[vifi]);
1943: /* TODO: XXX: should only lower the oif timer, so it can
1944: * be overwritten on multiaccess LAN. Spec bug.
1945: */
1946: VIFM_CLR(vifi, mrt->joined_oifs);
1947: VIFM_SET(vifi, mrt->pruned_oifs);
1948: change_interfaces(mrt,
1949: mrt->incoming,
1950: mrt->joined_oifs,
1951: mrt->pruned_oifs,
1952: mrt->leaves,
1953: mrt->asserted_oifs, 0);
1954: } /* (*,G) or (*,*,RP) found */
1955: } /* (*,G) prune */
1956: } /* while (num_p_srcs--) */
1957: /* End of (S,G) and (*,G) Prune handling */
1958:
1959: /* Jump back to the Join part and process it */
1960: data = data_group_j_start;
1961: while (num_j_srcs--) {
1962: GET_ESADDR(&esaddr, data);
1963: source = esaddr.src_addr;
1964: if (!inet_valid_host(source))
1965: continue;
1966:
1967: s_flags = esaddr.flags;
1968: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
1969: if ((s_flags & USADDR_WC_BIT) && (s_flags & USADDR_RP_BIT)) {
1970: /* (*,G) Join toward RP */
1971: /* It has been checked already that this RP address is
1972: * the same as the local RP-maping.
1973: */
1974: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
1975: if (!mrt)
1976: continue;
1977:
1978: VIFM_SET(vifi, mrt->joined_oifs);
1979: VIFM_CLR(vifi, mrt->pruned_oifs);
1980: VIFM_CLR(vifi, mrt->asserted_oifs);
1981: /* TODO: XXX: TIMER implem. dependency! */
1982: if (mrt->vif_timers[vifi] < holdtime) {
1983: SET_TIMER(mrt->vif_timers[vifi], holdtime);
1984: mrt->vif_deletion_delay[vifi] = holdtime/3;
1985: }
1986: if (mrt->timer < holdtime)
1987: SET_TIMER(mrt->timer, holdtime);
1988: mrt->flags &= ~MRTF_NEW;
1989: change_interfaces(mrt,
1990: mrt->incoming,
1991: mrt->joined_oifs,
1992: mrt->pruned_oifs,
1993: mrt->leaves,
1994: mrt->asserted_oifs, 0);
1995: /* Need to update the (S,G) entries, because of the previous
1996: * cleaning of the pruned_oifs. The reason is that if the
1997: * oifs for (*,G) weren't changed, the (S,G) entries won't
1998: * be updated by change_interfaces()
1999: */
2000: for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext)
2001: change_interfaces(mrt_srcs,
2002: mrt_srcs->incoming,
2003: mrt_srcs->joined_oifs,
2004: mrt_srcs->pruned_oifs,
2005: mrt_srcs->leaves,
2006: mrt_srcs->asserted_oifs, 0);
2007: continue;
2008: }
2009:
2010: if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) {
2011: /* (S,G) Join toward S */
2012: if (vifi == get_iif(source))
2013: continue; /* Ignore this (S,G) Join */
2014:
2015: mrt = find_route(source, group, MRTF_SG, CREATE);
2016: if (!mrt)
2017: continue;
2018:
2019: VIFM_SET(vifi, mrt->joined_oifs);
2020: VIFM_CLR(vifi, mrt->pruned_oifs);
2021: VIFM_CLR(vifi, mrt->asserted_oifs);
2022: /* TODO: XXX: TIMER implem. dependency! */
2023: if (mrt->vif_timers[vifi] < holdtime) {
2024: SET_TIMER(mrt->vif_timers[vifi], holdtime);
2025: mrt->vif_deletion_delay[vifi] = holdtime/3;
2026: }
2027: if (mrt->timer < holdtime)
2028: SET_TIMER(mrt->timer, holdtime);
2029: /* TODO: if this is a new entry, send immediately the
2030: * Join message toward S. The Join/Prune timer for new
2031: * entries is 0, but it does not means the message will
2032: * be sent immediately.
2033: */
2034: mrt->flags &= ~MRTF_NEW;
2035: /* Note that we must create (S,G) without the RPbit set.
2036: * If we already had such entry, change_interfaces() will
2037: * reset the RPbit propertly.
2038: */
2039: change_interfaces(mrt,
2040: mrt->source->incoming,
2041: mrt->joined_oifs,
2042: mrt->pruned_oifs,
2043: mrt->leaves,
2044: mrt->asserted_oifs, 0);
2045: continue;
2046: }
2047: } /* while (num_j_srcs--) */
2048: data = data_group_end;
2049: } /* for all groups */
2050:
2051: /* Now process the (*,*,RP) Join/Prune */
2052: if (star_star_rp_found != TRUE)
2053: return TRUE;
2054:
2055: data = data_start;
2056: while (num_groups--) {
2057: /* The conservative approach is to scan again the whole message,
2058: * just in case if we have more than one (*,*,RP) requests.
2059: */
2060: GET_EGADDR(&egaddr, data);
2061: GET_HOSTSHORT(num_j_srcs, data);
2062: GET_HOSTSHORT(num_p_srcs, data);
2063: group = egaddr.mcast_addr;
2064: if ((ntohl(group) != CLASSD_PREFIX)
2065: || (egaddr.masklen != STAR_STAR_RP_MSKLEN)) {
2066: /* This is not (*,*,RP). Jump to the next group. */
2067: data +=
2068: (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
2069: continue;
2070: }
2071: /* (*,*,RP) found */
2072: while (num_j_srcs--) {
2073: /* TODO: XXX: check that the iif is different from the Join oifs */
2074: GET_ESADDR(&esaddr, data);
2075: source = esaddr.src_addr;
2076: if (!inet_valid_host(source))
2077: continue;
2078:
2079: s_flags = esaddr.flags;
2080: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
2081: mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, CREATE);
2082: if (!mrt)
2083: continue;
2084:
2085: VIFM_SET(vifi, mrt->joined_oifs);
2086: VIFM_CLR(vifi, mrt->pruned_oifs);
2087: VIFM_CLR(vifi, mrt->asserted_oifs);
2088: /* TODO: XXX: TIMER implem. dependency! */
2089: if (mrt->vif_timers[vifi] < holdtime) {
2090: SET_TIMER(mrt->vif_timers[vifi], holdtime);
2091: mrt->vif_deletion_delay[vifi] = holdtime/3;
2092: }
2093: if (mrt->timer < holdtime)
2094: SET_TIMER(mrt->timer, holdtime);
2095: mrt->flags &= ~MRTF_NEW;
2096: change_interfaces(mrt,
2097: mrt->incoming,
2098: mrt->joined_oifs,
2099: mrt->pruned_oifs,
2100: mrt->leaves,
2101: mrt->asserted_oifs, 0);
2102:
2103: /* Need to update the (S,G) and (*,G) entries, because of
2104: * the previous cleaning of the pruned_oifs. The reason is
2105: * that if the oifs for (*,*,RP) weren't changed, the
2106: * (*,G) and (S,G) entries won't be updated by change_interfaces()
2107: */
2108: for (rp_grp = mrt->source->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
2109: for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
2110: /* Update the (*,G) entry */
2111: if (grp->grp_route) {
2112: change_interfaces(grp->grp_route,
2113: grp->grp_route->incoming,
2114: grp->grp_route->joined_oifs,
2115: grp->grp_route->pruned_oifs,
2116: grp->grp_route->leaves,
2117: grp->grp_route->asserted_oifs, 0);
2118: }
2119: /* Update the (S,G) entries */
2120: for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext)
2121: change_interfaces(mrt_srcs,
2122: mrt_srcs->incoming,
2123: mrt_srcs->joined_oifs,
2124: mrt_srcs->pruned_oifs,
2125: mrt_srcs->leaves,
2126: mrt_srcs->asserted_oifs, 0);
2127: }
2128: }
2129: continue;
2130: }
2131:
2132: while (num_p_srcs--) {
2133: /* TODO: XXX: can we have (*,*,RP) Prune? */
2134: GET_ESADDR(&esaddr, data);
2135: source = esaddr.src_addr;
2136: if (!inet_valid_host(source))
2137: continue;
2138:
2139: s_flags = esaddr.flags;
2140: MASKLEN_TO_MASK(esaddr.masklen, s_mask);
2141: mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, DONT_CREATE);
2142: if (!mrt)
2143: continue;
2144:
2145: /* If the link is point-to-point, timeout the oif
2146: * immediately, otherwise decrease the timer to allow
2147: * other downstream routers to override the prune.
2148: */
2149: /* TODO: XXX: increase the entry timer? */
2150: if (v->uv_flags & VIFF_POINT_TO_POINT) {
2151: FIRE_TIMER(mrt->vif_timers[vifi]);
2152: } else {
2153: /* TODO: XXX: TIMER implem. dependency! */
2154: if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
2155: SET_TIMER(mrt->vif_timers[vifi],
2156: mrt->vif_deletion_delay[vifi]);
2157: }
2158: IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
2159: VIFM_CLR(vifi, mrt->joined_oifs);
2160: VIFM_SET(vifi, mrt->pruned_oifs);
2161: VIFM_SET(vifi, mrt->asserted_oifs);
2162: change_interfaces(mrt,
2163: mrt->incoming,
2164: mrt->joined_oifs,
2165: mrt->pruned_oifs,
2166: mrt->leaves,
2167: mrt->asserted_oifs, 0);
2168: }
2169:
2170: }
2171: } /* For all groups processing (*,*,R) */
2172:
2173: return TRUE;
2174: }
2175:
2176:
2177: /*
2178: * TODO: NOT USED, probably buggy, but may need it in the future.
2179: */
2180: /*
2181: * TODO: create two functions: periodic which timeout the timers
2182: * and non-periodic which only check but don't timeout the timers.
2183: */
2184: /*
2185: * Create and send Join/Prune messages per interface.
2186: * Only the entries which have the Join/Prune timer expired are included.
2187: * In the special case when we have ~(S,G)RPbit Prune entry, we must
2188: * include any (*,G) or (*,*,RP)
2189: * Currently the whole table is scanned. In the future will have all
2190: * routing entries linked in a chain with the corresponding upstream
2191: * pim_nbr_entry.
2192: *
2193: * If pim_nbr is not NULL, then send to only this particular PIM neighbor,
2194: */
2195: int send_periodic_pim_join_prune(vifi_t vifi, pim_nbr_entry_t *pim_nbr, uint16_t holdtime)
2196: {
2197: grpentry_t *grp;
2198: mrtentry_t *mrt;
2199: rpentry_t *rp;
2200: uint32_t addr;
2201: struct uvif *v;
2202: pim_nbr_entry_t *nbr;
2203: cand_rp_t *cand_rp;
2204:
2205: /* Walk through all routing entries. The iif must match to include the
2206: * entry. Check first the (*,G) entry and then all associated (S,G).
2207: * At the end of the message will add any (*,*,RP) entries.
2208: * TODO: check other PIM-SM implementations and decide the more
2209: * appropriate place to put the (*,*,RP) entries: in the beginning of the
2210: * message or at the end.
2211: */
2212:
2213: v = &uvifs[vifi];
2214:
2215: /* Check the (*,G) and (S,G) entries */
2216: for (grp = grplist; grp; grp = grp->next) {
2217: mrt = grp->grp_route;
2218: /* TODO: XXX: TIMER implem. dependency! */
2219: if (mrt && (mrt->incoming == vifi) && (mrt->jp_timer <= TIMER_INTERVAL)) {
2220:
2221: /* If join/prune to a particular neighbor only was specified */
2222: if (pim_nbr && mrt->upstream != pim_nbr)
2223: continue;
2224:
2225: /* Don't send (*,G) or (S,G,rpt) Join/Prune */
2226: /* TODO: this handles (S,G,rpt) Join/Prune? */
2227: if (!(mrt->flags & MRTF_SG) && IN_PIM_SSM_RANGE(grp->group)) {
2228: logit(LOG_DEBUG, 0, "Skip j/p for SSM (!SG)");
2229: continue;
2230: }
2231:
2232: /* TODO: XXX: The J/P suppression timer is not in the spec! */
2233: if (!VIFM_ISEMPTY(mrt->joined_oifs) || (v->uv_flags & VIFF_DR)) {
2234: add_jp_entry(mrt->upstream, holdtime,
2235: grp->group,
2236: SINGLE_GRP_MSKLEN,
2237: grp->rpaddr,
2238: SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN);
2239: }
2240: /* TODO: XXX: TIMER implem. dependency! */
2241: if (VIFM_ISEMPTY(mrt->joined_oifs)
2242: && (!(v->uv_flags & VIFF_DR))
2243: && (mrt->jp_timer <= TIMER_INTERVAL)) {
2244: add_jp_entry(mrt->upstream, holdtime,
2245: grp->group, SINGLE_GRP_MSKLEN,
2246: grp->rpaddr,
2247: SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE);
2248: }
2249: }
2250:
2251: /* Check the (S,G) entries */
2252: for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext) {
2253: /* If join/prune to a particular neighbor only was specified */
2254: if (pim_nbr && mrt->upstream != pim_nbr)
2255: continue;
2256:
2257: if (mrt->flags & MRTF_RP) {
2258: /* RPbit set */
2259: addr = mrt->source->address;
2260: if (VIFM_ISEMPTY(mrt->joined_oifs) || find_vif_direct_local(addr) != NO_VIF) {
2261: /* TODO: XXX: TIMER implem. dependency! */
2262: if (grp->grp_route &&
2263: grp->grp_route->incoming == vifi &&
2264: grp->grp_route->jp_timer <= TIMER_INTERVAL)
2265: /* S is directly connected. Send toward RP */
2266: add_jp_entry(grp->grp_route->upstream,
2267: holdtime,
2268: grp->group, SINGLE_GRP_MSKLEN,
2269: addr, SINGLE_SRC_MSKLEN,
2270: MRTF_RP, PIM_ACTION_PRUNE);
2271: }
2272: }
2273: else {
2274: /* RPbit cleared */
2275: if (VIFM_ISEMPTY(mrt->joined_oifs)) {
2276: /* TODO: XXX: TIMER implem. dependency! */
2277: if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL)
2278: add_jp_entry(mrt->upstream, holdtime,
2279: grp->group, SINGLE_GRP_MSKLEN,
2280: mrt->source->address,
2281: SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE);
2282: } else {
2283: logit(LOG_DEBUG, 0 , "Joined not empty, group %s",
2284: inet_ntoa(*(struct in_addr *)&grp->group));
2285: /* TODO: XXX: TIMER implem. dependency! */
2286: if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL)
2287: add_jp_entry(mrt->upstream, holdtime,
2288: grp->group, SINGLE_GRP_MSKLEN,
2289: mrt->source->address,
2290: SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN);
2291: }
2292: /* TODO: XXX: TIMER implem. dependency! */
2293: if ((mrt->flags & MRTF_SPT) &&
2294: grp->grp_route &&
2295: mrt->incoming != grp->grp_route->incoming &&
2296: grp->grp_route->incoming == vifi &&
2297: grp->grp_route->jp_timer <= TIMER_INTERVAL)
2298: add_jp_entry(grp->grp_route->upstream, holdtime,
2299: grp->group, SINGLE_GRP_MSKLEN,
2300: mrt->source->address,
2301: SINGLE_SRC_MSKLEN, MRTF_RP,
2302: PIM_ACTION_PRUNE);
2303: }
2304: }
2305: }
2306:
2307: /* Check the (*,*,RP) entries */
2308: for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
2309: rp = cand_rp->rpentry;
2310:
2311: /* If join/prune to a particular neighbor only was specified */
2312: if (pim_nbr && rp->upstream != pim_nbr)
2313: continue;
2314:
2315: /* TODO: XXX: TIMER implem. dependency! */
2316: if (rp->mrtlink &&
2317: rp->incoming == vifi &&
2318: rp->mrtlink->jp_timer <= TIMER_INTERVAL) {
2319: add_jp_entry(rp->upstream, holdtime, htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN,
2320: rp->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, PIM_ACTION_JOIN);
2321: }
2322: }
2323:
2324: /* Send all pending Join/Prune messages */
2325: for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) {
2326: /* If join/prune to a particular neighbor only was specified */
2327: if (pim_nbr && (nbr != pim_nbr))
2328: continue;
2329:
2330: pack_and_send_jp_message(nbr);
2331: }
2332:
2333: return TRUE;
2334: }
2335:
2336:
2337: int add_jp_entry(pim_nbr_entry_t *pim_nbr, uint16_t holdtime, uint32_t group,
2338: uint8_t grp_msklen, uint32_t source, uint8_t src_msklen,
2339: uint16_t addr_flags, uint8_t join_prune)
2340: {
2341: build_jp_message_t *bjpm;
2342: uint8_t *data;
2343: uint8_t flags = 0;
2344: int rp_flag;
2345:
2346: bjpm = pim_nbr->build_jp_message;
2347: if (bjpm) {
2348: if ((bjpm->jp_message_size + bjpm->join_list_size +
2349: bjpm->prune_list_size + bjpm->rp_list_join_size +
2350: bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE)
2351: || (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE)
2352: || (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE)
2353: || (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE)
2354: || (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE)) {
2355: /* TODO: XXX: BUG: If the list is getting too large, must
2356: * be careful with the fragmentation.
2357: */
2358: pack_and_send_jp_message(pim_nbr);
2359: bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
2360: }
2361: }
2362:
2363: if (bjpm) {
2364: if ((bjpm->curr_group != group)
2365: || (bjpm->curr_group_msklen != grp_msklen)
2366: || (bjpm->holdtime != holdtime)) {
2367: pack_jp_message(pim_nbr);
2368: }
2369: }
2370:
2371: if (!bjpm) {
2372: bjpm = get_jp_working_buff();
2373: if (!bjpm) {
2374: logit(LOG_ERR, 0, "Failed allocating working buffer in add_jp_entry()");
2375: exit (-1);
2376: }
2377:
2378: pim_nbr->build_jp_message = bjpm;
2379: data = bjpm->jp_message;
2380: PUT_EUADDR(pim_nbr->address, data);
2381: PUT_BYTE(0, data); /* Reserved */
2382: bjpm->num_groups_ptr = data++; /* The pointer for numgroups */
2383: *(bjpm->num_groups_ptr) = 0; /* Zero groups */
2384: PUT_HOSTSHORT(holdtime, data);
2385: bjpm->holdtime = holdtime;
2386: bjpm->jp_message_size = data - bjpm->jp_message;
2387: }
2388:
2389: /* TODO: move somewhere else, only when it is a new group */
2390: bjpm->curr_group = group;
2391: bjpm->curr_group_msklen = grp_msklen;
2392:
2393: if (group == htonl(CLASSD_PREFIX) && grp_msklen == STAR_STAR_RP_MSKLEN)
2394: rp_flag = TRUE;
2395: else
2396: rp_flag = FALSE;
2397:
2398: switch (join_prune) {
2399: case PIM_ACTION_JOIN:
2400: if (rp_flag == TRUE)
2401: data = bjpm->rp_list_join + bjpm->rp_list_join_size;
2402: else
2403: data = bjpm->join_list + bjpm->join_list_size;
2404: break;
2405:
2406: case PIM_ACTION_PRUNE:
2407: if (rp_flag == TRUE)
2408: data = bjpm->rp_list_prune + bjpm->rp_list_prune_size;
2409: else
2410: data = bjpm->prune_list + bjpm->prune_list_size;
2411: break;
2412:
2413: default:
2414: return FALSE;
2415: }
2416:
2417: flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */
2418: if (addr_flags & MRTF_RP)
2419: flags |= USADDR_RP_BIT;
2420: if (addr_flags & MRTF_WC)
2421: flags |= USADDR_WC_BIT;
2422: PUT_ESADDR(source, src_msklen, flags, data);
2423:
2424: switch (join_prune) {
2425: case PIM_ACTION_JOIN:
2426: if (rp_flag == TRUE) {
2427: bjpm->rp_list_join_size = data - bjpm->rp_list_join;
2428: bjpm->rp_list_join_number++;
2429: } else {
2430: bjpm->join_list_size = data - bjpm->join_list;
2431: bjpm->join_addr_number++;
2432: }
2433: break;
2434:
2435: case PIM_ACTION_PRUNE:
2436: if (rp_flag == TRUE) {
2437: bjpm->rp_list_prune_size = data - bjpm->rp_list_prune;
2438: bjpm->rp_list_prune_number++;
2439: } else {
2440: bjpm->prune_list_size = data - bjpm->prune_list;
2441: bjpm->prune_addr_number++;
2442: }
2443: break;
2444:
2445: default:
2446: return FALSE;
2447: }
2448:
2449: return TRUE;
2450: }
2451:
2452:
2453: /* TODO: check again the size of the buffers */
2454: static build_jp_message_t *get_jp_working_buff(void)
2455: {
2456: build_jp_message_t *bjpm;
2457:
2458: if (build_jp_message_pool_counter == 0) {
2459: bjpm = calloc(1, sizeof(build_jp_message_t));
2460: if (!bjpm)
2461: return NULL;
2462:
2463: bjpm->next = NULL;
2464: bjpm->jp_message = calloc(1, MAX_JP_MESSAGE_SIZE +
2465: sizeof(pim_jp_encod_grp_t) +
2466: 2 * sizeof(pim_encod_src_addr_t));
2467: if (!bjpm->jp_message) {
2468: free(bjpm);
2469: return NULL;
2470: }
2471:
2472: bjpm->jp_message_size = 0;
2473: bjpm->join_list_size = 0;
2474: bjpm->join_addr_number = 0;
2475: bjpm->join_list = calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t));
2476: if (!bjpm->join_list) {
2477: free(bjpm->jp_message);
2478: free(bjpm);
2479: return NULL;
2480: }
2481: bjpm->prune_list_size = 0;
2482: bjpm->prune_addr_number = 0;
2483: bjpm->prune_list = calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t));
2484: if (!bjpm->prune_list) {
2485: free(bjpm->join_list);
2486: free(bjpm->jp_message);
2487: free(bjpm);
2488: return NULL;
2489: }
2490: bjpm->rp_list_join_size = 0;
2491: bjpm->rp_list_join_number = 0;
2492: bjpm->rp_list_join = calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t));
2493: if (!bjpm->rp_list_join) {
2494: free(bjpm->prune_list);
2495: free(bjpm->join_list);
2496: free(bjpm->jp_message);
2497: free(bjpm);
2498: return NULL;
2499: }
2500: bjpm->rp_list_prune_size = 0;
2501: bjpm->rp_list_prune_number = 0;
2502: bjpm->rp_list_prune = calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t));
2503: if (!bjpm->rp_list_prune) {
2504: free(bjpm->rp_list_join);
2505: free(bjpm->prune_list);
2506: free(bjpm->join_list);
2507: free(bjpm->jp_message);
2508: free(bjpm);
2509: return NULL;
2510: }
2511: bjpm->curr_group = INADDR_ANY_N;
2512: bjpm->curr_group_msklen = 0;
2513: bjpm->holdtime = 0;
2514:
2515: return bjpm;
2516: }
2517:
2518: bjpm = build_jp_message_pool;
2519: build_jp_message_pool = build_jp_message_pool->next;
2520: build_jp_message_pool_counter--;
2521: bjpm->jp_message_size = 0;
2522: bjpm->join_list_size = 0;
2523: bjpm->join_addr_number = 0;
2524: bjpm->prune_list_size = 0;
2525: bjpm->prune_addr_number = 0;
2526: bjpm->curr_group = INADDR_ANY_N;
2527: bjpm->curr_group_msklen = 0;
2528:
2529: return bjpm;
2530: }
2531:
2532:
2533: static void return_jp_working_buff(pim_nbr_entry_t *pim_nbr)
2534: {
2535: build_jp_message_t *bjpm = pim_nbr->build_jp_message;
2536:
2537: if (!bjpm)
2538: return;
2539:
2540: /* Don't waste memory by keeping too many free buffers */
2541: /* TODO: check/modify the definitions for POOL_NUMBER and size */
2542: if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER) {
2543: free(bjpm->jp_message);
2544: free(bjpm->join_list);
2545: free(bjpm->prune_list);
2546: free(bjpm->rp_list_join);
2547: free(bjpm->rp_list_prune);
2548: free(bjpm);
2549: } else {
2550: bjpm->next = build_jp_message_pool;
2551: build_jp_message_pool = bjpm;
2552: build_jp_message_pool_counter++;
2553: }
2554:
2555: pim_nbr->build_jp_message = NULL;
2556: }
2557:
2558:
2559: /* TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the
2560: * Join/Prune message. However, this particular implementation of PIM
2561: * processes the Join/Prune messages faster if (*,*,RP) is at the beginning.
2562: * Modify some of the functions below such that the
2563: * outgoing messages place (*,*,RP) at the beginning, not at the end.
2564: */
2565: static void pack_jp_message(pim_nbr_entry_t *pim_nbr)
2566: {
2567: build_jp_message_t *bjpm;
2568: uint8_t *data;
2569:
2570: bjpm = pim_nbr->build_jp_message;
2571: if (!bjpm || (bjpm->curr_group == INADDR_ANY_N))
2572: return;
2573:
2574: data = bjpm->jp_message + bjpm->jp_message_size;
2575: PUT_EGADDR(bjpm->curr_group, bjpm->curr_group_msklen, 0, data);
2576: PUT_HOSTSHORT(bjpm->join_addr_number, data);
2577: PUT_HOSTSHORT(bjpm->prune_addr_number, data);
2578: memcpy(data, bjpm->join_list, bjpm->join_list_size);
2579: data += bjpm->join_list_size;
2580: memcpy(data, bjpm->prune_list, bjpm->prune_list_size);
2581: data += bjpm->prune_list_size;
2582: bjpm->jp_message_size = (data - bjpm->jp_message);
2583: bjpm->curr_group = INADDR_ANY_N;
2584: bjpm->curr_group_msklen = 0;
2585: bjpm->join_list_size = 0;
2586: bjpm->join_addr_number = 0;
2587: bjpm->prune_list_size = 0;
2588: bjpm->prune_addr_number = 0;
2589: (*bjpm->num_groups_ptr)++;
2590:
2591: if (*bjpm->num_groups_ptr == ((uint8_t)~0 - 1)) {
2592: if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) {
2593: /* Add the (*,*,RP) at the end */
2594: data = bjpm->jp_message + bjpm->jp_message_size;
2595: PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data);
2596: PUT_HOSTSHORT(bjpm->rp_list_join_number, data);
2597: PUT_HOSTSHORT(bjpm->rp_list_prune_number, data);
2598: memcpy(data, bjpm->rp_list_join, bjpm->rp_list_join_size);
2599: data += bjpm->rp_list_join_size;
2600: memcpy(data, bjpm->rp_list_prune, bjpm->rp_list_prune_size);
2601: data += bjpm->rp_list_prune_size;
2602: bjpm->jp_message_size = (data - bjpm->jp_message);
2603: bjpm->rp_list_join_size = 0;
2604: bjpm->rp_list_join_number = 0;
2605: bjpm->rp_list_prune_size = 0;
2606: bjpm->rp_list_prune_number = 0;
2607: (*bjpm->num_groups_ptr)++;
2608: }
2609: send_jp_message(pim_nbr);
2610: }
2611: }
2612:
2613:
2614: void pack_and_send_jp_message(pim_nbr_entry_t *pim_nbr)
2615: {
2616: uint8_t *data;
2617: build_jp_message_t *bjpm;
2618:
2619: if (!pim_nbr || !pim_nbr->build_jp_message)
2620: return;
2621:
2622: pack_jp_message(pim_nbr);
2623:
2624: bjpm = pim_nbr->build_jp_message;
2625: if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) {
2626: /* Add the (*,*,RP) at the end */
2627: data = bjpm->jp_message + bjpm->jp_message_size;
2628: PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data);
2629: PUT_HOSTSHORT(bjpm->rp_list_join_number, data);
2630: PUT_HOSTSHORT(bjpm->rp_list_prune_number, data);
2631: memcpy(data, bjpm->rp_list_join, bjpm->rp_list_join_size);
2632: data += bjpm->rp_list_join_size;
2633: memcpy(data, bjpm->rp_list_prune, bjpm->rp_list_prune_size);
2634: data += bjpm->rp_list_prune_size;
2635: bjpm->jp_message_size = (data - bjpm->jp_message);
2636: bjpm->rp_list_join_size = 0;
2637: bjpm->rp_list_join_number = 0;
2638: bjpm->rp_list_prune_size = 0;
2639: bjpm->rp_list_prune_number = 0;
2640: (*bjpm->num_groups_ptr)++;
2641: }
2642: send_jp_message(pim_nbr);
2643: }
2644:
2645:
2646: static void send_jp_message(pim_nbr_entry_t *pim_nbr)
2647: {
2648: size_t len;
2649: vifi_t vifi;
2650:
2651: len = pim_nbr->build_jp_message->jp_message_size;
2652: vifi = pim_nbr->vifi;
2653: memcpy(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t),
2654: pim_nbr->build_jp_message->jp_message, len);
2655: logit(LOG_INFO, 0, "Send PIM JOIN/PRUNE from %s on %s",
2656: inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)), uvifs[vifi].uv_name);
2657: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
2658: PIM_JOIN_PRUNE, len);
2659: return_jp_working_buff(pim_nbr);
2660: }
2661:
2662:
2663: /************************************************************************
2664: * PIM_ASSERT
2665: ************************************************************************/
2666: int receive_pim_assert(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
2667: {
2668: vifi_t vifi;
2669: pim_encod_uni_addr_t eusaddr;
2670: pim_encod_grp_addr_t egaddr;
2671: uint32_t source, group;
2672: mrtentry_t *mrt, *mrt2;
2673: uint8_t *data;
2674: struct uvif *v;
2675: uint32_t assert_preference;
2676: uint32_t assert_metric;
2677: uint32_t assert_rptbit;
2678: uint32_t local_metric;
2679: uint32_t local_preference;
2680: uint8_t local_rptbit;
2681: uint8_t local_wins;
2682: pim_nbr_entry_t *original_upstream_router;
2683:
2684: vifi = find_vif_direct(src);
2685: if (vifi == NO_VIF) {
2686: /* Either a local vif or somehow received PIM_ASSERT from
2687: * non-directly connected router. Ignore it.
2688: */
2689: if (local_address(src) == NO_VIF)
2690: logit(LOG_DEBUG, 0, "Ignoring PIM_ASSERT from non-neighbor router %s",
2691: inet_fmt(src, s1, sizeof(s1)));
2692:
2693: return FALSE;
2694: }
2695:
2696: /* Checksum */
2697: if (inet_cksum((uint16_t *)msg, len))
2698: return FALSE;
2699:
2700: v = &uvifs[vifi];
2701: if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER))
2702: return FALSE; /* Shoudn't come on this interface */
2703:
2704: data = (uint8_t *)(msg + sizeof(pim_header_t));
2705:
2706: /* Get the group and source addresses */
2707: GET_EGADDR(&egaddr, data);
2708: GET_EUADDR(&eusaddr, data);
2709:
2710: /* Get the metric related info */
2711: GET_HOSTLONG(assert_preference, data);
2712: GET_HOSTLONG(assert_metric, data);
2713: assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT;
2714:
2715: source = eusaddr.unicast_addr;
2716: group = egaddr.mcast_addr;
2717:
2718: logit(LOG_INFO, 0, "Received PIM ASSERT from %s for group %s and source %s",
2719: inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)),
2720: inet_fmt(source, s3, sizeof(s3)));
2721:
2722: /* Find the longest "active" entry, i.e. the one with a kernel mirror */
2723: if (assert_rptbit) {
2724: mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
2725: if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) {
2726: if (mrt->flags & MRTF_WC)
2727: mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
2728: }
2729: } else {
2730: mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
2731: if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) {
2732: if (mrt->flags & MRTF_SG) {
2733: mrt2 = mrt->group->grp_route;
2734: if (mrt2 && (mrt2->flags & MRTF_KERNEL_CACHE))
2735: mrt = mrt2;
2736: else
2737: mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
2738: } else {
2739: if (mrt->flags & MRTF_WC)
2740: mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
2741: }
2742: }
2743: }
2744:
2745: if (!mrt || !(mrt->flags & MRTF_KERNEL_CACHE)) {
2746: /* No routing entry or not "active" entry. Ignore the assert */
2747: return FALSE;
2748: }
2749:
2750: /* Prepare the local preference and metric */
2751: if ((mrt->flags & MRTF_PMBR)
2752: || ((mrt->flags & MRTF_SG)
2753: && !(mrt->flags & MRTF_RP))) {
2754: /* Either (S,G) (toward S) or (*,*,RP). */
2755: /* TODO: XXX: get the info from mrt, or source or from kernel ? */
2756: /*
2757: local_metric = mrt->source->metric;
2758: local_preference = mrt->source->preference;
2759: */
2760: local_metric = mrt->metric;
2761: local_preference = mrt->preference;
2762: } else {
2763: /* Should be (*,G) or (S,G)RPbit entry.
2764: * Get what we need from the RP info.
2765: */
2766: /* TODO: get the info from mrt, RP-entry or kernel? */
2767: /*
2768: local_metric =
2769: mrt->group->active_rp_grp->rp->rpentry->metric;
2770: local_preference =
2771: mrt->group->active_rp_grp->rp->rpentry->preference;
2772: */
2773: local_metric = mrt->metric;
2774: local_preference = mrt->preference;
2775: }
2776:
2777: local_rptbit = (mrt->flags & MRTF_RP);
2778: if (local_rptbit) {
2779: /* Make the RPT bit the most significant one */
2780: local_preference |= PIM_ASSERT_RPT_BIT;
2781: }
2782:
2783: if (VIFM_ISSET(vifi, mrt->oifs)) {
2784: /* The ASSERT has arrived on oif */
2785:
2786: /* TODO: XXX: here the processing order is different from the spec.
2787: * The spec requires first eventually to create a routing entry
2788: * (see 3.5.2.1(1) and then compare the metrics. Here we compare
2789: * first the metrics with the existing longest match entry and
2790: * if we lose then create a new entry and compare again. This saves
2791: * us the unnecessary creating of a routing entry if we anyway are
2792: * going to lose: for example the local (*,*,RP) vs the remote
2793: * (*,*,RP) or (*,G)
2794: */
2795:
2796: local_wins = compare_metrics(local_preference, local_metric,
2797: v->uv_lcl_addr, assert_preference,
2798: assert_metric, src);
2799:
2800: if (local_wins == TRUE) {
2801: /* TODO: verify the parameters */
2802: send_pim_assert(source, group, vifi, mrt);
2803: return TRUE;
2804: }
2805:
2806: /* Create a "better" routing entry and try again */
2807: if (assert_rptbit && (mrt->flags & MRTF_PMBR)) {
2808: /* The matching entry was (*,*,RP). Create (*,G) */
2809: mrt2 = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
2810: } else if (!assert_rptbit && (mrt->flags & (MRTF_WC | MRTF_PMBR))) {
2811: /* create (S,G) */
2812: mrt2 = find_route(source, group, MRTF_SG, CREATE);
2813: } else {
2814: /* We have no chance to win. Give up and prune the oif */
2815: mrt2 = NULL;
2816: }
2817:
2818: if (mrt2 && (mrt2->flags & MRTF_NEW)) {
2819: mrt2->flags &= ~MRTF_NEW;
2820: /* TODO: XXX: The spec doesn't say what entry timer value
2821: * to use when the routing entry is created because of asserts.
2822: */
2823: SET_TIMER(mrt2->timer, PIM_DATA_TIMEOUT);
2824: if (mrt2->flags & MRTF_RP) {
2825: /* Either (*,G) or (S,G)RPbit entry.
2826: * Get what we need from the RP info.
2827: */
2828: /* TODO: where to get the metric+preference from? */
2829: /*
2830: local_metric =
2831: mrt->group->active_rp_grp->rp->rpentry->metric;
2832: local_preference =
2833: mrt->group->active_rp_grp->rp->rpentry->preference;
2834: */
2835: local_metric = mrt->metric;
2836: local_preference = mrt->preference;
2837: local_preference |= PIM_ASSERT_RPT_BIT;
2838: } else {
2839: /* (S,G) toward the source */
2840: /* TODO: where to get the metric from ? */
2841: /*
2842: local_metric = mrt->source->metric;
2843: local_preference = mrt->source->preference;
2844: */
2845: local_metric = mrt->metric;
2846: local_preference = mrt->preference;
2847: }
2848:
2849: local_wins = compare_metrics(local_preference, local_metric,
2850: v->uv_lcl_addr, assert_preference,
2851: assert_metric, src);
2852: if (local_wins == TRUE) {
2853: /* TODO: verify the parameters */
2854: send_pim_assert(source, group, vifi, mrt);
2855: return TRUE;
2856: }
2857: /* We lost, but have created the entry which has to be pruned */
2858: mrt = mrt2;
2859: }
2860:
2861: /* Have to remove that outgoing vifi from mrt */
2862: VIFM_SET(vifi, mrt->asserted_oifs);
2863: mrt->flags |= MRTF_ASSERTED;
2864: if (mrt->assert_timer < PIM_ASSERT_TIMEOUT)
2865: SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT);
2866:
2867: /* TODO: XXX: check that the timer of all affected routing entries
2868: * has been restarted.
2869: */
2870: change_interfaces(mrt,
2871: mrt->incoming,
2872: mrt->joined_oifs,
2873: mrt->pruned_oifs,
2874: mrt->leaves,
2875: mrt->asserted_oifs, 0);
2876:
2877: return FALSE; /* Doesn't matter the return value */
2878: } /* End of assert received on oif */
2879:
2880:
2881: if (mrt->incoming == vifi) {
2882: /* Assert received on iif */
2883: if (assert_rptbit) {
2884: if (!(mrt->flags & MRTF_RP))
2885: return TRUE; /* The locally used upstream router will
2886: * win the assert, so don't change it.
2887: */
2888: }
2889:
2890: /* Ignore assert message if we do not have an upstream router */
2891: if (mrt->upstream == NULL)
2892: return FALSE;
2893:
2894: /* TODO: where to get the local metric and preference from?
2895: * system call or mrt is fine?
2896: */
2897: local_metric = mrt->metric;
2898: local_preference = mrt->preference;
2899: if (mrt->flags & MRTF_RP)
2900: local_preference |= PIM_ASSERT_RPT_BIT;
2901:
2902: local_wins = compare_metrics(local_preference, local_metric,
2903: mrt->upstream->address,
2904: assert_preference, assert_metric, src);
2905:
2906: if (local_wins == TRUE)
2907: return TRUE; /* return whatever */
2908:
2909: /* The upstream must be changed to the winner */
2910: mrt->preference = assert_preference;
2911: mrt->metric = assert_metric;
2912: mrt->upstream = find_pim_nbr(src);
2913:
2914: /* Check if the upstream router is different from the original one */
2915: if (mrt->flags & MRTF_PMBR) {
2916: original_upstream_router = mrt->source->upstream;
2917: } else {
2918: if (mrt->flags & MRTF_RP)
2919: original_upstream_router = mrt->group->active_rp_grp->rp->rpentry->upstream;
2920: else
2921: original_upstream_router = mrt->source->upstream;
2922: }
2923:
2924: if (mrt->upstream != original_upstream_router) {
2925: mrt->flags |= MRTF_ASSERTED;
2926: SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT);
2927: } else {
2928: mrt->flags &= ~MRTF_ASSERTED;
2929: }
2930: }
2931:
2932: return TRUE;
2933: }
2934:
2935:
2936: int send_pim_assert(uint32_t source, uint32_t group, vifi_t vifi, mrtentry_t *mrt)
2937: {
2938: uint8_t *data;
2939: uint8_t *data_start;
2940: uint32_t local_preference;
2941: uint32_t local_metric;
2942: srcentry_t *srcentry __attribute__((unused));
2943:
2944: /* Don't send assert if the outgoing interface a tunnel or register vif */
2945: /* TODO: XXX: in the code above asserts are accepted over VIFF_TUNNEL.
2946: * Check if anything can go wrong if asserts are accepted and/or
2947: * sent over VIFF_TUNNEL.
2948: */
2949: if (uvifs[vifi].uv_flags & (VIFF_REGISTER | VIFF_TUNNEL))
2950: return FALSE;
2951:
2952: data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t));
2953: data_start = data;
2954: PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data);
2955: PUT_EUADDR(source, data);
2956:
2957: /* TODO: XXX: where to get the metric from: srcentry or mrt
2958: * or from the kernel?
2959: */
2960: if (mrt->flags & MRTF_PMBR) {
2961: /* (*,*,RP) */
2962: srcentry = mrt->source;
2963: /* TODO:
2964: set_incoming(srcentry, PIM_IIF_RP);
2965: */
2966: } else if (mrt->flags & MRTF_RP) {
2967: /* (*,G) or (S,G)RPbit (iif toward RP) */
2968: srcentry = mrt->group->active_rp_grp->rp->rpentry;
2969: /* TODO:
2970: set_incoming(srcentry, PIM_IIF_RP);
2971: */
2972: } else {
2973: /* (S,G) toward S */
2974: srcentry = mrt->source;
2975: /* TODO:
2976: set_incoming(srcentry, PIM_IIF_SOURCE);
2977: */
2978: }
2979:
2980: /* TODO: check again!
2981: local_metric = srcentry->metric;
2982: local_preference = srcentry->preference;
2983: */
2984: local_metric = mrt->metric;
2985: local_preference = mrt->preference;
2986:
2987: if (mrt->flags & MRTF_RP)
2988: local_preference |= PIM_ASSERT_RPT_BIT;
2989: PUT_HOSTLONG(local_preference, data);
2990: PUT_HOSTLONG(local_metric, data);
2991:
2992: logit(LOG_INFO, 0, "Send PIM ASSERT from %s for group %s and source %s",
2993: inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)),
2994: inet_fmt(group, s2, sizeof(s2)),
2995: inet_fmt(source, s3, sizeof(s3)));
2996:
2997: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
2998: PIM_ASSERT, data - data_start);
2999:
3000: return TRUE;
3001: }
3002:
3003:
3004: /* Return TRUE if the local win, otherwise FALSE */
3005: static int compare_metrics(uint32_t local_preference, uint32_t local_metric, uint32_t local_address,
3006: uint32_t remote_preference, uint32_t remote_metric, uint32_t remote_address)
3007: {
3008: /* Now lets see who has a smaller gun (aka "asserts war") */
3009: /* FYI, the smaller gun...err metric wins, but if the same
3010: * caliber, then the bigger network address wins. The order of
3011: * threatment is: preference, metric, address.
3012: */
3013: /* The RPT bits are already included as the most significant bits
3014: * of the preferences.
3015: */
3016: if (remote_preference > local_preference)
3017: return TRUE;
3018:
3019: if (remote_preference < local_preference)
3020: return FALSE;
3021:
3022: if (remote_metric > local_metric)
3023: return TRUE;
3024:
3025: if (remote_metric < local_metric)
3026: return FALSE;
3027:
3028: if (ntohl(local_address) > ntohl(remote_address))
3029: return TRUE;
3030:
3031: return FALSE;
3032: }
3033:
3034:
3035: /************************************************************************
3036: * PIM_BOOTSTRAP
3037: ************************************************************************/
3038: #define PIM_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN)
3039: int receive_pim_bootstrap(uint32_t src, uint32_t dst, char *msg, size_t len)
3040: {
3041: uint8_t *data;
3042: uint8_t *max_data;
3043: uint16_t new_bsr_fragment_tag;
3044: uint8_t new_bsr_hash_masklen;
3045: uint8_t new_bsr_priority;
3046: pim_encod_uni_addr_t new_bsr_uni_addr;
3047: uint32_t new_bsr_address;
3048: struct rpfctl rpfc;
3049: pim_nbr_entry_t *n, *rpf_neighbor __attribute__((unused));
3050: uint32_t neighbor_addr;
3051: vifi_t vifi, incoming = NO_VIF;
3052: int min_datalen;
3053: pim_encod_grp_addr_t curr_group_addr;
3054: pim_encod_uni_addr_t curr_rp_addr;
3055: uint8_t curr_rp_count;
3056: uint8_t curr_frag_rp_count;
3057: uint16_t reserved_short __attribute__((unused));
3058: uint16_t curr_rp_holdtime;
3059: uint8_t curr_rp_priority;
3060: uint8_t reserved_byte __attribute__((unused));
3061: uint32_t curr_group_mask;
3062: uint32_t prefix_h;
3063: grp_mask_t *grp_mask;
3064: grp_mask_t *grp_mask_next;
3065: rp_grp_entry_t *grp_rp;
3066: rp_grp_entry_t *grp_rp_next;
3067:
3068: /* Checksum */
3069: if (inet_cksum((uint16_t *)msg, len))
3070: return FALSE;
3071:
3072: if (find_vif_direct(src) == NO_VIF) {
3073: /* Either a local vif or somehow received PIM_BOOTSTRAP from
3074: * non-directly connected router. Ignore it.
3075: */
3076: if (local_address(src) == NO_VIF)
3077: logit(LOG_DEBUG, 0, "Ignoring PIM_BOOTSTRAP from non-neighbor router %s",
3078: inet_fmt(src, s1, sizeof(s1)));
3079:
3080: return FALSE;
3081: }
3082:
3083: /* sanity check for the minimum length */
3084: if (len < PIM_BOOTSTRAP_MINLEN) {
3085: logit(LOG_NOTICE, 0, "receive_pim_bootstrap: Bootstrap message size(%u) is too short from %s",
3086: len, inet_fmt(src, s1, sizeof(s1)));
3087:
3088: return FALSE;
3089: }
3090:
3091: data = (uint8_t *)(msg + sizeof(pim_header_t));
3092:
3093: /* Parse the PIM_BOOTSTRAP message */
3094: GET_HOSTSHORT(new_bsr_fragment_tag, data);
3095: GET_BYTE(new_bsr_hash_masklen, data);
3096: GET_BYTE(new_bsr_priority, data);
3097: GET_EUADDR(&new_bsr_uni_addr, data);
3098: new_bsr_address = new_bsr_uni_addr.unicast_addr;
3099:
3100: if (local_address(new_bsr_address) != NO_VIF)
3101: return FALSE; /* The new BSR is one of my local addresses */
3102:
3103: /*
3104: * Compare the current BSR priority with the priority of the BSR
3105: * included in the message.
3106: */
3107: /* TODO: if I am just starting and will become the BSR,
3108: * I should accept the message coming from the current BSR and get the
3109: * current Cand-RP-Set.
3110: */
3111: if ((curr_bsr_priority > new_bsr_priority) ||
3112: ((curr_bsr_priority == new_bsr_priority)
3113: && (ntohl(curr_bsr_address) > ntohl(new_bsr_address)))) {
3114: /* The message's BSR is less preferred than the current BSR */
3115: return FALSE; /* Ignore the received BSR message */
3116: }
3117:
3118: logit(LOG_INFO, 0, "Received PIM Bootstrap candidate %s, priority %d",
3119: inet_fmt(new_bsr_address, s1, sizeof(s1)), new_bsr_priority);
3120:
3121: /* Check the iif, if this was PIM-ROUTERS multicast */
3122: if (dst == allpimrouters_group) {
3123: k_req_incoming(new_bsr_address, &rpfc);
3124: if (rpfc.iif == NO_VIF || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) {
3125: /* coudn't find a route to the BSR */
3126: return FALSE;
3127: }
3128:
3129: neighbor_addr = rpfc.rpfneighbor.s_addr;
3130: incoming = rpfc.iif;
3131: if (uvifs[incoming].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
3132: return FALSE; /* Shoudn't arrive on that interface */
3133:
3134: /* Find the upstream router */
3135: for (n = uvifs[incoming].uv_pim_neighbors; n; n = n->next) {
3136: if (ntohl(neighbor_addr) < ntohl(n->address))
3137: continue;
3138:
3139: if (neighbor_addr == n->address) {
3140: rpf_neighbor = n;
3141: break;
3142: }
3143:
3144: return FALSE; /* No neighbor toward BSR found */
3145: }
3146:
3147: if (!n || n->address != src)
3148: return FALSE; /* Sender of this message is not the RPF neighbor */
3149:
3150: } else {
3151: if (local_address(dst) == NO_VIF) {
3152: /* TODO: XXX: this situation should be handled earlier:
3153: * The destination is neither ALL_PIM_ROUTERS neither me
3154: */
3155: return FALSE;
3156: }
3157:
3158: /* Probably unicasted from the current DR */
3159: if (cand_rp_list) {
3160: /* Hmmm, I do have a Cand-RP-list, but some neighbor has a
3161: * different opinion and is unicasting it to me. Ignore this guy.
3162: */
3163: return FALSE;
3164: }
3165:
3166: for (vifi = 0; vifi < numvifs; vifi++) {
3167: if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
3168: continue;
3169:
3170: if (uvifs[vifi].uv_lcl_addr == dst) {
3171: incoming = vifi;
3172: break;
3173: }
3174: }
3175:
3176: if (incoming == NO_VIF) {
3177: /* Cannot find the receiving iif toward that DR */
3178: IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
3179: logit(LOG_DEBUG, 0, "Unicast boostrap message from %s to ignored: cannot find iif",
3180: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
3181:
3182: return FALSE;
3183: }
3184: /* TODO: check the sender is directly connected and I am really the DR */
3185: }
3186:
3187: if (cand_rp_flag == TRUE) {
3188: /* If change in the BSR address, schedule immediate Cand-RP-Adv */
3189: /* TODO: use some random delay? */
3190: if (new_bsr_address != curr_bsr_address)
3191: SET_TIMER(pim_cand_rp_adv_timer, 0);
3192: }
3193:
3194: /* Forward the BSR Message first and then update the RP-set list */
3195: /* TODO: if the message was unicasted to me, resend? */
3196: for (vifi = 0; vifi < numvifs; vifi++) {
3197: if (vifi == incoming)
3198: continue;
3199:
3200: if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_NONBRS))
3201: continue;
3202:
3203: memcpy(pim_send_buf + sizeof(struct ip), msg, len);
3204: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
3205: PIM_BOOTSTRAP, len - sizeof(pim_header_t));
3206: }
3207:
3208: max_data = (uint8_t *)msg + len;
3209: /* TODO: XXX: this 22 is HARDCODING!!! Do a bunch of definitions
3210: * and make it stylish!
3211: */
3212: min_datalen = 22;
3213:
3214: if (new_bsr_fragment_tag != curr_bsr_fragment_tag || new_bsr_address != curr_bsr_address) {
3215: /* Throw away the old segment */
3216: delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
3217: }
3218:
3219: curr_bsr_address = new_bsr_address;
3220: curr_bsr_priority = new_bsr_priority;
3221: curr_bsr_fragment_tag = new_bsr_fragment_tag;
3222: MASKLEN_TO_MASK(new_bsr_hash_masklen, curr_bsr_hash_mask);
3223: SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
3224:
3225: while (data + min_datalen <= max_data) {
3226: GET_EGADDR(&curr_group_addr, data);
3227: GET_BYTE(curr_rp_count, data);
3228: GET_BYTE(curr_frag_rp_count, data);
3229: GET_HOSTSHORT(reserved_short, data);
3230: MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
3231: if (curr_rp_count == 0) {
3232: delete_grp_mask(&cand_rp_list, &grp_mask_list,
3233: curr_group_addr.mcast_addr, curr_group_mask);
3234: continue;
3235: }
3236:
3237: if (curr_rp_count == curr_frag_rp_count) {
3238: /* Add all RPs */
3239: while (curr_frag_rp_count--) {
3240: GET_EUADDR(&curr_rp_addr, data);
3241: GET_HOSTSHORT(curr_rp_holdtime, data);
3242: GET_BYTE(curr_rp_priority, data);
3243: GET_BYTE(reserved_byte, data);
3244: MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
3245: add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
3246: curr_rp_addr.unicast_addr, curr_rp_priority,
3247: curr_rp_holdtime, curr_group_addr.mcast_addr,
3248: curr_group_mask,
3249: curr_bsr_hash_mask,
3250: curr_bsr_fragment_tag);
3251: }
3252: continue;
3253: }
3254:
3255: /*
3256: * This is a partial list of the RPs for this group prefix.
3257: * Save until all segments arrive.
3258: */
3259: prefix_h = ntohl(curr_group_addr.mcast_addr & curr_group_mask);
3260: for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask->next) {
3261: if (ntohl(grp_mask->group_addr & grp_mask->group_mask) > prefix_h)
3262: continue;
3263:
3264: break;
3265: }
3266:
3267: if (grp_mask
3268: && (grp_mask->group_addr == curr_group_addr.mcast_addr)
3269: && (grp_mask->group_mask == curr_group_mask)
3270: && (grp_mask->group_rp_number + curr_frag_rp_count == curr_rp_count)) {
3271: /* All missing PRs have arrived. Add all RP entries */
3272: while (curr_frag_rp_count--) {
3273: GET_EUADDR(&curr_rp_addr, data);
3274: GET_HOSTSHORT(curr_rp_holdtime, data);
3275: GET_BYTE(curr_rp_priority, data);
3276: GET_BYTE(reserved_byte, data);
3277: MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
3278: add_rp_grp_entry(&cand_rp_list,
3279: &grp_mask_list,
3280: curr_rp_addr.unicast_addr,
3281: curr_rp_priority,
3282: curr_rp_holdtime,
3283: curr_group_addr.mcast_addr,
3284: curr_group_mask,
3285: curr_bsr_hash_mask,
3286: curr_bsr_fragment_tag);
3287: }
3288:
3289: /* Add the rest from the previously saved segments */
3290: for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp->grp_rp_next) {
3291: add_rp_grp_entry(&cand_rp_list,
3292: &grp_mask_list,
3293: grp_rp->rp->rpentry->address,
3294: grp_rp->priority,
3295: grp_rp->holdtime,
3296: curr_group_addr.mcast_addr,
3297: curr_group_mask,
3298: curr_bsr_hash_mask,
3299: curr_bsr_fragment_tag);
3300: }
3301: delete_grp_mask(&segmented_cand_rp_list,
3302: &segmented_grp_mask_list,
3303: curr_group_addr.mcast_addr,
3304: curr_group_mask);
3305: } else {
3306: /* Add the partially received RP-list to the group of pending RPs*/
3307: while (curr_frag_rp_count--) {
3308: GET_EUADDR(&curr_rp_addr, data);
3309: GET_HOSTSHORT(curr_rp_holdtime, data);
3310: GET_BYTE(curr_rp_priority, data);
3311: GET_BYTE(reserved_byte, data);
3312: MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
3313: add_rp_grp_entry(&segmented_cand_rp_list,
3314: &segmented_grp_mask_list,
3315: curr_rp_addr.unicast_addr,
3316: curr_rp_priority,
3317: curr_rp_holdtime,
3318: curr_group_addr.mcast_addr,
3319: curr_group_mask,
3320: curr_bsr_hash_mask,
3321: curr_bsr_fragment_tag);
3322: }
3323: }
3324: }
3325:
3326: /* Garbage collection. Check all group prefixes and if the
3327: * fragment_tag for a group-prefix is the same as curr_bsr_fragment_tag,
3328: * then remove all RPs for this group-prefix which have different
3329: * fragment tag.
3330: */
3331: for (grp_mask = grp_mask_list; grp_mask; grp_mask = grp_mask_next) {
3332: grp_mask_next = grp_mask->next;
3333:
3334: if (grp_mask->fragment_tag == curr_bsr_fragment_tag) {
3335: for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) {
3336: grp_rp_next = grp_rp->grp_rp_next;
3337:
3338: if (grp_rp->fragment_tag != curr_bsr_fragment_tag)
3339: delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp);
3340: }
3341: }
3342: }
3343:
3344: /* Cleanup also the list used by incompleted segments */
3345: for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask_next) {
3346: grp_mask_next = grp_mask->next;
3347:
3348: if (grp_mask->fragment_tag == curr_bsr_fragment_tag) {
3349: for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) {
3350: grp_rp_next = grp_rp->grp_rp_next;
3351:
3352: if (grp_rp->fragment_tag != curr_bsr_fragment_tag)
3353: delete_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, grp_rp);
3354: }
3355: }
3356: }
3357:
3358: return TRUE;
3359: }
3360:
3361:
3362: void send_pim_bootstrap(void)
3363: {
3364: size_t len;
3365: vifi_t vifi;
3366:
3367: if ((len = create_pim_bootstrap_message(pim_send_buf))) {
3368: for (vifi = 0; vifi < numvifs; vifi++) {
3369: if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
3370: continue;
3371:
3372: send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr,
3373: allpimrouters_group, PIM_BOOTSTRAP, len);
3374: }
3375: }
3376: }
3377:
3378:
3379: /************************************************************************
3380: * PIM_CAND_RP_ADV
3381: ************************************************************************/
3382: /*
3383: * If I am the Bootstrap router, process the advertisement, otherwise
3384: * ignore it.
3385: */
3386: #define PIM_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN)
3387: int receive_pim_cand_rp_adv(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
3388: {
3389: uint8_t prefix_cnt;
3390: uint8_t priority;
3391: uint16_t holdtime;
3392: pim_encod_uni_addr_t euaddr;
3393: pim_encod_grp_addr_t egaddr;
3394: uint8_t *data_ptr;
3395: uint32_t grp_mask;
3396:
3397: /* Checksum */
3398: if (inet_cksum((uint16_t *)msg, len))
3399: return FALSE;
3400:
3401: /* if I am not the bootstrap RP, then do not accept the message */
3402: if (cand_bsr_flag == FALSE || curr_bsr_address != my_bsr_address)
3403: return FALSE;
3404:
3405: /* sanity check for the minimum length */
3406: if (len < PIM_CAND_RP_ADV_MINLEN) {
3407: logit(LOG_NOTICE, 0, "%s(): cand_RP message size(%u) is too short from %s",
3408: __func__, len, inet_fmt(src, s1, sizeof(s1)));
3409:
3410: return FALSE;
3411: }
3412:
3413: data_ptr = (uint8_t *)(msg + sizeof(pim_header_t));
3414: /* Parse the CAND_RP_ADV message */
3415: /* TODO: XXX: check len whether it is at least the minimum */
3416: GET_BYTE(prefix_cnt, data_ptr);
3417: GET_BYTE(priority, data_ptr);
3418: GET_HOSTSHORT(holdtime, data_ptr);
3419: GET_EUADDR(&euaddr, data_ptr);
3420: if (prefix_cnt == 0) {
3421: /* The default 224.0.0.0 and masklen of 4 */
3422: MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, grp_mask);
3423: add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
3424: euaddr.unicast_addr, priority, holdtime,
3425: htonl(ALL_MCAST_GROUPS_ADDR), grp_mask,
3426: my_bsr_hash_mask,
3427: curr_bsr_fragment_tag);
3428:
3429: return TRUE;
3430: }
3431:
3432: while (prefix_cnt--) {
3433: GET_EGADDR(&egaddr, data_ptr);
3434: MASKLEN_TO_MASK(egaddr.masklen, grp_mask);
3435: /* Do not advertise internal virtual RP for SSM groups */
3436: if (!IN_PIM_SSM_RANGE(egaddr.mcast_addr)) {
3437: add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
3438: euaddr.unicast_addr, priority, holdtime,
3439: egaddr.mcast_addr, grp_mask,
3440: my_bsr_hash_mask,
3441: curr_bsr_fragment_tag);
3442: }
3443: /* TODO: Check for len */
3444: }
3445:
3446: return TRUE;
3447: }
3448:
3449:
3450: int send_pim_cand_rp_adv(void)
3451: {
3452: uint8_t prefix_cnt;
3453: uint32_t mask;
3454: pim_encod_grp_addr_t addr;
3455: uint8_t *data;
3456:
3457: if (!inet_valid_host(curr_bsr_address))
3458: return FALSE; /* No BSR yet */
3459:
3460: if (curr_bsr_address == my_bsr_address) {
3461: /* I am the BSR and have to include my own group-prefix stuff */
3462: prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr;
3463: if (prefix_cnt == 0) {
3464: /* The default 224.0.0.0 and masklen of 4 */
3465: MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, mask);
3466: add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
3467: my_cand_rp_address, my_cand_rp_priority,
3468: my_cand_rp_holdtime,
3469: htonl(ALL_MCAST_GROUPS_ADDR), mask,
3470: my_bsr_hash_mask,
3471: curr_bsr_fragment_tag);
3472: return TRUE;
3473: }
3474:
3475: /* TODO: hardcoding!! */
3476: data = cand_rp_adv_message.buffer + (4 + 6);
3477: while (prefix_cnt--) {
3478: GET_EGADDR(&addr, data);
3479: MASKLEN_TO_MASK(addr.masklen, mask);
3480: add_rp_grp_entry(&cand_rp_list,
3481: &grp_mask_list,
3482: my_cand_rp_address, my_cand_rp_priority,
3483: my_cand_rp_holdtime,
3484: addr.mcast_addr, mask,
3485: my_bsr_hash_mask,
3486: curr_bsr_fragment_tag);
3487: /* TODO: Check for len */
3488: }
3489:
3490: return TRUE;
3491: }
3492:
3493: data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t));
3494: memcpy(data, cand_rp_adv_message.buffer, cand_rp_adv_message.message_size);
3495: send_pim_unicast(pim_send_buf, 0, my_cand_rp_address, curr_bsr_address,
3496: PIM_CAND_RP_ADV, cand_rp_adv_message.message_size);
3497:
3498: return TRUE;
3499: }
3500:
3501: /**
3502: * Local Variables:
3503: * version-control: t
3504: * indent-tabs-mode: t
3505: * c-file-style: "ellemtel"
3506: * c-basic-offset: 4
3507: * End:
3508: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>