1
0
Files
irix-657m-src/irix/cmd/netman/lib/sn_local.c
2022-09-29 17:59:04 +03:00

538 lines
12 KiB
C

#ifdef __sgi
/*
* Copyright 1988 Silicon Graphics, Inc. All rights reserved.
*
* Local network interface snooper implementation.
*
* Changes History:
* Added tokenring.
*/
#include <bstring.h>
#include <ctype.h>
#include <errno.h>
#include <invent.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <net/route.h>
#include <sys/sysctl.h>
#include <net/if_types.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "exception.h"
#include "heap.h"
#include "macros.h"
#include "protocol.h"
#include "protoid.h"
#include "snooper.h"
DefineSnooperOperations(local_snops,lsn)
typedef struct {
char interface[RAW_IFNAMSIZ+1];
struct sfset sfset; /* snoopfilter set */
Snooper snooper; /* base class state */
} LocalSnooper;
#define LSN(sn) ((LocalSnooper *) (sn)->sn_private)
/*
* Use the kernel's hardware inventory to find an interface's raw protocol.
*/
struct typename {
char *ifnam;
int namlen;
int ctl; /* controllers for network types */
};
#define TN(name,ctl) { name, constrlen(name), ctl }
static struct typename controllers[] = {
TN("ec", INV_ETHER_EC),
TN("enp", INV_ETHER_ENP),
TN("et", INV_ETHER_ET),
TN("egl", INV_ETHER_EGL),
TN("fxp", INV_ETHER_FXP),
TN("cfe", INV_CRAYIOS_CFEI3),
TN("imf", INV_FDDI_IMF),
TN("ipg", INV_FDDI_IPG),
TN("xpi", INV_FDDI_XPI),
TN("rns", INV_FDDI_RNS),
TN("fv", INV_TOKEN_FV),
TN("et", INV_ETHER_EE),
TN("ef", INV_ETHER_EF),
TN("ep", INV_ETHER_EP), /* eplex */
TN("gtr", INV_TOKEN_GTR), /* token ring - gtr board */
TN("hip", INV_HIO_HIPPI),
TN("atm", INV_ATM_GIO64), /* ??? */
TN("atm", INV_ATM_QUADOC3), /* ??? */
0,
};
struct typeid {
int prid;
int inv_type;
int if_type;
};
#define TI(prid,inv_type, if_type) { prid, inv_type, if_type }
static struct typeid types[] = {
TI(PRID_ETHER, INV_NET_ETHER, IFT_ETHER),
TI(PRID_FDDI, INV_NET_FDDI, IFT_FDDI),
TI(PRID_CRAYIO, INV_NET_CRAYIOS, 0),
TI(PRID_TOKENRING, INV_NET_TOKEN, IFT_ISO88025),
TI(PRID_HIPPI, INV_NET_HIPPI, IFT_HIPPI),
TI(PRID_ATM, INV_NET_ATM, IFT_ATM),
TI(PRID_LOOP, -1, IFT_LOOP),
TI(PRID_IP, -1, IFT_SLIP),
TI(PRID_IP, -1, IFT_PTPSERIAL),
PRID_NULL,
};
/*
* Arguments to the hardware inventory raw protocol scanner.
*/
struct scanargs {
char *interface;
Protocol *rawproto;
};
/*
* This function is applied by scaninvent() to all of the hardware
* inventory items in the system.
*/
static int /* 1=stop scanning on this item */
rawprotoscan(inventory_t *ie, /* check this inventory item */
void *arg) /* for this interface name */
{
struct scanargs *sa;
struct typename *tn;
struct typeid *ti;
if (ie->inv_class != INV_NETWORK)
return 0;
/*
* Look for a hardware inventory controller type that
* we know about, and that has the same interface name
* as the interface that we are looking for.
*/
sa = arg;
for (tn = controllers; ; tn++) {
if (tn->ifnam == 0)
return 0;
if (tn->ctl == ie->inv_controller)
break;
}
/*
* We found this type of item in our tables.
* Is its name in our tables the same as the name of the interface
* we are looking for?
*/
if (strncmp(tn->ifnam, sa->interface, tn->namlen)
|| !isdigit(sa->interface[tn->namlen])) {
return 0;
}
/*
* Having found the right kind of controller with the right kind
* of name, map its network class type to a protocol ID.
* This cannot fail unless our static tables are broken.
*/
for (ti = types; ; ti++) {
if (ti->prid == PRID_NULL)
return 0;
if (ti->inv_type == ie->inv_type)
break;
}
sa->rawproto = findprotobyid(ti->prid);
return sa->rawproto != 0;
}
/*
* Given a network interface name, return a pointer to its raw protocol.
*/
Protocol *
getrawprotobyif(char *interface)
{
struct scanargs sa;
char *buf;
int mib[6];
size_t needed;
struct sockaddr_dl *sdl;
struct if_msghdr *ifm;
struct ifa_msghdr *ifam, *ifam_lim, *ifam2;
struct typeid *ti;
int serrno;
sa.interface = interface;
sa.rawproto = 0;
/*
* If the hardware inventory system recognizes the device
* name, then take its answer.
*/
if (scaninvent(rawprotoscan, &sa))
return sa.rawproto;
/*
* Ask the kernel about the device.
*/
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_IFLIST;
mib[5] = 0;
if (sysctl(mib, 6, 0,&needed, 0, 0) < 0) {
serrno = errno;
exc_raise(errno, "size probe sysctl() failed on %s",
interface);
errno = serrno;
return 0;
}
buf = (char *)malloc(needed);
if (buf == 0) {
exc_raise(0, "malloc() for sysctl() failed on %s",
interface);
return 0;
}
if (sysctl(mib, 6, buf,&needed, 0, 0) < 0) {
serrno = errno;
free(buf);
exc_raise(serrno, "sysctl() failed on %s", interface);
errno = serrno;
return 0;
}
ifam_lim = (struct ifa_msghdr *)(buf + needed);
for (ifam = (struct ifa_msghdr *)buf;
ifam < ifam_lim;
ifam = ifam2) {
ifam2 = (struct ifa_msghdr*)((char*)ifam
+ ifam->ifam_msglen);
if (ifam->ifam_type != RTM_IFINFO)
continue;
ifm = (struct if_msghdr *)ifam;
sdl = (struct sockaddr_dl *)(ifm + 1);
if (!strncmp(interface, sdl->sdl_data,sdl->sdl_nlen)) {
for (ti = types; ti->prid != PRID_NULL; ti++) {
if (ti->if_type == ifm->ifm_data.ifi_type) {
sa.rawproto=findprotobyid(ti->prid);
break;
}
}
break;
}
}
free(buf);
return sa.rawproto;
}
/*
* Approximate upper bound on socket buffer space reservation.
*/
#define MAXBUFSPACE 60000
Snooper *
localsnooper(char *interface, int port, int bufspace)
{
LocalSnooper *lsn;
int s, n;
struct sockaddr_raw sr;
/*
* Allocate a local snooper.
*/
lsn = new(LocalSnooper);
/*
* Open a snoop socket.
*/
s = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP);
if (s < 0)
goto sysfail;
/*
* Bind socket to interface.
*/
sr.sr_family = AF_RAW;
sr.sr_port = port;
if (interface == 0)
bzero(sr.sr_ifname, sizeof sr.sr_ifname);
else
(void) strncpy(sr.sr_ifname, interface, sizeof sr.sr_ifname);
if (bind(s, &sr, sizeof sr) < 0 && interface) {
int binderrno;
struct hostent *hp;
u_long addr;
char buf[8192];
struct ifconf ifc;
struct ifreq *ifr;
#define ifr_sin(ifr) ((struct sockaddr_in *) &(ifr)->ifr_addr)
/*
* Check whether interface is an IP hostname or number,
* which are often more convenient than an ifname.
*/
binderrno = errno;
hp = gethostbyname(interface);
if (hp && hp->h_addrtype == AF_INET)
bcopy(hp->h_addr, &addr, sizeof addr);
else
addr = inet_addr(interface);
if (addr == INADDR_NONE) {
errno = binderrno;
goto sysfail;
}
ifc.ifc_len = sizeof buf;
ifc.ifc_buf = buf;
if (ioctl(s, SIOCGIFCONF, (caddr_t) &ifc) < 0)
goto sysfail;
n = ifc.ifc_len / sizeof *ifr;
for (ifr = ifc.ifc_req; --n >= 0; ifr++) {
if (ifr->ifr_addr.sa_family == AF_INET
&& ifr_sin(ifr)->sin_addr.s_addr == addr) {
interface = ifr->ifr_name;
break;
}
}
(void) strncpy(sr.sr_ifname, interface, sizeof sr.sr_ifname);
if (bind(s, &sr, sizeof sr) < 0)
goto sysfail;
#undef ifr_sin
}
/*
* Get interface name if default.
*/
if (interface == 0) {
n = sizeof sr;
if (getsockname(s, &sr, &n) < 0)
goto sysfail;
interface = sr.sr_ifname;
}
(void) strncpy(lsn->interface, interface, RAW_IFNAMSIZ);
lsn->interface[RAW_IFNAMSIZ] = '\0';
/*
* Open socket receive buffer to a generous limit.
*/
if (bufspace > MAXBUFSPACE)
bufspace = MAXBUFSPACE;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t) &bufspace,
sizeof bufspace) < 0) {
goto sysfail;
}
/*
* Initialize common state. NB: sn_init calls exc_raise on error.
*/
if (!sn_init(&lsn->snooper, lsn, &local_snops, lsn->interface, s,
getrawprotobyif(lsn->interface))) {
n = lsn->snooper.sn_error;
goto fail;
}
sfs_init(&lsn->sfset);
return &lsn->snooper;
sysfail:
n = errno;
exc_raise((n == EADDRNOTAVAIL) ? 0 : n, "cannot snoop on %s",
interface ? interface : "default interface");
fail:
if (s >= 0)
(void) close(s);
delete(lsn);
errno = n;
return 0;
}
/*
* Add a filter for ex to this localsnooper.
*/
static int
lsn_add(Snooper *sn, struct expr **exp, struct exprerror *err)
{
struct expr *ex;
struct sfset tmp;
int i, n, tries;
SnoopFilter *sf;
ex = (exp == 0) ? 0 : *exp;
if (ex && sn->sn_rawproto == 0) {
sn->sn_error = EINVAL;
return 0;
}
/*
* Compile ex into a temporary filter set. If sfs_compile fails,
* sn->sn_error will be zero and err will be set.
*/
sfs_init(&tmp);
if (!sfs_compile(&tmp, ex, sn->sn_rawproto, sn->sn_rawhdrpad, err)) {
sn->sn_error = 0;
return 0;
}
/*
* Delete all kernel filters. The ioctl will fail with EACCES if
* passed the index of a filter not owned by sn.
*/
for (i = SNOOP_MAXFILTERS; --i >= 0; )
(void) lsn_ioctl(sn, SIOCDELSNOOP, &i);
sn->sn_error = 0;
/*
* Try to add all allocated filters in tmp. If we run out of room
* in the kernel's filter vector, unify tmp and retry once. If we
* can't add even one filter, fail with ENOMEM.
*/
tries = 0;
retry:
n = SNOOP_MAXFILTERS;
for (sf = &tmp.sfs_vec[0]; --n >= 0; sf++) {
if (sf->sf_allocated && !lsn_ioctl(sn, SIOCADDSNOOP, sf)) {
if (sn->sn_error != ENOMEM || ++tries == 2)
return 0;
sfs_unifyfilters(&tmp);
while (--sf >= &tmp.sfs_vec[0]) {
if (!sf->sf_allocated)
continue;
i = sf->sf_index;
(void) lsn_ioctl(sn, SIOCDELSNOOP, &i);
}
goto retry;
}
}
/*
* Snoop for error flags, if any.
* XXX No attempt is made to preserve flags set by an uncoordinated
* sn_seterrflags.
*/
i = tmp.sfs_errflags;
if (i != 0 && !lsn_ioctl(sn, SIOCERRSNOOP, &i))
return 0;
/*
* Now that all kernel filters have been set, update lsn->sfset.
*/
LSN(sn)->sfset = tmp;
return 1;
}
/*
* Delete the current filter from this localsnooper's interface.
*/
static int
lsn_delete(Snooper *sn)
{
LocalSnooper *lsn;
int n, i;
SnoopFilter *sf;
lsn = LSN(sn);
n = SNOOP_MAXFILTERS;
for (sf = &lsn->sfset.sfs_vec[0]; --n >= 0; sf++) {
if (!sfs_freefilter(&lsn->sfset, sf))
continue;
i = sf->sf_index;
if (!lsn_ioctl(sn, SIOCDELSNOOP, &i))
return 0;
}
if (lsn->sfset.sfs_errflags) {
lsn->sfset.sfs_errflags = 0;
if (!lsn_ioctl(sn, SIOCERRSNOOP, &lsn->sfset.sfs_errflags))
return 0;
}
return 1;
}
static int
lsn_read(Snooper *sn, SnoopPacket *sp, int len)
{
int cc;
if (len > sn->sn_packetsize)
len = sn->sn_packetsize;
cc = read(sn->sn_file, (char *)sp, sizeof sp->sp_hdr + len);
if (cc < 0) {
sn->sn_error = errno;
return cc;
}
cc -= sizeof sp->sp_hdr;
return (cc < 0) ? 0 : cc;
}
static int
lsn_write(Snooper *sn, SnoopPacket *sp, int len)
{
int cc;
cc = write(sn->sn_file, sp->sp_data + sn->sn_rawhdrpad, len);
if (cc < 0)
sn->sn_error = errno;
return cc;
}
static int
lsn_ioctl(Snooper *sn, int cmd, void *data)
{
if (ioctl(sn->sn_file, cmd, data) < 0) {
sn->sn_error = errno;
return 0;
}
return 1;
}
static int
lsn_getaddr(Snooper *sn, int cmd, struct sockaddr *sa)
{
LocalSnooper *lsn;
int sock, ok;
struct ifreq ifreq;
lsn = LSN(sn);
if (sa->sa_family == AF_RAW)
sock = sn->sn_file;
else {
sock = socket(sa->sa_family, SOCK_DGRAM, 0);
if (sock < 0) {
sn->sn_error = errno;
return 0;
}
}
(void) strncpy(ifreq.ifr_name, lsn->interface, sizeof ifreq.ifr_name);
if (ioctl(sock, cmd, (caddr_t) &ifreq) < 0) {
sn->sn_error = errno;
ok = 0;
} else {
*sa = ifreq.ifr_addr;
ok = 1;
}
if (sock != sn->sn_file)
(void) close(sock);
return ok;
}
static int
lsn_shutdown(Snooper *sn, enum snshutdownhow how)
{
if (shutdown(sn->sn_file, (int) how) < 0) {
sn->sn_error = errno;
return 0;
}
return 1;
}
static void
lsn_destroy(Snooper *sn)
{
(void) close(sn->sn_file);
delete(LSN(sn));
}
#endif