/* uch11.c --- chaosnet interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uch11.h" #include "chaosd.h" #include "hosttab.h" #include "misc.h" #include "ucfg.h" #include "ucode.h" #include "usim.h" #include "utrace.h" enum { CHAOS_CSR_TIMER_INTERRUPT_ENABLE = (01 << 00), /* CHBUSY */ CHAOS_CSR_LOOP_BACK = (01 << 01), /* CHLPBK */ CHAOS_CSR_RECEIVE_ALL = (01 << 02), /* CHSPY */ CHAOS_CSR_RECEIVER_CLEAR = (01 << 03), CHAOS_CSR_RECEIVE_ENABLE = (01 << 04), /* CHREN */ CHAOS_CSR_TRANSMIT_ENABLE = (01 << 05), /* CHRIEN */ CHAOS_CSR_INTERRUPT_ENABLES = (02 << 04), CHAOS_CSR_TRANSMIT_ABORT = (01 << 06), /* CHABRT */ CHAOS_CSR_TRANSMIT_DONE = (01 << 07), /* CHTDN */ CHAOS_CSR_TRANSMITTER_CLEAR = (01 << 010), /* CHTCLR */ CHAOS_CSR_LOST_COUNT = (04 << 011), /* CHLC */ CHAOS_CSR_RESET = (01 << 015), /* CHRST */ CHAOS_CSR_CRC_ERROR = (01 << 016), /* CHCRC */ CHAOS_CSR_RECEIVE_DONE = (01 << 017), /* CHRDN */ } CHAOS_HARDWARE_VALUES; int uch11_backend = UCH11_BACKEND_LOCAL; int uch11_myaddr = 0177040; /* LOCAL-CADR */ int uch11_serveraddr = 0177001; /* LOCAL-BRIDGE */ static int uch11_csr; static int uch11_bit_count; static int uch11_lost_count; static unsigned short uch11_xmit_buffer[4096]; static int uch11_xmit_buffer_size; static int uch11_xmit_buffer_ptr; static unsigned short uch11_rcv_buffer[4096]; static unsigned short uch11_rcv_buffer_toss[4096]; static int uch11_rcv_buffer_ptr; static int uch11_rcv_buffer_size; static bool uch11_rcv_buffer_empty; static int reconnect_delay; static bool reconnect_chaos; static int (*uch11_send)(char *, int); static void uch11_force_reconect(void); static u_short udp_bridge_chaddr; static int chudpopen(void); static int chaos_send_to_udp(char *buffer, int size); static int chaos_poll_udp(void); extern void settreeroot(const char *, const char *prefix); /* * RFC1071: Compute Internet Checksum for COUNT bytes beginning at * location ADDR. */ static unsigned short uch11_checksum(const unsigned char *addr, int count) { long sum; sum = 0; while (count > 1) { sum += *(addr) << 8 | *(addr + 1); addr += 2; count -= 2; } /* * Add left-over byte, if any. */ if (count > 0) sum += *(unsigned char *) addr; /* * Fold 32-bit sum to 16 bits. */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return (~sum) & 0xffff; } /* * Called when we are we have something in uch11_rcv_buffer. */ static void uch11_rx_pkt(void) { uch11_rcv_buffer_ptr = 0; uch11_bit_count = (uch11_rcv_buffer_size * 2 * 8) - 1; INFO(TRACE_CHAOS, "chaos: receiving %d words\n", uch11_rcv_buffer_size); if (uch11_rcv_buffer_size > 0) { DEBUG(TRACE_CHAOS, "chaos: set csr receive done, generate interrupt\n"); uch11_csr |= CHAOS_CSR_RECEIVE_DONE; if (uch11_csr & CHAOS_CSR_RECEIVE_ENABLE) assert_unibus_interrupt(0270); } else { DEBUG(TRACE_CHAOS, "chaos: recieve buffer empty\n"); } } static void uch11_xmit_done_intr(void) { uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; if (uch11_csr & CHAOS_CSR_TRANSMIT_ENABLE) assert_unibus_interrupt(0270); } void uch11_xmit_pkt(void) { INFO(TRACE_CHAOS, "chaos: transmitting %d bytes, data len %d\n", uch11_xmit_buffer_ptr * 2, (uch11_xmit_buffer_ptr > 0 ? uch11_xmit_buffer[1] & 0x3f : -1)); uch11_xmit_buffer_size = uch11_xmit_buffer_ptr; /* * Dest. is already in the buffer. */ uch11_xmit_buffer[uch11_xmit_buffer_size++] = (unsigned short) uch11_myaddr; /* Source. */ uch11_xmit_buffer[uch11_xmit_buffer_size] = uch11_checksum((unsigned char *) uch11_xmit_buffer, uch11_xmit_buffer_size * 2); /* Checksum. */ uch11_xmit_buffer_size++; (*uch11_send) ((char *) uch11_xmit_buffer, uch11_xmit_buffer_size * 2); uch11_xmit_buffer_ptr = 0; uch11_xmit_done_intr(); } int uch11_get_bit_count(void) { if (uch11_rcv_buffer_size > 0) return uch11_bit_count; DEBUG(TRACE_CHAOS, "chaos: returned empty bit count\n"); return 07777; } int uch11_get_rcv_buffer(void) { if (uch11_rcv_buffer_ptr < uch11_rcv_buffer_size) { int ret; ret = uch11_rcv_buffer[uch11_rcv_buffer_ptr++]; if (uch11_rcv_buffer_ptr == uch11_rcv_buffer_size) { DEBUG(TRACE_CHAOS, "chaos: marked recieve buffer as empty\n"); uch11_rcv_buffer_empty = true; } return ret; } /* * Read last word, clear receive done. */ DEBUG(TRACE_CHAOS, "chaos: cleared csr receive done\n"); uch11_csr &= ~CHAOS_CSR_RECEIVE_DONE; uch11_rcv_buffer_size = 0; return 0; } void uch11_put_xmit_buffer(int v) { if (uch11_xmit_buffer_ptr < (int) sizeof(uch11_xmit_buffer) / 2) uch11_xmit_buffer[uch11_xmit_buffer_ptr++] = (unsigned short) v; uch11_csr &= ~CHAOS_CSR_TRANSMIT_DONE; } int uch11_get_csr(void) { static int old_csr = 0; if (uch11_csr != old_csr) { DEBUG(TRACE_CHAOS, "chaos: read csr %o\n", uch11_csr); old_csr = uch11_csr; } return uch11_csr | ((uch11_lost_count << 9) & 017); } void uch11_set_csr(int v) { int mask; int old_csr; old_csr = uch11_csr; v &= 0xffff; /* * Writing these don't stick. */ /* *INDENT-OFF* */ mask = CHAOS_CSR_TRANSMIT_DONE | CHAOS_CSR_LOST_COUNT | CHAOS_CSR_CRC_ERROR | CHAOS_CSR_RECEIVE_DONE | CHAOS_CSR_RECEIVER_CLEAR; /* *INDENT-ON* */ uch11_csr = (uch11_csr & mask) | (v & ~mask); if (uch11_csr & CHAOS_CSR_RESET) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; uch11_xmit_buffer_ptr = 0; uch11_lost_count = 0; uch11_bit_count = 0; uch11_csr &= ~(CHAOS_CSR_RESET | CHAOS_CSR_RECEIVE_DONE); uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; reconnect_delay = 200; /* Do it right away. */ uch11_force_reconect(); } if (v & CHAOS_CSR_RECEIVER_CLEAR) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; uch11_lost_count = 0; uch11_bit_count = 0; uch11_csr &= ~CHAOS_CSR_RECEIVE_DONE; } if (v & (CHAOS_CSR_TRANSMITTER_CLEAR | CHAOS_CSR_TRANSMIT_DONE)) { uch11_xmit_buffer_ptr = 0; uch11_csr &= ~CHAOS_CSR_TRANSMIT_ABORT; uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; } if (uch11_csr & CHAOS_CSR_RECEIVE_ENABLE) { if ((old_csr & CHAOS_CSR_RECEIVE_ENABLE) == 0) DEBUG(TRACE_CHAOS, "chaos: CSR receive enable\n"); if (uch11_rcv_buffer_empty) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; } /* * If buffer is full, generate status and interrupt again. */ if (uch11_rcv_buffer_size > 0) { DEBUG(TRACE_CHAOS, "chaos: rx-enabled and buffer is full\n"); uch11_rx_pkt(); } } else if (old_csr & CHAOS_CSR_RECEIVE_ENABLE) DEBUG(TRACE_CHAOS, "chaos: CSR receive DISable\n"); if (uch11_csr & CHAOS_CSR_TRANSMIT_ENABLE) { if ((old_csr & CHAOS_CSR_TRANSMIT_ENABLE) == 0) DEBUG(TRACE_CHAOS, "chaos: CSR transmit enable\n"); uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; } else if (old_csr & CHAOS_CSR_TRANSMIT_ENABLE) DEBUG(TRACE_CHAOS, "chaos: CSR transmit DISable\n"); DEBUG(TRACE_CHAOS, "chaos: set csr bits 0%o, old 0%o, new 0%o\n", v, old_csr, uch11_csr); } /* Connect to a chaos daemon via a Unix socket. */ #define UNIX_SOCKET_PATH "/var/tmp/" #define UNIX_SOCKET_CLIENT_NAME "chaosd_" #define UNIX_SOCKET_SERVER_NAME "chaosd_server" #define UNIX_SOCKET_PERM S_IRWXU static int chaosd_fd; static int chaos_send_to_chaosd(char *buffer, int size) { int wcount, dest_addr; /* * Local loopback. */ if (uch11_csr & CHAOS_CSR_LOOP_BACK) { DEBUG(TRACE_CHAOS, "chaos: loopback %d bytes\n", size); memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); return 0; } wcount = (size + 1) / 2; dest_addr = ((unsigned short *) buffer)[wcount - 3]; DEBUG(TRACE_CHAOS, "chaos: sending packet to chaosd (dest_addr=%o, uch11_myaddr=%o, size %d, wcount %d)\n", dest_addr, uch11_myaddr, size, wcount); /* * Recieve packets addressed to us, but don't receive broadcasts we send */ if (dest_addr == uch11_myaddr) { memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); } if (chaosd_fd == -1) return 0; { struct iovec iov[2]; unsigned char lenbytes[4]; int ret; lenbytes[0] = size >> 8; lenbytes[1] = size; lenbytes[2] = 1; lenbytes[3] = 0; iov[0].iov_base = lenbytes; iov[0].iov_len = 4; iov[1].iov_base = buffer; iov[1].iov_len = size; ret = writev(chaosd_fd, iov, 2); if (ret < 0) { perror("chaos write"); return -1; } } return 0; } static int chaos_poll_chaosd(void) { ssize_t ret; struct pollfd pfd[1]; int nfds, timeout; unsigned char lenbytes[4]; ssize_t len; int dest_addr; if (reconnect_chaos == true) { uch11_reconnect(); } if (chaosd_fd == -1) { return 0; } timeout = 0; nfds = 1; pfd[0].fd = chaosd_fd; pfd[0].events = POLLIN; pfd[0].revents = 0; ret = poll(pfd, nfds, timeout); if (ret == -1) { ERR(TRACE_CHAOS, "chaos: polling chaosd: nothing there (RDN=%o)\n", uch11_csr & CHAOS_CSR_RECEIVE_DONE); reconnect_chaos = true; return -1; } else if (ret == 0) { ERR(TRACE_CHAOS, "chaos: timeout\n"); return -1; } /* * Is RX buffer full? */ if (!uch11_rcv_buffer_empty && (uch11_csr & CHAOS_CSR_RECEIVE_DONE)) { /* * Toss packets arriving when buffer is already in * use, they will be resent. */ ERR(TRACE_CHAOS, "chaos: polling chaosd: unread data, drop (RDN=%o, lost %d)\n", uch11_csr & CHAOS_CSR_RECEIVE_DONE, uch11_lost_count); uch11_lost_count++; ret = read(chaosd_fd, lenbytes, 4); if (ret != 4) perror("read"); len = (lenbytes[0] << 8) | lenbytes[1]; DEBUG(TRACE_CHAOS, "chaos: tossing packet of %d bytes\n", len); if (len > (ssize_t) sizeof(uch11_rcv_buffer_toss)) { ERR(TRACE_CHAOS, "chaos: packet won't fit (len=%d)", len); uch11_force_reconect(); return -1; } /* * Toss it... */ ret = read(chaosd_fd, (char *) uch11_rcv_buffer_toss, len); if (ret != len) perror("read"); return -1; } /* * Read header from chaosd. */ ret = read(chaosd_fd, lenbytes, 4); if (ret <= 0) { perror("chaos: header read error"); uch11_force_reconect(); return -1; } len = (lenbytes[0] << 8) | lenbytes[1]; if (len > (ssize_t) sizeof(uch11_rcv_buffer)) { ERR(TRACE_CHAOS, "chaos: packet too big: pkt size %d, buffer size %lu\n", len, sizeof(uch11_rcv_buffer)); /* * When we get out of synch break socket conn. */ uch11_force_reconect(); return -1; } ret = read(chaosd_fd, (char *) uch11_rcv_buffer, len); if (ret == -1) { perror("chaos: read"); uch11_force_reconect(); return -1; } else if (ret == 0) { ERR(TRACE_CHAOS, "chaos: read zero bytes\n"); return -1; } DEBUG(TRACE_CHAOS, "chaos: polling; got chaosd packet %d\n", ret); uch11_rcv_buffer_size = (ret + 1) / 2; uch11_rcv_buffer_empty = false; dest_addr = uch11_rcv_buffer[uch11_rcv_buffer_size - 3]; /* * If not to us, ignore. */ if (dest_addr != uch11_myaddr) { DEBUG(TRACE_CHAOS, "chaos: ignoring packet not to us (%o): %o\n", uch11_myaddr, dest_addr); uch11_rcv_buffer_size = 0; uch11_rcv_buffer_empty = true; /* * Should uch11_rx_pkt be called here? */ return 0; } DEBUG(TRACE_CHAOS, "chaos: recieving packet: from %o, my %o\n", dest_addr, uch11_myaddr); uch11_rx_pkt(); return 0; } /* Local Chaos -- this should go into chlib.c */ static pthread_mutex_t recvqueue; static pthread_mutex_t recvqueue; struct queue_head queuehead = TAILQ_HEAD_INITIALIZER(queuehead); static void chaos_queue(struct packet *packet) { struct packet_queue *node; node = malloc(sizeof(struct packet_queue)); node->packet = packet; pthread_mutex_lock(&recvqueue); TAILQ_INSERT_TAIL(&queuehead, node, next); pthread_mutex_unlock(&recvqueue); } int chaos_connection_queue(struct connection *conn, struct packet *packet) { struct packet_queue *node; unsigned short nextpacket; INFO(TRACE_CHAOS, "chaos: sending packet (0%o) to 0%o from 0%o (length: %d)\n", packet->pk_op, CH_ADDR_SHORT(packet->pk_daddr), CH_ADDR_SHORT(packet->pk_saddr), PH_LEN(packet->pk_phead)); if (trace_level == LOG_DEBUG && (trace_facilities & TRACE_CHAOS)) { dumpmem(packet->pk_cdata, PH_LEN(packet->pk_phead)); } /* * Any packet for remote gets queued. */ if (CH_ADDR_SHORT(packet->pk_daddr) == CH_ADDR_SHORT(conn->cn_faddr)) { for (;;) { if (chtfull(conn) && packet->pk_op != STSOP) { struct timespec ts; DEBUG(TRACE_CHAOS, "chaos: waiting for remote ack packet=%d (cn_tlast = %d cn_tacked = %d twsize = %d)\n", packet->pk_pkn, conn->cn_tlast, conn->cn_tacked, conn->cn_twsize); pthread_mutex_lock(&conn->twsem); clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 5; if (pthread_cond_timedwait(&conn->twcond, &conn->twsem, &ts)) { if (conn->lastpacket) { struct packet *retransmit; DEBUG(TRACE_CHAOS, "chaos: re-transmit last packet\n"); retransmit = conn->lastpacket; conn->lastpacket = 0; chaos_queue(retransmit); } } pthread_mutex_unlock(&conn->twsem); } else break; } if (packet->pk_op != STSOP && CH_INDEX_SHORT(packet->pk_didx) == CH_INDEX_SHORT(conn->cn_fidx) && cmp_gt(packet->pk_pkn, conn->cn_tlast)) conn->cn_tlast = packet->pk_pkn; conn->lastpacket = packet; chaos_queue(packet); return 0; } node = malloc(sizeof(struct packet_queue)); node->packet = packet; nextpacket = conn->cn_rlast + 1; if (cmp_gt(packet->pk_pkn, nextpacket)) { DEBUG(TRACE_CHAOS, "chaos: queuing out-of-order packet: nextpacket=%d packet=%d\n", nextpacket, packet->pk_pkn); pthread_mutex_lock(&conn->queuelock); TAILQ_INSERT_TAIL(&conn->orderhead, node, next); pthread_mutex_unlock(&conn->queuelock); return 0; } { pthread_mutex_lock(&conn->queuelock); TAILQ_INSERT_TAIL(&conn->queuehead, node, next); pthread_mutex_unlock(&conn->queuelock); } pthread_mutex_lock(&conn->queuesem); pthread_cond_signal(&conn->queuecond); pthread_mutex_unlock(&conn->queuesem); return 0; } struct packet * chaos_connection_dequeue(struct connection *conn) { struct packet *packet; struct packet_queue *node; #if 0 unsigned short nextpacket; #endif packet = NOPKT; node = 0; #if 0 nextpacket = conn->cn_rlast + 1; #endif if (conn->cn_state == CSCLOSED) return NOPKT; for (;;) { pthread_mutex_lock(&conn->queuelock); if (TAILQ_EMPTY(&conn->orderhead) == false) { #if 0 struct packet_queue *prev; for (node = conn->orderhead; node; prev = node, node = node->next) if (node->packet->pk_pkn == nextpacket) { if (prev == 0) conn->orderhead = node->next; else prev->next = node->next; if (conn->ordertail == node) conn->ordertail = prev; packet = node->packet; break; } #endif #if 0 TAILQ_FOREACH(node, &conn->orderhead, next) { if (node->packet->pk_pkn == nextpacket) { DEBUG(TRACE_CHAOS, "chaos: dequeued out-of-order packet %d\n", nextpacket); /* * ---!! magic */ break; } } #else DEBUG(TRACE_CHAOS, "chaos: ordered queue empty\n"); #endif } if (node == 0 && TAILQ_EMPTY(&conn->queuehead) == false) { node = TAILQ_FIRST(&conn->queuehead); packet = node->packet; TAILQ_REMOVE(&conn->queuehead, node, next); } pthread_mutex_unlock(&conn->queuelock); if (node) { free(node); node = 0; } if (packet) { if (cmp_gt(packet->pk_pkn, conn->cn_rlast)) conn->cn_rlast = packet->pk_pkn; conn->cn_tacked = packet->pk_ackn; if (3 * (short) (conn->cn_rlast - conn->cn_racked) > conn->cn_rwsize) { struct packet *status; status = chaos_allocate_packet(conn, STSOP, 2 * sizeof(unsigned short)); conn->cn_racked = conn->cn_rlast; *(unsigned short *) &status->pk_cdata[0] = conn->cn_rlast; *(unsigned short *) &status->pk_cdata[2] = conn->cn_rwsize; chaos_queue(status); } return packet; } pthread_mutex_lock(&conn->queuesem); pthread_cond_wait(&conn->queuecond, &conn->queuesem); pthread_mutex_unlock(&conn->queuesem); if (conn->cn_state == CSCLOSED) return NOPKT; } } static int chaos_queue_time_pkt(unsigned short saddr, unsigned short sidx) { time_t t; struct timeval time; struct packet *answer; INFO(TRACE_CHAOS, "chaos: RFC (TIME): answering...\n"); gettimeofday(&time, NULL); t = time.tv_sec; t += 60UL * 60 * 24 * ((1970 - 1900) * 365L + 1970 / 4 - 1900 / 4); answer = pkalloc(sizeof(long), 0); answer->pk_op = ANSOP; SET_PH_LEN(answer->pk_phead, sizeof(long)); SET_CH_ADDR(answer->pk_daddr, saddr); SET_CH_INDEX(answer->pk_didx, sidx); SET_CH_ADDR(answer->pk_saddr, chaos_addr(ucfg.chaos_servername, 0)); SET_CH_INDEX(answer->pk_sidx, 0); answer->pk_pkn = 0; answer->pk_ackn = 0; *(long *) &answer->pk_cdata[0] = t; chaos_queue(answer); return 0; } static int chaos_queue_uptime_pkt(unsigned short saddr, unsigned short sidx) { struct packet *answer; INFO(TRACE_CHAOS, "chaos: RFC (UPTIME): answering...\n"); answer = pkalloc(sizeof(long), 0); answer->pk_op = ANSOP; SET_PH_LEN(answer->pk_phead, sizeof(long)); SET_CH_ADDR(answer->pk_daddr, saddr); SET_CH_INDEX(answer->pk_didx, sidx); SET_CH_ADDR(answer->pk_saddr, chaos_addr(ucfg.chaos_servername, 0)); SET_CH_INDEX(answer->pk_sidx, 0); answer->pk_pkn = 0; answer->pk_ackn = 0; *(long *) &answer->pk_cdata[0] = 0; chaos_queue(answer); return 0; } static int chaos_queue_status_pkt(unsigned short saddr, unsigned short sidx) { struct packet *answer; struct status *status; INFO(TRACE_CHAOS, "chaos: RFC (STATUS): answering...\n"); answer = pkalloc(CHSTATNAME + 2 * 2, 0); /* Only room for name and subnet+nwords */ answer->pk_op = ANSOP; SET_PH_LEN(answer->pk_phead, CHSTATNAME + 2 * 2); SET_CH_ADDR(answer->pk_daddr, saddr); SET_CH_INDEX(answer->pk_didx, sidx); SET_CH_ADDR(answer->pk_saddr, chaos_addr(ucfg.chaos_servername, 0)); SET_CH_INDEX(answer->pk_sidx, 0); answer->pk_pkn = 0; answer->pk_ackn = 0; status = (struct status *) &answer->pk_cdata[0]; memset(status, 0, sizeof(struct status)); strncpy(status->sb_name, ucfg.chaos_servername, CHSTATNAME); status->sb_name[CHSTATNAME - 1] = '\0'; /* Only put in the subnet (with the 400 marker) */ status->sb_data->sb_ident = (chaos_addr(ucfg.chaos_servername, 0) >> 8) | 0400; /* and say no data is following */ status->sb_data->sb_nshorts = 0 * sizeof(int) / sizeof(short); chaos_queue(answer); return 0; } static int chaos_queue_file_pkt(struct packet *packet) { void processdata(struct connection *conn); struct packet *answer; struct connection *conn; packet->pk_cdata[PH_LEN(packet->pk_phead)] = '\0'; INFO(TRACE_CHAOS, "chaos: RFC (%s): answering...\n", &packet->pk_cdata); conn = allconn(); conn->cn_faddr = packet->pk_saddr; conn->cn_fidx = packet->pk_sidx; answer = chaos_allocate_packet(conn, OPNOP, 2 * sizeof(unsigned short)); answer->pk_ackn = packet->pk_pkn; conn->cn_racked = packet->pk_pkn; *(unsigned short *) &answer->pk_cdata[0] = packet->pk_pkn; /* Last packed received. */ *(unsigned short *) &answer->pk_cdata[2] = conn->cn_rwsize; chaos_connection_queue(conn, answer); conn->cn_state = CSLISTEN; processdata(conn); return 0; } static int chaos_queue_mini_pkt(struct packet *packet) { void processmini(struct connection *conn); struct packet *answer; struct connection *conn; packet->pk_cdata[PH_LEN(packet->pk_phead)] = '\0'; INFO(TRACE_CHAOS, "chaos: RFC (%s): answering...\n", &packet->pk_cdata); conn = allconn(); conn->cn_faddr = packet->pk_saddr; conn->cn_fidx = packet->pk_sidx; answer = chaos_allocate_packet(conn, OPNOP, 2 * sizeof(unsigned short)); answer->pk_ackn = packet->pk_pkn; conn->cn_racked = packet->pk_pkn; *(unsigned short *) &answer->pk_cdata[0] = packet->pk_pkn; /* Last packed received. */ *(unsigned short *) &answer->pk_cdata[2] = conn->cn_rwsize; chaos_connection_queue(conn, answer); conn->cn_state = CSLISTEN; conn->cn_rlast = 1; processmini(conn); return 0; } static int chaos_send_to_local(char *buffer, int size) { /* ---!!! rcvpkt ? */ struct packet *packet; struct connection *conn; packet = (struct packet *) buffer; /* * Local loopback. */ if (uch11_csr & CHAOS_CSR_LOOP_BACK) { DEBUG(TRACE_CHAOS, "chaos: loopback %d bytes\n", size); memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); return 0; } DEBUG(TRACE_CHAOS, "chaos: transmitting packet (dest_addr = %o, uch11_myaddr=%o, size %d, wcount %d, op: %o)\n", CH_ADDR_SHORT(packet->pk_daddr), uch11_myaddr, size, (size + 1) / 2, packet->pk_op); /* * Receive packets addressed to ourselves. */ if (CH_ADDR_SHORT(packet->pk_daddr) == uch11_myaddr) { memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); } /* * Ignore packets anything that isn't addressed to us. */ if (CH_ADDR_SHORT(packet->pk_daddr) != chaos_addr(ucfg.chaos_servername, 0)) { DEBUG(TRACE_CHAOS, "chaos: ignoring packet not to us (%o): %o\n", chaos_addr(ucfg.chaos_servername, 0), CH_ADDR_SHORT(packet->pk_daddr)); return 0; } conn = chaos_find_connection(CH_INDEX_SHORT(packet->pk_didx)); /* * ---!!! Large parts of this is in rcvrfc. */ if (packet->pk_op == RFCOP) { DEBUG(TRACE_CHAOS, "chaos: got RFC packet\n"); if (conn && conn->cn_state == CSLISTEN) { DEBUG(TRACE_CHAOS, "chaos: duplicate RFC\n"); return 0; } if (conn) { struct packet *pkt; pkt = pkalloc((size_t) size, 0); memcpy(pkt, packet, size); chaos_connection_queue(conn, pkt); return 0; } if (memcmp(&packet->pk_cdata, "STATUS", 6) == 0) return chaos_queue_status_pkt(CH_ADDR_SHORT(packet->pk_saddr), CH_INDEX_SHORT(packet->pk_sidx)); else if (memcmp(&packet->pk_cdata, "TIME", 4) == 0) return chaos_queue_time_pkt(CH_ADDR_SHORT(packet->pk_saddr), CH_INDEX_SHORT(packet->pk_sidx)); else if (memcmp(&packet->pk_cdata, "UPTIME", 6) == 0) return chaos_queue_uptime_pkt(CH_ADDR_SHORT(packet->pk_saddr), CH_INDEX_SHORT(packet->pk_sidx)); else if (memcmp(&packet->pk_cdata, "FILE", 4) == 0) return chaos_queue_file_pkt(packet); else if (memcmp(&packet->pk_cdata, "MINI", 4) == 0) return chaos_queue_mini_pkt(packet); else { char buffer[512]; strncpy(buffer, (char *) packet->pk_cdata, PH_LEN(packet->pk_phead)); buffer[PH_LEN(packet->pk_phead)] = '\0'; WARNING(TRACE_CHAOS, "chaos: RFC (%s): protocol not implemented\n", buffer); return 0; } } if (packet->pk_op == SNSOP && conn) { struct packet *sts; sts = pkalloc(2 * sizeof(unsigned short) + 3 * sizeof(unsigned short), 1); sts->pk_op = STSOP; SET_PH_LEN(sts->pk_phead, 2 * sizeof(unsigned short)); sts->pk_daddr = packet->pk_saddr; sts->pk_didx = packet->pk_sidx; conn->cn_faddr = packet->pk_saddr; conn->cn_fidx = packet->pk_sidx; sts->pk_saddr = conn->cn_laddr; sts->pk_sidx = conn->cn_lidx; sts->pk_pkn = packet->pk_pkn; if (cmp_gt(packet->pk_ackn, conn->cn_tacked)) conn->cn_tacked = packet->pk_ackn; sts->pk_ackn = conn->cn_racked; *(unsigned short *) &sts->pk_cdata[0] = conn->cn_racked; *(unsigned short *) &sts->pk_cdata[2] = conn->cn_rwsize; chaos_connection_queue(conn, sts); return 0; } if (packet->pk_op == STSOP) { if (conn) { if (cmp_gt(packet->pk_ackn, conn->cn_tacked)) conn->cn_tacked = packet->pk_ackn; if (conn->lastpacket && conn->cn_tacked == conn->lastpacket->pk_pkn) { free(conn->lastpacket); conn->lastpacket = 0; } conn->cn_state = CSOPEN; conn->cn_twsize = *(unsigned short *) &packet->pk_cdata[2]; DEBUG(TRACE_CHAOS, "chaos: STSOP: twsize = %d\n", conn->cn_twsize); pthread_mutex_lock(&conn->twsem); pthread_cond_signal(&conn->twcond); pthread_mutex_unlock(&conn->twsem); } return 0; } if (conn && packet->pk_op == CLSOP) { /* clsconn ? */ DEBUG(TRACE_CHAOS, "chaos: CLSOP: got close \n"); conn->cn_state = CSCLOSED; pthread_mutex_lock(&conn->queuesem); pthread_cond_signal(&conn->queuecond); pthread_mutex_unlock(&conn->queuesem); usleep(100000); /* wait for queue to wake up */ rlsconn(conn); return 0; } if (conn && (cmp_gt(conn->cn_rlast, packet->pk_pkn) || (conn->cn_rlast == packet->pk_pkn))) { struct packet *status; DEBUG(TRACE_CHAOS, "chaos: duplicate data packet\n"); status = chaos_allocate_packet(conn, STSOP, 2 * sizeof(unsigned short)); status->pk_pkn = packet->pk_pkn; conn->cn_racked = conn->cn_rlast; *(unsigned short *) &status->pk_cdata[0] = conn->cn_rlast; *(unsigned short *) &status->pk_cdata[2] = conn->cn_rwsize; chaos_connection_queue(conn, status); return 0; } if (conn) { struct packet *pkt; pkt = pkalloc((size_t) size, 0); memcpy(pkt, packet, size); chaos_connection_queue(conn, pkt); } return 0; } static int chaos_poll_local(void) { struct packet *packet; struct packet_queue *node; int size; /* * Is RX buffer full? */ if (!uch11_rcv_buffer_empty && (uch11_csr & CHAOS_CSR_RECEIVE_DONE)) { DEBUG(TRACE_CHAOS, "chaos: polling, but unread data exists\n"); return 0; } if (!uch11_rcv_buffer_empty) { DEBUG(TRACE_CHAOS, "chaos: polling, but buffer not empty\n"); return 0; } if (TAILQ_EMPTY(&queuehead) == true) { return 0; } if (!(uch11_csr & CHAOS_CSR_RECEIVE_ENABLE)) { DEBUG(TRACE_CHAOS, "chaos: polling but rx not enabled\n"); return 0; } pthread_mutex_lock(&recvqueue); node = TAILQ_FIRST(&queuehead); packet = node->packet; TAILQ_REMOVE(&queuehead, node, next); free(node); pthread_mutex_unlock(&recvqueue); size = ((PH_LEN(packet->pk_phead) & 0x0fff) + CHHEADSIZE + 1) / 2; /* * Ignore any packets not to us. */ if (CH_ADDR_SHORT(packet->pk_daddr) != uch11_myaddr) return 0; memcpy(uch11_rcv_buffer, packet, (size_t) size * sizeof(unsigned short)); /* * Hardware header. */ uch11_rcv_buffer[size] = CH_ADDR_SHORT(packet->pk_daddr); uch11_rcv_buffer[size + 1] = CH_ADDR_SHORT(packet->pk_saddr); uch11_rcv_buffer[size + 2] = 0; size += 3; uch11_rcv_buffer_size = size; uch11_rcv_buffer_empty = false; DEBUG(TRACE_CHAOS, "chaos: polling: got chaos packet of %d bytes\n", uch11_rcv_buffer_size * 2); #if 0 dumpbuffer(uch11_rcv_buffer, size * 2); #endif uch11_rx_pkt(); return 0; } void uch11_poll(void) { if (uch11_backend == UCH11_BACKEND_LOCAL) chaos_poll_local(); else if (uch11_backend == UCH11_BACKEND_DAEMON) chaos_poll_chaosd(); else if (uch11_backend == UCH11_BACKEND_UDP) chaos_poll_udp(); } static void uch11_force_reconect(void) { if (uch11_backend == UCH11_BACKEND_DAEMON) { WARNING(TRACE_CHAOS, "chaos: forcing reconnect to chaosd\n"); close(chaosd_fd); chaosd_fd = -1; reconnect_chaos = true; } } void uch11_reconnect(void) { static int reconnect_time; if (++reconnect_delay < 200) return; reconnect_delay = 0; if (reconnect_time && time(NULL) < (reconnect_time + 5)) /* Try every 5 seconds. */ return; reconnect_time = time(NULL); NOTICE(TRACE_CHAOS, "chaos: reconnecting to chaosd\n"); if (uch11_init() == 0) { INFO(TRACE_CHAOS, "chaos: chaosd reconnected\n"); reconnect_chaos = false; reconnect_delay = 0; } } int uch11_init(void) { char *root_directory; char *hosts_file; hosts_file = realpath(ucfg.chaos_hosts, NULL); if (hosts_file == NULL) { hosts_file = "hosts.text"; WARNING(TRACE_USIM, "chaos: no host table; using defaults\n"); } NOTICE(TRACE_USIM, "chaos: using hosts table from \"%s\"\n", hosts_file); readhosts(ucfg.chaos_myname, hosts_file); uch11_myaddr = chaos_addr(ucfg.chaos_myname, 0); uch11_serveraddr = chaos_addr(ucfg.chaos_servername, 0); NOTICE(TRACE_USIM, "chaos: I am %s (0%o)\n", ucfg.chaos_myname, uch11_myaddr); if (uch11_backend == UCH11_BACKEND_LOCAL) { NOTICE(TRACE_USIM, "chaos: backend is \"local\", connecting to %s (0%o)\n", ucfg.chaos_servername, uch11_serveraddr); TAILQ_INIT(&queuehead); uch11_send = &chaos_send_to_local; pthread_mutex_init(&recvqueue, NULL); } else if (uch11_backend == UCH11_BACKEND_DAEMON) { NOTICE(TRACE_USIM, "chaos: backend is \"chaosd\"\n"); uch11_send = &chaos_send_to_chaosd; chaosd_fd = chdopen(); if (chaosd_fd == -1) { close(chaosd_fd); return -1; } } else if (uch11_backend == UCH11_BACKEND_UDP) { if (hybrid_udp_and_local) NOTICE(TRACE_USIM, "chaos: backend is \"udp\" with server %s (%#o)\n", ucfg.chaos_servername, uch11_serveraddr); else NOTICE(TRACE_USIM, "chaos: backend is \"udp\"\n"); uch11_send = &chaos_send_to_udp; chaosd_fd = chudpopen(); if (chaosd_fd == -1) { close(chaosd_fd); return -1; } } uch11_rcv_buffer_empty = true; char *whichconf, *whichdir; if (ucfg.usim_sys_directory != NULL) { // Backwards compat whichconf = "sys_directory"; whichdir = "/tree"; root_directory = realpath(ucfg.usim_sys_directory, NULL); if (root_directory != NULL) settreeroot(root_directory, whichdir); } else { whichconf = "fs_root_directory"; whichdir = "/"; root_directory = realpath(ucfg.usim_fs_root_directory, NULL); if (root_directory != NULL) settreeroot(root_directory, NULL); } if (root_directory == NULL) err(1, "could not resolve %s", whichconf); NOTICE(TRACE_USIM, "chaos: mapping %s to %s\n", whichdir, root_directory); return 0; } /* Chaos over UDP */ int hybrid_udp_and_local = 0; static int chudpopen(void) { int sock, lport, res, udp_dport, braddr; struct sockaddr_in sin; struct addrinfo *he, hi; struct in_addr udp_dest; if (hybrid_udp_and_local) { if (uch11_serveraddr == 0) { ERR(TRACE_CHAOS, "You configured udp_local_hybrid but there is no server address! Disabling hybrid.\n"); hybrid_udp_and_local = 0; } else { /* Do the local init too */ TAILQ_INIT(&queuehead); pthread_mutex_init(&recvqueue, NULL); } } /* Parse the local port given */ if ((ucfg.chaos_bridgeport_local == NULL) || (ucfg.chaos_bridgeport_local[0] == '\0') || ((lport = atoi(ucfg.chaos_bridgeport_local)) <= 0) || (lport >= (1 << 16))) { /* Or default to 42042? */ err(1, "bad bridgeport_local"); } /* Parse the bridge port given */ if ((ucfg.chaos_bridgeport == NULL) || (ucfg.chaos_bridgeport[0] == '\0') || ((udp_dport = atoi(ucfg.chaos_bridgeport)) <= 0) || (udp_dport >= (1 << 16))) { /* Or default to 42042? */ err(1, "bad bridgeport"); } /* Parse the bridge address given */ if ((ucfg.chaos_bridgeip != NULL) && (ucfg.chaos_bridgeip[0] != '\0')) { /* @@@@ allow IPv6 */ /* Check if it is an explicit IPv4 address */ if (inet_aton(ucfg.chaos_bridgeip, &udp_dest) == 0) { /* Else try to parse a host name */ memset(&hi, 0, sizeof(hi)); hi.ai_family = AF_INET; hi.ai_flags = AI_ADDRCONFIG; if ((res = getaddrinfo(ucfg.chaos_bridgeip, NULL, &hi, &he)) == 0) { struct sockaddr_in *s = (struct sockaddr_in *) he->ai_addr; memcpy(&udp_dest.s_addr, (u_char *) & s->sin_addr, 4); } else { err(1, "bad bridgeip"); } } } else { err(1, "no bridge?"); } /* Parse bridge Chaos address */ if ((ucfg.chaos_bridgechaos == NULL) || (ucfg.chaos_bridgechaos[0] == '\0') | (sscanf(ucfg.chaos_bridgechaos, "%o", &braddr) != 1) || /* Check it's a valid address */ (braddr == 0) || (braddr >= (1 << 16)) || ((braddr & 0xff) == 0) || (((braddr >> 8) & 0xff) == 0)) { err(1, "bad bridgechaos"); } else { udp_bridge_chaddr = (braddr & 0xffff); } NOTICE(TRACE_USIM, "chaos: chudp init, bridge is %#o at %s:%d, local port %d, hybrid %s\n", udp_bridge_chaddr, inet_ntoa(udp_dest), udp_dport, lport, hybrid_udp_and_local ? "true" : "false"); /* Now create a socket, and bind it to our local port */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket failed"); exit(1); } sin.sin_family = AF_INET; sin.sin_port = htons(lport); sin.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("udp bind failed"); exit(1); } /* and then connect it to the remote address - since we're only using one and the same */ sin.sin_port = htons(udp_dport); memcpy(&sin.sin_addr.s_addr, &udp_dest.s_addr, 4); if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("udp connect failed"); exit(1); } return sock; } /* from chudp.h and cbridge-chaos.h */ // Max pkt size (12 bits) plus header // The limit of 488 bytes is from MIT AIM 628, although more would fit any modern pkt (and 12 bits would give 4096 as max). // This is due to original Chaos hardware pkts limited to 4032 bits, of which 16 bytes are header. #define CH_PK_MAX_DATALEN 488 /* Protocol version */ #define CHUDP_VERSION 1 /* Protocol function codes */ #define CHUDP_PKT 1 /* Chaosnet packet */ struct chudp_header { char chudp_version; char chudp_function; char chudp_arg1; char chudp_arg2; }; struct chaos_hw_trailer { unsigned short ch_hw_destaddr:16; unsigned short ch_hw_srcaddr:16; unsigned short ch_hw_checksum:16; }; /* Max: CHUDP header, Chaos header, Chaos data, trailer */ #define CHUDP_MAXLEN (sizeof(struct chudp_header)+sizeof(struct pkt_header)+CH_PK_MAX_DATALEN+sizeof(struct chaos_hw_trailer)) static u_char trans_chudpbuf[CHUDP_MAXLEN]; /* So sorry about this. */ void ntohs_buf(u_short *ibuf, u_short *obuf, int len) { int i; for (i = 0; i < len; i += 2) *obuf++ = ntohs(*ibuf++); } static int chaos_send_to_udp(char *buffer, int size) { int wcount, dest_addr; /* * Local loopback. */ if (uch11_csr & CHAOS_CSR_LOOP_BACK) { DEBUG(TRACE_CHAOS, "chaos: loopback %d bytes\n", size); memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); return 0; } if (hybrid_udp_and_local) { /* Check if it is for our "local server" */ struct packet *packet; packet = (struct packet *) buffer; if (CH_ADDR_SHORT(packet->pk_daddr) == chaos_addr(ucfg.chaos_servername, 0)) { return chaos_send_to_local(buffer, size); } } wcount = (size + 1) / 2; dest_addr = ((unsigned short *) buffer)[wcount - 3]; DEBUG(TRACE_CHAOS, "chaos: sending packet to udp (dest_addr=%o, uch11_myaddr=%o, size %d, wcount %d)\n", dest_addr, uch11_myaddr, size, wcount); if (size > (int) CHUDP_MAXLEN) { ERR(TRACE_CHAOS, "chaos: packet too long: %d", size); return -1; } /* * Receive packets addressed to us, or broadcasts. */ if ((dest_addr == uch11_myaddr) || (dest_addr == 0)) { memcpy(uch11_rcv_buffer, buffer, size); uch11_rcv_buffer_size = (size + 1) / 2; uch11_rcv_buffer_empty = false; uch11_rx_pkt(); if (dest_addr != 0) /* Broadcasts should be sent also to other */ return 0; } if (chaosd_fd == -1) { ERR(TRACE_CHAOS, "chaos: transmit but chaosd_fd not open!\n"); return 0; } { struct chudp_header *hp = (struct chudp_header *) &trans_chudpbuf; u_char *op = trans_chudpbuf + sizeof(struct chudp_header); int nb; memset(trans_chudpbuf, 0, sizeof(trans_chudpbuf)); /* Set up CHUDP header */ hp->chudp_version = CHUDP_VERSION; hp->chudp_function = CHUDP_PKT; memcpy(op, buffer, size); /* Update the hw trailer dest (and checksum) since what is there is probably the ultimate dest, * but it should be just the next hop */ struct pkt_header *ph = (struct pkt_header *) ((char *) op); u_short pklen = LENFC_LEN(ph->ph_lenfc); u_short offs = sizeof(struct pkt_header) + pklen; if (offs % 2) offs++; struct chaos_hw_trailer *tp = (struct chaos_hw_trailer *) (op + offs); u_short hwdest = tp->ch_hw_destaddr; u_short cksum = tp->ch_hw_checksum; if (hwdest != udp_bridge_chaddr) { INFO(TRACE_CHAOS, "chaos: hw trailer dest is %#o should be %#o\n", hwdest, udp_bridge_chaddr); tp->ch_hw_destaddr = udp_bridge_chaddr; } tp->ch_hw_checksum = htons(uch11_checksum(op, size - 2)); /* Now swap it */ ntohs_buf((u_short *) op, (u_short *) op, size); INFO(TRACE_CHAOS, "chaos: sending %d bytes (pkt size %d)\n", size + sizeof(struct chudp_header), size); if ((nb = send(chaosd_fd, (char *) hp, size + sizeof(struct chudp_header), 0)) < 0) { perror("chaos: send to udp"); ERR(TRACE_CHAOS, "chaos: send to udp failed"); return -1; } else if (nb != size + sizeof(struct chudp_header)) { ERR(TRACE_CHAOS, "chaos: could not send the full pkt: %d sent, expected %d", nb, size + sizeof(struct chudp_header)); return -1; } } return 0; } static int chaos_poll_udp(void) { /* basically copy chaos_poll_chaosd but skip CHUDP header and swap */ ssize_t ret; struct pollfd pfd[1]; int nfds, timeout; unsigned char lenbytes[4]; ssize_t len; int dest_addr; if (hybrid_udp_and_local) { /* Prioritize local traffic */ chaos_poll_local(); } if (chaosd_fd == -1) { return 0; } timeout = 0; nfds = 1; pfd[0].fd = chaosd_fd; pfd[0].events = POLLIN; pfd[0].revents = 0; ret = poll(pfd, nfds, timeout); if (ret == -1) { ERR(TRACE_CHAOS, "chaos: polling udp: nothing there (RDN=%o)\n", uch11_csr & CHAOS_CSR_RECEIVE_DONE); return -1; } else if (ret == 0) { // this happens all the time so don't // ERR(TRACE_CHAOS, "chaos: udp poll timeout\n"); return -1; } /* * Is RX buffer full? */ if (!uch11_rcv_buffer_empty && (uch11_csr & CHAOS_CSR_RECEIVE_DONE)) { /* * Toss packets arriving when buffer is already in * use, they will be resent. */ ERR(TRACE_CHAOS, "chaos: polling udp: unread data, drop (RDN=%o, lost %d)\n", uch11_csr & CHAOS_CSR_RECEIVE_DONE, uch11_lost_count); uch11_lost_count++; /* Toss it by reading it */ ret = recv(chaosd_fd, (char *) uch11_rcv_buffer_toss, sizeof(uch11_rcv_buffer_toss), 0); DEBUG(TRACE_CHAOS, "chaos: tossing udp packet of %d bytes\n", ret); return -1; } ret = recv(chaosd_fd, (char *) uch11_rcv_buffer, sizeof(uch11_rcv_buffer), 0); if (ret == -1) { perror("chaos: udp read"); return -1; } else if (ret == 0) { ERR(TRACE_CHAOS, "chaos: udp read zero bytes\n"); return -1; } else if (ret > (int) CHUDP_MAXLEN) { ERR(TRACE_CHAOS, "chaos: udp read too many bytes: %d\n", ret); return -1; } INFO(TRACE_CHAOS, "chaos: polling; got udp packet len %d\n", ret); struct chudp_header *hp = (struct chudp_header *) uch11_rcv_buffer; if (hp->chudp_version != CHUDP_VERSION) { ERR(TRACE_CHAOS, "chaos: chudp version is wrong: %d\n", hp->chudp_version); return -1; } if (hp->chudp_function != CHUDP_PKT) { ERR(TRACE_CHAOS, "chaos: chudp function is wrong: %d\n", hp->chudp_function); return -1; } /* zap chudp header */ memmove((char *) uch11_rcv_buffer, ((char *) uch11_rcv_buffer) + sizeof(struct chudp_header), ret - sizeof(struct chudp_header)); ret -= sizeof(struct chudp_header); /* and swap it */ ntohs_buf(uch11_rcv_buffer, uch11_rcv_buffer, ret); uch11_rcv_buffer_size = (ret + 1) / 2; uch11_rcv_buffer_empty = false; dest_addr = uch11_rcv_buffer[uch11_rcv_buffer_size - 3]; /* * If not to us (or broadcast), ignore. */ if ((dest_addr != uch11_myaddr) && (dest_addr != 0)) { INFO(TRACE_CHAOS, "chaos: ignoring packet not to us (%o): %o\n", uch11_myaddr, dest_addr); uch11_rcv_buffer_size = 0; uch11_rcv_buffer_empty = true; /* * Should uch11_rx_pkt be called here? */ return 0; } INFO(TRACE_CHAOS, "chaos: udp receiving packet: from %o, my %o\n", dest_addr, uch11_myaddr); uch11_rx_pkt(); return 0; }