int TUNTAP_SetNetMask( char* pszNetDevName, char* pszNetMask ) { struct hifr hifr; struct sockaddr_in* sin; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); sin = (struct sockaddr_in*)&hifr.hifr_netmask; sin->sin_family = AF_INET; set_sockaddr_in_sin_len( sin ); if( !pszNetMask || !inet_aton( pszNetMask, &sin->sin_addr ) ) { WRMSG( HHC00143, "E", pszNetDevName, !pszNetMask ? "NULL" : pszNetMask ); return -1; } return TUNTAP_IOCtl( 0, SIOCSIFNETMASK, (char*)&hifr ); } // End of function TUNTAP_SetNetMask()
int TUNTAP_SetMACAddr( char* pszNetDevName, char* pszMACAddr ) { struct hifr hifr; struct sockaddr* addr; MAC mac; if( !pszNetDevName || !*pszNetDevName ) { // "Invalid net device name %s" WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } if( !pszMACAddr || ParseMAC( pszMACAddr, mac ) != 0 ) { // "Net device %s: Invalid MAC address %s" WRMSG( HHC00145, "E", pszNetDevName, pszMACAddr ? pszMACAddr : "NULL" ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); addr = (struct sockaddr*)&hifr.hifr_hwaddr; memcpy( addr->sa_data, mac, IFHWADDRLEN ); addr->sa_family = 1; // ARPHRD_ETHER return TUNTAP_IOCtl( 0, SIOCSIFHWADDR, (char*)&hifr ); } // End of function TUNTAP_SetMACAddr()
// // TUNTAP_SetMTU // int TUNTAP_SetMTU( char* pszNetDevName, char* pszMTU ) { struct hifr hifr; int iMTU; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } if( !pszMTU || !*pszMTU ) { WRMSG( HHC00144, "E", pszNetDevName, pszMTU ? pszMTU : "NULL" ); return -1; } iMTU = atoi( pszMTU ); if( iMTU < 46 || iMTU > 65536 ) { WRMSG( HHC00144, "E", pszNetDevName, pszMTU ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); hifr.hifr_mtu = iMTU; return TUNTAP_IOCtl( 0, SIOCSIFMTU, (char*)&hifr ); } // End of function TUNTAP_SetMTU()
int TUNTAP_SetBCastAddr( char* pszNetDevName, char* pszBCastAddr ) { struct hifr hifr; struct sockaddr_in* sin; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); sin = (struct sockaddr_in*)&hifr.hifr_broadaddr; sin->sin_family = AF_INET; set_sockaddr_in_sin_len( sin ); if( !pszBCastAddr || !inet_aton( pszBCastAddr, &sin->sin_addr ) ) { WRMSG( HHC00155, "E", pszNetDevName, !pszBCastAddr ? "NULL" : pszBCastAddr ); return -1; } return TUNTAP_IOCtl( 0, SIOCSIFBRDADDR, (char*)&hifr ); } // End of function TUNTAP_SetBCastAddr()
int TUNTAP_SetMACAddr( char* pszNetDevName, char* pszMACAddr ) { struct hifr hifr; struct sockaddr* addr; MAC mac; memset( &hifr, 0, sizeof( struct hifr ) ); addr = (struct sockaddr*)&hifr.hifr_hwaddr; addr->sa_family = AF_UNIX; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } strcpy( hifr.hifr_name, pszNetDevName ); if( !pszMACAddr || ParseMAC( pszMACAddr, mac ) != 0 ) { WRMSG( HHC00145, "E", pszNetDevName, pszMACAddr ? pszMACAddr : "NULL" ); return -1; } memcpy( addr->sa_data, mac, IFHWADDRLEN ); return TUNTAP_IOCtl( 0, SIOCSIFHWADDR, (char*)&hifr ); } // End of function TUNTAP_SetMACAddr()
int TUNTAP_DelRoute( char* pszNetDevName, char* pszDestAddr, char* pszNetMask, char* pszGWAddr, int iFlags ) { struct rtentry rtentry; struct sockaddr_in* sin; memset( &rtentry, 0, sizeof( struct rtentry ) ); if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } rtentry.rt_dev = pszNetDevName; sin = (struct sockaddr_in*)&rtentry.rt_dst; sin->sin_family = AF_INET; set_sockaddr_in_sin_len( sin ); if( !pszDestAddr || !inet_aton( pszDestAddr, &sin->sin_addr ) ) { WRMSG(HHC00142, "E", pszNetDevName, pszDestAddr ? pszDestAddr : "NULL" ); return -1; } sin = (struct sockaddr_in*)&rtentry.rt_genmask; sin->sin_family = AF_INET; set_sockaddr_in_sin_len( sin ); if( !pszNetMask || !inet_aton( pszNetMask, &sin->sin_addr ) ) { WRMSG( HHC00143, "E", pszNetDevName, pszNetMask ? pszNetMask : "NULL" ); return -1; } sin = (struct sockaddr_in*)&rtentry.rt_gateway; sin->sin_family = AF_INET; set_sockaddr_in_sin_len( sin ); if( pszGWAddr ) { if( !inet_aton( pszGWAddr, &sin->sin_addr ) ) { WRMSG( HHC00146, "E", pszNetDevName, pszGWAddr ); return -1; } } rtentry.rt_flags = iFlags; return TUNTAP_IOCtl( 0, SIOCDELRT, (char*)&rtentry ); } // End of function TUNTAP_DelRoute()
// // TUNTAP_GetMACAddr // int TUNTAP_GetMACAddr( char* pszNetDevName, char** ppszMACAddr ) { #if defined(OPTION_TUNTAP_GETMACADDR) struct hifr hifr; struct sockaddr* addr; int rc; if( !pszNetDevName || !*pszNetDevName ) { // "Invalid net device name %s" WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } if( !ppszMACAddr ) { // HHC00136 "Error in function %s: %s" WRMSG(HHC00136, "E", "TUNTAP_GetMACAddr", "Invalid parameters" ); return -1; } *ppszMACAddr = NULL; memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); addr = (struct sockaddr*)&hifr.hifr_hwaddr; addr->sa_family = 1; // ARPHRD_ETHER #if defined( OPTION_W32_CTCI ) rc = TUNTAP_IOCtl( 0, SIOCGIFHWADDR, (char*)&hifr ); #else // (non-Win32 platforms) { int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); rc = ioctl( sockfd, SIOCGIFHWADDR, &hifr ); close( sockfd ); } #endif if( rc < 0 ) { // HHC00136 "Error in function %s: %s" WRMSG( HHC00136, "E", "TUNTAP_GetMACAddr", strerror( errno )); return -1; } return FormatMAC( ppszMACAddr, (BYTE*) addr->sa_data ); #else // defined(OPTION_TUNTAP_GETMACADDR) UNREFERENCED(pszNetDevName); UNREFERENCED(ppszMACAddr); WRMSG(HHC00136, "E", "TUNTAP_GetMACAddr", "Unsupported" ); return -1; // (unsupported) #endif // defined(OPTION_TUNTAP_GETMACADDR) } // End of function TUNTAP_GetMACAddr()
// // TUNTAP_GetMTU // int TUNTAP_GetMTU( char* pszNetDevName, char** ppszMTU ) { struct hifr hifr; int rc; char szMTU[8] = {0}; if( !pszNetDevName || !*pszNetDevName ) { // "Invalid net device name %s" WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } if( !ppszMTU ) { // HHC00136 "Error in function %s: %s" WRMSG(HHC00136, "E", "TUNTAP_GetMTU", "Invalid parameters" ); return -1; } *ppszMTU = NULL; memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); #if defined( OPTION_W32_CTCI ) rc = TUNTAP_IOCtl( 0, SIOCGIFMTU, (char*)&hifr ); #else // (non-Win32 platforms) { int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); rc = ioctl( sockfd, SIOCGIFMTU, &hifr ); close( sockfd ); } #endif if( rc < 0 ) { // HHC00136 "Error in function %s: %s" WRMSG( HHC00136, "E", "TUNTAP_GetMTU", strerror( errno )); return -1; } MSGBUF( szMTU, "%u", hifr.hifr_mtu ); if (!(*ppszMTU = strdup( szMTU ))) { errno = ENOMEM; return -1; } return 0; } // End of function TUNTAP_GetMTU()
int TUNTAP_SetIPAddr6( char* pszNetDevName, char* pszIPAddr6, char* pszPrefixSize6 ) { struct hifr hifr; int iPfxSiz; memset( &hifr, 0, sizeof( struct hifr ) ); if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } strcpy( hifr.hifr_name, pszNetDevName ); if( !pszIPAddr6 ) { WRMSG( HHC00141, "E", pszNetDevName, "NULL" ); return -1; } if( hinet_pton( AF_INET6, pszIPAddr6, &hifr.hifr6_addr ) != 1 ) { WRMSG( HHC00141, "E", pszNetDevName, pszIPAddr6 ); return -1; } if( !pszPrefixSize6 ) { WRMSG(HHC00153, "E", pszNetDevName, "NULL" ); return -1; } iPfxSiz = atoi( pszPrefixSize6 ); if( iPfxSiz < 0 || iPfxSiz > 128 ) { WRMSG(HHC00153, "E", pszNetDevName, pszPrefixSize6 ); return -1; } hifr.hifr6_prefixlen = iPfxSiz; hifr.hifr6_ifindex = hif_nametoindex( pszNetDevName ); hifr.hifr_afamily = AF_INET6; return TUNTAP_IOCtl( 0, SIOCSIFADDR, (char*)&hifr ); } // End of function TUNTAP_SetIPAddr6()
int TUNTAP_ClrIPAddr( char* pszNetDevName ) { struct hifr hifr; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name)); return TUNTAP_IOCtl( 0, SIOCDIFADDR, (char*)&hifr ); } // End of function TUNTAP_ClrIPAddr()
int TUNTAP_SetFlags ( char* pszNetDevName, int iFlags ) { struct hifr hifr; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } memset( &hifr, 0, sizeof( struct hifr ) ); strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name) ); hifr.hifr_flags = iFlags; return TUNTAP_IOCtl( 0, SIOCSIFFLAGS, (char*)&hifr ); } // End of function TUNTAP_SetFlags()
int TUNTAP_GetFlags ( char* pszNetDevName, int* piFlags ) { struct hifr hifr; struct sockaddr_in* sin; int rc; memset( &hifr, 0, sizeof( struct hifr ) ); sin = (struct sockaddr_in*)&hifr.hifr_addr; sin->sin_family = AF_INET; if( !pszNetDevName || !*pszNetDevName ) { WRMSG( HHC00140, "E", pszNetDevName ? pszNetDevName : "NULL" ); return -1; } strlcpy( hifr.hifr_name, pszNetDevName, sizeof(hifr.hifr_name) ); // PROGRAMMING NOTE: hercifc can't "get" information, // only "set" it. Thus because we normally use hercifc // to issue ioctl codes to the interface (on non-Win32) // we bypass hercifc altogether and issue the ioctl // ourselves directly to the device itself, bypassing // hercifc completely. Note that for Win32 however, // 'TUNTAP_IOCtl' routes to a TunTap32.DLL call and // thus works just fine. We need special handling // only for non-Win32 platforms. - Fish #if defined( OPTION_W32_CTCI ) rc = TUNTAP_IOCtl( 0, SIOCGIFFLAGS, (char*)&hifr ); #else // (non-Win32 platforms) { int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); rc = ioctl( sockfd, SIOCGIFFLAGS, &hifr ); } #endif *piFlags = hifr.hifr_flags; return rc; } // End of function TUNTAP_GetFlags()
// // TUNTAP_SetMode (TUNTAP_CreateInterface helper) // static int TUNTAP_SetMode (int fd, struct hifr *hifr, int iFlags) { int rc; /* Try TUNTAP_ioctl first */ rc = TUNTAP_IOCtl (fd, TUNSETIFF, (char *) hifr); #if !defined(OPTION_W32_CTCI) /* If invalid value, try with the pre-2.4.5 value */ if (0 > rc && errno == EINVAL) rc = TUNTAP_IOCtl (fd, ('T' << 8) | 202, (char *) hifr); /* kludge for EPERM and linux 2.6.18 */ if (0 > rc && errno == EPERM && !(IFF_NO_HERCIFC & iFlags)) { int ifd[2]; char *hercifc; pid_t pid; CTLREQ ctlreq; fd_set selset; struct timeval tv; int sv_err; int status; if (socketpair (AF_UNIX, SOCK_STREAM, 0, ifd) < 0) return -1; if (!(hercifc = getenv ("HERCULES_IFC"))) hercifc = HERCIFC_CMD; pid = fork(); if (pid < 0) return -1; else if (pid == 0) { /* child */ dup2 (ifd[0], STDIN_FILENO); dup2 (STDOUT_FILENO, STDERR_FILENO); dup2 (ifd[0], STDOUT_FILENO); close (ifd[1]); rc = execlp (hercifc, hercifc, NULL ); return -1; } /* parent */ close(ifd[0]); /* Request hercifc to issue the TUNSETIFF ioctl */ memset (&ctlreq, 0, CTLREQ_SIZE); ctlreq.iCtlOp = TUNSETIFF; ctlreq.iProcID = fd; memcpy (&ctlreq.iru.hifr, hifr, sizeof (struct hifr)); write (ifd[1], &ctlreq, CTLREQ_SIZE); /* Get response, if any, from hercifc */ FD_ZERO (&selset); FD_SET (ifd[1], &selset); tv.tv_sec = 5; tv.tv_usec = 0; rc = select (ifd[1]+1, &selset, NULL, NULL, &tv); if (rc > 0) { rc = read (ifd[1], &ctlreq, CTLREQ_SIZE); if (rc > 0) memcpy (hifr, &ctlreq.iru.hifr, sizeof (struct hifr)); } else if (rc == 0) { WRMSG (HHC00135, "E", hercifc); errno = EPERM; rc = -1; } /* clean-up */ sv_err = errno; close (ifd[1]); kill (pid, SIGKILL); waitpid (pid, &status, 0); errno = sv_err; } #endif /* if !defined(OPTION_W32_CTCI) */ return rc; } // End of function TUNTAP_SetMode()
int CTCI_Init( DEVBLK* pDEVBLK, int argc, char *argv[] ) { PCTCBLK pWrkCTCBLK = NULL; // Working CTCBLK PCTCBLK pDevCTCBLK = NULL; // Device CTCBLK int rc = 0; // Return code int nIFType; // Interface type int nIFFlags; // Interface flags char thread_name[32]; // CTCI_ReadThread nIFType = // Interface type 0 | IFF_TUN // ("TUN", not "tap") | IFF_NO_PI // (no packet info) ; 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; // CTC is a group device if(!group_device(pDEVBLK, CTC_DEVICES_IN_GROUP)) return 0; // Housekeeping pWrkCTCBLK = malloc( sizeof( CTCBLK ) ); if( !pWrkCTCBLK ) { logmsg( _("HHCCT037E %4.4X: Unable to allocate CTCBLK\n"), pDEVBLK->devnum ); return -1; } memset( pWrkCTCBLK, 0, sizeof( CTCBLK ) ); // Parse configuration file statement if( ParseArgs( pDEVBLK, pWrkCTCBLK, argc, (char**)argv ) != 0 ) { free( pWrkCTCBLK ); pWrkCTCBLK = NULL; return -1; } // Allocate the device CTCBLK and copy parsed information. pDevCTCBLK = malloc( sizeof( CTCBLK ) ); if( !pDevCTCBLK ) { logmsg( _("HHCCT038E %4.4X: Unable to allocate CTCBLK\n"), pDEVBLK->devnum ); free( pWrkCTCBLK ); pWrkCTCBLK = NULL; 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->szTUNCharName, sizeof( pDevCTCBLK->pDEVBLK[0]->filename ) ); strlcpy( pDevCTCBLK->pDEVBLK[1]->filename, pDevCTCBLK->szTUNCharName, sizeof( pDevCTCBLK->pDEVBLK[1]->filename ) ); rc = TUNTAP_CreateInterface( pDevCTCBLK->szTUNCharName, IFF_TUN | IFF_NO_PI, &pDevCTCBLK->fd, pDevCTCBLK->szTUNDevName ); if( rc < 0 ) { free( pWrkCTCBLK ); pWrkCTCBLK = NULL; return -1; } else { logmsg(_("HHCCT073I %4.4X: TUN device %s opened\n"), pDevCTCBLK->pDEVBLK[0]->devnum, pDevCTCBLK->szTUNDevName); } #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->szTUNDevName, sizeof(tt32ctl.tt32ctl_name) ); tt32ctl.tt32ctl_devbuffsize = pDevCTCBLK->iKernBuff; if( TUNTAP_IOCtl( pDevCTCBLK->fd, TT32SDEVBUFF, (char*)&tt32ctl ) != 0 ) { logmsg( _("HHCCT074W TT32SDEVBUFF failed for device %s: %s.\n"), pDevCTCBLK->szTUNDevName, strerror( errno ) ); } tt32ctl.tt32ctl_iobuffsize = pDevCTCBLK->iIOBuff; if( TUNTAP_IOCtl( pDevCTCBLK->fd, TT32SIOBUFF, (char*)&tt32ctl ) != 0 ) { logmsg( _("HHCCT075W TT32SIOBUFF failed for device %s: %s.\n"), pDevCTCBLK->szTUNDevName, strerror( errno ) ); } } #endif #ifdef OPTION_TUNTAP_CLRIPADDR VERIFY( TUNTAP_ClrIPAddr ( pDevCTCBLK->szTUNDevName ) == 0 ); #endif #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 ); snprintf ( pDevCTCBLK->szMACAddress, sizeof( 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->szTUNDevName ,pDevCTCBLK->szGuestIPAddr ,pDevCTCBLK->szMACAddress ); VERIFY( TUNTAP_SetMACAddr ( pDevCTCBLK->szTUNDevName, pDevCTCBLK->szMACAddress ) == 0 ); #endif VERIFY( TUNTAP_SetIPAddr ( pDevCTCBLK->szTUNDevName, pDevCTCBLK->szDriveIPAddr ) == 0 ); VERIFY( TUNTAP_SetDestAddr( pDevCTCBLK->szTUNDevName, pDevCTCBLK->szGuestIPAddr ) == 0 ); #ifdef OPTION_TUNTAP_SETNETMASK VERIFY( TUNTAP_SetNetMask ( pDevCTCBLK->szTUNDevName, pDevCTCBLK->szNetMask ) == 0 ); #endif VERIFY( TUNTAP_SetMTU ( pDevCTCBLK->szTUNDevName, pDevCTCBLK->szMTU ) == 0 ); VERIFY( TUNTAP_SetFlags ( pDevCTCBLK->szTUNDevName, nIFFlags ) == 0 ); // Copy the fd to make panel.c happy pDevCTCBLK->pDEVBLK[0]->fd = pDevCTCBLK->pDEVBLK[1]->fd = pDevCTCBLK->fd; snprintf(thread_name,sizeof(thread_name),"CTCI %4.4X ReadThread",pDEVBLK->devnum); thread_name[sizeof(thread_name)-1]=0; create_thread( &pDevCTCBLK->tid, JOINABLE, CTCI_ReadThread, pDevCTCBLK, thread_name ); pDevCTCBLK->pDEVBLK[0]->tid = pDevCTCBLK->tid; pDevCTCBLK->pDEVBLK[1]->tid = pDevCTCBLK->tid; free( pWrkCTCBLK ); pWrkCTCBLK = NULL; return 0; }