int csp_route_work(uint32_t timeout) { csp_qfifo_t input; csp_packet_t * packet; csp_conn_t * conn; csp_socket_t * socket; #ifdef CSP_USE_RDP /* Check connection timeouts (currently only for RDP) */ csp_conn_check_timeouts(); #endif /* Get next packet to route */ if (csp_qfifo_read(&input) != CSP_ERR_NONE) return -1; packet = input.packet; csp_log_packet("INP: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %"PRIu16" VIA: %s", packet->id.src, packet->id.dst, packet->id.dport, packet->id.sport, packet->id.pri, packet->id.flags, packet->length, input.interface->name); /* Here there be promiscuous mode */ #ifdef CSP_USE_PROMISC csp_promisc_add(packet); #endif #ifdef CSP_USE_DEDUP /* Check for duplicates */ if (csp_dedup_is_duplicate(packet)) { /* Discard packet */ csp_log_packet("Duplicate packet discarded"); csp_buffer_free(packet); return 0; } #endif /* If the message is not to me, route the message to the correct interface */ if ((packet->id.dst != csp_get_address()) && (packet->id.dst != CSP_BROADCAST_ADDR)) { /* Find the destination interface */ csp_iface_t * dstif = csp_rtable_find_iface(packet->id.dst); /* If the message resolves to the input interface, don't loop it back out */ if ((dstif == NULL) || ((dstif == input.interface) && (input.interface->split_horizon_off == 0))) { csp_buffer_free(packet); return 0; } /* Otherwise, actually send the message */ if (csp_send_direct(packet->id, packet, dstif, 0) != CSP_ERR_NONE) { csp_log_warn("Router failed to send"); csp_buffer_free(packet); } /* Next message, please */ return 0; } /* Discard packets with unsupported options */ if (csp_route_check_options(input.interface, packet) != CSP_ERR_NONE) { csp_buffer_free(packet); return 0; } /* The message is to me, search for incoming socket */ socket = csp_port_get_socket(packet->id.dport); /* If the socket is connection-less, deliver now */ if (socket && (socket->opts & CSP_SO_CONN_LESS)) { if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { csp_buffer_free(packet); return 0; } if (csp_queue_enqueue(socket->socket, &packet, 0) != CSP_QUEUE_OK) { csp_log_error("Conn-less socket queue full"); csp_buffer_free(packet); return 0; } return 0; } /* Search for an existing connection */ conn = csp_conn_find(packet->id.ext, CSP_ID_CONN_MASK); /* If this is an incoming packet on a new connection */ if (conn == NULL) { /* Reject packet if no matching socket is found */ if (!socket) { csp_buffer_free(packet); return 0; } /* Run security check on incoming packet */ if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { csp_buffer_free(packet); return 0; } /* New incoming connection accepted */ csp_id_t idout; idout.pri = packet->id.pri; idout.src = csp_get_address(); idout.dst = packet->id.src; idout.dport = packet->id.sport; idout.sport = packet->id.dport; idout.flags = packet->id.flags; /* Create connection */ conn = csp_conn_new(packet->id, idout); if (!conn) { csp_log_error("No more connections available"); csp_buffer_free(packet); return 0; } /* Store the socket queue and options */ conn->socket = socket->socket; conn->opts = socket->opts; /* Packet to existing connection */ } else { /* Run security check on incoming packet */ if (csp_route_security_check(conn->opts, input.interface, packet) < 0) { csp_buffer_free(packet); return 0; } } #ifdef CSP_USE_RDP /* Pass packet to RDP module */ if (packet->id.flags & CSP_FRDP) { csp_rdp_new_packet(conn, packet); return 0; } #endif /* Pass packet to UDP module */ csp_udp_new_packet(conn, packet); return 0; }
csp_conn_t * csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts) { /* Generate identifier */ csp_id_t incoming_id, outgoing_id; incoming_id.pri = prio; incoming_id.dst = my_address; incoming_id.src = dest; incoming_id.sport = dport; incoming_id.flags = 0; outgoing_id.pri = prio; outgoing_id.dst = dest; outgoing_id.src = my_address; outgoing_id.dport = dport; outgoing_id.flags = 0; /* Set connection options */ if (opts & CSP_O_RDP) { #ifdef CSP_USE_RDP incoming_id.flags |= CSP_FRDP; outgoing_id.flags |= CSP_FRDP; #else csp_log_error("Attempt to create RDP connection, but CSP was compiled without RDP support\r\n"); return NULL; #endif } if (opts & CSP_O_HMAC) { #ifdef CSP_USE_HMAC outgoing_id.flags |= CSP_FHMAC; incoming_id.flags |= CSP_FHMAC; #else csp_log_error("Attempt to create HMAC authenticated connection, but CSP was compiled without HMAC support\r\n"); return NULL; #endif } if (opts & CSP_O_XTEA) { #ifdef CSP_USE_XTEA outgoing_id.flags |= CSP_FXTEA; incoming_id.flags |= CSP_FXTEA; #else csp_log_error("Attempt to create XTEA encrypted connection, but CSP was compiled without XTEA support\r\n"); return NULL; #endif } if (opts & CSP_O_CRC32) { #ifdef CSP_USE_CRC32 outgoing_id.flags |= CSP_FCRC32; incoming_id.flags |= CSP_FCRC32; #else csp_log_error("Attempt to create CRC32 validated connection, but CSP was compiled without CRC32 support\r\n"); return NULL; #endif } /* Find an unused ephemeral port */ csp_conn_t * conn; /* Wait for sport lock */ if (csp_bin_sem_wait(&sport_lock, 1000) != CSP_SEMAPHORE_OK) return NULL; uint8_t start = sport; while (++sport != start) { if (sport > CSP_ID_PORT_MAX) sport = CSP_MAX_BIND_PORT + 1; outgoing_id.sport = sport; incoming_id.dport = sport; /* Match on destination port of _incoming_ identifier */ conn = csp_conn_find(incoming_id.ext, CSP_ID_DPORT_MASK); /* Break if we found an unused ephemeral port */ if (conn == NULL) break; } /* Post sport lock */ csp_bin_sem_post(&sport_lock); /* If no available ephemeral port was found */ if (sport == start) return NULL; /* Get storage for new connection */ conn = csp_conn_new(incoming_id, outgoing_id); if (conn == NULL) return NULL; /* Set connection options */ conn->opts = opts; #ifdef CSP_USE_RDP /* Call Transport Layer connect */ if (outgoing_id.flags & CSP_FRDP) { /* If the transport layer has failed to connect * deallocate connection structure again and return NULL */ if (csp_rdp_connect(conn, timeout) != CSP_ERR_NONE) { csp_close(conn); return NULL; } } #endif /* We have a successful connection */ return conn; }