1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-02-23 23:52:51 +00:00
Files
rcornwell.sims/PDP10/ka10_imp.c
2019-01-05 14:25:48 -05:00

1187 lines
40 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ka10_imp.c: IMP, interface message processor.
Copyright (c) 2018, Richard Cornwell based on code provided by
Lars Brinkhoff and Danny Gasparovski.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This emulates the MIT-AI/ML/MC Host/IMP interface.
*/
#include "ka10_defs.h"
#include "sim_ether.h"
#include <arpa/inet.h>
#if NUM_DEVS_IMP > 0
#define IMP_DEVNUM 0460
/* CONI */
#define IMPID 010 /* Input done. */
#define IMPI32 020 /* Input in 32 bit mode. */
#define IMPIB 040 /* Input busy. */
#define IMPOD 0100 /* Output done. */
#define IMPO32 0200 /* Output in 32-bit mode. */
#define IMPOB 0400 /* Output busy. */
#define IMPERR 01000 /* IMP error. */
#define IMPR 02000 /* IMP ready. */
#define IMPIC 04000 /* IMP interrupt condition. */
#define IMPHER 010000 /* Host error. */
#define IMPHR 020000 /* Host ready. */
#define IMPIHE 040000 /* Inhibit interrupt on host error. */
#define IMPLW 0100000 /* Last IMP word. */
/* CONO */
#define IMPIDC 010 /* Clear input done */
#define IMI32S 020 /* Set 32-bit output */
#define IMI32C 040 /* Clear 32-bit output */
#define IMPODC 0100 /* Clear output done */
#define IMO32S 0200 /* Set 32-bit input */
#define IMO32C 0400 /* Clear 32-bit input */
#define IMPODS 01000 /* Set output done */
#define IMPIR 04000 /* Enable interrupt on IMP ready */
#define IMPHEC 010000 /* Clear host error */
#define IMIIHE 040000 /* Inhibit interrupt on host error */
#define IMPLHW 0200000 /* Set last host word. */
/* CONI timeout. If no CONI instruction is executed for 3-5 seconds,
the interface will raise the host error signal. */
#define CONI_TIMEOUT 3000000
#define STATUS u3
#define OPOS u4 /* Output bit position */
#define IPOS u5 /* Input bit position */
#define ILEN u6 /* Size of input buffer in bits */
#ifdef _MSC_VER
# define PACKED_BEGIN __pragma( pack(push, 1) )
# define PACKED_END __pragma( pack(pop) )
# define QEMU_PACKED
#else
# define PACKED_BEGIN
#if defined(_WIN32)
# define PACKED_END __attribute__((gcc_struct, packed))
# define QEMU_PACKED __attribute__((gcc_struct, packed))
#else
# define PACKED_END __attribute__((packed))
# define QEMU_PACKED __attribute__((packed))
#endif
#endif
#define IMP_ARPTAB_SIZE 8
PACKED_BEGIN
struct imp_eth_hdr {
ETH_MAC dest;
ETH_MAC src;
uint16 type;
} PACKED_END;
#define ETHTYPE_ARP 0x0806
#define ETHTYPE_IP 0x0800
/*
* Structure of an internet header, naked of options.
*/
PACKED_BEGIN
struct ip {
uint8 ip_v_hl; /* version,header length */
uint8 ip_tos; /* type of service */
uint16 ip_len; /* total length */
uint16 ip_id; /* identification */
uint16 ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8 ip_ttl; /* time to live */
uint8 ip_p; /* protocol */
uint16 ip_sum; /* checksum */
in_addr_t ip_src;
in_addr_t ip_dst; /* source and dest address */
} PACKED_END;
#define TCP_PROTO 6
PACKED_BEGIN
struct tcp {
uint16 tcp_sport; /* Source port */
uint16 tcp_dport; /* Destination port */
uint32 seq; /* Sequence number */
uint32 ack; /* Ack number */
uint16 flags; /* Flags */
uint16 window; /* Window size */
uint16 chksum; /* packet checksum */
uint16 urgent; /* Urgent pointer */
uint8 payload[]; /* Payload data */
} PACKED_END;
#define UDP_PROTO 17
PACKED_BEGIN
struct udp {
uint16 udp_sport; /* Source port */
uint16 udp_dport; /* Destination port */
uint16 len; /* Length */
uint16 chksum; /* packet checksum */
} PACKED_END;
#define ICMP_PROTO 1
PACKED_BEGIN
struct icmp {
uint8 type; /* Type of packet */
uint8 code; /* Code */
uint16 chksum; /* packet checksum */
} PACKED_END;
PACKED_BEGIN
struct ip_hdr {
struct imp_eth_hdr ethhdr;
struct ip iphdr;
} PACKED_END;
#define ARP_REQUEST 1
#define ARP_REPLY 2
#define ARP_HWTYPE_ETH 1
PACKED_BEGIN
struct arp_hdr {
struct imp_eth_hdr ethhdr;
uint16 hwtype;
uint16 protocol;
uint8 hwlen;
uint8 protolen;
uint16 opcode;
ETH_MAC shwaddr;
in_addr_t sipaddr;
ETH_MAC dhwaddr;
in_addr_t dipaddr;
uint8 padding[18];
} PACKED_END;
struct arp_entry {
in_addr_t ipaddr;
ETH_MAC ethaddr;
uint16 time;
};
struct imp_packet {
struct imp_packet *next; /* Link to packets */
ETH_PACK packet;
in_addr_t dest; /* Destination IP address */
uint16 msg_id; /* Message ID */
int life; /* How many ticks to wait */
} imp_buffer[8];
struct imp_stats {
int recv; /* received packets */
int dropped; /* received packets dropped */
int xmit; /* transmitted packets */
int fail; /* transmit failed */
int runt; /* runts */
int reset; /* reset count */
int giant; /* oversize packets */
int setup; /* setup packets */
int loop; /* loopback packets */
int recv_overrun; /* receiver overruns */
};
struct imp_device {
/*+ initialized values - DO NOT MOVE */
ETH_PCALLBACK rcallback; /* read callback routine */
ETH_PCALLBACK wcallback; /* write callback routine */
ETH_MAC mac; /* Hardware MAC address */
struct imp_packet *sendq; /* Send queue */
struct imp_packet *freeq; /* Free queue */
in_addr_t ip; /* Local IP address */
in_addr_t ip_mask; /* Local IP mask */
in_addr_t hostip; /* IP address of local host */
in_addr_t gwip; /* Gateway IP address */
int maskbits; /* Mask length */
in_addr_t dhcpip; /* DHCP server address */
int dhcp; /* Use dhcp */
int init_state; /* Initialization state */
int padding; /* Type zero padding */
uint64 obuf; /* Output buffer */
uint64 ibuf; /* Input buffer */
int obits; /* Output bits */
int ibits; /* Input bits */
struct imp_stats stats;
uint8 sbuffer[ETH_FRAME_SIZE]; /* Temp send buffer */
uint8 rbuffer[ETH_FRAME_SIZE]; /* Temp receive buffer */
ETH_DEV etherface;
ETH_QUE ReadQ;
int32 idtmr; /* countdown for ID Timer */
uint32 must_poll; /* receiver must poll instead of counting on asynch polls */
t_bool initialized; /* flag for one time initializations */
int imp_error;
int host_error;
size_t bits_to_imp;
size_t bits_to_host;
} imp_data;
extern int32 tmxr_poll;
static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff};
static CONST in_addr_t broadcast_ipaddr = {0xffffffff};
static struct arp_entry arp_table[IMP_ARPTAB_SIZE];
t_stat imp_devio(uint32 dev, uint64 *data);
t_stat imp_srv(UNIT *);
t_stat imp_eth_srv(UNIT *);
t_stat imp_reset (DEVICE *dptr);
const char *imp_description (DEVICE *dptr);
t_stat imp_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat imp_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat imp_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat imp_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat imp_show_ip (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat imp_show_gwip (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat imp_set_gwip (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat imp_show_hostip (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat imp_set_hostip (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
void imp_timer_task(struct imp_device *imp);
void imp_packet_in(struct imp_device *imp, ETH_PACK *read_buffer);
void imp_send_packet (struct imp_device *imp_data, int len);
void imp_free_packet(struct imp_device *imp, struct imp_packet *p);
struct imp_packet * imp_get_packet();
void imp_arp_update(in_addr_t *ipaddr, ETH_MAC *ethaddr);
void imp_arp_arpin(struct imp_device *imp, ETH_PACK *packet);
void imp_packet_out(struct imp_device *imp, ETH_PACK *packet);
t_stat imp_attach (UNIT * uptr, CONST char * cptr);
t_stat imp_detach (UNIT * uptr);
int imp_mpx_lvl = 0;
int last_coni;
UNIT imp_unit[] = {
{UDATA(imp_srv, UNIT_IDLE+UNIT_ATTABLE+UNIT_DISABLE, 0)}, /* 0 */
{UDATA(imp_eth_srv, UNIT_IDLE+UNIT_DISABLE, 0)}, /* 0 */
};
DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, NULL};
MTAB imp_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx",
&imp_set_mac, &imp_show_mac, NULL, "MAC address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MPX", "MPX",
&imp_set_mpx, &imp_show_mpx, NULL},
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "IP", "IP=ddd.ddd.ddd.ddd/ddd",
&imp_set_ip, &imp_show_ip, NULL, "IP address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "GW", "GW=ddd.ddd.ddd.ddd",
&imp_set_gwip, &imp_show_gwip, NULL, "GW address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "HOST", "HOST=ddd.ddd.ddd.ddd",
&imp_set_hostip, &imp_show_hostip, NULL, "HOST IP address" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ETH", NULL, NULL,
&eth_show, NULL, "Display attachedable devices" },
{ 0 }
};
DEVICE imp_dev = {
"IMP", imp_unit, NULL, imp_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, &imp_reset, NULL, &imp_attach, &imp_detach,
&imp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug,
NULL, NULL, NULL, NULL, NULL, &imp_description
};
static void check_interrupts (UNIT *uptr)
{
clr_interrupt (IMP_DEVNUM);
if ((uptr->STATUS & (IMPERR | IMPIC)) == IMPERR)
set_interrupt(IMP_DEVNUM, uptr->STATUS);
if ((uptr->STATUS & (IMPR | IMPIC)) == (IMPR | IMPIC))
set_interrupt(IMP_DEVNUM, uptr->STATUS);
if ((uptr->STATUS & (IMPHER | IMPIHE)) == IMPHER)
set_interrupt(IMP_DEVNUM, uptr->STATUS);
if (uptr->STATUS & IMPID) {
if (uptr->STATUS & IMPLW)
set_interrupt(IMP_DEVNUM, uptr->STATUS);
else
set_interrupt_mpx(IMP_DEVNUM, uptr->STATUS, imp_mpx_lvl);
}
if (uptr->STATUS & IMPOD)
set_interrupt_mpx(IMP_DEVNUM, uptr->STATUS, imp_mpx_lvl + 1);
}
t_stat imp_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &imp_dev;
UNIT *uptr = imp_unit;
switch(dev & 07) {
case CONO:
sim_debug(DEBUG_CONO, dptr, "IMP %03o CONO %06o PC=%o\n", dev,
(uint32)*data, PC);
uptr->STATUS &= ~7;
uptr->STATUS |= *data & 7;
if (*data & IMPIDC) //Clear input done.
uptr->STATUS &= ~IMPID;
if (*data & IMI32S) //Set 32-bit input.
uptr->STATUS |= IMPI32;
if (*data & IMI32C) //Clear 32-bit input
uptr->STATUS &= ~IMPI32;
if (*data & IMPODC) //Clear output done.
uptr->STATUS &= ~IMPOD;
if (*data & IMO32C) //Clear 32-bit output.
uptr->STATUS &= ~IMPO32;
if (*data & IMO32S) //Set 32-bit output.
uptr->STATUS |= IMPO32;
if (*data & IMPODS) //Set output done.
uptr->STATUS |= IMPOD;
if (*data & IMPIR) { //Enable interrup on IMP ready.
uptr->STATUS |= IMPIC;
uptr->STATUS &= ~IMPERR;
}
if (*data & IMPHEC) { //Clear host error.
/* Only if there has been a CONI lately. */
if (last_coni - sim_interval < CONI_TIMEOUT)
uptr->STATUS &= ~IMPHER;
}
if (*data & IMIIHE) //Inhibit interrupt on host error.
uptr->STATUS |= IMPIHE;
if (*data & IMPLHW) //Last host word.
uptr->STATUS |= IMPLHW;
break;
case CONI:
last_coni = sim_interval;
*data = uptr->STATUS;
sim_debug(DEBUG_CONI, dptr, "IMP %03o CONI %012llo PC=%o\n", dev,
*data, PC);
break;
case DATAO:
uptr->STATUS |= IMPOB;
uptr->STATUS &= ~IMPOD;
imp_data.obuf = *data;
imp_data.obits = (uptr->STATUS & IMPO32) ? 32 : 36;
sim_debug(DEBUG_DATAIO, dptr, "IMP %03o DATO %012llo %d %08x PC=%o\n",
dev, *data, imp_data.obits, (uint32)(*data >> 4), PC);
break;
case DATAI:
*data = imp_data.ibuf;
uptr->STATUS &= ~(IMPID|IMPLW);
sim_debug(DEBUG_DATAIO, dptr, "IMP %03o DATI %012llo %08x PC=%o\n",
dev, *data, (uint32)(*data >> 4), PC);
if (uptr->ILEN != 0)
uptr->STATUS |= IMPIB;
break;
}
check_interrupts (uptr);
return SCPE_OK;
}
t_stat imp_srv(UNIT * uptr)
{
DEVICE *dptr = find_dev_from_unit(uptr);
int i;
int l;
if (uptr->STATUS & IMPOB && imp_data.sendq == NULL) {
int x = uptr->OPOS>>3;
if (imp_data.obits == 32)
imp_data.obuf >>= 4;
for (i = imp_data.obits - 1; i >= 0; i--) {
imp_data.sbuffer[uptr->OPOS>>3] |=
((imp_data.obuf >> i) & 1) << (7-(uptr->OPOS & 7));
uptr->OPOS++;
}
if (uptr->STATUS & IMPLHW) {
imp_send_packet (&imp_data, uptr->OPOS >> 3);
/* Allow room for ethernet header for later */
memset(imp_data.sbuffer, 0, ETH_FRAME_SIZE);
uptr->OPOS = 0;
uptr->STATUS &= ~IMPLHW;
}
uptr->STATUS &= ~IMPOB;
uptr->STATUS |= IMPOD;
check_interrupts (uptr);
}
if (uptr->STATUS & IMPIB) {
uptr->STATUS &= ~(IMPIB|IMPLW);
imp_data.ibuf = 0;
l = (uptr->STATUS & IMPI32) ? 4 : 0;
for (i = 35; i >= l; i--) {
if ((imp_data.rbuffer[uptr->IPOS>>3] >> (7-(uptr->IPOS & 7))) & 1)
imp_data.ibuf |= ((uint64)1) << i;
uptr->IPOS++;
if (uptr->IPOS > uptr->ILEN) {
uptr->STATUS |= IMPLW;
uptr->ILEN = 0;
break;
}
}
uptr->STATUS |= IMPID;
check_interrupts (uptr);
}
sim_activate(uptr, 200);
return SCPE_OK;
}
/*
* Update the checksum based on code from RFC1631
*/
void checksumadjust(uint8 *chksum, uint8 *optr,
int olen, uint8 *nptr, int nlen)
/* assuming: unsigned char is 8 bits, long is 32 bits.
- chksum points to the chksum in the packet
- optr points to the old data in the packet
- nptr points to the new data in the packet
- even number of octets updated.
*/
{
int32 x, old, new;
x=chksum[0]*256+chksum[1];
x=(~x & 0xffff);
while (olen > 1) {
old=optr[0]*256+optr[1];
optr+=2;
x-=old & 0xffff;
if (x<=0) { x--; x&=0xffff; }
olen-=2;
}
if (olen > 0) {
old=optr[0]*256;
x-=old & 0xffff;
if (x<=0) { x--; x&=0xffff; }
}
while (nlen > 1) {
new=nptr[0]*256+nptr[1];
nptr+=2;
x+=new & 0xffff;
if (x & 0x10000) { x++; x&=0xffff; }
nlen-=2;
}
if (nlen > 0) {
new=nptr[0]*256;
x+=new & 0xffff;
if (x & 0x10000) { x++; x&=0xffff; }
}
x=(~x & 0xffff);
chksum[0]=x/256; chksum[1]=x & 0xff;
}
t_stat imp_eth_srv(UNIT * uptr)
{
ETH_PACK read_buffer;
sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */
if (imp_data.init_state >= 3 && imp_data.init_state < 6) {
if (imp_unit[0].ILEN == 0) {
/* Queue up a nop packet */
imp_data.rbuffer[0] = 0xf;
imp_data.rbuffer[3] = 4;
imp_unit[0].STATUS |= IMPIB;
imp_unit[0].IPOS = 0;
imp_unit[0].ILEN = 12*8;
imp_data.init_state++;
sim_debug(DEBUG_DETAIL, &imp_dev, "IMP Send Nop %d\n",
imp_data.init_state);
}
} else if (uptr->ILEN == 0 &&
eth_read (&imp_data.etherface, &read_buffer, NULL) > 0) {
imp_packet_in(&imp_data, &read_buffer);
}
imp_timer_task(&imp_data);
return SCPE_OK;
}
void
imp_timer_task(struct imp_device *imp)
{
struct imp_packet *nq = NULL; /* New send queue */
/* Scan the send queue and see if any packets have timed out */
while (imp->sendq != NULL) {
struct imp_packet *temp = imp->sendq;
imp->sendq = temp->next;
if (--temp->life == 0) {
imp_free_packet(imp, temp);
sim_debug(DEBUG_DETAIL, &imp_dev,
"IMP packet timed out %08x\n", temp->dest);
} else {
/* Not yet, put back on queue */
temp->next = nq;
nq = temp;
}
}
imp->sendq = nq;
}
void
imp_packet_in(struct imp_device *imp, ETH_PACK *read_buffer)
{
struct imp_eth_hdr *hdr = (struct imp_eth_hdr *)(&read_buffer->msg[0]);
int type = ntohs(hdr->type);
int n;
int pad;
int i;
if (type == ETHTYPE_ARP) {
imp_arp_arpin(imp, read_buffer);
} else if (type == ETHTYPE_IP) {
struct ip *ip_hdr =
(struct ip *)(&read_buffer->msg[sizeof(struct imp_eth_hdr)]);
/* Process as IP if it is for us */
if (ip_hdr->ip_dst == imp_data.ip || ip_hdr->ip_dst == 0) {
/* Add mac address since we will probably need it later */
imp_arp_update(&ip_hdr->ip_src, &hdr->src);
/* Clear beginning of message */
memset(&imp->rbuffer[0], 0, 256);
imp->rbuffer[0] = 0xf;
imp->rbuffer[3] = 0;
imp->rbuffer[5] = (ntohl(ip_hdr->ip_src) >> 16) & 0xff;
imp->rbuffer[7] = 14;
imp->rbuffer[8] = 0233;
imp->rbuffer[18] = 0;
imp->rbuffer[19] = 0x80;
imp->rbuffer[21] = 0x30;
/* Copy message over */
pad = 12 + (imp->padding / 8);
n = read_buffer->len - (sizeof(struct imp_eth_hdr));
memcpy(&imp->rbuffer[pad], ip_hdr ,n);
// fprintf(stderr, "rec Len=%d: ", n);
// for (i = pad; i < n; i++)
// fprintf(stderr, "%02x ", imp->rbuffer[i]);
// fprintf(stderr, "\r\n");
/* Repoint IP header to copy packet */
ip_hdr = (struct ip *)(&imp->rbuffer[pad]);
/*
* If local IP defined, change destination to ip,
* and update checksum
*/
if (ip_hdr->ip_dst == imp_data.ip && imp_data.hostip != 0) {
uint8 *payload = (uint8 *)(&imp->rbuffer[pad +
(ip_hdr->ip_v_hl & 0xf) * 4]);
uint16 chk = ip_hdr->ip_sum;
checksumadjust((uint8 *)&ip_hdr->ip_sum,
(uint8 *)(&ip_hdr->ip_dst), sizeof(in_addr_t),
(uint8 *)(&imp_data.hostip), sizeof(in_addr_t));
/* If TCP packet update the TCP checksum */
if (ip_hdr->ip_p == TCP_PROTO) {
struct tcp *tcp_hdr = (struct tcp *)payload;
checksumadjust((uint8 *)&tcp_hdr->chksum,
(uint8 *)(&ip_hdr->ip_dst), sizeof(in_addr_t),
(uint8 *)(&imp_data.hostip), sizeof(in_addr_t));
/* Check if UDP */
} else if (ip_hdr->ip_p == UDP_PROTO) {
struct udp *udp_hdr = (struct udp *)payload;
checksumadjust((uint8 *)&udp_hdr->chksum,
(uint8 *)(&ip_hdr->ip_src), sizeof(in_addr_t),
(uint8 *)(&imp_data.hostip), sizeof(in_addr_t));
/* Lastly check if ICMP */
} else if (ip_hdr->ip_p == ICMP_PROTO) {
struct icmp *icmp_hdr = (struct icmp *)payload;
checksumadjust((uint8 *)&icmp_hdr->chksum,
(uint8 *)(&ip_hdr->ip_src), sizeof(in_addr_t),
(uint8 *)(&imp_data.hostip), sizeof(in_addr_t));
}
ip_hdr->ip_dst = imp_data.hostip;
}
n += pad;
imp_unit[0].STATUS |= IMPIB;
imp_unit[0].IPOS = 0;
imp_unit[0].ILEN = n*8;
// fprintf(stderr, "recv Len=%d: ", n);
// for (i = pad; i < n; i++)
// fprintf(stderr, "%02x ", imp->rbuffer[i]);
// fprintf(stderr, "\r\n");
}
/* Otherwise just ignore it */
}
}
void
imp_send_packet (struct imp_device *imp, int len)
{
ETH_PACK write_buffer;
int i;
UNIT *uptr = &imp_unit[1];
int n;
int st;
int lk;
// fprintf(stderr, "Out Len=%d: %d %d %d; ", len, imp->sbuffer[3], imp->sbuffer[8], imp->sbuffer[9]);
// for (i = 0; i < len; i++)
// fprintf(stderr, "%02x ", imp->sbuffer[i]);
// fprintf(stderr, "\r\n");
if (imp->sbuffer[0] != 0xF) {
// Send type 1 message.
/* Send back invalid leader message */
fprintf(stderr, "Invalid header\r\n");
return;
}
n = (imp->sbuffer[10] << 8) + (imp->sbuffer[11]);
st = imp->sbuffer[9] & 0xf;
lk = imp->sbuffer[8];
sim_debug(DEBUG_DETAIL, &imp_dev,
"IMP packet Type=%d ht=%d dh=%d imp=%d lk=%d %d st=%d Len=%d\n",
imp->sbuffer[3], imp->sbuffer[4], imp->sbuffer[5],
(imp->sbuffer[6] * 256) + imp->sbuffer[7],
lk, imp->sbuffer[9] >> 4, st, n);
switch(imp->sbuffer[3]) {
case 0: /* Regular packet */
switch(st) {
case 0: /* Regular */
case 1: /* Refusable */
if (lk == 0233) {
i = 12 + (imp->padding / 8);
n = len - i;
memcpy(&write_buffer.msg[sizeof(struct imp_eth_hdr)],
&imp->sbuffer[i], n);
write_buffer.len = n+sizeof(struct imp_eth_hdr);
imp_packet_out(imp, &write_buffer);
}
break;
case 2: /* Getting ready */
case 3: /* Uncontrolled */
default:
break;
}
break;
case 1: /* Error */
break;
case 2: /* Host going down */
break;
case 4: /* Nop */
if (imp->init_state < 3)
imp->init_state++;
imp->padding = st * 16;
sim_debug(DEBUG_DETAIL, &imp_dev,
"IMP recieve Nop %d padding= %d\n",
imp->init_state, imp->padding);
sim_activate(uptr, tmxr_poll); /* Start reciever task */
break;
case 8: /* Error with Message */
break;
defult:
break;
}
return;
}
/*
* Check if this packet can be sent to given IP.
* If it can we fill in the mac address and return false.
* If we can't we need to queue up and send a ARP packet and return true.
*/
void
imp_packet_out(struct imp_device *imp, ETH_PACK *packet) {
struct ip_hdr *pkt = (struct ip_hdr *)(&packet->msg[0]);
struct imp_packet *send;
struct arp_entry *tabptr;
struct arp_hdr *arp;
ETH_PACK arp_pkt;
in_addr_t ipaddr;
int i;
// fprintf(stderr, "pkt Len=%d: ", packet->len);
// for (i = sizeof(struct imp_eth_hdr); i < packet->len; i++)
// fprintf(stderr, "%02x ", packet->msg[i]);
// fprintf(stderr, "\r\n");
/* If local IP defined, change source to ip, and update checksum */
if (imp->hostip != 0) {
int hl = (pkt->iphdr.ip_v_hl & 0xf) * 4;
uint8 *payload = (uint8 *)(&packet->msg[
sizeof(struct imp_eth_hdr) + hl]);
/* If TCP packet update the TCP checksum */
if (pkt->iphdr.ip_p == TCP_PROTO) {
struct tcp *tcp_hdr = (struct tcp *)payload;
int thl = ((ntohs(tcp_hdr->flags) >> 12) & 0xf) * 4;
uint8 *tcp_payload = &packet->msg[
sizeof(struct imp_eth_hdr) + hl + thl];
checksumadjust((uint8 *)&tcp_hdr->chksum,
(uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_t),
(uint8 *)(&imp->ip), sizeof(in_addr_t));
/* Check if sending to FTP */
if (ntohs(tcp_hdr->tcp_dport) == 21 &&
strncmp(&tcp_payload[0], "PORT ", 5) == 0) {
/* We need to translate the IP address to new port number. */
int thl = ((tcp_hdr->flags >> 12) & 0xf) * 4;
char port_buffer[100];
int l = ntohs(pkt->iphdr.ip_len) - thl - hl;
int c;
uint32 nip = ntohl(imp->ip);
uint16 len;
/* Count out 4 commas */
for (i = c = 0; i < l && c < 4; i++) {
if (tcp_hdr->payload[i] == ',')
c++;
}
c = sprintf(port_buffer, "PORT %d,%d,%d,%d,",
(nip >> 24) & 0xFF, (nip >> 16) & 0xFF,
(nip >> 8) & 0xFF, nip&0xff);
/* Copy over rest of string */
while(i < l) {
port_buffer[c++] = tcp_hdr->payload[i++];
}
/* Now we need to update the checksums */
/* First new PORT command */
checksumadjust((uint8 *)&tcp_hdr->chksum,
(uint8 *)(&tcp_hdr->payload), l,
(uint8 *)(&port_buffer), c);
/* Now update the payload */
memcpy(&tcp_hdr->payload[0], port_buffer, c);
/* Now update the length */
len = htons(c + thl + hl);
checksumadjust((uint8 *)&pkt->iphdr.ip_sum,
(uint8 *)(&pkt->iphdr.ip_len), 2,
(uint8 *)(&len), 2);
checksumadjust((uint8 *)&tcp_hdr->chksum,
(uint8 *)(&pkt->iphdr.ip_len), 2,
(uint8 *)(&len), 2);
pkt->iphdr.ip_len = len;
packet->len = ntohs(len) + sizeof(struct imp_eth_hdr);
packet->len += (packet->len&1); /* Round to even size */
}
/* Check if UDP */
} else if (pkt->iphdr.ip_p == UDP_PROTO) {
struct udp *udp_hdr = (struct udp *)payload;
checksumadjust((uint8 *)&udp_hdr->chksum,
(uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_t),
(uint8 *)(&imp->ip), sizeof(in_addr_t));
/* Lastly check if ICMP */
} else if (pkt->iphdr.ip_p == ICMP_PROTO) {
struct icmp *icmp_hdr = (struct icmp *)payload;
checksumadjust((uint8 *)&icmp_hdr->chksum,
(uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_t),
(uint8 *)(&imp->ip), sizeof(in_addr_t));
}
/* Lastly update the header and IP address */
checksumadjust((uint8 *)&pkt->iphdr.ip_sum,
(uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_t),
(uint8 *)(&imp->ip), sizeof(in_addr_t));
pkt->iphdr.ip_src = imp->ip;
}
// fprintf(stderr, "pkt2 Len=%d: ", packet->len);
// for (i = sizeof(struct imp_eth_hdr); i < packet->len; i++)
// fprintf(stderr, "%02x ", packet->msg[i]);
// fprintf(stderr, "\r\n");
/* Try to send the packed */
ipaddr = pkt->iphdr.ip_dst;
/* Check if on our subnet */
if ((imp->ip & imp->ip_mask) != (ipaddr & imp->ip_mask))
ipaddr = imp->gwip;
for (i = 0; i < IMP_ARPTAB_SIZE; i++) {
tabptr = &arp_table[i];
if (ipaddr == tabptr->ipaddr) {
memcpy(&pkt->ethhdr.dest, &tabptr->ethaddr, 6);
memcpy(&pkt->ethhdr.src, &imp->mac, 6);
pkt->ethhdr.type = htons(ETHTYPE_IP);
packet->crc_len = eth_add_packet_crc32(&packet->msg[0], packet->len);
packet->len = packet->crc_len;
eth_write(&imp->etherface, packet, NULL);
return;
}
}
/* Queue packet for later send */
send = imp_get_packet(imp);
send->next = imp->sendq;
imp->sendq = send;
send->packet.len = packet->len;
send->life = 1000;
send->dest = pkt->iphdr.ip_dst;
memcpy(&send->packet.msg[0], pkt, send->packet.len);
/* We did not find it, so construct and send a ARP packet */
memset(&arp_pkt, 0, sizeof(ETH_PACK));
arp = (struct arp_hdr *)(&arp_pkt.msg[0]);
memcpy(&arp->ethhdr.dest, &broadcast_ethaddr, 6);
memcpy(&arp->ethhdr.src, &imp->mac, 6);
arp->ethhdr.type = htons(ETHTYPE_ARP);
memset(&arp->dhwaddr, 0x00, 6);
memcpy(&arp->shwaddr, &imp->mac, 6);
arp->dipaddr = ipaddr;
arp->sipaddr = imp->ip;
arp->opcode = htons(ARP_REQUEST);
arp->hwtype = htons(ARP_HWTYPE_ETH);
arp->protocol = htons(ETHTYPE_IP);
arp->hwlen = 6;
arp->protolen = 4;
arp_pkt.len = sizeof(struct arp_hdr);
packet->crc_len = eth_add_packet_crc32(&packet->msg[0], packet->len);
packet->len = packet->crc_len;
eth_write(&imp->etherface, &arp_pkt, NULL);
}
/*
* Update the ARP table, first use free entry, else use oldest.
*/
void
imp_arp_update(in_addr_t *ipaddr, ETH_MAC *ethaddr)
{
struct arp_entry *tabptr;
int i;
static int arptime = 0;
/* Check if entry already in the table. */
for (i = 0; i < IMP_ARPTAB_SIZE; i++) {
tabptr = &arp_table[i];
if (tabptr->ipaddr != 0) {
if (tabptr->ipaddr == *ipaddr) {
memcpy(&tabptr->ethaddr, ethaddr, sizeof(ETH_MAC));
tabptr->time = ++arptime;
return;
}
}
}
/* See if we can find an unused entry. */
for (i = 0; i < IMP_ARPTAB_SIZE; i++) {
tabptr = &arp_table[i];
if (tabptr->ipaddr == 0)
break;
}
/* If no empty entry search for oldest one. */
if (tabptr->ipaddr != 0) {
int fnd = 0;
uint16 tmpage = 0;
for (i = 0; i < IMP_ARPTAB_SIZE; i++) {
tabptr = &arp_table[i];
if (arptime - tabptr->time > tmpage) {
tmpage = arptime - tabptr->time;
fnd = i;
}
}
tabptr = &arp_table[fnd];
}
/* Now update the entry */
memcpy(&tabptr->ethaddr, ethaddr, sizeof(ETH_MAC));
tabptr->ipaddr = *ipaddr;
tabptr->time = ++arptime;
}
/*
* Process incomming ARP packet.
*/
void
imp_arp_arpin(struct imp_device *imp, ETH_PACK *packet)
{
struct arp_hdr *arp;
int op;
t_stat r;
/* Ignore packet if too short */
if (packet->len < sizeof(struct arp_hdr))
return;
arp = (struct arp_hdr *)(&packet->msg[0]);
op = ntohs(arp->opcode);
switch (op) {
case ARP_REQUEST:
if (arp->dipaddr == imp->ip) {
imp_arp_update(&arp->sipaddr, &arp->shwaddr);
arp->opcode = htons(ARP_REPLY);
memcpy(&arp->dhwaddr, &arp->shwaddr, 6);
memcpy(&arp->shwaddr, &imp->mac, 6);
memcpy(&arp->ethhdr.src, &imp->mac, 6);
memcpy(&arp->ethhdr.dest, &arp->dhwaddr, 6);
arp->dipaddr = arp->sipaddr;
arp->sipaddr = imp->ip;
arp->ethhdr.type = htons(ETHTYPE_ARP);
packet->len = sizeof(struct arp_hdr);
packet->crc_len = eth_add_packet_crc32(&packet->msg[0], packet->len);
packet->len = packet->crc_len;
eth_write(&imp->etherface, packet, NULL);
}
break;
case ARP_REPLY:
/* Check if this is our address */
if (arp->dipaddr == imp->ip) {
struct imp_packet *nq = NULL; /* New send queue */
imp_arp_update(&arp->sipaddr, &arp->shwaddr);
/* Scan send queue, and send all packets for this host */
while (imp->sendq != NULL) {
struct imp_packet *temp = imp->sendq;
imp->sendq = temp->next;
if (temp->dest == arp->sipaddr) {
struct ip_hdr *pkt = (struct ip_hdr *)
(&temp->packet.msg[0]);
memcpy(&pkt->ethhdr.dest, &arp->shwaddr, 6);
memcpy(&pkt->ethhdr.src, &imp->mac, 6);
pkt->ethhdr.type = htons(ETHTYPE_IP);
temp->packet.crc_len = eth_add_packet_crc32(
&temp->packet.msg[0], temp->packet.len);
temp->packet.len = temp->packet.crc_len;
eth_write(&imp->etherface, &temp->packet, NULL);
imp_free_packet(imp, temp);
} else {
temp->next = nq;
nq = temp;
}
}
imp->sendq = nq;
}
break;
}
return;
}
t_stat imp_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 mpx;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
mpx = (int32) get_uint (cptr, 8, 8, &r);
if (r != SCPE_OK)
return r;
imp_mpx_lvl = mpx;
return SCPE_OK;
}
t_stat imp_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (uptr == NULL)
return SCPE_IERR;
fprintf (st, "MPX=%o", imp_mpx_lvl);
return SCPE_OK;
}
t_stat imp_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
{
char buffer[20];
eth_mac_fmt(&imp_data.mac, buffer);
fprintf(st, "MAC=%s", buffer);
return SCPE_OK;
}
t_stat imp_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
t_stat status;
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
status = eth_mac_scan_ex(&imp_data.mac, cptr, uptr);
if (status != SCPE_OK)
return status;
return SCPE_OK;
}
t_stat imp_show_ip (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
struct in_addr ip;
ip.s_addr = imp_data.ip;
fprintf (st, "IP=%s/%d", inet_ntoa(ip), imp_data.maskbits);
}
t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
char tbuf[CBUFSIZE], gbuf[CBUFSIZE], abuf[CBUFSIZE];
struct in_addr ip;
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
cptr = get_glyph (cptr, abuf, '/');
if (cptr && *cptr)
imp_data.maskbits = atoi (cptr);
else
imp_data.maskbits = 32;
if (inet_aton (abuf, &ip)) {
imp_data.ip = ip.s_addr;
imp_data.ip_mask = htonl((0xffffffff) << (32 - imp_data.maskbits));
return SCPE_OK;
}
return SCPE_ARG;
}
t_stat imp_show_gwip (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
struct in_addr ip;
ip.s_addr = imp_data.gwip;
fprintf (st, "GW=%s", inet_ntoa(ip));
}
t_stat imp_set_gwip (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
struct in_addr ip;
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
if (inet_aton (cptr, &ip)) {
imp_data.gwip = ip.s_addr;
return SCPE_OK;
}
return SCPE_ARG;
}
t_stat imp_show_hostip (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
struct in_addr ip;
ip.s_addr = imp_data.hostip;
fprintf (st, "HOST=%s", inet_ntoa(ip));
}
t_stat imp_set_hostip (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
struct in_addr ip;
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
if (inet_aton (cptr, &ip)) {
imp_data.hostip = ip.s_addr;
return SCPE_OK;
}
return SCPE_ARG;
}
struct imp_packet *
imp_get_packet(struct imp_device *imp) {
struct imp_packet *ret;
/* Check if list empty */
if ((ret = imp->freeq) != NULL) {
imp->freeq = ret->next;
ret->next = NULL;
}
return ret;
}
void
imp_free_packet(struct imp_device *imp, struct imp_packet *p) {
p->next = imp->freeq;
imp->freeq = p;
}
t_stat imp_reset (DEVICE *dptr)
{
int i;
struct imp_packet *p;
/* Clear ARP table. */
for (i = 0; i < IMP_ARPTAB_SIZE; i++) {
arp_table[i].ipaddr = 0;
}
/* Clear queues. */
imp_data.sendq = NULL;
/* Set up free queue */
p = NULL;
for (i = 0; i < (sizeof(imp_buffer)/sizeof(struct imp_packet)); i++) {
imp_buffer[i].next = p;
p = &imp_buffer[i];
}
/* Fix last entry */
imp_data.freeq = p;
imp_data.init_state = 0;
last_coni = sim_interval;
sim_activate(&imp_unit[0], 200);
return SCPE_OK;
}
/* attach device: */
t_stat imp_attach(UNIT* uptr, CONST char* cptr)
{
t_stat status;
char* tptr;
tptr = (char *) malloc(strlen(cptr) + 1);
if (tptr == NULL) return SCPE_MEM;
strcpy(tptr, cptr);
status = eth_open(&imp_data.etherface, cptr, &imp_dev, 0xFFFF);
if (status != SCPE_OK) {
free(tptr);
return status;
}
// eth_set_throttle (&imp_data.etherface, imp_data.throttle_time, xu->var->throttle_burst, xu->var->throttle_delay);
if (SCPE_OK != eth_check_address_conflict (&imp_data.etherface, &imp_data.mac)) {
char buf[32];
eth_mac_fmt(&imp_data.mac, buf); /* format ethernet mac address */
sim_printf("%s: MAC Address Conflict on LAN for address %s\n", imp_dev.name, buf);
eth_close(&imp_data.etherface);
free(tptr);
return SCPE_NOATT;
}
if (SCPE_OK != eth_filter(&imp_data.etherface, 1, &imp_data.mac, 1, 0)) {
eth_close(&imp_data.etherface);
free(tptr);
return SCPE_NOATT;
}
uptr->filename = tptr;
uptr->flags |= UNIT_ATT;
eth_setcrc(&imp_data.etherface, 1); /* enable CRC */
/* init read queue (first time only) */
status = ethq_init(&imp_data.ReadQ, 8);
if (status != SCPE_OK) {
eth_close(&imp_data.etherface);
free(tptr);
return status;
}
return SCPE_OK;
}
/* detach device: */
t_stat imp_detach(UNIT* uptr)
{
if (uptr->flags & UNIT_ATT) {
eth_close (&imp_data.etherface);
free(uptr->filename);
uptr->filename = NULL;
uptr->flags &= ~UNIT_ATT;
/* cancel service timers */
sim_cancel (uptr); /* stop the receiver */
sim_cancel (uptr+1); /* stop the timer services */
}
return SCPE_OK;
}
const char *imp_description (DEVICE *dptr)
{
return "KA Host/IMP interface";
}
#endif