/* * Copyright (c) 1993,1994 * Texas A&M University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Texas A&M University * and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Developers: * David K. Hess, Douglas Lee Schales, David R. Safford * * Heavily modified for Metaware HighC + GNU C 2.8+ * Gisle Vanem 1998 */ #include #include #include #include #include #include #include #include "pcap-dos.h" #include "pcap-int.h" #include "msdos/ndis2.h" #if defined(USE_NDIS2) /* * Packet buffer handling */ extern int FreePktBuf (PktBuf *buf); extern int EnquePktBuf (PktBuf *buf); extern PktBuf* AllocPktBuf (void); /* * Various defines */ #define MAX_NUM_DEBUG_STRINGS 90 #define DEBUG_STRING_LENGTH 80 #define STACK_POOL_SIZE 6 #define STACK_SIZE 256 #define MEDIA_FDDI 1 #define MEDIA_ETHERNET 2 #define MEDIA_TOKEN 3 static int startDebug = 0; static int stopDebug = 0; static DWORD droppedPackets = 0L; static WORD frameSize = 0; static WORD headerSize = 0; static int mediaType = 0; static char *lastErr = NULL; static BYTE debugStrings [MAX_NUM_DEBUG_STRINGS][DEBUG_STRING_LENGTH]; static BYTE *freeStacks [STACK_POOL_SIZE]; static int freeStackPtr = STACK_POOL_SIZE - 1; static ProtMan protManEntry = NULL; static WORD protManDS = 0; static volatile int xmitPending; static struct _PktBuf *txBufPending; static struct _CardHandle *handle; static struct _CommonChars common; static struct _ProtocolChars protChars; static struct _ProtDispatch lowerTable; static struct _FailingModules failingModules; static struct _BindingsList bindings; static struct { WORD err_num; char *err_text; } ndis_errlist[] = { { ERR_SUCCESS, "The function completed successfully.\n" }, { ERR_WAIT_FOR_RELEASE, "The ReceiveChain completed successfully but the protocol has\n" "retained control of the buffer.\n" }, { ERR_REQUEST_QUEUED, "The current request has been queued.\n" }, { ERR_FRAME_NOT_RECOGNIZED, "Frame not recognized.\n" }, { ERR_FRAME_REJECTED, "Frame was discarded.\n" }, { ERR_FORWARD_FRAME, "Protocol wishes to forward frame to another protocol.\n" }, { ERR_OUT_OF_RESOURCE, "Out of resource.\n" }, { ERR_INVALID_PARAMETER, "Invalid parameter.\n" }, { ERR_INVALID_FUNCTION, "Invalid function.\n" }, { ERR_NOT_SUPPORTED, "Not supported.\n" }, { ERR_HARDWARE_ERROR, "Hardware error.\n" }, { ERR_TRANSMIT_ERROR, "The packet was not transmitted due to an error.\n" }, { ERR_NO_SUCH_DESTINATION, "Token ring packet was not recognized when transmitted.\n" }, { ERR_BUFFER_TOO_SMALL, "Provided buffer was too small.\n" }, { ERR_ALREADY_STARTED, "Network drivers already started.\n" }, { ERR_INCOMPLETE_BINDING, "Protocol driver could not complete its bindings.\n" }, { ERR_DRIVER_NOT_INITIALIZED, "MAC did not initialize properly.\n" }, { ERR_HARDWARE_NOT_FOUND, "Hardware not found.\n" }, { ERR_HARDWARE_FAILURE, "Hardware failure.\n" }, { ERR_CONFIGURATION_FAILURE, "Configuration failure.\n" }, { ERR_INTERRUPT_CONFLICT, "Interrupt conflict.\n" }, { ERR_INCOMPATIBLE_MAC, "The MAC is not compatible with the protocol.\n" }, { ERR_INITIALIZATION_FAILED, "Initialization failed.\n" }, { ERR_NO_BINDING, "Binding did not occur.\n" }, { ERR_NETWORK_MAY_NOT_BE_CONNECTED, "The network may not be connected to the adapter.\n" }, { ERR_INCOMPATIBLE_OS_VERSION, "The version of the operating system is incompatible with the protocol.\n" }, { ERR_ALREADY_REGISTERED, "The protocol is already registered.\n" }, { ERR_PATH_NOT_FOUND, "PROTMAN.EXE could not be found.\n" }, { ERR_INSUFFICIENT_MEMORY, "Insufficient memory.\n" }, { ERR_INFO_NOT_FOUND, "Protocol Mananger info structure is lost or corrupted.\n" }, { ERR_GENERAL_FAILURE, "General failure.\n" } }; /* * Some handy macros */ #define PERROR(str) printf("%s (%d): %s\n", __FILE__,__LINE__,str) #define DEBUG_RING() (debugStrings[stopDebug+1 == MAX_NUM_DEBUG_STRINGS ? \ stopDebug = 0 : ++stopDebug]) /* * needs rewrite for DOSX */ #define MAC_DISPATCH(hnd) ((struct _MacUpperDispatch*)(hnd)->common->upperDispatchTable) #define MAC_STATUS(hnd) ((struct _MacStatusTable*) (hnd)->common->serviceStatus) #define MAC_CHAR(hnd) ((struct _MacChars*) (hnd)->common->serviceChars) #ifdef NDIS_DEBUG #define DEBUG0(str) printf (str) #define DEBUG1(fmt,a) printf (fmt,a) #define DEBUG2(fmt,a,b) printf (fmt,a,b) #define TRACE0(str) sprintf (DEBUG_RING(),str) #define TRACE1(fmt,a) sprintf (DEBUG_RING(),fmt,a) #else #define DEBUG0(str) ((void)0) #define DEBUG1(fmt,a) ((void)0) #define DEBUG2(fmt,a,b) ((void)0) #define TRACE0(str) ((void)0) #define TRACE1(fmt,a) ((void)0) #endif /* * This routine is called from both threads */ void NdisFreeStack (BYTE *aStack) { GUARD(); if (freeStackPtr == STACK_POOL_SIZE - 1) PERROR ("tried to free too many stacks"); freeStacks[++freeStackPtr] = aStack; if (freeStackPtr == 0) TRACE0 ("freeStackPtr went positive\n"); UNGUARD(); } /* * This routine is called from callbacks to allocate local data */ BYTE *NdisAllocStack (void) { BYTE *stack; GUARD(); if (freeStackPtr < 0) { /* Ran out of stack buffers. Return NULL which will start * dropping packets */ TRACE0 ("freeStackPtr went negative\n"); stack = 0; } else stack = freeStacks[freeStackPtr--]; UNGUARD(); return (stack); } CALLBACK (NdisSystemRequest (DWORD param1, DWORD param2, WORD param3, WORD opcode, WORD targetDS)) { static int bindEntry = 0; struct _CommonChars *macCommon; volatile WORD result; switch (opcode) { case REQ_INITIATE_BIND: macCommon = (struct _CommonChars*) param2; if (macCommon == NULL) { printf ("There is an NDIS misconfiguration.\n"); result = ERR_GENERAL_FAILURE; break; } DEBUG2 ("module name %s\n" "module type %s\n", macCommon->moduleName, ((MacChars*) macCommon->serviceChars)->macName); /* Binding to the MAC */ result = macCommon->systemRequest ((DWORD)&common, (DWORD)&macCommon, 0, REQ_BIND, macCommon->moduleDS); if (!strcmp(bindings.moduleName[bindEntry], handle->moduleName)) handle->common = macCommon; else PERROR ("unknown module"); ++bindEntry; break; case REQ_INITIATE_UNBIND: macCommon = (struct _CommonChars*) param2; result = macCommon->systemRequest ((DWORD)&common, 0, 0, REQ_UNBIND, macCommon->moduleDS); break; default: result = ERR_GENERAL_FAILURE; break; } ARGSUSED (param1); ARGSUSED (param3); ARGSUSED (targetDS); return (result); } CALLBACK (NdisRequestConfirm (WORD protId, WORD macId, WORD reqHandle, WORD status, WORD request, WORD protDS)) { ARGSUSED (protId); ARGSUSED (macId); ARGSUSED (reqHandle); ARGSUSED (status); ARGSUSED (request); ARGSUSED (protDS); return (ERR_SUCCESS); } CALLBACK (NdisTransmitConfirm (WORD protId, WORD macId, WORD reqHandle, WORD status, WORD protDS)) { xmitPending--; FreePktBuf (txBufPending); /* Add passed ECB back to the free list */ ARGSUSED (reqHandle); ARGSUSED (status); ARGSUSED (protDS); return (ERR_SUCCESS); } /* * The primary function for receiving packets */ CALLBACK (NdisReceiveLookahead (WORD macId, WORD frameSize, WORD bytesAvail, BYTE *buffer, BYTE *indicate, WORD protDS)) { int result; PktBuf *pktBuf; WORD bytesCopied; struct _TDBufDescr tDBufDescr; #if 0 TRACE1 ("lookahead length = %d, ", bytesAvail); TRACE1 ("ecb = %08lX, ", *ecb); TRACE1 ("count = %08lX\n", count); TRACE1 ("offset = %08lX, ", offset); TRACE1 ("timesAllowed = %d, ", timesAllowed); TRACE1 ("packet size = %d\n", look->dataLookAheadLen); #endif /* Allocate a buffer for the packet */ if ((pktBuf = AllocPktBuf()) == NULL) { droppedPackets++; return (ERR_FRAME_REJECTED); } /* * Now kludge things. Note we will have to undo this later. This will * make the packet contiguous after the MLID has done the requested copy. */ tDBufDescr.tDDataCount = 1; tDBufDescr.tDBufDescrRec[0].tDPtrType = NDIS_PTR_PHYSICAL; tDBufDescr.tDBufDescrRec[0].tDDataPtr = pktBuf->buffer; tDBufDescr.tDBufDescrRec[0].tDDataLen = pktBuf->length; tDBufDescr.tDBufDescrRec[0].dummy = 0; result = MAC_DISPATCH(handle)->transferData (&bytesCopied, 0, &tDBufDescr, handle->common->moduleDS); pktBuf->packetLength = bytesCopied; if (result == ERR_SUCCESS) EnquePktBuf(pktBuf); else FreePktBuf (pktBuf); ARGSUSED (frameSize); ARGSUSED (bytesAvail); ARGSUSED (indicate); ARGSUSED (protDS); return (ERR_SUCCESS); } CALLBACK (NdisIndicationComplete (WORD macId, WORD protDS)) { ARGSUSED (macId); ARGSUSED (protDS); /* We don't give a hoot about these. Just return */ return (ERR_SUCCESS); } /* * This is the OTHER way we may receive packets */ CALLBACK (NdisReceiveChain (WORD macId, WORD frameSize, WORD reqHandle, struct _RxBufDescr *rxBufDescr, BYTE *indicate, WORD protDS)) { struct _PktBuf *pktBuf; int i; /* * For now we copy the entire packet over to a PktBuf structure. This may be * a performance hit but this routine probably isn't called very much, and * it is a lot of work to do it otherwise. Also if it is a filter protocol * packet we could end up sucking up MAC buffes. */ if ((pktBuf = AllocPktBuf()) == NULL) { droppedPackets++; return (ERR_FRAME_REJECTED); } pktBuf->packetLength = 0; /* Copy the packet to the buffer */ for (i = 0; i < rxBufDescr->rxDataCount; ++i) { struct _RxBufDescrRec *rxDescr = &rxBufDescr->rxBufDescrRec[i]; memcpy (pktBuf->buffer + pktBuf->packetLength, rxDescr->rxDataPtr, rxDescr->rxDataLen); pktBuf->packetLength += rxDescr->rxDataLen; } EnquePktBuf (pktBuf); ARGSUSED (frameSize); ARGSUSED (reqHandle); ARGSUSED (indicate); ARGSUSED (protDS); /* This frees up the buffer for the MAC to use */ return (ERR_SUCCESS); } CALLBACK (NdisStatusProc (WORD macId, WORD param1, BYTE *indicate, WORD opcode, WORD protDS)) { switch (opcode) { case STATUS_RING_STATUS: break; case STATUS_ADAPTER_CHECK: break; case STATUS_START_RESET: break; case STATUS_INTERRUPT: break; case STATUS_END_RESET: break; default: break; } ARGSUSED (macId); ARGSUSED (param1); ARGSUSED (indicate); ARGSUSED (opcode); ARGSUSED (protDS); /* We don't need to do anything about this stuff yet */ return (ERR_SUCCESS); } /* * Tell the NDIS driver to start the delivery of the packet */ int NdisSendPacket (struct _PktBuf *pktBuf, int macId) { struct _TxBufDescr txBufDescr; int result; xmitPending++; txBufPending = pktBuf; /* we only have 1 pending Tx at a time */ txBufDescr.txImmedLen = 0; txBufDescr.txImmedPtr = NULL; txBufDescr.txDataCount = 1; txBufDescr.txBufDescrRec[0].txPtrType = NDIS_PTR_PHYSICAL; txBufDescr.txBufDescrRec[0].dummy = 0; txBufDescr.txBufDescrRec[0].txDataLen = pktBuf->packetLength; txBufDescr.txBufDescrRec[0].txDataPtr = pktBuf->buffer; result = MAC_DISPATCH(handle)->transmitChain (common.moduleId, pktBuf->handle, &txBufDescr, handle->common->moduleDS); switch (result) { case ERR_OUT_OF_RESOURCE: /* Note that this should not happen but if it does there is not * much we can do about it */ printf ("ERROR: transmit queue overflowed\n"); return (0); case ERR_SUCCESS: /* Everything was hunky dory and synchronous. Free up the * packet buffer */ xmitPending--; FreePktBuf (pktBuf); return (1); case ERR_REQUEST_QUEUED: /* Everything was hunky dory and asynchronous. Do nothing */ return (1); default: printf ("Tx fail, code = %04X\n", result); return (0); } } static int ndis_nerr = sizeof(ndis_errlist) / sizeof(ndis_errlist[0]); static char *Ndis_strerror (WORD errorCode) { static char buf[30]; int i; for (i = 0; i < ndis_nerr; i++) if (errorCode == ndis_errlist[i].err_num) return (ndis_errlist[i].err_text); sprintf (buf,"unknown error %d",errorCode); return (buf); } char *NdisLastError (void) { char *errStr = lastErr; lastErr = NULL; return (errStr); } int NdisOpen (void) { struct _ReqBlock reqBlock; int result; int ndisFd = open (NDIS_PATH, O_RDONLY); if (ndisFd < 0) { printf ("Could not open NDIS Protocol Manager device.\n"); return (0); } memset (&reqBlock, 0, sizeof(ReqBlock)); reqBlock.opcode = PM_GET_PROTOCOL_MANAGER_LINKAGE; result = NdisGetLinkage (ndisFd, (char*)&reqBlock, sizeof(ReqBlock)); if (result != 0) { printf ("Could not get Protocol Manager linkage.\n"); close (ndisFd); return (0); } close (ndisFd); protManEntry = (ProtMan) reqBlock.pointer1; protManDS = reqBlock.word1; DEBUG2 ("Entry Point = %04X:%04X\n", FP_SEG(protManEntry),FP_OFF(protManEntry)); DEBUG1 ("ProtMan DS = %04X\n", protManDS); return (1); } int NdisRegisterAndBind (int promis) { struct _ReqBlock reqBlock; WORD result; memset (&common,0,sizeof(common)); common.tableSize = sizeof (common); common.majorNdisVersion = 2; common.minorNdisVersion = 0; common.majorModuleVersion = 2; common.minorModuleVersion = 0; /* Indicates binding from below and dynamically loaded */ common.moduleFlags = 0x00000006L; strcpy (common.moduleName, "PCAP"); common.protocolLevelUpper = 0xFF; common.protocolLevelLower = 1; common.interfaceLower = 1; #ifdef __DJGPP__ common.moduleDS = _dos_ds; /* the callback data segment */ #else common.moduleDS = _DS; #endif common.systemRequest = (SystemRequest) systemRequestGlue; common.serviceChars = (BYTE*) &protChars; common.serviceStatus = NULL; common.upperDispatchTable = NULL; common.lowerDispatchTable = (BYTE*) &lowerTable; protChars.length = sizeof (protChars); protChars.name[0] = 0; protChars.type = 0; lowerTable.backPointer = &common; lowerTable.requestConfirm = requestConfirmGlue; lowerTable.transmitConfirm = transmitConfirmGlue; lowerTable.receiveLookahead = receiveLookaheadGlue; lowerTable.indicationComplete = indicationCompleteGlue; lowerTable.receiveChain = receiveChainGlue; lowerTable.status = statusGlue; lowerTable.flags = 3; if (promis) lowerTable.flags |= 4; /* promiscous mode (receive everything) */ bindings.numBindings = 1; strcpy (bindings.moduleName[0], handle->moduleName); /* Register ourselves with NDIS */ reqBlock.opcode = PM_REGISTER_MODULE; reqBlock.pointer1 = (BYTE FAR*) &common; reqBlock.pointer2 = (BYTE FAR*) &bindings; result = (*protManEntry) (&reqBlock, protManDS); if (result) { printf ("Protman registering failed: %s\n", Ndis_strerror(result)); return (0); } /* Start the binding process */ reqBlock.opcode = PM_BIND_AND_START; reqBlock.pointer1 = (BYTE FAR*) &failingModules; result = (*protManEntry) (&reqBlock, protManDS); if (result) { printf ("Start binding failed: %s\n", Ndis_strerror(result)); return (0); } return (1); } static int CheckMacFeatures (CardHandle *card) { DWORD serviceFlags; BYTE _far *mediaString; BYTE _far *mac_addr; DEBUG2 ("checking card features\n" "common table address = %08lX, macId = %d\n", card->common, card->common->moduleId); serviceFlags = MAC_CHAR (handle)->serviceFlags; if ((serviceFlags & SF_PROMISCUOUS) == 0) { printf ("The MAC %s does not support promiscuous mode.\n", card->moduleName); return (0); } mediaString = MAC_CHAR (handle)->macName; DEBUG1 ("media type = %s\n",mediaString); /* Get the media type. And set the header size */ if (!strncmp(mediaString,"802.3",5) || !strncmp(mediaString,"DIX",3) || !strncmp(mediaString,"DIX+802.3",9)) headerSize = sizeof (EthernetIIHeader); else if (!strncmp(mediaString,"FDDI",4)) headerSize = sizeof (FddiHeader) + sizeof (Ieee802Dot2SnapHeader); else { printf ("Unsupported MAC type: `%s'\n", mediaString); return (0); } frameSize = MAC_CHAR (handle)->maxFrameSize; mac_addr = MAC_CHAR (handle)->currentAddress; printf ("Hardware address: %02X:%02X:%02X:%02X:%02X:%02X\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); return (1); } static int NdisStartMac (CardHandle *card) { WORD result; /* Set the lookahead length */ result = MAC_DISPATCH(handle)->request (common.moduleId, 0, headerSize, 0, REQ_SET_LOOKAHEAD, card->common->moduleDS); /* We assume that if we got INVALID PARAMETER then either this * is not supported or will work anyway. NE2000 does this. */ if (result != ERR_SUCCESS && result != ERR_INVALID_PARAMETER) { DEBUG1 ("Set lookahead failed: %s\n", Ndis_strerror(result)); return (0); } /* Set the packet filter. Note that for some medias and drivers we * must specify all three flags or the card(s) will not operate correctly. */ result = MAC_DISPATCH(handle)->request (common.moduleId, 0, /* all packets */ FILTER_PROMISCUOUS | /* packets to us */ FILTER_DIRECTED | /* broadcasts */ FILTER_BROADCAST, 0, REQ_SET_PACKET_FILTER, card->common->moduleDS); if (result != ERR_SUCCESS) { DEBUG1 ("Set packet filter failed: %s\n", Ndis_strerror(result)); return (0); } /* If OPEN/CLOSE supported then open the adapter */ if (MAC_CHAR(handle)->serviceFlags & SF_OPEN_CLOSE) { result = MAC_DISPATCH(handle)->request (common.moduleId, 0, 0, NULL, REQ_OPEN_ADAPTER, card->common->moduleDS); if (result != ERR_SUCCESS) { DEBUG1 ("Opening the MAC failed: %s\n", Ndis_strerror(result)); return (0); } } return (1); } void NdisShutdown (void) { struct _ReqBlock reqBlock; int result, i; if (!handle) return; /* If the adapters support open and are open then close them */ if ((MAC_CHAR(handle)->serviceFlags & SF_OPEN_CLOSE) && (MAC_STATUS(handle)->macStatus & MAC_OPEN)) { result = MAC_DISPATCH(handle)->request (common.moduleId, 0, 0, 0, REQ_CLOSE_ADAPTER, handle->common->moduleDS); if (result != ERR_SUCCESS) { printf ("Closing the MAC failed: %s\n", Ndis_strerror(result)); return; } } /* Tell the Protocol Manager to unbind and stop */ reqBlock.opcode = PM_UNBIND_AND_STOP; reqBlock.pointer1 = (BYTE FAR*) &failingModules; reqBlock.pointer2 = NULL; result = (*protManEntry) (&reqBlock, protManDS); if (result) printf ("Unbind failed: %s\n", Ndis_strerror(result)); for (i = 0; i < STACK_POOL_SIZE; ++i) free (freeStacks[i] - STACK_SIZE); handle = NULL; } int NdisInit (int promis) { int i, result; /* Allocate the real mode stacks used for NDIS callbacks */ for (i = 0; i < STACK_POOL_SIZE; ++i) { freeStacks[i] = malloc (STACK_SIZE); if (!freeStacks[i]) return (0); freeStacks[i] += STACK_SIZE; } if (!NdisOpen()) return (0); if (!NdisRegisterAndBind(promis)) return (0); DEBUG1 ("My module id: %d\n", common.moduleId); DEBUG1 ("Handle id; %d\n", handle->common->moduleId); DEBUG1 ("MAC card: %-16s - ", handle->moduleName); atexit (NdisShutdown); if (!CheckMacFeatures(&handle)) return (0); switch (mediaType) { case MEDIA_FDDI: DEBUG0 ("Media type: FDDI"); break; case MEDIA_ETHERNET: DEBUG0 ("Media type: ETHERNET"); break; default: DEBUG0 ("Unsupported media.\n"); return (0); } DEBUG1 (" - Frame size: %d\n", frameSize); if (!NdisStartMac(&handle)) return (0); return (1); } #endif /* USE_NDIS2 */