mirror of
https://github.com/PDP-10/klh10.git
synced 2026-04-08 22:21:19 +00:00
2860 lines
80 KiB
C
2860 lines
80 KiB
C
/* OSDNET.C - OS Dependent Network facilities
|
||
*/
|
||
/* $Id: osdnet.c,v 2.8 2003/02/23 18:22:08 klh Exp $
|
||
*/
|
||
/* Copyright © 1999, 2001 Kenneth L. Harrenstien
|
||
** All Rights Reserved
|
||
**
|
||
** This file is part of the KLH10 Distribution. Use, modification, and
|
||
** re-distribution is permitted subject to the terms in the file
|
||
** named "LICENSE", which contains the full text of the legal notices
|
||
** and should always accompany this Distribution.
|
||
**
|
||
** This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
|
||
**
|
||
** This notice (including the copyright and warranty disclaimer)
|
||
** must be included in all copies or derivations of this software.
|
||
*/
|
||
/*
|
||
* $Log: osdnet.c,v $
|
||
* Revision 2.8 2003/02/23 18:22:08 klh
|
||
* Fix various problems for NetBSD/Alpha 1.6.
|
||
*
|
||
* Revision 2.7 2002/03/18 04:25:03 klh
|
||
* Add support for promiscuous mode, fix minor ARP error output
|
||
*
|
||
* Revision 2.6 2001/11/19 10:32:34 klh
|
||
* Solaris port fixups.
|
||
*
|
||
* Revision 2.5 2001/11/10 21:28:59 klh
|
||
* Final 2.0 distribution checkin
|
||
*
|
||
*/
|
||
|
||
/* This file, like DPSUP.C, is intended to be included directly
|
||
into source code rather than compiled separately and then linked
|
||
together. The reason for this is that the actual configuration
|
||
of code will vary depending on its intended use, so a single .o
|
||
file cannot satisfy all programs.
|
||
*/
|
||
|
||
#include <unistd.h> /* For basic Unix syscalls */
|
||
|
||
/* The possible configuration macro definitions, with defaults:
|
||
*/
|
||
#ifndef NETIFC_MAX
|
||
# define NETIFC_MAX 20
|
||
#endif
|
||
|
||
#ifndef OSDNET_INCLUDED
|
||
# include "osdnet.h" /* Insurance to make sure our defs are there */
|
||
#endif
|
||
|
||
#ifdef RCSID
|
||
RCSID(osdnet_c,"$Id: osdnet.c,v 2.8 2003/02/23 18:22:08 klh Exp $")
|
||
#endif
|
||
|
||
/* Local predeclarations */
|
||
|
||
struct ifent *osn_iflookup(char *ifnam);
|
||
int osn_ifealookup(char *ifnam, unsigned char *eap);
|
||
|
||
/* Get a socket descriptor suitable for general net interface
|
||
examination and manipulation; this is not necessarily suitable for
|
||
use as a packetfilter.
|
||
This may only make sense on Unix.
|
||
*/
|
||
int
|
||
osn_ifsock(char *ifnam, ossock_t *as)
|
||
{
|
||
#if (KLH10_NET_NIT || KLH10_NET_DLPI || KLH10_NET_BPF || KLH10_NET_PFLT || \
|
||
KLH10_NET_TUN || KLH10_NET_LNX || KLH10_NET_TAP_BRIDGE)
|
||
return ((*as = socket(AF_INET, SOCK_DGRAM, 0)) >= 0);
|
||
#else
|
||
# error OSD implementation needed for osn_ifsock
|
||
#endif
|
||
}
|
||
|
||
int
|
||
osn_ifclose(ossock_t s)
|
||
{
|
||
#if (KLH10_NET_NIT || KLH10_NET_DLPI || KLH10_NET_BPF || KLH10_NET_PFLT || \
|
||
KLH10_NET_TUN || KLH10_NET_LNX || KLH10_NET_TAP_BRIDGE)
|
||
return (close(s) >= 0);
|
||
#else
|
||
# error OSD implementation needed for osn_ifclose
|
||
#endif
|
||
}
|
||
|
||
|
||
/* Minor utilities */
|
||
|
||
char *
|
||
eth_adrsprint(char *cp, unsigned char *ea)
|
||
{
|
||
sprintf(cp, "%x:%x:%x:%x:%x:%x", ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
|
||
return cp;
|
||
}
|
||
|
||
char *
|
||
ip_adrsprint(char *cp, unsigned char *ia)
|
||
{
|
||
sprintf(cp, "%d.%d.%d.%d", ia[0], ia[1], ia[2], ia[3]);
|
||
return cp;
|
||
}
|
||
|
||
|
||
/* Interface Table initialization
|
||
** Gets info about all net interfaces known to the native system.
|
||
** Used for several purposes, hence fairly general.
|
||
**
|
||
*/
|
||
/*
|
||
SIOCGIFCONF documentation is virtually non-existent. The following
|
||
info comes from looking at source code.
|
||
struct ifconf ifconf;
|
||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||
ioctl(s, SIOCGIFCONF, &ifconf);
|
||
|
||
ifconf has only two elements:
|
||
<sys/if.h>: struct ifconf
|
||
caddr_t ifcu_buf; Buffer to use
|
||
int ifc_len; Buffer size in bytes (or # bytes used)
|
||
|
||
On return, the buffer holds a series of "struct ifreq" entries, each of
|
||
which holds an interface name and address. On older systems, each
|
||
such entry was a fixed size, but this was later changed in order to
|
||
accomodate longer addresses and consequently,
|
||
=====>> EACH ENTRY MAY BE OF VARYING SIZE!!!! <<======
|
||
|
||
This, by the way, is why SIOCGIFCONF appeared to be broken on OSF/1 when
|
||
used with a single ifreq's worth of buffer -- since it needed a larger
|
||
"ifreq" than the buffer had room for, it returned nothing.
|
||
|
||
Although the second element of "struct ifreq" is a union, SIOCGIFCONF always
|
||
uses it to return a sockaddr:
|
||
<sys/if.h>: struct ifreq
|
||
char ifr_name[16] interface name, eg "de0"
|
||
struct sockaddr ifr_addr interface address
|
||
<sys/socket.h>: struct sockaddr
|
||
unsigned char sa_len # bytes in this sockaddr
|
||
unsigned char sa_family address type, AF_xxx
|
||
char sa_data[14] actually up to 253 bytes!
|
||
|
||
Note that the sa_len field is new; older versions of sockaddr had
|
||
just sa_family and sa_data.
|
||
|
||
To repeat, the actual size of an ifreq depends on the size of the
|
||
sockaddr it contains. This is ALWAYS at least sizeof(struct sockaddr),
|
||
so trickiness is only needed when sa_len is greater than this default
|
||
size.
|
||
|
||
#define _SIZEOF_ADDR_IFREQ(p) \
|
||
((p)->ifr_addr.sa_len <= sizeof(struct sockaddr) \
|
||
? sizeof(struct ifreq) \
|
||
: (sizeof(struct ifreq)-sizeof(struct sockaddr)+(p)->ifr_addr.sa_len))
|
||
|
||
or alternatively:
|
||
|
||
#define _SIZEOF_ADDR_IFREQ(ifr) \
|
||
((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) \
|
||
? (sizeof(struct ifreq) - sizeof(struct sockaddr) + (ifr).ifr_addr.sa_len) \
|
||
: sizeof(struct ifreq))
|
||
|
||
This has been made trickier by NetBSD 5.0, which doesn't put a sockaddr
|
||
but a (union with as largest member a) sockaddr_storage in the ifreq.
|
||
Now the size is always the same again, but not sizeof(struct sockaddr).
|
||
This can (probably) be recognised by the existence of
|
||
#define ifr_space ifr_ifru.ifru_space / * sockaddr_storage * /
|
||
|
||
*/
|
||
/*
|
||
Note that searching for AF_INET or IP addresses only finds interfaces that
|
||
are presently configured with IP addresses by "ifconfig", typically those
|
||
that are also up. An interface that is dedicated to the emulator will
|
||
normally be both "down" and have no IP address bound to it.
|
||
|
||
Another way to look at this is that until an interface is configured
|
||
up, it has no IP address bound to it. This is the same reason that
|
||
SIOCGIFADDR cannot be used to find the IP address of a dedicated
|
||
interface; there is none.
|
||
*/
|
||
|
||
/* Complete table as returned from system by SIOCGIFCONF
|
||
* Currently static for debugging, could be dynamic.
|
||
*/
|
||
static struct ifconf ifctab;
|
||
static char ifcbuf[sizeof(struct ifreq) * NETIFC_MAX];
|
||
|
||
/* Our own internal table of interface entries, filtered
|
||
to keep only the ones we're interested in.
|
||
*/
|
||
static int iftab_initf = 0;
|
||
static int iftab_nifs = 0;
|
||
static struct ifent iftab[NETIFC_MAX];
|
||
|
||
static void osn_iftab_pass(int opts, int npass, int s, struct ifconf *ifc);
|
||
|
||
/* Get table of all interfaces, using our own generic entry format.
|
||
* Routine works as follows:
|
||
*
|
||
* First, grabs a IFCONF table of all interfaces from the kernel.
|
||
* Each IFR entry contains a name and an address.
|
||
*
|
||
* Second, scan this IFCONF table (pass 1) to build our own table of IFE
|
||
* entries, made of all IFR entries that pass the following filter
|
||
* flags:
|
||
* IFTAB_IPS - if set, accepts AF_INET interfaces (with IP address)
|
||
* IFTAB_ETHS - if set, accepts LINK interfaces with Ether-like addrs.
|
||
* IFTAB_OTH - if set, accepts all others (with unknown addr)
|
||
*
|
||
* Third, make another scan of IFCONF (pass 2) that grabs *all* information
|
||
* about any interface present in our IFE table, regardless of what caused
|
||
* it to be inserted.
|
||
*/
|
||
int
|
||
osn_iftab_init(int opts)
|
||
{
|
||
int s;
|
||
struct ifconf *ifc = &ifctab;
|
||
register struct ifent *ifet;
|
||
|
||
/* Start out with empty table */
|
||
iftab_nifs = 0;
|
||
ifet = &iftab[0];
|
||
|
||
/* Open socket with AF_INET family to get at IP stuff.
|
||
*/
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
esfatal(1, "osn_iftab_init socket()");
|
||
}
|
||
|
||
/* Gobble table of config info about all interfaces, including name
|
||
and IP address.
|
||
*/
|
||
ifc->ifc_len = sizeof(ifcbuf);
|
||
ifc->ifc_buf = (caddr_t)ifcbuf;
|
||
if (ioctl(s, SIOCGIFCONF, (char *)ifc) < 0) {
|
||
close(s);
|
||
esfatal(1, "osn_iftab_init SIOCGIFCONF");
|
||
}
|
||
if (ifc->ifc_len < sizeof(struct ifreq)) {
|
||
if (DP_DBGFLG)
|
||
dbprintln("SIOCGIFCONF only got %d bytes (need %d)",
|
||
(int)ifc->ifc_len, (int)sizeof(struct ifreq));
|
||
close(s);
|
||
return 0; /* Assume no interfaces */
|
||
}
|
||
if (DP_DBGFLG)
|
||
dbprintln("SIOCGIFCONF returned %d bytes (%d ifcs?)",
|
||
(int)ifc->ifc_len, (int)(ifc->ifc_len/sizeof(struct ifreq)));
|
||
|
||
if (DP_DBGFLG)
|
||
osn_ifctab_show(stdout, ifc);
|
||
|
||
/* Grabbed everything from OS, now grovel through it */
|
||
osn_iftab_pass(opts, 1, s, ifc); /* Do pass 1 scan */
|
||
osn_iftab_pass(opts, 2, s, ifc); /* Do pass 2 scan */
|
||
iftab_initf = opts; /* Inited! */
|
||
|
||
close(s);
|
||
if (DP_DBGFLG)
|
||
osn_iftab_show(stdout, &iftab[0], iftab_nifs);
|
||
return iftab_nifs;
|
||
}
|
||
|
||
int
|
||
osn_nifents(void)
|
||
{
|
||
return iftab_nifs;
|
||
}
|
||
|
||
static void
|
||
osn_iftab_pass(int opts, int npass, int s, struct ifconf *ifc)
|
||
{
|
||
register int i;
|
||
int offset;
|
||
struct ifreq ifr;
|
||
register struct ifreq *ifp, *ifend, *ifnext;
|
||
register struct ifent *ife, *ifet;
|
||
|
||
/* Start out with empty table */
|
||
if (npass == 1)
|
||
iftab_nifs = 0;
|
||
|
||
ifet = &iftab[0];
|
||
|
||
ifp = ifc->ifc_req;
|
||
ifend = (struct ifreq *)((char *)ifp + ifc->ifc_len);
|
||
for (; ifp < ifend; ifp = ifnext) {
|
||
|
||
/* Find next pointer now, to simplify structure of code.
|
||
This is complicated by the fact that the returned table format
|
||
has changed under different OS versions. The "new regime"
|
||
uses a variable-size "ifreq" entry! Choke...
|
||
*/
|
||
ifnext = ifp + 1; /* Assume normal entry at first */
|
||
#if NETIF_HAS_SALEN && !defined(ifr_space)
|
||
if (ifp->ifr_addr.sa_len > sizeof(struct sockaddr)) {
|
||
offset = ifp->ifr_addr.sa_len - sizeof(struct sockaddr);
|
||
ifnext = (struct ifreq *)((char *)ifnext + offset);
|
||
}
|
||
#endif
|
||
/* See whether this entry is already in table.
|
||
*/
|
||
for (i = 0; i < iftab_nifs; ++i) {
|
||
if (strncmp(ifp->ifr_name, ifet[i].ife_name, sizeof(ifp->ifr_name))
|
||
== 0) {
|
||
/* Found existing entry! Use its pointer */
|
||
ife = &ifet[i];
|
||
break;
|
||
}
|
||
}
|
||
if (i >= iftab_nifs) {
|
||
if (npass == 2)
|
||
continue;
|
||
/* Pass1, decide now whether to create entry or not */
|
||
/* See if it's something we're interested in. */
|
||
if ((opts & IFTAB_IPS) && (ifp->ifr_addr.sa_family == AF_INET)) {
|
||
/* yes */
|
||
#if NETIF_HAS_ETHLINK
|
||
} else if ((opts & IFTAB_ETHS)
|
||
&& (ifp->ifr_addr.sa_family == AF_LINK)
|
||
&& (((struct sockaddr_dl *)&ifp->ifr_addr)->sdl_alen
|
||
== ETHER_ADRSIZ)){
|
||
/* yes */
|
||
#endif
|
||
} else if (opts & IFTAB_OTH) {
|
||
/* yes */
|
||
} else {
|
||
/* Nope, ignore this one */
|
||
continue;
|
||
}
|
||
|
||
/* Yes, create this entry */
|
||
if (iftab_nifs >= NETIFC_MAX-1) {
|
||
error("ifc table overflow, max %d", NETIFC_MAX);
|
||
return; /* Stop now */
|
||
}
|
||
ife = &ifet[iftab_nifs++]; /* New table entry to fill out */
|
||
|
||
/* Copy interface name and ensure null-terminated
|
||
even if sizes someday get out of synch.
|
||
*/
|
||
strncpy(ife->ife_name, ifp->ifr_name,
|
||
(sizeof(ife->ife_name) < sizeof(ifp->ifr_name))
|
||
? sizeof(ife->ife_name) : sizeof(ifp->ifr_name));
|
||
ife->ife_name[sizeof(ife->ife_name)-1] = '\0';
|
||
|
||
/* These ioctls should work for all interfaces */
|
||
strncpy(ifr.ifr_name, ifp->ifr_name, sizeof(ifr.ifr_name));
|
||
if (ioctl(s, SIOCGIFFLAGS, (char *)&ifr) < 0) {
|
||
syserr(errno, "SIOCGIFFLAGS for \"%s\"", ife->ife_name);
|
||
continue; /* Discard entry */
|
||
}
|
||
ife->ife_flags = ifr.ifr_flags;
|
||
|
||
/* SIOCGIFMTU: ifr_mtu */
|
||
/* SIOCGIFMETRIC: ifr_metric */
|
||
continue;
|
||
}
|
||
if (npass == 1)
|
||
continue;
|
||
|
||
/* Pass 2 and dealing with an existing entry. Flesh it out! */
|
||
switch (ifp->ifr_addr.sa_family) {
|
||
case AF_INET:
|
||
ife->ife_pinet = ifp; /* Remember pointer */
|
||
ife->ife_ipint = ((struct sockaddr_in *)
|
||
&ifp->ifr_addr)->sin_addr.s_addr;
|
||
break;
|
||
#if NETIF_HAS_ETHLINK
|
||
case AF_LINK:
|
||
if (((struct sockaddr_dl *)&ifp->ifr_addr)->sdl_alen
|
||
== ETHER_ADRSIZ) {
|
||
/* Looks like Ethernet physical link, save addr */
|
||
struct sockaddr_dl *dla = (struct sockaddr_dl *)&ifp->ifr_addr;
|
||
memcpy(ife->ife_ea, LLADDR(dla), dla->sdl_alen);
|
||
ife->ife_gotea = TRUE;
|
||
ife->ife_plink = ifp;
|
||
break;
|
||
}
|
||
#endif
|
||
/* Else drop through to "other" case */
|
||
default:
|
||
ife->ife_pother = ifp;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#include <stddef.h>
|
||
|
||
void
|
||
osn_ifctab_show(FILE *f, struct ifconf *ifc)
|
||
{
|
||
register struct ifreq *ifr;
|
||
struct ifreq *ifrend;
|
||
int len;
|
||
int i;
|
||
unsigned char *cp;
|
||
int nents = 0;
|
||
int nvary = 0;
|
||
|
||
fprintf(f, "sizeof struct ifreq = %d\r\n", (int) sizeof(struct ifreq));
|
||
fprintf(f, "IFNAMSIZ = %d\r\n", (int) IFNAMSIZ);
|
||
#if CENV_SYS_NETBSD
|
||
fprintf(f, "offset of struct sockaddr_storage = %d\r\n", (int) offsetof(struct ifreq, ifr_space));
|
||
#endif
|
||
fprintf(f, "sizeof struct sockaddr = %d\r\n", (int) sizeof(struct sockaddr));
|
||
fprintf(f, "sizeof struct sockaddr_storage = %d\r\n", (int) sizeof(struct sockaddr_storage));
|
||
fprintf(f, "sizeof union ifr_ifru = %d\r\n", (int) sizeof(ifr->ifr_ifru));
|
||
|
||
fprintf(f, "Interface table: %ld bytes (%d entries if std addr len %d)\r\n",
|
||
(long)ifc->ifc_len, (int)(ifc->ifc_len/sizeof(struct ifreq)),
|
||
(int)sizeof(struct sockaddr));
|
||
|
||
ifr = ifc->ifc_req;
|
||
ifrend = (struct ifreq *)((char *)(ifc->ifc_req) + ifc->ifc_len);
|
||
for (i = 0; ifr < ifrend; ++i) {
|
||
++nents;
|
||
|
||
#if NETIF_HAS_SALEN
|
||
len = ifr->ifr_addr.sa_len;
|
||
#else
|
||
len = sizeof(struct sockaddr);
|
||
#endif
|
||
|
||
fprintf(f, "offset: %d\r\n", (int)((char *)ifr - (char *)ifc->ifc_req));
|
||
/* Output entry data */
|
||
fprintf(f, "%2d: \"%.*s\" sockaddr.sa_family %d, .sa_len %d",
|
||
i, (int)sizeof(ifr->ifr_name), ifr->ifr_name,
|
||
ifr->ifr_addr.sa_family, len);
|
||
if (len) {
|
||
cp = (unsigned char *) ifr->ifr_addr.sa_data;
|
||
fprintf(f, " = (sockaddr.sa_data) %x", *cp);
|
||
for (--len; len > 0; --len) {
|
||
fprintf(f, ":%x", *++cp);
|
||
}
|
||
}
|
||
fprintf(f, "\r\n");
|
||
|
||
cp = (unsigned char *) ifr->ifr_addr.sa_data;
|
||
switch (ifr->ifr_addr.sa_family) {
|
||
case AF_INET:
|
||
{
|
||
struct sockaddr_in *skin = (struct sockaddr_in *) &ifr->ifr_addr;
|
||
struct in_addr *in = &skin->sin_addr;
|
||
unsigned char *ucp = (unsigned char *) &in->s_addr;
|
||
|
||
fprintf(f, " AF_INET = port %d, IP %d.%d.%d.%d\r\n",
|
||
(int)skin->sin_port,
|
||
ucp[0], ucp[1], ucp[2], ucp[3]);
|
||
}
|
||
break;
|
||
|
||
#if NETIF_HAS_ETHLINK
|
||
case AF_LINK:
|
||
{
|
||
struct sockaddr_dl *dla = (struct sockaddr_dl *) &ifr->ifr_addr;
|
||
fprintf(f, " AF_LINK = type %d, sdl_alen %d",
|
||
dla->sdl_type, dla->sdl_alen);
|
||
if (len = dla->sdl_alen) {
|
||
cp = (unsigned char *) LLADDR(dla);
|
||
fprintf(f, " = %x", *cp);
|
||
for (--len; len > 0; --len) {
|
||
fprintf(f, ":%x", *++cp);
|
||
}
|
||
}
|
||
fprintf(f, "\r\n");
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
#if defined(AF_INET6)
|
||
case AF_INET6:
|
||
fprintf(f, " AF_INET6 (No handler for this)\r\n");
|
||
break;
|
||
#endif
|
||
default:
|
||
fprintf(f, " No handler for this family\r\n");
|
||
}
|
||
|
||
|
||
/* Move onto next entry */
|
||
#if NETIF_HAS_SALEN && !defined(ifr_space)
|
||
if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)) {
|
||
++nvary;
|
||
ifr = (struct ifreq *)((char *)(ifr + 1) +
|
||
(ifr->ifr_addr.sa_len - sizeof(struct sockaddr)));
|
||
} else
|
||
#endif
|
||
ifr++;
|
||
}
|
||
if (nvary)
|
||
fprintf(f, "Interface summary: %d entries of varying length\r\n",
|
||
nents);
|
||
else
|
||
fprintf(f, "Interface summary: %d entries of std length %d\r\n",
|
||
nents, (int)sizeof(struct ifreq));
|
||
}
|
||
|
||
|
||
void
|
||
osn_iftab_show(FILE *f, struct ifent *ifents, int nents)
|
||
{
|
||
register struct ifent *ife;
|
||
int i;
|
||
|
||
fprintf(f, "Filtered IFE table: %d entries\r\n", nents);
|
||
|
||
for (i = 0, ife = ifents; i < nents; ++i, ++ife) {
|
||
fprintf(f, "%2d: \"%s\"", i, ife->ife_name);
|
||
if (ife->ife_pinet) {
|
||
unsigned char *ucp = ife->ife_ipchr;
|
||
fprintf(f, " (IP %d.%d.%d.%d)",
|
||
ucp[0], ucp[1], ucp[2], ucp[3]);
|
||
}
|
||
if (ife->ife_plink || ife->ife_gotea) {
|
||
unsigned char *ucp = ife->ife_ea;
|
||
fprintf(f, " (%sEther %x:%x:%x:%x:%x:%x)",
|
||
(ife->ife_plink ? "" : "Extracted "),
|
||
ucp[0], ucp[1], ucp[2], ucp[3], ucp[4], ucp[5]);
|
||
}
|
||
if (ife->ife_pother) {
|
||
fprintf(f, " (Other: fam %d)",
|
||
ife->ife_pother->ifr_addr.sa_family);
|
||
}
|
||
fprintf(f, "\r\n");
|
||
}
|
||
}
|
||
|
||
/* OSN_IFTAB_ARP - Look up an entry ARP-style (given IP address)
|
||
*/
|
||
struct ifent *
|
||
osn_iftab_arp(struct in_addr ia)
|
||
{
|
||
register int i = 0;
|
||
register struct ifent *ife = iftab;
|
||
|
||
for (; i < iftab_nifs; ++i, ++ife)
|
||
if (ife->ife_ipia.s_addr == ia.s_addr)
|
||
return ife;
|
||
return NULL;
|
||
}
|
||
|
||
/* OSN_IFTAB_ARPGET - As above, but returns EA if found.
|
||
*/
|
||
int
|
||
osn_iftab_arpget(struct in_addr ia, unsigned char *eap)
|
||
{
|
||
register struct ifent *ife;
|
||
|
||
if ((ife = osn_iftab_arp(ia)) && ife->ife_gotea) {
|
||
ea_set(eap, ife->ife_ea);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
/* OSN_IPDEFAULT - Find a default IP interface entry; take the first one
|
||
* that's up and isn't a loopback.
|
||
*/
|
||
struct ifent *
|
||
osn_ipdefault(void)
|
||
{
|
||
register int i = 0;
|
||
register struct ifent *ife = iftab;
|
||
|
||
for (; i < iftab_nifs; ++i, ++ife)
|
||
if ((ife->ife_flags & IFF_UP) && !(ife->ife_flags & IFF_LOOPBACK))
|
||
return ife;
|
||
return NULL;
|
||
}
|
||
|
||
/* OSN_IFLOOKUP - Find interface entry in our table; NULL if none.
|
||
*/
|
||
struct ifent *
|
||
osn_iflookup(char *ifnam)
|
||
{
|
||
register int i = 0;
|
||
register struct ifent *ife = iftab;
|
||
|
||
for (; i < iftab_nifs; ++i, ++ife)
|
||
if (strcmp(ifnam, ife->ife_name) == 0)
|
||
return ife;
|
||
return NULL;
|
||
}
|
||
|
||
/* OSN_IFEALOOKUP - Find ethernet address, barf if neither in our table nor
|
||
* available via OS.
|
||
*/
|
||
int
|
||
osn_ifealookup(char *ifnam, /* Interface name */
|
||
unsigned char *eap) /* Where to write ether address */
|
||
{
|
||
#if NETIF_HAS_ETHLINK
|
||
register struct ifent *ife;
|
||
|
||
if ((ife = osn_iflookup(ifnam))
|
||
&& (ife->ife_plink || ife->ife_gotea)) {
|
||
ea_set(eap, ife->ife_ea);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
#else
|
||
return osn_ifeaget(-1, ifnam, eap, (unsigned char *)NULL);
|
||
#endif
|
||
}
|
||
|
||
/* OSN_ARP_STUFF - stuff emulated-host ARP entry into kernel.
|
||
** Note it isn't necessary to specify an interface!
|
||
** Also, the code assumes that if an ARP entry already exists in the
|
||
** kernel for the given IP address, it will be reset to this new
|
||
** setting rather than (eg) failing.
|
||
*/
|
||
int
|
||
osn_arp_stuff(unsigned char *ipa, unsigned char *eap, int pubf)
|
||
{
|
||
char ipbuf[OSN_IPSTRSIZ];
|
||
char eabuf[OSN_EASTRSIZ];
|
||
|
||
#if NETIF_HAS_ARPIOCTL
|
||
struct arpreq arq;
|
||
int sock;
|
||
|
||
memset((char *)&arq, 0, sizeof(arq)); /* Clear & set up ARP req */
|
||
arq.arp_pa.sa_family = AF_INET; /* Protocol addr is IP type */
|
||
memcpy( /* Copy IP addr */
|
||
(char *) &((struct sockaddr_in *)&arq.arp_pa)->sin_addr,
|
||
ipa, sizeof(struct in_addr));
|
||
arq.arp_ha.sa_family = AF_UNSPEC; /* Hardware addr is Ether */
|
||
ea_set(arq.arp_ha.sa_data, eap); /* Copy Ether addr */
|
||
|
||
/* Set ARP flags. Always make permanent so needn't keep checking. */
|
||
arq.arp_flags = ATF_PERM; /* Make permanent */
|
||
if (pubf)
|
||
arq.arp_flags |= ATF_PUBL; /* Publish it for us too! */
|
||
|
||
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { /* Get random socket */
|
||
syserr(errno, "Cannot set ARP entry for %s %s - socket()",
|
||
ip_adrsprint(ipbuf, ipa),
|
||
eth_adrsprint(eabuf, eap));
|
||
return FALSE;
|
||
}
|
||
(void) ioctl(sock, SIOCDARP, (char *)&arq); /* Clear old info */
|
||
if (ioctl(sock, SIOCSARP, (char *)&arq) < 0) { /* Set new */
|
||
syserr(errno, "Cannot set ARP entry for %s %s - SIOCSARP",
|
||
ip_adrsprint(ipbuf, ipa),
|
||
eth_adrsprint(eabuf, eap));
|
||
close(sock);
|
||
return FALSE;
|
||
}
|
||
close(sock);
|
||
return TRUE;
|
||
#elif CENV_SYS_XBSD
|
||
/* The new BSD systems completely did away with the ARP ioctls
|
||
and instead substituted a far more complicated PF_ROUTE socket hack.
|
||
Rather than attempt to duplicate the arp(8) utility code here,
|
||
let's try simply invoking it!
|
||
arp -S <ipaddr> <ethaddr> pub
|
||
Note that NetBSD doesn't support -S yet, only -s. -S is like -s
|
||
but deletes any existing entry first, avoiding the need for -d
|
||
which is needed on NetBSD to avoid complaints from arp(8).
|
||
*/
|
||
FILE *f;
|
||
int err;
|
||
char arpbuff[200];
|
||
char resbuff[200];
|
||
|
||
sprintf(arpbuff,
|
||
# if CENV_SYS_NETBSD
|
||
"/usr/sbin/arp -d %s; /usr/sbin/arp -s %s %s %s",
|
||
ip_adrsprint(ipbuf, ipa),
|
||
# else
|
||
"/usr/sbin/arp -S %s %s %s",
|
||
# endif
|
||
ip_adrsprint(ipbuf, ipa),
|
||
eth_adrsprint(eabuf, eap),
|
||
(pubf ? "pub" : ""));
|
||
if (DP_DBGFLG)
|
||
dbprintln("invoking \"%s\"", arpbuff);
|
||
if ((f = popen(arpbuff, "r")) == NULL) {
|
||
syserr(errno, "cannot popen: %s", arpbuff);
|
||
error("Cannot set ARP entry for %s %s",
|
||
ip_adrsprint(ipbuf, ipa),
|
||
eth_adrsprint(eabuf, eap));
|
||
return FALSE;
|
||
}
|
||
/* Read resulting output to avoid possibility it might hang otherwise */
|
||
resbuff[0] = '\0';
|
||
(void) fgets(resbuff, sizeof(resbuff)-1, f);
|
||
err = pclose(f); /* Hope this doesn't wait4() too long */
|
||
if (err) {
|
||
dbprintln("arp exit error: status %d", err);
|
||
dbprintln("arp command was: %s", arpbuff);
|
||
}
|
||
if (DP_DBGFLG)
|
||
dbprintln("arp result \"%s\"", resbuff);
|
||
return TRUE;
|
||
#else
|
||
error("Cannot set ARP entry for %s %s - no implementation",
|
||
ip_adrsprint(ipbuf, ipa),
|
||
eth_adrsprint(eabuf, eap));
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
/* OSN_IFIPGET - get IP address for a given interface name.
|
||
* This is separate from osn_ifiplook() in case iftab_init
|
||
* screws up for some ports.
|
||
*/
|
||
int
|
||
osn_ifipget(int s, /* Socket for (AF_INET, SOCK_DGRAM, 0) */
|
||
char *ifnam, /* Interface name */
|
||
unsigned char *ipa) /* Where to write IP address */
|
||
{
|
||
int ownsock = FALSE;
|
||
char ipstr[OSN_IPSTRSIZ];
|
||
struct ifreq ifr;
|
||
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Can't get IP addr for \"%s\": socket()", ifnam);
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
|
||
/* Find IP address for this interface */
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(s, SIOCGIFADDR, (caddr_t *)&ifr) < 0) {
|
||
syserr(errno, "Can't get IP addr for \"%s\": SIOCGIFADDR", ifnam);
|
||
if (ownsock)
|
||
close(s);
|
||
return FALSE;
|
||
}
|
||
memcpy(ipa,
|
||
(char *)&(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr),
|
||
IP_ADRSIZ);
|
||
|
||
if (ownsock)
|
||
close(s);
|
||
if (DP_DBGFLG)
|
||
dbprintln("IP address for \"%s\" = %s",
|
||
ifnam, ip_adrsprint(ipstr, ipa));
|
||
return TRUE;
|
||
}
|
||
|
||
/* OSN_IFNMGET - get IP netmask for a given interface name.
|
||
* Only needed for IMP, not NI20.
|
||
*/
|
||
int
|
||
osn_ifnmget(int s, /* Socket for (AF_INET, SOCK_DGRAM, 0) */
|
||
char *ifnam, /* Interface name */
|
||
unsigned char *ipa) /* Where to write IP netmask */
|
||
{
|
||
int ownsock = FALSE;
|
||
char ipstr[OSN_IPSTRSIZ];
|
||
struct ifreq ifr;
|
||
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Can't get IP netmask for \"%s\": socket()", ifnam);
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
|
||
/* Find IP address for this interface */
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(s, SIOCGIFNETMASK, (caddr_t *)&ifr) < 0) {
|
||
syserr(errno, "Can't get IP netmask for \"%s\": SIOCGIFNETMASK",ifnam);
|
||
if (ownsock)
|
||
close(s);
|
||
return FALSE;
|
||
}
|
||
memcpy(ipa,
|
||
(char *)&(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr),
|
||
IP_ADRSIZ);
|
||
|
||
if (ownsock)
|
||
close(s);
|
||
if (DP_DBGFLG)
|
||
dbprintln("IP netmask for \"%s\" = %s",
|
||
ifnam, ip_adrsprint(ipstr, ipa));
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/* OSN_IFEAGET - get physical ethernet address for a given interface name.
|
||
*
|
||
* This is fairly tricky as the OSD mechanism for this tends to
|
||
* be *very* poorly documented.
|
||
*
|
||
* Apparently only DEC OSF/1 and Linux can find this directly.
|
||
* However, other systems seem to be divided into either of two
|
||
* camps:
|
||
* 4.3BSD: Hack - get the IP address of the interface, then use
|
||
* use SIOCGARP to look up the hardware address from the
|
||
* kernel's ARP table. Clever and gross.
|
||
* 4.4BSD: New regime, no ARP. But can get it directly as an
|
||
* AF_LINK family address in the ifconf table.
|
||
*/
|
||
int
|
||
osn_ifeaget(int s, /* Socket for (AF_INET, SOCK_DGRAM, 0) */
|
||
char *ifnam, /* Interface name */
|
||
unsigned char *eap, /* Current ether address */
|
||
unsigned char *def) /* Default (hardware) ether address */
|
||
{
|
||
char eastr[OSN_EASTRSIZ];
|
||
|
||
#if CENV_SYS_DECOSF /* Direct approach */
|
||
{
|
||
int ownsock = FALSE;
|
||
struct ifdevea ifdev;
|
||
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Can't get EN addr for \"%s\": socket()", ifnam);
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
strncpy(ifdev.ifr_name, ifnam, sizeof(ifdev.ifr_name));
|
||
if (ioctl(s, SIOCRPHYSADDR, &ifdev) < 0) {
|
||
syserr(errno, "Can't get EN addr for \"%s\": SIOCRPHYSADDR", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
if (ownsock)
|
||
close(s);
|
||
ea_set(eap, ifdev.current_pa);
|
||
if (def)
|
||
ea_set(def, ifdev.default_pa);
|
||
}
|
||
|
||
#elif KLH10_NET_LNX
|
||
{
|
||
int ownsock = FALSE;
|
||
struct ifreq ifr;
|
||
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Can't get EN addr for \"%s\": socket()", ifnam);
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0 ) {
|
||
syserr(errno, "SIOCGIFHWADDR of %s failed", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
|
||
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
|
||
error("%s is not an ethernet - ARPHRD type %d",
|
||
ifnam, ifr.ifr_hwaddr.sa_family);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
if (ownsock)
|
||
close(s);
|
||
ea_set(eap, &ifr.ifr_addr.sa_data[0]); /* Return the ether address */
|
||
if (def)
|
||
memset(def, 0, ETHER_ADRSIZ);
|
||
}
|
||
|
||
#elif NETIF_HAS_ARPIOCTL
|
||
{
|
||
/* Much more general hack */
|
||
unsigned char ipchr[IP_ADRSIZ];
|
||
char ipstr[OSN_IPSTRSIZ];
|
||
|
||
/* Get IP address for this interface, as an argument for ARP lookup */
|
||
if (!osn_ifipget(s, ifnam, ipchr)) {
|
||
error("Can't get EN addr for \"%s\": osn_ifipget failed", ifnam);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Have IP address, now do ARP lookup hackery */
|
||
if (!osn_arp_look((struct in_addr *)ipchr, eap)) {
|
||
syserr(errno,"Can't find EN addr for \"%s\" %s using ARP",
|
||
ifnam, ip_adrsprint(ipstr, ipchr));
|
||
return FALSE;
|
||
}
|
||
if (def)
|
||
memset(def, 0, ETHER_ADRSIZ);
|
||
}
|
||
|
||
#elif NETIF_HAS_ETHLINK
|
||
/* Should never happen unless osn_ifeaget() is called directly
|
||
without going through osn_ifealookup(). If so, attempt
|
||
to do right thing anyway.
|
||
*/
|
||
{
|
||
register struct ifent *ife;
|
||
|
||
if (iftab_initf == 0) {
|
||
/* Try to initialize iftab, ignore errors */
|
||
(void) osn_iftab_init(IFTAB_ETHS);
|
||
}
|
||
if (!(ife = osn_iflookup(ifnam))
|
||
|| !(ife->ife_gotea)) {
|
||
error("No EN addr in iftab for \"%s\"", ifnam);
|
||
return FALSE;
|
||
}
|
||
ea_set(eap, ife->ife_ea);
|
||
if (def)
|
||
memset(def, 0, ETHER_ADRSIZ);
|
||
}
|
||
|
||
#else
|
||
# error "Unimplemented OS routine osn_ifeaget()"
|
||
#endif
|
||
dbprintln("EN addr for \"%s\" = %s",
|
||
ifnam, eth_adrsprint(eastr, eap));
|
||
return TRUE;
|
||
}
|
||
|
||
static struct eth_addr emhost_ea = /* Emulated host ether addr for tap */
|
||
{ 0xf2, 0x0b, 0xa4, 0xff, 0xff, 0xff };
|
||
|
||
/* OSN_PFEAGET - get physical ethernet address for an open packetfilter FD.
|
||
*
|
||
* Also not well documented, but generally easier to perform.
|
||
*/
|
||
int
|
||
osn_pfeaget(int pfs, /* Packetfilter socket or FD */
|
||
char *ifnam, /* Interface name (sometimes needed) */
|
||
unsigned char *eap) /* Where to write ether address */
|
||
{
|
||
|
||
#if KLH10_NET_NIT
|
||
/* SunOS/Solaris: The EA apparently can't be found until after the PF FD
|
||
** is open; an attempt to simply do a socket(AF_UNSPEC, SOCK_DGRAM, 0)
|
||
** will just fail with a protocol-not-supported error. And using an
|
||
** AF_INET socket just gets us the IP address, sigh.
|
||
** Perhaps using AF_DECnet would work, as it does for OSF/1?
|
||
*/
|
||
struct ifreq ifr;
|
||
struct strioctl si; /* Must use kludgy indirect cuz stream */
|
||
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
si.ic_timout = INFTIM;
|
||
si.ic_cmd = SIOCGIFADDR;
|
||
si.ic_len = sizeof ifr;
|
||
si.ic_dp = (char *)𝔦
|
||
if (ioctl(pfs, I_STR, (char *)&si) < 0) {
|
||
syserr(errno, "ether SIOCGIFADDR failed for \"%s\"", ifnam);
|
||
ea_clr(eap);
|
||
return FALSE;
|
||
}
|
||
ea_set(eap, &ifr.ifr_addr.sa_data[0]); /* Return the ether address */
|
||
|
||
#elif KLH10_NET_PFLT /* Really DECOSF */
|
||
struct endevp endp;
|
||
|
||
if (ioctl(pfs, EIOCDEVP, (caddr_t *)&endp) < 0) {
|
||
syserr(errno, "EIOCDEVP failed");
|
||
ea_clr(eap);
|
||
return FALSE;
|
||
}
|
||
if (endp.end_dev_type != ENDT_10MB
|
||
|| endp.end_addr_len != ETHER_ADRSIZ) {
|
||
syserr(errno, "EIOCDEVP returned non-Ethernet info!");
|
||
ea_clr(eap);
|
||
return FALSE;
|
||
}
|
||
ea_set(eap, endp.end_addr);
|
||
|
||
#elif KLH10_NET_TAP_BRIDGE
|
||
/* If we do tap(4) + bridge(4), the ether address of the tap is wholly
|
||
* irrelevant, it is on the other side of the "wire".
|
||
* Our own address is something we can make up completely.
|
||
*/
|
||
if (emhost_ea.ea_octets[5] == 0xFF) {
|
||
time_t t = time(NULL);
|
||
emhost_ea.ea_octets[5] = t & 0xFE;
|
||
emhost_ea.ea_octets[4] = (t >> 8) & 0xFF;
|
||
emhost_ea.ea_octets[3] = (t >> 16) & 0xFF;
|
||
}
|
||
ea_set(eap, &emhost_ea); /* Return the ether address */
|
||
#elif (KLH10_NET_BPF && !CENV_SYS_NETBSD && !CENV_SYS_FREEBSD)
|
||
/* NetBSD no longer seems to support this (on bpf) */
|
||
struct ifreq ifr;
|
||
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(pfs, SIOCGIFADDR, (char *) &ifr) < 0) {
|
||
syserr(errno, "SIOCGIFADDR for EA failed (%d, \"%s\")",
|
||
pfs, ifnam);
|
||
ea_clr(eap);
|
||
return FALSE;
|
||
}
|
||
ea_set(eap, &ifr.ifr_addr.sa_data[0]); /* Return the ether address */
|
||
|
||
#elif KLH10_NET_DLPI
|
||
{
|
||
static int dleaget(int fd, unsigned char *eap);
|
||
|
||
/* Use handy auxiliary to hide hideous DLPI details */
|
||
if (!dleaget(pfs, eap))
|
||
return FALSE;
|
||
}
|
||
#else
|
||
if (!osn_ifeaget(pfs, ifnam, eap, (unsigned char *)NULL))
|
||
return FALSE;
|
||
#endif
|
||
|
||
if (DP_DBGFLG) {
|
||
char eastr[OSN_EASTRSIZ];
|
||
|
||
dbprintln("EN addr for \"%s\" = %s",
|
||
ifnam, eth_adrsprint(eastr, eap));
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
#if CENV_SYS_XBSD
|
||
static int osn_pareth(char *cp, unsigned char *adr);
|
||
#endif
|
||
|
||
/* OSN_ARP_LOOK - Attempt looking up ARP information from OS
|
||
* This may be needed by DPIMP to find the ether addr for an IP address,
|
||
* or by DPNI20 when trying to determine its own ether address.
|
||
* In any case, it's a very OS-dependent technique worth encapsulating.
|
||
*
|
||
* NOTE: Failure to find an address should not cause an error printout,
|
||
* unless there is some actual system glitch.
|
||
*/
|
||
int
|
||
osn_arp_look(struct in_addr *ipa, /* Look up this IP address */
|
||
unsigned char *eap) /* Return EA here */
|
||
{
|
||
#if NETIF_HAS_ARPIOCTL
|
||
char ipstr[OSN_IPSTRSIZ];
|
||
struct arpreq arq;
|
||
int sock;
|
||
|
||
/* Query kernel directly. */
|
||
memset((char *)&arq, 0, sizeof(arq)); /* Clear & set up ARP req */
|
||
arq.arp_pa.sa_family = AF_INET; /* Protocol addr is IP type */
|
||
arq.arp_ha.sa_family = AF_UNSPEC; /* Hardware addr is Ether */
|
||
((struct sockaddr_in *)&arq.arp_pa)->sin_addr = *ipa;
|
||
|
||
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { /* Get random socket */
|
||
syserr(errno, "osn_arp_look socket()");
|
||
return FALSE;
|
||
}
|
||
if (ioctl(sock, SIOCGARP, (char *)&arq) != 0) { /* Get ARP info */
|
||
close(sock);
|
||
/* If SIOCGARP fails, assume it just means the entry wasn't found,
|
||
rather than implying some error.
|
||
*/
|
||
if (DP_DBGFLG)
|
||
dbprintln("No ARP info for %s",
|
||
ip_adrsprint(ipstr, (unsigned char *)ipa));
|
||
return FALSE;
|
||
}
|
||
close(sock);
|
||
|
||
/* Won, check flags */
|
||
if (!(arq.arp_flags & ATF_COM)) {
|
||
if (DP_DBGFLG)
|
||
dbprintln("ARP entry incomplete for %s",
|
||
ip_adrsprint(ipstr, (unsigned char *)ipa));
|
||
return FALSE;
|
||
}
|
||
ea_set((char *)eap, arq.arp_ha.sa_data); /* Copy ether addr */
|
||
return TRUE;
|
||
|
||
#elif CENV_SYS_XBSD
|
||
/* The new BSD stuff did away with the ARP ioctls and substituted an
|
||
** extremely complicated routing IPC mechanism. For the time being
|
||
** I'll just use a horrible hack.
|
||
*/
|
||
unsigned char *cp = (unsigned char *)ipa;
|
||
char *arppath = "/usr/sbin/arp";
|
||
char arpbuff[128];
|
||
FILE *f;
|
||
char fhost[100];
|
||
char fip[32];
|
||
char fat[8];
|
||
char fhex[32];
|
||
struct eth_addr etha;
|
||
int res;
|
||
|
||
/* Use -n to avoid symbolic lookup hangs */
|
||
sprintf(arpbuff, "%s -n %u.%u.%u.%u", arppath,
|
||
cp[0], cp[1], cp[2], cp[3]);
|
||
if (DP_DBGFLG)
|
||
dbprintln("invoking \"%s\"", arpbuff);
|
||
if ((f = popen(arpbuff, "r")) == NULL) {
|
||
syserr(errno, "cannot popen: %s", arpbuff);
|
||
return FALSE;
|
||
}
|
||
arpbuff[0] = '\0';
|
||
(void) fgets(arpbuff, sizeof(arpbuff)-1, f);
|
||
res = pclose(f); /* Hope this doesn't wait4() too long */
|
||
if (res)
|
||
dbprintln("arp exit error: status %d", res);
|
||
if (DP_DBGFLG)
|
||
dbprintln("arp result \"%s\"", arpbuff);
|
||
|
||
/* Parse the result. There are three possible formats:
|
||
** <hostid> (<d.d.d.d>) at <etherhex> <options?> 4+ words
|
||
** <hostid> (<d.d.d.d>) at (incomplete) 4 words only?
|
||
** <hostid> (<d.d.d.d>) -- no entry 5 words only
|
||
*/
|
||
res = sscanf(arpbuff, "%99s %31s %7s %31s", fhost, fip, fat, fhex);
|
||
if (res == 4 && (strcmp(fat, "at")==0)
|
||
&& osn_pareth(fhex, (unsigned char *)ða)) {
|
||
ea_set(eap, ða);
|
||
return TRUE;
|
||
}
|
||
|
||
/* Failed, see if failure is understood or not */
|
||
if (res == 4
|
||
&& ( ((strcmp(fat, "at")==0) && strcmp(fhex, "(incomplete)")==0)
|
||
|| ((strcmp(fat, "--")==0) && strcmp(fhex, "no")==0))) {
|
||
|
||
/* Failed in a way we understand, so don't complain */
|
||
return FALSE;
|
||
}
|
||
error("osn_arp_look result unparseable: \"%s\"", arpbuff);
|
||
return FALSE;
|
||
|
||
#else
|
||
error("osn_arp_look not implemented");
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
#if CENV_SYS_XBSD /* Auxiliary to support code in osn_arp_look() */
|
||
static int
|
||
osn_pareth(char *cp, unsigned char *adr)
|
||
{
|
||
unsigned int b1, b2, b3, b4, b5, b6;
|
||
int cnt;
|
||
|
||
cnt = sscanf(cp, "%x:%x:%x:%x:%x:%x", &b1, &b2, &b3, &b4, &b5, &b6);
|
||
if (cnt != 6) {
|
||
/* Later try as single large address #? */
|
||
return FALSE;
|
||
}
|
||
if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255 || b5 > 255 || b6 > 255)
|
||
return FALSE;
|
||
*adr++ = b1;
|
||
*adr++ = b2;
|
||
*adr++ = b3;
|
||
*adr++ = b4;
|
||
*adr++ = b5;
|
||
*adr = b6;
|
||
return TRUE;
|
||
}
|
||
#endif /* CENV_SYS_XBSD */
|
||
|
||
#if !OSN_USE_IPONLY /* This stuff not needed for DPIMP */
|
||
|
||
/* OSN_IFEASET - Set physical ethernet address for a given interface name.
|
||
*
|
||
* Like osn_ifeaget, also tricky with obscure non-standard mechanisms.
|
||
*
|
||
*/
|
||
int
|
||
osn_ifeaset(int s, /* Socket for (AF_INET, SOCK_DGRAM, 0) */
|
||
char *ifnam, /* Interface name */
|
||
unsigned char *newpa) /* New ether address */
|
||
{
|
||
#if CENV_SYS_DECOSF || KLH10_NET_LNX || KLH10_NET_TAP_BRIDGE \
|
||
|| (CENV_SYS_FREEBSD && defined(SIOCSIFLLADDR))
|
||
|
||
/* Common preamble code */
|
||
int ownsock = FALSE;
|
||
struct ifreq ifr;
|
||
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Failed osn_ifeaset socket()");
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
|
||
# if CENV_SYS_DECOSF /* Direct approach */
|
||
/* NOTE!!! DECOSF Doc bug!
|
||
** Contrary to appearances in <sys/ioctl.h where SIOCSPHYSADDR is
|
||
** said to take a "struct devea" arg, it actually uses an ifreq!
|
||
** SIOCRPHYSADDR does use devea... barf!
|
||
*/
|
||
ifr.ifr_addr.sa_family = AF_DECnet; /* Odd choice, but works */
|
||
ea_set(ifr.ifr_addr.sa_data, newpa);
|
||
if (ioctl(s, SIOCSPHYSADDR, &ifr) < 0) {
|
||
syserr(errno, "\"%s\" SIOCSPHYSADDR failed", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
|
||
# elif KLH10_NET_LNX
|
||
|
||
/* Address family must match what device thinks it is, so find that
|
||
out first... sigh.
|
||
*/
|
||
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) {
|
||
syserr(errno, "\"%s\" SIOCGIFHWADDR failed", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
ea_set(ifr.ifr_addr.sa_data, newpa); /* Now set new addr */
|
||
if (ioctl(s, SIOCSIFHWADDR, &ifr) < 0) {
|
||
syserr(errno, "\"%s\" SIOCSIFHWADDR failed", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
|
||
# elif CENV_SYS_FREEBSD && defined(SIOCSIFLLADDR)
|
||
/* This works for 4.2 and up; as of 3.3 no known way to set ether addr. */
|
||
|
||
ifr.ifr_addr.sa_len = 6;
|
||
ifr.ifr_addr.sa_family = AF_LINK; /* Must be this */
|
||
ea_set(ifr.ifr_addr.sa_data, newpa);
|
||
if (ioctl(s, SIOCSIFLLADDR, &ifr) < 0) {
|
||
syserr(errno, "\"%s\" SIOCSIFLLADDR failed", ifnam);
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
# elif KLH10_NET_TAP_BRIDGE
|
||
ea_set(&emhost_ea, newpa);
|
||
# else
|
||
# error "Unimplemented OS routine osn_ifeaset()"
|
||
# endif
|
||
/* Common postamble code */
|
||
if (ownsock)
|
||
close(s);
|
||
return TRUE;
|
||
|
||
#else
|
||
# ifdef __GNUC__
|
||
# if CENV_SYS_NETBSD /* As of 1.6 NetBSD STILL has no way to do this */
|
||
# warning "NetBSD still sucks - Unimplemented OS routine osn_ifeaset()"
|
||
# else
|
||
# warning "Unimplemented OS routine osn_ifeaset()"
|
||
# endif
|
||
# endif
|
||
error("\"%s\" could not set ether addr - osn_ifeaset() unimplemented",
|
||
ifnam);
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
/* OSN_IFMCSET - Set ethernet multicast address (add or delete),
|
||
* or change promiscuous mode.
|
||
* Hack for now: if "pa" argument is NULL, we are dealing with
|
||
* promiscuous mode; "delf" is TRUE to turn off, else it's turned on.
|
||
*/
|
||
int
|
||
osn_ifmcset(int s,
|
||
char *ifnam,
|
||
int delf,
|
||
unsigned char *pa)
|
||
{
|
||
#if CENV_SYS_DECOSF || KLH10_NET_LNX || CENV_SYS_FREEBSD
|
||
|
||
/* Common preamble code */
|
||
int ownsock = FALSE;
|
||
struct ifreq ifr;
|
||
|
||
if (s == -1) {
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
syserr(errno, "Failed osn_ifmcset socket()");
|
||
return FALSE;
|
||
}
|
||
ownsock = TRUE;
|
||
}
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
|
||
# if CENV_SYS_DECOSF
|
||
ifr.ifr_addr.sa_family = AF_DECnet; /* Known to work; AF_UNSPEC may not */
|
||
# elif CENV_SYS_LINUX
|
||
ifr.ifr_addr.sa_family = AF_UNSPEC; /* MUST be this for Linux! */
|
||
# elif CENV_SYS_FREEBSD
|
||
ifr.ifr_addr.sa_family = AF_LINK; /* MUST be this for FreeBSD! */
|
||
# else
|
||
# error "Unimplemented OS routine osn_ifmcset()"
|
||
# endif
|
||
|
||
if (pa) {
|
||
/* Doing multicast stuff */
|
||
ea_set(ifr.ifr_addr.sa_data, pa);
|
||
|
||
if (ioctl(s, (delf ? SIOCDELMULTI : SIOCADDMULTI), &ifr) < 0) {
|
||
syserr(errno, "\"%s\" %s failed", ifnam,
|
||
(delf ? "SIOCDELMULTI" : "SIOCADDMULTI"));
|
||
bad:
|
||
if (ownsock) close(s);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
/* Doing promiscuous stuff */
|
||
int flags;
|
||
|
||
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
|
||
syserr(errno, "SIOCGIFFLAGS failed for interface \"%s\"", ifnam);
|
||
goto bad;
|
||
|
||
}
|
||
if (delf)
|
||
ifr.ifr_flags &= ~IFF_PROMISC; /* Turn off */
|
||
else
|
||
ifr.ifr_flags |= IFF_PROMISC; /* Turn on */
|
||
|
||
if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
|
||
syserr(errno, "SIOCSIFFLAGS failed for interface \"%s\"", ifnam);
|
||
goto bad;
|
||
}
|
||
}
|
||
if (ownsock)
|
||
close(s);
|
||
return TRUE;
|
||
|
||
#else
|
||
# ifdef __GNUC__
|
||
# warning "Unimplemented OS routine osn_ifmcset()"
|
||
# endif
|
||
error("\"%s\" could not %s multicast addr - osn_ifmcset() unimplemented",
|
||
ifnam, (delf ? "delete" : "add"));
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
#endif /* !OSN_USE_IPONLY */
|
||
|
||
/* OSN_PFINIT - Get and initialize file descriptor for packetfilter.
|
||
* Actual degree to which the PF is initialized is still
|
||
* very OSD.
|
||
* FD is always opened for both read/write.
|
||
*/
|
||
|
||
#if KLH10_NET_PFLT || KLH10_NET_BPF
|
||
|
||
/* Adapted from DEC's pfopen.c - doing it ourselves here because pfopen(3)
|
||
* did not always exist, and this way we can report errors better.
|
||
*/
|
||
|
||
static int
|
||
pfopen(void)
|
||
{
|
||
#if KLH10_NET_PFLT
|
||
# define PFDEVPREF "/dev/pf/pfilt"
|
||
#elif KLH10_NET_BPF
|
||
# define PFDEVPREF "/dev/bpf"
|
||
#endif
|
||
char pfname[sizeof(PFDEVPREF)+10];
|
||
int fd;
|
||
int i = 0;
|
||
|
||
/* Find first free packetfilter device */
|
||
do {
|
||
(void) sprintf(pfname, "%s%d", PFDEVPREF, i++);
|
||
fd = open(pfname, O_RDWR, 0);
|
||
} while (fd < 0 && errno == EBUSY); /* If device busy, keep looking */
|
||
|
||
if (fd < 0) {
|
||
/* Note possible error meanings:
|
||
ENOENT - no such filename
|
||
ENXIO - not configured in kernel
|
||
*/
|
||
esfatal(1, "Couldn't find or open packetfilter device, last tried %s",
|
||
pfname);
|
||
}
|
||
return fd; /* Success! */
|
||
}
|
||
|
||
#endif /* KLH10_NET_PFLT || KLH10_NET_BPF */
|
||
|
||
|
||
#if KLH10_NET_PFLT
|
||
|
||
int
|
||
osn_pfinit(struct osnpf *osnpf, void *pfarg)
|
||
{
|
||
int fd;
|
||
int arg;
|
||
unsigned short bits;
|
||
|
||
/* Open PF fd. We'll be doing both R and W. */
|
||
fd = pfopen(); /* Open, will abort if fails */
|
||
if (osnpf->osnpf_ifnam && osnpf->osnpf_ifnam[0]) { /* Allow default ifc */
|
||
/* Bind to specified HW interface */
|
||
if (ioctl(fd, EIOCSETIF, osnpf->osnpf_ifnam) < 0)
|
||
esfatal(1, "Couldn't open packetfilter for \"%s\" - EIOCSETIF",
|
||
osnpf->osnpf_ifnam);
|
||
}
|
||
|
||
/* Set up various mode & flag stuff */
|
||
|
||
/* Only the super-user can allow promiscuous or copy-all to be set. */
|
||
#if 0
|
||
/* Allow promiscuous mode if someone wants to use it.
|
||
** For shared interface, never true.
|
||
*/
|
||
arg = 1;
|
||
if (ioctl(fd, EIOCALLOWPROMISC, &arg))
|
||
esfatal(1, "EIOCALLOWPROMISC failed"); /* Maybe not SU? */
|
||
#endif
|
||
/* Allow ENCOPYALL bit to be set.
|
||
** Always use this for shared interface so the multicast-bit stuff
|
||
** we grab can also be seen by kernel and other sniffers.
|
||
*/
|
||
arg = 1;
|
||
if (ioctl(fd, EIOCALLOWCOPYALL, &arg))
|
||
esfatal(1, "EIOCALLOWCOPYALL failed"); /* Maybe not SU? */
|
||
|
||
|
||
/* Ensure following bits clear:
|
||
** no timestamps, no batching, no held signal
|
||
*/
|
||
bits = (ENTSTAMP | ENBATCH | ENHOLDSIG);
|
||
if (ioctl(fd, EIOCMBIC, &bits))
|
||
esfatal(1, "EIOCMBIC failed");
|
||
|
||
/* Set:
|
||
** ??? Put interface into promisc mode (if allowed by SU)
|
||
** KLH: No longer -- shouldn't be necessary.
|
||
** Pass packet along to other filters
|
||
** Copy packets to/from native kernel ptcls - to allow talking with
|
||
** native host platform!
|
||
*/
|
||
bits = (/* ENPROMISC | */ ENNONEXCL | ENCOPYALL);
|
||
if (ioctl(fd, EIOCMBIS, &bits))
|
||
esfatal(1, "ioctl: EIOCMBIS");
|
||
|
||
/* Set up packet filter for it - only needed if sharing interface */
|
||
if (!osnpf->osnpf_dedic) {
|
||
if (ioctl(fd, EIOCSETF, pfbuild(pfarg, &(osnpf->osnpf_ip.ia_addr))) < 0)
|
||
esfatal(1, "EIOCSETF failed");
|
||
}
|
||
|
||
/* Now can get our interface's ethernet address.
|
||
** In general, this has to wait until after the packetfilter is opened,
|
||
** since until then we don't have a handle on the specific interface
|
||
** that will be used.
|
||
*/
|
||
{
|
||
struct endevp endp;
|
||
|
||
if (ioctl(fd, EIOCDEVP, (caddr_t *)&endp) < 0)
|
||
esfatal(1, "EIOCDEVP failed");
|
||
if (endp.end_dev_type != ENDT_10MB
|
||
|| endp.end_addr_len != 6)
|
||
esfatal(1, "EIOCDEVP returned non-Ethernet info!");
|
||
ea_set(&(osnpf->osnpf_ea), endp.end_addr);
|
||
}
|
||
|
||
/* Miscellaneous stuff */
|
||
|
||
/* Hack: use timeout mechanism to see if it helps avoid wedging system
|
||
** when using OSF/1 V3.0.
|
||
*/
|
||
if (osnpf->osnpf_rdtmo)
|
||
{
|
||
struct timeval tv;
|
||
|
||
tv.tv_sec = osnpf->osnpf_rdtmo;
|
||
tv.tv_usec = 0;
|
||
|
||
if (ioctl(fd, EIOCSRTIMEOUT, &tv) < 0)
|
||
esfatal(1, "EIOCSRTIMEOUT failed");
|
||
}
|
||
|
||
/* If backlog param was provided, try to set system's idea of how many
|
||
** input packets can be kept on kernel queue while waiting for us to
|
||
** read them into user space.
|
||
*/
|
||
if (osnpf->osnpf_backlog) {
|
||
if (ioctl(fd, EIOCSETW, &(osnpf->osnpf_backlog)) < 0)
|
||
esfatal(1, "EIOCSETW failed");
|
||
}
|
||
|
||
/* Ready to roll! */
|
||
return fd;
|
||
}
|
||
#endif /* KLH10_NET_PFLT */
|
||
|
||
#if KLH10_NET_BPF
|
||
|
||
int
|
||
osn_pfinit(register struct osnpf *osnpf, void *arg)
|
||
{
|
||
int fd;
|
||
struct bpf_version bv;
|
||
struct ifreq ifr;
|
||
u_int u;
|
||
int i;
|
||
char *ifnam = osnpf->osnpf_ifnam;
|
||
|
||
/* No "default interface" concept here */
|
||
if (!ifnam || !ifnam[0])
|
||
esfatal(1, "Packetfilter interface must be specified");
|
||
|
||
/* Open an unused BPF device for R/W */
|
||
fd = pfopen(); /* Will abort if fail */
|
||
|
||
/* Check the filter language version number */
|
||
if (ioctl(fd, BIOCVERSION, (char *) &bv) < 0)
|
||
esfatal(1, "kernel BPF interpreter out of date");
|
||
else if (bv.bv_major != BPF_MAJOR_VERSION ||
|
||
bv.bv_minor < BPF_MINOR_VERSION)
|
||
efatal(1, "requires BPF language %d.%d or higher; kernel is %d.%d",
|
||
BPF_MAJOR_VERSION, BPF_MINOR_VERSION, bv.bv_major, bv.bv_minor);
|
||
|
||
/* Set immediate mode so that packets are processed as they arrive,
|
||
rather than waiting until timeout or buffer full.
|
||
|
||
WARNING: NetBSD does not implement this correctly! The code
|
||
in src/sys/net/bpf.c:bpfread() treats the immediate flag in a way
|
||
that causes it to return EWOULDBLOCK if no input is available. But
|
||
this flag must still be set in order for bpfpoll() to detect input
|
||
as soon as it arrives!
|
||
See dpni20 and dpimp read loops for workaround.
|
||
*/
|
||
i = 1;
|
||
if (ioctl(fd, BIOCIMMEDIATE, (char *) &i) < 0)
|
||
esfatal(1, "BIOCIMMEDIATE failed");
|
||
|
||
/* Set the read() buffer size.
|
||
Must be set before interface is attached!
|
||
*/
|
||
u = OSN_BPF_MTU;
|
||
if (ioctl(fd, BIOCSBLEN, (char *) &u) < 0)
|
||
esfatal(1, "BIOCSBLEN failed");
|
||
|
||
/* Set up packet filter for it - only needed if sharing interface?
|
||
Not sure whether it's needed for a dedicated interface; will need
|
||
to experiment.
|
||
*/
|
||
if (!osnpf->osnpf_dedic) {
|
||
struct OSN_PFSTRUCT *pf;
|
||
|
||
/* Set the kernel packet filter */
|
||
pf = pfbuild(arg, &(osnpf->osnpf_ip.ia_addr));
|
||
if (ioctl(fd, BIOCSETF, (char *)pf) < 0)
|
||
esfatal(1, "BIOCSETF failed");
|
||
}
|
||
|
||
|
||
/* Set read timeout.
|
||
Safety check in order to avoid infinite hangs if something
|
||
wedges up. The periodic re-check overhead is insignificant.
|
||
*/
|
||
/* Set read timeout.
|
||
Safety hack derived from need to use timeout to avoid wedging system
|
||
when using OSF/1 V3.0. Probably useful for other systems too.
|
||
*/
|
||
if (osnpf->osnpf_rdtmo)
|
||
{
|
||
struct timeval tv;
|
||
|
||
tv.tv_sec = osnpf->osnpf_rdtmo;
|
||
tv.tv_usec = 0;
|
||
|
||
if (ioctl(fd, BIOCSRTIMEOUT, (char *) &tv) < 0)
|
||
esfatal(1, "BIOCSRTIMEOUT failed");
|
||
}
|
||
|
||
/* Attach/bind to desired interface device
|
||
*/
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(fd, BIOCSETIF, (char *) &ifr) < 0)
|
||
esfatal(1, "BIOCSETIF failed for interface \"%s\"", ifnam);
|
||
|
||
/* This code only works with Ethernet, so check for that.
|
||
Note cannot check until after interface is attached.
|
||
*/
|
||
if (ioctl(fd, BIOCGDLT, (char *) &u) < 0)
|
||
esfatal(1, "BIOCGDLT failed for interface \"%s\"", ifnam);
|
||
if (u != DLT_EN10MB)
|
||
efatal(1, "%s is not an ethernet", ifnam);
|
||
|
||
|
||
/* Now get our interface's ethernet address.
|
||
** In general, this has to wait until after the packetfilter is opened,
|
||
** since until then we don't have a handle on the specific interface
|
||
** that will be used.
|
||
*/
|
||
(void) osn_pfeaget(fd, ifnam, (unsigned char *)&(osnpf->osnpf_ea));
|
||
|
||
/* Ready to roll! */
|
||
return fd;
|
||
}
|
||
|
||
#endif /* KLH10_NET_BPF */
|
||
|
||
#if KLH10_NET_TUN
|
||
/*
|
||
In order to use the TUN interface we have to do the equivalent of
|
||
(1) "ifconfig tun0 <localaddr> <destaddr> up"
|
||
as well as
|
||
(2) "arp <destaddr> <localetheraddr> perm pub"
|
||
and finally
|
||
(3) "sysctl -w net.inet.ip.forwarding=1"
|
||
if you want to connect in from other machines besides
|
||
the one the emulator is running on. This last step must
|
||
still be done externally as it affects overall system
|
||
security.
|
||
|
||
For (1) the code must flush any existing address, add the new address, and
|
||
ensure the interface's IFF_UP flag is set. Assuming we'll always be running
|
||
on *BSD because that's the only system supporting TUN at the moment, the
|
||
necessary calls are
|
||
SIOCDIFADDR - Delete existing address
|
||
SIOCAIFADDR - Add existing address (and broadcast and mask)
|
||
SIOCSIFFLAGS - To set IFF_UP
|
||
|
||
Some comments about how SIOCAIFADDR and SIOCDIFADDR work, based on
|
||
observation of the FreeBSD code:
|
||
|
||
The miniscule doc is in netintro(4). Use an AF_INET, DGRAM socket.
|
||
Both take:
|
||
|
||
DELETE takes a struct ifreq (can use an ifaliasreq like the doc claims,
|
||
but the SIOCDIFADDR ioctl only defines it as taking an ifreq's worth of
|
||
data!). If the first addr matches an existing one, that one is
|
||
deleted. Otherwise, the first AF_INET address is deleted; INADDR_ANY
|
||
from in.h (ie all zeros) is probably the right thing for this case.
|
||
|
||
|
||
ADD takes a new struct so as to set everything at once:
|
||
|
||
struct ifaliasreq {
|
||
char ifra_name[IFNAMSIZ]; // if name, e.g. "en0"
|
||
struct sockaddr ifra_addr;
|
||
struct sockaddr ifra_broadaddr;
|
||
struct sockaddr ifra_mask;
|
||
};
|
||
|
||
For ADD, the ifra_addr field is the new address to add.
|
||
However, the ifra_broadaddr field is actually used in two different ways.
|
||
If the interface has IFF_BROADCAST set, then ifra_broadaddr is the
|
||
broadcast address, as advertised.
|
||
But if it has IFF_POINTOPOINT set, it is interpreted as "ifra_dstaddr";
|
||
the destination (or remote) address. It is an error if this address
|
||
is INADDR_ANY.
|
||
The ifra_mask field is ignored (left as-is or zeroed if new) unless
|
||
ifra_mask.sin_len is non-zero.
|
||
|
||
*/
|
||
|
||
int
|
||
osn_pfinit(register struct osnpf *osnpf, void *arg)
|
||
{
|
||
int allowextern = TRUE; /* For now, always try for external access */
|
||
int fd;
|
||
char tunname[sizeof "/dev/tun000"];
|
||
char *ifnam = tunname + sizeof("/dev/")-1;
|
||
int i = -1;
|
||
char ipb1[OSN_IPSTRSIZ];
|
||
char ipb2[OSN_IPSTRSIZ];
|
||
struct ifent *ife = NULL; /* Native host's default IP interface if one */
|
||
struct in_addr iplocal; /* TUN ifc address at hardware OS end */
|
||
struct in_addr ipremote; /* Address at remote (emulated host) end */
|
||
static unsigned char ipremset[4] = { 192, 168, 0, 44};
|
||
|
||
/* Remote address is always that of emulated machine */
|
||
ipremote = osnpf->osnpf_ip.ia_addr;
|
||
|
||
/* Local address is that of hardware machine if we want to permit
|
||
external access. If not, it doesn't matter (and may not even
|
||
exist, if there is no hardware interface)
|
||
*/
|
||
if (allowextern) {
|
||
if (osn_iftab_init(IFTAB_IPS) && (ife = osn_ipdefault())) {
|
||
iplocal = ife->ife_ipia;
|
||
} else {
|
||
error("Cannot find default IP interface for host");
|
||
allowextern = FALSE;
|
||
}
|
||
}
|
||
if (!allowextern) {
|
||
/* Make up bogus IP address for internal use */
|
||
memcpy((char *)&iplocal, ipremset, 4);
|
||
}
|
||
|
||
if (DP_DBGFLG)
|
||
dbprint("Opening TUN device");
|
||
|
||
do {
|
||
sprintf(tunname, "/dev/tun%d", ++i);
|
||
} while ((fd = open(tunname, O_RDWR)) < 0 && errno == EBUSY);
|
||
|
||
if (fd < 0)
|
||
esfatal(1, "Couldn't open tunnel device %s", tunname);
|
||
|
||
if (DP_DBGFLG)
|
||
dbprintln("Opened %s, configuring for local %s, remote %s",
|
||
ifnam,
|
||
ip_adrsprint(ipb1, (unsigned char *)&iplocal),
|
||
ip_adrsprint(ipb2, (unsigned char *)&ipremote));
|
||
|
||
|
||
/* Activate TUN device.
|
||
First address is "local" -- doesn't matter if all we care about is
|
||
talking to the native/hardware host, and it can be set to any of the IP
|
||
address ranges reserved for LAN-only (non-Internet) use, such as
|
||
10.0.0.44.
|
||
However, if planning to allow other machines to access the virtual
|
||
host, probably best to use an address suitable for the same LAN
|
||
subnet as the hardware host.
|
||
Unclear yet whether it works to use the host's own address; it at
|
||
least allows the configuration to happen.
|
||
|
||
Second address is "remote" -- the one the emulated host is using.
|
||
It should probably match the same network as the local address,
|
||
especially if planning to connect from other machines.
|
||
*/
|
||
#if 0 /* Hacky method */
|
||
{
|
||
char cmdbuff[128];
|
||
int res;
|
||
sprintf(cmdbuff, "ifconfig %s %s %s up",
|
||
ifnam,
|
||
ip_adrsprint(ipb1, (unsigned char *)&iplocal),
|
||
ip_adrsprint(ipb2, (unsigned char *)&ipremote));
|
||
|
||
if ((res = system(cmdbuff)) != 0) {
|
||
esfatal(1, "osn_pfinit: ifconfig failed to initialize tunnel device?");
|
||
}
|
||
}
|
||
#else
|
||
{
|
||
/* Internal method */
|
||
int s;
|
||
struct ifaliasreq ifra;
|
||
struct ifreq ifr;
|
||
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
esfatal(1, "pf_init: tun socket() failed");
|
||
}
|
||
|
||
/* Delete first (only) IP address for this device, if any.
|
||
Ignore errors.
|
||
*/
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
|
||
if (ioctl(s, SIOCDIFADDR, &ifr) < 0) {
|
||
if (DP_DBGFLG)
|
||
syserr(errno, "osn_pfinit tun SIOCDIFADDR failed");
|
||
}
|
||
|
||
memset(&ifra, 0, sizeof(ifra));
|
||
strncpy(ifra.ifra_name, ifnam, sizeof(ifra.ifra_name));
|
||
((struct sockaddr_in *)(&ifra.ifra_addr))->sin_len = sizeof(struct sockaddr_in);
|
||
((struct sockaddr_in *)(&ifra.ifra_addr))->sin_family = AF_INET;
|
||
((struct sockaddr_in *)(&ifra.ifra_addr))->sin_addr = iplocal;
|
||
((struct sockaddr_in *)(&ifra.ifra_broadaddr))->sin_len = sizeof(struct sockaddr_in);
|
||
((struct sockaddr_in *)(&ifra.ifra_broadaddr))->sin_family = AF_INET;
|
||
((struct sockaddr_in *)(&ifra.ifra_broadaddr))->sin_addr = ipremote;
|
||
if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
|
||
esfatal(1, "osn_pfinit tun SIOCAIFADDR failed");
|
||
}
|
||
|
||
/* Finally, turn on IFF_UP just in case the above didn't do it.
|
||
Note interface name is still there from the SIOCDIFADDR.
|
||
*/
|
||
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
|
||
esfatal(1, "osn_pfinit tun SIOCGIFFLAGS failed");
|
||
}
|
||
if (!(ifr.ifr_flags & IFF_UP)) {
|
||
ifr.ifr_flags |= IFF_UP;
|
||
if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
|
||
esfatal(1, "osn_pfinit tun SIOCSIFFLAGS failed");
|
||
}
|
||
if (DP_DBGFLG)
|
||
dbprint("osn_pfinit tun did SIOCSIFFLAGS");
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* Now optionally determine ethernet address.
|
||
This amounts to what if anything we should put in the native
|
||
host's ARP tables.
|
||
- If we only intend to use the net between the virtual host and
|
||
its hardware host, then no ARP hackery is needed.
|
||
- However, if the intent is to allow traffic between the virtual
|
||
host and other machines on the LAN or Internet, then an ARP
|
||
entry is required. It must advertise the virtual host's IP
|
||
address, using one of the hardware host's ethernet addresses
|
||
so any packets on the LAN for the virtual host will at least
|
||
wind up arriving at the hardware host it's running on.
|
||
*/
|
||
|
||
/* Simple method is to get the ifconf table and scan it to find the
|
||
first interface with both an IP and ethernet address given.
|
||
Non-4.4BSD systems may not provide the latter datum, but if
|
||
we're using TUN then we almost certainly also have the good stuff
|
||
in ifconf.
|
||
*/
|
||
if (allowextern && ife) {
|
||
/* Need to determine ether addr of our default interface, then
|
||
publish an ARP entry mapping the virtual host to the same
|
||
ether addr.
|
||
*/
|
||
(void) osn_arp_stuff((unsigned char *)&ipremote, ife->ife_ea, TRUE);
|
||
|
||
/* Return that as our ether address */
|
||
ea_set((char *)&osnpf->osnpf_ea, ife->ife_ea);
|
||
} else {
|
||
/* Assume no useful ether addr */
|
||
ea_clr((char *)&osnpf->osnpf_ea);
|
||
}
|
||
|
||
if (DP_DBGFLG)
|
||
dbprintln("osn_pfinit tun completed");
|
||
|
||
return fd;
|
||
}
|
||
|
||
#endif /* KLH10_NET_TUN */
|
||
|
||
#if KLH10_NET_LNX
|
||
|
||
/*
|
||
The Linux PF_PACKET interface is described to some extent
|
||
by the packet(7) man page.
|
||
|
||
Linux provides no kernel packet filtering mechanism other than
|
||
possibly a check on the ethernet protocol type, but this is useless
|
||
for us since we'll always want to check for more than just one type;
|
||
e.g. IP and ARP, plus possibly 802.3 or DECNET packets.
|
||
|
||
From the man page for packet(7):
|
||
By default all packets of the specified protocol type are
|
||
passed to a packet socket. To only get packets from a spe-
|
||
cific interface use bind(2) specifying an address in a
|
||
struct sockaddr_ll to bind the packet socket to an inter-
|
||
face. Only the sll_protocol and the sll_ifindex address
|
||
fields are used for purposes of binding.
|
||
*/
|
||
int
|
||
osn_pfinit(struct osnpf *osnpf, void *arg)
|
||
{
|
||
int fd;
|
||
char *ifcname = osnpf->osnpf_ifnam;
|
||
struct ifreq ifr;
|
||
|
||
/* Open a socket of the desired type.
|
||
*/
|
||
struct sockaddr_ll sll;
|
||
int ifx;
|
||
|
||
/* Get raw packets with ethernet headers
|
||
*/
|
||
fd = socket(PF_PACKET, SOCK_RAW,
|
||
#if 0 /*OSN_USE_IPONLY*/ /* If DPIMP or otherwise IP only */
|
||
htons(ETH_P_IP) /* for IP only */
|
||
#else
|
||
htons(ETH_P_ALL) /* for everything */
|
||
#endif
|
||
);
|
||
if (fd < 0)
|
||
esfatal(1, "Couldn't open packet socket");
|
||
|
||
/* Need ifc index in order to do binding, so get it. */
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strncpy(ifr.ifr_name, ifcname, sizeof(ifr.ifr_name));
|
||
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0 )
|
||
esfatal(1, "SIOCGIFINDEX of %s failed", ifcname);
|
||
ifx = ifr.ifr_ifindex;
|
||
|
||
/* Bind to proper device/interface using ifc index */
|
||
memset(&sll, 0, sizeof(sll));
|
||
sll.sll_family = AF_PACKET;
|
||
sll.sll_protocol = htons(ETH_P_ALL);
|
||
sll.sll_ifindex = ifx;
|
||
if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)))
|
||
esfatal(1, "bind to %s failed", ifcname);
|
||
|
||
/* This code only works with Ethernet, so check for that */
|
||
memset(&ifr, 0, sizeof(ifr));
|
||
strncpy(ifr.ifr_name, ifcname, sizeof(ifr.ifr_name));
|
||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0 )
|
||
esfatal(1, "SIOCGIFHWADDR of %s failed", ifcname);
|
||
|
||
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
|
||
efatal(1, "%s is not an ethernet - ARPHRD type %d",
|
||
ifcname, ifr.ifr_hwaddr.sa_family);
|
||
|
||
/* Finally, attempt to determine current ethernet MAC address.
|
||
Assume above call returned it in sa_data.
|
||
*/
|
||
ea_set(&osnpf->osnpf_ea, &ifr.ifr_addr.sa_data[0]);
|
||
|
||
return fd;
|
||
}
|
||
|
||
#endif /* KLH10_NET_LNX */
|
||
|
||
#if KLH10_NET_NIT
|
||
|
||
/* NIT packetfilter initialization */
|
||
|
||
int
|
||
osn_pfinit(struct osnpf *osnpf, void *arg)
|
||
{
|
||
int fd;
|
||
struct strioctl si;
|
||
struct ifreq ifr;
|
||
|
||
/* Open NIT stream. We'll be doing both R and W. */
|
||
if ((fd = open("/dev/nit", O_RDWR)) < 0)
|
||
esfatal(1, "Couldn't open NIT");
|
||
|
||
/* Ensure that each read gives us one packet */
|
||
if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0)
|
||
esfatal(1, "I_SRDOPT failed");
|
||
|
||
/* Set up kernel filtering */
|
||
if (ioctl(fd, I_PUSH, "pf") < 0)
|
||
esfatal("I_PUSH pf failed");
|
||
|
||
/* Set up packet filter for it */
|
||
if (!osnpf->osnpf_dedic) {
|
||
struct OSN_PFSTRUCT *pf;
|
||
pf = pfbuild(arg, &osnpf->osnpf_ip.ia_addr);
|
||
si.ic_timout = INFTIM; /* Should this be osnpf_rdtmo? */
|
||
si.ic_cmd = NIOCSETF; /* Set packet filter */
|
||
si.ic_len = sizeof(*pf); /* XXX Unfortunate dependency */
|
||
si.ic_dp = (char *)pf;
|
||
if (ioctl(fd, I_STR, (char *)&si) < 0)
|
||
esfatal(1, "NIOCSETF failed");
|
||
}
|
||
|
||
/* Finally, bind to proper device and flush anything accumulated */
|
||
strncpy(ifr.ifr_name, osnpf->osnpf_ifnam, sizeof(ifr.ifr_name));
|
||
si.ic_cmd = NIOCBIND;
|
||
si.ic_len = sizeof(ifr);
|
||
si.ic_dp = (char *)𝔦
|
||
if (ioctl(fd, I_STR, (char *)&si) < 0)
|
||
esfatal(1, "NIOCBIND failed");
|
||
|
||
if (ioctl(fd, I_FLUSH, (char *)FLUSHR) < 0)
|
||
esfatal(1, "I_FLUSH failed");
|
||
|
||
/* Get our ethernet address.
|
||
** This can't be done until after the NIT is open and bound.
|
||
*/
|
||
(void) osn_pfeaget(fd, osnpf->osnpf_ifnam, &(osnpf->osnpf_ea));
|
||
|
||
/* Ready to roll! */
|
||
return fd;
|
||
}
|
||
#endif /* KLH10_NET_NIT */
|
||
|
||
/*
|
||
* Too bad that this is never called...
|
||
*/
|
||
osn_pfdeinit()
|
||
{
|
||
#if KLH10_NET_TAP_BRIDGE
|
||
void tap_bridge_close();
|
||
tap_bridge_close();
|
||
#endif
|
||
}
|
||
|
||
#if KLH10_NET_TAP_BRIDGE
|
||
|
||
osn_pfinit(register struct osnpf *osnpf, void *arg)
|
||
{
|
||
int fd;
|
||
char *ifnam = osnpf->osnpf_ifnam;
|
||
|
||
/* No "default interface" concept here */
|
||
if (!ifnam || !ifnam[0])
|
||
esfatal(1, "Packetfilter interface must be specified");
|
||
|
||
fd = tap_bridge_open(ifnam);
|
||
|
||
/* Now get our fresh new virtual interface's ethernet address.
|
||
*/
|
||
(void) osn_pfeaget(fd, ifnam, (unsigned char *)&(osnpf->osnpf_ea));
|
||
|
||
return fd;
|
||
}
|
||
|
||
#include <net/if_tap.h>
|
||
#include <net/if_bridgevar.h>
|
||
#include <stdint.h>
|
||
|
||
static struct ifreq br_ifr;
|
||
static struct ifreq tap_ifr;
|
||
static int my_tap;
|
||
|
||
/*
|
||
* A TAP is a virtual ethernet interface, much like TUN is a virtual IP
|
||
* interface. We can use it to inject packets into the Unix input stream,
|
||
* provided it is UP and the host side has a matching IP address and
|
||
* netmask (also much like TUN), or that it is bridged to another interface.
|
||
*
|
||
* Here we try to create the user-given interface and then bridge it to
|
||
* the "default" interface. This is probably the most common configuration.
|
||
* If something else is desired, the user can set up the tap herself,
|
||
* and we'll just use it as it is. This is useful for a routed approach,
|
||
* for instance.
|
||
*/
|
||
int
|
||
tap_bridge_open(char *ifnam)
|
||
{
|
||
int tapfd;
|
||
int res;
|
||
union ipaddr netmask;
|
||
char cmdbuff[128];
|
||
struct ifent *ife;
|
||
int s;
|
||
int i;
|
||
struct ifbreq br_req;
|
||
struct ifdrv br_ifd;
|
||
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
esfatal(1, "tap_bridge_open: socket() failed");
|
||
}
|
||
|
||
/* try to create tapN as specified by the user */
|
||
memset(&tap_ifr, 0, sizeof(tap_ifr));
|
||
strcpy(tap_ifr.ifr_name, ifnam);
|
||
res = ioctl(s, SIOCIFCREATE, &tap_ifr);
|
||
if (res == 0) {
|
||
my_tap = 1;
|
||
dbprintln("Created host-side tap \"%s\"", ifnam);
|
||
} else {
|
||
if (errno != EEXIST)
|
||
esfatal(1, "tap_bridge_open: can't create tap \"%s\"?", ifnam);
|
||
my_tap = 0;
|
||
dbprintln("Host-side tap \"%s\" alread exists; use it as-is", ifnam);
|
||
}
|
||
|
||
sprintf(cmdbuff, "/dev/%s", ifnam);
|
||
tapfd = open(cmdbuff, O_RDWR, 0);
|
||
|
||
if (tapfd < 0) {
|
||
/* Note possible error meanings:
|
||
ENOENT - no such filename
|
||
ENXIO - not configured in kernel
|
||
*/
|
||
esfatal(1, "Couldn't find or open 10-side tap \"%s\"", cmdbuff);
|
||
}
|
||
|
||
dbprintln("Opened 10-side tap \"%s\"", cmdbuff);
|
||
|
||
/* Finally, turn on IFF_UP just in case the above didn't do it.
|
||
Note interface name is still there from the SIOCIFCREATE.
|
||
*/
|
||
if (ioctl(s, SIOCGIFFLAGS, &tap_ifr) < 0) {
|
||
esfatal(1, "tap_bridge_open tap SIOCGIFFLAGS failed");
|
||
}
|
||
if (!(tap_ifr.ifr_flags & IFF_UP)) {
|
||
tap_ifr.ifr_flags |= IFF_UP;
|
||
if (ioctl(s, SIOCSIFFLAGS, &tap_ifr) < 0) {
|
||
esfatal(1, "tap_bridge_open tap SIOCSIFFLAGS failed");
|
||
}
|
||
if (DP_DBGFLG)
|
||
dbprint("tap_bridge_open tap did SIOCSIFFLAGS");
|
||
}
|
||
|
||
if (my_tap) {
|
||
for (i = 0; i < 1000; i++) {
|
||
/* try to create bridge%d */
|
||
memset(&br_ifr, 0, sizeof(br_ifr));
|
||
sprintf(br_ifr.ifr_name, "bridge%d", i);
|
||
res = ioctl(s, SIOCIFCREATE, &br_ifr);
|
||
if (res == 0)
|
||
break;
|
||
if (errno != EEXIST)
|
||
esfatal(1, "tap_bridge_open: can't create bridge \"%s\"?", br_ifr.ifr_name);
|
||
}
|
||
dbprintln("Created bridge \"%s\"", br_ifr.ifr_name);
|
||
|
||
/*
|
||
* Find default IP interface to bridge with.
|
||
* It might find the wrong one if there is more than one.
|
||
*/
|
||
|
||
ife = osn_ipdefault();
|
||
if (!ife)
|
||
esfatal(0, "Couldn't find default interface");
|
||
|
||
if (swstatus)
|
||
dbprintln("Bridging with default interface \"%s\"", ife->ife_name);
|
||
|
||
if (1) {
|
||
sprintf(cmdbuff, "/sbin/brconfig %s add %s add %s up",
|
||
br_ifr.ifr_name, ife->ife_name, ifnam);
|
||
res = system(cmdbuff);
|
||
dbprintln("%s => %d", cmdbuff, res);
|
||
} else {
|
||
/* do whatever brconfig bridge0 add intf0 does... */
|
||
memset(&br_ifd, 0, sizeof(br_ifd));
|
||
memset(&br_req, 0, sizeof(br_req));
|
||
|
||
/* set name of the bridge */
|
||
strcpy(br_ifd.ifd_name, br_ifr.ifr_name);
|
||
br_ifd.ifd_cmd = BRDGADD;
|
||
br_ifd.ifd_len = sizeof(br_req);
|
||
br_ifd.ifd_data = &br_req;
|
||
|
||
/* brconfig bridge0 add tap0 (the virtual interface) */
|
||
strcpy(br_req.ifbr_ifsname, ifnam);
|
||
res = ioctl(s, SIOCSDRVSPEC, &br_ifd);
|
||
if (res == -1)
|
||
esfatal(1, "tap_bridge_open: can't add virtual intf to bridge?");
|
||
|
||
/* brconfig bridge0 add vr0 (the hardware interface) */
|
||
strcpy(br_req.ifbr_ifsname, ife->ife_name);
|
||
res = ioctl(s, SIOCSDRVSPEC, &br_ifd);
|
||
if (res == -1)
|
||
esfatal(1, "tap_bridge_open: can't add real intf to bridge?");
|
||
|
||
/* Finally, turn on IFF_UP just in case the above didn't do it.
|
||
* Note interface name is still there.
|
||
*/
|
||
if (ioctl(s, SIOCGIFFLAGS, &br_ifr) < 0) {
|
||
esfatal(1, "tap_bridge_open bridge SIOCGIFFLAGS failed");
|
||
}
|
||
if (!(br_ifr.ifr_flags & IFF_UP)) {
|
||
br_ifr.ifr_flags |= IFF_UP;
|
||
if (ioctl(s, SIOCSIFFLAGS, &br_ifr) < 0) {
|
||
esfatal(1, "tap_bridge_open bridge SIOCSIFFLAGS failed");
|
||
}
|
||
if (DP_DBGFLG)
|
||
dbprint("tap_bridge_open bridge did SIOCSIFFLAGS");
|
||
}
|
||
|
||
}
|
||
}
|
||
close(s);
|
||
|
||
return tapfd; /* Success! */
|
||
}
|
||
|
||
void
|
||
tap_bridge_close()
|
||
{
|
||
if (my_tap) {
|
||
int s, res;
|
||
struct ifreq tap_ifr;
|
||
|
||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||
esfatal(1, "tap_bridge_close: socket() failed");
|
||
}
|
||
|
||
/* Destroy bridge */
|
||
res = ioctl(s, SIOCIFDESTROY, &br_ifr);
|
||
res = ioctl(s, SIOCIFDESTROY, &tap_ifr);
|
||
|
||
close(s);
|
||
}
|
||
}
|
||
|
||
#endif /* KLH10_NET_TAP_BRIDGE */
|
||
|
||
#if KLH10_NET_DLPI
|
||
|
||
/* DLPI packetfilter initialization */
|
||
|
||
#define MAXSIZE (32 * 1024) /* Whuffo? */
|
||
#define MAXADDR 1024
|
||
#define MAXCTLBUF (sizeof (dl_unitdata_req_t) + sizeof (long) + MAXADDR)
|
||
#define MAXDLBUF 8192 /* Maybe just MAXCTLBUF */
|
||
|
||
struct ledladdr {
|
||
struct ether_addr dl_phys;
|
||
unsigned short dl_sap;
|
||
};
|
||
|
||
struct dlpictx {
|
||
int dc_fd;
|
||
struct strbuf dc_ctl;
|
||
char dc_ebuf[200]; /* Error output */
|
||
long dc_buf[MAXDLBUF];
|
||
};
|
||
|
||
/* Fake SAP must be above 1500 to avoid 802.3 interpretation, but
|
||
** might be dangerous if it were 2048 which is IP (try that later, tho)
|
||
*/
|
||
#define FAKESAP 2049
|
||
|
||
#if OSN_USE_IPONLY
|
||
static int dlfastpathon(struct dlpictx *dc, int sap);
|
||
#endif
|
||
|
||
static int strioctl(int fd, int cmd, int len, char *dp);
|
||
static int dl_sendreq(struct dlpictx *dc, char *ptr, int len, char *what);
|
||
static int strgetmsg(struct dlpictx *dc, char *caller, struct strbuf *datap,
|
||
int *flagsp);
|
||
static int dlattachreq(struct dlpictx *dc, long ppa);
|
||
static int dlokack(struct dlpictx *dc);
|
||
static int dlbindreq(struct dlpictx *dc, u_long sap, u_long max_conind,
|
||
unsigned short service_mode, unsigned short conn_mgmt);
|
||
static int dlbindack(struct dlpictx *dc);
|
||
static int dlpromisconreq(struct dlpictx *dc, u_long level);
|
||
static int dladdrreq(struct dlpictx *dc, int type);
|
||
static int dladdrack(struct dlpictx *dc, unsigned char *addr);
|
||
static int dlsetaddrreq(struct dlpictx *dc, unsigned char *addr);
|
||
#if 0
|
||
static int dlpromiscoff(struct dlpictx *dc);
|
||
static int dlinforeq(struct dlpictx *dc);
|
||
static int dlinfoack(struct dlpictx *dc);
|
||
static int dlenabmulti(struct dlpictx *dc, char *addrp, int len);
|
||
static int dldisabmulti(struct dlpictx *dc, char *addrp, int len);
|
||
static int dldetachreq(struct dlpictx *dc);
|
||
static int dlunbindreq(struct dlpictx *dc);
|
||
#endif
|
||
|
||
|
||
int osn_pfinit(struct osnpf *osnpf, void *arg)
|
||
{
|
||
int fd;
|
||
int ppa;
|
||
char *cp;
|
||
unsigned char curea[ETHER_ADRSIZ];
|
||
char devpath[IFNAMSIZ+40];
|
||
char eastr[OSN_EASTRSIZ];
|
||
unsigned char *ucp;
|
||
struct dlpictx dc;
|
||
|
||
/* Figure out right name of device to use.
|
||
** Munch device spec, eg "hmeNN" becomes /dev/hme, unit NN.
|
||
** Code formerly allowed full pathname as device spec, but no longer.
|
||
*/
|
||
if (osnpf->osnpf_ifnam[0] == 0)
|
||
efatal(1, "Ethernet DLPI interface must be specified");
|
||
strcpy(devpath, "/dev/");
|
||
strcat(devpath, osnpf->osnpf_ifnam); /* Buffer always big enough */
|
||
ppa = 0; /* Assume unit #0 */
|
||
cp = &devpath[strlen(devpath)-1]; /* Point to end of string */
|
||
if (isdigit(*cp)) { /* Go to start of unit digits */
|
||
while (isdigit(*--cp)); /* (code safe cuz of /dev/ prefix) */
|
||
ppa = atoi(++cp);
|
||
*cp = '\0'; /* Chop off unit digits */
|
||
}
|
||
|
||
/* Open device. We'll be doing both R and W. */
|
||
if ((fd = open(devpath, O_RDWR)) < 0) {
|
||
esfatal(1, "Couldn't open DLPI packetfilter for ifc \"%s\" (%s)",
|
||
devpath, osnpf->osnpf_ifnam);
|
||
}
|
||
memset((void *)&dc, 0, sizeof(dc));
|
||
dc.dc_fd = fd;
|
||
|
||
/* Attach to specific unit */
|
||
if (dlattachreq(&dc, (long)ppa)
|
||
|| dlokack(&dc))
|
||
efatal(1, dc.dc_ebuf);
|
||
|
||
/* Bind */
|
||
#if OSN_USE_IPONLY /* Note using IP SAP */
|
||
if (dlbindreq(&dc, 0x800, 0, DL_CLDLS, 0)
|
||
#else /* Note using fake SAP to avoid Solaris lossage */
|
||
if (dlbindreq(&dc, FAKESAP, 0, DL_CLDLS, 0)
|
||
#endif
|
||
|| dlbindack(&dc))
|
||
efatal(1, dc.dc_ebuf);
|
||
|
||
#if OSN_USE_IPONLY /* Apparently only needed for this */
|
||
/* Do stuff for "fastpath" which may be needed to allow header to
|
||
be included in data buffer rather than separate control struct.
|
||
*/
|
||
if (dlfastpathon(&dc, 0) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
#elif 0 /* !OSN_USE_IPONLY */ /* Apparently not needed */
|
||
if (dlfastpathon(&dc, FAKESAP) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
#endif
|
||
|
||
/* Set up various mode & flag stuff */
|
||
|
||
/* Do input side of DLPI stream */
|
||
|
||
#if CENV_SYS_SOLARIS
|
||
/* Turn on raw receive path, so that ethernet header is included in data.
|
||
** This is a special Solaris frob.
|
||
*/
|
||
if (strioctl(fd, DLIOCRAW, 0, NULL) < 0) {
|
||
esfatal(1, "DLIOCRAW ioctl");
|
||
}
|
||
#endif
|
||
|
||
#if !OSN_USE_IPONLY
|
||
/* Don't need this hackery if only want IP packets */
|
||
/* Enable receiving all packets, all SAPs (ethernet header type values) */
|
||
|
||
# if CENV_SYS_SOLARIS
|
||
/* Enable promiscuous mode. On Solaris, this is required in order
|
||
** for ALLSAP to work!!! Gross bletcherous misfeatured bug!!!
|
||
*/
|
||
if ((dlpromisconreq(&dc, DL_PROMISC_PHYS) < 0)
|
||
|| dlokack(&dc) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
# endif
|
||
/* Evidently must explicitly ask for promiscuous SAPs */
|
||
if (dlpromisconreq(&dc, DL_PROMISC_SAP) < 0
|
||
|| dlokack(&dc) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
/* And multicast too!? To quote tcpdump,
|
||
** "you would have thought promiscuous would be sufficient"
|
||
*/
|
||
if (dlpromisconreq(&dc, DL_PROMISC_MULTI) < 0
|
||
|| dlokack(&dc) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
#endif /* !OSN_USE_IPONLY */
|
||
|
||
/* Find the physical ethernet address of the interface we got.
|
||
** Do it now, because it may be needed in order to set up the
|
||
** correct packet filter (sigh).
|
||
*/
|
||
if (dladdrreq(&dc, DL_CURR_PHYS_ADDR) < 0
|
||
|| dladdrack(&dc, (unsigned char *)curea) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
|
||
/* HACK HACK -- see if ethernet addr already given, and if so,
|
||
** try to set it if different.
|
||
*/
|
||
if (!ea_isclr(&osnpf->osnpf_ea)
|
||
&& (ea_cmp(&osnpf->osnpf_ea, curea) != 0)) {
|
||
char old[OSN_EASTRSIZ];
|
||
char new[OSN_EASTRSIZ];
|
||
|
||
/* Attempt to set our EN addr */
|
||
eth_adrsprint(old, (unsigned char *)curea);
|
||
eth_adrsprint(new, (unsigned char *)&osnpf->osnpf_ea);
|
||
|
||
if (dlsetaddrreq(&dc, (unsigned char *)&osnpf->osnpf_ea) < 0
|
||
|| dlokack(&dc) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
|
||
/* Double-check by fetching new addr again and using it */
|
||
if (dladdrreq(&dc, DL_CURR_PHYS_ADDR) < 0
|
||
|| dladdrack(&dc, (unsigned char *)curea) < 0)
|
||
efatal(1, dc.dc_ebuf);
|
||
|
||
if (ea_cmp(&osnpf->osnpf_ea, curea) == 0) {
|
||
dbprintln("\"%s\" E/N addr changed: Old=%s New=%s",
|
||
osnpf->osnpf_ifnam, old, new);
|
||
} else {
|
||
dbprintln("\"%s\" E/N addr change failed, Old=%s New=%s",
|
||
osnpf->osnpf_ifnam, old, new);
|
||
}
|
||
}
|
||
ea_set(&osnpf->osnpf_ea, curea);
|
||
|
||
#if 0
|
||
/* Ensure that each read gives us one packet.
|
||
** Shouldn't matter since we use getmsg(), but code left here in case.
|
||
*/
|
||
if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0)
|
||
esfatal(1, "I_SRDOPT failed");
|
||
#endif
|
||
|
||
/* Set up packet filter for it - should only be needed if
|
||
** sharing interface, but on Solaris we may be always in promiscuous
|
||
** mode!
|
||
*/
|
||
#if !CENV_SYS_SOLARIS
|
||
if (!osnpf->osnpf_dedic)
|
||
#endif
|
||
{
|
||
struct OSN_PFSTRUCT *pf;
|
||
|
||
if (ioctl(fd, I_PUSH, "pfmod") < 0)
|
||
esfatal(1, "PUSH of pfmod failed");
|
||
|
||
#if !OSN_USE_IPONLY
|
||
if (osnpf->osnpf_dedic)
|
||
/* Filter on our ether addr */
|
||
pf = pfeabuild(arg, (unsigned char *)&osnpf->osnpf_ea);
|
||
else
|
||
#endif
|
||
/* Filter on our IP addr */
|
||
pf = pfbuild(arg, &osnpf->osnpf_ip.ia_addr);
|
||
|
||
if (strioctl(dc.dc_fd, PFIOCSETF, sizeof(*pf), (char *)pf) < 0)
|
||
esfatal(1, "PFIOCSETF failed");
|
||
}
|
||
|
||
/* Needed? Flush read side to ensure clear of anything accumulated */
|
||
if (ioctl(fd, I_FLUSH, (char *)FLUSHR) < 0)
|
||
esfatal(1, "I_FLUSH failed");
|
||
|
||
/* Ready to roll! */
|
||
return fd;
|
||
}
|
||
|
||
/* Handy auxiliary to pick up EA address given the PF FD, in case
|
||
it's needed again after osn_pfinit is done.
|
||
*/
|
||
static int
|
||
dleaget(int fd, unsigned char *eap)
|
||
{
|
||
struct dlpictx dc;
|
||
|
||
dc.dc_fd = fd;
|
||
if ((dladdrreq(&dc, DL_CURR_PHYS_ADDR) < 0)
|
||
|| (dladdrack(&dc, (unsigned char *)eap) < 0)) {
|
||
error(dc.dc_ebuf);
|
||
ea_clr(eap);
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
#if OSN_USE_IPONLY /* Apparently only needed for this */
|
||
static int
|
||
dlfastpathon(struct dlpictx *dc, int sap)
|
||
{
|
||
dl_unitdata_req_t *req;
|
||
struct ledladdr {
|
||
struct ether_addr dl_phys;
|
||
unsigned short dl_sap;
|
||
} *dladdrp;
|
||
int n;
|
||
|
||
/* Construct DL_UNITDATA_REQ primitive. */
|
||
req = (dl_unitdata_req_t *) dc->dc_buf;
|
||
req->dl_primitive = DL_UNITDATA_REQ;
|
||
req->dl_dest_addr_length = sizeof (short) + ETHER_ADRSIZ;
|
||
req->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
|
||
req->dl_priority.dl_min = 0;
|
||
req->dl_priority.dl_max = 0;
|
||
|
||
/* Set up addr - instead of a specific dest address, just clear it out */
|
||
dladdrp = (struct ledladdr *)
|
||
(((char *)req) + req->dl_dest_addr_offset);
|
||
dladdrp->dl_sap = sap;
|
||
memset(&dladdrp->dl_phys, 0, ETHER_ADRSIZ);
|
||
|
||
if ((n = strioctl(dc->dc_fd, DL_IOC_HDR_INFO,
|
||
sizeof(*req)+sizeof(*dladdrp),
|
||
(char *)req)) < 0) {
|
||
esfatal(1, "DL_IOC_HDR_INFO ioctl failed");
|
||
}
|
||
return n;
|
||
}
|
||
#endif /* OSN_USE_IPONLY */
|
||
|
||
|
||
/* Derived variously from TCPDUMP and Sun's ugly DLTX sample program
|
||
*/
|
||
|
||
static int
|
||
strioctl(int fd, int cmd, int len, char *dp)
|
||
{
|
||
struct strioctl str;
|
||
int rc;
|
||
|
||
str.ic_cmd = cmd;
|
||
str.ic_timout = INFTIM;
|
||
str.ic_len = len;
|
||
str.ic_dp = dp;
|
||
rc = ioctl(fd, I_STR, &str);
|
||
|
||
return (rc < 0) ? rc : str.ic_len;
|
||
}
|
||
|
||
static int
|
||
dl_sendreq(struct dlpictx *dc, char *ptr, int len, char *what)
|
||
{
|
||
struct strbuf ctl;
|
||
int flags = 0;
|
||
|
||
ctl.maxlen = 0;
|
||
ctl.len = len;
|
||
ctl.buf = ptr;
|
||
|
||
if (putmsg(dc->dc_fd, &ctl, (struct strbuf *) NULL, flags) < 0) {
|
||
sprintf(dc->dc_ebuf, "%s putmsg failed: %s",
|
||
what, dp_strerror(errno));
|
||
return (-1);
|
||
}
|
||
return (0);
|
||
}
|
||
|
||
#if OSN_USE_IPONLY
|
||
# define DL_MAXWAIT 0
|
||
#else
|
||
# define DL_MAXWAIT 15 /* secs to wait for ACK msg */
|
||
#endif
|
||
|
||
#if DL_MAXWAIT
|
||
|
||
#include <signal.h>
|
||
|
||
static void
|
||
sigalrm(s)
|
||
{
|
||
}
|
||
#endif
|
||
|
||
static int
|
||
strgetmsg(struct dlpictx *dc,
|
||
char *caller,
|
||
struct strbuf *datap,
|
||
int *flagsp)
|
||
{
|
||
int rc;
|
||
int flags = 0;
|
||
|
||
dc->dc_ctl.maxlen = MAXDLBUF;
|
||
dc->dc_ctl.len = 0;
|
||
dc->dc_ctl.buf = (char *)dc->dc_buf;
|
||
if (flagsp)
|
||
*flagsp = 0;
|
||
else
|
||
flagsp = &flags;
|
||
|
||
#if DL_MAXWAIT
|
||
signal(SIGALRM, sigalrm);
|
||
if (alarm(DL_MAXWAIT) < 0) {
|
||
esfatal(1, "%s: alarm set", caller);
|
||
}
|
||
#endif
|
||
|
||
/* Get expected message */
|
||
if ((rc = getmsg(dc->dc_fd, &dc->dc_ctl, datap, flagsp)) < 0) {
|
||
sprintf(dc->dc_ebuf, "%s: getmsg - %s",
|
||
caller, dp_strerror(errno));
|
||
return -1;
|
||
}
|
||
|
||
#if DL_MAXWAIT
|
||
if (alarm(0) < 0) {
|
||
esfatal(1, "%s: alarm clear", caller);
|
||
}
|
||
#endif
|
||
|
||
/* Got it - now do paranoia check */
|
||
if (rc & (MORECTL | MOREDATA)) {
|
||
sprintf(dc->dc_ebuf, "%s: getmsg returned%s%s",
|
||
caller,
|
||
((rc & MORECTL) ? " MORECTL" : ""),
|
||
((rc & MOREDATA) ? " MOREDATA" : ""));
|
||
return -1;
|
||
}
|
||
|
||
/* Yet more paranoia - control info should exist */
|
||
/* Take this out if all callers check length */
|
||
if (dc->dc_ctl.len < sizeof (long)) {
|
||
sprintf(dc->dc_ebuf, "%s: getmsg ctl.len too short: %d",
|
||
dc->dc_ctl.len);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
#undef DL_MAXWAIT
|
||
|
||
static int
|
||
dlattachreq(struct dlpictx *dc, long ppa)
|
||
{
|
||
dl_attach_req_t req;
|
||
|
||
req.dl_primitive = DL_ATTACH_REQ;
|
||
req.dl_ppa = ppa;
|
||
return dl_sendreq(dc, (char *)&req, sizeof(req), "dlattach");
|
||
}
|
||
|
||
static int
|
||
dlokack(struct dlpictx *dc)
|
||
{
|
||
union DL_primitives *dlp;
|
||
|
||
if (strgetmsg(dc, "dlokack", NULL, NULL) < 0)
|
||
return -1;
|
||
|
||
dlp = (union DL_primitives *) dc->dc_ctl.buf;
|
||
|
||
if (dlp->dl_primitive != DL_OK_ACK) {
|
||
sprintf(dc->dc_ebuf, "dlokack unexpected primitive %0lX",
|
||
(long)dlp->dl_primitive);
|
||
return -1;
|
||
#if 0
|
||
/* Possibly insert general error handler (DLTX) */
|
||
if (dlp->dl_primitive == DL_ERROR_ACK)
|
||
dlerror(dlp->error_ack.dl_errno);
|
||
else
|
||
printdlprim(dlp);
|
||
#endif
|
||
}
|
||
|
||
if (dc->dc_ctl.len != sizeof(dl_ok_ack_t)) {
|
||
sprintf(dc->dc_ebuf, "dlokack incorrect size %ld",
|
||
(long)dc->dc_ctl.len);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
dlbindreq(struct dlpictx *dc,
|
||
u_long sap,
|
||
u_long max_conind,
|
||
unsigned short service_mode,
|
||
unsigned short conn_mgmt)
|
||
{
|
||
dl_bind_req_t req;
|
||
|
||
req.dl_primitive = DL_BIND_REQ;
|
||
req.dl_sap = sap;
|
||
req.dl_max_conind = max_conind;
|
||
req.dl_service_mode = service_mode;
|
||
req.dl_conn_mgmt = conn_mgmt;
|
||
req.dl_xidtest_flg = 0;
|
||
|
||
return dl_sendreq(dc, (char *)&req, sizeof(req), "dlbind");
|
||
}
|
||
|
||
static int
|
||
dlbindack(struct dlpictx *dc)
|
||
{
|
||
union DL_primitives *dlp;
|
||
|
||
if (strgetmsg(dc, "dlbindack", NULL, NULL) < 0)
|
||
return -1;
|
||
|
||
dlp = (union DL_primitives *) dc->dc_ctl.buf;
|
||
|
||
if (dlp->dl_primitive != DL_BIND_ACK) {
|
||
sprintf(dc->dc_ebuf, "dlbindack unexpected response %0lX",
|
||
(long)dlp->dl_primitive);
|
||
return -1;
|
||
}
|
||
|
||
if (dc->dc_ctl.len < sizeof (dl_bind_ack_t)) {
|
||
sprintf(dc->dc_ebuf, "dlbindack: short response: %d",
|
||
dc->dc_ctl.len);
|
||
return -1;
|
||
}
|
||
#if 0 /* Don't understand this */
|
||
if (flags != RS_HIPRI) {
|
||
sprintf(dc->dc_ebuf, "dlbindack: DL_OK_ACK was not M_PCPROTO");
|
||
return -1;
|
||
}
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int
|
||
dlpromisconreq(struct dlpictx *dc, u_long level)
|
||
{
|
||
dl_promiscon_req_t req;
|
||
|
||
req.dl_primitive = DL_PROMISCON_REQ;
|
||
req.dl_level = level;
|
||
return dl_sendreq(dc, (char *)&req, sizeof(req), "dlpromiscon");
|
||
}
|
||
|
||
/*
|
||
* type arg: DL_FACT_PHYS_ADDR for "factory" address,
|
||
* DL_CURR_PHYS_ADDR for actual current address.
|
||
*/
|
||
static int
|
||
dladdrreq(struct dlpictx *dc, int type)
|
||
{
|
||
dl_phys_addr_req_t req;
|
||
|
||
req.dl_primitive = DL_PHYS_ADDR_REQ;
|
||
req.dl_addr_type = type; /* DL_{FACT,CURR}_PHYS_ADDR */
|
||
return dl_sendreq(dc, (char *)&req, sizeof(req), "dladdr");
|
||
}
|
||
|
||
static int
|
||
dladdrack(struct dlpictx *dc, unsigned char *addr)
|
||
{
|
||
union DL_primitives *dlp;
|
||
unsigned char *ucp;
|
||
int len;
|
||
|
||
if (strgetmsg(dc, "dladdrack", NULL, NULL) < 0)
|
||
return -1;
|
||
|
||
dlp = (union DL_primitives *) dc->dc_ctl.buf;
|
||
|
||
if (dlp->dl_primitive != DL_PHYS_ADDR_ACK) {
|
||
sprintf(dc->dc_ebuf, "dladdrack unexpected response %0lX",
|
||
(long)dlp->dl_primitive);
|
||
return -1;
|
||
}
|
||
if (dc->dc_ctl.len < sizeof (dl_phys_addr_ack_t)) {
|
||
sprintf(dc->dc_ebuf, "dladdrack: short response: %d",
|
||
dc->dc_ctl.len);
|
||
return -1;
|
||
}
|
||
|
||
/* Hardwired paranoia check, assume ethernet for now */
|
||
len = dlp->physaddr_ack.dl_addr_length;
|
||
if (len != ETHER_ADRSIZ) {
|
||
sprintf(dc->dc_ebuf, "dladdrack: non-Ether addr len %d", len);
|
||
return (-1);
|
||
}
|
||
ucp = ((unsigned char *)dlp) + dlp->physaddr_ack.dl_addr_offset;
|
||
memcpy(addr, ucp, (size_t)len);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
dlsetaddrreq(struct dlpictx *dc, unsigned char *addr)
|
||
{
|
||
/* Request is bigger than struct def; overlay it in buffer */
|
||
dl_set_phys_addr_req_t *req = (dl_set_phys_addr_req_t *)dc->dc_buf;
|
||
|
||
req->dl_primitive = DL_SET_PHYS_ADDR_REQ;
|
||
req->dl_addr_length = ETHER_ADRSIZ;
|
||
req->dl_addr_offset = sizeof(dl_set_phys_addr_req_t);
|
||
memcpy(&dc->dc_buf[req->dl_addr_offset], addr, ETHER_ADRSIZ);
|
||
|
||
return dl_sendreq(dc, (char *)req, sizeof(*req) + ETHER_ADRSIZ,
|
||
"dlsetaddr");
|
||
}
|
||
|
||
#endif /* KLH10_NET_DLPI */
|
||
|
||
#if 0 /* KLH10_NET_DLPI */
|
||
|
||
/* DLPI functions not currently used, but kept around in case
|
||
*/
|
||
|
||
/* Turn off promiscuous mode
|
||
*/
|
||
static int
|
||
dlpromiscoff(struct dlpictx *dc)
|
||
{
|
||
dl_promiscoff_req_t req;
|
||
|
||
req.dl_primitive = DL_PROMISCOFF_REQ;
|
||
return dl_sendreq(dc, (char *)&req, sizeof(req), "dlpromiscoff");
|
||
}
|
||
|
||
|
||
static int
|
||
dlinforeq(struct dlpictx *dc)
|
||
{
|
||
dl_info_req_t req;
|
||
|
||
req.dl_primitive = DL_INFO_REQ;
|
||
return (dl_sendreq(dc, (char *)&req, sizeof(req), "info"));
|
||
}
|
||
|
||
static int
|
||
dlinfoack(struct dlpictx *dc)
|
||
{
|
||
union DL_primitives *dlp;
|
||
|
||
if (strgetmsg(dc, "dlinfoack", NULL, NULL) < 0)
|
||
return -1;
|
||
|
||
dlp = (union DL_primitives *) dc->dc_ctl.buf;
|
||
if (dlp->dl_primitive != DL_INFO_ACK) {
|
||
sprintf(dc->dc_ebuf, "dlinfoack unexpected primitive %ld",
|
||
(long)dlp->dl_primitive);
|
||
return -1;
|
||
}
|
||
|
||
/* Extra stuff like the broadcast address can be returned */
|
||
if (dc->dc_ctl.len < DL_INFO_ACK_SIZE) {
|
||
sprintf(dc->dc_ebuf, "dlinfoack: incorrect size %ld",
|
||
(long)dc->dc_ctl.len);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
dlenabmulti(struct dlpictx *dc,
|
||
char *addrp,
|
||
int len)
|
||
{
|
||
dl_enabmulti_req_t *req;
|
||
|
||
req = (dl_enabmulti_req_t *) dc->dc_buf;
|
||
req->dl_primitive = DL_ENABMULTI_REQ;
|
||
req->dl_addr_length = len;
|
||
req->dl_addr_offset = sizeof (dl_enabmulti_req_t);
|
||
memcpy((dc->dc_buf + sizeof(dl_enabmulti_req_t)), addrp, len);
|
||
|
||
return (dl_sendreq(dc, (char *)req, sizeof(*req)+len, "dlenabmulti"));
|
||
}
|
||
|
||
static int
|
||
dldisabmulti(struct dlpictx *dc,
|
||
char *addrp,
|
||
int len)
|
||
{
|
||
dl_disabmulti_req_t *req;
|
||
|
||
req = (dl_disabmulti_req_t *) dc->dc_buf;
|
||
req->dl_primitive = DL_DISABMULTI_REQ;
|
||
req->dl_addr_length = len;
|
||
req->dl_addr_offset = sizeof (dl_disabmulti_req_t);
|
||
memcpy((dc->dc_buf + sizeof(dl_disabmulti_req_t)), addrp, len);
|
||
|
||
return (dl_sendreq(dc, (char *)req, sizeof(*req)+len, "dldisabmulti"));
|
||
}
|
||
|
||
static int
|
||
dldetachreq(struct dlpictx *dc)
|
||
{
|
||
dl_detach_req_t req;
|
||
|
||
req.dl_primitive = DL_DETACH_REQ;
|
||
return (dl_sendreq(dc, (char *)&req, sizeof(req), "dldetach"));
|
||
}
|
||
|
||
static int
|
||
dlunbindreq(struct dlpictx *dc)
|
||
{
|
||
dl_unbind_req_t req;
|
||
|
||
req.dl_primitive = DL_UNBIND_REQ;
|
||
return (dl_sendreq(dc, (char *)&req, sizeof(req), "dlunbind"));
|
||
}
|
||
|
||
#endif /* KLH10_NET_DLPI */
|
||
|
||
/* Auxiliary OSDNET.C stuff stops here */
|
||
|
||
#if 0 /* Limit of included code so far */
|
||
|
||
/* Both need to dump packets out
|
||
*/
|
||
void
|
||
dumppkt(unsigned char *ucp, int cnt)
|
||
{
|
||
int i;
|
||
|
||
while (cnt > 0) {
|
||
for (i = 8; --i >= 0 && cnt > 0;) {
|
||
if (--cnt >= 0)
|
||
fprintf(stderr, " %02x", *ucp++);
|
||
if (--cnt >= 0)
|
||
fprintf(stderr, "%02x", *ucp++);
|
||
}
|
||
fprintf(stderr, "\r\n");
|
||
}
|
||
}
|
||
|
||
/* Merge DPNI20's arp_myreply() with DPIMP's arp_req() ??
|
||
But one sends out a reply, the other a request...
|
||
*/
|
||
|
||
/* There was an ether_write() for NIT unused in dpni20.c
|
||
whereas dpimp.c still uses it for everything.
|
||
*/
|
||
|
||
#endif /* if 0 - still-excluded code */
|