#ifndef lint static char *RCSid = "$Header: /proj/freeware1.0/nfswatch/nfswatch4.1/RCS/dlpi.c,v 1.1 1995/08/08 04:40:23 rck Exp $"; #endif #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.1 1995/08/08 04:40:23 rck * initial checkin * * 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 #include #include #include #ifdef SUNOS5 #include #endif #include #include #include #include #include #include #include #include #include #include #include "nfswatch.h" #include "externs.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(); /* * setup_dlpi_dev - set up the data link provider interface. */ int setup_dlpi_dev(device) 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) { /* * Grab a socket. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { error("socket"); finish(-1); } ifc.ifc_buf = cbuf; ifc.ifc_len = sizeof(cbuf); /* * See what devices we've got. */ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) { error("ioctl: SIOCGIFCONF"); finish(-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); error(devname); finish(-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); } /* * We want the ethernet in promiscuous mode if we're looking * at nodes other than ourselves. */ if (allflag || dstflag) { #ifdef DL_PROMISC_PHYS dlpromisconreq(fd, DL_PROMISC_PHYS); if (dlokack(fd, buf) < 0) { fprintf(stderr, "%s: DL_PROMISC_PHYS failed.\n", pname); finish(-1); } #else fprintf(stderr, "%s: DLPI 1.3 does not support promiscuous ", pname); fprintf(stderr, "mode operation.\n"); fprintf(stderr, "%s: cannot implement -all or -dst options.\n", pname); finish(-1); #endif } /* * Bind to the specific unit. */ dlbindreq(fd, DLPI_DEFAULTSAP, 0, DL_CLDLS, 0, 0); if (dlbindack(fd, buf) < 0) { fprintf(stderr, "%s: dlbindack failed.\n", pname); finish(-1); } #ifdef SUNOS5 /* * 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); finish(-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) { error("ioctl: I_STR DLIOCRAW"); finish(-1); } #endif /* SUNOS5 */ /* * Arrange to get discrete messages. */ if (ioctl(fd, I_SRDOPT, (char *) RMSGD) < 0) { error("ioctl: I_SRDOPT RMSGD"); finish(-1); } #ifdef SUNOS5 /* * Push and configure the streams buffering module. This is once * again SunOS-specific. */ if (ioctl(fd, I_PUSH, DLPI_BUFMOD) < 0) { error("ioctl: I_PUSH BUFMOD"); finish(-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) { error("ioctl: I_STR SBIOCSTIME"); finish(-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) { error("ioctl: I_STR SBIOCSCHUNK"); finish(-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) { error("ioctl: I_STR SBIOCSSNAP"); finish(-1); } #endif /* SUNOS5 */ return(fd); } /* * flush_dlpi - flush data from the dlpi. */ void flush_dlpi(fd) int fd; { if (ioctl(fd, I_FLUSH, (char *) FLUSHR) < 0) { error("ioctl: I_FLUSH"); finish(-1); } } /* * dlpi_devtype - determine the type of device we're looking at. */ int dlpi_devtype(fd) 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); 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(fd) 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) { error("putmsg"); finish(-1); } } /* * dlattachreq - send a request to attach. */ static void dlattachreq(fd, ppa) u_long ppa; int fd; { 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) { error("putmsg"); finish(-1); } } #ifdef DL_PROMISCON_REQ /* * dlpromisconreq - send a request to turn promiscuous mode on. */ static void dlpromisconreq(fd, level) u_long level; int fd; { 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) { error("putmsg"); finish(-1); } } #endif /* DL_PROMISCON_REQ */ /* * dlbindreq - send a request to bind. */ static void dlbindreq(fd, sap, max_conind, service_mode, conn_mgmt, xidtest) u_long sap, max_conind, service_mode, conn_mgmt, xidtest; int fd; { 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) { error("putmsg"); finish(-1); } } /* * dlokack - general acknowledgement reception. */ static int dlokack(fd, bufp) char *bufp; int fd; { 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(fd, bufp) char *bufp; int fd; { 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(fd, bufp) char *bufp; int fd; { 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(prim, dlp) union DL_primitives *dlp; int prim; { if (dlp->dl_primitive != (u_long)prim) return(-1); return(0); } /* * strgetmsg - get a message from a stream, with timeout. */ static int strgetmsg(fd, ctlp, datap, flagsp, caller) struct strbuf *ctlp, *datap; char *caller; int *flagsp; int fd; { int rc; void sigalrm(); /* * Start timer. */ (void) sigset(SIGALRM, sigalrm); if (alarm(DLPI_MAXWAIT) < 0) { error("alarm"); finish(-1); } /* * Set flags argument and issue getmsg(). */ *flagsp = 0; if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) { error("getmsg"); finish(-1); } /* * Stop timer. */ if (alarm(0) < 0) { error("alarm"); finish(-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"); } #endif /* USE_DLPI */