From c00e6f8ad052e920e1e0ee1498169aed4176f613 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Sat, 12 Jan 2019 23:33:08 -0500 Subject: [PATCH] KA10: Minor code cleanup. --- PDP10/ka10_imp.c | 2488 +++++++++++++++++++++++----------------------- PDP10/ka10_tu.c | 2 +- 2 files changed, 1245 insertions(+), 1245 deletions(-) diff --git a/PDP10/ka10_imp.c b/PDP10/ka10_imp.c index 4a24b34..e487579 100644 --- a/PDP10/ka10_imp.c +++ b/PDP10/ka10_imp.c @@ -1,1244 +1,1244 @@ -/* 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" - -static char * -ipv4_inet_ntoa(struct in_addr ip) -{ -static char str[20]; - -sprintf (str, "%d.%d.%d.%d", ip.s_addr & 0xFF, (ip.s_addr >> 8) & 0xFF, (ip.s_addr >> 16) & 0xFF, (ip.s_addr >> 24) & 0xFF); -return str; -} - -typedef uint32 in_addr_t; - -static -int ipv4_inet_aton(const char *str, struct in_addr *inp) -{ -unsigned long bytes[4]; -int i = 0; -char *end; -in_addr_t val; - -for (i=0; (i < 4) && isdigit (*str); i++) { - bytes[i] = strtoul (str, &end, 0); - if (str == end) - return 0; - str = end; - if (*str == '.') - ++str; - } -if (*str && (*str != '/')) - return 0; -switch (i) { - case 1: - val = bytes[0]; - break; - case 2: - if ((bytes[0] > 0xFF) || (bytes[1] > 0xFFFFFF)) - return 0; - val = (bytes[0] << 24) | bytes[1]; - break; - case 3: - if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFFFF)) - return 0; - val = (bytes[0] << 24) | (bytes[1] << 16) | bytes[2]; - break; - case 4: - if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFF) || (bytes[3] > 0xFF)) - return 0; - val = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; - break; - default: - return 0; - } -if (inp) - *(in_addr_t *)inp = htonl (val); -return 1; -} - -#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, - ð_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; - 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; - default: - 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; - - /* 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", ipv4_inet_ntoa(ip), imp_data.maskbits); - return SCPE_OK; -} - -t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc) -{ - char 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 (ipv4_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", ipv4_inet_ntoa(ip)); - return SCPE_OK; -} - -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 (ipv4_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", ipv4_inet_ntoa(ip)); - return SCPE_OK; -} - -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 (ipv4_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 +/* 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" + +static char * +ipv4_inet_ntoa(struct in_addr ip) +{ +static char str[20]; + +sprintf (str, "%d.%d.%d.%d", ip.s_addr & 0xFF, (ip.s_addr >> 8) & 0xFF, (ip.s_addr >> 16) & 0xFF, (ip.s_addr >> 24) & 0xFF); +return str; +} + +typedef uint32 in_addr_t; + +static +int ipv4_inet_aton(const char *str, struct in_addr *inp) +{ +unsigned long bytes[4]; +int i = 0; +char *end; +in_addr_t val; + +for (i=0; (i < 4) && isdigit (*str); i++) { + bytes[i] = strtoul (str, &end, 0); + if (str == end) + return 0; + str = end; + if (*str == '.') + ++str; + } +if (*str && (*str != '/')) + return 0; +switch (i) { + case 1: + val = bytes[0]; + break; + case 2: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFFFFFF)) + return 0; + val = (bytes[0] << 24) | bytes[1]; + break; + case 3: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFFFF)) + return 0; + val = (bytes[0] << 24) | (bytes[1] << 16) | bytes[2]; + break; + case 4: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFF) || (bytes[3] > 0xFF)) + return 0; + val = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; + break; + default: + return 0; + } +if (inp) + *(in_addr_t *)inp = htonl (val); +return 1; +} + +#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, + ð_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; + 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; + default: + 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; + + /* 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", ipv4_inet_ntoa(ip), imp_data.maskbits); + return SCPE_OK; +} + +t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + char 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 (ipv4_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", ipv4_inet_ntoa(ip)); + return SCPE_OK; +} + +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 (ipv4_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", ipv4_inet_ntoa(ip)); + return SCPE_OK; +} + +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 (ipv4_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 diff --git a/PDP10/ka10_tu.c b/PDP10/ka10_tu.c index 3c1d4aa..24d78fc 100644 --- a/PDP10/ka10_tu.c +++ b/PDP10/ka10_tu.c @@ -872,7 +872,7 @@ t_stat tu_srv(UNIT * uptr) df->buf = 0; } if (tu_frame[ctlr] != 0 && uptr->u4 == 0 && df10_read(df) == 0) - uptr->u4 != 010; + uptr->u4 |= 010; if ((uptr->u3 & CS_MOTION) != 0) { if (uptr->u4 == 0)