1
0
mirror of https://github.com/Interlisp/maiko.git synced 2026-01-16 00:04:59 +00:00
2021-01-03 18:30:04 +00:00

642 lines
13 KiB
C

/* $Id: dlpi.c,v 1.3 2001/12/24 01:09:00 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved */
#include "os.h"
#ifdef USE_DLPI
/*
* dpli.c - routines for messing with the Data Link Provider Interface.
*
* The code in this module is based in large part (especially the dl*
* routines) on the example code provided with the document "How to Use
* DLPI", by Neal Nuckolls of Sun Internet Engineering. Gotta give credit
* where credit is due. If it weren't for Neal's excellent document,
* this module just plain wouldn't exist.
*
* David A. Curry
* Purdue University
* Engineering Computer Network
* 1285 Electrical Engineering Building
* West Lafayette, IN 47907-1285
* davy@ecn.purdue.edu
*
* $Log: dlpi.c,v $
* Revision 1.3 2001/12/24 01:09:00 sybalsky
* past changes
*
* Revision 1.2 1999/01/03 02:06:54 sybalsky
* Add ID comments / static to files for CVS use
*
* Revision 1.1.1.1 1998/12/17 05:03:20 sybalsky
* Import of Medley 3.5 emulator
*
* Revision 4.1 1993/09/15 20:50:44 davy
* GCC fixes from Guy Harris.
*
* Revision 4.1 1993/09/15 20:50:44 davy
* GCC fixes from Guy Harris.
*
* Revision 4.0 1993/03/01 19:59:00 davy
* NFSWATCH Version 4.0.
*
* Revision 1.5 1993/02/19 19:54:36 davy
* Another change in hopes of making things work on SVR4.
*
* Revision 1.4 1993/01/26 13:19:05 davy
* Fixed a goof in passing buffer size.
*
* Revision 1.3 1993/01/26 13:18:39 davy
* Added ifdef's to make it work on DLPI 1.3.
*
* Revision 1.2 1993/01/15 19:33:39 davy
* Miscellaneous cleanups.
*
* Revision 1.1 1993/01/15 15:42:32 davy
* Initial revision
*
*/
#include <sys/param.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#ifdef OS5
#include <sys/bufmod.h>
#include <unistd.h>
#include <stdlib.h>
#include <stropts.h>
#include <malloc.h>
#endif
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include "nfswatch.h"
static void dlbindreq();
static void dlinforeq();
static void dlattachreq();
static void dlpromisconreq();
static void sigalrm();
static int dlokack();
static int dlinfoack();
static int dlbindack();
static int expecting();
static int strgetmsg();
static char *savestr();
static char *pname;
extern unsigned char ether_host[6];
int truncation = 1500;
/*
* setup_dlpi_dev - set up the data link provider interface.
*/
int setup_dlpi_dev(char *device)
{
char *p;
u_int chunksz;
char cbuf[BUFSIZ];
struct ifconf ifc;
struct ifreq *ifrp;
struct strioctl si;
char devname[BUFSIZ];
int n, s, fd, devppa;
struct timeval timeout;
long buf[DLPI_MAXDLBUF];
/*
* If the interface device was not specified,
* get the default one.
*/
if ((device == NULL) || *device == NULL) {
/*
* Grab a socket.
*/
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
return (-1);
}
ifc.ifc_buf = cbuf;
ifc.ifc_len = sizeof(cbuf);
/*
* See what devices we've got.
*/
if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
perror("ioctl: SIOCGIFCONF");
return (-1);
}
/*
* Take the first device we encounter.
*/
ifrp = ifc.ifc_req;
for (n = ifc.ifc_len / sizeof(struct ifreq); n > 0; n--, ifrp++) {
/*
* Skip the loopback interface.
*/
if (strcmp(ifrp->ifr_name, "lo0") == 0) continue;
device = savestr(ifrp->ifr_name);
break;
}
(void)close(s);
}
/*
* Split the device name into type and unit number.
*/
if ((p = strpbrk(device, "0123456789")) == NULL) return (-1);
strcpy(devname, DLPI_DEVDIR);
strncat(devname, device, p - device);
devppa = atoi(p);
/*
* Open the device.
*/
if ((fd = open(devname, O_RDWR)) < 0) {
if (errno == ENOENT || errno == ENXIO) return (-1);
/*perror(devname);*/
return (-1);
}
/*
* Attach to the device. If this fails, the device
* does not exist.
*/
dlattachreq(fd, devppa);
if (dlokack(fd, buf) < 0) {
close(fd);
return (-1);
}
/*
* Bind to the specific unit.
*/
dlbindreq(fd, 0x0600, 0, DL_CLDLS, 0, 0);
if (dlbindack(fd, buf) < 0) {
fprintf(stderr, "%s: dlbindack failed.\n", pname);
return (-1);
}
#ifdef OS5
/*
* We really want all types of packets. However, the SVR4 DLPI does
* not let you have the packet frame header, so we won't be able to
* distinguish protocol types. But SunOS5 gives you the DLIOCRAW
* ioctl to get the frame headers, so we can do this on SunOS5.
*/
dlpromisconreq(fd, DL_PROMISC_SAP);
if (dlokack(fd, buf) < 0) {
fprintf(stderr, "%s: DL_PROMISC_SAP failed.\n", pname);
return (-1);
}
dlpromisconreq(fd, DL_PROMISC_MULTI);
if (dlokack(fd, buf) < 0) {
fprintf(stderr, "%s: DL_PROMISC_MULTI failed.\n", pname);
return (-1);
}
/*
* We want raw packets with the packet frame header. But we can
* only get this in SunOS5 with the DLIOCRAW ioctl; it's not in
* standard SVR4.
*/
si.ic_cmd = DLIOCRAW;
si.ic_timout = -1;
si.ic_len = 0;
si.ic_dp = 0;
if (ioctl(fd, I_STR, &si) < 0) {
perror("ioctl: I_STR DLIOCRAW");
return (-1);
}
#endif /* OS5 */
/*
* Arrange to get discrete messages.
*/
if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) {
perror("ioctl: I_SRDOPT RMSGD");
return (-1);
}
#ifdef OS5
/*
* Push and configure the streams buffering module. This is once
* again SunOS-specific.
*/
#ifdef NEVER
if (ioctl(fd, I_PUSH, DLPI_BUFMOD) < 0) {
perror("ioctl: I_PUSH BUFMOD");
return (-1);
}
/*
* Set the read timeout.
*/
timeout.tv_sec = 1;
timeout.tv_usec = 0;
si.ic_cmd = SBIOCSTIME;
si.ic_timout = INFTIM;
si.ic_len = sizeof(timeout);
si.ic_dp = (char *)&timeout;
if (ioctl(fd, I_STR, (char *)&si) < 0) {
perror("ioctl: I_STR SBIOCSTIME");
return (-1);
}
/*
* Set the chunk size.
*/
chunksz = DLPI_CHUNKSIZE;
si.ic_cmd = SBIOCSCHUNK;
si.ic_len = sizeof(chunksz);
si.ic_dp = (char *)&chunksz;
if (ioctl(fd, I_STR, (char *)&si) < 0) {
perror("ioctl: I_STR SBIOCSCHUNK");
return (-1);
}
/*
* Set snapshot mode.
*/
si.ic_cmd = SBIOCSSNAP;
si.ic_len = sizeof(truncation);
si.ic_dp = (char *)&truncation;
if (ioctl(fd, I_STR, (char *)&si) < 0) {
perror("ioctl: I_STR SBIOCSSNAP");
return (-1);
}
#endif /* NEVER */
#endif /* OS5 */
return (fd);
}
/*
* flush_dlpi - flush data from the dlpi.
*/
void flush_dlpi(int fd)
{
if (ioctl(fd, I_FLUSH, (char *)FLUSHR) < 0) {
perror("ioctl: I_FLUSH");
return;
}
}
/*
* dlpi_devtype - determine the type of device we're looking at.
*/
int dlpi_devtype(int fd)
{
long buf[DLPI_MAXDLBUF];
union DL_primitives *dlp;
dlp = (union DL_primitives *)buf;
dlinforeq(fd);
if (dlinfoack(fd, buf) < 0) return (DLT_EN10MB);
bcopy((char *)dlp + dlp->info_ack.dl_addr_offset, ether_host, 6);
switch (dlp->info_ack.dl_mac_type) {
case DL_CSMACD:
case DL_ETHER: return (DLT_EN10MB);
#ifdef DL_FDDI
case DL_FDDI: return (DLT_FDDI);
#endif
default:
fprintf(stderr, "%s: DLPI MACtype %d unknown, ", pname, dlp->info_ack.dl_mac_type);
fprintf(stderr, "assuming ethernet.\n");
return (DLT_EN10MB);
}
}
/*
* dlinforeq - request information about the data link provider.
*/
static void dlinforeq(int fd)
{
dl_info_req_t info_req;
struct strbuf ctl;
int flags;
info_req.dl_primitive = DL_INFO_REQ;
ctl.maxlen = 0;
ctl.len = sizeof(info_req);
ctl.buf = (char *)&info_req;
flags = RS_HIPRI;
if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
perror("putmsg");
return;
}
}
/*
* dlattachreq - send a request to attach.
*/
static void dlattachreq(int fd, u_long ppa)
{
dl_attach_req_t attach_req;
struct strbuf ctl;
int flags;
attach_req.dl_primitive = DL_ATTACH_REQ;
attach_req.dl_ppa = ppa;
ctl.maxlen = 0;
ctl.len = sizeof(attach_req);
ctl.buf = (char *)&attach_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
perror("putmsg");
return;
}
}
#ifdef DL_PROMISCON_REQ
/*
* dlpromisconreq - send a request to turn promiscuous mode on.
*/
static void dlpromisconreq(int fd, u_long level)
{
dl_promiscon_req_t promiscon_req;
struct strbuf ctl;
int flags;
promiscon_req.dl_primitive = DL_PROMISCON_REQ;
promiscon_req.dl_level = level;
ctl.maxlen = 0;
ctl.len = sizeof(promiscon_req);
ctl.buf = (char *)&promiscon_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
perror("putmsg");
return;
}
}
#endif /* DL_PROMISCON_REQ */
/*
* dlbindreq - send a request to bind.
*/
static void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest)
{
dl_bind_req_t bind_req;
struct strbuf ctl;
int flags;
bind_req.dl_primitive = DL_BIND_REQ;
bind_req.dl_sap = sap;
bind_req.dl_max_conind = max_conind;
bind_req.dl_service_mode = service_mode;
bind_req.dl_conn_mgmt = conn_mgmt;
#ifdef DL_PROMISC_PHYS
/*
* DLPI 2.0 only?
*/
bind_req.dl_xidtest_flg = xidtest;
#endif
ctl.maxlen = 0;
ctl.len = sizeof(bind_req);
ctl.buf = (char *)&bind_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
perror("putmsg");
return;
}
}
/*
* dlokack - general acknowledgement reception.
*/
static int dlokack(int fd, char *bufp)
{
union DL_primitives *dlp;
struct strbuf ctl;
int flags;
ctl.maxlen = DLPI_MAXDLBUF;
ctl.len = 0;
ctl.buf = bufp;
if (strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlokack") < 0) return (-1);
dlp = (union DL_primitives *)ctl.buf;
if (expecting(DL_OK_ACK, dlp) < 0) return (-1);
if (ctl.len < sizeof(dl_ok_ack_t)) return (-1);
if (flags != RS_HIPRI) return (-1);
if (ctl.len < sizeof(dl_ok_ack_t)) return (-1);
return (0);
}
/*
* dlinfoack - receive an ack to a dlinforeq.
*/
static int dlinfoack(int fd, char *bufp)
{
union DL_primitives *dlp;
struct strbuf ctl;
int flags;
ctl.maxlen = DLPI_MAXDLBUF;
ctl.len = 0;
ctl.buf = bufp;
if (strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlinfoack") < 0) return (-1);
dlp = (union DL_primitives *)ctl.buf;
if (expecting(DL_INFO_ACK, dlp) < 0) return (-1);
if (ctl.len < sizeof(dl_info_ack_t)) return (-1);
if (flags != RS_HIPRI) return (-1);
if (ctl.len < sizeof(dl_info_ack_t)) return (-1);
return (0);
}
/*
* dlbindack - receive an ack to a dlbindreq.
*/
static int dlbindack(int fd, char *bufp)
{
union DL_primitives *dlp;
struct strbuf ctl;
int flags;
ctl.maxlen = DLPI_MAXDLBUF;
ctl.len = 0;
ctl.buf = bufp;
if (strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlbindack") < 0) return (-1);
dlp = (union DL_primitives *)ctl.buf;
if (expecting(DL_BIND_ACK, dlp) < 0) return (-1);
if (flags != RS_HIPRI) return (-1);
if (ctl.len < sizeof(dl_bind_ack_t)) return (-1);
return (0);
}
/*
* expecting - see if we got what we wanted.
*/
static int expecting(int prim, union DL_primitives *dlp)
{
if (dlp->dl_primitive != (u_long)prim) return (-1);
return (0);
}
/*
* strgetmsg - get a message from a stream, with timeout.
*/
static int strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller)
{
int rc;
void sigalrm();
/*
* Start timer.
*/
(void)sigset(SIGALRM, sigalrm);
if (alarm(DLPI_MAXWAIT) < 0) {
perror("alarm");
return (-1);
}
/*
* Set flags argument and issue getmsg().
*/
*flagsp = 0;
if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
perror("getmsg");
return (-1);
}
/*
* Stop timer.
*/
if (alarm(0) < 0) {
perror("alarm");
return (-1);
}
/*
* Check for MOREDATA and/or MORECTL.
*/
if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) return (-1);
if (rc & MORECTL) return (-1);
if (rc & MOREDATA) return (-1);
/*
* Check for at least sizeof (long) control data portion.
*/
if (ctlp->len < sizeof(long)) return (-1);
return (0);
}
/*
* sigalrm - handle alarms.
*/
static void sigalrm() { (void)fprintf(stderr, "dlpi: timeout\n"); }
/*
* savestr - save string in dynamic memory.
*/
char *savestr(char *s)
{
char *t;
if ((t = malloc(strlen(s) + 1)) == NULL) {
(void)fprintf(stderr, "%s: out of memory.\n", pname);
(void)exit(1);
}
(void)strcpy(t, s);
return (t);
}
int dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen)
{
long buf[DLPI_MAXDLBUF];
union DL_primitives *dlp;
struct strbuf data, ctl;
dlp = (union DL_primitives *)buf;
dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
dlp->unitdata_req.dl_dest_addr_length = addrlen + 2;
dlp->unitdata_req.dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
dlp->unitdata_req.dl_priority.dl_min = minpri;
dlp->unitdata_req.dl_priority.dl_max = maxpri;
(void)memcpy(OFFADDR(dlp, sizeof(dl_unitdata_req_t)), addrp, addrlen);
(void)memcpy(OFFADDR(dlp, sizeof(dl_unitdata_req_t) + addrlen), (char *)addrp + 12, 2);
ctl.maxlen = 0;
ctl.len = sizeof(dl_unitdata_req_t) + addrlen + 2;
ctl.buf = (char *)buf;
data.maxlen = 0;
data.len = datalen;
data.buf = (char *)datap;
#ifdef NEVER
if (putmsg(fd, &ctl, &data, 0) < 0)
#else
if (putmsg(fd, NULL, &data, 0) < 0)
#endif /* NEVER tst on 9/30/96 jds to see if raw out works */
return -1;
else
return 0;
}
#endif /* USE_DLPI */