H316: Rewrote h316_udp to use TMXR lines for UDP transport of data. Changed h316_mi to leverage built-in loopback mode in TMXR.

This gives UDP transport on all simh host platforms.
This commit is contained in:
Mark Pizzolato 2013-11-26 13:30:11 -08:00
parent 378e3e03a3
commit c9f73eac90
6 changed files with 126 additions and 313 deletions

View file

@ -192,7 +192,7 @@ extern t_stat mi_tx_service (uint32 quantum);
t_stat udp_create (DEVICE *pdtr, char *premote, int32 *plink); t_stat udp_create (DEVICE *pdtr, char *premote, int32 *plink);
t_stat udp_release (DEVICE *dptr, int32 link); t_stat udp_release (DEVICE *dptr, int32 link);
t_stat udp_send (DEVICE *pdtr, int32 link, uint16 *pdata, uint16 count); t_stat udp_send (DEVICE *pdtr, int32 link, uint16 *pdata, uint16 count);
t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count); t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback);
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbufg); int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbufg);
#endif // #ifndef _H316_IMP_H_ #endif // #ifndef _H316_IMP_H_

View file

@ -319,6 +319,7 @@ MIDB *const mi_midbs [MI_NUM] = {&mi1_db, &mi2_db, &mi3_db, &mi4_db,
void mi_reset_rx (uint16 line) void mi_reset_rx (uint16 line)
{ {
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
PMIDB(line)->rxerror = PMIDB(line)->rxpending = FALSE; PMIDB(line)->rxerror = PMIDB(line)->rxpending = FALSE;
PMIDB(line)->rxtotal = 0; PMIDB(line)->rxtotal = 0;
CLR_RX_IRQ(line); CLR_RX_IEN(line); CLR_RX_IRQ(line); CLR_RX_IEN(line);
@ -328,6 +329,7 @@ void mi_reset_rx (uint16 line)
void mi_reset_tx (uint16 line) void mi_reset_tx (uint16 line)
{ {
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0; PMIDB(line)->txtotal = PMIDB(line)->txdelay = 0;
CLR_TX_IRQ(line); CLR_TX_IEN(line); CLR_TX_IRQ(line); CLR_TX_IEN(line);
} }
@ -424,9 +426,6 @@ void mi_start_tx (uint16 line)
if (PMIDB(line)->iloop) { if (PMIDB(line)->iloop) {
mi_rx_local(line, next, count); mi_rx_local(line, next, count);
} else if (PMIDB(line)->link != NOLINK) { } else if (PMIDB(line)->link != NOLINK) {
if (PMIDB(line)->lloop)
ret = udp_send_self(PDEVICE(line), PMIDB(line)->link, &M[next], count);
else
ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count); ret = udp_send(PDEVICE(line), PMIDB(line)->link, &M[next], count);
if (ret != SCPE_OK) mi_link_error(line); if (ret != SCPE_OK) mi_link_error(line);
} }
@ -581,15 +580,21 @@ int32 mi_io (uint16 line, int32 inst, int32 fnc, int32 dat, int32 dev)
case 001: case 001:
// MnUNXP - un-cross patch modem ... // MnUNXP - un-cross patch modem ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"un-cross patch modem (PC=%06o)\n", PC-1); sim_debug(IMP_DBG_IOT,PDEVICE(line),"un-cross patch modem (PC=%06o)\n", PC-1);
PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE; return dat; PMIDB(line)->iloop = PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
return dat;
case 002: case 002:
// MnLXP - enable line cross patch ... // MnLXP - enable line cross patch ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable line cross patch (PC=%06o)\n", PC-1); sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable line cross patch (PC=%06o)\n", PC-1);
PMIDB(line)->lloop = TRUE; PMIDB(line)->iloop = FALSE; return dat; PMIDB(line)->lloop = TRUE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, TRUE);
PMIDB(line)->iloop = FALSE; return dat;
case 003: case 003:
// MnIXP - enable interface cross patch ... // MnIXP - enable interface cross patch ...
sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable interface cross patch (PC=%06o)\n", PC-1); sim_debug(IMP_DBG_IOT,PDEVICE(line),"enable interface cross patch (PC=%06o)\n", PC-1);
PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE; return dat; PMIDB(line)->iloop = TRUE; PMIDB(line)->lloop = FALSE;
udp_set_link_loopback (PDEVICE(line), PMIDB(line)->link, FALSE);
return dat;
case 004: case 004:
// MnIN - start modem input ... // MnIN - start modem input ...
mi_debug_mio(line, PDIB(line)->rxdmc, "input"); mi_debug_mio(line, PDIB(line)->rxdmc, "input");

View file

@ -29,7 +29,8 @@
udp socket routines udp socket routines
26-Jun-13 RLA Rewritten from TCP version 26-Jun-13 RLA Rewritten from TCP version
26-Nov-13 MP Rewritten to use TMXR layer packet semantics thus
allowing portability to all simh hosts.
OVERVIEW OVERVIEW
@ -134,15 +135,7 @@
*/ */
#ifdef VM_IMPTIP #ifdef VM_IMPTIP
#include "sim_defs.h" // simh machine independent definitions #include "sim_defs.h" // simh machine independent definitions
#ifdef _WIN32 // WINSOCK definitions #include "sim_tmxr.h" // The MUX layer exposes packet send and receive semantics
#include <winsock2.h> // at least Windows puts it all in one file!
#elif defined(__linux__) // Linux definitions
#include <sys/socket.h> // struct socketaddr_in, et al
#include <netinet/in.h> // INADDR_NONE, et al
#include <netdb.h> // gethostbyname()
#include <fcntl.h> // fcntl() (what else??)
#include <unistd.h> // getpid(), more?
#endif
#include "h316_defs.h" // H316 emulator definitions #include "h316_defs.h" // H316 emulator definitions
#include "h316_imp.h" // ARPAnet IMP/TIP definitions #include "h316_imp.h" // ARPAnet IMP/TIP definitions
@ -158,30 +151,15 @@
// the fragments arrive intact then the destination should reassemble them. // the fragments arrive intact then the destination should reassemble them.
#define MAXDATA 16384 // longest possible IMP packet (in H316 words) #define MAXDATA 16384 // longest possible IMP packet (in H316 words)
// Compatibility hacks for WINSOCK vs Linux ...
#ifdef __linux__
#define WSAGetLastError() errno
#define closesocket close
#define SOCKET int32
#define SOCKADDR struct sockaddr
#define WSAEWOULDBLOCK EWOULDBLOCK
#define INVALID_SOCKET ((SOCKET) (-1))
#define SOCKET_ERROR (-1)
#endif
// UDP connection data structure ... // UDP connection data structure ...
// One of these blocks is allocated for every simulated modem link. // One of these blocks is allocated for every simulated modem link.
struct _UDP_LINK { struct _UDP_LINK {
t_bool used; // TRUE if this UDP_LINK is in use t_bool used; // TRUE if this UDP_LINK is in use
uint32 ipremote; // IP address of the remote system char rhostport[64]; // Remote host:port
uint16 rxport; // OUR receiving port number char lport[64]; // Local port
uint16 txport; // HIS receiving port number (on ipremote)
struct sockaddr_in rxaddr; // OUR receiving address (goes with rxsock!)
struct sockaddr_in txaddr; // HIS transmitting address (pairs with txsock!)
SOCKET rxsock; // socket for receiving incoming packets
SOCKET txsock; // socket for sending outgoing packets
uint32 rxsequence; // next message sequence number for receive uint32 rxsequence; // next message sequence number for receive
uint32 txsequence; // next message sequence number for transmit uint32 txsequence; // next message sequence number for transmit
DEVICE *dptr; // Device associated with link
}; };
typedef struct _UDP_LINK UDP_LINK; typedef struct _UDP_LINK UDP_LINK;
@ -206,44 +184,9 @@ typedef struct _UDP_PACKET UDP_PACKET;
#define UDP_HEADER_LEN (2*sizeof(uint32) + sizeof(uint16)) #define UDP_HEADER_LEN (2*sizeof(uint32) + sizeof(uint16))
// Locals ... // Locals ...
t_bool udp_wsa_started = FALSE; // TRUE if WSAStartup() has been called
UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection UDP_LINK udp_links[MAXLINKS] = {0}; // data for every active connection
TMLN udp_lines[MAXLINKS] = { 0 }; // line descriptors
TMXR udp_tmxr = { MAXLINKS, NULL, 0, udp_lines};// datagram mux
t_stat udp_startup (DEVICE *dptr)
{
// WINSOCK requires that WSAStartup be called exactly once before any other
// network calls are made. That's a bit inconvenient, but this routine deals
// with it by using a static variable to call WSAStartup the first time thru
// and then never again.
#ifdef _WIN32
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData; int32 ret;
if (!udp_wsa_started) {
ret = WSAStartup (wVersionRequested, &wsaData);
if (ret != 0) {
fprintf(stderr,"UDP - WINSOCK startup error %d\n", ret);
return SCPE_IERR;
} else
sim_debug(IMP_DBG_UDP, dptr, "WSAStartup() called\n");
udp_wsa_started = TRUE;
}
#endif
return SCPE_OK;
}
t_stat udp_shutdown (DEVICE *dptr)
{
// This routine calls WSACleanup() after the last socket has been closed.
// It's essentially the opposite of udp_startup() ...
#ifdef _WIN32
if (udp_wsa_started) {
WSACleanup(); udp_wsa_started = FALSE;
sim_debug(IMP_DBG_UDP, dptr, "WSACleanup() called\n");
}
#endif
return SCPE_OK;
}
int32 udp_find_free_link (void) int32 udp_find_free_link (void)
{ {
@ -253,91 +196,12 @@ int32 udp_find_free_link (void)
for (i = 0; i < MAXLINKS; ++i) { for (i = 0; i < MAXLINKS; ++i) {
if (udp_links[i].used == 0) { if (udp_links[i].used == 0) {
memset(&udp_links[i], 0, sizeof(UDP_LINK)); memset(&udp_links[i], 0, sizeof(UDP_LINK));
// Just in case these values aren't zero!
udp_links[i].rxsock = udp_links[i].txsock = INVALID_SOCKET;
return i; return i;
} }
} }
return NOLINK; return NOLINK;
} }
char *udp_format_remote (int32 link)
{
// Format the remote address and port in the format "w.x.y.z:pppp" . It's
// a bit ugly (OK, it's a lot ugly!) but it's just for error messages...
static char buf[64];
sprintf(buf, "%d.%d.%d.%d:%d",
(udp_links[link].ipremote >> 24) & 0xFF,
(udp_links[link].ipremote >> 16) & 0xFF,
(udp_links[link].ipremote >> 8) & 0xFF,
udp_links[link].ipremote & 0xFF,
udp_links[link].txport);
return buf;
}
/* get_ipaddr IP address:port
Inputs:
cptr = pointer to input string
Outputs:
ipa = pointer to IP address (may be NULL), 0 = none
ipp = pointer to IP port (may be NULL), 0 = none
result = status
*/
static t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp)
{
char gbuf[CBUFSIZE];
char *addrp, *portp, *octetp;
uint32 i, addr, port, octet;
t_stat r;
if ((cptr == NULL) || (*cptr == 0))
return SCPE_ARG;
strncpy (gbuf, cptr, CBUFSIZE);
addrp = gbuf; /* default addr */
if ((portp = strchr (gbuf, ':'))) /* x:y? split */
*portp++ = 0;
else if (strchr (gbuf, '.')) /* x.y...? */
portp = NULL;
else {
portp = gbuf; /* port only */
addrp = NULL; /* no addr */
}
if (portp) { /* port string? */
if (ipp == NULL) /* not wanted? */
return SCPE_ARG;
port = (int32) get_uint (portp, 10, 65535, &r);
if ((r != SCPE_OK) || (port == 0))
return SCPE_ARG;
}
else port = 0;
if (addrp) { /* addr string? */
if (ipa == NULL) /* not wanted? */
return SCPE_ARG;
for (i = addr = 0; i < 4; i++) { /* four octets */
octetp = strchr (addrp, '.'); /* find octet end */
if (octetp != NULL) /* split string */
*octetp++ = 0;
else if (i < 3) /* except last */
return SCPE_ARG;
octet = (int32) get_uint (addrp, 10, 255, &r);
if (r != SCPE_OK)
return SCPE_ARG;
addr = (addr << 8) | octet;
addrp = octetp;
}
if (((addr & 0377) == 0) || ((addr & 0377) == 255))
return SCPE_ARG;
}
else addr = 0;
if (ipp) /* return req values */
*ipp = port;
if (ipa)
*ipa = addr;
return SCPE_OK;
}
t_stat udp_parse_remote (int32 link, char *premote) t_stat udp_parse_remote (int32 link, char *premote)
{ {
// This routine will parse a remote address string in any of these forms - // This routine will parse a remote address string in any of these forms -
@ -356,102 +220,46 @@ t_stat udp_parse_remote (int32 link, char *premote)
// yourself!! In both cases, "w.x.y.z" is a dotted IP for the remote machine // yourself!! In both cases, "w.x.y.z" is a dotted IP for the remote machine
// and "name.domain.com" is its name (which will be looked up to get the IP). // and "name.domain.com" is its name (which will be looked up to get the IP).
// If the host name/IP is omitted then it defaults to "localhost". // If the host name/IP is omitted then it defaults to "localhost".
char *end, *colon; int32 port; struct hostent *he; char *end; int32 lport, rport; t_stat ret;
char host[64], port[16];
if (*premote == '\0') return SCPE_2FARG; if (*premote == '\0') return SCPE_2FARG;
// Look for the local port number. If it's not there, set rxport to zero for now. memset (udp_links[link].lport, 0, sizeof(udp_links[link].lport));
port = strtoul(premote, &end, 10); udp_links[link].rxport = 0; memset (udp_links[link].rhostport, 0, sizeof(udp_links[link].rhostport));
if ((*end == ':') && (port > 0)) { // Handle the llll::rrrr case first
udp_links[link].rxport = port; premote = end+1; if (2 == sscanf (premote, "%d::%d", &lport, &rport)) {
if ((lport < 1) || (lport >65535) || (rport < 1) || (rport >65535)) return SCPE_ARG;
sprintf (udp_links[link].lport, "%d", lport);
sprintf (udp_links[link].rhostport, "localhost:%d", rport);
return SCPE_OK;
} }
// Look for "name:port" and extract the remote port... // Look for the local port number and save it away.
if ((colon = strchr(premote, ':')) == NULL) return SCPE_ARG; lport = strtoul(premote, &end, 10);
*colon++ = '\0'; port = strtoul(colon, &end, 10); if ((*end == ':') && (lport > 0)) {
if ((*end != '\0') || (port == 0)) return SCPE_ARG; sprintf (udp_links[link].lport, "%d", lport);
udp_links[link].txport = port; premote = end+1;
if (udp_links[link].rxport == 0) udp_links[link].rxport = port; }
// Now try to parse the host as a dotted IP address ... ret = sim_parse_addr (premote, host, sizeof(host), "localhost", port, sizeof(port), NULL, NULL);
if (get_ipaddr(premote, &udp_links[link].ipremote, NULL) == SCPE_OK) return SCPE_OK; if (ret != SCPE_OK) return SCPE_ARG;
sprintf (udp_links[link].rhostport, "%s:%s", host, port);
if (udp_links[link].lport[0] == '\0')
strcpy (udp_links[link].lport, port);
// Special kludge - allow just ":port" to mean "localhost:port" ... if ((strcmp (udp_links[link].lport, port) == 0) &&
if(*premote == '\0') { (strcmp ("localhost", host) == 0))
if (udp_links[link].rxport == udp_links[link].txport)
fprintf(stderr,"WARNING - use different transmit and receive ports!\n"); fprintf(stderr,"WARNING - use different transmit and receive ports!\n");
premote = "localhost";
}
// Not a dotted IP - try to lookup a host name ...
if ((he = gethostbyname(premote)) == NULL) return SCPE_OPENERR;
udp_links[link].ipremote = * (unsigned long *) he->h_addr_list[0];
if (udp_links[link].ipremote == INADDR_NONE) {
fprintf(stderr,"WARNING - unable to resolve \"%s\"\n", premote);
return SCPE_OPENERR;
}
udp_links[link].ipremote = ntohl(udp_links[link].ipremote);
return SCPE_OK; return SCPE_OK;
} }
t_stat udp_socket_error (int32 link, const char *msg) t_stat udp_error (int32 link, const char *msg)
{ {
// This routine is called whenever a SOCKET_ERROR is returned for any I/O. // This routine is called whenever a SOCKET_ERROR is returned for any I/O.
fprintf(stderr,"UDP%d - %s failed with error %d\n", link, msg, WSAGetLastError()); fprintf(stderr,"UDP%d - %s failed with error %d\n", link, msg, WSAGetLastError());
return SCPE_IOERR; return SCPE_IOERR;
} }
t_stat udp_create_rx_socket (int32 link)
{
// This routine will create the receiver socket for the virtual modem.
// Sockets are always UDP and, in the case of the receiver, bound to the port
// specified. Receiving sockets are also always set to NON BLOCKING mode.
int32 iret; uint32 flags = 1;
// Creating the socket works on both Windows and Linux ...
udp_links[link].rxsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_links[link].rxsock == INVALID_SOCKET)
return udp_socket_error(link, "RX socket()");
udp_links[link].rxaddr.sin_family = AF_INET;
udp_links[link].rxaddr.sin_port = htons(udp_links[link].rxport);
udp_links[link].rxaddr.sin_addr.s_addr = htonl(INADDR_ANY);
iret = bind(udp_links[link].rxsock, (SOCKADDR *) &udp_links[link].rxaddr, sizeof(struct sockaddr_in));
if (iret != 0)
return udp_socket_error(link, "bind()");
// But making it non-blocking is a problem ...
#ifdef _WIN32
iret = ioctlsocket(udp_links[link].rxsock, FIONBIO, (u_long *) &flags);
if (iret != 0)
return udp_socket_error(link, "ioctlsocket()");
#elif defined(__linux__)
flags = fcntl(udp_links[link].rxsock, F_GETFL, 0);
if (flags == -1) return udp_socket_error(link, "fcntl(F_GETFL)");
iret = fcntl(udp_links[link].rxsock, F_SETFL, flags | O_NONBLOCK);
if (iret == -1) return udp_socket_error(link, "fcntl(F_SETFL)");
iret = fcntl(udp_links[link].rxsock, F_SETOWN, getpid());
if (iret == -1) return udp_socket_error(link, "fcntl(F_SETOWN)");
#endif
return SCPE_OK;
}
t_stat udp_create_tx_socket (int32 link)
{
// This routine will create the transmitter socket for the virtual modem.
// In the case of the transmitter, we don't bind the socket at this time -
// WINSOCK will automatically bind it for us to a free port on the first IO.
// Also note that transmitting sockets are blocking; we don't have code (yet!)
// to allow them to be nonblocking.
udp_links[link].txsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_links[link].txsock == INVALID_SOCKET)
return udp_socket_error(link, "TX socket()");
// Initialize the txaddr structure too - note that this isn't used now; it's
// the sockaddr we will use when we later do a sendto() the remote host!
udp_links[link].txaddr.sin_family = AF_INET;
udp_links[link].txaddr.sin_port = htons(udp_links[link].txport);
udp_links[link].txaddr.sin_addr.s_addr = htonl(udp_links[link].ipremote);
return SCPE_OK;
}
t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln) t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln)
{ {
// Create a logical UDP link to the specified remote system. The "remote" // Create a logical UDP link to the specified remote system. The "remote"
@ -467,22 +275,26 @@ t_stat udp_create (DEVICE *dptr, char *premote, int32 *pln)
// which is a handle used to identify this connection to all future udp_xyz() // which is a handle used to identify this connection to all future udp_xyz()
// calls. // calls.
t_stat ret; t_stat ret;
char linkinfo[128];
int32 link = udp_find_free_link(); int32 link = udp_find_free_link();
if (link < 0) return SCPE_MEM; if (link < 0) return SCPE_MEM;
// Make sure WINSOCK is initialized ...
if ((ret = udp_startup(dptr)) != SCPE_OK) return ret;
// Parse the remote name and set up the ipaddr and port ... // Parse the remote name and set up the ipaddr and port ...
if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret; if ((ret = udp_parse_remote(link, premote)) != SCPE_OK) return ret;
// Create the sockets for the transmitter and receiver ... // Create the socket connection to the destination ...
if ((ret = udp_create_rx_socket(link)) != SCPE_OK) return ret; sprintf(linkinfo, "Line=%d,%s,UDP,Connect=%s", link, udp_links[link].lport, udp_links[link].rhostport);
if ((ret = udp_create_tx_socket(link)) != SCPE_OK) return ret; ret = tmxr_open_master (&udp_tmxr, linkinfo);
if (ret != SCPE_OK) return ret;
// All done - mark the TCP_LINK data as "used" and return the index. // All done - mark the TCP_LINK data as "used" and return the index.
udp_links[link].used = TRUE; *pln = link; udp_links[link].used = TRUE; *pln = link;
sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %d and sending to %s\n", link, udp_links[link].rxport, udp_format_remote(link)); udp_lines[link].dptr = udp_links[link].dptr = dptr; // save device
udp_tmxr.uptr = dptr->units;
udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects
tmxr_poll_conn (&udp_tmxr); // force connection initialization now
udp_tmxr.last_poll_time = 1; // h316'a use of TMXR doesn't poll periodically for connects
sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %s and sending to %s\n", link, udp_links[link].lport, udp_links[link].rhostport);
return SCPE_OK; return SCPE_OK;
} }
@ -491,26 +303,18 @@ t_stat udp_release (DEVICE *dptr, int32 link)
// Close a link that was created by udp_create() and release any resources // Close a link that was created by udp_create() and release any resources
// allocated to it. We always return SCPE_OK unless the link specified is // allocated to it. We always return SCPE_OK unless the link specified is
// already unused. // already unused.
int32 iret, i;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
// Close the sockets associated with this connection - that's easy ... tmxr_detach_ln (&udp_lines[link]);
iret = closesocket(udp_links[link].rxsock);
if (iret != 0) udp_socket_error(link, "closesocket()");
iret = closesocket(udp_links[link].txsock);
if (iret != 0) udp_socket_error(link, "closesocket()");
udp_links[link].used = FALSE; udp_links[link].used = FALSE;
sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link); sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link);
// If we just closed the last link, then call udp_shutdown() ... return SCPE_OK;
for (i = 0; i < MAXLINKS; ++i) {
if (udp_links[i].used) return SCPE_OK;
}
return udp_shutdown(dptr);
} }
t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKADDR *pdest) t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count)
{ {
// This routine does all the work of sending an IMP data packet. pdata // This routine does all the work of sending an IMP data packet. pdata
// is a pointer (usually into H316 simulated memory) to the IMP packet data, // is a pointer (usually into H316 simulated memory) to the IMP packet data,
@ -520,10 +324,11 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA
// doesn't necessarily need to have the same endian-ness as this machine. // doesn't necessarily need to have the same endian-ness as this machine.
// Second, notice that transmitting sockets are NOT set to non blocking so // Second, notice that transmitting sockets are NOT set to non blocking so
// this routine might wait, but we assume the wait will never be too long. // this routine might wait, but we assume the wait will never be too long.
UDP_PACKET pkt; int pktlen; uint16 i; int32 iret; UDP_PACKET pkt; int pktlen; uint16 i; t_stat iret;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR;
if ((pdata == NULL) || (count == 0) || (count > MAXDATA)) return SCPE_IERR; if ((pdata == NULL) || (count == 0) || (count > MAXDATA)) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
// Build the UDP packet, filling in our own header information and copying // Build the UDP packet, filling in our own header information and copying
// the H316 words from memory. REMEMBER THAT EVERYTHING IS IN NETWORK ORDER! // the H316 words from memory. REMEMBER THAT EVERYTHING IS IN NETWORK ORDER!
@ -534,28 +339,19 @@ t_stat udp_send_to (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count, SOCKA
pktlen = UDP_HEADER_LEN + count*sizeof(uint16); pktlen = UDP_HEADER_LEN + count*sizeof(uint16);
// Send it and we're outta here ... // Send it and we're outta here ...
iret = sendto(udp_links[link].txsock, (const char *) &pkt, pktlen, 0, pdest, sizeof (struct sockaddr_in)); iret = tmxr_put_packet_ln (&udp_lines[link], (const uint8 *)&pkt, (size_t)pktlen);
if (iret == SOCKET_ERROR) return udp_socket_error(link, "sendto()"); if (iret != SCPE_OK) return udp_error(link, "tmxr_put_packet_ln()");
sim_debug(IMP_DBG_UDP, dptr, "link %d - packet sent (sequence=%d, length=%d)\n", link, ntohl(pkt.sequence), ntohs(pkt.count)); sim_debug(IMP_DBG_UDP, dptr, "link %d - packet sent (sequence=%d, length=%d)\n", link, ntohl(pkt.sequence), ntohs(pkt.count));
return SCPE_OK; return SCPE_OK;
} }
t_stat udp_send (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count) t_stat udp_set_link_loopback (DEVICE *dptr, int32 link, t_bool enable_loopback)
{ {
// Send an IMP packet to the remote simh. This is the usual case - the only if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
// reason there's any other options at all is so we can emulate loopback. if (!udp_links[link].used) return SCPE_IERR;
return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &(udp_links[link].txaddr)); if (dptr != udp_links[link].dptr) return SCPE_IERR;
}
t_stat udp_send_self (DEVICE *dptr, int32 link, uint16 *pdata, uint16 count) return tmxr_set_line_loopback (&udp_lines[link], enable_loopback);
{
// Send an IMP packet to our own receiving socket. This might seem silly,
// but it's used to emulate the line loopback function...
struct sockaddr_in self;
self.sin_family = AF_INET;
self.sin_port = htons(udp_links[link].rxport);
self.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
return udp_send_to (dptr, link, pdata, count, (SOCKADDR *) &self);
} }
int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt) int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt)
@ -568,27 +364,22 @@ int32 udp_receive_packet (int32 link, UDP_PACKET *ppkt)
// Note that this routine only receives the packet - it doesn't handle any // Note that this routine only receives the packet - it doesn't handle any
// of the checking for valid packets, unexpected packets, duplicate or out of // of the checking for valid packets, unexpected packets, duplicate or out of
// sequence packets. That's strictly the caller's problem! // sequence packets. That's strictly the caller's problem!
int32 pktsiz; size_t pktsiz;
struct sockaddr_in sender; uint8 *pbuf;
#if defined (macintosh) || defined (__linux) || defined (__linux__) || \ t_stat ret;
defined (__APPLE__) || defined (__OpenBSD__) || \
defined(__NetBSD__) || defined(__FreeBSD__) || \
(defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED))
socklen_t sndsiz = (socklen_t)sizeof(sender);
#elif defined (_WIN32) || defined (__EMX__) || \
(defined (__ALPHA) && defined (__unix__)) || \
defined (__hpux)
int sndsiz = (int)sizeof(sender);
#else
size_t sndsiz = sizeof(sender);
#endif
pktsiz = recvfrom(udp_links[link].rxsock, (char *) ppkt, sizeof(UDP_PACKET), udp_lines[link].rcve = TRUE; // Enable receiver
0, (SOCKADDR *) &sender, &sndsiz); tmxr_poll_rx (&udp_tmxr);
if (pktsiz >= 0) return pktsiz; ret = tmxr_get_packet_ln (&udp_lines[link], &pbuf, &pktsiz);
if (WSAGetLastError() == WSAEWOULDBLOCK) return 0; udp_lines[link].rcve = FALSE; // Disable receiver
udp_socket_error(link, "recvfrom()"); if (ret != SCPE_OK) {
udp_error(link, "tmxr_get_packet_ln()");
return NOLINK; return NOLINK;
}
if (pbuf == NULL) return 0;
// Got a packet, so copy it to the packet buffer
memcpy (ppkt, pbuf, pktsiz);
return pktsiz;
} }
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf) int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf)
@ -611,6 +402,7 @@ int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf)
UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq; UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq;
if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR;
if (!udp_links[link].used) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR;
if (dptr != udp_links[link].dptr) return SCPE_IERR;
while ((pktlen = udp_receive_packet(link, &pkt)) > 0) { while ((pktlen = udp_receive_packet(link, &pkt)) > 0) {
// First do some header checks for a valid UDP packet ... // First do some header checks for a valid UDP packet ...

View file

@ -599,7 +599,7 @@ if (hostp != NULL) {
hostp[strlen(hostp)-1] = '\0'; hostp[strlen(hostp)-1] = '\0';
} }
} }
if (host) /* host wanted? */ if (host) { /* host wanted? */
if (hostp != NULL) { if (hostp != NULL) {
if (strlen(hostp) >= host_len) if (strlen(hostp) >= host_len)
return SCPE_ARG; /* no room */ return SCPE_ARG; /* no room */
@ -613,12 +613,14 @@ if (host) /* host wanted? */
strcpy (host, default_host); strcpy (host, default_host);
} }
else { else {
if (default_host) if (default_host) {
if (strlen(default_host) >= host_len) if (strlen(default_host) >= host_len)
return SCPE_ARG; /* no room */ return SCPE_ARG; /* no room */
else else
strcpy (host, default_host); strcpy (host, default_host);
} }
}
}
if (validate_addr) { if (validate_addr) {
struct addrinfo *ai_host, *ai_validate, *ai; struct addrinfo *ai_host, *ai_validate, *ai;
t_stat status; t_stat status;

View file

@ -664,6 +664,9 @@ else { /* Telnet connection */
written = sim_write_sock (lp->sock, &(lp->txb[i]), length); written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
if (written == SOCKET_ERROR) /* did an error occur? */ if (written == SOCKET_ERROR) /* did an error occur? */
if (lp->datagram)
return written; /* ignore errors on datagram sockets */
else
return -1; /* return error indication */ return -1; /* return error indication */
else else
return written; return written;
@ -1543,19 +1546,19 @@ return val;
NULL, but success (SCPE_OK) is returned NULL, but success (SCPE_OK) is returned
*/ */
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize) t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize)
{ {
return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0); return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
} }
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte) t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte)
{ {
int32 c; int32 c;
size_t pktsize; size_t pktsize;
size_t fc_size = (frame_byte ? 1 : 0); size_t fc_size = (frame_byte ? 1 : 0);
while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
if (lp->rxpboffset + 1 > lp->rxpbsize) { if (lp->rxpboffset + 3 > lp->rxpbsize) {
lp->rxpbsize += 512; lp->rxpbsize += 512;
lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize); lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
} }
@ -1563,8 +1566,15 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1); tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1);
continue; continue;
} }
lp->rxpb[lp->rxpboffset] = c & 0xFF; if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
lp->rxpboffset += 1; /* Datagram packet length is provided as a part of the natural datagram
delivery, for TCP lines, we read the packet length from the data stream.
So, here we stuff packet size into head of packet buffer so it looks like
it was delivered by TCP and the below return logic doesn't have to worry */
lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
}
lp->rxpb[lp->rxpboffset++] = c & 0xFF;
if (lp->rxpboffset >= (2 + fc_size)) { if (lp->rxpboffset >= (2 + fc_size)) {
pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size]; pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
if (pktsize == (lp->rxpboffset - 2)) { if (pktsize == (lp->rxpboffset - 2)) {
@ -1612,10 +1622,12 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
lp->rxbsz - lp->rxbpi); lp->rxbsz - lp->rxbpi);
if (nbytes < 0) { /* line error? */ if (nbytes < 0) { /* line error? */
if (!lp->datagram) { /* ignore errors reading UDP sockets */
if (!lp->txbfd || lp->notelnet) if (!lp->txbfd || lp->notelnet)
lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */
tmxr_close_ln (lp); /* disconnect line */ tmxr_close_ln (lp); /* disconnect line */
} }
}
else if (nbytes > 0) { /* if data rcvd */ else if (nbytes > 0) { /* if data rcvd */
@ -1816,6 +1828,7 @@ t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 fra
{ {
t_stat r; t_stat r;
size_t fc_size = (frame_byte ? 1 : 0); size_t fc_size = (frame_byte ? 1 : 0);
size_t pktlen_size = (lp->datagram ? 0 : 2);
if (!lp->conn) if (!lp->conn)
return SCPE_LOST; return SCPE_LOST;
@ -1823,17 +1836,19 @@ if (lp->txppoffset < lp->txppsize) {
tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size); tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size);
return SCPE_STALL; return SCPE_STALL;
} }
if (lp->txpbsize < size + 2 + fc_size) { if (lp->txpbsize < size + pktlen_size + fc_size) {
lp->txpbsize = size + 2 + fc_size; lp->txpbsize = size + pktlen_size + fc_size;
lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize); lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
} }
lp->txpb[0] = frame_byte; lp->txpb[0] = frame_byte;
lp->txpb[0+fc_size] = (size >> 8) & 0xFF; if (!lp->datagram) {
lp->txpb[1+fc_size] = size & 0xFF; lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
memcpy (lp->txpb + 2 + fc_size, buf, size); lp->txpb[1+fc_size] = size & 0xFF;
lp->txppsize = size + 2 + fc_size; }
memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
lp->txppsize = size + pktlen_size + fc_size;
lp->txppoffset = 0; lp->txppoffset = 0;
tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[2+fc_size], size); tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[pktlen_size+fc_size], size);
++lp->txpcnt; ++lp->txpcnt;
while ((lp->txppoffset < lp->txppsize) && while ((lp->txppoffset < lp->txppsize) &&
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset])))) (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
@ -1896,7 +1911,6 @@ if (nbytes) { /* >0? write */
sbytes = tmxr_write (lp, nbytes); /* write all data */ sbytes = tmxr_write (lp, nbytes); /* write all data */
else else
sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */ sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
if (sbytes >= 0) { /* ok? */ if (sbytes >= 0) { /* ok? */
tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes); tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes);
lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */

View file

@ -185,8 +185,8 @@ int32 tmxr_poll_conn (TMXR *mp);
t_stat tmxr_reset_ln (TMLN *lp); t_stat tmxr_reset_ln (TMLN *lp);
t_stat tmxr_detach_ln (TMLN *lp); t_stat tmxr_detach_ln (TMLN *lp);
int32 tmxr_getc_ln (TMLN *lp); int32 tmxr_getc_ln (TMLN *lp);
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize); t_stat tmxr_get_packet_ln (TMLN *lp, uint8 **pbuf, size_t *psize);
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte); t_stat tmxr_get_packet_ln_ex (TMLN *lp, uint8 **pbuf, size_t *psize, uint8 frame_byte);
void tmxr_poll_rx (TMXR *mp); void tmxr_poll_rx (TMXR *mp);
t_stat tmxr_putc_ln (TMLN *lp, int32 chr); t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size); t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);