Files
org-hyperion-cules/ctc_ctci.c

1681 lines
59 KiB
C

/* 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; i<argc && i<MAX_ARGS;i++)
argn[i]=argx[i];
// Housekeeping
memset( &addr, 0, sizeof( struct in_addr ) );
memset( &mac, 0, sizeof( MAC ) );
// Set some initial defaults
strlcpy( pCTCBLK->szMTU, "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 */