Files
Arquivotheca.SunOS-4.1.4/sys/net/if.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

593 lines
13 KiB
C

/*
* 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 this notice is preserved and that due credit is given
* to the University of California at Berkeley. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
*
* @(#)if.c 1.1 94/10/31 SMI; from UCB 7.3 4/7/88
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/user.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/af.h>
#include <net/if_arp.h>
#ifdef OPENPROMS
#include <mon/obpdefs.h>
#else OPENPROMS
#include <mon/sunromvec.h>
#endif OPENPROMS
#include <sun/autoconf.h>
#include <sun/openprom.h>
#include "ether.h"
#ifdef OPENPROMS
#ifdef MULTIPROCESSOR
/*
* We can't risk user processes on other cpus bashing the output device
* while the boot prom is also bashing it, so go through the code
* that coordinates the output device.
*/
#define prom_printf mpprom_printf
#endif MULTIPROCESSOR
/*
* Undefine DPRINTF to remove compiled debug code.
* #define DPRINTF if (bootpath_debug) prom_printf
*/
#ifdef DPRINTF
static int bootpath_debug = 1;
#endif DPRINTF
#endif OPENPROMS
/*LINTLIBRARY*/
int ifqmaxlen = IFQ_MAXLEN;
/*
* Network interface utility routines.
*
* Routines with ifa_ifwith* names typically take sockaddr *'s as
* parameters.
*/
ifinit()
{
register struct ifnet *ifp;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (ifp->if_snd.ifq_maxlen == 0)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
if_slowtimo();
}
#ifdef vax
/*
* Call each interface on a Unibus reset.
*/
ifubareset(uban)
int uban;
{
register struct ifnet *ifp;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (ifp->if_reset)
(*ifp->if_reset)(ifp->if_unit, uban);
}
#endif
/*
* Attach an interface to the
* list of "active" interfaces.
*/
if_attach(ifp)
struct ifnet *ifp;
{
register struct ifnet **p = &ifnet;
while (*p)
p = &((*p)->if_next);
*p = ifp;
}
/*
* Locate an interface based on a complete address.
*/
/*ARGSUSED*/
struct ifaddr *
ifa_ifwithaddr(addr)
struct sockaddr *addr;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
#define equal(a1, a2) \
(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 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(&ifa->ifa_addr, addr))
return (ifa);
if ((ifp->if_flags & IFF_BROADCAST) &&
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)
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(&ifa->ifa_dstaddr, addr))
return (ifa);
}
return ((struct ifaddr *)0);
}
/*
* Find an interface on a specific network. If many, choice
* is first found.
*/
struct ifaddr *
ifa_ifwithnet(addr)
register struct sockaddr *addr;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
register u_int af = addr->sa_family;
register int (*netmatch)();
if (af >= AF_MAX)
return (0);
netmatch = afswitch[af].af_netmatch;
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 ((*netmatch)(&ifa->ifa_addr, addr))
return (ifa);
}
return ((struct ifaddr *)0);
}
/*
* Find an interface using a specific address family.
* First try for one with the UP bit set, then accept anyone.
*/
struct ifaddr *
ifa_ifwithaf(af)
register int af;
{
register struct ifnet *ifp;
register struct ifaddr *ifa;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (ifp->if_flags & IFF_UP)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr.sa_family == af)
return (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);
}
/*
* At this point, we have not initialised any interface (except
* loopback!), so we choose an arbitrary interface on which to
* try a revarp.
*/
/*ARGSUSED*/
struct ifnet *
ifb_ifwithaf(af)
int af;
{
#ifndef OPENPROMS
register struct bootparam *bp;
#endif
register struct ifnet *ifp;
#ifdef OPENPROMS
char name[OBP_MAXDEVNAME];
#else OPENPROMS
char name[32];
#endif OPENPROMS
short unit;
#ifdef DPRINTF
int i;
#endif DPRINTF
extern char *strcpy(), *strncpy();
struct ifnet *ifname();
#ifdef OPENPROMS
extern char *prom_bootpath(); /* For debug only! */
#ifdef DPRINTF
char *path;
path = prom_bootpath();
DPRINTF("ifb_ifwithaf: prom bootpath %s --> ", path ? path : "<NONE>");
#endif DPRINTF
name[0] = (char)0; /* In case of error getting name */
#ifdef DPRINTF
i = prom_get_boot_dev_name(name, sizeof name);
#else
(void) prom_get_boot_dev_name(name, sizeof name);
#endif
#ifdef DPRINTF
if (i == 0)
DPRINTF("device %s, ", name);
else
DPRINTF("device <NONE?>, ");
#endif DPRINTF
unit = (short)prom_get_boot_dev_unit();
#ifdef DPRINTF
DPRINTF("unit %d\n", (int)unit);
#endif DPRINTF
#else OPENPROMS
bp = *romp->v_bootparam;
name[0] = bp->bp_dev[0];
name[1] = bp->bp_dev[1];
name[2] = '\0';
unit = (short) bp->bp_ctlr;
#endif
/*
* Translate "gn" to "fddi"
* -- this hack for the Narya VME FDDI/DX interface.
*/
if (!strcmp(name, "gn"))
(void) strcpy(name, "fddi");
/*
* Look for matching name/unit in the ifnet linked list.
*/
if (ifp = ifname(name, unit))
return (ifp);
/*
* Otherwise, default to first found non-point-to-point,
* non-loopback interface.
*/
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (!(ifp->if_flags & IFF_POINTOPOINT) &&
!(ifp->if_flags & IFF_LOOPBACK))
return (ifp);
return (NULL);
}
/*
* Mark an interface down and notify protocols of
* the transition.
* NOTE: must be called at splnet or eqivalent.
*/
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);
}
/*
* Flush an interface queue.
*/
if_qflush(ifq)
register struct ifqueue *ifq;
{
register struct mbuf *m, *n;
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;
}
/*
* Handle interface watchdog timer routines. Called
* from softclock, we decrement timers (if set) and
* call the appropriate interface routine on expiration.
*/
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);
}
timeout(if_slowtimo, (caddr_t)0, hz / IFNET_SLOWHZ);
}
/*
* Map interface name to
* interface structure pointer.
*/
struct ifnet *
ifunit(name, size)
register char *name;
int size;
{
register char *cp;
register struct ifnet *ifp;
int unit;
int namlen = 0;
for (cp = name; cp < name + size && *cp; cp++) {
if (*cp >= '0' && *cp <= '9')
break;
namlen++;
}
if (*cp == '\0' || cp == name + size || cp == name)
return ((struct ifnet *)0);
unit = 0;
while (*cp && cp < name + size)
unit = 10*unit + (*cp++ - '0');
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (unit == ifp->if_unit && namlen == strlen(ifp->if_name) &&
bcmp(ifp->if_name, name, namlen) == 0)
return (ifp);
return ((struct ifnet *)0);
}
/*
* Map interface name and unit
* to interface structure pointer.
* Return NULL if not found.
*/
struct ifnet *
ifname(name, unit)
char *name;
short unit;
{
register struct ifnet *ifp;
for (ifp = ifnet; ifp; ifp = ifp->if_next)
if (!strcmp(ifp->if_name, name) && (ifp->if_unit == unit))
return (ifp);
return (NULL);
}
/*
* Interface ioctls.
*/
ifioctl(so, cmd, data)
struct socket *so;
int cmd;
caddr_t data;
{
register struct ifnet *ifp, *oifp;
register struct ifreq *ifr;
switch (cmd) {
case SIOCGIFCONF:
return (ifconf(cmd, data));
#if defined(INET) && NETHER > 0
case SIOCSARP:
case SIOCDARP:
if (!suser())
return (u.u_error);
/* FALL THROUGH */
case SIOCGARP:
return (arpioctl(cmd, data));
#endif
}
ifr = (struct ifreq *)data;
ifp = ifunit(ifr->ifr_name, IFNAMSIZ);
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 (!suser())
return (u.u_error);
if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
int s = splimp();
if_down(ifp);
(void) splx(s);
}
#ifdef VDDRV
/*
* For a loadable driver, we need to set ifqmaxlen here,
* since it wasn't done in ifinit.
*/
if (ifp->if_snd.ifq_maxlen == 0)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
#endif
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 (!suser())
return (u.u_error);
ifp->if_metric = ifr->ifr_metric;
break;
case SIOCUPPER:
oifp = ifunit(ifr->ifr_oname, IFNAMSIZ);
if (oifp == 0)
return (ENXIO);
if (oifp->if_input == 0)
return (EINVAL);
ifp->if_upper = oifp;
break;
case SIOCLOWER:
oifp = ifunit(ifr->ifr_oname, IFNAMSIZ);
if (oifp == 0)
return (ENXIO);
if (oifp->if_output == 0)
return (EINVAL);
ifp->if_lower = oifp;
break;
case SIOCSPROMISC:
if (!suser())
return (u.u_error);
return (ifpromisc(ifp, *(int *)data));
/*
* Set/zap multicast address. We demand root privilege,
* but otherwise leave it to the protocol to decide what
* to do with it. This strategy has the same coordination
* problems among multiple protocols that setting the
* individual address does...
*/
case SIOCADDMULTI:
case SIOCDELMULTI:
if (!suser())
return (u.u_error);
/* FALL THROUGH */
default:
if (so->so_proto == 0)
return (EOPNOTSUPP);
return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
cmd, data, ifp));
}
return (0);
}
/*
* Handle transitions into and out of promiscuous mode. Keep track
* of how many outstanding requests there are for the mode and call
* the interface to change mode on transitions to and from zero.
*/
int
ifpromisc(ifp, on)
register struct ifnet *ifp;
register int on;
{
register int change;
if (on)
change = ifp->if_promisc++ == 0;
else
change = (ifp->if_promisc != 0) ? (--ifp->if_promisc == 0) : 0;
if (!change)
return (0);
/*
* Force the IFF_PROMISC bit to match the
* value implied by the if_promisc field.
*/
if (on)
ifp->if_flags |= IFF_PROMISC;
else
ifp->if_flags &= ~IFF_PROMISC;
return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t) 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;
register char *cp, *ep;
struct ifreq ifr, *ifrp;
int space = ifc->ifc_len, error = 0;
ifrp = ifc->ifc_req;
ep = ifr.ifr_name + sizeof (ifr.ifr_name) - 4;
for (; space >= sizeof (ifr) && ifp; ifp = ifp->if_next) {
bcopy(ifp->if_name, ifr.ifr_name, sizeof (ifr.ifr_name) - 2);
for (cp = ifr.ifr_name; cp < ep && *cp; cp++)
;
if (ifp->if_unit > 99)
*cp++ = '0' + (ifp->if_unit / 100);
if (ifp->if_unit > 9)
*cp++ = '0' + (ifp->if_unit % 100)/10;
*cp++ = '0' + (ifp->if_unit % 10);
*cp = '\0';
if ((ifa = ifp->if_addrlist) == 0) {
bzero((caddr_t)&ifr.ifr_addr, sizeof (ifr.ifr_addr));
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
(u_int)(sizeof ifr));
if (error)
break;
space -= sizeof (ifr), ifrp++;
} else
for (; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) {
ifr.ifr_addr = ifa->ifa_addr;
error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
sizeof (ifr));
if (error)
break;
space -= sizeof (ifr), ifrp++;
}
}
ifc->ifc_len -= space;
return (error);
}