xref: /netbsd-src/usr.sbin/mrouted/prune.c (revision 58cb611abb8097a2b7084638a1a04f2f1504424d)
1*58cb611aSlukem /*	$NetBSD: prune.c,v 1.18 2009/04/17 16:05:43 lukem Exp $	*/
2c60d41a9Swiz 
3c60d41a9Swiz /*
4c60d41a9Swiz  * The mrouted program is covered by the license in the accompanying file
5c60d41a9Swiz  * named "LICENSE".  Use of the mrouted program represents acceptance of
6c60d41a9Swiz  * the terms and conditions listed in that file.
7c60d41a9Swiz  *
8c60d41a9Swiz  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9c60d41a9Swiz  * Leland Stanford Junior University.
10c60d41a9Swiz  */
11c60d41a9Swiz 
12c60d41a9Swiz 
13c60d41a9Swiz #include "defs.h"
14c60d41a9Swiz 
15c60d41a9Swiz extern int cache_lifetime;
16c60d41a9Swiz extern int max_prune_lifetime;
17c60d41a9Swiz extern struct rtentry *routing_table;
18c60d41a9Swiz 
19c60d41a9Swiz extern int phys_vif;
20c60d41a9Swiz 
21c60d41a9Swiz /*
22c60d41a9Swiz  * dither cache lifetime to obtain a value between x and 2*x
23c60d41a9Swiz  */
24e2c411ceSitojun #define CACHE_LIFETIME(x) ((x) + (arc4random() % (x)))
25c60d41a9Swiz 
26c60d41a9Swiz #define CHK_GS(x, y) {	\
27c60d41a9Swiz 		switch(x) { \
28c60d41a9Swiz 			case 2:	\
29c60d41a9Swiz 			case 4:	\
30c60d41a9Swiz 			case 8:	\
31c60d41a9Swiz 			case 16: \
32c60d41a9Swiz 			case 32: \
33c60d41a9Swiz 			case 64: \
34c60d41a9Swiz 			case 128: \
353479c0a6Smrg 			/*case 256:*/ y = 1; \
36c60d41a9Swiz 				  break; \
37c60d41a9Swiz 			default:  y = 0; \
38c60d41a9Swiz 		} \
39c60d41a9Swiz 	}
40c60d41a9Swiz 
41c60d41a9Swiz struct gtable *kernel_table;		/* ptr to list of kernel grp entries*/
42c60d41a9Swiz static struct gtable *kernel_no_route;	/* list of grp entries w/o routes   */
43c60d41a9Swiz struct gtable *gtp;			/* pointer for kernel rt entries    */
44c60d41a9Swiz unsigned int kroutes;			/* current number of cache entries  */
45c60d41a9Swiz 
46c60d41a9Swiz /****************************************************************************
47c60d41a9Swiz                        Functions that are local to prune.c
48c60d41a9Swiz ****************************************************************************/
49c60d41a9Swiz static void		prun_add_ttls(struct gtable *gt);
50c60d41a9Swiz static int		pruning_neighbor(vifi_t vifi, u_int32_t addr);
51c60d41a9Swiz static int		can_mtrace(vifi_t vifi, u_int32_t addr);
52c60d41a9Swiz static struct ptable *	find_prune_entry(u_int32_t vr, struct ptable *pt);
53c60d41a9Swiz static void		expire_prune(vifi_t vifi, struct gtable *gt);
54c60d41a9Swiz static void		send_prune(struct gtable *gt);
55c60d41a9Swiz static void		send_graft(struct gtable *gt);
56c60d41a9Swiz static void		send_graft_ack(u_int32_t src, u_int32_t dst,
57c60d41a9Swiz 				       u_int32_t origin, u_int32_t grp);
58c60d41a9Swiz static void		update_kernel(struct gtable *g);
59*58cb611aSlukem static const char *	scaletime(u_long t);
60c60d41a9Swiz 
61c60d41a9Swiz /*
62c60d41a9Swiz  * Updates the ttl values for each vif.
63c60d41a9Swiz  */
64c60d41a9Swiz static void
prun_add_ttls(struct gtable * gt)65c60d41a9Swiz prun_add_ttls(struct gtable *gt)
66c60d41a9Swiz {
67c60d41a9Swiz     struct uvif *v;
68c60d41a9Swiz     vifi_t vifi;
69c60d41a9Swiz 
70c60d41a9Swiz     for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
71c60d41a9Swiz 	if (VIFM_ISSET(vifi, gt->gt_grpmems))
72c60d41a9Swiz 	    gt->gt_ttls[vifi] = v->uv_threshold;
73c60d41a9Swiz 	else
74c60d41a9Swiz 	    gt->gt_ttls[vifi] = 0;
75c60d41a9Swiz     }
76c60d41a9Swiz }
77c60d41a9Swiz 
78c60d41a9Swiz /*
79c60d41a9Swiz  * checks for scoped multicast addresses
80c60d41a9Swiz  */
81c60d41a9Swiz #define GET_SCOPE(gt) { \
82c60d41a9Swiz 	vifi_t _i; \
83c60d41a9Swiz 	if ((ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \
84c60d41a9Swiz 	    for (_i = 0; _i < numvifs; _i++) \
85c60d41a9Swiz 		if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
86c60d41a9Swiz 		    VIFM_SET(_i, (gt)->gt_scope); \
87c60d41a9Swiz 	}
88c60d41a9Swiz 
89c60d41a9Swiz int
scoped_addr(vifi_t vifi,u_int32_t addr)90c60d41a9Swiz scoped_addr(vifi_t vifi, u_int32_t addr)
91c60d41a9Swiz {
92c60d41a9Swiz     struct vif_acl *acl;
93c60d41a9Swiz 
94c60d41a9Swiz     for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next)
95c60d41a9Swiz 	if ((addr & acl->acl_mask) == acl->acl_addr)
96c60d41a9Swiz 	    return 1;
97c60d41a9Swiz 
98c60d41a9Swiz     return 0;
99c60d41a9Swiz }
100c60d41a9Swiz 
101c60d41a9Swiz /*
102c60d41a9Swiz  * Determine if mcastgrp has a listener on vifi
103c60d41a9Swiz  */
104c60d41a9Swiz int
grplst_mem(vifi_t vifi,u_int32_t mcastgrp)105c60d41a9Swiz grplst_mem(vifi_t vifi, u_int32_t mcastgrp)
106c60d41a9Swiz {
107c60d41a9Swiz     struct listaddr *g;
108c60d41a9Swiz     struct uvif *v;
109c60d41a9Swiz 
110c60d41a9Swiz     v = &uvifs[vifi];
111c60d41a9Swiz 
112c60d41a9Swiz     for (g = v->uv_groups; g != NULL; g = g->al_next)
113c60d41a9Swiz 	if (mcastgrp == g->al_addr)
114c60d41a9Swiz 	    return 1;
115c60d41a9Swiz 
116c60d41a9Swiz     return 0;
117c60d41a9Swiz }
118c60d41a9Swiz 
119c60d41a9Swiz /*
120c60d41a9Swiz  * Finds the group entry with the specified source and netmask.
121c60d41a9Swiz  * If netmask is 0, it uses the route's netmask.
122c60d41a9Swiz  *
123c60d41a9Swiz  * Returns TRUE if found a match, and the global variable gtp is left
124c60d41a9Swiz  * pointing to entry before the found entry.
125c60d41a9Swiz  * Returns FALSE if no exact match found, gtp is left pointing to before
126c60d41a9Swiz  * the entry in question belongs, or is NULL if the it belongs at the
127c60d41a9Swiz  * head of the list.
128c60d41a9Swiz  */
129c60d41a9Swiz int
find_src_grp(u_int32_t src,u_int32_t mask,u_int32_t grp)130c60d41a9Swiz find_src_grp(u_int32_t src, u_int32_t mask, u_int32_t grp)
131c60d41a9Swiz {
132c60d41a9Swiz     struct gtable *gt;
133c60d41a9Swiz 
134c60d41a9Swiz     gtp = NULL;
135c60d41a9Swiz     gt = kernel_table;
136c60d41a9Swiz     while (gt != NULL) {
137c60d41a9Swiz 	if (grp == gt->gt_mcastgrp &&
138c60d41a9Swiz 	    (mask ? (gt->gt_route->rt_origin == src &&
139c60d41a9Swiz 		     gt->gt_route->rt_originmask == mask) :
140c60d41a9Swiz 		    ((src & gt->gt_route->rt_originmask) ==
141c60d41a9Swiz 		     gt->gt_route->rt_origin)))
142c60d41a9Swiz 	    return TRUE;
143c60d41a9Swiz 	if (ntohl(grp) > ntohl(gt->gt_mcastgrp) ||
144c60d41a9Swiz 	    (grp == gt->gt_mcastgrp &&
145c60d41a9Swiz 	     (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) ||
146c60d41a9Swiz 	      (mask == gt->gt_route->rt_originmask &&
147c60d41a9Swiz 	       (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) {
148c60d41a9Swiz 	    gtp = gt;
149c60d41a9Swiz 	    gt = gt->gt_gnext;
150c60d41a9Swiz 	}
151c60d41a9Swiz 	else break;
152c60d41a9Swiz     }
153c60d41a9Swiz     return FALSE;
154c60d41a9Swiz }
155c60d41a9Swiz 
156c60d41a9Swiz /*
157c60d41a9Swiz  * Check if the neighbor supports pruning
158c60d41a9Swiz  */
159c60d41a9Swiz static int
pruning_neighbor(vifi_t vifi,u_int32_t addr)160c60d41a9Swiz pruning_neighbor(vifi_t vifi, u_int32_t addr)
161c60d41a9Swiz {
162c60d41a9Swiz     struct listaddr *n = neighbor_info(vifi, addr);
163c60d41a9Swiz     int vers;
164c60d41a9Swiz 
165c60d41a9Swiz     if (n == NULL)
166c60d41a9Swiz 	return 0;
167c60d41a9Swiz 
168c60d41a9Swiz     if (n->al_flags & NF_PRUNE)
169c60d41a9Swiz 	return 1;
170c60d41a9Swiz 
171c60d41a9Swiz     /*
172c60d41a9Swiz      * Versions from 3.0 to 3.4 relied on the version number to identify
173c60d41a9Swiz      * that they could handle pruning.
174c60d41a9Swiz      */
175c60d41a9Swiz     vers = NBR_VERS(n);
176c60d41a9Swiz     return (vers >= 0x0300 && vers <= 0x0304);
177c60d41a9Swiz }
178c60d41a9Swiz 
179c60d41a9Swiz /*
180c60d41a9Swiz  * Can the neighbor in question handle multicast traceroute?
181c60d41a9Swiz  */
182c60d41a9Swiz static int
can_mtrace(vifi_t vifi,u_int32_t addr)183c60d41a9Swiz can_mtrace(vifi_t vifi, u_int32_t addr)
184c60d41a9Swiz {
185c60d41a9Swiz     struct listaddr *n = neighbor_info(vifi, addr);
186c60d41a9Swiz     int vers;
187c60d41a9Swiz 
188c60d41a9Swiz     if (n == NULL)
189c60d41a9Swiz 	return 0;
190c60d41a9Swiz 
191c60d41a9Swiz     if (n->al_flags & NF_MTRACE)
192c60d41a9Swiz 	return 1;
193c60d41a9Swiz 
194c60d41a9Swiz     /*
195c60d41a9Swiz      * Versions 3.3 and 3.4 relied on the version number to identify
196c60d41a9Swiz      * that they could handle traceroute.
197c60d41a9Swiz      */
198c60d41a9Swiz     vers = NBR_VERS(n);
199c60d41a9Swiz     return (vers >= 0x0303 && vers <= 0x0304);
200c60d41a9Swiz }
201c60d41a9Swiz 
202c60d41a9Swiz /*
203c60d41a9Swiz  * Returns the prune entry of the router, or NULL if none exists
204c60d41a9Swiz  */
205c60d41a9Swiz static struct ptable *
find_prune_entry(u_int32_t vr,struct ptable * pt)206c60d41a9Swiz find_prune_entry(u_int32_t vr, struct ptable *pt)
207c60d41a9Swiz {
208c60d41a9Swiz     while (pt) {
209c60d41a9Swiz 	if (pt->pt_router == vr)
210c60d41a9Swiz 	    return pt;
211c60d41a9Swiz 	pt = pt->pt_next;
212c60d41a9Swiz     }
213c60d41a9Swiz 
214c60d41a9Swiz     return NULL;
215c60d41a9Swiz }
216c60d41a9Swiz 
217c60d41a9Swiz /*
218c60d41a9Swiz  * Send a prune message to the dominant router for
219c60d41a9Swiz  * this source.
220c60d41a9Swiz  *
221c60d41a9Swiz  * Record an entry that a prune was sent for this group
222c60d41a9Swiz  */
223c60d41a9Swiz static void
send_prune(struct gtable * gt)224c60d41a9Swiz send_prune(struct gtable *gt)
225c60d41a9Swiz {
226c60d41a9Swiz     struct ptable *pt;
227c60d41a9Swiz     char *p;
228c60d41a9Swiz     int i;
229c60d41a9Swiz     int datalen;
230c60d41a9Swiz     u_int32_t src;
231c60d41a9Swiz     u_int32_t dst;
232c60d41a9Swiz     u_int32_t tmp;
233c60d41a9Swiz 
234c60d41a9Swiz     /* Don't process any prunes if router is not pruning */
235c60d41a9Swiz     if (pruning == 0)
236c60d41a9Swiz 	return;
237c60d41a9Swiz 
238c60d41a9Swiz     /* Can't process a prune if we don't have an associated route */
239c60d41a9Swiz     if (gt->gt_route == NULL)
240c60d41a9Swiz 	return;
241c60d41a9Swiz 
242c60d41a9Swiz     /* Don't send a prune to a non-pruning router */
243c60d41a9Swiz     if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway))
244c60d41a9Swiz 	return;
245c60d41a9Swiz 
246c60d41a9Swiz     /*
247c60d41a9Swiz      * sends a prune message to the router upstream.
248c60d41a9Swiz      */
249c60d41a9Swiz     src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
250c60d41a9Swiz     dst = gt->gt_route->rt_gateway;
251c60d41a9Swiz 
252c60d41a9Swiz     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
253c60d41a9Swiz     datalen = 0;
254c60d41a9Swiz 
255c60d41a9Swiz     /*
256c60d41a9Swiz      * determine prune lifetime
257c60d41a9Swiz      */
258c60d41a9Swiz     gt->gt_prsent_timer = gt->gt_timer;
259c60d41a9Swiz     for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next)
260c60d41a9Swiz 	if (pt->pt_timer < gt->gt_prsent_timer)
261c60d41a9Swiz 	    gt->gt_prsent_timer = pt->pt_timer;
262c60d41a9Swiz 
263c60d41a9Swiz     /*
264c60d41a9Swiz      * If we have a graft pending, cancel graft retransmission
265c60d41a9Swiz      */
266c60d41a9Swiz     gt->gt_grftsnt = 0;
267c60d41a9Swiz 
268c60d41a9Swiz     for (i = 0; i < 4; i++)
269c60d41a9Swiz 	*p++ = ((char *)&(gt->gt_route->rt_origin))[i];
270c60d41a9Swiz     for (i = 0; i < 4; i++)
271c60d41a9Swiz 	*p++ = ((char *)&(gt->gt_mcastgrp))[i];
272c60d41a9Swiz     tmp = htonl(gt->gt_prsent_timer);
273c60d41a9Swiz     for (i = 0; i < 4; i++)
274c60d41a9Swiz 	*p++ = ((char *)&(tmp))[i];
275c60d41a9Swiz     datalen += 12;
276c60d41a9Swiz 
277c60d41a9Swiz     send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE,
278c60d41a9Swiz 	      htonl(MROUTED_LEVEL), datalen);
279c60d41a9Swiz 
280c60d41a9Swiz     logit(LOG_DEBUG, 0, "sent prune for (%s %s)/%d on vif %d to %s",
281ee70b5d6Sdsl       inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask),
282ee70b5d6Sdsl       inet_fmt(gt->gt_mcastgrp),
283c60d41a9Swiz       gt->gt_prsent_timer, gt->gt_route->rt_parent,
284ee70b5d6Sdsl       inet_fmt(gt->gt_route->rt_gateway));
285c60d41a9Swiz }
286c60d41a9Swiz 
287c60d41a9Swiz /*
288c60d41a9Swiz  * a prune was sent upstream
289c60d41a9Swiz  * so, a graft has to be sent to annul the prune
290c60d41a9Swiz  * set up a graft timer so that if an ack is not
291c60d41a9Swiz  * heard within that time, another graft request
292c60d41a9Swiz  * is sent out.
293c60d41a9Swiz  */
294c60d41a9Swiz static void
send_graft(struct gtable * gt)295c60d41a9Swiz send_graft(struct gtable *gt)
296c60d41a9Swiz {
297c60d41a9Swiz     char *p;
298c60d41a9Swiz     int i;
299c60d41a9Swiz     int datalen;
300c60d41a9Swiz     u_int32_t src;
301c60d41a9Swiz     u_int32_t dst;
302c60d41a9Swiz 
303c60d41a9Swiz     /* Can't send a graft without an associated route */
304c60d41a9Swiz     if (gt->gt_route == NULL)
305c60d41a9Swiz 	return;
306c60d41a9Swiz 
307c60d41a9Swiz     src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
308c60d41a9Swiz     dst = gt->gt_route->rt_gateway;
309c60d41a9Swiz 
310c60d41a9Swiz     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
311c60d41a9Swiz     datalen = 0;
312c60d41a9Swiz 
313c60d41a9Swiz     for (i = 0; i < 4; i++)
314c60d41a9Swiz 	*p++ = ((char *)&(gt->gt_route->rt_origin))[i];
315c60d41a9Swiz     for (i = 0; i < 4; i++)
316c60d41a9Swiz 	*p++ = ((char *)&(gt->gt_mcastgrp))[i];
317c60d41a9Swiz     datalen += 8;
318c60d41a9Swiz 
319c60d41a9Swiz     if (datalen != 0) {
320c60d41a9Swiz 	send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
321c60d41a9Swiz 		  htonl(MROUTED_LEVEL), datalen);
322c60d41a9Swiz     }
323c60d41a9Swiz     logit(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d",
324ee70b5d6Sdsl 	inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask),
325ee70b5d6Sdsl 	inet_fmt(gt->gt_mcastgrp),
326ee70b5d6Sdsl 	inet_fmt(gt->gt_route->rt_gateway),
327761f7bebSitojun 	gt->gt_route->rt_parent);
328c60d41a9Swiz }
329c60d41a9Swiz 
330c60d41a9Swiz /*
331c60d41a9Swiz  * Send an ack that a graft was received
332c60d41a9Swiz  */
333c60d41a9Swiz static void
send_graft_ack(u_int32_t src,u_int32_t dst,u_int32_t origin,u_int32_t grp)334c60d41a9Swiz send_graft_ack(u_int32_t src, u_int32_t dst, u_int32_t origin, u_int32_t grp)
335c60d41a9Swiz {
336c60d41a9Swiz     char *p;
337c60d41a9Swiz     int i;
338c60d41a9Swiz     int datalen;
339c60d41a9Swiz 
340c60d41a9Swiz     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
341c60d41a9Swiz     datalen = 0;
342c60d41a9Swiz 
343c60d41a9Swiz     for (i = 0; i < 4; i++)
344c60d41a9Swiz 	*p++ = ((char *)&(origin))[i];
345c60d41a9Swiz     for (i = 0; i < 4; i++)
346c60d41a9Swiz 	*p++ = ((char *)&(grp))[i];
347c60d41a9Swiz     datalen += 8;
348c60d41a9Swiz 
349c60d41a9Swiz     send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
350c60d41a9Swiz 	      htonl(MROUTED_LEVEL), datalen);
351c60d41a9Swiz 
352c60d41a9Swiz     logit(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
353ee70b5d6Sdsl 	inet_fmt(origin), inet_fmt(grp),
354ee70b5d6Sdsl 	inet_fmt(dst));
355c60d41a9Swiz }
356c60d41a9Swiz 
357c60d41a9Swiz /*
358c60d41a9Swiz  * Update the kernel cache with all the routes hanging off the group entry
359c60d41a9Swiz  */
360c60d41a9Swiz static void
update_kernel(struct gtable * g)361c60d41a9Swiz update_kernel(struct gtable *g)
362c60d41a9Swiz {
363c60d41a9Swiz     struct stable *st;
364c60d41a9Swiz 
365c60d41a9Swiz     for (st = g->gt_srctbl; st; st = st->st_next)
366c60d41a9Swiz 	k_add_rg(st->st_origin, g);
367c60d41a9Swiz }
368c60d41a9Swiz 
369c60d41a9Swiz /****************************************************************************
370c60d41a9Swiz                           Functions that are used externally
371c60d41a9Swiz ****************************************************************************/
372c60d41a9Swiz 
373c60d41a9Swiz #ifdef SNMP
374c60d41a9Swiz #include <sys/types.h>
375c60d41a9Swiz #include "snmp.h"
376c60d41a9Swiz 
377c60d41a9Swiz /*
378c60d41a9Swiz  * Find a specific group entry in the group table
379c60d41a9Swiz  */
380c60d41a9Swiz struct gtable *
find_grp(u_long grp)3812dd997ffSwiz find_grp(u_long grp)
382c60d41a9Swiz {
383c60d41a9Swiz    struct gtable *gt;
384c60d41a9Swiz 
385c60d41a9Swiz    for (gt = kernel_table; gt; gt = gt->gt_gnext) {
386c60d41a9Swiz       if (ntohl(grp) < ntohl(gt->gt_mcastgrp))
387c60d41a9Swiz       	 break;
388c60d41a9Swiz       if (gt->gt_mcastgrp == grp)
389c60d41a9Swiz          return gt;
390c60d41a9Swiz    }
391c60d41a9Swiz    return NULL;
392c60d41a9Swiz }
393c60d41a9Swiz 
394c60d41a9Swiz /*
395c60d41a9Swiz  * Given a group entry and source, find the corresponding source table
396c60d41a9Swiz  * entry
397c60d41a9Swiz  */
398c60d41a9Swiz struct stable *
find_grp_src(struct gtable * gt,u_long src)399c60d41a9Swiz find_grp_src(struct gtable *gt, u_long src)
400c60d41a9Swiz {
401c60d41a9Swiz    struct stable *st;
402c60d41a9Swiz    u_long grp = gt->gt_mcastgrp;
403c60d41a9Swiz    struct gtable *gtcurr;
404c60d41a9Swiz 
405c60d41a9Swiz    for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) {
406c60d41a9Swiz       for (st = gtcurr->gt_srctbl; st; st = st->st_next)
407c60d41a9Swiz 	 if (st->st_origin == src)
408c60d41a9Swiz 	    return st;
409c60d41a9Swiz    }
410c60d41a9Swiz    return NULL;
411c60d41a9Swiz }
412c60d41a9Swiz 
413c60d41a9Swiz /*
414c60d41a9Swiz  * Find next entry > specification
415c60d41a9Swiz  *
416c60d41a9Swiz  * gtpp: ordered by group
417c60d41a9Swiz  * stpp: ordered by source
418c60d41a9Swiz  */
419c60d41a9Swiz int
next_grp_src_mask(struct gtable ** gtpp,struct stable ** stpp,u_long grp,u_long src,u_long mask)420c60d41a9Swiz next_grp_src_mask(struct gtable **gtpp, struct stable **stpp, u_long grp,
421c60d41a9Swiz 		  u_long src, u_long mask)
422c60d41a9Swiz {
423c60d41a9Swiz    struct gtable *gt, *gbest = NULL;
424c60d41a9Swiz    struct stable *st, *sbest = NULL;
425c60d41a9Swiz 
426c60d41a9Swiz    /* Find first group entry >= grp spec */
427c60d41a9Swiz    (*gtpp) = kernel_table;
428c60d41a9Swiz    while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp))
429c60d41a9Swiz       (*gtpp)=(*gtpp)->gt_gnext;
430c60d41a9Swiz    if (!(*gtpp))
431c60d41a9Swiz       return 0; /* no more groups */
432c60d41a9Swiz 
433c60d41a9Swiz    for (gt = kernel_table; gt; gt=gt->gt_gnext) {
434c60d41a9Swiz       /* Since grps are ordered, we can stop when group changes from gbest */
435c60d41a9Swiz       if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp)
436c60d41a9Swiz          break;
437c60d41a9Swiz       for (st = gt->gt_srctbl; st; st=st->st_next) {
438c60d41a9Swiz 
439c60d41a9Swiz          /* Among those entries > spec, find "lowest" one */
440c60d41a9Swiz          if (((ntohl(gt->gt_mcastgrp)> ntohl(grp))
441c60d41a9Swiz            || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
442c60d41a9Swiz               && ntohl(st->st_origin)> ntohl(src))
443c60d41a9Swiz            || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
444c60d41a9Swiz               && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask)))
445c60d41a9Swiz           && (!gbest
446c60d41a9Swiz            || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp))
447c60d41a9Swiz            || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp)
448c60d41a9Swiz               && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) {
449c60d41a9Swiz                gbest = gt;
450c60d41a9Swiz                sbest = st;
451c60d41a9Swiz          }
452c60d41a9Swiz       }
453c60d41a9Swiz    }
454c60d41a9Swiz    (*gtpp) = gbest;
455c60d41a9Swiz    (*stpp) = sbest;
456c60d41a9Swiz    return (*gtpp)!=0;
457c60d41a9Swiz }
458c60d41a9Swiz 
459c60d41a9Swiz /*
460c60d41a9Swiz  * Ensure that sg contains current information for the given group,source.
461c60d41a9Swiz  * This is fetched from the kernel as a unit so that counts for the entry
462c60d41a9Swiz  * are consistent, i.e. packet and byte counts for the same entry are
463c60d41a9Swiz  * read at the same time.
464c60d41a9Swiz  */
465c60d41a9Swiz void
refresh_sg(struct sioc_sg_req * sg,struct gtable * gt,struct stable * st)466c60d41a9Swiz refresh_sg(struct sioc_sg_req *sg, struct gtable *gt, struct stable *st)
467c60d41a9Swiz {
468c60d41a9Swiz    static   int lastq = -1;
469c60d41a9Swiz 
470c60d41a9Swiz    if (quantum != lastq || sg->src.s_addr!=st->st_origin
471c60d41a9Swiz     || sg->grp.s_addr!=gt->gt_mcastgrp) {
472c60d41a9Swiz        lastq = quantum;
473c60d41a9Swiz        sg->src.s_addr = st->st_origin;
474c60d41a9Swiz        sg->grp.s_addr = gt->gt_mcastgrp;
475c60d41a9Swiz        ioctl(igmp_socket, SIOCGETSGCNT, (char *)sg);
476c60d41a9Swiz    }
477c60d41a9Swiz }
478c60d41a9Swiz 
479c60d41a9Swiz /*
480c60d41a9Swiz  * Return pointer to a specific route entry.  This must be a separate
481c60d41a9Swiz  * function from find_route() which modifies rtp.
482c60d41a9Swiz  */
483c60d41a9Swiz struct rtentry *
snmp_find_route(u_long src,u_long mask)484c60d41a9Swiz snmp_find_route(u_long src, u_long mask)
485c60d41a9Swiz {
486c60d41a9Swiz     struct rtentry *rt;
487c60d41a9Swiz 
488c60d41a9Swiz    for (rt = routing_table; rt; rt = rt->rt_next) {
489c60d41a9Swiz       if (src == rt->rt_origin && mask == rt->rt_originmask)
490c60d41a9Swiz          return rt;
491c60d41a9Swiz    }
492c60d41a9Swiz    return NULL;
493c60d41a9Swiz }
494c60d41a9Swiz 
495c60d41a9Swiz /*
496c60d41a9Swiz  * Find next route entry > specification
497c60d41a9Swiz  */
498c60d41a9Swiz int
next_route(struct rtentry ** rtpp,u_long src,u_long mask)499c60d41a9Swiz next_route(struct rtentry **rtpp, u_long src, u_long mask)
500c60d41a9Swiz {
501c60d41a9Swiz    struct rtentry *rt, *rbest = NULL;
502c60d41a9Swiz 
503c60d41a9Swiz    /* Among all entries > spec, find "lowest" one in order */
504c60d41a9Swiz    for (rt = routing_table; rt; rt=rt->rt_next) {
505c60d41a9Swiz       if ((ntohl(rt->rt_origin) > ntohl(src)
506c60d41a9Swiz           || (ntohl(rt->rt_origin) == ntohl(src)
507c60d41a9Swiz              && ntohl(rt->rt_originmask) > ntohl(mask)))
508c60d41a9Swiz        && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
509c60d41a9Swiz           || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
510c60d41a9Swiz              && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
511c60d41a9Swiz                rbest = rt;
512c60d41a9Swiz    }
513c60d41a9Swiz    (*rtpp) = rbest;
514c60d41a9Swiz    return (*rtpp)!=0;
515c60d41a9Swiz }
516c60d41a9Swiz 
517c60d41a9Swiz /*
518c60d41a9Swiz  * Given a routing table entry, and a vifi, find the next vifi/entry
519c60d41a9Swiz  *
520c60d41a9Swiz  * vifi: vifi at which to start looking
521c60d41a9Swiz  */
522c60d41a9Swiz int
next_route_child(struct rtentry ** rtpp,u_long src,u_long mask,vifi_t * vifi)523c60d41a9Swiz next_route_child(struct rtentry **rtpp, u_long src, u_long mask, vifi_t *vifi)
524c60d41a9Swiz {
525c60d41a9Swiz    struct rtentry *rt;
526c60d41a9Swiz 
527c60d41a9Swiz    /* Get (S,M) entry */
528c60d41a9Swiz    if (!((*rtpp) = snmp_find_route(src,mask)))
529c60d41a9Swiz       if (!next_route(rtpp, src, mask))
530c60d41a9Swiz          return 0;
531c60d41a9Swiz 
532c60d41a9Swiz    /* Continue until we get one with a valid next vif */
533c60d41a9Swiz    do {
534c60d41a9Swiz       for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
535c60d41a9Swiz          if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
536c60d41a9Swiz             return 1;
537c60d41a9Swiz       *vifi = 0;
538c60d41a9Swiz    } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
539c60d41a9Swiz 
540c60d41a9Swiz    return 0;
541c60d41a9Swiz }
542c60d41a9Swiz 
543c60d41a9Swiz /*
544c60d41a9Swiz  * Given a routing table entry, and a vifi, find the next entry
545c60d41a9Swiz  * equal to or greater than those
546c60d41a9Swiz  *
547c60d41a9Swiz  * vifi: vifi at which to start looking
548c60d41a9Swiz  */
549c60d41a9Swiz int
next_child(struct gtable ** gtpp,struct stable ** stpp,u_long grp,u_long src,u_long mask,vifi_t * vifi)550c60d41a9Swiz next_child(struct gtable **gtpp, struct stable **stpp, u_long grp, u_long src,
551c60d41a9Swiz 	   u_long mask, vifi_t *vifi)
552c60d41a9Swiz {
553c60d41a9Swiz    struct stable *st;
554c60d41a9Swiz 
555c60d41a9Swiz    /* Get (G,S,M) entry */
556c60d41a9Swiz    if (mask!=0xFFFFFFFF
557c60d41a9Swiz     || !((*gtpp) = find_grp(grp))
558c60d41a9Swiz     || !((*stpp) = find_grp_src((*gtpp),src)))
559c60d41a9Swiz       if (!next_grp_src_mask(gtpp, stpp, grp, src, mask))
560c60d41a9Swiz          return 0;
561c60d41a9Swiz 
562c60d41a9Swiz    /* Continue until we get one with a valid next vif */
563c60d41a9Swiz    do {
564c60d41a9Swiz       for (; (*gtpp)->gt_route->rt_children && *vifi<numvifs; (*vifi)++)
565c60d41a9Swiz          if (VIFM_ISSET(*vifi, (*gtpp)->gt_route->rt_children))
566c60d41a9Swiz             return 1;
567c60d41a9Swiz       *vifi = 0;
568c60d41a9Swiz    } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp,
569c60d41a9Swiz 		(*stpp)->st_origin, 0xFFFFFFFF) );
570c60d41a9Swiz 
571c60d41a9Swiz    return 0;
572c60d41a9Swiz }
573c60d41a9Swiz #endif /* SNMP */
574c60d41a9Swiz 
575c60d41a9Swiz /*
576c60d41a9Swiz  * Initialize the kernel table structure
577c60d41a9Swiz  */
578c60d41a9Swiz void
init_ktable(void)579c60d41a9Swiz init_ktable(void)
580c60d41a9Swiz {
581c60d41a9Swiz     kernel_table 	= NULL;
582c60d41a9Swiz     kernel_no_route	= NULL;
583c60d41a9Swiz     kroutes		= 0;
584c60d41a9Swiz }
585c60d41a9Swiz 
586c60d41a9Swiz /*
587c60d41a9Swiz  * Add a new table entry for (origin, mcastgrp)
588c60d41a9Swiz  */
589c60d41a9Swiz void
add_table_entry(u_int32_t origin,u_int32_t mcastgrp)590c60d41a9Swiz add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
591c60d41a9Swiz {
592c60d41a9Swiz     struct rtentry *r;
593c60d41a9Swiz     struct gtable *gt,**gtnp,*prev_gt;
594c60d41a9Swiz     struct stable *st,**stnp;
595c60d41a9Swiz     vifi_t i;
596c60d41a9Swiz 
597c60d41a9Swiz #ifdef DEBUG_MFC
598c60d41a9Swiz     md_log(MD_MISS, origin, mcastgrp);
599c60d41a9Swiz #endif
600c60d41a9Swiz 
601c60d41a9Swiz     r = determine_route(origin);
602c60d41a9Swiz     prev_gt = NULL;
603c60d41a9Swiz     if (r == NULL) {
604c60d41a9Swiz 	/*
605c60d41a9Swiz 	 * Look for it on the no_route table; if it is found then
606c60d41a9Swiz 	 * it will be detected as a duplicate below.
607c60d41a9Swiz 	 */
608c60d41a9Swiz 	for (gt = kernel_no_route; gt; gt = gt->gt_next)
609c60d41a9Swiz 	    if (mcastgrp == gt->gt_mcastgrp &&
610c60d41a9Swiz 		gt->gt_srctbl && gt->gt_srctbl->st_origin == origin)
611c60d41a9Swiz 			break;
612c60d41a9Swiz 	gtnp = &kernel_no_route;
613c60d41a9Swiz     } else {
614c60d41a9Swiz 	gtnp = &r->rt_groups;
615c60d41a9Swiz 	while ((gt = *gtnp) != NULL) {
616c60d41a9Swiz 	    if (gt->gt_mcastgrp >= mcastgrp)
617c60d41a9Swiz 		break;
618c60d41a9Swiz 	    gtnp = &gt->gt_next;
619c60d41a9Swiz 	    prev_gt = gt;
620c60d41a9Swiz 	}
621c60d41a9Swiz     }
622c60d41a9Swiz 
623c60d41a9Swiz     if (gt == NULL || gt->gt_mcastgrp != mcastgrp) {
624c60d41a9Swiz 	gt = (struct gtable *)malloc(sizeof(struct gtable));
6256388d5ccSchristos 	if (gt == NULL) {
626c60d41a9Swiz 	    logit(LOG_ERR, 0, "ran out of memory");
6276388d5ccSchristos 	    return;
6286388d5ccSchristos 	}
629c60d41a9Swiz 
630c60d41a9Swiz 	gt->gt_mcastgrp	    = mcastgrp;
631c60d41a9Swiz 	gt->gt_timer   	    = CACHE_LIFETIME(cache_lifetime);
632c60d41a9Swiz 	time(&gt->gt_ctime);
633c60d41a9Swiz 	gt->gt_grpmems	    = 0;
634c60d41a9Swiz 	gt->gt_scope	    = 0;
635c60d41a9Swiz 	gt->gt_prsent_timer = 0;
636c60d41a9Swiz 	gt->gt_grftsnt	    = 0;
637c60d41a9Swiz 	gt->gt_srctbl	    = NULL;
638c60d41a9Swiz 	gt->gt_pruntbl	    = NULL;
639c60d41a9Swiz 	gt->gt_route	    = r;
640c60d41a9Swiz #ifdef RSRR
641c60d41a9Swiz 	gt->gt_rsrr_cache   = NULL;
642c60d41a9Swiz #endif
643c60d41a9Swiz 
644c60d41a9Swiz 	if (r != NULL) {
645c60d41a9Swiz 	    /* obtain the multicast group membership list */
646c60d41a9Swiz 	    for (i = 0; i < numvifs; i++) {
647c60d41a9Swiz 		if (VIFM_ISSET(i, r->rt_children) &&
648c60d41a9Swiz 		    !(VIFM_ISSET(i, r->rt_leaves)))
649c60d41a9Swiz 		    VIFM_SET(i, gt->gt_grpmems);
650c60d41a9Swiz 
651c60d41a9Swiz 		if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
652c60d41a9Swiz 		    VIFM_SET(i, gt->gt_grpmems);
653c60d41a9Swiz 	    }
654c60d41a9Swiz 	    GET_SCOPE(gt);
655c60d41a9Swiz 	    if (VIFM_ISSET(r->rt_parent, gt->gt_scope))
656c60d41a9Swiz 		gt->gt_scope = -1;
657c60d41a9Swiz 	    gt->gt_grpmems &= ~gt->gt_scope;
658c60d41a9Swiz 	} else {
659c60d41a9Swiz 	    gt->gt_scope = -1;
660c60d41a9Swiz 	    gt->gt_grpmems = 0;
661c60d41a9Swiz 	}
662c60d41a9Swiz 
663c60d41a9Swiz 	/* update ttls */
664c60d41a9Swiz 	prun_add_ttls(gt);
665c60d41a9Swiz 
666c60d41a9Swiz 	gt->gt_next = *gtnp;
667c60d41a9Swiz 	*gtnp = gt;
668c60d41a9Swiz 	if (gt->gt_next)
669c60d41a9Swiz 	    gt->gt_next->gt_prev = gt;
670c60d41a9Swiz 	gt->gt_prev = prev_gt;
671c60d41a9Swiz 
672c60d41a9Swiz 	if (r) {
673c60d41a9Swiz 	    if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) {
674c60d41a9Swiz 		struct gtable *g;
675c60d41a9Swiz 
676c60d41a9Swiz 		g = gtp ? gtp->gt_gnext : kernel_table;
677c60d41a9Swiz 		logit(LOG_WARNING, 0, "Entry for (%s %s) (rt:%p) exists (rt:%p)",
678ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
679ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp),
680c60d41a9Swiz 		    r, g->gt_route);
681c60d41a9Swiz 	    } else {
682c60d41a9Swiz 		if (gtp) {
683c60d41a9Swiz 		    gt->gt_gnext = gtp->gt_gnext;
684c60d41a9Swiz 		    gt->gt_gprev = gtp;
685c60d41a9Swiz 		    gtp->gt_gnext = gt;
686c60d41a9Swiz 		} else {
687c60d41a9Swiz 		    gt->gt_gnext = kernel_table;
688c60d41a9Swiz 		    gt->gt_gprev = NULL;
689c60d41a9Swiz 		    kernel_table = gt;
690c60d41a9Swiz 		}
691c60d41a9Swiz 		if (gt->gt_gnext)
692c60d41a9Swiz 		    gt->gt_gnext->gt_gprev = gt;
693c60d41a9Swiz 	    }
694c60d41a9Swiz 	} else {
695c60d41a9Swiz 	    gt->gt_gnext = gt->gt_gprev = NULL;
696c60d41a9Swiz 	}
697c60d41a9Swiz     }
698c60d41a9Swiz 
699c60d41a9Swiz     stnp = &gt->gt_srctbl;
700c60d41a9Swiz     while ((st = *stnp) != NULL) {
701c60d41a9Swiz 	if (ntohl(st->st_origin) >= ntohl(origin))
702c60d41a9Swiz 	    break;
703c60d41a9Swiz 	stnp = &st->st_next;
704c60d41a9Swiz     }
705c60d41a9Swiz 
706c60d41a9Swiz     if (st == NULL || st->st_origin != origin) {
707c60d41a9Swiz 	st = (struct stable *)malloc(sizeof(struct stable));
708c60d41a9Swiz 	if (st == NULL)
709c60d41a9Swiz 	    logit(LOG_ERR, 0, "ran out of memory");
710c60d41a9Swiz 
711c60d41a9Swiz 	st->st_origin = origin;
712c60d41a9Swiz 	st->st_pktcnt = 0;
713c60d41a9Swiz 	st->st_next = *stnp;
714c60d41a9Swiz 	*stnp = st;
715c60d41a9Swiz     } else {
716c60d41a9Swiz #ifdef DEBUG_MFC
717c60d41a9Swiz 	md_log(MD_DUPE, origin, mcastgrp);
718c60d41a9Swiz #endif
719c60d41a9Swiz 	logit(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
720ee70b5d6Sdsl 		inet_fmt(origin),
721ee70b5d6Sdsl 		inet_fmt(mcastgrp));
722c60d41a9Swiz 	/* XXX Doing this should cause no harm, and may ensure
723c60d41a9Swiz 	 * kernel<>mrouted synchronization */
724c60d41a9Swiz 	k_add_rg(origin, gt);
725c60d41a9Swiz 	return;
726c60d41a9Swiz     }
727c60d41a9Swiz 
728c60d41a9Swiz     kroutes++;
729c60d41a9Swiz     k_add_rg(origin, gt);
730c60d41a9Swiz 
731c60d41a9Swiz     logit(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d",
732ee70b5d6Sdsl 	inet_fmt(origin),
733ee70b5d6Sdsl 	inet_fmt(mcastgrp),
734c60d41a9Swiz 	gt->gt_grpmems, r ? r->rt_parent : -1);
735c60d41a9Swiz 
736c60d41a9Swiz     /* If there are no leaf vifs
737c60d41a9Swiz      * which have this group, then
738c60d41a9Swiz      * mark this src-grp as a prune candidate.
739c60d41a9Swiz      */
740c60d41a9Swiz     if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway)
741c60d41a9Swiz 	send_prune(gt);
742c60d41a9Swiz }
743c60d41a9Swiz 
744c60d41a9Swiz /*
745c60d41a9Swiz  * An mrouter has gone down and come up on an interface
746c60d41a9Swiz  * Forward on that interface immediately
747c60d41a9Swiz  */
748c60d41a9Swiz void
reset_neighbor_state(vifi_t vifi,u_int32_t addr)749c60d41a9Swiz reset_neighbor_state(vifi_t vifi, u_int32_t addr)
750c60d41a9Swiz {
751c60d41a9Swiz     struct rtentry *r;
752c60d41a9Swiz     struct gtable *g;
753c60d41a9Swiz     struct ptable *pt, **ptnp;
754c60d41a9Swiz     struct stable *st;
755c60d41a9Swiz 
756c60d41a9Swiz     for (g = kernel_table; g; g = g->gt_gnext) {
757c60d41a9Swiz 	r = g->gt_route;
758c60d41a9Swiz 
759c60d41a9Swiz 	/*
760c60d41a9Swiz 	 * If neighbor was the parent, remove the prune sent state
761c60d41a9Swiz 	 * and all of the source cache info so that prunes get
762c60d41a9Swiz 	 * regenerated.
763c60d41a9Swiz 	 */
764c60d41a9Swiz 	if (vifi == r->rt_parent) {
765c60d41a9Swiz 	    if (addr == r->rt_gateway) {
766c60d41a9Swiz 		logit(LOG_DEBUG, 0, "reset_neighbor_state parent reset (%s %s)",
767ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
768ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp));
769c60d41a9Swiz 
770c60d41a9Swiz 		g->gt_prsent_timer = 0;
771c60d41a9Swiz 		g->gt_grftsnt = 0;
772c60d41a9Swiz 		while ((st = g->gt_srctbl) != NULL) {
773c60d41a9Swiz 		    g->gt_srctbl = st->st_next;
774c60d41a9Swiz 		    k_del_rg(st->st_origin, g);
775c60d41a9Swiz 		    kroutes--;
776c60d41a9Swiz 		    free(st);
777c60d41a9Swiz 		}
778c60d41a9Swiz 	    }
779c60d41a9Swiz 	} else {
780c60d41a9Swiz 	    /*
781c60d41a9Swiz 	     * Neighbor was not the parent, send grafts to join the groups
782c60d41a9Swiz 	     */
783c60d41a9Swiz 	    if (g->gt_prsent_timer) {
784c60d41a9Swiz 		g->gt_grftsnt = 1;
785c60d41a9Swiz 		send_graft(g);
786c60d41a9Swiz 		g->gt_prsent_timer = 0;
787c60d41a9Swiz 	    }
788c60d41a9Swiz 
789c60d41a9Swiz 	    /*
790c60d41a9Swiz 	     * Remove any prunes that this router has sent us.
791c60d41a9Swiz 	     */
792c60d41a9Swiz 	    ptnp = &g->gt_pruntbl;
793c60d41a9Swiz 	    while ((pt = *ptnp) != NULL) {
794c60d41a9Swiz 		if (pt->pt_vifi == vifi && pt->pt_router == addr) {
795c60d41a9Swiz 		    *ptnp = pt->pt_next;
796c60d41a9Swiz 		    free(pt);
797c60d41a9Swiz 		} else
798c60d41a9Swiz 		    ptnp = &pt->pt_next;
799c60d41a9Swiz 	    }
800c60d41a9Swiz 
801c60d41a9Swiz 	    /*
802c60d41a9Swiz 	     * And see if we want to forward again.
803c60d41a9Swiz 	     */
804c60d41a9Swiz 	    if (!VIFM_ISSET(vifi, g->gt_grpmems)) {
805c60d41a9Swiz 		if (VIFM_ISSET(vifi, r->rt_children) &&
806c60d41a9Swiz 		    !(VIFM_ISSET(vifi, r->rt_leaves)))
807c60d41a9Swiz 		    VIFM_SET(vifi, g->gt_grpmems);
808c60d41a9Swiz 
809c60d41a9Swiz 		if (VIFM_ISSET(vifi, r->rt_leaves) &&
810c60d41a9Swiz 		    grplst_mem(vifi, g->gt_mcastgrp))
811c60d41a9Swiz 		    VIFM_SET(vifi, g->gt_grpmems);
812c60d41a9Swiz 
813c60d41a9Swiz 		g->gt_grpmems &= ~g->gt_scope;
814c60d41a9Swiz 		prun_add_ttls(g);
815c60d41a9Swiz 
816c60d41a9Swiz 		/* Update kernel state */
817c60d41a9Swiz 		update_kernel(g);
818c60d41a9Swiz #ifdef RSRR
819c60d41a9Swiz 		/* Send route change notification to reservation protocol. */
820c60d41a9Swiz 		rsrr_cache_send(g,1);
821c60d41a9Swiz #endif /* RSRR */
822c60d41a9Swiz 
823c60d41a9Swiz 		logit(LOG_DEBUG, 0, "reset member state (%s %s) gm:%x",
824ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
825ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp), g->gt_grpmems);
826c60d41a9Swiz 	    }
827c60d41a9Swiz 	}
828c60d41a9Swiz     }
829c60d41a9Swiz }
830c60d41a9Swiz 
831c60d41a9Swiz /*
832c60d41a9Swiz  * Delete table entry from the kernel
833c60d41a9Swiz  * del_flag determines how many entries to delete
834c60d41a9Swiz  */
835c60d41a9Swiz void
del_table_entry(struct rtentry * r,u_int32_t mcastgrp,u_int del_flag)836c60d41a9Swiz del_table_entry(struct rtentry *r, u_int32_t mcastgrp, u_int del_flag)
837c60d41a9Swiz {
838c60d41a9Swiz     struct gtable *g, *prev_g;
839c60d41a9Swiz     struct stable *st, *prev_st;
840c60d41a9Swiz     struct ptable *pt, *prev_pt;
841c60d41a9Swiz 
842c60d41a9Swiz     if (del_flag == DEL_ALL_ROUTES) {
843c60d41a9Swiz 	g = r->rt_groups;
844c60d41a9Swiz 	while (g) {
845c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
846ee70b5d6Sdsl 		inet_fmts(r->rt_origin, r->rt_originmask),
847ee70b5d6Sdsl 		inet_fmt(g->gt_mcastgrp));
848c60d41a9Swiz 	    st = g->gt_srctbl;
849c60d41a9Swiz 	    while (st) {
850c60d41a9Swiz 		if (k_del_rg(st->st_origin, g) < 0) {
851c60d41a9Swiz 		    logit(LOG_WARNING, errno,
852c60d41a9Swiz 			"del_table_entry trying to delete (%s, %s)",
853ee70b5d6Sdsl 			inet_fmt(st->st_origin),
854ee70b5d6Sdsl 			inet_fmt(g->gt_mcastgrp));
855c60d41a9Swiz 		}
856c60d41a9Swiz 		kroutes--;
857c60d41a9Swiz 		prev_st = st;
858c60d41a9Swiz 		st = st->st_next;
859c60d41a9Swiz 		free(prev_st);
860c60d41a9Swiz 	    }
861c60d41a9Swiz 	    g->gt_srctbl = NULL;
862c60d41a9Swiz 
863c60d41a9Swiz 	    pt = g->gt_pruntbl;
864c60d41a9Swiz 	    while (pt) {
865c60d41a9Swiz 		prev_pt = pt;
866c60d41a9Swiz 		pt = pt->pt_next;
867c60d41a9Swiz 		free(prev_pt);
868c60d41a9Swiz 	    }
869c60d41a9Swiz 	    g->gt_pruntbl = NULL;
870c60d41a9Swiz 
871c60d41a9Swiz 	    if (g->gt_gnext)
872c60d41a9Swiz 		g->gt_gnext->gt_gprev = g->gt_gprev;
873c60d41a9Swiz 	    if (g->gt_gprev)
874c60d41a9Swiz 		g->gt_gprev->gt_gnext = g->gt_gnext;
875c60d41a9Swiz 	    else
876c60d41a9Swiz 		kernel_table = g->gt_gnext;
877c60d41a9Swiz 
878c60d41a9Swiz #ifdef RSRR
879c60d41a9Swiz 	    /* Send route change notification to reservation protocol. */
880c60d41a9Swiz 	    rsrr_cache_send(g,0);
881c60d41a9Swiz 	    rsrr_cache_clean(g);
882c60d41a9Swiz #endif /* RSRR */
883c60d41a9Swiz 	    prev_g = g;
884c60d41a9Swiz 	    g = g->gt_next;
885c60d41a9Swiz 	    free(prev_g);
886c60d41a9Swiz 	}
887c60d41a9Swiz 	r->rt_groups = NULL;
888c60d41a9Swiz     }
889c60d41a9Swiz 
890c60d41a9Swiz     /*
891c60d41a9Swiz      * Dummy routine - someday this may be needed, so it is just there
892c60d41a9Swiz      */
893c60d41a9Swiz     if (del_flag == DEL_RTE_GROUP) {
894c60d41a9Swiz 	prev_g = (struct gtable *)&r->rt_groups;
895c60d41a9Swiz 	for (g = r->rt_groups; g; g = g->gt_next) {
896c60d41a9Swiz 	    if (g->gt_mcastgrp == mcastgrp) {
897c60d41a9Swiz 		logit(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
898ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
899ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp));
900c60d41a9Swiz 		st = g->gt_srctbl;
901c60d41a9Swiz 		while (st) {
902c60d41a9Swiz 		    if (k_del_rg(st->st_origin, g) < 0) {
903c60d41a9Swiz 			logit(LOG_WARNING, errno,
904c60d41a9Swiz 			    "del_table_entry trying to delete (%s, %s)",
905ee70b5d6Sdsl 			    inet_fmt(st->st_origin),
906ee70b5d6Sdsl 			    inet_fmt(g->gt_mcastgrp));
907c60d41a9Swiz 		    }
908c60d41a9Swiz 		    kroutes--;
909c60d41a9Swiz 		    prev_st = st;
910c60d41a9Swiz 		    st = st->st_next;
911c60d41a9Swiz 		    free(prev_st);
912c60d41a9Swiz 		}
913c60d41a9Swiz 		g->gt_srctbl = NULL;
914c60d41a9Swiz 
915c60d41a9Swiz 		pt = g->gt_pruntbl;
916c60d41a9Swiz 		while (pt) {
917c60d41a9Swiz 		    prev_pt = pt;
918c60d41a9Swiz 		    pt = pt->pt_next;
919c60d41a9Swiz 		    free(prev_pt);
920c60d41a9Swiz 		}
921c60d41a9Swiz 		g->gt_pruntbl = NULL;
922c60d41a9Swiz 
923c60d41a9Swiz 		if (g->gt_gnext)
924c60d41a9Swiz 		    g->gt_gnext->gt_gprev = g->gt_gprev;
925c60d41a9Swiz 		if (g->gt_gprev)
926c60d41a9Swiz 		    g->gt_gprev->gt_gnext = g->gt_gnext;
927c60d41a9Swiz 		else
928c60d41a9Swiz 		    kernel_table = g->gt_gnext;
929c60d41a9Swiz 
930c60d41a9Swiz 		if (prev_g != (struct gtable *)&r->rt_groups)
931c60d41a9Swiz 		    g->gt_next->gt_prev = prev_g;
932c60d41a9Swiz 		else
933c60d41a9Swiz 		    g->gt_next->gt_prev = NULL;
934c60d41a9Swiz 		prev_g->gt_next = g->gt_next;
935c60d41a9Swiz 
936c60d41a9Swiz #ifdef RSRR
937c60d41a9Swiz 		/* Send route change notification to reservation protocol. */
938c60d41a9Swiz 		rsrr_cache_send(g,0);
939c60d41a9Swiz 		rsrr_cache_clean(g);
940c60d41a9Swiz #endif /* RSRR */
941c60d41a9Swiz 		free(g);
942c60d41a9Swiz 		g = prev_g;
943c60d41a9Swiz 	    } else {
944c60d41a9Swiz 		prev_g = g;
945c60d41a9Swiz 	    }
946c60d41a9Swiz 	}
947c60d41a9Swiz     }
948c60d41a9Swiz }
949c60d41a9Swiz 
950c60d41a9Swiz /*
951c60d41a9Swiz  * update kernel table entry when a route entry changes
952c60d41a9Swiz  */
953c60d41a9Swiz void
update_table_entry(struct rtentry * r)954c60d41a9Swiz update_table_entry(struct rtentry *r)
955c60d41a9Swiz {
956c60d41a9Swiz     struct gtable *g;
957c60d41a9Swiz     struct ptable *pt, *prev_pt;
958c60d41a9Swiz     vifi_t i;
959c60d41a9Swiz 
960c60d41a9Swiz     for (g = r->rt_groups; g; g = g->gt_next) {
961c60d41a9Swiz 	pt = g->gt_pruntbl;
962c60d41a9Swiz 	while (pt) {
963c60d41a9Swiz 	    prev_pt = pt->pt_next;
964c60d41a9Swiz 	    free(pt);
965c60d41a9Swiz 	    pt = prev_pt;
966c60d41a9Swiz 	}
967c60d41a9Swiz 	g->gt_pruntbl = NULL;
968c60d41a9Swiz 
969c60d41a9Swiz 	g->gt_grpmems = 0;
970c60d41a9Swiz 
971c60d41a9Swiz 	/* obtain the multicast group membership list */
972c60d41a9Swiz 	for (i = 0; i < numvifs; i++) {
973c60d41a9Swiz 	    if (VIFM_ISSET(i, r->rt_children) &&
974c60d41a9Swiz 		!(VIFM_ISSET(i, r->rt_leaves)))
975c60d41a9Swiz 		VIFM_SET(i, g->gt_grpmems);
976c60d41a9Swiz 
977c60d41a9Swiz 	    if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, g->gt_mcastgrp))
978c60d41a9Swiz 		VIFM_SET(i, g->gt_grpmems);
979c60d41a9Swiz 	}
980c60d41a9Swiz 	if (VIFM_ISSET(r->rt_parent, g->gt_scope))
981c60d41a9Swiz 	    g->gt_scope = -1;
982c60d41a9Swiz 	g->gt_grpmems &= ~g->gt_scope;
983c60d41a9Swiz 
984c60d41a9Swiz 	logit(LOG_DEBUG, 0, "updating cache entries (%s %s) gm:%x",
985ee70b5d6Sdsl 	    inet_fmts(r->rt_origin, r->rt_originmask),
986ee70b5d6Sdsl 	    inet_fmt(g->gt_mcastgrp),
987c60d41a9Swiz 	    g->gt_grpmems);
988c60d41a9Swiz 
989c60d41a9Swiz 	if (g->gt_grpmems && g->gt_prsent_timer) {
990c60d41a9Swiz 	    g->gt_grftsnt = 1;
991c60d41a9Swiz 	    send_graft(g);
992c60d41a9Swiz 	    g->gt_prsent_timer = 0;
993c60d41a9Swiz 	}
994c60d41a9Swiz 
995c60d41a9Swiz 	/* update ttls and add entry into kernel */
996c60d41a9Swiz 	prun_add_ttls(g);
997c60d41a9Swiz 	update_kernel(g);
998c60d41a9Swiz #ifdef RSRR
999c60d41a9Swiz 	/* Send route change notification to reservation protocol. */
1000c60d41a9Swiz 	rsrr_cache_send(g,1);
1001c60d41a9Swiz #endif /* RSRR */
1002c60d41a9Swiz 
1003c60d41a9Swiz 	/* Check if we want to prune this group */
1004c60d41a9Swiz 	if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
1005c60d41a9Swiz 	    g->gt_timer = CACHE_LIFETIME(cache_lifetime);
1006c60d41a9Swiz 	    send_prune(g);
1007c60d41a9Swiz 	}
1008c60d41a9Swiz     }
1009c60d41a9Swiz }
1010c60d41a9Swiz 
1011c60d41a9Swiz /*
1012c60d41a9Swiz  * set the forwarding flag for all mcastgrps on this vifi
1013c60d41a9Swiz  */
1014c60d41a9Swiz void
update_lclgrp(vifi_t vifi,u_int32_t mcastgrp)1015c60d41a9Swiz update_lclgrp(vifi_t vifi, u_int32_t mcastgrp)
1016c60d41a9Swiz {
1017c60d41a9Swiz     struct rtentry *r;
1018c60d41a9Swiz     struct gtable *g;
1019c60d41a9Swiz 
1020c60d41a9Swiz     logit(LOG_DEBUG, 0, "group %s joined on vif %d",
1021ee70b5d6Sdsl 	inet_fmt(mcastgrp), vifi);
1022c60d41a9Swiz 
1023c60d41a9Swiz     for (g = kernel_table; g; g = g->gt_gnext) {
1024c60d41a9Swiz 	if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
1025c60d41a9Swiz 	    break;
1026c60d41a9Swiz 
1027c60d41a9Swiz 	r = g->gt_route;
1028c60d41a9Swiz 	if (g->gt_mcastgrp == mcastgrp &&
1029c60d41a9Swiz 	    VIFM_ISSET(vifi, r->rt_children)) {
1030c60d41a9Swiz 
1031c60d41a9Swiz 	    VIFM_SET(vifi, g->gt_grpmems);
1032c60d41a9Swiz 	    g->gt_grpmems &= ~g->gt_scope;
1033c60d41a9Swiz 	    if (g->gt_grpmems == 0)
1034c60d41a9Swiz 		continue;
1035c60d41a9Swiz 
1036c60d41a9Swiz 	    prun_add_ttls(g);
1037c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
1038ee70b5d6Sdsl 		inet_fmts(r->rt_origin, r->rt_originmask),
1039ee70b5d6Sdsl 		inet_fmt(g->gt_mcastgrp), g->gt_grpmems);
1040c60d41a9Swiz 
1041c60d41a9Swiz 	    update_kernel(g);
1042c60d41a9Swiz #ifdef RSRR
1043c60d41a9Swiz 	    /* Send route change notification to reservation protocol. */
1044c60d41a9Swiz 	    rsrr_cache_send(g,1);
1045c60d41a9Swiz #endif /* RSRR */
1046c60d41a9Swiz 	}
1047c60d41a9Swiz     }
1048c60d41a9Swiz }
1049c60d41a9Swiz 
1050c60d41a9Swiz /*
1051c60d41a9Swiz  * reset forwarding flag for all mcastgrps on this vifi
1052c60d41a9Swiz  */
1053c60d41a9Swiz void
delete_lclgrp(vifi_t vifi,u_int32_t mcastgrp)1054c60d41a9Swiz delete_lclgrp(vifi_t vifi, u_int32_t mcastgrp)
1055c60d41a9Swiz {
1056c60d41a9Swiz     struct rtentry *r;
1057c60d41a9Swiz     struct gtable *g;
1058c60d41a9Swiz 
1059c60d41a9Swiz     logit(LOG_DEBUG, 0, "group %s left on vif %d",
1060ee70b5d6Sdsl 	inet_fmt(mcastgrp), vifi);
1061c60d41a9Swiz 
1062c60d41a9Swiz     for (g = kernel_table; g; g = g->gt_gnext) {
1063c60d41a9Swiz 	if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
1064c60d41a9Swiz 	    break;
1065c60d41a9Swiz 
1066c60d41a9Swiz 	if (g->gt_mcastgrp == mcastgrp) {
1067c60d41a9Swiz 	    int stop_sending = 1;
1068c60d41a9Swiz 
1069c60d41a9Swiz 	    r = g->gt_route;
1070c60d41a9Swiz 	    /*
1071c60d41a9Swiz 	     * If this is not a leaf, then we have router neighbors on this
1072c60d41a9Swiz 	     * vif.  Only turn off forwarding if they have all pruned.
1073c60d41a9Swiz 	     */
1074c60d41a9Swiz 	    if (!VIFM_ISSET(vifi, r->rt_leaves)) {
1075c60d41a9Swiz 		struct listaddr *vr;
1076c60d41a9Swiz 
1077c60d41a9Swiz 		for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
1078c60d41a9Swiz 		  if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) {
1079c60d41a9Swiz 		      stop_sending = 0;
1080c60d41a9Swiz 		      break;
1081c60d41a9Swiz 		  }
1082c60d41a9Swiz 	    }
1083c60d41a9Swiz 
1084c60d41a9Swiz 	    if (stop_sending) {
1085c60d41a9Swiz 		VIFM_CLR(vifi, g->gt_grpmems);
1086c60d41a9Swiz 		logit(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
1087ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
1088ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp), g->gt_grpmems);
1089c60d41a9Swiz 
1090c60d41a9Swiz 		prun_add_ttls(g);
1091c60d41a9Swiz 		update_kernel(g);
1092c60d41a9Swiz #ifdef RSRR
1093c60d41a9Swiz 		/* Send route change notification to reservation protocol. */
1094c60d41a9Swiz 		rsrr_cache_send(g,1);
1095c60d41a9Swiz #endif /* RSRR */
1096c60d41a9Swiz 
1097c60d41a9Swiz 		/*
1098c60d41a9Swiz 		 * If there are no more members of this particular group,
1099c60d41a9Swiz 		 *  send prune upstream
1100c60d41a9Swiz 		 */
1101c60d41a9Swiz 		if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway)
1102c60d41a9Swiz 		    send_prune(g);
1103c60d41a9Swiz 	    }
1104c60d41a9Swiz 	}
1105c60d41a9Swiz     }
1106c60d41a9Swiz }
1107c60d41a9Swiz 
1108c60d41a9Swiz /*
1109c60d41a9Swiz  * Takes the prune message received and then strips it to
1110c60d41a9Swiz  * determine the (src, grp) pair to be pruned.
1111c60d41a9Swiz  *
1112c60d41a9Swiz  * Adds the router to the (src, grp) entry then.
1113c60d41a9Swiz  *
1114c60d41a9Swiz  * Determines if further packets have to be sent down that vif
1115c60d41a9Swiz  *
1116c60d41a9Swiz  * Determines if a corresponding prune message has to be generated
1117c60d41a9Swiz  */
1118c60d41a9Swiz void
accept_prune(u_int32_t src,u_int32_t dst,char * p,int datalen)1119c60d41a9Swiz accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
1120c60d41a9Swiz {
1121c60d41a9Swiz     u_int32_t prun_src;
1122c60d41a9Swiz     u_int32_t prun_grp;
1123c60d41a9Swiz     u_int32_t prun_tmr;
1124c60d41a9Swiz     vifi_t vifi;
1125c60d41a9Swiz     int i;
1126c60d41a9Swiz     int stop_sending;
1127c60d41a9Swiz     struct rtentry *r;
1128c60d41a9Swiz     struct gtable *g;
1129c60d41a9Swiz     struct ptable *pt;
1130c60d41a9Swiz     struct listaddr *vr;
1131c60d41a9Swiz 
1132c60d41a9Swiz     /* Don't process any prunes if router is not pruning */
1133c60d41a9Swiz     if (pruning == 0)
1134c60d41a9Swiz 	return;
1135c60d41a9Swiz 
1136c60d41a9Swiz     if ((vifi = find_vif(src, dst)) == NO_VIF) {
1137c60d41a9Swiz 	logit(LOG_INFO, 0,
1138c60d41a9Swiz     	    "ignoring prune report from non-neighbor %s",
1139ee70b5d6Sdsl 	    inet_fmt(src));
1140c60d41a9Swiz 	return;
1141c60d41a9Swiz     }
1142c60d41a9Swiz 
1143c60d41a9Swiz     /* Check if enough data is present */
1144c60d41a9Swiz     if (datalen < 12)
1145c60d41a9Swiz 	{
1146c60d41a9Swiz 	    logit(LOG_WARNING, 0,
1147c60d41a9Swiz 		"non-decipherable prune from %s",
1148ee70b5d6Sdsl 		inet_fmt(src));
1149c60d41a9Swiz 	    return;
1150c60d41a9Swiz 	}
1151c60d41a9Swiz 
1152c60d41a9Swiz     for (i = 0; i< 4; i++)
1153c60d41a9Swiz 	((char *)&prun_src)[i] = *p++;
1154c60d41a9Swiz     for (i = 0; i< 4; i++)
1155c60d41a9Swiz 	((char *)&prun_grp)[i] = *p++;
1156c60d41a9Swiz     for (i = 0; i< 4; i++)
1157c60d41a9Swiz 	((char *)&prun_tmr)[i] = *p++;
1158c60d41a9Swiz     prun_tmr = ntohl(prun_tmr);
1159c60d41a9Swiz 
1160c60d41a9Swiz     logit(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d",
1161ee70b5d6Sdsl 	inet_fmt(src), vifi,
1162ee70b5d6Sdsl 	inet_fmt(prun_src), inet_fmt(prun_grp), prun_tmr);
1163c60d41a9Swiz 
1164c60d41a9Swiz     /*
1165c60d41a9Swiz      * Find the subnet for the prune
1166c60d41a9Swiz      */
1167c60d41a9Swiz     if (find_src_grp(prun_src, 0, prun_grp)) {
1168c60d41a9Swiz 	g = gtp ? gtp->gt_gnext : kernel_table;
1169c60d41a9Swiz     	r = g->gt_route;
1170c60d41a9Swiz 
1171c60d41a9Swiz 	if (!VIFM_ISSET(vifi, r->rt_children)) {
1172c60d41a9Swiz 	    logit(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)",
1173ee70b5d6Sdsl 		inet_fmt(src), inet_fmt(prun_src),
1174ee70b5d6Sdsl 		inet_fmt(prun_grp));
1175c60d41a9Swiz 	    return;
1176c60d41a9Swiz 	}
1177c60d41a9Swiz 	if (VIFM_ISSET(vifi, g->gt_scope)) {
1178c60d41a9Swiz 	    logit(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)",
1179ee70b5d6Sdsl 		inet_fmt(src), inet_fmt(prun_src),
1180ee70b5d6Sdsl 		inet_fmt(prun_grp));
1181c60d41a9Swiz 	    return;
1182c60d41a9Swiz 	}
1183c60d41a9Swiz 	if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) {
1184c60d41a9Swiz 	    /*
1185c60d41a9Swiz 	     * If it's about to expire, then it's only still around because
1186c60d41a9Swiz 	     * of timer granularity, so don't warn about it.
1187c60d41a9Swiz 	     */
1188c60d41a9Swiz 	    if (pt->pt_timer > 10) {
1189c60d41a9Swiz 		logit(LOG_WARNING, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x",
1190c60d41a9Swiz 		    "duplicate prune received on vif",
1191ee70b5d6Sdsl 		    vifi, inet_fmt(src), inet_fmt(prun_src),
1192ee70b5d6Sdsl 		    inet_fmt(prun_grp), prun_tmr,
1193c60d41a9Swiz 		    "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems);
1194c60d41a9Swiz 	    }
1195c60d41a9Swiz 	    pt->pt_timer = prun_tmr;
1196c60d41a9Swiz 	} else {
1197c60d41a9Swiz 	    /* allocate space for the prune structure */
1198c60d41a9Swiz 	    pt = (struct ptable *)(malloc(sizeof(struct ptable)));
11996388d5ccSchristos 	    if (pt == NULL) {
1200c60d41a9Swiz 	      logit(LOG_ERR, 0, "pt: ran out of memory");
12016388d5ccSchristos 	      return;
12026388d5ccSchristos 	    }
1203c60d41a9Swiz 
1204c60d41a9Swiz 	    pt->pt_vifi = vifi;
1205c60d41a9Swiz 	    pt->pt_router = src;
1206c60d41a9Swiz 	    pt->pt_timer = prun_tmr;
1207c60d41a9Swiz 
1208c60d41a9Swiz 	    pt->pt_next = g->gt_pruntbl;
1209c60d41a9Swiz 	    g->gt_pruntbl = pt;
1210c60d41a9Swiz 	}
1211c60d41a9Swiz 
1212c60d41a9Swiz 	/* Refresh the group's lifetime */
1213c60d41a9Swiz 	g->gt_timer = CACHE_LIFETIME(cache_lifetime);
1214*58cb611aSlukem 	if ((u_int32_t)g->gt_timer < prun_tmr)
1215c60d41a9Swiz 	    g->gt_timer = prun_tmr;
1216c60d41a9Swiz 
1217c60d41a9Swiz 	/*
1218c60d41a9Swiz 	 * check if any more packets need to be sent on the
1219c60d41a9Swiz 	 * vif which sent this message
1220c60d41a9Swiz 	 */
1221c60d41a9Swiz 	stop_sending = 1;
1222c60d41a9Swiz 	for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
1223c60d41a9Swiz 	  if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL)  {
1224c60d41a9Swiz 	      stop_sending = 0;
1225c60d41a9Swiz 	      break;
1226c60d41a9Swiz 	  }
1227c60d41a9Swiz 
1228c60d41a9Swiz 	if (stop_sending && !grplst_mem(vifi, prun_grp)) {
1229c60d41a9Swiz 	    VIFM_CLR(vifi, g->gt_grpmems);
1230c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
1231ee70b5d6Sdsl 		inet_fmts(r->rt_origin, r->rt_originmask),
1232ee70b5d6Sdsl 		inet_fmt(g->gt_mcastgrp), vifi, g->gt_grpmems);
1233c60d41a9Swiz 
1234c60d41a9Swiz 	    prun_add_ttls(g);
1235c60d41a9Swiz 	    update_kernel(g);
1236c60d41a9Swiz #ifdef RSRR
1237c60d41a9Swiz 	    /* Send route change notification to reservation protocol. */
1238c60d41a9Swiz 	    rsrr_cache_send(g,1);
1239c60d41a9Swiz #endif /* RSRR */
1240c60d41a9Swiz 	}
1241c60d41a9Swiz 
1242c60d41a9Swiz 	/*
1243c60d41a9Swiz 	 * check if all the child routers have expressed no interest
1244c60d41a9Swiz 	 * in this group and if this group does not exist in the
1245c60d41a9Swiz 	 * interface
1246c60d41a9Swiz 	 * Send a prune message then upstream
1247c60d41a9Swiz 	 */
1248c60d41a9Swiz 	if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
1249c60d41a9Swiz 	    send_prune(g);
1250c60d41a9Swiz 	}
1251c60d41a9Swiz     } else {
1252c60d41a9Swiz 	/*
1253c60d41a9Swiz 	 * There is no kernel entry for this group.  Therefore, we can
1254c60d41a9Swiz 	 * simply ignore the prune, as we are not forwarding this traffic
1255c60d41a9Swiz 	 * downstream.
1256c60d41a9Swiz 	 */
1257c60d41a9Swiz 	logit(LOG_DEBUG, 0, "%s (%s %s)/%d from %s",
1258c60d41a9Swiz 	    "prune message received with no kernel entry for",
1259ee70b5d6Sdsl 	    inet_fmt(prun_src), inet_fmt(prun_grp),
1260ee70b5d6Sdsl 	    prun_tmr, inet_fmt(src));
1261c60d41a9Swiz 	return;
1262c60d41a9Swiz     }
1263c60d41a9Swiz }
1264c60d41a9Swiz 
1265c60d41a9Swiz /*
1266c60d41a9Swiz  * Checks if this mcastgrp is present in the kernel table
1267c60d41a9Swiz  * If so and if a prune was sent, it sends a graft upwards
1268c60d41a9Swiz  */
1269c60d41a9Swiz void
chkgrp_graft(vifi_t vifi,u_int32_t mcastgrp)1270c60d41a9Swiz chkgrp_graft(vifi_t vifi, u_int32_t mcastgrp)
1271c60d41a9Swiz {
1272c60d41a9Swiz     struct rtentry *r;
1273c60d41a9Swiz     struct gtable *g;
1274c60d41a9Swiz 
1275c60d41a9Swiz     for (g = kernel_table; g; g = g->gt_gnext) {
1276c60d41a9Swiz 	if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
1277c60d41a9Swiz 	    break;
1278c60d41a9Swiz 
1279c60d41a9Swiz 	r = g->gt_route;
1280c60d41a9Swiz 	if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children))
1281c60d41a9Swiz 	    if (g->gt_prsent_timer) {
1282c60d41a9Swiz 		VIFM_SET(vifi, g->gt_grpmems);
1283c60d41a9Swiz 
1284c60d41a9Swiz 		/*
1285c60d41a9Swiz 		 * If the vif that was joined was a scoped vif,
1286c60d41a9Swiz 		 * ignore it ; don't graft back
1287c60d41a9Swiz 		 */
1288c60d41a9Swiz 		g->gt_grpmems &= ~g->gt_scope;
1289c60d41a9Swiz 		if (g->gt_grpmems == 0)
1290c60d41a9Swiz 		    continue;
1291c60d41a9Swiz 
1292c60d41a9Swiz 		/* set the flag for graft retransmission */
1293c60d41a9Swiz 		g->gt_grftsnt = 1;
1294c60d41a9Swiz 
1295c60d41a9Swiz 		/* send graft upwards */
1296c60d41a9Swiz 		send_graft(g);
1297c60d41a9Swiz 
1298c60d41a9Swiz 		/* reset the prune timer and update cache timer*/
1299c60d41a9Swiz 		g->gt_prsent_timer = 0;
1300c60d41a9Swiz 		g->gt_timer = max_prune_lifetime;
1301c60d41a9Swiz 
1302c60d41a9Swiz 		logit(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
1303ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
1304ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp), g->gt_grpmems);
1305c60d41a9Swiz 
1306c60d41a9Swiz 		prun_add_ttls(g);
1307c60d41a9Swiz 		update_kernel(g);
1308c60d41a9Swiz #ifdef RSRR
1309c60d41a9Swiz 		/* Send route change notification to reservation protocol. */
1310c60d41a9Swiz 		rsrr_cache_send(g,1);
1311c60d41a9Swiz #endif /* RSRR */
1312c60d41a9Swiz 	    }
1313c60d41a9Swiz     }
1314c60d41a9Swiz }
1315c60d41a9Swiz 
1316c60d41a9Swiz /* determine the multicast group and src
1317c60d41a9Swiz  *
1318c60d41a9Swiz  * if it does, then determine if a prune was sent
1319c60d41a9Swiz  * upstream.
1320c60d41a9Swiz  * if prune sent upstream, send graft upstream and send
1321c60d41a9Swiz  * ack downstream.
1322c60d41a9Swiz  *
1323c60d41a9Swiz  * if no prune sent upstream, change the forwarding bit
1324c60d41a9Swiz  * for this interface and send ack downstream.
1325c60d41a9Swiz  *
1326c60d41a9Swiz  * if no entry exists for this group send ack downstream.
1327c60d41a9Swiz  */
1328c60d41a9Swiz void
accept_graft(u_int32_t src,u_int32_t dst,char * p,int datalen)1329c60d41a9Swiz accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
1330c60d41a9Swiz {
1331c60d41a9Swiz     vifi_t 	vifi;
1332c60d41a9Swiz     u_int32_t 	graft_src;
1333c60d41a9Swiz     u_int32_t	graft_grp;
1334c60d41a9Swiz     int 	i;
1335c60d41a9Swiz     struct rtentry *r;
1336c60d41a9Swiz     struct gtable *g;
1337c60d41a9Swiz     struct ptable *pt, **ptnp;
1338c60d41a9Swiz 
1339c60d41a9Swiz     if ((vifi = find_vif(src, dst)) == NO_VIF) {
1340c60d41a9Swiz 	logit(LOG_INFO, 0,
1341c60d41a9Swiz     	    "ignoring graft from non-neighbor %s",
1342ee70b5d6Sdsl 	    inet_fmt(src));
1343c60d41a9Swiz 	return;
1344c60d41a9Swiz     }
1345c60d41a9Swiz 
1346c60d41a9Swiz     if (datalen < 8) {
1347c60d41a9Swiz 	logit(LOG_WARNING, 0,
1348c60d41a9Swiz 	    "received non-decipherable graft from %s",
1349ee70b5d6Sdsl 	    inet_fmt(src));
1350c60d41a9Swiz 	return;
1351c60d41a9Swiz     }
1352c60d41a9Swiz 
1353c60d41a9Swiz     for (i = 0; i< 4; i++)
1354c60d41a9Swiz 	((char *)&graft_src)[i] = *p++;
1355c60d41a9Swiz     for (i = 0; i< 4; i++)
1356c60d41a9Swiz 	((char *)&graft_grp)[i] = *p++;
1357c60d41a9Swiz 
1358c60d41a9Swiz     logit(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)",
1359ee70b5d6Sdsl 	inet_fmt(src), vifi,
1360ee70b5d6Sdsl 	inet_fmt(graft_src), inet_fmt(graft_grp));
1361c60d41a9Swiz 
1362c60d41a9Swiz     /*
1363c60d41a9Swiz      * Find the subnet for the graft
1364c60d41a9Swiz      */
1365c60d41a9Swiz     if (find_src_grp(graft_src, 0, graft_grp)) {
1366c60d41a9Swiz 	g = gtp ? gtp->gt_gnext : kernel_table;
1367c60d41a9Swiz 	r = g->gt_route;
1368c60d41a9Swiz 
1369c60d41a9Swiz 	if (VIFM_ISSET(vifi, g->gt_scope)) {
1370c60d41a9Swiz 	    logit(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)",
1371ee70b5d6Sdsl 		inet_fmt(src), inet_fmt(graft_src),
1372ee70b5d6Sdsl 		inet_fmt(graft_grp));
1373c60d41a9Swiz 	    return;
1374c60d41a9Swiz 	}
1375c60d41a9Swiz 
1376c60d41a9Swiz 	ptnp = &g->gt_pruntbl;
1377c60d41a9Swiz 	while ((pt = *ptnp) != NULL) {
1378c60d41a9Swiz 	    if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) {
1379c60d41a9Swiz 		*ptnp = pt->pt_next;
1380c60d41a9Swiz 		free(pt);
1381c60d41a9Swiz 
1382c60d41a9Swiz 		VIFM_SET(vifi, g->gt_grpmems);
1383c60d41a9Swiz 		logit(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
1384ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
1385ee70b5d6Sdsl 		    inet_fmt(g->gt_mcastgrp), g->gt_grpmems);
1386c60d41a9Swiz 
1387c60d41a9Swiz 		prun_add_ttls(g);
1388c60d41a9Swiz 		update_kernel(g);
1389c60d41a9Swiz #ifdef RSRR
1390c60d41a9Swiz 		/* Send route change notification to reservation protocol. */
1391c60d41a9Swiz 		rsrr_cache_send(g,1);
1392c60d41a9Swiz #endif /* RSRR */
1393c60d41a9Swiz 		break;
1394c60d41a9Swiz 	    } else {
1395c60d41a9Swiz 		ptnp = &pt->pt_next;
1396c60d41a9Swiz 	    }
1397c60d41a9Swiz 	}
1398c60d41a9Swiz 
1399c60d41a9Swiz 	/* send ack downstream */
1400c60d41a9Swiz 	send_graft_ack(dst, src, graft_src, graft_grp);
1401c60d41a9Swiz 	g->gt_timer = max_prune_lifetime;
1402c60d41a9Swiz 
1403c60d41a9Swiz 	if (g->gt_prsent_timer) {
1404c60d41a9Swiz 	    /* set the flag for graft retransmission */
1405c60d41a9Swiz 	    g->gt_grftsnt = 1;
1406c60d41a9Swiz 
1407c60d41a9Swiz 	    /* send graft upwards */
1408c60d41a9Swiz 	    send_graft(g);
1409c60d41a9Swiz 
1410c60d41a9Swiz 	    /* reset the prune sent timer */
1411c60d41a9Swiz 	    g->gt_prsent_timer = 0;
1412c60d41a9Swiz 	}
1413c60d41a9Swiz     } else {
1414c60d41a9Swiz 	/*
1415c60d41a9Swiz 	 * We have no state for the source and group in question.
1416c60d41a9Swiz 	 * We can simply acknowledge the graft, since we know
1417c60d41a9Swiz 	 * that we have no prune state, and grafts are requests
1418c60d41a9Swiz 	 * to remove prune state.
1419c60d41a9Swiz 	 */
1420c60d41a9Swiz 	send_graft_ack(dst, src, graft_src, graft_grp);
1421c60d41a9Swiz 	logit(LOG_DEBUG, 0, "%s (%s %s) from %s",
1422c60d41a9Swiz 	    "graft received with no kernel entry for",
1423ee70b5d6Sdsl 	    inet_fmt(graft_src), inet_fmt(graft_grp),
1424ee70b5d6Sdsl 	    inet_fmt(src));
1425c60d41a9Swiz 	return;
1426c60d41a9Swiz     }
1427c60d41a9Swiz }
1428c60d41a9Swiz 
1429c60d41a9Swiz /*
1430c60d41a9Swiz  * find out which group is involved first of all
1431c60d41a9Swiz  * then determine if a graft was sent.
1432c60d41a9Swiz  * if no graft sent, ignore the message
1433c60d41a9Swiz  * if graft was sent and the ack is from the right
1434c60d41a9Swiz  * source, remove the graft timer so that we don't
1435c60d41a9Swiz  * have send a graft again
1436c60d41a9Swiz  */
1437c60d41a9Swiz void
accept_g_ack(u_int32_t src,u_int32_t dst,char * p,int datalen)1438c60d41a9Swiz accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
1439c60d41a9Swiz {
1440c60d41a9Swiz     struct gtable *g;
1441c60d41a9Swiz     vifi_t 	vifi;
1442c60d41a9Swiz     u_int32_t 	grft_src;
1443c60d41a9Swiz     u_int32_t	grft_grp;
1444c60d41a9Swiz     int 	i;
1445c60d41a9Swiz 
1446c60d41a9Swiz     if ((vifi = find_vif(src, dst)) == NO_VIF) {
1447c60d41a9Swiz 	logit(LOG_INFO, 0,
1448c60d41a9Swiz     	    "ignoring graft ack from non-neighbor %s",
1449ee70b5d6Sdsl 	    inet_fmt(src));
1450c60d41a9Swiz 	return;
1451c60d41a9Swiz     }
1452c60d41a9Swiz 
1453c60d41a9Swiz     if (datalen < 0  || datalen > 8) {
1454c60d41a9Swiz 	logit(LOG_WARNING, 0,
1455c60d41a9Swiz 	    "received non-decipherable graft ack from %s",
1456ee70b5d6Sdsl 	    inet_fmt(src));
1457c60d41a9Swiz 	return;
1458c60d41a9Swiz     }
1459c60d41a9Swiz 
1460c60d41a9Swiz     for (i = 0; i< 4; i++)
1461c60d41a9Swiz 	((char *)&grft_src)[i] = *p++;
1462c60d41a9Swiz     for (i = 0; i< 4; i++)
1463c60d41a9Swiz 	((char *)&grft_grp)[i] = *p++;
1464c60d41a9Swiz 
1465c60d41a9Swiz     logit(LOG_DEBUG, 0, "%s on vif %d acks graft (%s, %s)",
1466ee70b5d6Sdsl 	inet_fmt(src), vifi,
1467ee70b5d6Sdsl 	inet_fmt(grft_src), inet_fmt(grft_grp));
1468c60d41a9Swiz 
1469c60d41a9Swiz     /*
1470c60d41a9Swiz      * Find the subnet for the graft ack
1471c60d41a9Swiz      */
1472c60d41a9Swiz     if (find_src_grp(grft_src, 0, grft_grp)) {
1473c60d41a9Swiz 	g = gtp ? gtp->gt_gnext : kernel_table;
1474c60d41a9Swiz 	g->gt_grftsnt = 0;
1475c60d41a9Swiz     } else {
1476c60d41a9Swiz 	logit(LOG_WARNING, 0, "%s (%s, %s) from %s",
1477c60d41a9Swiz 	    "rcvd graft ack with no kernel entry for",
1478ee70b5d6Sdsl 	    inet_fmt(grft_src), inet_fmt(grft_grp),
1479ee70b5d6Sdsl 	    inet_fmt(src));
1480c60d41a9Swiz 	return;
1481c60d41a9Swiz     }
1482c60d41a9Swiz }
1483c60d41a9Swiz 
1484c60d41a9Swiz 
1485c60d41a9Swiz /*
1486c60d41a9Swiz  * free all prune entries and kernel routes
1487c60d41a9Swiz  * normally, this should inform the kernel that all of its routes
1488c60d41a9Swiz  * are going away, but this is only called by restart(), which is
1489c60d41a9Swiz  * about to call MRT_DONE which does that anyway.
1490c60d41a9Swiz  */
1491c60d41a9Swiz void
free_all_prunes(void)1492c60d41a9Swiz free_all_prunes(void)
1493c60d41a9Swiz {
1494c60d41a9Swiz     struct rtentry *r;
1495c60d41a9Swiz     struct gtable *g, *prev_g;
1496c60d41a9Swiz     struct stable *s, *prev_s;
1497c60d41a9Swiz     struct ptable *p, *prev_p;
1498c60d41a9Swiz 
1499c60d41a9Swiz     for (r = routing_table; r; r = r->rt_next) {
1500c60d41a9Swiz 	g = r->rt_groups;
1501c60d41a9Swiz 	while (g) {
1502c60d41a9Swiz 	    s = g->gt_srctbl;
1503c60d41a9Swiz 	    while (s) {
1504c60d41a9Swiz 		prev_s = s;
1505c60d41a9Swiz 		s = s->st_next;
1506c60d41a9Swiz 		free(prev_s);
1507c60d41a9Swiz 	    }
1508c60d41a9Swiz 
1509c60d41a9Swiz 	    p = g->gt_pruntbl;
1510c60d41a9Swiz 	    while (p) {
1511c60d41a9Swiz 		prev_p = p;
1512c60d41a9Swiz 		p = p->pt_next;
1513c60d41a9Swiz 		free(prev_p);
1514c60d41a9Swiz 	    }
1515c60d41a9Swiz 
1516c60d41a9Swiz 	    prev_g = g;
1517c60d41a9Swiz 	    g = g->gt_next;
1518c60d41a9Swiz 	    free(prev_g);
1519c60d41a9Swiz 	}
1520c60d41a9Swiz 	r->rt_groups = NULL;
1521c60d41a9Swiz     }
1522c60d41a9Swiz     kernel_table = NULL;
1523c60d41a9Swiz 
1524c60d41a9Swiz     g = kernel_no_route;
1525c60d41a9Swiz     while (g) {
1526c60d41a9Swiz 	if (g->gt_srctbl)
1527c60d41a9Swiz 	    free(g->gt_srctbl);
1528c60d41a9Swiz 
1529c60d41a9Swiz 	prev_g = g;
1530c60d41a9Swiz 	g = g->gt_next;
1531c60d41a9Swiz 	free(prev_g);
1532c60d41a9Swiz     }
1533c60d41a9Swiz     kernel_no_route = NULL;
1534c60d41a9Swiz }
1535c60d41a9Swiz 
1536c60d41a9Swiz /*
1537c60d41a9Swiz  * When a new route is created, search
1538c60d41a9Swiz  * a) The less-specific part of the routing table
1539c60d41a9Swiz  * b) The route-less kernel table
1540c60d41a9Swiz  * for sources that the new route might want to handle.
1541c60d41a9Swiz  *
1542c60d41a9Swiz  * "Inheriting" these sources might be cleanest, but simply deleting
1543c60d41a9Swiz  * them is easier, and letting the kernel re-request them.
1544c60d41a9Swiz  */
1545c60d41a9Swiz void
steal_sources(struct rtentry * rt)1546c60d41a9Swiz steal_sources(struct rtentry *rt)
1547c60d41a9Swiz {
1548c60d41a9Swiz     struct rtentry *rp;
1549c60d41a9Swiz     struct gtable *gt, **gtnp;
1550c60d41a9Swiz     struct stable *st, **stnp;
1551c60d41a9Swiz 
1552c60d41a9Swiz     for (rp = rt->rt_next; rp; rp = rp->rt_next) {
1553c60d41a9Swiz 	if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
1554c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Route for %s stealing sources from %s",
1555ee70b5d6Sdsl 		inet_fmts(rt->rt_origin, rt->rt_originmask),
1556ee70b5d6Sdsl 		inet_fmts(rp->rt_origin, rp->rt_originmask));
1557c60d41a9Swiz 	    for (gt = rp->rt_groups; gt; gt = gt->gt_next) {
1558c60d41a9Swiz 		stnp = &gt->gt_srctbl;
1559c60d41a9Swiz 		while ((st = *stnp) != NULL) {
1560c60d41a9Swiz 		    if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) {
1561c60d41a9Swiz 			logit(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
1562ee70b5d6Sdsl 			    inet_fmts(rt->rt_origin, rt->rt_originmask),
1563ee70b5d6Sdsl 			    inet_fmt(st->st_origin),
1564ee70b5d6Sdsl 			    inet_fmt(gt->gt_mcastgrp),
1565ee70b5d6Sdsl 			    inet_fmts(rp->rt_origin, rp->rt_originmask));
1566c60d41a9Swiz 			if (k_del_rg(st->st_origin, gt) < 0) {
1567c60d41a9Swiz 			    logit(LOG_WARNING, errno, "%s (%s, %s)",
1568c60d41a9Swiz 				"steal_sources trying to delete",
1569ee70b5d6Sdsl 				inet_fmt(st->st_origin),
1570ee70b5d6Sdsl 				inet_fmt(gt->gt_mcastgrp));
1571c60d41a9Swiz 			}
1572c60d41a9Swiz 			*stnp = st->st_next;
1573c60d41a9Swiz 			kroutes--;
1574c60d41a9Swiz 			free(st);
1575c60d41a9Swiz 		    } else {
1576c60d41a9Swiz 			stnp = &st->st_next;
1577c60d41a9Swiz 		    }
1578c60d41a9Swiz 		}
1579c60d41a9Swiz 	    }
1580c60d41a9Swiz 	}
1581c60d41a9Swiz     }
1582c60d41a9Swiz 
1583c60d41a9Swiz     gtnp = &kernel_no_route;
1584c60d41a9Swiz     while ((gt = *gtnp) != NULL) {
1585c60d41a9Swiz 	if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask)
1586c60d41a9Swiz 				    == rt->rt_origin)) {
1587c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
1588ee70b5d6Sdsl 		inet_fmts(rt->rt_origin, rt->rt_originmask),
1589ee70b5d6Sdsl 		inet_fmt(gt->gt_srctbl->st_origin),
1590ee70b5d6Sdsl 		inet_fmt(gt->gt_mcastgrp),
1591c60d41a9Swiz 		"no_route table");
1592c60d41a9Swiz 	    if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
1593c60d41a9Swiz 		logit(LOG_WARNING, errno, "%s (%s %s)",
1594c60d41a9Swiz 		    "steal_sources trying to delete",
1595ee70b5d6Sdsl 		    inet_fmt(gt->gt_srctbl->st_origin),
1596ee70b5d6Sdsl 		    inet_fmt(gt->gt_mcastgrp));
1597c60d41a9Swiz 	    }
1598c60d41a9Swiz 	    kroutes--;
1599c60d41a9Swiz 	    free(gt->gt_srctbl);
1600c60d41a9Swiz 	    *gtnp = gt->gt_next;
1601c60d41a9Swiz 	    if (gt->gt_next)
1602c60d41a9Swiz 		gt->gt_next->gt_prev = gt->gt_prev;
1603c60d41a9Swiz 	    free(gt);
1604c60d41a9Swiz 	} else {
1605c60d41a9Swiz 	    gtnp = &gt->gt_next;
1606c60d41a9Swiz 	}
1607c60d41a9Swiz     }
1608c60d41a9Swiz }
1609c60d41a9Swiz 
1610c60d41a9Swiz /*
1611c60d41a9Swiz  * Advance the timers on all the cache entries.
1612c60d41a9Swiz  * If there are any entries whose timers have expired,
1613c60d41a9Swiz  * remove these entries from the kernel cache.
1614c60d41a9Swiz  */
1615c60d41a9Swiz void
age_table_entry(void)1616c60d41a9Swiz age_table_entry(void)
1617c60d41a9Swiz {
1618c60d41a9Swiz     struct rtentry *r;
1619c60d41a9Swiz     struct gtable *gt, **gtnptr;
1620c60d41a9Swiz     struct stable *st, **stnp;
1621c60d41a9Swiz     struct ptable *pt, **ptnp;
1622c60d41a9Swiz     struct sioc_sg_req sg_req;
1623c60d41a9Swiz 
1624c60d41a9Swiz     logit(LOG_DEBUG, 0, "ageing entries");
1625c60d41a9Swiz 
1626c60d41a9Swiz     gtnptr = &kernel_table;
1627c60d41a9Swiz     while ((gt = *gtnptr) != NULL) {
1628c60d41a9Swiz 	r = gt->gt_route;
1629c60d41a9Swiz 
1630c60d41a9Swiz 	/* advance the timer for the kernel entry */
1631c60d41a9Swiz 	gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
1632c60d41a9Swiz 
1633c60d41a9Swiz 	/* decrement prune timer if need be */
1634c60d41a9Swiz 	if (gt->gt_prsent_timer > 0) {
1635c60d41a9Swiz 	    gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY;
1636c60d41a9Swiz 	    if (gt->gt_prsent_timer <= 0) {
1637c60d41a9Swiz 		logit(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
1638ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
1639ee70b5d6Sdsl 		    inet_fmt(gt->gt_mcastgrp));
1640c60d41a9Swiz 		gt->gt_prsent_timer = -1;
1641c60d41a9Swiz 	    }
1642c60d41a9Swiz 	}
1643c60d41a9Swiz 
1644c60d41a9Swiz 	/* retransmit graft if graft sent flag is still set */
1645c60d41a9Swiz 	if (gt->gt_grftsnt) {
1646c60d41a9Swiz 	    int y;
1647c60d41a9Swiz 	    CHK_GS(gt->gt_grftsnt++, y);
1648c60d41a9Swiz 	    if (y)
1649c60d41a9Swiz 		send_graft(gt);
1650c60d41a9Swiz 	}
1651c60d41a9Swiz 
1652c60d41a9Swiz 	/*
1653c60d41a9Swiz 	 * Age prunes
1654c60d41a9Swiz 	 *
1655c60d41a9Swiz 	 * If a prune expires, forward again on that vif.
1656c60d41a9Swiz 	 */
1657c60d41a9Swiz 	ptnp = &gt->gt_pruntbl;
1658c60d41a9Swiz 	while ((pt = *ptnp) != NULL) {
1659c60d41a9Swiz 	    if ((pt->pt_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) {
1660c60d41a9Swiz 		logit(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
1661ee70b5d6Sdsl 		    inet_fmts(r->rt_origin, r->rt_originmask),
1662ee70b5d6Sdsl 		    inet_fmt(gt->gt_mcastgrp),
1663ee70b5d6Sdsl 		    inet_fmt(pt->pt_router),
1664c60d41a9Swiz 		    pt->pt_vifi);
1665c60d41a9Swiz 
1666c60d41a9Swiz 		expire_prune(pt->pt_vifi, gt);
1667c60d41a9Swiz 
1668c60d41a9Swiz 		/* remove the router's prune entry and await new one */
1669c60d41a9Swiz 		*ptnp = pt->pt_next;
1670c60d41a9Swiz 		free(pt);
1671c60d41a9Swiz 	    } else {
1672c60d41a9Swiz 		ptnp = &pt->pt_next;
1673c60d41a9Swiz 	    }
1674c60d41a9Swiz 	}
1675c60d41a9Swiz 
1676c60d41a9Swiz 	/*
1677c60d41a9Swiz 	 * If the cache entry has expired, delete source table entries for
1678c60d41a9Swiz 	 * silent sources.  If there are no source entries left, and there
1679c60d41a9Swiz 	 * are no downstream prunes, then the entry is deleted.
1680c60d41a9Swiz 	 * Otherwise, the cache entry's timer is refreshed.
1681c60d41a9Swiz 	 */
1682c60d41a9Swiz 	if (gt->gt_timer <= 0) {
1683c60d41a9Swiz 	    /* Check for traffic before deleting source entries */
1684c60d41a9Swiz 	    sg_req.grp.s_addr = gt->gt_mcastgrp;
1685c60d41a9Swiz 	    stnp = &gt->gt_srctbl;
1686c60d41a9Swiz 	    while ((st = *stnp) != NULL) {
1687c60d41a9Swiz 		sg_req.src.s_addr = st->st_origin;
1688c60d41a9Swiz 		if (ioctl(igmp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
1689c60d41a9Swiz 		    logit(LOG_WARNING, errno, "%s (%s %s)",
1690c60d41a9Swiz 			"age_table_entry: SIOCGETSGCNT failing for",
1691ee70b5d6Sdsl 			inet_fmt(st->st_origin),
1692ee70b5d6Sdsl 			inet_fmt(gt->gt_mcastgrp));
1693c60d41a9Swiz 		    /* Make sure it gets deleted below */
1694c60d41a9Swiz 		    sg_req.pktcnt = st->st_pktcnt;
1695c60d41a9Swiz 		}
1696c60d41a9Swiz 		if (sg_req.pktcnt == st->st_pktcnt) {
1697c60d41a9Swiz 		    *stnp = st->st_next;
1698c60d41a9Swiz 		    logit(LOG_DEBUG, 0, "age_table_entry deleting (%s %s)",
1699ee70b5d6Sdsl 			inet_fmt(st->st_origin),
1700ee70b5d6Sdsl 			inet_fmt(gt->gt_mcastgrp));
1701c60d41a9Swiz 		    if (k_del_rg(st->st_origin, gt) < 0) {
1702c60d41a9Swiz 			logit(LOG_WARNING, errno,
1703c60d41a9Swiz 			    "age_table_entry trying to delete (%s %s)",
1704ee70b5d6Sdsl 			    inet_fmt(st->st_origin),
1705ee70b5d6Sdsl 			    inet_fmt(gt->gt_mcastgrp));
1706c60d41a9Swiz 		    }
1707c60d41a9Swiz 		    kroutes--;
1708c60d41a9Swiz 		    free(st);
1709c60d41a9Swiz 		} else {
1710c60d41a9Swiz 		    st->st_pktcnt = sg_req.pktcnt;
1711c60d41a9Swiz 		    stnp = &st->st_next;
1712c60d41a9Swiz 		}
1713c60d41a9Swiz 	    }
1714c60d41a9Swiz 
1715c60d41a9Swiz 	    /*
1716c60d41a9Swiz 	     * Retain the group entry if we have downstream prunes or if
1717c60d41a9Swiz 	     * there is at least one source in the list that still has
1718c60d41a9Swiz 	     * traffic, or if our upstream prune timer is running.
1719c60d41a9Swiz 	     */
1720c60d41a9Swiz 	    if (gt->gt_pruntbl != NULL || gt->gt_srctbl != NULL ||
1721c60d41a9Swiz 		gt->gt_prsent_timer > 0) {
1722c60d41a9Swiz 		gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
1723c60d41a9Swiz 		if (gt->gt_prsent_timer == -1) {
1724c60d41a9Swiz 		    if (gt->gt_grpmems == 0)
1725c60d41a9Swiz 			send_prune(gt);
1726c60d41a9Swiz 		    else
1727c60d41a9Swiz 			gt->gt_prsent_timer = 0;
1728c60d41a9Swiz 		}
1729c60d41a9Swiz 		gtnptr = &gt->gt_gnext;
1730c60d41a9Swiz 		continue;
1731c60d41a9Swiz 	    }
1732c60d41a9Swiz 
1733c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
1734ee70b5d6Sdsl 		inet_fmts(r->rt_origin, r->rt_originmask),
1735ee70b5d6Sdsl 		inet_fmt(gt->gt_mcastgrp));
1736c60d41a9Swiz 
1737c60d41a9Swiz 	    if (gt->gt_prev)
1738c60d41a9Swiz 		gt->gt_prev->gt_next = gt->gt_next;
1739c60d41a9Swiz 	    else
1740c60d41a9Swiz 		gt->gt_route->rt_groups = gt->gt_next;
1741c60d41a9Swiz 	    if (gt->gt_next)
1742c60d41a9Swiz 		gt->gt_next->gt_prev = gt->gt_prev;
1743c60d41a9Swiz 
1744c60d41a9Swiz 	    if (gt->gt_gprev) {
1745c60d41a9Swiz 		gt->gt_gprev->gt_gnext = gt->gt_gnext;
1746c60d41a9Swiz 		gtnptr = &gt->gt_gprev->gt_gnext;
1747c60d41a9Swiz 	    } else {
1748c60d41a9Swiz 		kernel_table = gt->gt_gnext;
1749c60d41a9Swiz 		gtnptr = &kernel_table;
1750c60d41a9Swiz 	    }
1751c60d41a9Swiz 	    if (gt->gt_gnext)
1752c60d41a9Swiz 		gt->gt_gnext->gt_gprev = gt->gt_gprev;
1753c60d41a9Swiz 
1754c60d41a9Swiz #ifdef RSRR
1755c60d41a9Swiz 	    /* Send route change notification to reservation protocol. */
1756c60d41a9Swiz 	    rsrr_cache_send(gt,0);
1757c60d41a9Swiz 	    rsrr_cache_clean(gt);
1758c60d41a9Swiz #endif /* RSRR */
1759c60d41a9Swiz 	    free((char *)gt);
1760c60d41a9Swiz 	} else {
1761c60d41a9Swiz 	    if (gt->gt_prsent_timer == -1) {
1762c60d41a9Swiz 		if (gt->gt_grpmems == 0)
1763c60d41a9Swiz 		    send_prune(gt);
1764c60d41a9Swiz 		else
1765c60d41a9Swiz 		    gt->gt_prsent_timer = 0;
1766c60d41a9Swiz 	    }
1767c60d41a9Swiz 	    gtnptr = &gt->gt_gnext;
1768c60d41a9Swiz 	}
1769c60d41a9Swiz     }
1770c60d41a9Swiz 
1771c60d41a9Swiz     /*
1772c60d41a9Swiz      * When traversing the no_route table, the decision is much easier.
1773c60d41a9Swiz      * Just delete it if it has timed out.
1774c60d41a9Swiz      */
1775c60d41a9Swiz     gtnptr = &kernel_no_route;
1776c60d41a9Swiz     while ((gt = *gtnptr) != NULL) {
1777c60d41a9Swiz 	/* advance the timer for the kernel entry */
1778c60d41a9Swiz 	gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
1779c60d41a9Swiz 
1780c60d41a9Swiz 	if (gt->gt_timer < 0) {
1781c60d41a9Swiz 	    if (gt->gt_srctbl) {
1782c60d41a9Swiz 		if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
1783c60d41a9Swiz 		    logit(LOG_WARNING, errno, "%s (%s %s)",
1784c60d41a9Swiz 			"age_table_entry trying to delete no-route",
1785ee70b5d6Sdsl 			inet_fmt(gt->gt_srctbl->st_origin),
1786ee70b5d6Sdsl 			inet_fmt(gt->gt_mcastgrp));
1787c60d41a9Swiz 		}
1788c60d41a9Swiz 		free(gt->gt_srctbl);
1789c60d41a9Swiz 	    }
1790c60d41a9Swiz 	    *gtnptr = gt->gt_next;
1791c60d41a9Swiz 	    if (gt->gt_next)
1792c60d41a9Swiz 		gt->gt_next->gt_prev = gt->gt_prev;
1793c60d41a9Swiz 
1794c60d41a9Swiz 	    free((char *)gt);
1795c60d41a9Swiz 	} else {
1796c60d41a9Swiz 	    gtnptr = &gt->gt_next;
1797c60d41a9Swiz 	}
1798c60d41a9Swiz     }
1799c60d41a9Swiz }
1800c60d41a9Swiz 
1801c60d41a9Swiz /*
1802c60d41a9Swiz  * Modify the kernel to forward packets when one or multiple prunes that
1803c60d41a9Swiz  * were received on the vif given by vifi, for the group given by gt,
1804c60d41a9Swiz  * have expired.
1805c60d41a9Swiz  */
1806c60d41a9Swiz static void
expire_prune(vifi_t vifi,struct gtable * gt)1807c60d41a9Swiz expire_prune(vifi_t vifi, struct gtable *gt)
1808c60d41a9Swiz {
1809c60d41a9Swiz     /*
1810c60d41a9Swiz      * No need to send a graft, any prunes that we sent
1811c60d41a9Swiz      * will expire before any prunes that we have received.
1812c60d41a9Swiz      */
1813c60d41a9Swiz     if (gt->gt_prsent_timer > 0) {
1814c60d41a9Swiz         logit(LOG_DEBUG, 0, "prune expired with %d left on %s",
1815c60d41a9Swiz 		gt->gt_prsent_timer, "prsent_timer");
1816c60d41a9Swiz         gt->gt_prsent_timer = 0;
1817c60d41a9Swiz     }
1818c60d41a9Swiz 
1819c60d41a9Swiz     /* modify the kernel entry to forward packets */
1820c60d41a9Swiz     if (!VIFM_ISSET(vifi, gt->gt_grpmems)) {
1821c60d41a9Swiz         struct rtentry *rt = gt->gt_route;
1822c60d41a9Swiz         VIFM_SET(vifi, gt->gt_grpmems);
1823c60d41a9Swiz         logit(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
1824ee70b5d6Sdsl 	inet_fmts(rt->rt_origin, rt->rt_originmask),
1825ee70b5d6Sdsl 	inet_fmt(gt->gt_mcastgrp), gt->gt_grpmems, vifi);
1826c60d41a9Swiz 
1827c60d41a9Swiz         prun_add_ttls(gt);
1828c60d41a9Swiz         update_kernel(gt);
1829c60d41a9Swiz #ifdef RSRR
1830c60d41a9Swiz         /* Send route change notification to reservation protocol. */
1831c60d41a9Swiz         rsrr_cache_send(gt,1);
1832c60d41a9Swiz #endif /* RSRR */
1833c60d41a9Swiz     }
1834c60d41a9Swiz }
1835c60d41a9Swiz 
1836c60d41a9Swiz 
1837*58cb611aSlukem static const char *
scaletime(u_long t)1838c60d41a9Swiz scaletime(u_long t)
1839c60d41a9Swiz {
1840c60d41a9Swiz     static char buf1[5];
1841c60d41a9Swiz     static char buf2[5];
1842c60d41a9Swiz     static char *buf=buf1;
1843c60d41a9Swiz     char s;
1844c60d41a9Swiz     char *p;
1845c60d41a9Swiz 
1846c60d41a9Swiz     p = buf;
1847c60d41a9Swiz     if (buf == buf1)
1848c60d41a9Swiz 	buf = buf2;
1849c60d41a9Swiz     else
1850c60d41a9Swiz 	buf = buf1;
1851c60d41a9Swiz 
1852c60d41a9Swiz     if (t < 120) {
1853c60d41a9Swiz 	s = 's';
1854c60d41a9Swiz     } else if (t < 3600) {
1855c60d41a9Swiz 	t /= 60;
1856c60d41a9Swiz 	s = 'm';
1857c60d41a9Swiz     } else if (t < 86400) {
1858c60d41a9Swiz 	t /= 3600;
1859c60d41a9Swiz 	s = 'h';
1860c60d41a9Swiz     } else if (t < 864000) {
1861c60d41a9Swiz 	t /= 86400;
1862c60d41a9Swiz 	s = 'd';
1863c60d41a9Swiz     } else {
1864c60d41a9Swiz 	t /= 604800;
1865c60d41a9Swiz 	s = 'w';
1866c60d41a9Swiz     }
1867c60d41a9Swiz     if (t > 999)
1868c60d41a9Swiz 	return "*** ";
1869c60d41a9Swiz 
1870761f7bebSitojun     snprintf(p, 5, "%3d%c", (int)t, s);
1871c60d41a9Swiz 
1872c60d41a9Swiz     return p;
1873c60d41a9Swiz }
1874c60d41a9Swiz 
1875c60d41a9Swiz /*
1876c60d41a9Swiz  * Print the contents of the cache table on file 'fp2'.
1877c60d41a9Swiz  */
1878c60d41a9Swiz void
dump_cache(FILE * fp2)1879c60d41a9Swiz dump_cache(FILE *fp2)
1880c60d41a9Swiz {
1881c60d41a9Swiz     struct rtentry *r;
1882c60d41a9Swiz     struct gtable *gt;
1883c60d41a9Swiz     struct stable *st;
1884c60d41a9Swiz     vifi_t i;
1885c60d41a9Swiz     time_t thyme = time(0);
1886c60d41a9Swiz 
1887c60d41a9Swiz     fprintf(fp2,
1888c60d41a9Swiz 	    "Multicast Routing Cache Table (%d entries)\n%s", kroutes,
1889c60d41a9Swiz     " Origin             Mcast-group     CTmr  Age Ptmr IVif Forwvifs\n");
1890c60d41a9Swiz 
1891c60d41a9Swiz     for (gt = kernel_no_route; gt; gt = gt->gt_next) {
1892c60d41a9Swiz 	if (gt->gt_srctbl) {
1893c60d41a9Swiz 	    fprintf(fp2, " %-18s %-15s %-4s %-4s    - -1\n",
1894ee70b5d6Sdsl 		inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff),
1895ee70b5d6Sdsl 		inet_fmt(gt->gt_mcastgrp), scaletime(gt->gt_timer),
1896c60d41a9Swiz 		scaletime(thyme - gt->gt_ctime));
1897ee70b5d6Sdsl 	    fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin));
1898c60d41a9Swiz 	}
1899c60d41a9Swiz     }
1900c60d41a9Swiz 
1901c60d41a9Swiz     for (gt = kernel_table; gt; gt = gt->gt_gnext) {
1902c60d41a9Swiz 	r = gt->gt_route;
1903c60d41a9Swiz 	fprintf(fp2, " %-18s %-15s",
1904ee70b5d6Sdsl 	    inet_fmts(r->rt_origin, r->rt_originmask),
1905ee70b5d6Sdsl 	    inet_fmt(gt->gt_mcastgrp));
1906c60d41a9Swiz 
1907c60d41a9Swiz 	fprintf(fp2, " %-4s", scaletime(gt->gt_timer));
1908c60d41a9Swiz 
1909c60d41a9Swiz 	fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime),
1910c60d41a9Swiz 			gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
1911c60d41a9Swiz 					      "   -");
1912c60d41a9Swiz 
1913c60d41a9Swiz 	fprintf(fp2, "%2u%c%c ", r->rt_parent,
1914c60d41a9Swiz 	    gt->gt_prsent_timer ? 'P' : ' ',
1915c60d41a9Swiz 	    VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' ');
1916c60d41a9Swiz 
1917c60d41a9Swiz 	for (i = 0; i < numvifs; ++i) {
1918c60d41a9Swiz 	    if (VIFM_ISSET(i, gt->gt_grpmems))
1919c60d41a9Swiz 		fprintf(fp2, " %u ", i);
1920c60d41a9Swiz 	    else if (VIFM_ISSET(i, r->rt_children) &&
1921c60d41a9Swiz 		     !VIFM_ISSET(i, r->rt_leaves))
1922c60d41a9Swiz 		fprintf(fp2, " %u%c", i,
1923c60d41a9Swiz 			VIFM_ISSET(i, gt->gt_scope) ? 'b' : 'p');
1924c60d41a9Swiz 	}
1925c60d41a9Swiz 	fprintf(fp2, "\n");
1926c60d41a9Swiz 	for (st = gt->gt_srctbl; st; st = st->st_next) {
1927ee70b5d6Sdsl 	    fprintf(fp2, ">%s\n", inet_fmt(st->st_origin));
1928c60d41a9Swiz 	}
1929c60d41a9Swiz #ifdef DEBUG_PRUNES
1930c60d41a9Swiz 	for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) {
1931ee70b5d6Sdsl 	    fprintf(fp2, "<r:%s v:%d t:%d\n", inet_fmt(pt->pt_router),
1932c60d41a9Swiz 		pt->pt_vifi, pt->pt_timer);
1933c60d41a9Swiz 	}
1934c60d41a9Swiz #endif
1935c60d41a9Swiz     }
1936c60d41a9Swiz }
1937c60d41a9Swiz 
1938c60d41a9Swiz /*
1939c60d41a9Swiz  * Traceroute function which returns traceroute replies to the requesting
1940c60d41a9Swiz  * router. Also forwards the request to downstream routers.
1941c60d41a9Swiz  *
1942c60d41a9Swiz  * no: promoted u_char
1943c60d41a9Swiz  */
1944c60d41a9Swiz void
accept_mtrace(u_int32_t src,u_int32_t dst,u_int32_t group,char * data,u_int no,int datalen)1945c60d41a9Swiz accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
1946c60d41a9Swiz 	      u_int no, int datalen)
1947c60d41a9Swiz {
1948c60d41a9Swiz     u_char type;
1949c60d41a9Swiz     struct rtentry *rt;
1950c60d41a9Swiz     struct gtable *gt;
1951c60d41a9Swiz     struct tr_query *qry;
1952c60d41a9Swiz     struct tr_resp  *resp;
1953c60d41a9Swiz     int vifi;
1954c60d41a9Swiz     char *p;
1955c60d41a9Swiz     int rcount;
1956c60d41a9Swiz     int errcode = TR_NO_ERR;
1957c60d41a9Swiz     int resptype;
1958c60d41a9Swiz     struct timeval tp;
1959c60d41a9Swiz     struct sioc_vif_req v_req;
1960c60d41a9Swiz     struct sioc_sg_req sg_req;
1961c60d41a9Swiz 
1962c60d41a9Swiz     /* Remember qid across invocations */
1963c60d41a9Swiz     static u_int32_t oqid = 0;
1964c60d41a9Swiz 
1965c60d41a9Swiz     /* timestamp the request/response */
1966c60d41a9Swiz     gettimeofday(&tp, 0);
1967c60d41a9Swiz 
1968c60d41a9Swiz     /*
1969c60d41a9Swiz      * Check if it is a query or a response
1970c60d41a9Swiz      */
1971c60d41a9Swiz     if (datalen == QLEN) {
1972c60d41a9Swiz 	type = QUERY;
1973c60d41a9Swiz 	logit(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s",
1974ee70b5d6Sdsl 	    inet_fmt(src), inet_fmt(dst));
1975c60d41a9Swiz     }
1976c60d41a9Swiz     else if ((datalen - QLEN) % RLEN == 0) {
1977c60d41a9Swiz 	type = RESP;
1978c60d41a9Swiz 	logit(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s",
1979ee70b5d6Sdsl 	    inet_fmt(src), inet_fmt(dst));
1980c60d41a9Swiz 	if (IN_MULTICAST(ntohl(dst))) {
1981c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Dropping multicast response");
1982c60d41a9Swiz 	    return;
1983c60d41a9Swiz 	}
1984c60d41a9Swiz     }
1985c60d41a9Swiz     else {
1986c60d41a9Swiz 	logit(LOG_WARNING, 0, "%s from %s to %s",
1987c60d41a9Swiz 	    "Non decipherable traceroute request received",
1988ee70b5d6Sdsl 	    inet_fmt(src), inet_fmt(dst));
1989c60d41a9Swiz 	return;
1990c60d41a9Swiz     }
1991c60d41a9Swiz 
1992c60d41a9Swiz     qry = (struct tr_query *)data;
1993c60d41a9Swiz 
1994c60d41a9Swiz     /*
1995c60d41a9Swiz      * if it is a packet with all reports filled, drop it
1996c60d41a9Swiz      */
1997*58cb611aSlukem     if ((u_int)(rcount = (datalen - QLEN)/RLEN) == no) {
1998c60d41a9Swiz 	logit(LOG_DEBUG, 0, "packet with all reports filled in");
1999c60d41a9Swiz 	return;
2000c60d41a9Swiz     }
2001c60d41a9Swiz 
2002761f7bebSitojun     logit(LOG_DEBUG, 0, "s: %s g: %s d: %s ",
2003ee70b5d6Sdsl 	    inet_fmt(qry->tr_src),
2004ee70b5d6Sdsl 	    inet_fmt(group),
2005ee70b5d6Sdsl 	    inet_fmt(qry->tr_dst));
2006c60d41a9Swiz     logit(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl,
2007ee70b5d6Sdsl 	    inet_fmt(qry->tr_raddr));
2008c60d41a9Swiz     logit(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
2009c60d41a9Swiz 
2010c60d41a9Swiz     /* determine the routing table entry for this traceroute */
2011c60d41a9Swiz     rt = determine_route(qry->tr_src);
2012c60d41a9Swiz     if (rt) {
2013c60d41a9Swiz 	logit(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d",
2014ee70b5d6Sdsl 		rt->rt_parent, inet_fmt(rt->rt_gateway),
2015761f7bebSitojun 		rt->rt_metric);
2016c60d41a9Swiz 	logit(LOG_DEBUG, 0, "rt origin %s",
2017ee70b5d6Sdsl 		inet_fmts(rt->rt_origin, rt->rt_originmask));
2018c60d41a9Swiz     } else
2019c60d41a9Swiz 	logit(LOG_DEBUG, 0, "...no route");
2020c60d41a9Swiz 
2021c60d41a9Swiz     /*
2022c60d41a9Swiz      * Query type packet - check if rte exists
2023c60d41a9Swiz      * Check if the query destination is a vif connected to me.
2024c60d41a9Swiz      * and if so, whether I should start response back
2025c60d41a9Swiz      */
2026c60d41a9Swiz     if (type == QUERY) {
2027c60d41a9Swiz 	if (oqid == qry->tr_qid) {
2028c60d41a9Swiz 	    /*
2029c60d41a9Swiz 	     * If the multicast router is a member of the group being
2030c60d41a9Swiz 	     * queried, and the query is multicasted, then the router can
2031c60d41a9Swiz 	     * receive multiple copies of the same query.  If we have already
2032c60d41a9Swiz 	     * replied to this traceroute, just ignore it this time.
2033c60d41a9Swiz 	     *
2034c60d41a9Swiz 	     * This is not a total solution, but since if this fails you
2035c60d41a9Swiz 	     * only get N copies, N <= the number of interfaces on the router,
2036c60d41a9Swiz 	     * it is not fatal.
2037c60d41a9Swiz 	     */
2038c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
2039c60d41a9Swiz 	    return;
2040c60d41a9Swiz 	}
2041c60d41a9Swiz 
2042c60d41a9Swiz 	if (rt == NULL) {
2043c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s",
2044ee70b5d6Sdsl 		   inet_fmt(qry->tr_src));
2045c60d41a9Swiz 	    if (IN_MULTICAST(ntohl(dst)))
2046c60d41a9Swiz 		return;
2047c60d41a9Swiz 	}
2048c60d41a9Swiz 	vifi = find_vif(qry->tr_dst, 0);
2049c60d41a9Swiz 
2050c60d41a9Swiz 	if (vifi == NO_VIF) {
2051c60d41a9Swiz 	    /* The traceroute destination is not on one of my subnet vifs. */
2052c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Destination %s not an interface",
2053ee70b5d6Sdsl 		   inet_fmt(qry->tr_dst));
2054c60d41a9Swiz 	    if (IN_MULTICAST(ntohl(dst)))
2055c60d41a9Swiz 		return;
2056c60d41a9Swiz 	    errcode = TR_WRONG_IF;
2057c60d41a9Swiz 	} else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
2058761f7bebSitojun 	    logit(LOG_DEBUG, 0,
2059761f7bebSitojun 		    "Destination %s not on forwarding tree for src %s",
2060ee70b5d6Sdsl 		   inet_fmt(qry->tr_dst),
2061ee70b5d6Sdsl 		   inet_fmt(qry->tr_src));
2062c60d41a9Swiz 	    if (IN_MULTICAST(ntohl(dst)))
2063c60d41a9Swiz 		return;
2064c60d41a9Swiz 	    errcode = TR_WRONG_IF;
2065c60d41a9Swiz 	}
2066c60d41a9Swiz     }
2067c60d41a9Swiz     else {
2068c60d41a9Swiz 	/*
2069c60d41a9Swiz 	 * determine which interface the packet came in on
2070c60d41a9Swiz 	 * RESP packets travel hop-by-hop so this either traversed
2071c60d41a9Swiz 	 * a tunnel or came from a directly attached mrouter.
2072c60d41a9Swiz 	 */
2073c60d41a9Swiz 	if ((vifi = find_vif(src, dst)) == NO_VIF) {
2074c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Wrong interface for packet");
2075c60d41a9Swiz 	    errcode = TR_WRONG_IF;
2076c60d41a9Swiz 	}
2077c60d41a9Swiz     }
2078c60d41a9Swiz 
2079c60d41a9Swiz     /* Now that we've decided to send a response, save the qid */
2080c60d41a9Swiz     oqid = qry->tr_qid;
2081c60d41a9Swiz 
2082c60d41a9Swiz     logit(LOG_DEBUG, 0, "Sending traceroute response");
2083c60d41a9Swiz 
2084c60d41a9Swiz     /* copy the packet to the sending buffer */
2085c60d41a9Swiz     p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
2086c60d41a9Swiz 
2087c60d41a9Swiz     bcopy(data, p, datalen);
2088c60d41a9Swiz 
2089c60d41a9Swiz     p += datalen;
2090c60d41a9Swiz 
2091c60d41a9Swiz     /*
2092c60d41a9Swiz      * If there is no room to insert our reply, coopt the previous hop
2093c60d41a9Swiz      * error indication to relay this fact.
2094c60d41a9Swiz      */
2095c60d41a9Swiz     if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) {
2096c60d41a9Swiz 	resp = (struct tr_resp *)p - 1;
2097c60d41a9Swiz 	resp->tr_rflags = TR_NO_SPACE;
2098c60d41a9Swiz 	rt = NULL;
2099c60d41a9Swiz 	goto sendit;
2100c60d41a9Swiz     }
2101c60d41a9Swiz 
2102c60d41a9Swiz     /*
2103c60d41a9Swiz      * fill in initial response fields
2104c60d41a9Swiz      */
2105c60d41a9Swiz     resp = (struct tr_resp *)p;
2106c60d41a9Swiz     bzero(resp, sizeof(struct tr_resp));
2107c60d41a9Swiz     datalen += RLEN;
2108c60d41a9Swiz 
2109c60d41a9Swiz     resp->tr_qarr    = htonl((tp.tv_sec + JAN_1970) << 16) +
2110c60d41a9Swiz 				((tp.tv_usec >> 4) & 0xffff);
2111c60d41a9Swiz 
2112c60d41a9Swiz     resp->tr_rproto  = PROTO_DVMRP;
2113c60d41a9Swiz     if (errcode != TR_NO_ERR) {
2114c60d41a9Swiz 	resp->tr_rflags	 = errcode;
2115c60d41a9Swiz 	rt = NULL;	/* hack to enforce send straight to requestor */
2116c60d41a9Swiz 	goto sendit;
2117c60d41a9Swiz     }
2118c60d41a9Swiz     resp->tr_outaddr = uvifs[vifi].uv_lcl_addr;
2119c60d41a9Swiz     resp->tr_fttl    = uvifs[vifi].uv_threshold;
2120c60d41a9Swiz     resp->tr_rflags  = TR_NO_ERR;
2121c60d41a9Swiz 
2122c60d41a9Swiz     /*
2123c60d41a9Swiz      * obtain # of packets out on interface
2124c60d41a9Swiz      */
2125c60d41a9Swiz     v_req.vifi = vifi;
2126c60d41a9Swiz     if (ioctl(igmp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
2127c60d41a9Swiz 	resp->tr_vifout  =  htonl(v_req.ocount);
2128c60d41a9Swiz 
2129c60d41a9Swiz     /*
2130c60d41a9Swiz      * fill in scoping & pruning information
2131c60d41a9Swiz      */
2132c60d41a9Swiz     if (rt)
2133c60d41a9Swiz 	for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
2134c60d41a9Swiz 	    if (gt->gt_mcastgrp >= group)
2135c60d41a9Swiz 		break;
2136c60d41a9Swiz 	}
2137c60d41a9Swiz     else
2138c60d41a9Swiz 	gt = NULL;
2139c60d41a9Swiz 
2140c60d41a9Swiz     if (gt && gt->gt_mcastgrp == group) {
2141c60d41a9Swiz 	sg_req.src.s_addr = qry->tr_src;
2142c60d41a9Swiz 	sg_req.grp.s_addr = group;
2143c60d41a9Swiz 	if (ioctl(igmp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
2144c60d41a9Swiz 	    resp->tr_pktcnt = htonl(sg_req.pktcnt);
2145c60d41a9Swiz 
2146c60d41a9Swiz 	if (VIFM_ISSET(vifi, gt->gt_scope))
2147c60d41a9Swiz 	    resp->tr_rflags = TR_SCOPED;
2148c60d41a9Swiz 	else if (gt->gt_prsent_timer)
2149c60d41a9Swiz 	    resp->tr_rflags = TR_PRUNED;
2150c60d41a9Swiz 	else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) {
2151c60d41a9Swiz 	    if (VIFM_ISSET(vifi, rt->rt_children) &&
2152c60d41a9Swiz 		!VIFM_ISSET(vifi, rt->rt_leaves))
2153c60d41a9Swiz 		resp->tr_rflags = TR_OPRUNED;
2154c60d41a9Swiz 	    else
2155c60d41a9Swiz 		resp->tr_rflags = TR_NO_FWD;
2156c60d41a9Swiz 	}
2157c60d41a9Swiz     } else {
2158c60d41a9Swiz 	if (scoped_addr(vifi, group))
2159c60d41a9Swiz 	    resp->tr_rflags = TR_SCOPED;
2160c60d41a9Swiz 	else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
2161c60d41a9Swiz 	    resp->tr_rflags = TR_NO_FWD;
2162c60d41a9Swiz     }
2163c60d41a9Swiz 
2164c60d41a9Swiz     /*
2165c60d41a9Swiz      *  if no rte exists, set NO_RTE error
2166c60d41a9Swiz      */
2167c60d41a9Swiz     if (rt == NULL) {
2168c60d41a9Swiz 	src = dst;		/* the dst address of resp. pkt */
2169c60d41a9Swiz 	resp->tr_inaddr   = 0;
2170c60d41a9Swiz 	resp->tr_rflags   = TR_NO_RTE;
2171c60d41a9Swiz 	resp->tr_rmtaddr  = 0;
2172c60d41a9Swiz     } else {
2173c60d41a9Swiz 	/* get # of packets in on interface */
2174c60d41a9Swiz 	v_req.vifi = rt->rt_parent;
2175c60d41a9Swiz 	if (ioctl(igmp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
2176c60d41a9Swiz 	    resp->tr_vifin = htonl(v_req.icount);
2177c60d41a9Swiz 
2178c60d41a9Swiz 	MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
2179c60d41a9Swiz 	src = uvifs[rt->rt_parent].uv_lcl_addr;
2180c60d41a9Swiz 	resp->tr_inaddr = src;
2181c60d41a9Swiz 	resp->tr_rmtaddr = rt->rt_gateway;
2182c60d41a9Swiz 	if (!VIFM_ISSET(vifi, rt->rt_children)) {
2183761f7bebSitojun 	    logit(LOG_DEBUG, 0,
2184761f7bebSitojun 		    "Destination %s not on forwarding tree for src %s",
2185ee70b5d6Sdsl 		   inet_fmt(qry->tr_dst),
2186ee70b5d6Sdsl 		   inet_fmt(qry->tr_src));
2187c60d41a9Swiz 	    resp->tr_rflags = TR_WRONG_IF;
2188c60d41a9Swiz 	}
2189c60d41a9Swiz 	if (rt->rt_metric >= UNREACHABLE) {
2190c60d41a9Swiz 	    resp->tr_rflags = TR_NO_RTE;
2191c60d41a9Swiz 	    /* Hack to send reply directly */
2192c60d41a9Swiz 	    rt = NULL;
2193c60d41a9Swiz 	}
2194c60d41a9Swiz     }
2195c60d41a9Swiz 
2196c60d41a9Swiz sendit:
2197c60d41a9Swiz     /*
2198c60d41a9Swiz      * if metric is 1 or no. of reports is 1, send response to requestor
2199c60d41a9Swiz      * else send to upstream router.  If the upstream router can't handle
2200c60d41a9Swiz      * mtrace, set an error code and send to requestor anyway.
2201c60d41a9Swiz      */
2202c60d41a9Swiz     logit(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
2203c60d41a9Swiz 
2204*58cb611aSlukem     if (((u_int)(rcount + 1) == no) || (rt == NULL) || (rt->rt_metric == 1)) {
2205c60d41a9Swiz 	resptype = IGMP_MTRACE_REPLY;
2206c60d41a9Swiz 	dst = qry->tr_raddr;
2207c60d41a9Swiz     } else
2208c60d41a9Swiz 	if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
2209c60d41a9Swiz 	    dst = qry->tr_raddr;
2210c60d41a9Swiz 	    resp->tr_rflags = TR_OLD_ROUTER;
2211c60d41a9Swiz 	    resptype = IGMP_MTRACE_REPLY;
2212c60d41a9Swiz 	} else {
2213c60d41a9Swiz 	    dst = rt->rt_gateway;
2214c60d41a9Swiz 	    resptype = IGMP_MTRACE_QUERY;
2215c60d41a9Swiz 	}
2216c60d41a9Swiz 
2217c60d41a9Swiz     if (IN_MULTICAST(ntohl(dst))) {
2218c60d41a9Swiz 	/*
2219c60d41a9Swiz 	 * Send the reply on a known multicast capable vif.
2220c60d41a9Swiz 	 * If we don't have one, we can't source any multicasts anyway.
2221c60d41a9Swiz 	 */
2222c60d41a9Swiz 	if (phys_vif != -1) {
2223c60d41a9Swiz 	    logit(LOG_DEBUG, 0, "Sending reply to %s from %s",
2224ee70b5d6Sdsl 		inet_fmt(dst), inet_fmt(uvifs[phys_vif].uv_lcl_addr));
2225c60d41a9Swiz 	    k_set_ttl(qry->tr_rttl);
2226c60d41a9Swiz 	    send_igmp(uvifs[phys_vif].uv_lcl_addr, dst,
2227c60d41a9Swiz 		      resptype, no, group,
2228c60d41a9Swiz 		      datalen);
2229c60d41a9Swiz 	    k_set_ttl(1);
2230c60d41a9Swiz 	} else
2231c60d41a9Swiz 	    logit(LOG_INFO, 0, "No enabled phyints -- %s",
2232c60d41a9Swiz 			"dropping traceroute reply");
2233c60d41a9Swiz     } else {
2234c60d41a9Swiz 	logit(LOG_DEBUG, 0, "Sending %s to %s from %s",
2235c60d41a9Swiz 	    resptype == IGMP_MTRACE_REPLY ?  "reply" : "request on",
2236ee70b5d6Sdsl 	    inet_fmt(dst), inet_fmt(src));
2237c60d41a9Swiz 
2238c60d41a9Swiz 	send_igmp(src, dst,
2239c60d41a9Swiz 		  resptype, no, group,
2240c60d41a9Swiz 		  datalen);
2241c60d41a9Swiz     }
2242c60d41a9Swiz     return;
2243c60d41a9Swiz }
2244