/* CTC_CTCI.C (c) Copyright Roger Bowler, 2000-2012 */ /* (c) Copyright James A. Pierson, 2002-2009 */ /* (c) Copyright "Fish" (David B. Trout), 2002-2009 */ /* (c) Copyright Fritz Elfert, 2001-2009 */ /* Hercules IP Channel-to-Channel Support (CTCI) */ /* */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /*********************************************************************/ /* Please keep to this terminology: */ /* */ /* * A character special device file is a file system object, for */ /* example /dev/net/tun. It can be opened if permissions allow. */ /* The ls command shows such objects: */ /* crw-rw---- 1 root staff 10, 200 Nov 24 15:01 /dev/net/tun */ /* Permissions are set when the special device file is created by */ /* mknod or by a udev rule invoked as a result of creating the */ /* object. */ /* */ /* * A network interface represents an IP adapter, real or virtual. */ /* Interfaces are displayed and controlled by the ifconfig */ /* command (to be replaced by the ip command). */ #if 0 tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:192.168.2.65 P-t-P:10.0.0.33 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 RX packets:70834 errors:0 dropped:0 overruns:0 frame:0 TX packets:47 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:4262823 (4.0 MiB) TX bytes:2602 (2.5 KiB) #endif /* */ /* Opening the character special file obtains a file handle that can */ /* be associated with an interface. No special permissions are */ /* required to open the character special file, as you cannot do */ /* anything with the file descriptor until you associate it with an */ /* interface. The operation of associating a newly created file */ /* descriptor with interface requires root privileges unless the */ /* interface was created by, e.g., openvpn which can set the user */ /* and group that my associate the interface. So this is where */ /* hercifc comes into play. */ /* */ /* A preconfigured interface is assumed when only the interface name */ /* is specified. */ /*********************************************************************/ #include "hstdinc.h" /* jbs 10/27/2007 added _SOLARIS_ silly typo fixed 01/18/08 when looked at this again */ #if !defined(__SOLARIS__) #include "hercules.h" #include "ctcadpt.h" #include "tuntap.h" #include "opcode.h" /* getopt dynamic linking kludge */ #include "herc_getopt.h" /*-------------------------------------------------------------------*/ /* Ivan Warren 20040227 */ /* This table is used by channel.c to determine if a CCW code is an */ /* immediate command or not */ /* The tape is addressed in the DEVHND structure as 'DEVIMM immed' */ /* 0 : Command is NOT an immediate command */ /* 1 : Command is an immediate command */ /* Note : An immediate command is defined as a command which returns */ /* CE (channel end) during initialisation (that is, no data is */ /* actually transfered. In this case, IL is not indicated for a CCW */ /* Format 0 or for a CCW Format 1 when IL Suppression Mode is in */ /* effect */ /*-------------------------------------------------------------------*/ static BYTE CTCI_Immed_Commands[256]= { 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // ==================================================================== // Declarations // ==================================================================== static void* CTCI_ReadThread( void* arg /*PCTCBLK pCTCBLK */ ); static int CTCI_EnqueueIPFrame( DEVBLK* pDEVBLK, BYTE* pData, size_t iSize ); static int ParseArgs( DEVBLK* pDEVBLK, PCTCBLK pCTCBLK, int argc, char** argv ); // -------------------------------------------------------------------- // Device Handler Information Block // -------------------------------------------------------------------- DEVHND ctci_device_hndinfo = { &CTCI_Init, /* Device Initialisation */ &CTCI_ExecuteCCW, /* Device CCW execute */ &CTCI_Close, /* Device Close */ &CTCI_Query, /* Device Query */ NULL, /* Device Extended Query */ NULL, /* Device Start channel pgm */ NULL, /* Device End channel pgm */ NULL, /* Device Resume channel pgm */ NULL, /* Device Suspend channel pgm */ NULL, /* Device Halt channel pgm */ NULL, /* Device Read */ NULL, /* Device Write */ NULL, /* Device Query used */ NULL, /* Device Reserve */ NULL, /* Device Release */ NULL, /* Device Attention */ CTCI_Immed_Commands, /* Immediate CCW Codes */ NULL, /* Signal Adapter Input */ NULL, /* Signal Adapter Output */ NULL, /* Signal Adapter Sync */ NULL, /* Signal Adapter Output Mult */ NULL, /* QDIO subsys desc */ NULL, /* QDIO set subchan ind */ NULL, /* Hercules suspend */ NULL /* Hercules resume */ }; // ==================================================================== // // ==================================================================== // // CTCI_Init // #define CTC_DEVICES_IN_GROUP 2 // a read and write device int CTCI_Init( DEVBLK* pDEVBLK, int argc, char *argv[] ) { CTCBLK wblk; PCTCBLK pWrkCTCBLK = &wblk; // Working CTCBLK PCTCBLK pDevCTCBLK = NULL; // Device CTCBLK int rc = 0; // Return code int nIFFlags; // Interface flags char thread_name[32]; // CTCI_ReadThread nIFFlags = // Interface flags 0 | IFF_UP // (interface is being enabled) | IFF_BROADCAST // (interface broadcast addr is valid) ; #if defined( TUNTAP_IFF_RUNNING_NEEDED ) nIFFlags |= // ADDITIONAL Interface flags 0 | IFF_RUNNING // (interface is ALSO operational) ; #endif /* defined( TUNTAP_IFF_RUNNING_NEEDED ) */ pDEVBLK->devtype = 0x3088; pDEVBLK->excps = 0; // CTC is a group device if(!group_device(pDEVBLK, CTC_DEVICES_IN_GROUP)) return 0; // Housekeeping memset( pWrkCTCBLK, 0, sizeof( CTCBLK ) ); // Parse configuration file statement if( ParseArgs( pDEVBLK, pWrkCTCBLK, argc, (char**)argv ) != 0 ) return -1; // Allocate the device CTCBLK and copy parsed information. pDevCTCBLK = malloc( sizeof( CTCBLK ) ); if( !pDevCTCBLK ) { char buf[40]; MSGBUF(buf, "malloc(%d)", (int)sizeof(CTCBLK)); // "%1d:%04X CTC: error in function '%s': '%s'" WRMSG(HHC00900, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, buf, strerror(errno) ); return -1; } memcpy( pDevCTCBLK, pWrkCTCBLK, sizeof( CTCBLK ) ); // New format has only one device statement for both addresses // We need to dynamically allocate the read device block pDevCTCBLK->pDEVBLK[0] = pDEVBLK->group->memdev[0]; pDevCTCBLK->pDEVBLK[1] = pDEVBLK->group->memdev[1]; pDevCTCBLK->pDEVBLK[0]->dev_data = pDevCTCBLK; pDevCTCBLK->pDEVBLK[1]->dev_data = pDevCTCBLK; SetSIDInfo( pDevCTCBLK->pDEVBLK[0], 0x3088, 0x08, 0x3088, 0x01 ); SetSIDInfo( pDevCTCBLK->pDEVBLK[1], 0x3088, 0x08, 0x3088, 0x01 ); pDevCTCBLK->pDEVBLK[0]->ctctype = CTC_CTCI; pDevCTCBLK->pDEVBLK[0]->ctcxmode = 1; pDevCTCBLK->pDEVBLK[1]->ctctype = CTC_CTCI; pDevCTCBLK->pDEVBLK[1]->ctcxmode = 1; pDevCTCBLK->sMTU = atoi( pDevCTCBLK->szMTU ); pDevCTCBLK->iMaxFrameBufferSize = sizeof(pDevCTCBLK->bFrameBuffer); initialize_lock( &pDevCTCBLK->Lock ); initialize_lock( &pDevCTCBLK->EventLock ); initialize_condition( &pDevCTCBLK->Event ); // Give both Herc devices a reasonable name... strlcpy( pDevCTCBLK->pDEVBLK[0]->filename, pDevCTCBLK->szTUNCharDevName, sizeof( pDevCTCBLK->pDEVBLK[0]->filename ) ); strlcpy( pDevCTCBLK->pDEVBLK[1]->filename, pDevCTCBLK->szTUNCharDevName, sizeof( pDevCTCBLK->pDEVBLK[1]->filename ) ); /* It might be tempting to add IFF_TUN_EXCL to the flags to */ /* avoid a race, but it does not work like open exclusive; it */ /* would appear that the bit is permanent so that hercifc cannot */ /* configure the interface. */ rc = TUNTAP_CreateInterface( pDevCTCBLK->szTUNCharDevName, #if defined(BUILD_HERCIFC) (pDevCTCBLK->fPreconfigured ? IFF_NO_HERCIFC : 0) | #endif // __HERCIFC_H_ IFF_TUN | IFF_NO_PI, &pDevCTCBLK->fd, pDevCTCBLK->szTUNIfName ); if( rc < 0 ) return -1; // HHC00901 "%1d:%04X %s: interface %s, type %s opened" WRMSG(HHC00901, "I", SSID_TO_LCSS(pDevCTCBLK->pDEVBLK[0]->ssid), pDevCTCBLK->pDEVBLK[0]->devnum, pDevCTCBLK->pDEVBLK[0]->typname, pDevCTCBLK->szTUNIfName, "TUN"); if (!pDevCTCBLK->fPreconfigured) { #if defined(OPTION_W32_CTCI) // Set the specified driver/dll i/o buffer sizes.. { struct tt32ctl tt32ctl; memset( &tt32ctl, 0, sizeof(tt32ctl) ); strlcpy( tt32ctl.tt32ctl_name, pDevCTCBLK->szTUNIfName, sizeof(tt32ctl.tt32ctl_name) ); tt32ctl.tt32ctl_devbuffsize = pDevCTCBLK->iKernBuff; if( TUNTAP_IOCtl( pDevCTCBLK->fd, TT32SDEVBUFF, (char*)&tt32ctl ) != 0 ) { // "%1d:%04X %s: ioctl '%s' failed for device '%s': '%s'" WRMSG(HHC00902, "W", SSID_TO_LCSS(pDevCTCBLK->pDEVBLK[0]->ssid), pDevCTCBLK->pDEVBLK[0]->devnum, pDevCTCBLK->pDEVBLK[0]->typname, "TT32SDEVBUFF", pDevCTCBLK->szTUNIfName, strerror( errno ) ); } tt32ctl.tt32ctl_iobuffsize = pDevCTCBLK->iIOBuff; if( TUNTAP_IOCtl( pDevCTCBLK->fd, TT32SIOBUFF, (char*)&tt32ctl ) != 0 ) { // "%1d:%04X %s: ioctl '%s' failed for device '%s': '%s'" WRMSG(HHC00902, "W", SSID_TO_LCSS(pDevCTCBLK->pDEVBLK[0]->ssid), pDevCTCBLK->pDEVBLK[0]->devnum, pDevCTCBLK->pDEVBLK[0]->typname, "TT32SIOBUFF", pDevCTCBLK->szTUNIfName, strerror( errno ) ); } } #ifdef OPTION_TUNTAP_SETMACADDR if( !pDevCTCBLK->szMACAddress[0] ) // (if MAC address unspecified) { in_addr_t wrk_guest_ip_addr; MAC wrk_guest_mac_addr; if ((in_addr_t)-1 != (wrk_guest_ip_addr = inet_addr( pDevCTCBLK->szGuestIPAddr ))) { build_herc_iface_mac ( wrk_guest_mac_addr, (const BYTE*) &wrk_guest_ip_addr ); MSGBUF( pDevCTCBLK->szMACAddress, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" ,wrk_guest_mac_addr[0] ,wrk_guest_mac_addr[1] ,wrk_guest_mac_addr[2] ,wrk_guest_mac_addr[3] ,wrk_guest_mac_addr[4] ,wrk_guest_mac_addr[5] ); } } TRACE ( "** CTCI_Init: %4.4X (%s): IP \"%s\" --> default MAC \"%s\"\n" ,pDevCTCBLK->pDEVBLK[0]->devnum ,pDevCTCBLK->szTUNIfName ,pDevCTCBLK->szGuestIPAddr ,pDevCTCBLK->szMACAddress ); VERIFY( TUNTAP_SetMACAddr ( pDevCTCBLK->szTUNIfName, pDevCTCBLK->szMACAddress ) == 0 ); #endif #ifdef OPTION_TUNTAP_CLRIPADDR VERIFY( TUNTAP_ClrIPAddr ( pDevCTCBLK->szTUNIfName ) == 0 ); #endif #endif VERIFY( TUNTAP_SetIPAddr ( pDevCTCBLK->szTUNIfName, pDevCTCBLK->szDriveIPAddr ) == 0 ); VERIFY( TUNTAP_SetDestAddr( pDevCTCBLK->szTUNIfName, pDevCTCBLK->szGuestIPAddr ) == 0 ); #ifdef OPTION_TUNTAP_SETNETMASK VERIFY( TUNTAP_SetNetMask ( pDevCTCBLK->szTUNIfName, pDevCTCBLK->szNetMask ) == 0 ); #endif VERIFY( TUNTAP_SetMTU ( pDevCTCBLK->szTUNIfName, pDevCTCBLK->szMTU ) == 0 ); VERIFY( TUNTAP_SetFlags ( pDevCTCBLK->szTUNIfName, nIFFlags ) == 0 ); } // Copy the fd to make panel.c happy pDevCTCBLK->pDEVBLK[0]->fd = pDevCTCBLK->pDEVBLK[1]->fd = pDevCTCBLK->fd; MSGBUF(thread_name, "CTCI %4.4X ReadThread", pDEVBLK->devnum); rc = create_thread( &pDevCTCBLK->tid, JOINABLE, CTCI_ReadThread, pDevCTCBLK, thread_name ); if(rc) { // "Error in function create_thread(): %s" WRMSG(HHC00102, "E", strerror(rc)); } pDevCTCBLK->pDEVBLK[0]->tid = pDevCTCBLK->tid; pDevCTCBLK->pDEVBLK[1]->tid = pDevCTCBLK->tid; return 0; } // // CTCI_ExecuteCCW // void CTCI_ExecuteCCW( DEVBLK* pDEVBLK, BYTE bCode, BYTE bFlags, BYTE bChained, U32 sCount, BYTE bPrevCode, int iCCWSeq, BYTE* pIOBuf, BYTE* pMore, BYTE* pUnitStat, U32* pResidual ) { int iNum; // Number of bytes to move BYTE bOpCode; // CCW opcode with modifier // bits masked off UNREFERENCED( bFlags ); UNREFERENCED( bChained ); UNREFERENCED( bPrevCode ); UNREFERENCED( iCCWSeq ); // Intervention required if the device file is not open if( pDEVBLK->fd < 0 && !IS_CCW_SENSE( bCode ) && !IS_CCW_CONTROL( bCode ) ) { pDEVBLK->sense[0] = SENSE_IR; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } // Mask off the modifier bits in the CCW bOpCode if( ( bCode & 0x07 ) == 0x07 ) bOpCode = 0x07; else if( ( bCode & 0x03 ) == 0x02 ) bOpCode = 0x02; else if( ( bCode & 0x0F ) == 0x0C ) bOpCode = 0x0C; else if( ( bCode & 0x03 ) == 0x01 ) bOpCode = pDEVBLK->ctcxmode ? ( bCode & 0x83 ) : 0x01; else if( ( bCode & 0x1F ) == 0x14 ) bOpCode = 0x14; else if( ( bCode & 0x47 ) == 0x03 ) bOpCode = 0x03; else if( ( bCode & 0xC7 ) == 0x43 ) bOpCode = 0x43; else bOpCode = bCode; // Process depending on CCW bOpCode switch (bOpCode) { case 0x01: // 0MMMMM01 WRITE //------------------------------------------------------------ // WRITE //------------------------------------------------------------ // Return normal status if CCW count is zero if( sCount == 0 ) { *pUnitStat = CSW_CE | CSW_DE; break; } CTCI_Write( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual ); break; case 0x81: // 1MMMMM01 WEOF //------------------------------------------------------------ // WRITE EOF //------------------------------------------------------------ // Return normal status *pUnitStat = CSW_CE | CSW_DE; break; case 0x02: // MMMMMM10 READ case 0x0C: // MMMM1100 RDBACK // ----------------------------------------------------------- // READ & READ BACKWARDS // ----------------------------------------------------------- // Read data and set unit status and residual byte count CTCI_Read( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual, pMore ); break; case 0x07: // MMMMM111 CTL // ----------------------------------------------------------- // CONTROL // ----------------------------------------------------------- *pUnitStat = CSW_CE | CSW_DE; break; case 0x03: // M0MMM011 NOP // ----------------------------------------------------------- // CONTROL NO-OPERATON // ----------------------------------------------------------- *pUnitStat = CSW_CE | CSW_DE; break; case 0x43: // 00XXX011 SBM // ----------------------------------------------------------- // SET BASIC MODE // ----------------------------------------------------------- // Command reject if in basic mode if( pDEVBLK->ctcxmode == 0 ) { pDEVBLK->sense[0] = SENSE_CR; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; break; } // Reset extended mode and return normal status pDEVBLK->ctcxmode = 0; *pResidual = 0; *pUnitStat = CSW_CE | CSW_DE; break; case 0xC3: // 11000011 SEM // ----------------------------------------------------------- // SET EXTENDED MODE // ----------------------------------------------------------- pDEVBLK->ctcxmode = 1; *pResidual = 0; *pUnitStat = CSW_CE | CSW_DE; break; case 0xE3: // 11100011 // ----------------------------------------------------------- // PREPARE (PREP) // ----------------------------------------------------------- *pUnitStat = CSW_CE | CSW_DE; break; case 0x14: // XXX10100 SCB // ----------------------------------------------------------- // SENSE COMMAND BYTE // ----------------------------------------------------------- *pUnitStat = CSW_CE | CSW_DE; break; case 0x04: // 00000100 SENSE // ----------------------------------------------------------- // SENSE // ----------------------------------------------------------- // Command reject if in basic mode if( pDEVBLK->ctcxmode == 0 ) { pDEVBLK->sense[0] = SENSE_CR; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; break; } // Calculate residual byte count iNum = ( sCount < pDEVBLK->numsense ) ? sCount : pDEVBLK->numsense; *pResidual = sCount - iNum; if( sCount < pDEVBLK->numsense ) *pMore = 1; // Copy device sense bytes to channel I/O buffer memcpy( pIOBuf, pDEVBLK->sense, iNum ); // Clear the device sense bytes memset( pDEVBLK->sense, 0, sizeof( pDEVBLK->sense ) ); // Return unit status *pUnitStat = CSW_CE | CSW_DE; break; case 0xE4: // 11100100 SID // ----------------------------------------------------------- // SENSE ID // ----------------------------------------------------------- // Calculate residual byte count iNum = ( sCount < pDEVBLK->numdevid ) ? sCount : pDEVBLK->numdevid; *pResidual = sCount - iNum; if( sCount < pDEVBLK->numdevid ) *pMore = 1; // Copy device identifier bytes to channel I/O buffer memcpy( pIOBuf, pDEVBLK->devid, iNum ); // Return unit status *pUnitStat = CSW_CE | CSW_DE; break; default: // ------------------------------------------------------------ // INVALID OPERATION // ------------------------------------------------------------ // Set command reject sense byte, and unit check status pDEVBLK->sense[0] = SENSE_CR; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; } } // ------------------------------------------------------------------- // CTCI_Close // ------------------------------------------------------------------- int CTCI_Close( DEVBLK* pDEVBLK ) { /* DEVBLK* pDEVBLK2; */ PCTCBLK pCTCBLK = (PCTCBLK)pDEVBLK->dev_data; // Close the device file (if not already closed) if( pCTCBLK->fd >= 0 ) { // PROGRAMMING NOTE: there's currently no way to interrupt // the "CTCI_ReadThread"s TUNTAP_Read of the adapter. Thus // we must simply wait for CTCI_ReadThread to eventually // notice that we're doing a close (via our setting of the // fCloseInProgress flag). Its TUNTAP_Read will eventually // timeout after a few seconds (currently 5, which is dif- // ferent than the CTC_READ_TIMEOUT_SECS timeout value the // CTCI_Read function uses) and will then do the close of // the adapter for us (TUNTAP_Close) so we don't have to. // All we need to do is ask it to exit (via our setting of // the fCloseInProgress flag) and then wait for it to exit // (which, as stated, could take up to a max of 5 seconds). // All of this is simply because it's poor form to close a // device from one thread while another thread is reading // from it. Attempting to do so could trip a race condition // wherein the internal i/o buffers used to process the // read request could have been freed (by the close call) // by the time the read request eventually gets serviced. TID tid = pCTCBLK->tid; pCTCBLK->fCloseInProgress = 1; // (ask read thread to exit) signal_thread( tid, SIGUSR2 ); // (for non-Win32 platforms) //FIXME signal_thread not working for non-MSVC platforms #if defined(_MSVC_) join_thread( tid, NULL ); // (wait for thread to end) #endif detach_thread( tid ); // (wait for thread to end) } pDEVBLK->fd = -1; // indicate we're now closed return 0; } // ------------------------------------------------------------------- // CTCI_Query // ------------------------------------------------------------------- void CTCI_Query( DEVBLK* pDEVBLK, char** ppszClass, int iBufLen, char* pBuffer ) { CTCBLK* pCTCBLK; BEGIN_DEVICE_CLASS_QUERY( "CTCA", pDEVBLK, ppszClass, iBufLen, pBuffer ); pCTCBLK = (CTCBLK*) pDEVBLK->dev_data; if(!pCTCBLK) { strlcpy(pBuffer,"*Uninitialized",iBufLen); } else { snprintf( pBuffer, iBufLen-1, "CTCI %s/%s (%s)%s IO[%" I64_FMT "u]", pCTCBLK->szGuestIPAddr, pCTCBLK->szDriveIPAddr, pCTCBLK->szTUNIfName, pCTCBLK->fDebug ? " -d" : "", pDEVBLK->excps ); pBuffer[iBufLen-1] = '\0'; } return; } // ------------------------------------------------------------------- // CTCI_Read // ------------------------------------------------------------------- // // Once an IP frame is received by the Read Thread, it is enqueued // on the device frame buffer for presentation to the host program. // The residual byte count is set to indicate the amount of the buffer // which was not filled. // // For details regarding the actual buffer layout, please refer to // the comments preceding the CTCI_ReadThread function. // Input: // pDEVBLK A pointer to the CTC adapter device block // sCount The I/O buffer length from the read CCW // pIOBuf The I/O buffer from the read CCW // // Output: // pUnitStat The CSW status (CE+DE or CE+DE+UC or CE+DE+UC+SM) // pResidual The CSW residual byte count // pMore Set to 1 if packet data exceeds CCW count // void CTCI_Read( DEVBLK* pDEVBLK, U32 sCount, BYTE* pIOBuf, BYTE* pUnitStat, U32* pResidual, BYTE* pMore ) { PCTCBLK pCTCBLK = (PCTCBLK)pDEVBLK->dev_data; PCTCIHDR pFrame = NULL; size_t iLength = 0; int rc = 0; for ( ; ; ) { obtain_lock( &pCTCBLK->Lock ); if( !pCTCBLK->fDataPending ) { release_lock( &pCTCBLK->Lock ); { struct timespec waittime; struct timeval now; gettimeofday( &now, NULL ); waittime.tv_sec = now.tv_sec + CTC_READ_TIMEOUT_SECS; waittime.tv_nsec = now.tv_usec * 1000; obtain_lock( &pCTCBLK->EventLock ); rc = timed_wait_condition( &pCTCBLK->Event, &pCTCBLK->EventLock, &waittime ); } release_lock( &pCTCBLK->EventLock ); if( rc == ETIMEDOUT || rc == EINTR ) { // check for halt condition if( pDEVBLK->scsw.flag2 & SCSW2_FC_HALT || pDEVBLK->scsw.flag2 & SCSW2_FC_CLEAR ) { if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep ) { // "%1d:%04X CTC: halt or clear recognized" WRMSG(HHC00904, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); } *pUnitStat = CSW_CE | CSW_DE; *pResidual = sCount; return; } continue; } obtain_lock( &pCTCBLK->Lock ); } // Sanity check if( pCTCBLK->iFrameOffset == 0 ) { release_lock( &pCTCBLK->Lock ); continue; } // Fix-up frame pointer and terminate block pFrame = (PCTCIHDR)( pCTCBLK->bFrameBuffer + sizeof( CTCIHDR ) + pCTCBLK->iFrameOffset ); STORE_HW( pFrame->hwOffset, 0x0000 ); // (fix for day-1 bug offered by Vince Weaver [vince@deater.net]) // iLength = pCTCBLK->iFrameOffset + sizeof( CTCIHDR ) + 2; iLength = pCTCBLK->iFrameOffset + sizeof( CTCIHDR ); if( sCount < iLength ) { *pMore = 1; *pResidual = 0; iLength = sCount; } else { *pMore = 0; *pResidual -= (U16)iLength; } *pUnitStat = CSW_CE | CSW_DE; memcpy( pIOBuf, pCTCBLK->bFrameBuffer, iLength ); if( pCTCBLK->fDebug ) { // "%1d:%04X CTC: received %d bytes size frame" WRMSG(HHC00905, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, (int)iLength ); packet_trace( pCTCBLK->bFrameBuffer, (int)iLength, '>' ); } // Reset frame buffer pCTCBLK->iFrameOffset = 0; pCTCBLK->fDataPending = 0; release_lock( &pCTCBLK->Lock ); return; } } // ------------------------------------------------------------------- // CTCI_Write // ------------------------------------------------------------------- // // For details regarding the actual buffer layout, please refer to // the comments preceding the CTCI_ReadThread function. // void CTCI_Write( DEVBLK* pDEVBLK, U32 sCount, BYTE* pIOBuf, BYTE* pUnitStat, U32* pResidual ) { PCTCBLK pCTCBLK = (PCTCBLK)pDEVBLK->dev_data; PCTCIHDR pFrame; // -> Frame header PCTCISEG pSegment; // -> Segment in buffer U16 sOffset; // Offset of next frame U16 sSegLen; // Current segment length U16 sDataLen; // Length of IP Frame data int iPos; // Offset into buffer U16 i; // Array subscript int rc; // Return code BYTE szStackID[33]; // VSE IP stack identity U32 iStackCmd; // VSE IP stack command // Check that CCW count is sufficient to contain block header if( sCount < sizeof( CTCIHDR ) ) { // "%1d:%04X CTC: write CCW count %u is invalid" WRMSG(HHC00906, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, sCount ); pDEVBLK->sense[0] = SENSE_DC; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } // Fix-up frame pointer pFrame = (PCTCIHDR)pIOBuf; // Extract the frame length from the header FETCH_HW( sOffset, pFrame->hwOffset ); // Check for special VSE TCP/IP stack command packet if( sOffset == 0 && sCount == 40 ) { // Extract the 32-byte stack identity string for( i = 0; i < sizeof( szStackID ) - 1 && i < sCount - 4; i++) szStackID[i] = guest_to_host( pIOBuf[i+4] ); szStackID[i] = '\0'; // Extract the stack command word FETCH_FW( iStackCmd, *((FWORD*)&pIOBuf[36]) ); // Display stack command and discard the packet // "%1d:%04X CTC: interface command: '%s' %8.8X" WRMSG(HHC00907, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, szStackID, iStackCmd ); *pUnitStat = CSW_CE | CSW_DE; *pResidual = 0; return; } // Check for special L/390 initialization packet if( sOffset == 0 ) { // Return normal status and discard the packet *pUnitStat = CSW_CE | CSW_DE; *pResidual = 0; return; } #if 0 // Notes: It appears that TurboLinux has gotten sloppy in their // ways. They are now giving us buffer sizes that are // greater than the CCW count, but the segment size // is within the count. // Check that the frame offset is valid if( sOffset < sizeof( CTCIHDR ) || sOffset > sCount ) { logmsg( _("CTC101W %4.4X: Write buffer contains invalid " "frame offset %u\n"), pDEVBLK->devnum, sOffset ); pDEVBLK->sense[0] = SENSE_CR; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } #endif // Adjust the residual byte count *pResidual -= sizeof( CTCIHDR ); // Process each segment in the buffer for( iPos = sizeof( CTCIHDR ); iPos < sOffset; iPos += sSegLen ) { // Check that the segment is fully contained within the block if( iPos + sizeof( CTCISEG ) > sOffset ) { // "%1d:%04X CTC: incomplete write buffer segment header at offset %4.4X" WRMSG(HHC00908, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, iPos ); pDEVBLK->sense[0] = SENSE_DC; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } // Fix-up segment header in the I/O buffer pSegment = (PCTCISEG)(pIOBuf + iPos); // Extract the segment length from the segment header FETCH_HW( sSegLen, pSegment->hwLength ); // Check that the segment length is valid if( ( sSegLen < sizeof( CTCISEG ) ) || ( (U32)iPos + sSegLen > sOffset ) || ( (U32)iPos + sSegLen > sCount ) ) { // "%1d:%04X CTC: invalid write buffer segment length %u at offset %4.4X" WRMSG(HHC00909, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, sSegLen, iPos ); pDEVBLK->sense[0] = SENSE_DC; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } // Calculate length of IP frame data sDataLen = sSegLen - sizeof( CTCISEG ); // Trace the IP packet before sending to TUN device if( pCTCBLK->fDebug ) { // "%1d:%04X CTC: sending packet to device '%s'" WRMSG(HHC00910, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pCTCBLK->szTUNIfName ); packet_trace( pSegment->bData, sDataLen, '<' ); } // Write the IP packet to the TUN/TAP interface rc = TUNTAP_Write( pCTCBLK->fd, pSegment->bData, sDataLen ); if( rc < 0 ) { // "%1d:%04X CTC: error writing to device '%s': '%s'" WRMSG(HHC00911, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pCTCBLK->szTUNIfName, strerror( errno ) ); pDEVBLK->sense[0] = SENSE_EC; *pUnitStat = CSW_CE | CSW_DE | CSW_UC; return; } // Adjust the residual byte count *pResidual -= sSegLen; // We are done if current segment satisfies CCW count if( (U32)iPos + sSegLen == sCount ) { *pResidual -= sSegLen; *pUnitStat = CSW_CE | CSW_DE; return; } } // Set unit status and residual byte count *pUnitStat = CSW_CE | CSW_DE; *pResidual = 0; } // -------------------------------------------------------------------- // CTCI_ReadThread // -------------------------------------------------------------------- // // When an IP frame is received from the TUN/TAP interface, the frame // is enqueued on the device frame buffer. // // The device frame buffer is a chain of blocks. The first 2 bytes of // a block (CTCIHDR) specify the offset in the buffer of the next block. // The final block in indicated by a CTCIHDR offset value of 0x0000. // // Within each block, each IP frame is preceeded by a segment header // (CTCISEG). This segment header has a 2 byte length field that // specifies the length of the segment (including the segment header), // a 2 byte frame type field (always 0x0800 = IPv4), and a 2 byte // reserved area (always 0000), followed by the actual frame data. // // The CTCI_ReadThread reads the IP frame, then CTCI_EnqueueIPFrame // function is called to add it to the frame buffer (which precedes // each one with a CTCISEG and adjusts the block header (CTCIHDR) // offset value as appropriate. // // Oddly, it is the CTCI_Read function (called by CCW processing in // response to a guest SIO request) that adds the CTCIHDR with the // 000 offset value marking the end of the buffer's chain of blocks, // and not the CTCI_EnqueueIPFrame nor the CTCI_ReadThread as would // be expected. // // Also note that the iFrameOffset field in the CTCI device's CTCBLK // control block is the offset from the end of the buffer's first // CTCIHDR to where the end-of-chain CTCIHDR is, and is identical to // all of the queued CTCISEG's hwLength fields added together. // static void* CTCI_ReadThread( void* arg ) { PCTCBLK pCTCBLK = (PCTCBLK) arg; DEVBLK* pDEVBLK = pCTCBLK->pDEVBLK[0]; int iLength; BYTE szBuff[2048]; // ZZ FIXME: Try to avoid race condition at startup with hercifc #if defined(BUILD_HERCIFC) SLEEP(10); #endif pCTCBLK->pid = getpid(); while( pCTCBLK->fd != -1 && !pCTCBLK->fCloseInProgress ) { // Read frame from the TUN/TAP interface iLength = TUNTAP_Read( pCTCBLK->fd, szBuff, sizeof(szBuff) ); // Check for error condition if( iLength < 0 ) { if( !pCTCBLK->fCloseInProgress ) { // "%1d:%04X CTC: error reading from device '%s': '%s'" WRMSG(HHC00912, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pCTCBLK->szTUNIfName, strerror( errno ) ); } break; } if( iLength == 0 ) // (probably EINTR; ignore) continue; if( pCTCBLK->fDebug ) { // "%1d:%04X CTC: received %d bytes size packet from device '%s'" WRMSG(HHC00913, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, iLength, pCTCBLK->szTUNIfName ); packet_trace( szBuff, iLength, '>' ); } // Enqueue frame on buffer, if buffer is full, keep trying while( CTCI_EnqueueIPFrame( pDEVBLK, szBuff, iLength ) < 0 && pCTCBLK->fd != -1 && !pCTCBLK->fCloseInProgress ) { if( EMSGSIZE == errno ) // (if too large for buffer) { if( pCTCBLK->fDebug ) { // "%1d:%04X CTC: packet frame too big, dropped" WRMSG(HHC00914, "W", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); } break; // (discard it...) } ASSERT( ENOBUFS == errno ); // Don't use sched_yield() here; use an actual non-dispatchable // delay instead so as to allow another [possibly lower priority] // thread to 'read' (remove) some packet(s) from our frame buffer. usleep( CTC_DELAY_USECS ); // (wait a bit before retrying...) } } // We must do the close since we were the one doing the i/o... VERIFY( pCTCBLK->fd == -1 || TUNTAP_Close( pCTCBLK->fd ) == 0 ); pCTCBLK->fd = -1; return NULL; } // -------------------------------------------------------------------- // CTCI_EnqueueIPFrame // -------------------------------------------------------------------- // // Places the provided IP frame in the next available frame slot in // the adapter buffer. For details regarding the actual buffer layout // please refer to the comments preceding the CTCI_ReadThread function. // // Returns: // // 0 == Success // -1 == Failure; errno = ENOBUFS: No buffer space available // EMSGSIZE: Message too long // static int CTCI_EnqueueIPFrame( DEVBLK* pDEVBLK, BYTE* pData, size_t iSize ) { PCTCIHDR pFrame; PCTCISEG pSegment; PCTCBLK pCTCBLK = (PCTCBLK)pDEVBLK->dev_data; // Will frame NEVER fit into buffer?? if( iSize > MAX_CTCI_FRAME_SIZE( pCTCBLK ) || iSize > 9000 ) { errno = EMSGSIZE; // Message too long return -1; // (-1==failure) } obtain_lock( &pCTCBLK->Lock ); // Ensure we dont overflow the buffer if( ( pCTCBLK->iFrameOffset + // Current buffer Offset sizeof( CTCIHDR ) + // Size of Block Header sizeof( CTCISEG ) + // Size of Segment Header iSize + // Size of Ethernet packet sizeof(pFrame->hwOffset) ) // Size of Block terminator > pCTCBLK->iMaxFrameBufferSize ) // Size of Frame buffer { release_lock( &pCTCBLK->Lock ); errno = ENOBUFS; // No buffer space available return -1; // (-1==failure) } // Fix-up Frame pointer pFrame = (PCTCIHDR)pCTCBLK->bFrameBuffer; // Fix-up Segment pointer pSegment = (PCTCISEG)( pCTCBLK->bFrameBuffer + sizeof( CTCIHDR ) + pCTCBLK->iFrameOffset ); // Initialize segment memset( pSegment, 0, iSize + sizeof( CTCISEG ) ); // Increment offset pCTCBLK->iFrameOffset += (U16)(sizeof( CTCISEG ) + iSize); // Update next frame offset STORE_HW( pFrame->hwOffset, pCTCBLK->iFrameOffset + sizeof( CTCIHDR ) ); // Store segment length STORE_HW( pSegment->hwLength, (U16)(sizeof( CTCISEG ) + iSize) ); // Store Frame type STORE_HW( pSegment->hwType, ETH_TYPE_IP ); // Copy data memcpy( pSegment->bData, pData, iSize ); // Mark data pending pCTCBLK->fDataPending = 1; release_lock( &pCTCBLK->Lock ); obtain_lock( &pCTCBLK->EventLock ); signal_condition( &pCTCBLK->Event ); release_lock( &pCTCBLK->EventLock ); return 0; // (0==success) } // // ParseArgs // static int ParseArgs( DEVBLK* pDEVBLK, PCTCBLK pCTCBLK, int argc, char** argx ) { int saw_if = 0; /* -if specified */ int saw_conf = 0; /* Other configuration flags present */ struct in_addr addr; // Work area for addresses int iMTU; int i; MAC mac; // Work area for MAC address #if defined(OPTION_W32_CTCI) int iKernBuff; int iIOBuff; #endif char *argn[MAX_ARGS]; char **argv = argn; // Copy argv (as this routine needs to make local changes) for(i=0; iszMTU, "1500", sizeof(pCTCBLK->szMTU) ); strlcpy( pCTCBLK->szNetMask, "255.255.255.255", sizeof(pCTCBLK->szNetMask) ); #if defined( OPTION_W32_CTCI ) strlcpy( pCTCBLK->szTUNCharDevName, tt32_get_default_iface(), sizeof(pCTCBLK->szTUNCharDevName) ); #else strlcpy( pCTCBLK->szTUNCharDevName, HERCTUN_DEV, sizeof(pCTCBLK->szTUNCharDevName) ); #endif #if defined( OPTION_W32_CTCI ) pCTCBLK->iKernBuff = DEF_CAPTURE_BUFFSIZE; pCTCBLK->iIOBuff = DEF_PACKET_BUFFSIZE; #endif // Initialize getopt's counter. This is necessary in the case // that getopt was used previously for another device. OPTRESET(); optind = 0; // Check for correct number of arguments if( argc < 1 ) { // "%1d:%04X CTC: incorrect number of parameters" WRMSG(HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); return -1; } // Compatability with old format configuration files needs to be // maintained. Old format statements have the tun character device // name as the second argument on Linux, or CTCI-W32 as the first // argument on Windows. if( ( strncasecmp( argv[0], "/", 1 ) == 0 ) || ( strncasecmp( pDEVBLK->typname, "CTCI-W32", 8 ) == 0 ) ) { pCTCBLK->fOldFormat = 1; } else { // Build new argv list. // getopt_long used to work on old format configuration statements // because LCS was the first argument passed to the device // initialization routine (and was interpreted by getopt* // as the program name and ignored). Now that argv[0] is a valid // argument, we need to shift the arguments and insert a dummy // argv[0]; // Don't allow us to exceed the allocated storage (sanity check) if( argc > (MAX_ARGS-1) ) argc = (MAX_ARGS-1); for( i = argc; i > 0; i-- ) argv[i] = argv[i - 1]; argc++; argv[0] = pDEVBLK->typname; } // Parse any optional arguments if not old format while( !pCTCBLK->fOldFormat ) { int c; #if defined( OPTION_W32_CTCI ) #define CTCI_OPTSTRING "n:k:i:m:t:s:d" #else #define CTCI_OPTSTRING "n:x:t:s:d" #endif #if defined(HAVE_GETOPT_LONG) int iOpt; static struct option options[] = { { "dev", required_argument, NULL, 'n' }, #if !defined( OPTION_W32_CTCI ) { "tundev", required_argument, NULL, 'x' }, { "if", required_argument, NULL, 'x' }, #endif #if defined( OPTION_W32_CTCI ) { "kbuff", required_argument, NULL, 'k' }, { "ibuff", required_argument, NULL, 'i' }, { "mac", required_argument, NULL, 'm' }, #endif { "mtu", required_argument, NULL, 't' }, { "netmask", required_argument, NULL, 's' }, { "debug", no_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; c = getopt_long( argc, argv, CTCI_OPTSTRING, options, &iOpt ); #else /* defined(HAVE_GETOPT_LONG) */ c = getopt( argc, argv, CTCI_OPTSTRING ); #endif /* defined(HAVE_GETOPT_LONG) */ if( c == -1 ) // No more options found break; switch( c ) { case 'n': // Network Device (special character device) #if defined( OPTION_W32_CTCI ) // This could be the IP or MAC address of the // host ethernet adapter. if( inet_aton( optarg, &addr ) == 0 ) { // Not an IP address, check for valid MAC if( ParseMAC( optarg, mac ) != 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "adapter address", optarg ); return -1; } } #endif // defined( OPTION_W32_CTCI ) // This is the file name of the special TUN/TAP character device if( strlen( optarg ) > sizeof( pCTCBLK->szTUNCharDevName ) - 1 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "device name", optarg ); return -1; } strlcpy( pCTCBLK->szTUNCharDevName, optarg, sizeof(pCTCBLK->szTUNCharDevName) ); break; #if !defined( OPTION_W32_CTCI ) case 'x': // TUN network interface name if( strlen( optarg ) > sizeof(pCTCBLK->szTUNIfName)-1 ) { // HHC00916 "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "TUN device name", optarg ); return -1; } strlcpy( pCTCBLK->szTUNIfName, optarg, sizeof(pCTCBLK->szTUNIfName) ); saw_if = 1; break; #endif #if defined( OPTION_W32_CTCI ) case 'k': // Kernel Buffer Size (Windows only) iKernBuff = atoi( optarg ); if( iKernBuff * 1024 < MIN_CAPTURE_BUFFSIZE || iKernBuff * 1024 > MAX_CAPTURE_BUFFSIZE ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "kernel buffer size", optarg ); return -1; } pCTCBLK->iKernBuff = iKernBuff * 1024; break; case 'i': // I/O Buffer Size (Windows only) iIOBuff = atoi( optarg ); if( iIOBuff * 1024 < MIN_PACKET_BUFFSIZE || iIOBuff * 1024 > MAX_PACKET_BUFFSIZE ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "dll i/o buffer size", optarg ); return -1; } pCTCBLK->iIOBuff = iIOBuff * 1024; break; case 'm': if( ParseMAC( optarg, mac ) != 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "MAC address", optarg ); return -1; } strlcpy( pCTCBLK->szMACAddress, optarg, sizeof(pCTCBLK->szMACAddress) ); saw_conf = 1; break; #endif // defined( OPTION_W32_CTCI ) case 't': // MTU of point-to-point link (ignored if Windows) iMTU = atoi( optarg ); if( iMTU < 46 || iMTU > 65536 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "MTU size", optarg ); return -1; } strlcpy( pCTCBLK->szMTU, optarg, sizeof(pCTCBLK->szMTU) ); saw_conf = 1; break; case 's': // Netmask of point-to-point link if( inet_aton( optarg, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "netmask", optarg ); return -1; } strlcpy( pCTCBLK->szNetMask, optarg, sizeof(pCTCBLK->szNetMask) ); saw_conf = 1; break; case 'd': // Diagnostics pCTCBLK->fDebug = TRUE; break; default: break; } } // Shift past any options argc -= optind; argv += optind; i = 0; if( !pCTCBLK->fOldFormat ) { // New format. // For *nix, there can be either:- // a) Two parameters (a pair of IPv4 addresses). If the -x option // has not been specified, CTCI will use a TUN interface whose // name is allocated by the kernel (e.g. tun0), that is // configured by CTCI. If the -x option has been specified, // CTCI will use a pre-named TUN interface. The TUN interface // may have been created before CTCI was started, or it may be // created by CTCI, but in either case the TUN interface is // configured by CTCI. // b) One parameter when the -x option has not been specified. // The single parameter specifies the name of a pre-configured // TUN inferface that CTCI will use. // c) Zero parameters when the -x option has been specified. The // The -x option specified the name of a pre-configured TUN // inferface that CTCI will use.. // For Windows there can be:- // a) Two parameters (a pair of IPv4 addresses). if (argc == 2 ) /* Not pre-configured, but possibly pre-named */ { // Guest IP Address if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "IP address", *argv ); return -1; } strlcpy( pCTCBLK->szGuestIPAddr, *argv, sizeof(pCTCBLK->szGuestIPAddr) ); argc--; argv++; // Driver IP Address if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "IP address", *argv ); return -1; } strlcpy( pCTCBLK->szDriveIPAddr, *argv, sizeof(pCTCBLK->szDriveIPAddr) ); argc--; argv++; pCTCBLK->fPreconfigured = FALSE; } #if !defined( OPTION_W32_CTCI ) else if (argc == 1 && !saw_if && !saw_conf) /* Pre-configured using name */ { if( strlen( *argv ) > sizeof(pCTCBLK->szTUNIfName)-1 ) { // HHC00916 "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "TUN device name", optarg ); return -1; } strlcpy( pCTCBLK->szTUNIfName, *argv, sizeof(pCTCBLK->szTUNIfName) ); argc--; argv++; pCTCBLK->fPreconfigured = TRUE; } else if (argc == 0 && saw_if && !saw_conf) /* Pre-configured using -x option */ { pCTCBLK->fPreconfigured = TRUE; } #endif /* !defined( OPTION_W32_CTCI ) */ else { // "%1d:%04X CTC: incorrect number of parameters" WRMSG(HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); return -1; } #if defined(__APPLE__) || defined(__FreeBSD__) if (TRUE == pCTCBLK->fPreconfigured) { /* Need to append the interface number to the character */ /* device name to open the requested interface. */ char * s = pCTCBLK->szTUNIfName + strlen(pCTCBLK->szTUNIfName); while(isdigit(s[- 1])) s--; strlcat( pCTCBLK->szTUNCharDevName, s, sizeof(pCTCBLK->szTUNCharDevName) ); } #endif } else // if( pCTCBLK->fOldFormat ) { #if !defined( OPTION_W32_CTCI ) // All arguments are non-optional in linux old-format // Old format has 5 and only 5 arguments if( argc != 5 ) { // "%1d:%04X CTC: incorrect number of parameters" WRMSG(HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); return -1; } // TUN/TAP Device if( **argv != '/' || strlen( *argv ) > sizeof( pCTCBLK->szTUNCharDevName ) - 1 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "device name", *argv ); return -1; } strlcpy( pCTCBLK->szTUNCharDevName, *argv, sizeof(pCTCBLK->szTUNCharDevName) ); argc--; argv++; // MTU Size iMTU = atoi( *argv ); if( iMTU < 46 || iMTU > 65536 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "MTU size", *argv ); return -1; } strlcpy( pCTCBLK->szMTU, *argv, sizeof(pCTCBLK->szMTU) ); argc--; argv++; // Guest IP Address if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "IP address", *argv ); return -1; } strlcpy( pCTCBLK->szGuestIPAddr, *argv, sizeof(pCTCBLK->szGuestIPAddr) ); argc--; argv++; // Driver IP Address if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "IP address", *argv ); return -1; } strlcpy( pCTCBLK->szDriveIPAddr, *argv, sizeof(pCTCBLK->szDriveIPAddr) ); argc--; argv++; // Netmask if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "netmask", *argv ); return -1; } strlcpy( pCTCBLK->szNetMask, *argv, sizeof(pCTCBLK->szNetMask) ); argc--; argv++; if( argc > 0 ) { // "%1d:%04X CTC: incorrect number of parameters" WRMSG(HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); return -1; } #else // defined( OPTION_W32_CTCI ) // There are 2 non-optional arguments in the Windows old-format: // Guest IP address and Gateway address. // There are also 2 additional optional arguments: // Kernel buffer size and I/O buffer size. while( argc > 0 ) { switch( i ) { case 0: // Non-optional arguments // Guest IP Address if( inet_aton( *argv, &addr ) == 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "IP address", *argv ); return -1; } strlcpy( pCTCBLK->szGuestIPAddr, *argv, sizeof(pCTCBLK->szGuestIPAddr) ); argc--; argv++; // Destination (Gateway) Address if( inet_aton( *argv, &addr ) == 0 ) { // Not an IP address, check for valid MAC if( ParseMAC( *argv, mac ) != 0 ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "MAC address", *argv ); return -1; } } strlcpy( pCTCBLK->szTUNCharDevName, *argv, sizeof(pCTCBLK->szTUNCharDevName) ); // Kludge: This may look strange at first, but with // TunTap32, only the last 3 bytes of the "driver IP // address" is actually used. It's purpose is to // generate a unique MAC for the virtual interface. // Thus, having the same address for the adapter and // destination is not an issue. This used to be // generated from the guest IP address, I screwed up // TunTap32 V2. (JAP) // This also fixes the confusing error messages from // TunTap.c when a MAC is given for this argument. strlcpy( pCTCBLK->szDriveIPAddr, pCTCBLK->szGuestIPAddr, sizeof(pCTCBLK->szDriveIPAddr) ); argc--; argv++; i++; continue; case 1: // Optional arguments from here on: // Kernel Buffer Size iKernBuff = atoi( *argv ); if( iKernBuff * 1024 < MIN_CAPTURE_BUFFSIZE || iKernBuff * 1024 > MAX_CAPTURE_BUFFSIZE ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "kernel buffer size", *argv ); return -1; } pCTCBLK->iKernBuff = iKernBuff * 1024; argc--; argv++; i++; continue; case 2: // I/O Buffer Size iIOBuff = atoi( *argv ); if( iIOBuff * 1024 < MIN_PACKET_BUFFSIZE || iIOBuff * 1024 > MAX_PACKET_BUFFSIZE ) { // "%1d:%04X CTC: option '%s' value '%s' invalid" WRMSG(HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "dll i/o buffer size", *argv ); return -1; } pCTCBLK->iIOBuff = iIOBuff * 1024; argc--; argv++; i++; continue; default: // "%1d:%04X CTC: incorrect number of parameters" WRMSG(HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum ); return -1; } } #endif // !defined( OPTION_W32_CTCI ) } return 0; } #endif /* !defined(__SOLARIS__) jbs */