diff options
Diffstat (limited to 'usr.sbin/pim6dd/vif.c')
-rw-r--r-- | usr.sbin/pim6dd/vif.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/usr.sbin/pim6dd/vif.c b/usr.sbin/pim6dd/vif.c new file mode 100644 index 0000000..55bdb1f --- /dev/null +++ b/usr.sbin/pim6dd/vif.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: vif.c,v 1.3 1999/09/12 17:00:11 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +/* + * Exported variables. + */ +struct uvif uvifs[MAXMIFS]; /* array of all virtual interfaces */ +vifi_t numvifs; /* Number of vifs in use */ +int vifs_down; /* 1=>some interfaces are down */ +int phys_vif; /* An enabled vif */ +int udp_socket; /* Since the honkin' kernel doesn't support */ + /* ioctls on raw IP sockets, we need a UDP */ + /* socket as well as our IGMP (raw) socket. */ + /* How dumb. */ +int total_interfaces; /* Number of all interfaces: including the + * non-configured, but excluding the + * loopback interface and the non-multicast + * capable interfaces. + */ +if_set if_nullset; /* an interface face that has all-0 bit + * (for comparison) + */ + +/* + * Forward declarations. + */ +static void start_vif __P((vifi_t vifi)); +static void stop_vif __P((vifi_t vifi)); +static void start_all_vifs __P((void)); + + +void +init_vifs() +{ + vifi_t vifi; + struct uvif *v; + int enabled_vifs; + + numvifs = 0; + vifs_down = FALSE; + + /* + * Configure the vifs based on the interface configuration of + * the kernel and the contents of the configuration file. + * (Open a UDP socket for ioctl use in the config procedures if + * the kernel can't handle IOCTL's on the MLD socket.) + */ +#ifdef IOCTL_OK_ON_RAW_SOCKET + udp_socket = mld6_socket; +#else + if ((udp_socket = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + log(LOG_ERR, errno, "UDP6 socket"); +#endif + + /* + * Clean up all vifs + */ + for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) { + memset(v, 0, sizeof(*v)); + v->uv_flags = 0; + v->uv_metric = DEFAULT_METRIC; + v->uv_admetric = 0; + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; + strncpy(v->uv_name, "", IFNAMSIZ); + v->uv_groups = (struct listaddr *)NULL; + v->uv_dvmrp_neighbors = (struct listaddr *)NULL; + NBRM_CLRALL(v->uv_nbrmap); + v->uv_querier = (struct listaddr *)NULL; + v->uv_prune_lifetime = 0; + v->uv_acl = (struct vif_acl *)NULL; + v->uv_leaf_timer = 0; + v->uv_addrs = (struct phaddr *)NULL; + v->uv_filter = (struct vif_filter *)NULL; + v->uv_pim_hello_timer = 0; + v->uv_gq_timer = 0; + v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL; + v->uv_local_pref = default_source_preference; + v->uv_local_metric = default_source_metric; + } + + log(LOG_INFO, 0, "Getting ifs from kernel"); + config_vifs_from_kernel(); + log(LOG_INFO, 0, "Getting ifs from %s", configfilename); + config_vifs_from_file(); + + /* + * Quit if there are fewer than two enabled vifs or there is a vif + * which has no link local address. + */ + enabled_vifs = 0; + phys_vif = -1; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) + continue; + if (v->uv_linklocal == NULL) + log(LOG_ERR, 0, + "there is no link-local address on vif#%d", vifi); + if (phys_vif == -1) { + struct phaddr *p; + + /* + * If this vif has a global address, set its id + * to phys_vif. + */ + for(p = v->uv_addrs; p; p = p->pa_next) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) { + phys_vif = vifi; + break; + } + } + } + enabled_vifs++; + } + + if (enabled_vifs < 2) + log(LOG_ERR, 0, "can't forward: %s", + enabled_vifs == 0 ? "no enabled ifs" : "only one enabled if"); + + memset(&if_nullset, 0, sizeof(if_nullset)); + k_init_pim(mld6_socket); /* Call to kernel to initiliaze structures */ + + start_all_vifs(); +} + + +static void +start_all_vifs() +{ + vifi_t vifi; + struct uvif *v; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + /* Start vif if not DISABLED or DOWN */ + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) { + if (v->uv_flags & VIFF_DISABLED) + log(LOG_INFO, 0, + "%s is DISABLED; if #%u out of service", + v->uv_name, vifi); + else + log(LOG_INFO, 0, + "%s is DOWN; if #%u out of service", + v->uv_name, vifi); + } + else + start_vif(vifi); + } +} + + + +/* + * stop all vifs + */ +void +stop_all_vifs() +{ + vifi_t vifi; + struct uvif *v; + + for (vifi = 0, v=uvifs; vifi < numvifs; ++vifi, ++v) { + if (!(v->uv_flags & VIFF_DOWN)) { + stop_vif(vifi); + } + } +} + + +/* + * Initialize the vif and add to the kernel. The vif can be either + * physical, tunnel (tunnels will be used in the future + * when this code becomes PIM multicast boarder router.) + */ +static void +start_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + u_long random_delay; + + v = &uvifs[vifi]; + /* Initialy no router on any vif */ + v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~VIFF_DOWN; + v->uv_pim_hello_timer = 1 + RANDOM() % PIM_TIMER_HELLO_PERIOD; + /* TODO: CHECK THE TIMERS!!!!! Set or reset? */ + v->uv_gq_timer = 0; + v->uv_pim_neighbors = (pim_nbr_entry_t *)NULL; + + /* Tell kernel to add, i.e. start this vif */ + k_add_vif(mld6_socket, vifi, &uvifs[vifi]); + log(LOG_INFO, 0, "%s comes up; if #%u now in service", v->uv_name, vifi); + + /* + * Join the PIM multicast group on the interface. + */ + k_join(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex); + + /* + * Join the ALL-ROUTERS multicast group on the interface. + * This allows mtrace requests to loop back if they are run + * on the multicast router. + */ + k_join(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex); + + /* + * Until neighbors are discovered, assume responsibility for sending + * periodic group membership queries to the subnet. Send the first + * query. + */ + v->uv_flags |= VIFF_QUERIER; + query_groups(v); + + /* + * To avoid synchronization among routers booting simultaneously, set + * the hello timer to a random value between 1 to PIM_TIMER_HELLO_PERIOD. + */ + random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1)); + v->uv_pim_hello_timer = random_delay; +} + + +/* + * Stop a vif (either physical interface or tunnel). + * If we are running only PIM we don't have tunnels. + */ +static void +stop_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + struct listaddr *a; + register pim_nbr_entry_t *n, *next; + struct vif_acl *acl; + + /* + * TODO: make sure that the kernel viftable is + * consistent with the daemon table + */ + v = &uvifs[vifi]; + k_leave(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex); + k_leave(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex); + /* + * Discard all group addresses. (No need to tell kernel; + * the k_del_vif() call will clean up kernel state.) + */ + while (v->uv_groups != NULL) { + a = v->uv_groups; + v->uv_groups = a->al_next; + free((char *)a); + } + + /* + * TODO: inform (eventually) the neighbors I am going down by sending + * PIM_HELLO with holdtime=0 so someone else should become a DR. + */ + + /* TODO: dummy! Implement it!! Any problems if don't use it? */ + delete_vif_from_mrt(vifi); + + /* + * Delete the interface from the kernel's vif structure. + */ + k_del_vif(mld6_socket, vifi); + v->uv_flags = (v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS ) + | VIFF_DOWN; + v->uv_pim_hello_timer = 0; + v->uv_gq_timer = 0; + for (n = v->uv_pim_neighbors; n != NULL; n = next) { + next = n->next; /* Free the space for each neighbour */ + free((char *)n); + } + v->uv_pim_neighbors = NULL; + + /* TODO: currently not used */ + /* The Access Control List (list with the scoped addresses) */ + while (v->uv_acl != NULL) { + acl = v->uv_acl; + v->uv_acl = acl->acl_next; + free((char *)acl); + } + + vifs_down = TRUE; + log(LOG_INFO, 0, + "%s goes down; if #%u out of service", v->uv_name, vifi); +} + +/* + * return the max global Ipv6 address of an UP and ENABLED interface + * other than the MIFF_REGISTER interface. +*/ +struct sockaddr_in6 * +max_global_address() +{ + vifi_t vifi; + struct uvif *v; + struct phaddr *p; + struct phaddr *pmax = NULL; + + for(vifi=0,v=uvifs;vifi< numvifs;++vifi,++v) + { + if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER)) + continue; + /* + * take first the max global address of the interface + * (without link local) => aliasing + */ + for(p=v->uv_addrs;p!=NULL;p=p->pa_next) + { + /* + * If this is the first global address, take it anyway. + */ + if (pmax == NULL) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + pmax = p; + } + else { + if (inet6_lessthan(&pmax->pa_addr, + &p->pa_addr) && + !IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + pmax=p; + } + } + } + + return(pmax ? &pmax->pa_addr : NULL); +} + +struct sockaddr_in6 * +uv_global(vifi) + vifi_t vifi; +{ + struct uvif *v = &uvifs[vifi]; + struct phaddr *p; + + for (p = v->uv_addrs; p; p = p->pa_next) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + return(&p->pa_addr); + } + + return(NULL); +} + +/* + * See if any interfaces have changed from up state to down, or vice versa, + * including any non-multicast-capable interfaces that are in use as local + * tunnel end-points. Ignore interfaces that have been administratively + * disabled. + */ +void +check_vif_state() +{ + register vifi_t vifi; + register struct uvif *v; + struct ifreq ifr; + static int checking_vifs = 0; + + /* + * XXX: TODO: True only for DVMRP?? Check. + * If we get an error while checking, (e.g. two interfaces go down + * at once, and we decide to send a prune out one of the failed ones) + * then don't go into an infinite loop! + */ + if (checking_vifs) + return; + + vifs_down = FALSE; + checking_vifs = 1; + /* TODO: Check all potential interfaces!!! */ + /* Check the physical interfaces only */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & VIFF_DISABLED) + continue; + + strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ); + /* get the interface flags */ + if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) + log(LOG_ERR, errno, + "check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); + + if (v->uv_flags & VIFF_DOWN) { + if (ifr.ifr_flags & IFF_UP) { + start_vif(vifi); + } + else vifs_down = TRUE; + } + else { + if (!(ifr.ifr_flags & IFF_UP)) { + log(LOG_NOTICE, 0, + "%s has gone down; if #%u taken out of service", + v->uv_name, vifi); + stop_vif(vifi); + vifs_down = TRUE; + } + } + } + checking_vifs = 0; +} + + +/* + * If the source is directly connected to us, find the vif number for + * the corresponding physical interface (tunnels excluded). + * Local addresses are excluded. + * Return the vif number or NO_VIF if not found. + */ +vifi_t +find_vif_direct(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr)) + return(NO_VIF); + if (inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask)) + return(vifi); + } + } + return (NO_VIF); +} + + +/* + * Checks if src is local address. If "yes" return the vif index, + * otherwise return value is NO_VIF. + */ +vifi_t +local_address(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr)) + return(vifi); + } + } + /* Returning NO_VIF means not a local address */ + return (NO_VIF); +} + +/* + * If the source is directly connected, or is local address, + * find the vif number for the corresponding physical interface + * (tunnels excluded). + * Return the vif number or NO_VIF if not found. + */ +vifi_t +find_vif_direct_local(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr) || + inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask)) + return(vifi); + } + } + return (NO_VIF); +} |