593 lines
12 KiB
C
593 lines
12 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)rpc.etherd.c 1.1 94/10/31 Copyr 1987 Sun Micro";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <rpc/rpc.h>
|
|
#include <netdb.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/file.h>
|
|
#include <sys/time.h>
|
|
#include <sys/errno.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/udp.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/udp_var.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <rpcsvc/ether.h>
|
|
|
|
#include <sys/stropts.h>
|
|
|
|
#include <net/nit_if.h>
|
|
#include <net/nit_buf.h>
|
|
|
|
#define NIT_DEV "/dev/nit"
|
|
|
|
union netheader {
|
|
struct udpiphdr nh_udpipheader;
|
|
struct ether_arp nh_arpheader;
|
|
#define nh_ipovlyheader nh_udpipheader.ui_i
|
|
#define nh_udpheader nh_udpipheader.ui_u
|
|
} netheader;
|
|
|
|
struct ether_header eheader;
|
|
int wirelen;
|
|
|
|
#define IP ((struct ip *)&netheader.nh_ipovlyheader)
|
|
#define ARP (&netheader.nh_arpheader)
|
|
#define TYPE (ntohs(eheader.ether_type))
|
|
|
|
#define BUFSPACE 4*8192
|
|
#define CHUNKSIZE 2048
|
|
#define SNAPLEN (sizeof(struct ether_header)+sizeof(union netheader))
|
|
|
|
int chunksize = CHUNKSIZE;
|
|
char buf[BUFSPACE];
|
|
struct etherhmem *srchtable[HASHSIZE];
|
|
struct etherhmem *dsthtable[HASHSIZE];
|
|
|
|
struct ether_addr baddr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
|
|
extern int errno;
|
|
|
|
int senddata();
|
|
|
|
int if_fd = -1;
|
|
char *device;
|
|
int net;
|
|
struct etherstat etherstat;
|
|
struct etheraddrs etheraddrs;
|
|
struct addrmask srcmask, dstmask, protomask, lnthmask;
|
|
|
|
main(argc, argv)
|
|
char **argv;
|
|
{
|
|
SVCXPRT *transp;
|
|
int readfds, s;
|
|
struct timeval timeout;
|
|
int readbit, rpcbit;
|
|
struct sockaddr_in addr;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: etherd interface\n");
|
|
exit(1);
|
|
}
|
|
device = argv[1];
|
|
initdevice();
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
transp = svcudp_create(s);
|
|
if (transp == NULL) {
|
|
fprintf(stderr, "couldn't create an RPC server\n");
|
|
exit(1);
|
|
}
|
|
pmap_unset(ETHERSTATPROG, ETHERSTATVERS);
|
|
if (!svc_register(transp, ETHERSTATPROG, ETHERSTATVERS,
|
|
senddata, IPPROTO_UDP)) {
|
|
fprintf(stderr, "couldn't register ETHERSTAT service\n");
|
|
exit(1);
|
|
}
|
|
/*
|
|
* what if this machine is a gateway?
|
|
*/
|
|
get_myaddress(&addr);
|
|
net = inet_netof(addr.sin_addr);
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 500000;
|
|
readbit = 1 << if_fd;
|
|
rpcbit = 1 << s;
|
|
|
|
/*
|
|
* should default be to init device?
|
|
*/
|
|
deinitdevice();
|
|
for (;;) {
|
|
if (if_fd < 0)
|
|
readfds = rpcbit;
|
|
else
|
|
readfds = rpcbit | readbit;
|
|
if (select(32, &readfds, 0, 0, 0) < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
perror("etherd: select");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
if (readfds & readbit)
|
|
readdata();
|
|
if (readfds & rpcbit)
|
|
svc_getreq(1 << s);
|
|
}
|
|
}
|
|
|
|
senddata(rqstp, transp)
|
|
struct svc_req *rqstp;
|
|
SVCXPRT *transp;
|
|
{
|
|
switch (rqstp->rq_proc) {
|
|
case NULLPROC:
|
|
if (!svc_sendreply(transp, xdr_void, 0)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_GETDATA:
|
|
/* if (!svc_getargs(transp, xdr_void, NULL)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
} */
|
|
gettimeofday(ðerstat.e_time, 0);
|
|
if (!svc_sendreply(transp, xdr_etherstat, ðerstat)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_GETSRCDATA:
|
|
/* if (!svc_getargs(transp, xdr_void, NULL)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
} */
|
|
gettimeofday(ðeraddrs.e_time, 0);
|
|
etheraddrs.e_addrs = srchtable;
|
|
if (!svc_sendreply(transp, xdr_etheraddrs,
|
|
ðeraddrs)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_GETDSTDATA:
|
|
/* if (!svc_getargs(transp, xdr_void, NULL)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
} */
|
|
gettimeofday(ðeraddrs.e_time, 0);
|
|
etheraddrs.e_addrs = dsthtable;
|
|
if (!svc_sendreply(transp, xdr_etheraddrs,
|
|
ðeraddrs)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_ON:
|
|
/* if (!svc_getargs(transp, xdr_void, NULL)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
} */
|
|
initdevice();
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_OFF:
|
|
/* if (!svc_getargs(transp, xdr_void, NULL)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
} */
|
|
deinitdevice();
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_SELECTSRC:
|
|
if (!svc_getargs(transp, xdr_addrmask, &srcmask)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_SELECTDST:
|
|
if (!svc_getargs(transp, xdr_addrmask, &dstmask)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_SELECTPROTO:
|
|
if (!svc_getargs(transp, xdr_addrmask, &protomask)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
case ETHERSTATPROC_SELECTLNTH:
|
|
if (!svc_getargs(transp, xdr_addrmask, &lnthmask)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL)) {
|
|
fprintf(stderr,"rpc.etherd:%s",
|
|
" couldn't reply to RPC call\n");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
return;
|
|
default:
|
|
svcerr_noproc(transp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
readdata()
|
|
{
|
|
register struct nit_bufhdr *hdrp;
|
|
register struct etherstat *p;
|
|
register struct etheraddrs *q;
|
|
caddr_t bp, bufstop;
|
|
char *packet;
|
|
char *edst, *esrc;
|
|
int cc;
|
|
|
|
p = ðerstat;
|
|
q = ðeraddrs;
|
|
|
|
if ((cc = read(if_fd, buf, sizeof buf)) < 0) {
|
|
perror("etherd: read");
|
|
deinitdevice();
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Process each packet in the chunk.
|
|
*/
|
|
for (bp = buf, bufstop = buf + cc; bp < bufstop;
|
|
bp += hdrp->nhb_totlen) {
|
|
/*
|
|
* Set up pointers to successive objects
|
|
* embedded in the current message.
|
|
*/
|
|
hdrp = (struct nit_bufhdr *)bp;
|
|
packet = (char *)(hdrp + 1);
|
|
bcopy(packet, (char *)&eheader, sizeof (eheader));
|
|
bcopy(packet + sizeof (eheader),
|
|
(char *)&netheader, sizeof (netheader));
|
|
wirelen = hdrp->nhb_msglen;
|
|
|
|
if (wirelen < MINPACKETLEN) {
|
|
edst = (char *)&eheader.ether_dhost;
|
|
esrc = (char *)&eheader.ether_shost;
|
|
fprintf(stderr,
|
|
"%s %d dst %.2x%.2x%.2x%.2x%.2x%.2x",
|
|
"rpc.etherd: bad lnth",
|
|
wirelen,
|
|
0xff & edst[0], 0xff & edst[1], 0xff & edst[2],
|
|
0xff & edst[3], 0xff & edst[4], 0xff & edst[5]);
|
|
fprintf(stderr,
|
|
"src %.2x%.2x%.2x%.2x%.2x%.2x\n",
|
|
0xff & esrc[0], 0xff & esrc[1], 0xff & esrc[2],
|
|
0xff & esrc[3], 0xff & esrc[4], 0xff & esrc[5]);
|
|
continue;
|
|
}
|
|
if (srcmask.a_mask && !srcmatch())
|
|
continue;
|
|
if (dstmask.a_mask && !dstmatch())
|
|
continue;
|
|
if (protomask.a_mask && !protomatch())
|
|
continue;
|
|
if (lnthmask.a_mask && !lnthmatch())
|
|
continue;
|
|
p->e_packets++;
|
|
q->e_packets++;
|
|
p->e_bytes += wirelen;
|
|
q->e_bytes += wirelen;
|
|
p->e_size[(wirelen - MINPACKETLEN)/BUCKETLNTH]++;
|
|
|
|
/*
|
|
* first compute dst, checking for broadcast specially
|
|
*/
|
|
if (bcmp((char *)&eheader.ether_dhost,
|
|
(char *) &baddr, sizeof (struct ether_addr)) == 0) {
|
|
p->e_bcast++;
|
|
q->e_bcast++;
|
|
}
|
|
else if (TYPE == ETHERTYPE_ARP) {
|
|
struct in_addr a;
|
|
|
|
bcopy((char *)ARP->arp_tpa,
|
|
(char *)&a, sizeof (struct in_addr));
|
|
adddst(a);
|
|
}
|
|
else if (TYPE == ETHERTYPE_IP)
|
|
adddst(IP->ip_dst);
|
|
|
|
if (TYPE == ETHERTYPE_ARP) {
|
|
struct in_addr a;
|
|
|
|
bcopy((char *)ARP->arp_spa,
|
|
(char *)&a, sizeof (struct in_addr));
|
|
addsrc(a);
|
|
p->e_proto[ARPPROTO]++;
|
|
}
|
|
else if (TYPE == ETHERTYPE_IP) {
|
|
addsrc(IP->ip_src);
|
|
switch(IP->ip_p) {
|
|
case IPPROTO_ND:
|
|
p->e_proto[NDPROTO]++;
|
|
break;
|
|
case IPPROTO_UDP:
|
|
p->e_proto[UDPPROTO]++;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
p->e_proto[TCPPROTO]++;
|
|
break;
|
|
case IPPROTO_ICMP:
|
|
p->e_proto[ICMPPROTO]++;
|
|
break;
|
|
default:
|
|
p->e_proto[OTHERPROTO]++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
p->e_proto[OTHERPROTO]++;
|
|
}
|
|
}
|
|
|
|
initdevice()
|
|
{
|
|
struct strioctl si;
|
|
struct ifreq ifr;
|
|
struct timeval timeout;
|
|
u_long flags;
|
|
|
|
if (if_fd >= 0)
|
|
return; /* already open */
|
|
|
|
if ((if_fd = open(NIT_DEV, O_RDONLY)) < 0) {
|
|
perror("nit open");
|
|
exit (1);
|
|
}
|
|
|
|
/*
|
|
* Arrange to get discrete messages from the stream.
|
|
*/
|
|
if (ioctl(if_fd, I_SRDOPT, (char *)RMSGD) < 0) {
|
|
perror("ioctl (I_SRDOPT)");
|
|
exit (1);
|
|
}
|
|
|
|
si.ic_timout = INFTIM;
|
|
|
|
/*
|
|
* Push and configure the buffering module.
|
|
*/
|
|
if (ioctl(if_fd, I_PUSH, "nbuf") < 0) {
|
|
perror("ioctl (I_PUSH \"nbuf\")");
|
|
exit (1);
|
|
}
|
|
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
si.ic_cmd = NIOCSTIME;
|
|
si.ic_len = sizeof timeout;
|
|
si.ic_dp = (char *)&timeout;
|
|
if (ioctl(if_fd, I_STR, (char *)&si) < 0) {
|
|
perror("ioctl (I_STR: NIOCSTIME)");
|
|
exit (1);
|
|
}
|
|
|
|
si.ic_cmd = NIOCSCHUNK;
|
|
si.ic_len = sizeof chunksize;
|
|
si.ic_dp = (char *)&chunksize;
|
|
if (ioctl(if_fd, I_STR, (char *)&si) < 0) {
|
|
perror("ioctl (I_STR: NIOCSCHUNK)");
|
|
exit (1);
|
|
}
|
|
|
|
/*
|
|
* Configure the nit device, binding it to the proper
|
|
* underlying interface and setting the interface into
|
|
* promiscuous mode if necessary.
|
|
*/
|
|
(void) strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
|
|
ifr.ifr_name[sizeof ifr.ifr_name - 1] = '\0';
|
|
si.ic_cmd = NIOCBIND;
|
|
si.ic_len = sizeof ifr;
|
|
si.ic_dp = (char *)𝔦
|
|
if (ioctl(if_fd, I_STR, (char *)&si) < 0) {
|
|
perror("ioctl (I_STR: NIOCBIND)");
|
|
exit(1);
|
|
}
|
|
|
|
flags = NI_PROMISC;
|
|
si.ic_cmd = NIOCSFLAGS;
|
|
si.ic_len = sizeof flags;
|
|
si.ic_dp = (char *)&flags;
|
|
if (ioctl(if_fd, I_STR, (char *)&si) < 0) {
|
|
perror("ioctl (I_STR: NIOCSFLAGS)");
|
|
exit (1);
|
|
}
|
|
|
|
/*
|
|
* Flush the read queue, to get rid of anything that
|
|
* accumulated before the device reached its final configuration.
|
|
*/
|
|
if (ioctl(if_fd, I_FLUSH, (char *)FLUSHR) < 0) {
|
|
perror("ioctl (I_FLUSH)");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
deinitdevice()
|
|
{
|
|
/* Already de-initialized? */
|
|
if (if_fd < 0)
|
|
return;
|
|
|
|
(void) close(if_fd);
|
|
if_fd = -1;
|
|
}
|
|
|
|
addsrc(addr)
|
|
struct in_addr addr;
|
|
{
|
|
int x;
|
|
struct etherhmem *p;
|
|
|
|
x = addr.s_addr & 0xff;
|
|
for (p = srchtable[x]; p != NULL; p = p->ht_nxt) {
|
|
if (p->ht_addr == addr.s_addr) {
|
|
p->ht_cnt++;
|
|
return;
|
|
}
|
|
}
|
|
p = (struct etherhmem *)malloc(sizeof(struct etherhmem));
|
|
p->ht_addr = addr.s_addr;
|
|
p->ht_cnt = 1;
|
|
p->ht_nxt = srchtable[x];
|
|
srchtable[x] = p;
|
|
}
|
|
|
|
adddst(addr)
|
|
struct in_addr addr;
|
|
{
|
|
int x;
|
|
struct etherhmem *p;
|
|
|
|
x = addr.s_addr & 0xff;
|
|
for (p = dsthtable[x]; p != NULL; p = p->ht_nxt) {
|
|
if (p->ht_addr == addr.s_addr) {
|
|
p->ht_cnt++;
|
|
return;
|
|
}
|
|
}
|
|
p = (struct etherhmem *)malloc(sizeof(struct etherhmem));
|
|
p->ht_addr = addr.s_addr;
|
|
p->ht_cnt = 1;
|
|
p->ht_nxt = dsthtable[x];
|
|
dsthtable[x] = p;
|
|
}
|
|
|
|
dstmatch()
|
|
{
|
|
int addr;
|
|
|
|
switch (TYPE) {
|
|
case ETHERTYPE_IP:
|
|
addr = IP->ip_dst.s_addr;
|
|
break;
|
|
case ETHERTYPE_ARP:
|
|
bcopy(ARP->arp_tpa, (caddr_t)&addr, sizeof (addr));
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
return ((addr & dstmask.a_mask)==(dstmask.a_addr & dstmask.a_mask));
|
|
}
|
|
|
|
srcmatch()
|
|
{
|
|
int addr;
|
|
|
|
switch (TYPE) {
|
|
case ETHERTYPE_IP:
|
|
addr = IP->ip_src.s_addr;
|
|
break;
|
|
case ETHERTYPE_ARP:
|
|
bcopy(ARP->arp_spa, (caddr_t)&addr, sizeof (addr));
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
return ((addr & srcmask.a_mask) == (srcmask.a_addr & srcmask.a_mask));
|
|
}
|
|
|
|
protomatch()
|
|
{
|
|
int proto;
|
|
|
|
switch (TYPE) {
|
|
case ETHERTYPE_IP:
|
|
proto = IP->ip_p;
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
return ((proto & protomask.a_mask) ==
|
|
(protomask.a_addr & protomask.a_mask));
|
|
}
|
|
|
|
/*
|
|
* for size, a_addr is lowvalue, a_mask is high value
|
|
*/
|
|
lnthmatch()
|
|
{
|
|
return (wirelen >= lnthmask.a_addr && wirelen <= lnthmask.a_mask);
|
|
}
|
|
|
|
/*
|
|
* for debugging
|
|
*/
|
|
#if 0
|
|
printdata()
|
|
{
|
|
int i;
|
|
struct etherhmem *p;
|
|
|
|
for (i = 0; i < HASHSIZE; i++) {
|
|
for (p = srchtable[i]; p != NULL; p = p->ht_nxt)
|
|
printf("%x %d\n", p->ht_addr, p->ht_cnt);
|
|
}
|
|
}
|
|
#endif
|