/* Terminal Mixer - multi-point multi-user access to terminal applications Copyright (C) 2007 LluĂ­s Batlle i Rossell Please find the license in the provided COPYING file. */ #include #include #include #include #include #include "eth_linux.h" #include "main.h" enum { MAXPACKET = 1500, MAXSEQ = 100, HEAD = 9 }; static struct { int socket; char partner[6]; int partner_set; int send_acked; int send_retries_left; unsigned int seq_send; unsigned int seq_wait; unsigned int wrong_recv; char send_buffer[MAXPACKET]; int send_buffer_size; } edata; enum Control { SEND, ACK, INIT }; static char eth_buffer[MAXPACKET]; static void eth_fill_mac(unsigned char *mac, const char *str); static int make_head(unsigned char *data, unsigned int seq, enum Control c, int size) { *((unsigned int *) data) = htonl(seq); data[4] = (unsigned char) c; *((unsigned int *)(data+5)) = htonl(size); return HEAD; } static int parse_head(unsigned char *data, unsigned int *seq, enum Control *c, int *size) { *seq = ntohl( *((unsigned int*) data) ); *c = data[4]; if (size) *size = ntohl(*((unsigned int *)(data+5))); return HEAD; } int eth_proto_max_send() { return MAXPACKET - HEAD; } void eth_proto_init() { edata.socket = -1; edata.seq_send = 0; edata.seq_wait = 0; edata.send_acked = 1; /* Fine at the beginning, as if the last data was acked */ edata.partner_set = 0; } int eth_proto_allow_sending() { return edata.send_acked; } static int seq_next(int val) { if (val >= 0 && val < MAXSEQ) val = val + 1; else val = 0; return val; } static int seq_before(int val) { if (val > 0 && val < MAXSEQ) val = val - 1; else val = MAXSEQ; return val; } int eth_proto_open() { edata.socket = eth_open(command_line.eth_device); if (edata.socket == -1) error("Cannot open device %s", command_line.eth_device); if ( !command_line.is_server) { if (command_line.c_param.server_address == 0) error("You must specify the MAC you want to connect to."); eth_fill_mac(edata.partner, command_line.c_param.server_address); edata.partner_set = 1; make_head(eth_buffer, edata.seq_send, INIT, 0); eth_send(command_line.eth_device, edata.partner, eth_buffer, HEAD); edata.seq_send = seq_next(edata.seq_send); } return edata.socket; } int eth_proto_recv(char *data, int size) { int res; int seq; int data_length; enum Control c; char partner[6]; do { res = eth_recv(eth_buffer, sizeof(eth_buffer), partner); edata.partner_set = 1; } while(res < HEAD); parse_head(eth_buffer, &seq, &c, &data_length); /* We admit any first connection */ if (seq == 0 && c == INIT) { edata.seq_wait = 1; /* next of the just receive 0 */ memcpy(edata.partner, partner, sizeof(edata.partner)); return -1; /* Nothing the parent should care about */ } if (c == SEND) { if (seq != edata.seq_wait && seq != seq_before(edata.seq_wait)) { dump_line("Wrong data packet seq received. Recvd: %i Expected: %i\n", seq, edata.seq_wait); edata.wrong_recv++; return -1; } if (seq == seq_before(edata.seq_wait)) dump_line("Repeated data seq received: %i\n", seq); if (seq == edata.seq_wait) { if (data_length == 0) { edata.partner_set = 0; edata.seq_wait = 0; edata.seq_send = 0; /* We should send ACK anyway */ } else { memcpy(data, eth_buffer + HEAD, data_length); edata.seq_wait = seq_next(edata.seq_wait); } } /* Ack the packed we received. In these conditions: * - We received the packed we expected * - We received a repeat of the old packet. The * ACK was lost probably, so we resend it */ make_head(eth_buffer, seq, ACK, 0); eth_send(command_line.eth_device, edata.partner, eth_buffer, HEAD); } else if (c == ACK) { if (seq == edata.seq_send) { edata.send_acked = 1; edata.seq_send = seq_next(edata.seq_send); unprogram_timeout(); } else { dump_line("Wrong ack received. Recvd: %i Expected: %i\n", seq, edata.seq_send); } return -1; /* not data */ } else return -1; return data_length; } static int eth_proto_link_send() { int sent; sent = eth_send(command_line.eth_device, edata.partner, edata.send_buffer, edata.send_buffer_size); if (sent >= 0) /* expected */ { edata.send_retries_left -= 1; edata.send_acked = 0; program_timeout(1); } else /* strange case, data not sent */ { sent = 0; edata.send_acked = 1; } return sent; } int eth_proto_send(const char *data, int size) { int sent; assert(edata.send_acked); if (!edata.partner_set) { if (edata.seq_send == 0 && !command_line.is_server && command_line.c_param.server_address != 0) { eth_fill_mac(edata.partner, command_line.c_param.server_address); edata.partner_set = 1; } else return 0; } edata.send_retries_left = 3; /* Prepare packet */ make_head(edata.send_buffer, edata.seq_send, SEND, size); memcpy(edata.send_buffer+HEAD, data, size); edata.send_buffer_size = size + HEAD; sent = eth_proto_link_send(); sent -= HEAD; return sent; } int eth_proto_process_timeouts() { if (!edata.send_acked && did_timeout_happen()) { unprogram_timeout(); if (edata.send_retries_left > 0) { dump_line("Retrying. Left:%i\n", edata.send_retries_left); eth_proto_link_send(); } else { /* The connection has been lost */ dump_line("Connection lost"); edata.send_acked = 1; edata.partner_set = 0; edata.seq_wait = 0; edata.seq_send = 0; return 0; /* FAIL */ } } return 1; /* OK */ } static void eth_fill_mac(unsigned char *mac, const char *str) { int res; int imac[6]; res = sscanf(str, "%x:%x:%x:%x:%x:%x", &imac[0], &imac[1], &imac[2], &imac[3], &imac[4], &imac[5]); if (res != 6) { error("Error parsing mac: %02x:%02x:%02x:%02x:%02x:%02x\n", imac[0], imac[1], imac[2], imac[3], imac[4], imac[5]); } mac[0] = imac[0]; mac[1] = imac[1]; mac[2] = imac[2]; mac[3] = imac[3]; mac[4] = imac[4]; mac[5] = imac[5]; }