vif.c revision 1.7.10.1 1 /* $NetBSD: vif.c,v 1.7.10.1 2000/10/17 19:50:25 tv Exp $ */
2
3 /*
4 * The mrouted program is covered by the license in the accompanying file
5 * named "LICENSE". Use of the mrouted program represents acceptance of
6 * the terms and conditions listed in that file.
7 *
8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9 * Leland Stanford Junior University.
10 */
11
12
13 #include "defs.h"
14 #include <fcntl.h>
15
16 /*
17 * Exported variables.
18 */
19 struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */
20 vifi_t numvifs; /* number of vifs in use */
21 int vifs_down; /* 1=>some interfaces are down */
22 int phys_vif; /* An enabled vif */
23 int udp_socket; /* Since the honkin' kernel doesn't support */
24 /* ioctls on raw IP sockets, we need a UDP */
25 /* socket as well as our IGMP (raw) socket. */
26 /* How dumb. */
27 int vifs_with_neighbors; /* == 1 if I am a leaf */
28
29 typedef struct {
30 vifi_t vifi;
31 struct listaddr *g;
32 int q_time;
33 } cbk_t;
34
35 /*
36 * Forward declarations.
37 */
38 static void start_vif __P((vifi_t vifi));
39 static void start_vif2 __P((vifi_t vifi));
40 static void stop_vif __P((vifi_t vifi));
41 static void age_old_hosts __P((void));
42 static void send_probe_on_vif __P((struct uvif *v));
43 static int info_version __P((char *p));
44 static void DelVif __P((void *arg));
45 static int SetTimer __P((int vifi, struct listaddr *g));
46 static int DeleteTimer __P((int id));
47 static void SendQuery __P((void *arg));
48 static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
49 int q_time));
50
51
52 /*
53 * Initialize the virtual interfaces, but do not install
54 * them in the kernel. Start routing on all vifs that are
55 * not down or disabled.
56 */
57 void
58 init_vifs()
59 {
60 vifi_t vifi;
61 struct uvif *v;
62 int enabled_vifs, enabled_phyints;
63 extern char *configfilename;
64
65 numvifs = 0;
66 vifs_with_neighbors = 0;
67 vifs_down = FALSE;
68
69 /*
70 * Configure the vifs based on the interface configuration of the
71 * the kernel and the contents of the configuration file.
72 * (Open a UDP socket for ioctl use in the config procedures.)
73 */
74 if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
75 log(LOG_ERR, errno, "UDP socket");
76 log(LOG_INFO,0,"Getting vifs from kernel interfaces");
77 config_vifs_from_kernel();
78 log(LOG_INFO,0,"Getting vifs from %s",configfilename);
79 config_vifs_from_file();
80
81 /*
82 * Quit if there are fewer than two enabled vifs.
83 */
84 enabled_vifs = 0;
85 enabled_phyints = 0;
86 phys_vif = -1;
87 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
88 if (!(v->uv_flags & VIFF_DISABLED)) {
89 ++enabled_vifs;
90 if (!(v->uv_flags & VIFF_TUNNEL)) {
91 if (phys_vif == -1)
92 phys_vif = vifi;
93 ++enabled_phyints;
94 }
95 }
96 }
97 if (enabled_vifs < 2)
98 log(LOG_ERR, 0, "can't forward: %s",
99 enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif");
100
101 if (enabled_phyints == 0)
102 log(LOG_WARNING, 0,
103 "no enabled interfaces, forwarding via tunnels only");
104
105 log(LOG_INFO, 0, "Installing vifs in mrouted...");
106 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
107 if (!(v->uv_flags & VIFF_DISABLED)) {
108 if (!(v->uv_flags & VIFF_DOWN)) {
109 if (v->uv_flags & VIFF_TUNNEL)
110 log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
111 inet_fmt(v->uv_lcl_addr, s1),
112 inet_fmt(v->uv_rmt_addr, s2));
113 else
114 log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
115 inet_fmt(v->uv_lcl_addr, s1));
116 start_vif2(vifi);
117 } else log(LOG_INFO, 0,
118 "%s is not yet up; vif #%u not in service",
119 v->uv_name, vifi);
120 }
121 }
122 }
123
124 /*
125 * Start routing on all virtual interfaces that are not down or
126 * administratively disabled.
127 */
128 void
129 init_installvifs()
130 {
131 vifi_t vifi;
132 struct uvif *v;
133
134 log(LOG_INFO, 0, "Installing vifs in kernel...");
135 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
136 if (!(v->uv_flags & VIFF_DISABLED)) {
137 if (!(v->uv_flags & VIFF_DOWN)) {
138 if (v->uv_flags & VIFF_TUNNEL)
139 log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
140 inet_fmt(v->uv_lcl_addr, s1),
141 inet_fmt(v->uv_rmt_addr, s2));
142 else
143 log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
144 inet_fmt(v->uv_lcl_addr, s1));
145 k_add_vif(vifi, &uvifs[vifi]);
146 } else log(LOG_INFO, 0,
147 "%s is not yet up; vif #%u not in service",
148 v->uv_name, vifi);
149 }
150 }
151 }
152
153 /*
154 * See if any interfaces have changed from up state to down, or vice versa,
155 * including any non-multicast-capable interfaces that are in use as local
156 * tunnel end-points. Ignore interfaces that have been administratively
157 * disabled.
158 */
159 void
160 check_vif_state()
161 {
162 register vifi_t vifi;
163 register struct uvif *v;
164 struct ifreq ifr;
165
166 vifs_down = FALSE;
167 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
168
169 if (v->uv_flags & VIFF_DISABLED) continue;
170
171 strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
172 if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
173 log(LOG_ERR, errno,
174 "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
175
176 if (v->uv_flags & VIFF_DOWN) {
177 if (ifr.ifr_flags & IFF_UP) {
178 v->uv_flags &= ~VIFF_DOWN;
179 start_vif(vifi);
180 log(LOG_INFO, 0,
181 "%s has come up; vif #%u now in service",
182 v->uv_name, vifi);
183 }
184 else vifs_down = TRUE;
185 }
186 else {
187 if (!(ifr.ifr_flags & IFF_UP)) {
188 stop_vif(vifi);
189 v->uv_flags |= VIFF_DOWN;
190 log(LOG_INFO, 0,
191 "%s has gone down; vif #%u taken out of service",
192 v->uv_name, vifi);
193 vifs_down = TRUE;
194 }
195 }
196 }
197 }
198
199 /*
200 * Send a probe message on vif v
201 */
202 static void
203 send_probe_on_vif(v)
204 register struct uvif *v;
205 {
206 register char *p;
207 register int datalen = 0;
208 struct listaddr *nbr;
209 int i;
210
211 p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
212
213 for (i = 0; i < 4; i++)
214 *p++ = ((char *)&(dvmrp_genid))[i];
215 datalen += 4;
216
217 /*
218 * add the neighbor list on the interface to the message
219 */
220 nbr = v->uv_neighbors;
221
222 while (nbr) {
223 for (i = 0; i < 4; i++)
224 *p++ = ((char *)&nbr->al_addr)[i];
225 datalen +=4;
226 nbr = nbr->al_next;
227 }
228
229 send_igmp(v->uv_lcl_addr,
230 (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
231 : dvmrp_group,
232 IGMP_DVMRP, DVMRP_PROBE,
233 htonl(MROUTED_LEVEL |
234 ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS)),
235 datalen);
236 }
237
238 /*
239 * Add a vifi to the kernel and start routing on it.
240 */
241 static void
242 start_vif(vifi)
243 vifi_t vifi;
244 {
245 /*
246 * Install the interface in the kernel's vif structure.
247 */
248 k_add_vif(vifi, &uvifs[vifi]);
249
250 start_vif2(vifi);
251 }
252
253 /*
254 * Add a vifi to all the user-level data structures but don't add
255 * it to the kernel yet.
256 */
257 static void
258 start_vif2(vifi)
259 vifi_t vifi;
260 {
261 struct uvif *v;
262 u_int32_t src;
263 struct phaddr *p;
264
265 v = &uvifs[vifi];
266 src = v->uv_lcl_addr;
267
268 /*
269 * Update the existing route entries to take into account the new vif.
270 */
271 add_vif_to_routes(vifi);
272
273 if (!(v->uv_flags & VIFF_TUNNEL)) {
274 /*
275 * Join the DVMRP multicast group on the interface.
276 * (This is not strictly necessary, since the kernel promiscuously
277 * receives IGMP packets addressed to ANY IP multicast group while
278 * multicast routing is enabled. However, joining the group allows
279 * this host to receive non-IGMP packets as well, such as 'pings'.)
280 */
281 k_join(dvmrp_group, src);
282
283 /*
284 * Join the ALL-ROUTERS multicast group on the interface.
285 * This allows mtrace requests to loop back if they are run
286 * on the multicast router.
287 */
288 k_join(allrtrs_group, src);
289
290 /*
291 * Install an entry in the routing table for the subnet to which
292 * the interface is connected.
293 */
294 start_route_updates();
295 update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi);
296 for (p = v->uv_addrs; p; p = p->pa_next) {
297 start_route_updates();
298 update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi);
299 }
300
301 /*
302 * Until neighbors are discovered, assume responsibility for sending
303 * periodic group membership queries to the subnet. Send the first
304 * query.
305 */
306 v->uv_flags |= VIFF_QUERIER;
307 send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY,
308 (v->uv_flags & VIFF_IGMPV1) ? 0 :
309 IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
310 age_old_hosts();
311 }
312
313 v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
314
315 /*
316 * Send a probe via the new vif to look for neighbors.
317 */
318 send_probe_on_vif(v);
319 }
320
321 /*
322 * Stop routing on the specified virtual interface.
323 */
324 static void
325 stop_vif(vifi)
326 vifi_t vifi;
327 {
328 struct uvif *v;
329 struct listaddr *a;
330 struct phaddr *p;
331
332 v = &uvifs[vifi];
333
334 if (!(v->uv_flags & VIFF_TUNNEL)) {
335 /*
336 * Depart from the DVMRP multicast group on the interface.
337 */
338 k_leave(dvmrp_group, v->uv_lcl_addr);
339
340 /*
341 * Depart from the ALL-ROUTERS multicast group on the interface.
342 */
343 k_leave(allrtrs_group, v->uv_lcl_addr);
344
345 /*
346 * Update the entry in the routing table for the subnet to which
347 * the interface is connected, to take into account the interface
348 * failure.
349 */
350 start_route_updates();
351 update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi);
352 for (p = v->uv_addrs; p; p = p->pa_next) {
353 start_route_updates();
354 update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi);
355 }
356
357 /*
358 * Discard all group addresses. (No need to tell kernel;
359 * the k_del_vif() call, below, will clean up kernel state.)
360 */
361 while (v->uv_groups != NULL) {
362 a = v->uv_groups;
363 v->uv_groups = a->al_next;
364 free((char *)a);
365 }
366
367 v->uv_flags &= ~VIFF_QUERIER;
368 }
369
370 /*
371 * Update the existing route entries to take into account the vif failure.
372 */
373 delete_vif_from_routes(vifi);
374
375 /*
376 * Delete the interface from the kernel's vif structure.
377 */
378 k_del_vif(vifi);
379
380 /*
381 * Discard all neighbor addresses.
382 */
383 if (v->uv_neighbors)
384 vifs_with_neighbors--;
385
386 while (v->uv_neighbors != NULL) {
387 a = v->uv_neighbors;
388 v->uv_neighbors = a->al_next;
389 free((char *)a);
390 }
391 }
392
393
394 /*
395 * stop routing on all vifs
396 */
397 void
398 stop_all_vifs()
399 {
400 vifi_t vifi;
401 struct uvif *v;
402 struct listaddr *a;
403 struct vif_acl *acl;
404
405 for (vifi = 0; vifi < numvifs; vifi++) {
406 v = &uvifs[vifi];
407 while (v->uv_groups != NULL) {
408 a = v->uv_groups;
409 v->uv_groups = a->al_next;
410 free((char *)a);
411 }
412 while (v->uv_neighbors != NULL) {
413 a = v->uv_neighbors;
414 v->uv_neighbors = a->al_next;
415 free((char *)a);
416 }
417 while (v->uv_acl != NULL) {
418 acl = v->uv_acl;
419 v->uv_acl = acl->acl_next;
420 free((char *)acl);
421 }
422 }
423 }
424
425
426 /*
427 * Find the virtual interface from which an incoming packet arrived,
428 * based on the packet's source and destination IP addresses.
429 */
430 vifi_t
431 find_vif(src, dst)
432 register u_int32_t src;
433 register u_int32_t dst;
434 {
435 register vifi_t vifi;
436 register struct uvif *v;
437 register struct phaddr *p;
438
439 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
440 if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
441 if (v->uv_flags & VIFF_TUNNEL) {
442 if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr)
443 return(vifi);
444 }
445 else {
446 if ((src & v->uv_subnetmask) == v->uv_subnet &&
447 ((v->uv_subnetmask == 0xffffffff) ||
448 (src != v->uv_subnetbcast)))
449 return(vifi);
450 for (p=v->uv_addrs; p; p=p->pa_next) {
451 if ((src & p->pa_subnetmask) == p->pa_subnet &&
452 ((p->pa_subnetmask == 0xffffffff) ||
453 (src != p->pa_subnetbcast)))
454 return(vifi);
455 }
456 }
457 }
458 }
459 return (NO_VIF);
460 }
461
462 static void
463 age_old_hosts()
464 {
465 register vifi_t vifi;
466 register struct uvif *v;
467 register struct listaddr *g;
468
469 /*
470 * Decrement the old-hosts-present timer for each
471 * active group on each vif.
472 */
473 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
474 for (g = v->uv_groups; g != NULL; g = g->al_next)
475 if (g->al_old)
476 g->al_old--;
477 }
478
479
480 /*
481 * Send group membership queries to all subnets for which I am querier.
482 */
483 void
484 query_groups()
485 {
486 register vifi_t vifi;
487 register struct uvif *v;
488
489 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
490 if (v->uv_flags & VIFF_QUERIER) {
491 send_igmp(v->uv_lcl_addr, allhosts_group,
492 IGMP_HOST_MEMBERSHIP_QUERY,
493 (v->uv_flags & VIFF_IGMPV1) ? 0 :
494 IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
495 }
496 }
497 age_old_hosts();
498 }
499
500 /*
501 * Process an incoming host membership query
502 */
503 void
504 accept_membership_query(src, dst, group, tmo)
505 u_int32_t src, dst, group;
506 int tmo;
507 {
508 register vifi_t vifi;
509 register struct uvif *v;
510
511 if ((vifi = find_vif(src, dst)) == NO_VIF ||
512 (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
513 log(LOG_INFO, 0,
514 "ignoring group membership query from non-adjacent host %s",
515 inet_fmt(src, s1));
516 return;
517 }
518
519 v = &uvifs[vifi];
520
521 /*
522 * If we consider ourselves the querier for this vif, but hear a
523 * query from a router with a lower IP address, yield to them.
524 *
525 * This is done here as well as in the neighbor discovery in case
526 * there is a querier that doesn't speak DVMRP.
527 *
528 * XXX If this neighbor doesn't speak DVMRP, then we need to create
529 * some neighbor state for him so that we can time him out!
530 */
531 if ((v->uv_flags & VIFF_QUERIER) &&
532 (ntohl(src) < ntohl(v->uv_lcl_addr))) {
533 v->uv_flags &= ~VIFF_QUERIER;
534
535 }
536 }
537
538 /*
539 * Process an incoming group membership report.
540 */
541 void
542 accept_group_report(src, dst, group, r_type)
543 u_int32_t src, dst, group;
544 int r_type;
545 {
546 register vifi_t vifi;
547 register struct uvif *v;
548 register struct listaddr *g;
549
550 if ((vifi = find_vif(src, dst)) == NO_VIF ||
551 (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
552 log(LOG_INFO, 0,
553 "ignoring group membership report from non-adjacent host %s",
554 inet_fmt(src, s1));
555 return;
556 }
557
558 v = &uvifs[vifi];
559
560 /*
561 * Look for the group in our group list; if found, reset its timer.
562 */
563 for (g = v->uv_groups; g != NULL; g = g->al_next) {
564 if (group == g->al_addr) {
565 if (r_type == IGMP_v1_HOST_MEMBERSHIP_REPORT)
566 g->al_old = OLD_AGE_THRESHOLD;
567 #ifdef SNMP
568 g->al_genid = src;
569 #endif /* SNMP */
570
571 /** delete old timers, set a timer for expiration **/
572 g->al_timer = GROUP_EXPIRE_TIME;
573 if (g->al_query)
574 g->al_query = DeleteTimer(g->al_query);
575 if (g->al_timerid)
576 g->al_timerid = DeleteTimer(g->al_timerid);
577 g->al_timerid = SetTimer(vifi, g);
578 break;
579 }
580 }
581
582 /*
583 * If not found, add it to the list and update kernel cache.
584 */
585 if (g == NULL) {
586 g = (struct listaddr *)malloc(sizeof(struct listaddr));
587 if (g == NULL)
588 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
589
590 g->al_addr = group;
591 if (r_type == IGMP_v2_HOST_MEMBERSHIP_REPORT)
592 g->al_old = 0;
593 else
594 g->al_old = OLD_AGE_THRESHOLD;
595 #ifdef SNMP
596 g->al_genid = src;
597 #endif
598
599 /** set a timer for expiration **/
600 g->al_query = 0;
601 g->al_timer = GROUP_EXPIRE_TIME;
602 time(&g->al_ctime);
603 g->al_timerid = SetTimer(vifi, g);
604 g->al_next = v->uv_groups;
605 v->uv_groups = g;
606
607 update_lclgrp(vifi, group);
608 }
609
610 /*
611 * Check if a graft is necessary for this group
612 */
613 chkgrp_graft(vifi, group);
614 }
615
616
617 void
618 accept_leave_message(src, dst, group)
619 u_int32_t src, dst, group;
620 {
621 register vifi_t vifi;
622 register struct uvif *v;
623 register struct listaddr *g;
624
625 if ((vifi = find_vif(src, dst)) == NO_VIF ||
626 (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
627 log(LOG_INFO, 0,
628 "ignoring group leave report from non-adjacent host %s",
629 inet_fmt(src, s1));
630 return;
631 }
632
633 v = &uvifs[vifi];
634
635 if (!(v->uv_flags & VIFF_QUERIER) || (v->uv_flags & VIFF_IGMPV1))
636 return;
637
638 /*
639 * Look for the group in our group list in order to set up a short-timeout
640 * query.
641 */
642 for (g = v->uv_groups; g != NULL; g = g->al_next) {
643 if (group == g->al_addr) {
644 log(LOG_DEBUG, 0,
645 "[vif.c, _accept_leave_message] %d %ld \n",
646 g->al_old, g->al_query);
647
648 /* Ignore the leave message if there are old hosts present */
649 if (g->al_old)
650 return;
651
652 /* still waiting for a reply to a query, ignore the leave */
653 if (g->al_query)
654 return;
655
656 /** delete old timer set a timer for expiration **/
657 if (g->al_timerid)
658 g->al_timerid = DeleteTimer(g->al_timerid);
659
660 /** send a group specific querry **/
661 g->al_timer = LEAVE_EXPIRE_TIME;
662 send_igmp(v->uv_lcl_addr, g->al_addr,
663 IGMP_HOST_MEMBERSHIP_QUERY,
664 LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE,
665 g->al_addr, 0);
666 g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3,
667 LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE);
668 g->al_timerid = SetTimer(vifi, g);
669 break;
670 }
671 }
672 }
673
674
675 /*
676 * Send a periodic probe on all vifs.
677 * Useful to determine one-way interfaces.
678 * Detect neighbor loss faster.
679 */
680 void
681 probe_for_neighbors()
682 {
683 register vifi_t vifi;
684 register struct uvif *v;
685
686 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
687 if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
688 send_probe_on_vif(v);
689 }
690 }
691 }
692
693
694 /*
695 * Send a list of all of our neighbors to the requestor, `src'.
696 */
697 void
698 accept_neighbor_request(src, dst)
699 u_int32_t src, dst;
700 {
701 vifi_t vifi;
702 struct uvif *v;
703 u_char *p, *ncount;
704 struct listaddr *la;
705 int datalen;
706 u_int32_t temp_addr, us, them = src;
707
708 /* Determine which of our addresses to use as the source of our response
709 * to this query.
710 */
711 if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
712 int udp; /* find best interface to reply on */
713 struct sockaddr_in addr;
714 int addrlen = sizeof(addr);
715
716 addr.sin_family = AF_INET;
717 #if (defined(BSD) && (BSD >= 199103))
718 addr.sin_len = sizeof addr;
719 #endif
720 addr.sin_addr.s_addr = dst;
721 addr.sin_port = htons(2000); /* any port over 1024 will do... */
722 if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
723 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
724 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
725 log(LOG_WARNING, errno, "Determining local address");
726 close(udp);
727 return;
728 }
729 close(udp);
730 us = addr.sin_addr.s_addr;
731 } else /* query sent to us alone */
732 us = dst;
733
734 #define PUT_ADDR(a) temp_addr = ntohl(a); \
735 *p++ = temp_addr >> 24; \
736 *p++ = (temp_addr >> 16) & 0xFF; \
737 *p++ = (temp_addr >> 8) & 0xFF; \
738 *p++ = temp_addr & 0xFF;
739
740 p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
741 datalen = 0;
742
743 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
744 if (v->uv_flags & VIFF_DISABLED)
745 continue;
746
747 ncount = 0;
748
749 for (la = v->uv_neighbors; la; la = la->al_next) {
750
751 /* Make sure that there's room for this neighbor... */
752 if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
753 send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
754 htonl(MROUTED_LEVEL), datalen);
755 p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
756 datalen = 0;
757 ncount = 0;
758 }
759
760 /* Put out the header for this neighbor list... */
761 if (ncount == 0) {
762 PUT_ADDR(v->uv_lcl_addr);
763 *p++ = v->uv_metric;
764 *p++ = v->uv_threshold;
765 ncount = p;
766 *p++ = 0;
767 datalen += 4 + 3;
768 }
769
770 PUT_ADDR(la->al_addr);
771 datalen += 4;
772 (*ncount)++;
773 }
774 }
775
776 if (datalen != 0)
777 send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL),
778 datalen);
779 }
780
781 /*
782 * Send a list of all of our neighbors to the requestor, `src'.
783 */
784 void
785 accept_neighbor_request2(src, dst)
786 u_int32_t src, dst;
787 {
788 vifi_t vifi;
789 struct uvif *v;
790 u_char *p, *ncount;
791 struct listaddr *la;
792 int datalen;
793 u_int32_t us, them = src;
794
795 /* Determine which of our addresses to use as the source of our response
796 * to this query.
797 */
798 if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
799 int udp; /* find best interface to reply on */
800 struct sockaddr_in addr;
801 int addrlen = sizeof(addr);
802
803 addr.sin_family = AF_INET;
804 #if (defined(BSD) && (BSD >= 199103))
805 addr.sin_len = sizeof addr;
806 #endif
807 addr.sin_addr.s_addr = dst;
808 addr.sin_port = htons(2000); /* any port over 1024 will do... */
809 if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
810 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
811 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
812 log(LOG_WARNING, errno, "Determining local address");
813 close(udp);
814 return;
815 }
816 close(udp);
817 us = addr.sin_addr.s_addr;
818 } else /* query sent to us alone */
819 us = dst;
820
821 p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
822 datalen = 0;
823
824 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
825 register u_short vflags = v->uv_flags;
826 register u_char rflags = 0;
827 if (vflags & VIFF_TUNNEL)
828 rflags |= DVMRP_NF_TUNNEL;
829 if (vflags & VIFF_SRCRT)
830 rflags |= DVMRP_NF_SRCRT;
831 if (vflags & VIFF_DOWN)
832 rflags |= DVMRP_NF_DOWN;
833 if (vflags & VIFF_DISABLED)
834 rflags |= DVMRP_NF_DISABLED;
835 if (vflags & VIFF_QUERIER)
836 rflags |= DVMRP_NF_QUERIER;
837 if (vflags & VIFF_LEAF)
838 rflags |= DVMRP_NF_LEAF;
839 ncount = 0;
840 la = v->uv_neighbors;
841 if (la == NULL) {
842 /*
843 * include down & disabled interfaces and interfaces on
844 * leaf nets.
845 */
846 if (rflags & DVMRP_NF_TUNNEL)
847 rflags |= DVMRP_NF_DOWN;
848 if (datalen > MAX_DVMRP_DATA_LEN - 12) {
849 send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
850 htonl(MROUTED_LEVEL), datalen);
851 p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
852 datalen = 0;
853 }
854 *(u_int*)p = v->uv_lcl_addr;
855 p += 4;
856 *p++ = v->uv_metric;
857 *p++ = v->uv_threshold;
858 *p++ = rflags;
859 *p++ = 1;
860 *(u_int*)p = v->uv_rmt_addr;
861 p += 4;
862 datalen += 12;
863 } else {
864 for ( ; la; la = la->al_next) {
865 /* Make sure that there's room for this neighbor... */
866 if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
867 send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
868 htonl(MROUTED_LEVEL), datalen);
869 p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
870 datalen = 0;
871 ncount = 0;
872 }
873 /* Put out the header for this neighbor list... */
874 if (ncount == 0) {
875 *(u_int*)p = v->uv_lcl_addr;
876 p += 4;
877 *p++ = v->uv_metric;
878 *p++ = v->uv_threshold;
879 *p++ = rflags;
880 ncount = p;
881 *p++ = 0;
882 datalen += 4 + 4;
883 }
884 *(u_int*)p = la->al_addr;
885 p += 4;
886 datalen += 4;
887 (*ncount)++;
888 }
889 }
890 }
891 if (datalen != 0)
892 send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL),
893 datalen);
894 }
895
896 void
897 accept_info_request(src, dst, p, datalen)
898 u_int32_t src, dst;
899 u_char *p;
900 int datalen;
901 {
902 u_char *q;
903 int len;
904 int outlen = 0;
905
906 q = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
907
908 /* To be general, this must deal properly with breaking up over-sized
909 * packets. That implies passing a length to each function, and
910 * allowing each function to request to be called again. Right now,
911 * we're only implementing the one thing we are positive will fit into
912 * a single packet, so we wimp out.
913 */
914 while (datalen > 0) {
915 len = 0;
916 switch (*p) {
917 case DVMRP_INFO_VERSION:
918 len = info_version(q);
919 break;
920
921 case DVMRP_INFO_NEIGHBORS:
922 default:
923 log(LOG_INFO, 0, "ignoring unknown info type %d", *p);
924 break;
925 }
926 *(q+1) = len++;
927 outlen += len * 4;
928 q += len * 4;
929 len = (*(p+1) + 1) * 4;
930 p += len;
931 datalen -= len;
932 }
933
934 if (outlen != 0)
935 send_igmp(INADDR_ANY, src, IGMP_DVMRP, DVMRP_INFO_REPLY,
936 htonl(MROUTED_LEVEL), outlen);
937 }
938
939 /*
940 * Information response -- return version string
941 */
942 static int
943 info_version(p)
944 char *p;
945 {
946 int len;
947 extern char versionstring[];
948
949 *p++ = DVMRP_INFO_VERSION;
950 p++; /* skip over length */
951 *p++ = 0; /* zero out */
952 *p++ = 0; /* reserved fields */
953 strcpy(p, versionstring); /* XXX strncpy!!! */
954
955 len = strlen(versionstring);
956 return ((len + 3) / 4);
957 }
958
959 /*
960 * Process an incoming neighbor-list message.
961 */
962 void
963 accept_neighbors(src, dst, p, datalen, level)
964 u_int32_t src, dst, level;
965 u_char *p;
966 int datalen;
967 {
968 log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s",
969 inet_fmt(src, s1), inet_fmt(dst, s2));
970 }
971
972
973 /*
974 * Process an incoming neighbor-list message.
975 */
976 void
977 accept_neighbors2(src, dst, p, datalen, level)
978 u_int32_t src, dst, level;
979 u_char *p;
980 int datalen;
981 {
982 log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
983 inet_fmt(src, s1), inet_fmt(dst, s2));
984 }
985
986 /*
987 * Process an incoming info reply message.
988 */
989 void
990 accept_info_reply(src, dst, p, datalen)
991 u_int32_t src, dst;
992 u_char *p;
993 int datalen;
994 {
995 log(LOG_INFO, 0, "ignoring spurious DVMRP info reply from %s to %s",
996 inet_fmt(src, s1), inet_fmt(dst, s2));
997 }
998
999
1000 /*
1001 * Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
1002 * 'msgtype' is the type of DVMRP message received from the neighbor.
1003 * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise.
1004 */
1005 int
1006 update_neighbor(vifi, addr, msgtype, p, datalen, level)
1007 vifi_t vifi;
1008 u_int32_t addr;
1009 int msgtype;
1010 char *p;
1011 int datalen;
1012 u_int32_t level;
1013 {
1014 register struct uvif *v;
1015 register struct listaddr *n;
1016 u_int32_t genid = 0;
1017 u_int32_t router;
1018 u_int32_t send_tables = 0;
1019 int do_reset = FALSE;
1020 int nflags;
1021
1022 v = &uvifs[vifi];
1023 nflags = (level >> 16) & 0xff;
1024
1025 /*
1026 * Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
1027 * IT IS ASSUMED that this was preceded by a call to find_vif(), which
1028 * checks that 'addr' is either a valid remote tunnel endpoint or a
1029 * non-broadcast address belonging to a directly-connected subnet.
1030 * Therefore, here we check only that 'addr' is not our own address
1031 * (due to an impostor or erroneous loopback) or an address of the form
1032 * {subnet,0} ("the unknown host"). These checks are not performed in
1033 * find_vif() because those types of address are acceptable for some
1034 * types of IGMP message (such as group membership reports).
1035 */
1036 if (!(v->uv_flags & VIFF_TUNNEL) &&
1037 (addr == v->uv_lcl_addr ||
1038 addr == v->uv_subnet )) {
1039 log(LOG_WARNING, 0,
1040 "received DVMRP message from 'the unknown host' or self: %s",
1041 inet_fmt(addr, s1));
1042 return (FALSE);
1043 }
1044
1045 /*
1046 * Look for addr in list of neighbors.
1047 */
1048 for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
1049 if (addr == n->al_addr) {
1050 break;
1051 }
1052 }
1053
1054 /*
1055 * Found it. Reset its timer, and check for a version change
1056 */
1057 if (n) {
1058 n->al_timer = 0;
1059
1060 /*
1061 * update the neighbors version and protocol number
1062 * if changed => router went down and came up,
1063 * so take action immediately.
1064 */
1065 if ((n->al_pv != (level & 0xff)) ||
1066 (n->al_mv != ((level >> 8) & 0xff))) {
1067
1068 do_reset = TRUE;
1069 log(LOG_DEBUG, 0,
1070 "version change neighbor %s [old:%d.%d, new:%d.%d]",
1071 inet_fmt(addr, s1),
1072 n->al_pv, n->al_mv, level&0xff, (level >> 8) & 0xff);
1073
1074 n->al_pv = level & 0xff;
1075 n->al_mv = (level >> 8) & 0xff;
1076 }
1077 } else {
1078 /*
1079 * If not found, add it to the list. If the neighbor has a lower
1080 * IP address than me, yield querier duties to it.
1081 */
1082 log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x",
1083 inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff,
1084 (level >> 16) & 0xff);
1085
1086 n = (struct listaddr *)malloc(sizeof(struct listaddr));
1087 if (n == NULL)
1088 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
1089
1090 n->al_addr = addr;
1091 n->al_pv = level & 0xff;
1092 n->al_mv = (level >> 8) & 0xff;
1093 n->al_genid = 0;
1094
1095 time(&n->al_ctime);
1096 n->al_timer = 0;
1097 n->al_next = v->uv_neighbors;
1098
1099 /*
1100 * If we thought that we had no neighbors on this vif, send a route
1101 * report to the vif. If this is just a new neighbor on the same
1102 * vif, send the route report just to the new neighbor.
1103 */
1104 if (v->uv_neighbors == NULL) {
1105 send_tables = (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group;
1106 vifs_with_neighbors++;
1107 } else {
1108 send_tables = addr;
1109 }
1110
1111 v->uv_neighbors = n;
1112
1113 if (!(v->uv_flags & VIFF_TUNNEL) &&
1114 ntohl(addr) < ntohl(v->uv_lcl_addr))
1115 v->uv_flags &= ~VIFF_QUERIER;
1116 }
1117
1118 /*
1119 * Check if the router gen-ids are the same.
1120 * Need to reset the prune state of the router if not.
1121 * Also check for one-way interfaces by seeing if we are in our
1122 * neighbor's list of known routers.
1123 */
1124 if (msgtype == DVMRP_PROBE) {
1125
1126 /* Check genid neighbor flag. Also check version number; 3.3 and
1127 * 3.4 didn't set this flag. */
1128 if ((((level >> 16) & 0xff) & NF_GENID) ||
1129 (((level & 0xff) == 3) && (((level >> 8) & 0xff) > 2))) {
1130
1131 int i;
1132
1133 if (datalen < 4) {
1134 log(LOG_WARNING, 0,
1135 "received truncated probe message from %s (len %d)",
1136 inet_fmt(addr, s1), datalen);
1137 return (FALSE);
1138 }
1139
1140 for (i = 0; i < 4; i++)
1141 ((char *)&genid)[i] = *p++;
1142 datalen -= 4;
1143
1144 if (n->al_genid == 0)
1145 n->al_genid = genid;
1146 else if (n->al_genid != genid) {
1147 log(LOG_DEBUG, 0,
1148 "new genid neigbor %s on vif %d [old:%x, new:%x]",
1149 inet_fmt(addr, s1), vifi, n->al_genid, genid);
1150
1151 n->al_genid = genid;
1152 do_reset = TRUE;
1153 }
1154
1155 /*
1156 * loop through router list and check for one-way ifs.
1157 */
1158
1159 v->uv_flags |= VIFF_ONEWAY;
1160
1161 while (datalen > 0) {
1162 if (datalen < 4) {
1163 log(LOG_WARNING, 0,
1164 "received truncated probe message from %s (len %d)",
1165 inet_fmt(addr, s1), datalen);
1166 return (FALSE);
1167 }
1168 for (i = 0; i < 4; i++)
1169 ((char *)&router)[i] = *p++;
1170 datalen -= 4;
1171 if (router == v->uv_lcl_addr) {
1172 v->uv_flags &= ~VIFF_ONEWAY;
1173 break;
1174 }
1175 }
1176 }
1177 }
1178 if (n->al_flags != nflags) {
1179 n->al_flags = nflags;
1180
1181 if (n->al_flags & NF_LEAF) {
1182 /*XXX If we have non-leaf neighbors then we know we shouldn't
1183 * mark this vif as a leaf. For now we just count on other
1184 * probes and/or reports resetting the timer. */
1185 if (!v->uv_leaf_timer)
1186 v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
1187 } else {
1188 /* If we get a leaf to non-leaf transition, we *must* update
1189 * the routing table. */
1190 if (v->uv_flags & VIFF_LEAF && send_tables == 0)
1191 send_tables = addr;
1192 v->uv_flags &= ~VIFF_LEAF;
1193 v->uv_leaf_timer = 0;
1194 }
1195 }
1196 if (do_reset) {
1197 reset_neighbor_state(vifi, addr);
1198 if (!send_tables)
1199 send_tables = addr;
1200 }
1201 if (send_tables)
1202 report(ALL_ROUTES, vifi, send_tables);
1203
1204 return (TRUE);
1205 }
1206
1207
1208 /*
1209 * On every timer interrupt, advance the timer in each neighbor and
1210 * group entry on every vif.
1211 */
1212 void
1213 age_vifs()
1214 {
1215 register vifi_t vifi;
1216 register struct uvif *v;
1217 register struct listaddr *a, *prev_a, *n;
1218 register u_int32_t addr;
1219
1220 for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
1221 if (v->uv_leaf_timer && (v->uv_leaf_timer -= TIMER_INTERVAL == 0)) {
1222 v->uv_flags |= VIFF_LEAF;
1223 }
1224
1225 for (prev_a = (struct listaddr *)&(v->uv_neighbors),
1226 a = v->uv_neighbors;
1227 a != NULL;
1228 prev_a = a, a = a->al_next) {
1229
1230 if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME)
1231 continue;
1232
1233 /*
1234 * Neighbor has expired; delete it from the neighbor list,
1235 * delete it from the 'dominants' and 'subordinates arrays of
1236 * any route entries and assume querier duties unless there is
1237 * another neighbor with a lower IP address than mine.
1238 */
1239 addr = a->al_addr;
1240 prev_a->al_next = a->al_next;
1241 free((char *)a);
1242 a = prev_a;
1243
1244 delete_neighbor_from_routes(addr, vifi);
1245
1246 if (v->uv_neighbors == NULL)
1247 vifs_with_neighbors--;
1248
1249 v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
1250
1251 if (!(v->uv_flags & VIFF_TUNNEL)) {
1252 v->uv_flags |= VIFF_QUERIER;
1253 for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
1254 if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) {
1255 v->uv_flags &= ~VIFF_QUERIER;
1256 }
1257 if (!(n->al_flags & NF_LEAF)) {
1258 v->uv_leaf_timer = 0;
1259 }
1260 }
1261 }
1262 }
1263 }
1264 }
1265
1266 /*
1267 * Returns the neighbor info struct for a given neighbor
1268 */
1269 struct listaddr *
1270 neighbor_info(vifi, addr)
1271 vifi_t vifi;
1272 u_int32_t addr;
1273 {
1274 struct listaddr *u;
1275
1276 for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next)
1277 if (u->al_addr == addr)
1278 return u;
1279
1280 return NULL;
1281 }
1282
1283 /*
1284 * Print the contents of the uvifs array on file 'fp'.
1285 */
1286 void
1287 dump_vifs(fp)
1288 FILE *fp;
1289 {
1290 register vifi_t vifi;
1291 register struct uvif *v;
1292 register struct listaddr *a;
1293 register struct phaddr *p;
1294 struct sioc_vif_req v_req;
1295
1296 fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors);
1297
1298 if (vifs_with_neighbors == 1)
1299 fprintf(fp,"[This host is a leaf]\n\n");
1300
1301 fprintf(fp,
1302 "\nVirtual Interface Table\n%s",
1303 "Vif Name Local-Address ");
1304 fprintf(fp,
1305 "M Thr Rate Flags\n");
1306
1307 for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
1308
1309 fprintf(fp, "%2u %6s %-15s %6s: %-18s %2u %3u %5u ",
1310 vifi,
1311 v->uv_name,
1312 inet_fmt(v->uv_lcl_addr, s1),
1313 (v->uv_flags & VIFF_TUNNEL) ?
1314 "tunnel":
1315 "subnet",
1316 (v->uv_flags & VIFF_TUNNEL) ?
1317 inet_fmt(v->uv_rmt_addr, s2) :
1318 inet_fmts(v->uv_subnet, v->uv_subnetmask, s3),
1319 v->uv_metric,
1320 v->uv_threshold,
1321 v->uv_rate_limit);
1322
1323 if (v->uv_flags & VIFF_ONEWAY) fprintf(fp, " one-way");
1324 if (v->uv_flags & VIFF_DOWN) fprintf(fp, " down");
1325 if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled");
1326 if (v->uv_flags & VIFF_QUERIER) fprintf(fp, " querier");
1327 if (v->uv_flags & VIFF_SRCRT) fprintf(fp, " src-rt");
1328 if (v->uv_flags & VIFF_LEAF) fprintf(fp, " leaf");
1329 if (v->uv_flags & VIFF_IGMPV1) fprintf(fp, " IGMPv1");
1330 fprintf(fp, "\n");
1331
1332 if (v->uv_addrs != NULL) {
1333 fprintf(fp, " alternate subnets: %s\n",
1334 inet_fmts(v->uv_addrs->pa_subnet, v->uv_addrs->pa_subnetmask, s1));
1335 for (p = v->uv_addrs->pa_next; p; p = p->pa_next) {
1336 fprintf(fp, " %s\n",
1337 inet_fmts(p->pa_subnet, p->pa_subnetmask, s1));
1338 }
1339 }
1340
1341 if (v->uv_neighbors != NULL) {
1342 fprintf(fp, " peers: %s (%d.%d) (0x%x)\n",
1343 inet_fmt(v->uv_neighbors->al_addr, s1),
1344 v->uv_neighbors->al_pv, v->uv_neighbors->al_mv,
1345 v->uv_neighbors->al_flags);
1346 for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) {
1347 fprintf(fp, " %s (%d.%d) (0x%x)\n",
1348 inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
1349 a->al_flags);
1350 }
1351 }
1352
1353 if (v->uv_groups != NULL) {
1354 fprintf(fp, " groups: %-15s\n",
1355 inet_fmt(v->uv_groups->al_addr, s1));
1356 for (a = v->uv_groups->al_next; a != NULL; a = a->al_next) {
1357 fprintf(fp, " %-15s\n",
1358 inet_fmt(a->al_addr, s1));
1359 }
1360 }
1361 if (v->uv_acl != NULL) {
1362 struct vif_acl *acl;
1363
1364 fprintf(fp, " boundaries: %-18s\n",
1365 inet_fmts(v->uv_acl->acl_addr, v->uv_acl->acl_mask, s1));
1366 for (acl = v->uv_acl->acl_next; acl != NULL; acl = acl->acl_next) {
1367 fprintf(fp, " : %-18s\n",
1368 inet_fmts(acl->acl_addr, acl->acl_mask, s1));
1369 }
1370 }
1371 v_req.vifi = vifi;
1372 if (ioctl(igmp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
1373 log(LOG_WARNING, 0,
1374 "SIOCGETVIFCNT fails");
1375 }
1376 else {
1377 fprintf(fp, " pkts in : %ld\n",
1378 v_req.icount);
1379 fprintf(fp, " pkts out: %ld\n",
1380 v_req.ocount);
1381 }
1382 fprintf(fp, "\n");
1383 }
1384 fprintf(fp, "\n");
1385 }
1386
1387 /*
1388 * Time out record of a group membership on a vif
1389 */
1390 static void
1391 DelVif(arg)
1392 void *arg;
1393 {
1394 cbk_t *cbk = (cbk_t *)arg;
1395 vifi_t vifi = cbk->vifi;
1396 struct uvif *v = &uvifs[vifi];
1397 struct listaddr *a, **anp, *g = cbk->g;
1398
1399 /*
1400 * Group has expired
1401 * delete all kernel cache entries with this group
1402 */
1403 if (g->al_query)
1404 DeleteTimer(g->al_query);
1405
1406 delete_lclgrp(vifi, g->al_addr);
1407
1408 anp = &(v->uv_groups);
1409 while ((a = *anp) != NULL) {
1410 if (a == g) {
1411 *anp = a->al_next;
1412 free((char *)a);
1413 } else {
1414 anp = &a->al_next;
1415 }
1416 }
1417
1418 free(cbk);
1419 }
1420
1421 /*
1422 * Set a timer to delete the record of a group membership on a vif.
1423 */
1424 static int
1425 SetTimer(vifi, g)
1426 vifi_t vifi;
1427 struct listaddr *g;
1428 {
1429 cbk_t *cbk;
1430
1431 cbk = (cbk_t *) malloc(sizeof(cbk_t));
1432 cbk->g = g;
1433 cbk->vifi = vifi;
1434 return timer_setTimer(g->al_timer, (cfunc_t)DelVif, (void *)cbk);
1435 }
1436
1437 /*
1438 * Delete a timer that was set above.
1439 */
1440 static int
1441 DeleteTimer(id)
1442 int id;
1443 {
1444 timer_clearTimer(id);
1445 return 0;
1446 }
1447
1448 /*
1449 * Send a group-specific query.
1450 */
1451 static void
1452 SendQuery(arg)
1453 void *arg;
1454 {
1455 cbk_t *cbk = (cbk_t *)arg;
1456 register struct uvif *v = &uvifs[cbk->vifi];
1457
1458 send_igmp(v->uv_lcl_addr, cbk->g->al_addr,
1459 IGMP_HOST_MEMBERSHIP_QUERY,
1460 cbk->q_time, cbk->g->al_addr, 0);
1461 cbk->g->al_query = 0;
1462 free(cbk);
1463 }
1464
1465 /*
1466 * Set a timer to send a group-specific query.
1467 */
1468 static int
1469 SetQueryTimer(g, vifi, to_expire, q_time)
1470 struct listaddr *g;
1471 vifi_t vifi;
1472 int to_expire, q_time;
1473 {
1474 cbk_t *cbk;
1475
1476 cbk = (cbk_t *) malloc(sizeof(cbk_t));
1477 cbk->g = g;
1478 cbk->q_time = q_time;
1479 cbk->vifi = vifi;
1480 return timer_setTimer(to_expire, (cfunc_t)SendQuery, (void *)cbk);
1481 }
1482