diff options
Diffstat (limited to 'usr.sbin/pim6sd/vif.c')
-rw-r--r-- | usr.sbin/pim6sd/vif.c | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/usr.sbin/pim6sd/vif.c b/usr.sbin/pim6sd/vif.c new file mode 100644 index 0000000..e7ef2d9 --- /dev/null +++ b/usr.sbin/pim6sd/vif.c @@ -0,0 +1,824 @@ +/* + * 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. + * + * $FreeBSD$ + */ +/* + * Questions concerning this software should be directed to + * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg. + * + */ +/* + * This program has been derived from pim6dd. + * The pim6dd program is covered by the license in the accompanying file + * named "LICENSE.pim6dd". + */ +/* + * This program has been derived from pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + */ +/* + * 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. + * + */ + +#include <sys/ioctl.h> +#include <errno.h> +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include "vif.h" +#include "mld6.h" +#include "pim6.h" +#include "pimd.h" +#include "route.h" +#include "config.h" +#include "inet6.h" +#include "kern.h" +#include "mld6_proto.h" +#include "pim6_proto.h" +#include "mrt.h" +#include "debug.h" +#include "timer.h" + +struct uvif uvifs[MAXMIFS]; /*the list of virtualsinterfaces */ +vifi_t numvifs; /*total number of interface */ +int vifs_down; +vifi_t reg_vif_num; /*register interface*/ +int phys_vif; /* An enabled vif that has a global address */ +int udp_socket; +int total_interfaces; +if_set if_nullset; +if_set if_result; + +int init_reg_vif(); +void start_all_vifs(); +void start_vif( vifi_t vifi ); +void stop_vif( vifi_t vivi ); +int update_reg_vif( vifi_t register_vifi); + +extern int cfparse(int, int); + +void init_vifs() +{ + vifi_t vifi; + struct uvif *v; + int enabled_vifs; + + numvifs = 0; + reg_vif_num = NO_VIF; + + /* + * 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 all the interfaces ... */ + + for(vifi = 0,v=uvifs; vifi < MAXVIFS; ++ vifi, ++v) + { + memset(v,0,sizeof(*v)); /* everything is zeroed => NULL , pointer NULL , addrANY ...) */ + v->uv_metric = DEFAULT_METRIC; + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; + strncpy(v->uv_name,"",IFNAMSIZ); + v->uv_local_pref = default_source_preference; + v->uv_local_metric = default_source_metric; + } + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG,0,"Interfaces world initialized..."); + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG,0,"Getting vifs from kernel"); + config_vifs_from_kernel(); + if (max_global_address() == NULL) + log(LOG_ERR, 0, "There's no global address"); + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG,0,"Getting vifs from %s",configfilename); + + /* read config from file */ + if (cfparse(1, 0) != 0) + log(LOG_ERR, 0, "fatal error in parsing the config file"); + + enabled_vifs = 0; + phys_vif = -1; + + for( vifi = 0, v = uvifs ; vifi < numvifs ; ++ vifi,++v) + { + if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER)) + continue; + if(v->uv_linklocal == NULL) + log(LOG_ERR,0,"there is no link-local address on vif %s",v->uv_name); + 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 vifs" : "only one enabled vif" ); + + memset(&if_nullset,0,sizeof(if_nullset)); + k_init_pim(mld6_socket); + IF_DEBUG(DEBUG_PIM_DETAIL) + log(LOG_DEBUG,0,"Pim kernel initialization done"); + + + /* Add a dummy virtual interface to support Registers in the kernel. */ + init_reg_vif(); + + start_all_vifs(); + +} +int init_reg_vif() +{ + struct uvif *v; + vifi_t i; + + v = &uvifs[numvifs]; + if (( numvifs+1 ) == MAXVIFS ) + { + /* Exit the program! The PIM router must have a Register vif */ + log(LOG_ERR, 0, + "cannot install the Register vif: too many interfaces"); + /* To make lint happy */ + return (FALSE); + } + + /* + * So far in PIM we need only one register vif and we save its number in + * the global reg_vif_num. + */ + + + reg_vif_num = numvifs; + + /* Use the address of the first available physical interface to + * create the register vif. + */ + + for(i =0 ; i < numvifs ; i++) + { + if(uvifs[i].uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER)) + continue; + else + break; + } + if( i >= numvifs) + { + log(LOG_ERR, 0, "No physical interface enabled"); + return -1; + } + + + memcpy(v,&uvifs[i],sizeof(*v)); + strncpy(v->uv_name,"register_mif0",IFNAMSIZ); + v->uv_flags = MIFF_REGISTER; + +#ifdef PIM_EXPERIMENTAL + v->uv_flags |= MIFF_REGISTER_KERNEL_ENCAP; +#endif + + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG,0,"Interface %s (subnet %s) ,installed on vif #%u - rate = %d", + v->uv_name,net6name(&v->uv_prefix.sin6_addr,&v->uv_subnetmask), + reg_vif_num,v->uv_rate_limit); + + numvifs++; + total_interfaces++; + return 0; +} + +void start_all_vifs() +{ + vifi_t vifi; + struct uvif *v; + u_int action; + + + /* Start first the NON-REGISTER vifs */ + + for(action=0; ;action = MIFF_REGISTER ) + { + for(vifi= 0,v = uvifs;vifi < numvifs ; ++vifi, ++v) + { + if (( v->uv_flags & MIFF_REGISTER ) ^ action ) + /* If starting non-registers but the vif is a register + * or if starting registers, but the interface is not + * a register, then just continue. + */ + continue; + + if ( v->uv_flags & (VIFF_DISABLED | VIFF_DOWN )) + { + IF_DEBUG(DEBUG_IF) + { + if ( v-> uv_flags & VIFF_DISABLED) + log(LOG_DEBUG,0,"%s is DISABLED ; vif #%u out of service",v->uv_name,vifi); + else + log(LOG_DEBUG,0,"%s is DOWN ; vif #%u out of service",v->uv_name,vifi); + } + } + else + start_vif(vifi); + } + if ( action == MIFF_REGISTER) + break; + } +} + +/* + * Initialize the vif and add to the kernel. The vif can be either + * physical, register or tunnel (tunnels will be used in the future + * when this code becomes PIM multicast boarder router. + */ + + +void start_vif (vifi_t vifi) +{ + struct uvif *v; + + v = &uvifs[vifi]; + + /* Initialy no router on any vif */ + + if( v-> uv_flags & MIFF_REGISTER) + v->uv_flags = v->uv_flags & ~VIFF_DOWN; + else + { + v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~ VIFF_DOWN; + v->uv_pim_hello_timer = 1 + RANDOM() % pim_hello_period; + v->uv_jp_timer = 1 + RANDOM() % pim_join_prune_period; + } + + /* Tell kernel to add, i.e. start this vif */ + + k_add_vif(mld6_socket,vifi,&uvifs[vifi]); + IF_DEBUG(DEBUG_IF) + log(LOG_DEBUG,0,"%s comes up ,vif #%u now in service",v->uv_name,vifi); + + if(!(v->uv_flags & MIFF_REGISTER)) + { + + /* + * 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.this allow receiving mld6 messages too. + */ + + 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); + + /* + * Send a probe via the new vif to look for neighbors. + */ + + send_pim6_hello( v , pim_hello_holdtime ); + } +} + +/* + * Stop a vif (either physical interface, tunnel or + * register.) If we are running only PIM we don't have tunnels. + */ + + +void stop_vif( vifi_t vifi ) +{ + struct uvif *v; + struct listaddr *a; + register pim_nbr_entry_t *n; + register pim_nbr_entry_t *next; + struct vif_acl *acl; + + + /* + * TODO: make sure that the kernel viftable is + * consistent with the daemon table + */ + + v=&uvifs[vifi]; + if( !( v->uv_flags&MIFF_REGISTER ) ) + { + 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; + if( !(v->uv_flags & MIFF_REGISTER )) + { + RESET_TIMER(v->uv_pim_hello_timer); + RESET_TIMER(v->uv_jp_timer); + RESET_TIMER(v->uv_gq_timer); + + 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; + + IF_DEBUG(DEBUG_IF) + log( LOG_DEBUG ,0,"%s goes down , vif #%u out of service" , v->uv_name , vifi); +} + +/* + * Update the register vif in the multicast routing daemon and the + * kernel because the interface used initially to get its local address + * is DOWN. register_vifi is the index to the Register vif which needs + * to be updated. As a result the Register vif has a new uv_lcl_addr and + * is UP (virtually :)) + */ +int +update_reg_vif( vifi_t register_vifi ) +{ + register struct uvif *v; + register vifi_t vifi; + + /* Find the first useable vif with solid physical background */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL + | MIFF_REGISTER)) + continue; + /* Found. Stop the bogus Register vif first */ + stop_vif(register_vifi); + uvifs[register_vifi].uv_linklocal->pa_addr = + uvifs[vifi].uv_linklocal->pa_addr; + start_vif(register_vifi); + IF_DEBUG(DEBUG_PIM_REGISTER | DEBUG_IF) + log(LOG_NOTICE, 0, "%s has come up; vif #%u now in service", + uvifs[register_vifi].uv_name, register_vifi); + return 0; + } + vifs_down = TRUE; + log(LOG_WARNING, 0, "Cannot start Register vif: %s", + uvifs[vifi].uv_name); + return(-1); +} + +/* + * 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); +} + +/* + * Check if the interface exists in the mif table. If true + * return the highest address of the interface else return NULL. + */ +struct sockaddr_in6 * +local_iface(char *ifname) +{ + register struct uvif *v; + vifi_t vifi; + 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; + if(EQUAL(v->uv_name, ifname)) + { + for(p=v->uv_addrs; p!=NULL; p=p->pa_next) + { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr)&& + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) { + /* + * If this is the first global address + * or larger than the current MAX global + * address, remember it. + */ + if (pmax == NULL || + inet6_lessthan(&pmax->pa_addr, + &p->pa_addr)) + pmax = p; + } + } + if (pmax) + return(&pmax->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=TRUE; + + /* TODO: Check all potential interfaces!!! */ + /* Check the physical and tunnels only */ + for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v ) + { + if( v->uv_flags & ( VIFF_DISABLED|MIFF_REGISTER ) ) + 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 ; vif #%u taken out of service", + v->uv_name , vifi ); + stop_vif ( vifi ); + vifs_down = TRUE; + } + } + } + + /* Check the register(s) vif(s) */ + for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v ) + { + register vifi_t vifi2; + register struct uvif *v2; + int found; + + if( !(v->uv_flags & MIFF_REGISTER ) ) + continue; + else + { + found=0; + + /* Find a physical vif with the same IP address as the + * Register vif. + */ + for( vifi2=0 , v2=uvifs ; vifi2<numvifs ; ++vifi2 , ++v2 ) + { + if( v2->uv_flags & ( VIFF_DISABLED|VIFF_DOWN|VIFF_TUNNEL|MIFF_REGISTER )) + continue; + if( IN6_ARE_ADDR_EQUAL( &v->uv_linklocal->pa_addr.sin6_addr, + &v2->uv_linklocal->pa_addr.sin6_addr )) + { + found=1; + break; + } + } + if(!found) + /* The physical interface with the IP address as the Register + * vif is probably DOWN. Get a replacement. + */ + update_reg_vif( vifi ); + } + } + 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|MIFF_REGISTER)) + 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 | MIFF_REGISTER)) + 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 |MIFF_REGISTER)) + 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); +} + +int +vif_forwarder(if_set *p1 , if_set *p2) +{ + int idx; + + for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++) + { + if (p1->ifs_bits[idx] & p2->ifs_bits[idx]) + return(TRUE); + + } + + /* (p1 & p2) is empty. We're not the forwarder */ + return(FALSE); +} + +if_set * +vif_and(if_set *p1 , if_set *p2, if_set *result) +{ + int idx; + + IF_ZERO(result); + + for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++) + { + result->ifs_bits[idx] = p1->ifs_bits[idx] & p2->ifs_bits[idx]; + } + + return(result); +} + +if_set * +vif_xor(if_set *p1 , if_set *p2, if_set *result) +{ + int idx; + + IF_ZERO(result); + + for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++) + { + result->ifs_bits[idx] = + p1->ifs_bits[idx] ^ p2->ifs_bits[idx]; + } + + return(result); +} +/* + * 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); + } + } +} + +struct uvif * +find_vif(ifname) + char *ifname; +{ + struct uvif *v; + vifi_t vifi; + + for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) { + if (strcasecmp(v->uv_name, ifname) == 0) + return(v); + } + + return(NULL); +} |