Files
Arquivotheca.AIX-4.1.3/bos/kernel/net/if.c
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1008 lines
22 KiB
C

static char sccsid[] = "@(#)27 1.46.1.20 src/bos/kernel/net/if.c, sysnet, bos41J, 9518A_all 5/1/95 10:51:55";
/*
* COMPONENT_NAME: SYSNET
*
* FUNCTIONS: if_attach
* if_detach
* if_down
* if_nostat
* if_qflush
* if_slowsched
* if_slowtimo
* ifa_ifwithaddr
* ifa_ifwithaf
* ifa_ifwithdstaddr
* ifa_ifwithnet
* ifaof_ifpforaddr
* ifconf
* ifinit
* ifioctl
* ifreset
* ifubareset
* ifunit
* link_rtrequest
*
* ORIGINS: 26,27,85
*
*
* (C) COPYRIGHT International Business Machines Corp. 1989,1993
* All Rights Reserved
* Licensed Materials - Property of IBM
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
* (c) Copyright 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC.
* ALL RIGHTS RESERVED
*/
/*
* OSF/1 1.2
*/
/*
* Copyright (C) 1988,1989 Encore Computer Corporation. All Rights Reserved
*
* Property of Encore Computer Corporation.
* This software is made available solely pursuant to the terms of
* a software license agreement which governs its use. Unauthorized
* duplication, distribution or sale are strictly prohibited.
*
*/
/*
* Copyright (c) 1980, 1986 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 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.
*
* Base: if.c 7.8 (Berkeley) 5/5/89
* Merged: if.c 7.13 (Berkeley) 6/28/90
*/
#include "net/net_globals.h"
#include "sys/param.h"
#include "sys/time.h"
#ifdef _AIX_FULLOSF
#include "sys/kernel.h"
#endif /* _AIX_FULLOSF */
#include "sys/ioctl.h"
#include "sys/errno.h"
#ifndef _AIX_FULLOSF
#include "sys/timer.h"
#include "sys/intr.h"
#endif /* _AIX_FULLOSF */
#include "sys/mbuf.h"
#include "sys/socket.h"
#include "sys/socketvar.h"
#include "sys/protosw.h"
#include "net/if.h"
#include "net/if_dl.h"
#include "net/if_types.h"
#include "net/netisr.h"
#include "net/route.h"
#ifdef _AIX
#include "sys/cdli.h"
#include "net/nd_lan.h"
#endif /* _AIX */
#include "net/net_malloc.h"
#ifdef IP_MULTICAST
#include <netinet/in_var.h>
#endif
#include <sys/domain.h>
LOCK_ASSERTL_DECL
int ifqmaxlen = IFQ_MAXLEN;
#ifdef NETSYNC_LOCK
simple_lock_data_t ifa_refcnt_lock;
#endif
volatile rt_ifp_del_hit;
/*
* Network interface utility routines.
*
* Routines with ifa_ifwith* names take sockaddr *'s as
* parameters.
*/
struct ifnet *ifnet;
extern struct ifnet loif;
int if_index, if_indexlim = 8;
struct ifaddr **ifnet_addrs;
void
ifinit()
{
register struct ifnet *ifp;
loinit(); /* Be sure to init loopback */
for (ifp = ifnet; ifp; ifp = ifp->if_next) {
if (ifp->if_snd.ifq_maxlen == 0)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
if (ifp->if_hdrlen > max_linkhdr)
max_linkhdr = ifp->if_hdrlen;
}
(void) netisr_add(NETISR_IFSLOW, if_slowtimo,
(struct ifqueue *)0, (struct domain *)0);
#ifdef _AIX_FULLOSF
if_slowtimo();
#else
if_slowsched();
#endif /* _AIX_FULLOSF */
}
#ifdef vax
void
ifubareset(uban)
{
ifreset(uban);
}
#endif
/*
* Call each interface on a bus reset.
*/
void
ifreset(n)
int n;
{
register struct ifnet *ifp;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (ifp->if_reset)
(*ifp->if_reset)(ifp->if_unit, n);
}
/*
* Attach an interface to the
* list of "active" interfaces.
*/
void
if_attach(ifp)
struct ifnet *ifp;
{
unsigned socksize, ifasize;
int namelen, unitlen;
char workbuf[16];
register struct ifnet **p = &ifnet;
register struct sockaddr_dl *sdl;
register struct ifaddr *ifa;
if (ifp != &loif) {
IFQ_LOCKINIT(&(ifp->if_snd));
#ifdef IP_MULTICAST
IFMULTI_INITLOCK(ifp);
#endif /* IP_MULTICAST */
NETSTAT_LOCKINIT(&(ifp->if_slock));
}
/*
* It is here assumed if_attach is called only during configure().
*/
while (*p)
p = &((*p)->if_next);
*p = ifp;
ifp->if_index = ++if_index;
if (ifnet_addrs == 0 || if_index >= if_indexlim) {
unsigned n = (if_indexlim <<= 1) * sizeof(ifa);
struct ifaddr **q;
NET_MALLOC(q, struct ifaddr **, n, M_IFADDR, M_WAITOK);
if (ifnet_addrs) {
bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
NET_FREE(ifnet_addrs, M_IFADDR);
}
ifnet_addrs = q;
}
/*
* Only add a LINK addr if one doesn't already exist.
*/
for (ifa=ifp->if_addrlist;
ifa && ifa->ifa_addr->sa_family != AF_LINK;
ifa = ifa->ifa_next);
if (!ifa) {
/*
* create a Link Level name for this device
*/
sprintf(workbuf, ifp->if_unit);
namelen = strlen(ifp->if_name);
unitlen = strlen(workbuf);
#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
socksize = _offsetof(struct sockaddr_dl, sdl_data[0]) +
unitlen + namelen + ifp->if_addrlen;
socksize = (socksize + (sizeof(long)-1)) & ~(sizeof(long)-1);
if (socksize < sizeof(*sdl))
socksize = sizeof(*sdl);
ifasize = sizeof(*ifa) + 2 * socksize;
NET_MALLOC(ifa, struct ifaddr *, ifasize, M_IFADDR, M_WAITOK);
bzero((caddr_t)ifa, ifasize);
sdl = (struct sockaddr_dl *)(ifa + 1);
ifa->ifa_addr = (struct sockaddr *)sdl;
ifa->ifa_ifp = ifp;
sdl->sdl_len = socksize;
sdl->sdl_family = AF_LINK;
bcopy(ifp->if_name, sdl->sdl_data, namelen);
bcopy((caddr_t)workbuf, namelen + (caddr_t)sdl->sdl_data, unitlen);
sdl->sdl_nlen = (namelen += unitlen);
sdl->sdl_index = ifp->if_index;
sdl->sdl_type = ifp->if_type;
#ifdef _AIX_FULLOSF
/*
* TODO: generalize this.
*/
if (sdl->sdl_type == IFT_ETHER && ifp->if_addrlen == 6) {
sdl->sdl_alen = ifp->if_addrlen;
bcopy((caddr_t)(ifp + 1), /* XXX struct arpcom -> ac_enaddr */
namelen + (caddr_t)sdl->sdl_data,
sdl->sdl_alen);
} /* end TODO */
#else
if (ifp->if_addrlen != 0) {
sdl->sdl_alen = ifp->if_addrlen;
/*
* copy hwaddr from the arpcom struct. This bcopy
* assumes the hwaddr immediately follows the ifnet
* struct in the arpcom struct.
*/
bcopy((caddr_t)(ifp + 1), namelen + (caddr_t)sdl->sdl_data,
sdl->sdl_alen);
} else
sdl->sdl_alen = 0;
#endif /* _AIX_FULLOSF */
sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
ifa->ifa_netmask = (struct sockaddr *)sdl;
sdl->sdl_len = socksize - ifp->if_addrlen;
while (namelen != 0)
sdl->sdl_data[--namelen] = 0xff;
ifa->ifa_next = ifp->if_addrlist;
ifa->ifa_rtrequest = link_rtrequest;
ifnet_addrs[ifp->if_index - 1] = ifp->if_addrlist = ifa;
}
#ifdef _AIX
{
struct timestruc_t ct;
curtime (&ct);
ifp->if_lastchange.tv_sec = (int) ct.tv_sec;
ifp->if_lastchange.tv_usec = (int) ct.tv_nsec / 1000;
}
#endif /* _AIX */
}
/*
* Locate an interface based on a complete address.
*/
/*ARGSUSED*/
struct ifaddr *
ifa_ifwithaddr(addr)
register struct sockaddr *addr;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
#define equal(a1, a2) \
(bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
for (ifp = ifnet; ifp; ifp = ifp->if_next)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != addr->sa_family)
continue;
if (equal(addr, ifa->ifa_addr))
return (ifa);
if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
equal(ifa->ifa_broadaddr, addr))
return (ifa);
}
return ((struct ifaddr *)0);
}
/*
* Locate the point to point interface with a given destination address.
*/
/*ARGSUSED*/
struct ifaddr *
ifa_ifwithdstaddr(addr)
register struct sockaddr *addr;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (ifp->if_flags & IFF_POINTOPOINT)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != addr->sa_family)
continue;
if (equal(addr, ifa->ifa_dstaddr))
return (ifa);
}
return ((struct ifaddr *)0);
}
/*
* Find an interface on a specific network. If many, choice
* is first found.
*/
struct ifaddr *
ifa_ifwithnet(addr)
struct sockaddr *addr;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
u_int af = addr->sa_family;
if (af >= AF_MAX)
return (0);
if (af == AF_LINK) {
register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
if (sdl->sdl_index && sdl->sdl_index <= if_index)
return (ifnet_addrs[sdl->sdl_index - 1]);
}
for (ifp = ifnet; ifp; ifp = ifp->if_next)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
register char *cp, *cp2, *cp3;
register char *cplim;
if (ifa->ifa_addr->sa_family != af || ifa->ifa_netmask == 0)
continue;
cp = addr->sa_data;
cp2 = ifa->ifa_addr->sa_data;
cp3 = ifa->ifa_netmask->sa_data;
cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
for (; cp3 < cplim; cp3++)
if ((*cp++ ^ *cp2++) & *cp3)
break;
if (cp3 == cplim)
return (ifa);
}
return ((struct ifaddr *)0);
}
/*
* Find an interface using a specific address family
*/
struct ifaddr *
ifa_ifwithaf(af)
register int af;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == af)
return (ifa);
return ((struct ifaddr *)0);
}
/*
* Find an interface address specific to an interface best matching
* a given address.
*/
struct ifaddr *
ifaof_ifpforaddr(addr, ifp)
struct sockaddr *addr;
register struct ifnet *ifp;
{
register struct ifaddr *ifa;
register char *cp, *cp2, *cp3;
register char *cplim;
struct ifaddr *ifa_maybe = 0;
u_int af = addr->sa_family;
if (af >= AF_MAX)
return (0);
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != af)
continue;
ifa_maybe = ifa;
if (ifa->ifa_netmask == 0) {
if (equal(addr, ifa->ifa_addr) ||
(ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
return (ifa);
continue;
}
cp = addr->sa_data;
cp2 = ifa->ifa_addr->sa_data;
cp3 = ifa->ifa_netmask->sa_data;
cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
for (; cp3 < cplim; cp3++)
if ((*cp++ ^ *cp2++) & *cp3)
break;
if (cp3 == cplim)
return (ifa);
}
return (ifa_maybe);
}
/*
* Default action when installing a route with a Link Level gateway.
* Lookup an appropriate real ifa to point to.
* This should be moved to /sys/net/link.c eventually.
*/
void
link_rtrequest(cmd, rt, sa)
register struct rtentry *rt;
struct sockaddr *sa;
{
register struct ifaddr *ifa;
struct sockaddr *dst;
struct ifnet *ifp, *oldifnet = ifnet;
IFAREFCNT_LOCK_DECL()
if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
return;
if (ifa = ifaof_ifpforaddr(dst, ifp)) {
IFAREFCNT_LOCK()
IFAFREE(rt->rt_ifa);
rt->rt_ifa = ifa;
ifa->ifa_refcnt++;
IFAREFCNT_UNLOCK()
if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
ifa->ifa_rtrequest(cmd, rt, sa);
}
}
/*
* Mark an interface down and notify protocols of
* the transition.
* NOTE: must be called at splnet or eqivalent.
*/
void
if_down(ifp)
register struct ifnet *ifp;
{
register struct ifaddr *ifa;
ifp->if_flags &= ~IFF_UP;
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
if_qflush(&ifp->if_snd);
#ifdef _AIX
{
struct timestruc_t ct;
curtime (&ct);
ifp->if_lastchange.tv_sec = (int) ct.tv_sec;
ifp->if_lastchange.tv_usec = (int) ct.tv_nsec / 1000;
}
#endif /* _AIX */
}
/*
* Flush an interface queue.
*/
void
if_qflush(ifq)
register struct ifqueue *ifq;
{
register struct mbuf *m, *n;
IFQ_LOCK_DECL()
IFQ_LOCK(ifq);
n = ifq->ifq_head;
while (m = n) {
n = m->m_act;
m_freem(m);
}
ifq->ifq_head = 0;
ifq->ifq_tail = 0;
ifq->ifq_len = 0;
IFQ_UNLOCK(ifq);
}
/*
* Handle interface watchdog timer routines. Called
* from softclock, we decrement timers (if set) and
* call the appropriate interface routine on expiration.
*/
void
if_slowtimo()
{
register struct ifnet *ifp;
for (ifp = ifnet; ifp; ifp = ifp->if_next) {
if (ifp->if_timer == 0 || --ifp->if_timer)
continue;
if (ifp->if_watchdog)
(*ifp->if_watchdog)(ifp->if_unit);
}
#ifdef _AIX_FULLOSF
timeout(netisr_timeout, (caddr_t)NETISR_IFSLOW, hz/IFNET_SLOWHZ);
#endif /* _AIX_FULLOSF */
}
#ifndef _AIX_FULLOSF
if_slowsched()
{
int ticks;
extern int if_slowsched();
static int first = 1;
static struct trb *iftime;
if (first) {
first = 0;
/* Allocate timer */
iftime = talloc();
iftime->func = (void *)if_slowsched;
iftime->func_data = (ulong) 0;
iftime->ipri = INTCLASS2;
}
schednetisr(NETISR_IFSLOW);
iftime->timeout.it_value.tv_sec = IFNET_SLOWHZ;
iftime->timeout.it_value.tv_nsec = 0;
iftime->ipri = INTCLASS2;
tstart(iftime);
}
/*
* Zero statistical elements of the interface array before
* attach.
*/
if_nostat(ifp)
struct ifnet *ifp;
{
ifp->if_ipackets = 0;
ifp->if_ierrors = 0;
ifp->if_opackets = 0;
ifp->if_oerrors = 0;
ifp->if_collisions = 0;
ifp->if_ibytes=0;
ifp->if_obytes=0;
ifp->if_imcasts=0;
ifp->if_omcasts=0;
ifp->if_noproto=0;
ifp->if_iqdrops=0;
ifp->if_snd.ifq_len = 0;
ifp->if_snd.ifq_drops=0;
}
if_ifp_rtflush(rn, ifp)
struct radix_node *rn;
struct ifnet *ifp;
{
int error;
int flags;
struct sockaddr netmask;
bzero(&netmask, sizeof(netmask));
for (; rn; rn = rn->rn_dupedkey) {
struct rtentry *rt = (struct rtentry *)rn;
if ( (rn->rn_flags & RNF_ROOT) || (rt->rt_ifp != ifp) )
continue;
if (rt->rt_flags & RTF_HOST)
flags = RTF_HOST;
else
flags = RTF_GATEWAY;
if (((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr != 0)
netmask = *(rt->rt_ifa->ifa_netmask);
if (rtrequest_nolock(RTM_DELETE, rt_key(rt), rt->rt_gateway,
&netmask, flags, NULL) == 0)
rt_ifp_del_hit++;
}
return (0);
}
/*
* NAME: if_rt_delete
* FUNCTION: removes routing entries associated with given interface.
* DESCRIPTION:
* called from if_detach().
* RETURNS:
* nothing.
*/
if_rt_delete(ifp)
struct ifnet *ifp;
{
struct ifaddr *ifa;
struct radix_node_head *rnh;
int i;
ROUTE_LOCK_DECL()
ROUTE_WRITE_LOCK();
for (rnh = radix_node_head; rnh; rnh = rnh->rnh_next) {
if (rnh->rnh_af != AF_INET)
continue;
for (i = 0; i < 256; i++) {
rt_ifp_del_hit = 0;
rt_walk(rnh->rnh_treetop, if_ifp_rtflush, ifp);
if (!rt_ifp_del_hit)
break;
}
}
ROUTE_WRITE_UNLOCK();
}
/*
* Detach an interface from the
* list of "active" interfaces.
*/
if_detach(ifp)
register struct ifnet *ifp;
{
register struct ifnet *p;
register struct ifnet *prevp;
struct mbuf *m;
struct ifaddr *ifa;
struct domain *dp;
struct protosw CONST *pr;
int i;
struct in_aliasreq ifra;
IFQ_LOCK_DECL()
pfctlinput(PRC_IFDETACH, (struct sockaddr *)ifp);
if_down(ifp);
for (p = ifnet; p && p != ifp; p = p->if_next)
prevp = p;
if (p == ifp) {
if (p == ifnet)
ifnet = NULL;
else
prevp->if_next = p->if_next;
ifp->if_next = NULL;
} else
return(ENOENT);
IF_DEQUEUE(&ifp->if_snd, m);
while(m) {
m_freem(m);
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
}
if_rt_delete(ifp);
bzero(&ifra, sizeof(ifra));
sprintf(ifra.ifra_name, "%s%d", ifp->if_name, ifp->if_unit);
ifra.ifra_addr.sin_family = AF_INET;
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
struct socket *so;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
ifra.ifra_addr.sin_addr.s_addr =
((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr;
if (socreate(AF_INET, &so, SOCK_DGRAM, 0) == 0) {
SOCKET_LOCK(so);
ifioctl(so, SIOCDIFADDR, &ifra);
sofree(so);
SOCKET_UNLOCK(so);
}
}
if (ifp->if_ioctl)
(void) (*ifp->if_ioctl)(ifp, SIOCIFDETACH, 0);
return(0);
}
#endif /* _AIX_FULLOSF */
/*
* Map interface name to
* interface structure pointer.
*/
struct ifnet *
ifunit(name)
register char *name;
{
register char *cp;
register struct ifnet *ifp;
int unit;
unsigned len;
char *ep, c;
for (cp = name; cp < name + IFNAMSIZ && *cp; cp++)
if (*cp >= '0' && *cp <= '9')
break;
if (*cp == '\0' || cp == name + IFNAMSIZ)
return ((struct ifnet *)0);
/*
* Save first char of unit, and pointer to it,
* so we can put a null there to avoid matching
* initial substrings of interface names.
*/
len = cp - name + 1;
c = *cp;
ep = cp;
for (unit = 0; *cp >= '0' && *cp <= '9'; )
unit = unit * 10 + *cp++ - '0';
*ep = 0;
for (ifp = ifnet; ifp; ifp = ifp->if_next) {
if (bcmp(ifp->if_name, name, len))
continue;
if (unit == ifp->if_unit)
break;
}
*ep = c;
return (ifp);
}
/*
* Interface ioctls.
*/
ifioctl(so, cmd, data)
struct socket *so;
int cmd;
caddr_t data;
{
register struct ifnet *ifp;
register struct ifreq *ifr;
int error;
LOCK_ASSERT("ifioctl", SOCKET_ISLOCKED(so));
switch (cmd) {
case SIOCGIFCONF:
#ifdef OSIOCGIFCONF
case OSIOCGIFCONF:
#endif
#ifdef CSIOCGIFCONF
case CSIOCGIFCONF:
#endif
return (ifconf(cmd, data));
case SIOCSARP:
case SIOCDARP:
if (!(so->so_state & SS_PRIV))
return (EACCES);
/* FALL THROUGH */
case SIOCGARP:
#ifdef OSIOCGARP
case OSIOCGARP:
#endif
#ifndef _AIX
return (arpioctl(cmd, data));
#else /* _AIX */
{
int af;
struct arpreq *ar;
ar = (struct arpreq *)data;
af = ar->arp_pa.sa_family;
if ( (af > AF_MAX) || (af_table[af].config.ioctl == NULL) )
return(EINVAL);
return( (*(af_table[af].config.ioctl))(cmd, data) );
}
#endif /* _AIX */
}
ifr = (struct ifreq *)data;
ifp = ifunit(ifr->ifr_name);
if (ifp == 0)
return (ENXIO);
switch (cmd) {
case SIOCGIFFLAGS:
ifr->ifr_flags = ifp->if_flags;
break;
case SIOCGIFMETRIC:
ifr->ifr_metric = ifp->if_metric;
break;
case SIOCSIFFLAGS:
if (!(so->so_state & SS_PRIV))
return (EACCES);
if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
if_down(ifp);
}
ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
(ifr->ifr_flags &~ IFF_CANTCHANGE);
if (ifp->if_ioctl)
(void) (*ifp->if_ioctl)(ifp, cmd, data);
break;
case SIOCSIFMETRIC:
if (!(so->so_state & SS_PRIV))
return (EACCES);
ifp->if_metric = ifr->ifr_metric;
break;
#ifdef _AIX
case SIOCSIFMTU:
if (!(so->so_state & SS_PRIV))
return (EACCES);
ifp->if_mtu = ifr->ifr_mtu;
break;
case SIOCIFATTACH:
/*
* Notify the protocols that an interface is up and
* attached to a NDD...
*/
if (!(so->so_state & SS_PRIV))
return(EACCES);
(*so->so_proto->pr_ctlinput)(PRC_IFATTACH, ifp, (caddr_t)NULL);
break;
case SIOCIFDETACH:
if (!(so->so_state & SS_PRIV))
return(EACCES);
if_detach(ifp);
break;
case SIOCGIFMTU:
ifr->ifr_mtu = ifp->if_mtu;
break;
case SIOCSX25XLATE:
case SIOCDX25XLATE:
if (!(so->so_state & SS_PRIV))
return (EACCES);
if (ifp->if_ioctl)
(void) (*ifp->if_ioctl)(ifp, cmd, data);
break;
#endif /* _AIX */
#ifdef IP_MULTICAST
case SIOCADDMULTI:
case SIOCDELMULTI:
if (!(so->so_state & SS_PRIV))
return (EACCES);
if (ifp->if_ioctl)
return (*ifp->if_ioctl)(ifp, cmd, data);
else
return (EOPNOTSUPP);
#endif IP_MULTICAST
default:
if (so->so_proto == 0)
return (EOPNOTSUPP);
#ifndef COMPAT_43
return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
cmd, data, ifp));
#else
{
int ocmd = cmd;
switch (cmd) {
case SIOCSIFDSTADDR:
case SIOCSIFADDR:
case SIOCSIFBRDADDR:
case SIOCSIFNETMASK:
#if BYTE_ORDER != BIG_ENDIAN
if (ifr->ifr_addr.sa_family == 0 &&
ifr->ifr_addr.sa_len < 16) {
ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
ifr->ifr_addr.sa_len = 16;
}
#else
if (ifr->ifr_addr.sa_len == 0)
ifr->ifr_addr.sa_len = 16;
#endif
break;
#ifdef OSIOCGIFADDR
case OSIOCGIFADDR:
cmd = SIOCGIFADDR;
break;
case OSIOCGIFDSTADDR:
cmd = SIOCGIFDSTADDR;
break;
case OSIOCGIFBRDADDR:
cmd = SIOCGIFBRDADDR;
break;
case OSIOCGIFNETMASK:
cmd = SIOCGIFNETMASK;
#endif
}
error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
(struct mbuf *)cmd,(struct mbuf *)data,(struct mbuf *)ifp));
#ifdef OSIOCGIFADDR
switch (ocmd) {
case OSIOCGIFADDR:
case OSIOCGIFDSTADDR:
case OSIOCGIFBRDADDR:
case OSIOCGIFNETMASK:
((struct osockaddr *)&ifr->ifr_addr)->sa_family =
ifr->ifr_addr.sa_family;
}
#endif
return (error);
}
#endif
}
return (0);
}
/*
* Return interface configuration
* of system. List may be used
* in later ioctl's (above) to get
* other information.
*/
/*ARGSUSED*/
ifconf(cmd, data)
int cmd;
caddr_t data;
{
register struct ifconf *ifc = (struct ifconf *)data;
register struct ifnet *ifp = ifnet;
register struct ifaddr *ifa;
struct ifreq ifr, *ifrp;
int space = ifc->ifc_len, error = 0;
ifrp = ifc->ifc_req;
for (; space > sizeof (ifr) && ifp; ifp = ifp->if_next) {
sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
if ((ifa = ifp->if_addrlist) == 0) {
bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr));
if (error)
break;
space -= sizeof (ifr), ifrp++;
} else
for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) {
register struct sockaddr *sa = ifa->ifa_addr;
#if defined(COMPAT_43) && defined(OSIOCGIFCONF)
if (cmd == OSIOCGIFCONF || cmd == CSIOCGIFCONF) {
struct osockaddr *osa =
(struct osockaddr *)&ifr.ifr_addr;
ifr.ifr_addr = *sa;
osa->sa_family = sa->sa_family;
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
sizeof (ifr));
ifrp++;
} else
#endif
if (sa->sa_len <= sizeof(*sa)) {
ifr.ifr_addr = *sa;
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
sizeof (ifr));
ifrp++;
} else {
space -= sa->sa_len - sizeof(*sa);
if (space < sizeof (ifr))
break;
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
sizeof (ifr.ifr_name));
if (error == 0)
error = copyout((caddr_t)sa,
(caddr_t)&ifrp->ifr_addr, sa->sa_len);
ifrp = (struct ifreq *)
(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
}
if (error)
break;
space -= sizeof (ifr);
}
}
ifc->ifc_len -= space;
return (error);
}