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_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_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_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_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_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_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_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_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_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_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_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