/* uch11.c --- chaosnet interface
*/
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#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;
}