/* * dev_nic.c - NIC (Network Controler) generic device API implementation. * * Author Alain Greiner (2016,2017,2018,2019,2020) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////////////////// // Extern global variables ///////////////////////////////////////////////////////////////////////////////////////// extern chdev_directory_t chdev_dir; // allocated in kernel_init.c //////////////////////////////////// void dev_nic_init( chdev_t * chdev ) { thread_t * new_thread; error_t error; // get "channel" & "is_rx" fields from chdev descriptor uint32_t channel = chdev->channel; bool_t is_rx = chdev->is_rx; // set chdev name if( is_rx ) snprintf( chdev->name , 16 , "nic%d_rx" , channel ); else snprintf( chdev->name , 16 , "nic%d_tx" , channel ); // initialize the root of the listening sockets list xlist_root_init( XPTR( local_cxy , &chdev->ext.nic.root ) ); // initialize the lock protecting this list remote_busylock_init( XPTR( local_cxy , &chdev->ext.nic.lock ), LOCK_LISTEN_SOCKET ); // call driver init function for this chdev hal_drivers_nic_init( chdev ); // select a core to execute the NIC server thread lid_t lid = cluster_select_local_core( local_cxy ); // bind the NIC IRQ to the selected core dev_pic_bind_irq( lid , chdev ); // build pointer on server function void * func = is_rx ? &dev_nic_rx_server : &dev_nic_tx_server; // create server thread error = thread_kernel_create( &new_thread, THREAD_DEV, func, chdev, lid ); if( error ) { printk("\n[PANIC] in %s : cannot create server thread\n", __FUNCTION__ ); return; } // set "server" field in chdev descriptor chdev->server = new_thread; // set "chdev" field in thread descriptor new_thread->chdev = chdev; // unblock server thread thread_unblock( XPTR( local_cxy , new_thread ) , THREAD_BLOCKED_GLOBAL ); #if (DEBUG_DEV_NIC_TX || DEBUG_DEV_NIC_RX) thread_t * this = CURRENT_THREAD; if( is_rx ) printk("\n[%s] thread[%x,%x] initialized NIC_RX[%d] / server %x\n", __FUNCTION__, this->process->pid, this->trdid, channel, new_thread->trdid ); else printk("\n[%s] thread[%x,%x] initialized NIC_TX[%d] / server %x\n", __FUNCTION__, this->process->pid, this->trdid, channel, new_thread->trdid ); #endif } // end dev_nic_init() /////////////////////////////////////////////////////////////////////////////////////////// // Functions directly called by the client threads /////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// uint32_t dev_nic_get_key( uint32_t addr, uint16_t port ) { thread_t * this = CURRENT_THREAD; xptr_t dev_xp = chdev_dir.nic_tx[0]; chdev_t * dev_ptr = GET_PTR( dev_xp ); if( dev_xp == XPTR_NULL ) return -1; // set command arguments in client thread descriptor this->nic_cmd.buffer = (uint8_t *)addr; this->nic_cmd.length = (uint32_t)port; this->nic_cmd.dev_xp = dev_xp; this->nic_cmd.type = NIC_CMD_GET_KEY; // call driver dev_ptr->cmd( XPTR( local_cxy , this ) ); // get "status" return this->nic_cmd.status; } ////////////////////////////////////////// error_t dev_nic_set_run( uint32_t channel, uint32_t run ) { thread_t * this = CURRENT_THREAD; if( channel >= LOCAL_CLUSTER->nb_nic_channels ) return -1; xptr_t dev_xp = chdev_dir.nic_tx[0]; chdev_t * dev_ptr = GET_PTR( dev_xp ); if( dev_xp == XPTR_NULL ) return -1; // set command arguments in client thread descriptor this->nic_cmd.dev_xp = dev_xp; this->nic_cmd.type = NIC_CMD_SET_RUN; this->nic_cmd.length = channel; this->nic_cmd.status = run; // call driver dev_ptr->cmd( XPTR( local_cxy , this ) ); // return "error" return this->nic_cmd.error; } ////////////////////////////////// error_t dev_nic_get_instru( void ) { thread_t * this = CURRENT_THREAD; xptr_t dev_xp = chdev_dir.nic_tx[0]; chdev_t * dev_ptr = GET_PTR( dev_xp ); if( dev_xp == XPTR_NULL ) return -1; // set command arguments in client thread descriptor this->nic_cmd.dev_xp = dev_xp; this->nic_cmd.type = NIC_CMD_GET_INSTRU; // call driver dev_ptr->cmd( XPTR( local_cxy , this ) ); // return "error" return this->nic_cmd.error; } //////////////////////////////////// error_t dev_nic_clear_instru( void ) { thread_t * this = CURRENT_THREAD; xptr_t dev_xp = chdev_dir.nic_tx[0]; chdev_t * dev_ptr = GET_PTR( dev_xp ); if( dev_xp == XPTR_NULL ) return -1; // set command arguments in client thread descriptor this->nic_cmd.dev_xp = dev_xp; this->nic_cmd.type = NIC_CMD_GET_INSTRU; // call driver dev_ptr->cmd( XPTR( local_cxy , this ) ); // return "error" return this->nic_cmd.error; } //////////////////////////////////////////////////////////////////////////////////////////// // Static functions called by the NIC_RX server & NIC_TX server threads //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// // This static function is used by the dev_nic_rx_handle_tcp() function // to check acceptability of a given sequence number. It returns true when // the argument is contained in a wrap-around window defined by the // and arguments. The window wrap-around when (min > max). //////////////////////////////////////////////////////////////////////////////////////////// // @ seq : [in] value to be checked. // @ min : [in] first base. // @ max : [in] window size. //////////////////////////////////////////////////////////////////////////////////////////// static inline bool_t is_in_window( uint32_t seq, uint32_t min, uint32_t max ) { if( max >= min ) // no wrap_around => only one window [min,max] { return( (seq >= min) && (seq <= max) ); } else // window wrap-around => two windows [min,0xFFFFFFFF] and [0,max] { return( (seq <= max) || (seq >= min) ); } } //////////////////////////////////////////////////////////////////////////////////////// // This static function computes the checksum for an IP packet header. // The "checksum" field itself is not taken into account for this computation. //////////////////////////////////////////////////////////////////////////////////////// // @ buffer : [in] pointer on IP packet header (20 bytes) // @ return the checksum value on 16 bits //////////////////////////////////////////////////////////////////////////////////////// static uint16_t dev_nic_ip_checksum( uint8_t * buffer ) { uint32_t i; uint32_t cs; // 32 bits accumulator uint16_t * buf; buf = (uint16_t *)buffer; // compute checksum for( i = 0 , cs = 0 ; i < 10 ; i++ ) { if( i != 5 ) cs += buf[i]; } // one's complement return ~cs; } //////////////////////////////////////////////////////////////////////////////////////// // This static function computes the checksum for an UDP packet defined by // the and arguments. //////////////////////////////////////////////////////////////////////////////////////// // @ buffer : [in] pointer on UDP packet base. // @ size : [in] number of bytes in this packet (including header). // @ return the checksum value on 16 bits //////////////////////////////////////////////////////////////////////////////////////// static uint16_t dev_nic_udp_checksum( uint8_t * buffer, uint32_t size ) { uint32_t i; uint32_t carry; uint32_t cs; // 32 bits accumulator uint16_t * buf; uint32_t max; // number of uint16_t in packet // compute max & buf buf = (uint16_t *)buffer; max = size >> 1; // extend buffer[] if required if( size & 1 ) { max++; buffer[size] = 0; } // compute checksum for UDP packet for( i = 0 , cs = 0 ; i < size ; i++ ) cs += buf[i]; // handle carry carry = (cs >> 16); if( carry ) { cs += carry; carry = (cs >> 16); if( carry ) cs += carry; } // one's complement return ~cs; } //////////////////////////////////////////////////////////////////////////////////////// // This static function computes the checksum for a TCP segment defined by the // and arguments. It includes the pseudo header defined by the , // , arguments, and by the TCP_PROTOCOL code. //////////////////////////////////////////////////////////////////////////////////////// // @ buffer : [in] pointer on TCP segment base. // @ tcp_length : [in] number of bytes in this TCP segment (including header). // @ src_ip_addr : [in] source IP address (pseudo header) // @ dst_ip_addr : [in] destination IP address (pseudo header) // @ return the checksum value on 16 bits //////////////////////////////////////////////////////////////////////////////////////// static uint16_t dev_nic_tcp_checksum( uint8_t * buffer, uint32_t tcp_length, uint32_t src_ip_addr, uint32_t dst_ip_addr ) { uint32_t i; uint32_t carry; uint32_t cs; // 32 bits accumulator uint16_t * buf; uint32_t max; // number of uint16_t in segment // compute max & buf buf = (uint16_t *)buffer; max = tcp_length >> 1; // extend buffer[] if required if( tcp_length & 1 ) { max++; buffer[tcp_length] = 0; } // compute checksum for TCP segment for( i = 0 , cs = 0 ; i < tcp_length ; i++ ) cs += buf[i]; // complete checksum for pseudo-header cs += src_ip_addr; cs += dst_ip_addr; cs += PROTOCOL_TCP; cs += tcp_length; // handle carry carry = (cs >> 16); if( carry ) { cs += carry; carry = (cs >> 16); if( carry ) cs += carry; } // one's complement return ~cs; } /////////////////////////////////////////////////////////////////////////////////////////// // This static function can be called by the NIC_TX or NIC_RX server threads to unblock // the TX client thread after completion (success or error) of a TX command registered // in a socket identified by the argument. The argument defines // the command success/failure status: a null value signals a success, a non-null value // signals a failure. For all commands, it copies the status value in the tx_sts field, // and print an error message on TXT0 in case of failure. /////////////////////////////////////////////////////////////////////////////////////////// // @ socket_xp : [in] extended pointer on socket // @ status : [in] command status (see above) /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_unblock_tx_client( xptr_t socket_xp, int32_t status ) { // get socket thread cluster and local pointer socket_t * socket_ptr = GET_PTR( socket_xp ); cxy_t socket_cxy = GET_CXY( socket_xp ); if( status != CMD_STS_SUCCESS ) { uint32_t sock_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state )); uint32_t cmd_type = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_cmd )); pid_t pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid )); fdid_t fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid )); printk("\n[WARNING] reported by %s : socket[%x,%d] / %s / cmd %s / status %s \n", __FUNCTION__, pid, fdid, socket_state_str(sock_state), socket_cmd_type_str(cmd_type), socket_cmd_sts_str(status) ); } // set tx_sts field in socket descriptor hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_sts ) , status ); // get extended point on TX client thread xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client )); // unblock the client thread thread_unblock( client_xp , THREAD_BLOCKED_IO ); } // end dev_nic_unblock_tx_client() /////////////////////////////////////////////////////////////////////////////////////////// // This static function can be called by the NIC_TX or NIC_RX server threads to unblock // the RX client thread after completion (success or error) of an RX command registered // in a socket identified by the argument. The argument defines // the command success/failure status: a null value signals a success, a non-null value // signals a failure. For all commands, it copies the status value in the rx_sts field, // and print an error message on TXT0 in case of failure. /////////////////////////////////////////////////////////////////////////////////////////// // @ socket_xp : [in] extended pointer on socket // @ status : [in] command status (see above) /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_unblock_rx_client( xptr_t socket_xp, int32_t status ) { // get socket thread cluster and local pointer socket_t * socket_ptr = GET_PTR( socket_xp ); cxy_t socket_cxy = GET_CXY( socket_xp ); if( status != CMD_STS_SUCCESS ) { uint32_t sock_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state )); uint32_t cmd_type = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->rx_cmd )); pid_t pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid )); fdid_t fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid )); printk("\n[WARNING] reported by %s : socket[%x,%d] / %s / cmd %s / status %s\n", __FUNCTION__, pid, fdid, socket_state_str(sock_state), socket_cmd_type_str(cmd_type), socket_cmd_sts_str(status) ); } // set rx_sts field in socket descriptor hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_sts ) , status ); // get extended point on RX client thread xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->rx_client )); // unblock the client thread thread_unblock( client_xp , THREAD_BLOCKED_IO ); } // end dev_nic_unblock_rx_client() /////////////////////////////////////////////////////////////////////////////////////////// // Functions called by the NIC_RX server thread /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_rx_server() function. // It analyses an Ethernet frame contained in the kernel buffer defined // by the argument, and returns in the argument the length // of the IP packet contained in the Ethernet packet payload. /////////////////////////////////////////////////////////////////////////////////////////// // @ buffer : [in] pointer on a received Ethernet packet // @ ip_length : [out] length of IP packet (in bytes). // @ return 0 if success / return -1 if illegal packet length. /////////////////////////////////////////////////////////////////////////////////////////// static error_t dev_nic_rx_check_eth( uint8_t * buffer, uint32_t * ip_length ) { uint32_t length = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13]; *ip_length = length; return 0; } /////////////////////////////////////////////////////////////////////////////////////////// // This static function analyses the IP packet contained in the kernel buffer // defined by the argument, and returns in the , , // and arguments the informations contained in the IP header. // It checks the IP packet length versus the value contained in Ethernet header. // It checks the IP header checksum. /////////////////////////////////////////////////////////////////////////////////////////// // @ buffer : [in] pointer on the IP packet. // @ expected_length : [in] expected IP packet length (from Ethernet header). // @ ip_src_addr : [out] source IP address. // @ ip_dst_addr : [out] destination IP address. // @ protocol : [out] transport protocol type. // @ return 0 if success / return -1 if illegal packet. /////////////////////////////////////////////////////////////////////////////////////////// static error_t dev_nic_rx_check_ip( uint8_t * buffer, uint32_t expected_length, uint32_t * ip_src_addr, uint32_t * ip_dst_addr, uint32_t * trsp_protocol ) { #if DEBUG_DEV_NIC_RX thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); #endif // get packet length uint32_t length = ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3]; // discard packet if eth_payload_length != ip_length if( length != expected_length ) { #if DEBUG_DEV_NIC_RX if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] failure : length (%d) != expected_length (%d)\n", __FUNCTION__, this->process->pid, this->trdid, length, expected_length ); #endif return -1; } // get transport protocol type uint8_t protocol = buffer[9]; // discard packet if unsupported protocol if( (protocol != PROTOCOL_TCP) && (protocol != PROTOCOL_UDP) ) { #if DEBUG_DEV_NIC_RX if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] failure : unsupported transport protocol (%d)\n", __FUNCTION__, this->process->pid, this->trdid, protocol ); #endif return -1; } // compute IP header checksum uint32_t computed_cs = (uint32_t)dev_nic_ip_checksum( buffer ); // extract IP header checksum uint32_t received_cs = ((uint32_t)buffer[10] << 8) | ((uint32_t)buffer[11]); // discard packet if bad checksum if( received_cs != computed_cs ) { #if DEBUG_DEV_NIC_RX if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] failure : computed checksum (%d) != received checksum (%d)\n", __FUNCTION__, this->process->pid, this->trdid, computed_cs, received_cs ); #endif return -1; } *ip_src_addr = ((uint32_t)buffer[12] << 24) | ((uint32_t)buffer[13] << 16) | ((uint32_t)buffer[14] << 8) | ((uint32_t)buffer[15] ) ; *ip_dst_addr = ((uint32_t)buffer[16] << 24) | ((uint32_t)buffer[17] << 16) | ((uint32_t)buffer[18] << 8) | ((uint32_t)buffer[19] ) ; *trsp_protocol = protocol; return 0; } /////////////////////////////////////////////////////////////////////////////////////////// // This static function analyses the UDP packet contained in the kernel buffer // defined by the and arguments. // It checks the UDP checksum, and discard corrupted packets. // It scans the list of sockets attached to the NIC_RX chdev to find a matching socket, // and discard the received packet if no UDP socket found. // Finally, it copies the payload to the socket "rx_buf", as long as the packet payload // is not larger than the rx_buf. // It set the "rx_valid" flip-flop, and unblock the client thread when the last expected // byte has been received. /////////////////////////////////////////////////////////////////////////////////////////// // @ chdev : [in] local pointer on local NIC_RX chdev descriptor. // @ k_buf : [in] pointer on the UDP packet in local kernel buffer. // @ k_length : [in] number of bytes in buffer (including UDP header). // @ pkt_src_addr : [in] source IP address (from IP packet header). // @ pkt_dst_addr : [in] destination IP address (from IP packet header). /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_rx_handle_udp_packet( chdev_t * chdev, uint8_t * k_buf, uint32_t k_length, uint32_t pkt_src_addr, uint32_t pkt_dst_addr ) { xptr_t root_xp; // extended pointer on attached sockets list root xptr_t lock_xp; // extended pointer on chdev lock xptr_t iter_xp; // iterator on socket list xptr_t socket_xp; // extended pointer on socket descriptor cxy_t socket_cxy; socket_t * socket_ptr; uint32_t socket_type; // socket type uint32_t socket_state; // socket state uint32_t local_addr; // local IP address from socket uint32_t local_port; // local port from socket uint32_t remote_addr; // remote IP address from socket uint32_t remote_port; // remote port from socket bool_t match_socket; // matching socket found uint16_t checksum; // computed checksum uint16_t pkt_checksum; // received checksum xptr_t socket_rbuf_xp; // extended pointer on socket rx_buf xptr_t socket_lock_xp; // extended pointer on socket lock xptr_t socket_client_xp; // extended pointer on socket rx_client field xptr_t client_xp; // extended pointer on client thread descriptor uint32_t payload; // number of bytes in payload uint32_t status; // number of bytes in rx_buf uint32_t space; // number of free slots in rx_buf uint32_t moved_bytes; // number of bytes actually moved to rx_buf // build extended pointers on list of sockets attached to NIC_RX chdev root_xp = XPTR( local_cxy , &chdev->wait_root ); lock_xp = XPTR( local_cxy , &chdev->wait_lock ); // compute UDP packet checksum checksum = dev_nic_udp_checksum( k_buf , k_length ); // get checksum from received packet header pkt_checksum = ((uint16_t)k_buf[6] << 8) | (uint16_t)k_buf[7]; // discard corrupted packet if( pkt_checksum != checksum ) return; // get src_port and dst_port from UDP header uint32_t pkt_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1]; uint32_t pkt_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3]; // discard unexpected packet if( xlist_is_empty( root_xp ) ) return; // take the lock protecting the sockets list remote_busylock_acquire( lock_xp ); match_socket = false; // scan sockets list to find a match XLIST_FOREACH( root_xp , iter_xp ) { // get socket cluster and local pointer socket_xp = XLIST_ELEMENT( iter_xp , socket_t , rx_list ); socket_ptr = GET_PTR( socket_xp ); socket_cxy = GET_CXY( socket_xp ); // get socket type socket_type = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type )); socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state )); // skip TCP socket if( socket_type == SOCK_STREAM ) continue; // get relevant info from socket descriptor local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); // compute matching bool_t local_match = (local_addr == pkt_dst_addr) && (local_port == pkt_dst_port); bool_t remote_match = (remote_addr == pkt_src_addr) && (remote_port == pkt_src_port); if (socket_state == UDP_STATE_ESTAB ) match_socket = local_match && remote_match; else match_socket = local_match; // exit loop when socket found if( match_socket ) break; } // release the lock protecting the sockets list remote_busylock_release( lock_xp ); // discard unexpected packet if( match_socket == false ) return; // build extended pointers on various socket fields socket_rbuf_xp = XPTR( socket_cxy , &socket_ptr->rx_buf ); socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); socket_client_xp = XPTR( socket_cxy , &socket_ptr->rx_client ); // take the lock protecting the socket remote_queuelock_acquire( socket_lock_xp ); // get status & space from rx_buf status = remote_buf_status( socket_rbuf_xp ); space = NIC_RX_BUF_SIZE - status; // get client thread client_xp = hal_remote_l64( socket_client_xp ); // get number of bytes in payload payload = k_length - UDP_HEAD_LEN; // compute number of bytes to move : min (space , seg_payload) moved_bytes = ( space < payload ) ? space : payload; // move payload from kernel buffer to socket rx_buf remote_buf_put_from_kernel( socket_rbuf_xp, k_buf + UDP_HEAD_LEN, moved_bytes ); // unblock client thread if( client_xp != XPTR_NULL ) { thread_unblock( client_xp , THREAD_BLOCKED_IO ); } // release the lock protecting the socket remote_queuelock_release( socket_lock_xp ); } // end dev_nic_rx_handle_udp_packet() /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_rx_server() function to handle one RX // TCP segment contained in a kernel buffer defined by the & arguments. // The and arguments are obtained from the received // IP packet header. It the received segment doesn't match any connected socket attached // to the selected chdev[k], or any listening socket waiting connection, or if the segment // is corrupted, this segment is discarded. // If required by the TCP flags, it registers an R2T request in the socket R2T queue // to implement the TCP handcheck for close and connect. /////////////////////////////////////////////////////////////////////////////////////////// // Implementation note: // A "connected" socket is actually a TCP socket already attached to a given NIC_RX[k] // chdev, and can therefore receive a TCP segment on the NIC channel . A "listening" // socket is a TCP socket in the LISTEN state. This function operates in 6 steps: // 1) It checks the TCP checksum, and discard the corrupted segment if corrupted. // 2) It scans the list of sockets attached to the NIC_RX[k] chdev, to find one TCP // socket matching the TCP segment header. // 3) When a matching connected socket is found, it handles the received segment, including // the SYN, FIN, ACK and RST flags. It updates the socket state when required, moves // data to the rx_buf when possible, and return. It takes the lock protecting the socket, // because an connected socket is accessed by both the NIC_TX and NIC_RX server threads. // 4) If no matching connected socket has been found, it scans the list of listening // sockets to find a matching listening socket. // 5) When a matching listening socket is found, it simply registers a new connection // request in the listening socket CRQ queue, when the SYN flag is set, and insert // a SYN-ACK request in the socket R2T queue. // 6) It discards the packet if no connected or listening socket has been found. /////////////////////////////////////////////////////////////////////////////////////////// // @ chdev : [in] local pointer on local NIC_RX chdev descriptor. // @ k_buf : [in] pointer on the TCP packet in local kernel buffer. // @ k_length : [in] number of bytes in buffer (including TCP header). // @ seg_remote_addr : [in] remote IP address (from IP packet header). // @ seg_local_addr : [in] local IP address (from IP packet header). /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_rx_handle_tcp_segment( chdev_t * chdev, uint8_t * k_buf, uint32_t k_length, uint32_t seg_remote_addr, uint32_t seg_local_addr ) { xptr_t root_xp; // extended pointer on connected sockets list root xptr_t lock_xp; // extended pointer on chdev lock xptr_t iter_xp; // iterator for these queues bool_t attached_match; // true if one attached socket match bool_t listening_match; // true if one listening socket match xptr_t socket_xp; // extended pointer on matching socket cxy_t socket_cxy; // cluster identifier of matching socket socket_t * socket_ptr; // local pointer on matching socket uint32_t socket_local_addr; // local IP address from socket uint32_t socket_local_port; // local port from socket uint32_t socket_remote_addr; // remote IP address from socket uint32_t socket_remote_port; // remote port from socket uint32_t socket_state; // socket state uint32_t socket_type; // socket type bool_t socket_tx_valid; // TX command valid uint32_t socket_tx_cmd; // TX command type uint32_t socket_tx_todo; // number of TX bytes not sent yet uint32_t socket_tx_nxt; // next byte to send in TX stream uint32_t socket_tx_una; // first unacknowledged byte in TX stream bool_t socket_rx_valid; // RX command valid uint32_t socket_rx_cmd; // TX command type uint32_t socket_rx_nxt; // next expected byte in RX stream uint32_t socket_rx_wnd; // current window value in RX stream uint32_t socket_rx_irs; // initial sequence index in RX stream xptr_t socket_lock_xp; // extended pointer on lock protecting socket state xptr_t socket_rx_buf_xp; // extended pointer on socket rx_buf xptr_t socket_r2tq_xp; // extended pointer on socket R2T queue xptr_t socket_crqq_xp; // extended pointer on socket CRQ queue uint16_t checksum; // computed TCP segment chechsum error_t error; // get relevant infos from TCP segment header uint32_t seg_remote_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1]; uint32_t seg_local_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3]; uint32_t seg_seq_num = ((uint32_t)k_buf[4] << 24) | ((uint32_t)k_buf[5] << 16) | ((uint32_t)k_buf[6] << 8) | ((uint32_t)k_buf[7] ); uint32_t seg_ack_num = ((uint32_t)k_buf[8] << 24) | ((uint32_t)k_buf[9] << 16) | ((uint32_t)k_buf[10] << 8) | ((uint32_t)k_buf[11] ); uint8_t seg_hlen = k_buf[12] << 2; // TCP header length in bytes uint8_t seg_flags = k_buf[13]; bool_t seg_ack_set = ((seg_flags & TCP_FLAG_ACK) != 0); bool_t seg_syn_set = ((seg_flags & TCP_FLAG_SYN) != 0); bool_t seg_fin_set = ((seg_flags & TCP_FLAG_FIN) != 0); bool_t seg_rst_set = ((seg_flags & TCP_FLAG_RST) != 0); uint16_t seg_window = ((uint32_t)k_buf[14] << 8) | (uint32_t)k_buf[15]; uint16_t seg_checksum = ((uint32_t)k_buf[16] << 8) | (uint32_t)k_buf[17]; uint32_t seg_data_len = k_length - seg_hlen; // number of bytes in payload #if DEBUG_DEV_NIC_RX thread_t * this = CURRENT_THREAD; uint32_t cycle; uint32_t fdid; pid_t pid; #endif #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] enters / tcp_length %d / tcp_flags %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, k_length, seg_flags , cycle ); #endif // compute and check TCP checksum k_buf[16] = 0; k_buf[17] = 0; checksum = dev_nic_tcp_checksum( k_buf, k_length, seg_remote_addr, seg_local_addr ); // discard segment if corrupted if( seg_checksum != checksum ) { #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] tcp checksum failure : received %x / computed %x\n", __FUNCTION__, this->process->pid, this->trdid, seg_checksum, checksum ); #endif return; } // scan list of attached sockets to find a matching TCP socket attached_match = false; // build extended pointer on xlist of sockets attached to NIC_RX chdev root_xp = XPTR( local_cxy , &chdev->wait_root ); lock_xp = XPTR( local_cxy , &chdev->wait_lock ); // take the lock protecting the list of attached sockets remote_busylock_acquire( lock_xp ); XLIST_FOREACH( root_xp , iter_xp ) { // get socket cluster and local pointer socket_xp = XLIST_ELEMENT( iter_xp , socket_t , rx_list ); socket_ptr = GET_PTR( socket_xp ); socket_cxy = GET_CXY( socket_xp ); // get socket type and state socket_type = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type )); socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state )); // skip UDP socket if( socket_type == SOCK_DGRAM ) continue; // get relevant socket infos for matching socket_local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); socket_remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); socket_local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); socket_remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); // compute matching condition for a connected socket attached_match = (socket_local_addr == seg_local_addr) && (socket_local_port == seg_local_port) && (socket_remote_addr == seg_remote_addr) && (socket_remote_port == seg_remote_port) ; // exit loop if matching if( attached_match ) { #if DEBUG_DEV_NIC_RX fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ) ); pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ) ); if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] matching attached socket[%d,%d] / state %s\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif break; } } // end loop on attached sockets // release the lock protecting the list of attached sockets remote_busylock_release( lock_xp ); // handle TCP segment for an attached socket if( attached_match ) { // The actions depend on both the socket state and the received segment flags : // - update socket state, // - move data to rx_buf, // - register a request in R2T queue when required // build extended pointers on various socket fields socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); socket_rx_buf_xp = XPTR( socket_cxy , &socket_ptr->rx_buf ); socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq ); // take the lock protecting the matching socket remote_queuelock_acquire( socket_lock_xp ); // get relevant socket infos from socket descriptor socket_tx_valid = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_valid )); socket_tx_cmd = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_cmd )); socket_tx_todo = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_todo )); socket_tx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_nxt )); socket_tx_una = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_una )); socket_rx_valid = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_valid )); socket_rx_cmd = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_cmd )); socket_rx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_nxt )); socket_rx_wnd = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_wnd )); socket_rx_irs = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_irs )); // handle the received segment, depending on the matching socket state switch( socket_state ) { //////////////////////// case TCP_STATE_SYN_SENT: // TCP client waiting for SYN-ACK in connect handshake { // [1] check ACK flag if( seg_ack_set ) { if( seg_ack_num != TCP_ISS_CLIENT + 1 ) // bad ACK => report error { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect ack_num %x / get %x\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), TCP_ISS_CLIENT + 1, seg_ack_num ); #endif // make an RST request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_RST, chdev->channel ); // report error to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADACK ); break; } } // [2] check RST flag // receive RST => report error if( seg_rst_set ) { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_BOUND ); // signal error to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_RST ); break; } // [3] handle security & precedence TODO ... someday // [4] handle SYN-ACK if( seg_syn_set && seg_ack_set ) // received SYN and ACK => report success { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received expected SYN-ACK\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // set socket.tx_una hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num ); // set socket.rx_irs hal_remote_s32( XPTR(socket_cxy , &socket_ptr->rx_irs), seg_seq_num ); // set socket.rx_nxt hal_remote_s32( XPTR(socket_cxy , &socket_ptr->rx_nxt), seg_seq_num + 1 ); // update socket.state hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), TCP_STATE_ESTAB ); // make an ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_ACK, chdev->channel ); // report succes to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } else // received SYN without ACK => client becomes server { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] for socket[%x,%d] %s : received SYN-ACK => become server\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // update socket.state hal_remote_s32( XPTR(socket_cxy,&socket_ptr->state), TCP_STATE_SYN_RCVD ); // set socket.tx_nxt hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_SERVER ); // set socket.rx_nxt to seg_seq_num + 1 hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_nxt), seg_seq_num + 1 ); // make a SYN.ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_SYN | TCP_FLAG_ACK, chdev->channel ); } break; } //////////////////////// case TCP_STATE_SYN_RCVD: // TCP server waiting last ACK in connect handshake { // [1] check sequence number if( seg_seq_num != socket_rx_nxt ) // unexpected SEQ_NUM => discard { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect seq_num %x / get %x\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), socket_rx_nxt, seg_seq_num ); #endif // discard segment without reporting break; } // [2] handle RST flag // received RST => report error if( seg_rst_set ) { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_BOUND ); // report error to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_RST ); break; } // [3] handle security & precedence TODO ... someday // [4] handle SYN flag if( seg_syn_set ) // received SYN => discard { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received SYN flag\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // discard segment without reporting break; } // [5] handle ACK flag if( seg_ack_set == false ) // missing ACK => discard { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : no ACK in TCP segment\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // discard segment without reporting break; } else if( seg_ack_num != (TCP_ISS_SERVER + 1) ) // unacceptable ACK { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : expect ack_num %x / get %x\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), TCP_ISS_SERVER + 1, seg_ack_num ); #endif // register an RST request to R2TQ for remote TCP client socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_RST, chdev->channel ); // report error to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADACK ); } else // acceptable ACK { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received expected ACK\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // set socket.tx_una hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num ); // update socket.state hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), TCP_STATE_ESTAB ); // report success to local TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } break; } ///////////////////// case TCP_STATE_ESTAB: case TCP_STATE_FIN_WAIT1: case TCP_STATE_FIN_WAIT2: case TCP_STATE_CLOSE_WAIT: case TCP_STATE_CLOSING: case TCP_STATE_LAST_ACK: case TCP_STATE_TIME_WAIT: { // [1] check sequence number : out_of_order segments not accepted if( seg_seq_num != socket_rx_nxt ) { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : illegal SEQ_NUM %x / expected %x\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), seg_seq_num, socket_rx_nxt ); #endif // discard segment break; } // check all bytes in window when the payload exist // TODO : we could accept bytes that are in window, // but this implementation reject all bytes in segment if( seg_data_len > 0 ) { // compute min & max acceptable sequence numbers uint32_t seq_min = socket_rx_nxt; uint32_t seq_max = socket_rx_nxt + socket_rx_wnd - 1; // compute sequence number for last byte in segment uint32_t seg_seq_last = seg_seq_num + seg_data_len - 1; if( is_in_window( seg_seq_last, seq_min, seq_max ) == false ) { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : last SEQ_NUM %x not in [%x,%x]\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), seg_seq_last, seq_min, seq_max ); #endif // discard segment break; } } // [2] handle RST flag if( seg_rst_set ) { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received RST flag\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif if( (socket_state == TCP_STATE_ESTAB ) || (socket_state == TCP_STATE_FIN_WAIT1 ) || (socket_state == TCP_STATE_FIN_WAIT2 ) || (socket_state == TCP_STATE_CLOSE_WAIT) ) { // TODO all pending send & received commands // must receive "reset" responses // TODO destroy the socket } else // states CLOSING / LAST_ACK / TIME_WAIT { // TODO } break; } // [3] handle security & precedence TODO ... someday // [4] check SYN flag if( seg_syn_set ) // received SYN => ERROR { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : received unexpected SYN\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid , socket_state_str(socket_state) ); #endif // TODO signal error to user // make an RST request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_RST, chdev->channel ); // update socket state hal_remote_s32( XPTR(socket_cxy , &socket_ptr->state), TCP_STATE_BOUND ); break; } // [5] check ACK if( seg_ack_set == false ) // missing ACK { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : no ACK flag\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // discard segment break; } else if( is_in_window( seg_ack_num, socket_tx_una, socket_tx_nxt ) == false ) // unacceptable ACK { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : ACK_NUM %x not in [%x,%x]\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), seg_ack_num, socket_tx_una, socket_tx_nxt ); #endif // discard segment break; } else // acceptable ack { // update socket.tx_una hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_una), seg_ack_num ); // update socket.tx_wnd hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_wnd), seg_window ); // check last data byte acknowledged for a SEND command if( (socket_tx_todo == 0) && (seg_ack_num == socket_tx_nxt) && (socket_tx_cmd == CMD_TX_SEND) ) { // signal success to TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } } // [7] handle URG flag TODO ... someday // [8] Move DATA to rx_buf / ACK request to R2T queue / unblock rx_client if( seg_data_len ) { if( (socket_state == TCP_STATE_ESTAB) || (socket_state == TCP_STATE_FIN_WAIT1) || (socket_state == TCP_STATE_FIN_WAIT2) ) { // get number of bytes already stored in rx_buf uint32_t status = remote_buf_status( socket_rx_buf_xp ); // compute empty space in rx_buf uint32_t space = NIC_RX_BUF_SIZE - status; // compute number of bytes to move : min (space , seg_data_len) uint32_t nbytes = ( space < seg_data_len ) ? space : seg_data_len; // move payload from k_buf to rx_buf remote_buf_put_from_kernel( socket_rx_buf_xp, k_buf + seg_hlen, nbytes ); #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : move %d bytes to rx_buf\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state), nbytes ); #endif // update socket.rx_nxt hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), socket_rx_nxt + nbytes ); // update socket.rx_wnd hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ), socket_rx_wnd - nbytes ); // make an ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_ACK, chdev->channel ); // check pending RX_RECV command if( (socket_rx_valid == true) && (socket_rx_cmd == CMD_RX_RECV) ) { // reset rx_valid hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_valid), false ); // report success to RX client thread dev_nic_unblock_rx_client( socket_xp , CMD_STS_SUCCESS ); #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : unblock waiting RX client thread\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif } } } // [9] handle FIN flag if( socket_state == TCP_STATE_ESTAB ) { if( seg_fin_set ) // received ACK & FIN { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSE_WAIT\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // update socket.rx_nxt when FIN received hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), socket_rx_nxt + 1 ); // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSE_WAIT ); // make an ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_ACK, chdev->channel ); // check pending RX_RECV command if( (socket_rx_valid == true) && (socket_rx_cmd == CMD_RX_RECV) ) { // reset rx_valid hal_remote_s32( XPTR(socket_cxy,&socket_ptr->rx_valid), false ); // report error to RX client thread dev_nic_unblock_rx_client( socket_xp , CMD_STS_EOF ); #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : unblock RX client waiting on RECV\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif } } } else if( socket_state == TCP_STATE_FIN_WAIT1 ) { if( seg_fin_set ) // received ACK & FIN { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSING\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // update socket.rx_nxt when FIN received hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), socket_rx_nxt + 1 ); // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSING ); // make an ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_ACK, chdev->channel ); } else // received ACK only { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : only ACK => goes FIN_WAIT2\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_FIN_WAIT2 ); } } else if( socket_state == TCP_STATE_FIN_WAIT2 ) { if( seg_fin_set ) // received ACK & FIN { #if DEBUG_DEV_NIC_RX printk("\n[%s] thread[%x,%x] socket[%x,%d] %s : FIN-ACK => goes CLOSED / unblock client\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif // update socket.rx_nxt when FIN received hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), socket_rx_nxt + 1 ); // update socket.state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSED ); // TODO change to TIME_WAIT // make an ACK request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_ACK, chdev->channel ); // report success to TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); // TODO start the MSL timer / turn off others timers } } else if( socket_state == TCP_STATE_CLOSING ) // received ACK { // update socket.state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSED ); // todo change to TIME_WAIT // TODO start the MSL timer / turn off others timers } else if( socket_state == TCP_STATE_TIME_WAIT ) { // TODO wait msl_time_out before unblocking TX thread // update socket.state when ACK received hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSED ); // unblock TX client thead for success dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } else if( socket_state == TCP_STATE_CLOSE_WAIT ) { // do nothing } else if( socket_state == TCP_STATE_LAST_ACK ) { // update socket.state when ACK received hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_CLOSED ); // unblock TX client thead for success dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } } // end case connecteded states } // end switch socket state // release the lock protecting socket state remote_queuelock_release( socket_lock_xp ); return; } // end if attached socket // 4. scan the list of listening sockets listening_match = false; // get pointers on NIC_RX[0] chdev xptr_t rx0_chdev_xp = chdev_dir.nic_rx[0]; chdev_t * rx0_chdev_ptr = GET_PTR( rx0_chdev_xp ); cxy_t rx0_chdev_cxy = GET_CXY( rx0_chdev_xp ); // build extended pointers on list of listening sockets xptr_t rx0_root_xp = XPTR( rx0_chdev_cxy , &rx0_chdev_ptr->ext.nic.root ); xptr_t rx0_lock_xp = XPTR( rx0_chdev_cxy , &rx0_chdev_ptr->ext.nic.lock ); // take the lock protecting the list of listening sockets remote_busylock_acquire( rx0_lock_xp ); // scan the xlist of listening socket XLIST_FOREACH( rx0_root_xp , iter_xp ) { // get socket cluster and local pointer socket_xp = XLIST_ELEMENT( iter_xp , socket_t , rx_list ); socket_ptr = GET_PTR( socket_xp ); socket_cxy = GET_CXY( socket_xp ); // get relevant socket type and state socket_type = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type )); socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state )); // check socket type and state assert( (socket_type == SOCK_STREAM ) , "illegal socket type" ); assert( (socket_state == TCP_STATE_LISTEN ) , "illegal socket state" ); // get relevant socket infos for matching socket_local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); socket_local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); // compute matching condition for a listening socket listening_match = (socket_local_addr == seg_local_addr) && (socket_local_port == seg_local_port); // exit loop if matching if( listening_match ) { #if DEBUG_DEV_NIC_RX fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid ) ); pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid ) ); if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] matching listening socket[%d,%d] / state %s\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str(socket_state) ); #endif break; } } // end loop on listening sockets // release the lock protecting the list of listening sockets remote_busylock_release( rx0_lock_xp ); // 5. handle TCP segment for a matching listening socket if( listening_match ) { // The actions depend on the received segment flags // - discard segment for RST or ACK // - update socket state & remote IP address, // register connect request in socket CRQ queue, // and unblock client thread for SYN // discard segment if RST flag if( seg_rst_set ) { #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : received RST\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid ); #endif return; } // discard segment if ACK flag if( seg_ack_set ) { #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : received ACK\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid ); #endif return; } // SYN flag == CONNECT request / seq_num cannot be wrong if( seg_syn_set ) { // build extended pointer on listening socket CRQ socket_crqq_xp = XPTR( socket_cxy , &socket_ptr->crqq ); // try to register request into CRQ queue error = socket_put_crq_request( socket_crqq_xp, seg_remote_addr, seg_remote_port, seg_seq_num, seg_window ); if ( error ) // CRQ full { #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] listening socket[%x,%d] CRQ full => send RST\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid ); #endif // make an RST request to R2T queue socket_put_r2t_request( socket_r2tq_xp, TCP_FLAG_RST, chdev->channel ); } else // new connection request registered in CRQ { #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] : register request in CRQ\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid ); #endif // check pending RX_ACCEPT command if( (hal_remote_l32(XPTR(socket_cxy,&socket_ptr->rx_valid)) == true) && (hal_remote_l32(XPTR(socket_cxy,&socket_ptr->rx_cmd)) == CMD_RX_ACCEPT) ) { // reset rx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_valid ), false ); // report success to RX client thread dev_nic_unblock_rx_client( socket_xp , CMD_STS_SUCCESS ); #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] for listening socket[%x,%d] unblock RX client thread\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid ); #endif } } // end register request in CRQ } // end if SYN return; } // end if listening_match // 6. no socket found => discard segment #if DEBUG_DEV_NIC_RX if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] exit failure : no socket found => discard segment\n", __FUNCTION__, this->process->pid, this->trdid ); #endif } // end dev_nic_rx_handle_tcp_segment() ///////////////////////////////////////// void dev_nic_rx_server( chdev_t * chdev ) { uint8_t k_buf[2048]; // kernel buffer for one ETH/IP/UDP packet uint32_t pkt_src_addr; // packet source IP address uint32_t pkt_dst_addr; // packet destination IP address uint32_t trsp_protocol; // transport protocol (TCP / UDP) uint32_t eth_length; // size of Ethernet packet (bytes) uint32_t ip_length; // size of IP packet in bytes error_t error; thread_t * this = CURRENT_THREAD; // check thread can yield thread_assert_can_yield( this , __FUNCTION__ ); // check chdev direction and type assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == true) , "illegal chdev type or direction" ); #if DEBUG_DEV_NIC_RX uint32_t cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_RX ) printk("\n[%s] thread[%x,%x] starts / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // get extended pointers on server tread and chdev xptr_t thread_xp = XPTR( local_cxy , this ); xptr_t chdev_xp = XPTR( local_cxy , chdev ); while( 1 ) { // call NIC driver to move one packet from NIC_RX queue to kernel buffer this->nic_cmd.dev_xp = chdev_xp; this->nic_cmd.type = NIC_CMD_READ; this->nic_cmd.buffer = k_buf; chdev->cmd( XPTR( local_cxy , this ) ); // get packet length eth_length = this->nic_cmd.status; // check success if( eth_length == 0 ) // queue empty => block and deschedule { #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] NIC_RX_QUEUE empty => blocks on / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // enable NIC_RX IRQ dev_pic_enable_irq( this->core->lid , chdev_xp ); // block and deschedule thread_block( thread_xp , THREAD_BLOCKED_ISR ); sched_yield("client blocked on NIC_TX queue full"); // disable NIC-RX IRQ dev_pic_disable_irq( this->core->lid , chdev_xp ); #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] resumes / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif } else // success => handle packet { #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) dev_nic_packet_display( false, // is_tx this->process->pid, this->trdid, cycle, k_buf ); #endif // analyse the ETH header error = dev_nic_rx_check_eth( k_buf, &ip_length ); // discard packet if error reported by Ethernet layer if( error ) { #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] discard ETH packet / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif continue; } #if (DEBUG_DEV_NIC_RX & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] successfully checked ETH packet / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // analyse the IP header error = dev_nic_rx_check_ip( k_buf + ETH_HEAD_LEN, ip_length, &pkt_src_addr, &pkt_dst_addr, &trsp_protocol ); // discard packet if error reported by IP layer if( error ) { #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] discarded IP packet / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif continue; } #if (DEBUG_DEV_NIC_RX & 1 ) cycle = (uint32_t)hal_get_cycles(); if( (DEBUG_DEV_NIC_RX < cycle) && (trsp_protocol == PROTOCOL_UDP) ) printk("\n[%s] thread[%x,%x] successfully checked UDP packet / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); if( (DEBUG_DEV_NIC_RX < cycle) && (trsp_protocol == PROTOCOL_TCP) ) printk("\n[%s] thread[%x,%x] successfully checked TCP segment / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // call relevant transport protocol if( trsp_protocol == PROTOCOL_UDP ) { dev_nic_rx_handle_udp_packet( chdev, k_buf + ETH_HEAD_LEN + IP_HEAD_LEN, ip_length - IP_HEAD_LEN, pkt_src_addr, pkt_dst_addr ); } else if ( trsp_protocol == PROTOCOL_TCP) { dev_nic_rx_handle_tcp_segment( chdev, k_buf + ETH_HEAD_LEN + IP_HEAD_LEN, ip_length - IP_HEAD_LEN, pkt_src_addr, pkt_dst_addr ); } else // discard packet if unsupported transport protocol { #if DEBUG_DEV_NIC_RX cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_RX < cycle ) printk("\n[%s] thread[%x,%x] discarded unsupported transport protocol %d\n", __FUNCTION__, this->process->pid, this->trdid, trsp_protocol, cycle ); #endif continue; } } } // end of while loop } // end dev_nic_rx_server() /////////////////////////////////////////////////////////////////////////////////////////// // Functions used by the NIC_TX server thread /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_build_packet() function. // It moves one ETH/IP/UDP packet from the kernel buffer identified by the and // arguments to the NIC_TX_QUEUE identified the argument. // It blocks and deschedules on the BLOCKED_ISR condition if the queue is full. /////////////////////////////////////////////////////////////////////////////////////////// // @ chdev : [in] local pointer on NIC_TX chdev. // @ k_buf : [in] pointer on a local kernel buffer (2K bytes). // @ length : [in] actual Ethernet packet length in bytes. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_move_packet( chdev_t * chdev, uint8_t * k_buf, uint32_t length ) { thread_t * this = CURRENT_THREAD; // get extended pointers on server tread and chdev xptr_t thread_xp = XPTR( local_cxy , this ); xptr_t chdev_xp = XPTR( local_cxy , chdev ); // check thread can yield thread_assert_can_yield( this , __FUNCTION__ ); #if (DEBUG_DEV_NIC_TX & 1) uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_TX < cycle ) printk("\n[%s] thread[%x,%x] enter / buf %x / length %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, k_buf, length, cycle ); #endif // initialize WRITE command in server thread descriptor this->nic_cmd.dev_xp = chdev_xp; this->nic_cmd.type = NIC_CMD_WRITE; this->nic_cmd.buffer = k_buf; this->nic_cmd.length = length; while( 1 ) { // call driver to move TX packet chdev->cmd( thread_xp ); // exit while if success if( this->nic_cmd.status == length ) // exit while & return if success { #if (DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_TX < cycle ) printk("\n[%s] thread[%x,%x] exit SUCCESS / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif break; } else // block and deschedule if queue full { #if (DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_TX < cycle ) printk("\n[%s] thread[%x,%x] NIC_TX_QUEUE full => blocks on ISR / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // enable NIC_TX IRQ dev_pic_enable_irq( this->core->lid , chdev_xp ); // TX server thread blocks and deschedules thread_block( thread_xp , THREAD_BLOCKED_ISR ); sched_yield("client blocked on NIC_TX queue full"); // disable NIC-TX IRQ dev_pic_disable_irq( this->core->lid , chdev_xp ); #if (DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_DEV_NIC_TX < cycle ) printk("\n[%s] thread[%x,%x] resumes / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif } } } // end dev_nic_tx_move_packet() /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_server() function to build an UDP // header in the kernel buffer defined by the arguement, as specified by the // argument. The argument defines the number of bytes in payload. // It set the "src_port", "dst_port", "total_length" and "checksum" fields in UDP header. // The payload must be previouly loaded in the pernel buffer. /////////////////////////////////////////////////////////////////////////////////////////// // @ k_buf : [in] pointer on first byte of UDP header in kernel buffer. // @ socket_xp : [in] extended pointer on socket. // @ length : [in] number of bytes in payload. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_build_udp_header( uint8_t * k_buf, xptr_t socket_xp, uint32_t length ) { uint16_t checksum; // checksum value uint32_t total_length; // total UDP packet length uint32_t local_addr; // local IP address uint32_t remote_addr; // remote IP address uint32_t local_port; // local port uint32_t remote_port; // remote port // get socket cluster an local pointer socket_t * socket_ptr = GET_PTR( socket_xp ); cxy_t socket_cxy = GET_CXY( socket_xp ); // get relevant infos from socket local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); // compute UDP packet total length total_length = length + UDP_HEAD_LEN; // set src_port and dst_port in header k_buf[0] = local_port >> 8; k_buf[1] = local_port; k_buf[2] = remote_port >> 8; k_buf[3] = remote_port; // set packet length in header k_buf[4] = total_length >> 8; k_buf[5] = total_length; // compute UDP packet checksum checksum = dev_nic_udp_checksum( k_buf , total_length ); // set checksum k_buf[6] = checksum >> 8; k_buf[7] = checksum; } // end dev_nic_tx_build_udp_header() /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_server() function. // It builds a TCP header in the kernel buffer defined by the argument. // The payload must have been previouly registered in this buffer. // The "local_addr", "local_port", "remote_addr", "remote_port", seq_num", "ack_num", // and "window" fields are obtained from the argument. // The argument defines the number of bytes in payload, and the argument // defines the flags to be set in TCP header. /////////////////////////////////////////////////////////////////////////////////////////// // @ k_buf : [in] pointer on first byte of TCP header in kernel buffer. // @ socket_xp : [in] extended pointer on socket. // @ length : [in] number of bytes in payload. // @ flags : [in] flags to be set in TCP header. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_build_tcp_header( uint8_t * k_buf, xptr_t socket_xp, uint32_t length, uint8_t flags ) { uint16_t checksum; // global segment checksum uint32_t total_length; // total UDP packet length uint32_t src_addr; // local IP address uint32_t dst_addr; // remote IP address uint16_t src_port; // local port uint16_t dst_port; // remote port uint32_t seq_num; // first byte of segment in TX stream uint32_t ack_num; // next expected byte in RX stream uint32_t window; // window of accepted segments in RX stream // get socket cluster an local pointer socket_t * sock_ptr = GET_PTR( socket_xp ); cxy_t sock_cxy = GET_CXY( socket_xp ); // get relevant infos from socket src_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_addr )); dst_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_addr )); src_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_port )); dst_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_port )); seq_num = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->tx_nxt )); ack_num = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_nxt )); window = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_wnd )); // compute TCP segment total length total_length = length + TCP_HEAD_LEN; // set "src_port" and "dst_port" k_buf[0] = src_port >> 8; k_buf[1] = src_port; k_buf[2] = dst_port >> 8; k_buf[3] = dst_port; // set "seq_num" k_buf[4] = seq_num >> 24; k_buf[5] = seq_num >> 16; k_buf[6] = seq_num >> 8; k_buf[7] = seq_num; // set "ack_num" k_buf[8] = ack_num >> 24; k_buf[9] = ack_num >> 16; k_buf[10] = ack_num >> 8; k_buf[11] = ack_num; // set "hlen" k_buf[12] = 5; // set "flags" k_buf[13] = flags & 0x3F; // set "window" k_buf[14] = window >> 8; k_buf[15] = window; // reset "checksum" k_buf[16] = 0; k_buf[17] = 0; // set "urgent_ptr" k_buf[18] = 0; k_buf[19] = 0; // compute TCP segment checksum checksum = dev_nic_tcp_checksum( k_buf, total_length, src_addr, dst_addr ); // set "checksum" k_buf[16] = checksum >> 8; k_buf[17] = checksum; } // end dev_nic_tx_build_tcp_header() /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_server() function. // It builds the IP header in the 20 first bytes of . /////////////////////////////////////////////////////////////////////////////////////////// // @ buffer : pointer on first byte of IP header in kernel buffer // @ src_addr : source IP address. // @ dst_addr : destination IP address. // @ length : number of bytes in IP packet payload. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_build_ip_header( uint8_t * buffer, uint32_t src_addr, uint32_t dst_addr, uint8_t protocol, uint16_t length ) { uint16_t hcs; uint16_t total = length + IP_HEAD_LEN; buffer[0] = 0x45; // IPV4 / IHL = 20 bytes buffer[1] = 0; // DSCP / ECN buffer[2] = total >> 8; buffer[3] = total; buffer[4] = 0x40; // Don't Fragment buffer[5] = 0; buffer[6] = 0; buffer[7] = 0; buffer[8] = 0xFF; // TTL buffer[9] = protocol; // transport protocol buffer[12] = src_addr >> 24; buffer[13] = src_addr >> 16; buffer[14] = src_addr >> 8; buffer[15] = src_addr; buffer[16] = dst_addr >> 24; buffer[17] = dst_addr >> 16; buffer[18] = dst_addr >> 8; buffer[19] = dst_addr; // compute IP header checksum hcs = dev_nic_ip_checksum( buffer ); // set checksum buffer[10] = hcs >> 8; buffer[11] = hcs; } // end dev_nic_tx_build_ip_header /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_server() function. // It builds the Ethernet header in the 14 first bytes of . /////////////////////////////////////////////////////////////////////////////////////////// // @ buffer : pointer on first byte of Ethernet header in kernel buffer // @ src_mac_54 : two MSB bytes in source MAC address. // @ src_mac_32 : two MED bytes in source MAC address. // @ src_mac_10 : two LSB bytes in source MAC address. // @ dst_mac_54 : two MSB bytes in destination MAC address. // @ dst_mac_32 : two MED bytes in destination MAC address. // @ dst_mac_10 : two LSB bytes in destination MAC address. // @ length : number of bytes in Ethernet frame payload. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_build_eth_header( uint8_t * buffer, uint8_t src_mac_5, uint8_t src_mac_4, uint8_t src_mac_3, uint8_t src_mac_2, uint8_t src_mac_1, uint8_t src_mac_0, uint8_t dst_mac_5, uint8_t dst_mac_4, uint8_t dst_mac_3, uint8_t dst_mac_2, uint8_t dst_mac_1, uint8_t dst_mac_0, uint32_t length ) { buffer[0] = dst_mac_5; buffer[1] = dst_mac_4; buffer[2] = dst_mac_3; buffer[3] = dst_mac_2; buffer[4] = dst_mac_1; buffer[5] = dst_mac_0; buffer[6] = src_mac_5; buffer[7] = src_mac_4; buffer[8] = src_mac_3; buffer[9] = src_mac_2; buffer[10] = src_mac_1; buffer[11] = src_mac_0; buffer[12] = length >> 8; buffer[13] = length; } // end dev_nic_tx_build_eth_header() /////////////////////////////////////////////////////////////////////////////////////////// // This static function is called by the dev_nic_tx_server() function to handle one TX // command, or one R2T request, as defined by the and arguments, // for the socket identified by the argument. It builds an ETH/IP/UDP packet // or ETH/IP/TCP segment, in the buffer defined by the argument, and registers // it in the NIC_TX queue defined by the argument. // For a TCP header, the "seq_num", ack_num", and "window" fiels are defined by the // "socket.tx_next", "socket.rx_next" and "socket.rx_wnd" fields respectively. // It updates the "socket.state", "socket.tx_nxt", "socket.r2tq", and "socket.crqq" // The supported TX command types are CONNECT / ACCEPT / SEND / CLOSE. // fields as required by the command type. // - For an UDP socket, it reset the "socket.tx_valid" field, and unblock the client // thread when the packet has been sent, or when an error must be reported. // - For a TCP socket, it reset the "socket.tx_valid" field when the segment has been // sent, but does not unblocks the client thread, that will be unblocqued by the // NIC_RX thread when the TX command is fully completed. /////////////////////////////////////////////////////////////////////////////////////////// // To build a packet, it makes the following actions: // 1) it takes the lock protecting the socket state. // 2) it get the command arguments from socket descriptor. // 3) it build an UDP packet or a TCP segment, depending on command type and socket state. // 4) it updates the socket state. // 5) it releases the lock protecting the socket. // 6) it build the IP header. // 7) it build the ETH header. // 8) it copies the packet in the NIC_TX queue. /////////////////////////////////////////////////////////////////////////////////////////// // @ cmd_state : [in] TX command valid in socket descriptor. // @ r2t_valid : [in] R2T request valid in command descriptor. // @ socket_xp : [in] extended pointer on client socket. // @ k_buf : [in] local pointer on kernel buffer (2 Kbytes). // @ chdev : [in] local pointer on NIC_RX chdev. /////////////////////////////////////////////////////////////////////////////////////////// static void dev_nic_tx_build_packet( bool_t cmd_valid, bool_t r2t_valid, xptr_t socket_xp, uint8_t * k_buf, chdev_t * chdev ) { socket_t * socket_ptr; cxy_t socket_cxy; xptr_t client_xp; // extended pointer on client thread uint32_t cmd_type; // NIC command type uint8_t * tx_buf; // local pointer on kernel buffer for payload uint32_t len; // tx_buf length (bytes) uint32_t todo; // number of bytes not yet sent uint32_t socket_type; // socket type (UDP/TCP) uint32_t socket_state; // socket state xptr_t socket_lock_xp; // extended pointer on socket lock xptr_t socket_r2tq_xp; // extended pointer on R2T queue uint32_t src_ip_addr; // source IP address uint32_t dst_ip_addr; // destination IP address uint32_t tx_nxt; // next sequence number in TX stream uint32_t nbytes; // number of bytes in UDP/TCP packet payload uint8_t * k_trsp_base; // pointer on UDP/TCP packet in kernel buffer uint32_t trsp_length; // length of TCP/UDP packet uint8_t trsp_protocol; // transport protocol type (UDP/TCP) uint8_t r2t_flags; // flags defined by one R2T queue request bool_t do_send; // build & send a packet when true // get socket cluster and local pointer socket_cxy = GET_CXY( socket_xp ); socket_ptr = GET_PTR( socket_xp ); #if DEBUG_DEV_NIC_TX thread_t * this = CURRENT_THREAD;; uint32_t cycle = (uint32_t)hal_get_cycles(); uint32_t fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid )); uint32_t pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid )); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] enter for socket[%x,%d] : cmd_valid %d / r2t_valid %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, cmd_valid, r2t_valid, cycle ); #endif // build extended pointers on socket lock and r2t queue socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq ); // 1. take lock protecting this socket remote_queuelock_acquire( socket_lock_xp ); // get relevant socket infos socket_type = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->type )); socket_state = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->state )); src_ip_addr = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->local_addr )); dst_ip_addr = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->remote_addr )); // compute UDP/TCP packet base in kernel buffer k_trsp_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN; // set default values do_send = false; trsp_length = 0; nbytes = 0; if( cmd_valid ) // handle TX command { // 2. get command arguments from socket cmd_type = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_cmd )); tx_buf = hal_remote_lpt( XPTR( socket_cxy , &socket_ptr->tx_buf )); len = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_len )); todo = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_todo )); client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client )); #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] cmd_valid for socket[%x,%d] : %s / %s / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_cmd_type_str(cmd_type), socket_state_str(socket_state), cycle ); #endif ////////////////////////////////////////////////////////// // 3. UDP : build UDP packet and update UDP socket state if( socket_type == SOCK_DGRAM ) { trsp_protocol = PROTOCOL_UDP; if( socket_state != UDP_STATE_ESTAB ) { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false ); // unblock client thread / report error dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); } else { if( cmd_type == CMD_TX_SEND ) { // compute payload length nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo; // move payload from tx_buf to 2 Kbytes kernel buffer memcpy( k_trsp_base + UDP_HEAD_LEN, tx_buf + (len - todo), nbytes ); // build UDP header dev_nic_tx_build_udp_header( k_trsp_base, socket_xp, nbytes ); // update "tx_todo" in socket descriptor hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_todo), todo - nbytes ); // send UDP packet trsp_length = UDP_HEAD_LEN + nbytes; do_send = true; #if( DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] UDP packet build / length %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, trsp_length , cycle ); #endif if( nbytes == todo ) // last byte sent { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false ); // report success to TX client dev_nic_unblock_tx_client( socket_xp , CMD_STS_SUCCESS ); } } else // CONNECT, ACCEPT, or CLOSE commands are illegal for UDP { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false ); // report error dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADCMD ); } } } // end UDP /////////////////////////////////////////////////////////// // 3. TCP : build TCP segment and update TCP socket state else if( socket_type == SOCK_STREAM ) { trsp_protocol = PROTOCOL_TCP; // handle R2T request / initialize r2t_flags if( r2t_valid ) { // build extended pointers on r2t queue socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq ); // get one request from R2T queue remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 ); } else { r2t_flags = 0; } //////////////////////////////// if( cmd_type == CMD_TX_CONNECT ) // always executed by a TCP client { if( (socket_state == TCP_STATE_BOUND) || (socket_state == TCP_STATE_LISTEN) ) // send a SYN segment { // initialises socket tx_nxt, and rx_wnd hal_remote_s32(XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_CLIENT ); hal_remote_s32(XPTR(socket_cxy , &socket_ptr->rx_wnd), TCP_MAX_WINDOW ); // build TCP SYN segment dev_nic_tx_build_tcp_header( k_trsp_base, socket_xp, 0, // length TCP_FLAG_SYN ); // send segment trsp_length = TCP_HEAD_LEN; do_send = true; #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CONNECT / " "TCP SYN build / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str( socket_state ), cycle ); #endif // update socket.state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_SYN_SENT ); // update socket.tx_nxt hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), TCP_ISS_CLIENT + 1 ); // reset tx_valid but do not unblock client thread hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); } else // report error for all other socket states { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ) , false ); // report error dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); } } //////////////////////////////////// else if( cmd_type == CMD_TX_ACCEPT ) // always executed by a TCP server { if( socket_state == TCP_STATE_SYN_RCVD ) // send a SYN-ACK segment { // initialize socket tx_nxt, and rx_wnd hal_remote_s32(XPTR(socket_cxy , &socket_ptr->tx_nxt), TCP_ISS_SERVER ); hal_remote_s32(XPTR(socket_cxy , &socket_ptr->rx_wnd), NIC_RX_BUF_SIZE); // build TCP ACK-SYN segment dev_nic_tx_build_tcp_header( k_trsp_base, socket_xp, 0, // length TCP_FLAG_SYN | TCP_FLAG_ACK ); // send segment trsp_length = TCP_HEAD_LEN; do_send = true; #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / ACCEPT / send SYN-ACK / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str( socket_state ), cycle ); #endif // update socket.state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), TCP_STATE_SYN_RCVD ); // update socket.tx_nxt hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), TCP_ISS_SERVER + 1 ); // reset tx_valid but do not unblock client thread hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); } else // report error in all other socket states { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); // report error to TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); } } /////////////////////////////////// else if( cmd_type == CMD_TX_CLOSE ) { if( (socket_state == TCP_STATE_SYN_RCVD ) || (socket_state == TCP_STATE_ESTAB ) || (socket_state == TCP_STATE_CLOSE_WAIT) ) // send a FIN-ACK segment { // get "tx_nxt" from socket descriptor tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt )); // compute next state uint32_t state = (socket_state == TCP_STATE_CLOSE_WAIT) ? TCP_STATE_LAST_ACK : TCP_STATE_FIN_WAIT1; // update socket state hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), state ); // build TCP FIN segment dev_nic_tx_build_tcp_header( k_trsp_base, socket_xp, 0, // length TCP_FLAG_FIN | TCP_FLAG_ACK ); // update "tx_nxt" in socket descriptor hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), tx_nxt + 1 ); // send segment trsp_length = TCP_HEAD_LEN; do_send = true; #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CLOSE / send FIN-ACK / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str( socket_state ), cycle ); #endif // reset tx_valid but do not unblock client thread hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); } else // all other states => signal error { #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / CLOSE / error BADSTATE / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str( socket_state ), cycle ); #endif // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); // report error dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); } } ///////////////////////////////////// else if( cmd_type == CMD_TX_SEND ) { if( (socket_state == TCP_STATE_ESTAB ) || (socket_state == TCP_STATE_CLOSE_WAIT) ) { // get "tx_nxt" from socket descriptor tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt )); // compute actual payload length nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo; // compute TCP segment base in kernel buffer k_trsp_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN; // move payload to k_buf memcpy( k_trsp_base + TCP_HEAD_LEN, tx_buf + (len - todo), nbytes ); // build TCP header dev_nic_tx_build_tcp_header( k_trsp_base, socket_xp, nbytes, // payload TCP_FLAG_ACK | r2t_flags ); // flags // update "tx_todo" in socket descriptor hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_todo), todo - nbytes ); // update "tx_nxt" in socket descriptor hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_nxt), tx_nxt + nbytes ); // send TCP segment trsp_length = TCP_HEAD_LEN + nbytes; do_send = true; if( todo == nbytes ) // last byte sent { // reset tx_valid when last byte has been sent hal_remote_s32( XPTR(socket_cxy , &socket_ptr->tx_valid), false ); } #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] socket[%x,%d] %s / SEND / " "TCP DATA build / payload %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, socket_state_str( socket_state ), nbytes, cycle ); #endif } else // all other socket states { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); // report error to TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADSTATE ); } } /////////////////////////////////// else // undefined TX command type { // reset tx_valid hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_valid ), false ); // report error to TX client thread dev_nic_unblock_tx_client( socket_xp , CMD_STS_BADCMD ); } } // end TCP } else // no valid TX command => handle R2T request only { // get one request from R2T queue remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 ); #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] only r2t_valid for socket[%x,%d] / flags %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, r2t_flags, cycle ); #endif // build TCP header dev_nic_tx_build_tcp_header( k_trsp_base, socket_xp, 0, // payload length r2t_flags ); // flags // send TCP segment trsp_protocol = PROTOCOL_TCP; trsp_length = TCP_HEAD_LEN; do_send = true; } // 4. release the lock protecting the socket remote_queuelock_release( socket_lock_xp ); // return if no packet to send if( do_send == false ) return; // 5. build IP header dev_nic_tx_build_ip_header( k_buf + ETH_HEAD_LEN, src_ip_addr, dst_ip_addr, trsp_protocol, trsp_length ); #if( DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] IP header build / length %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, IP_HEAD_LEN + trsp_length , cycle ); #endif // 6. build ETH header dev_nic_tx_build_eth_header( k_buf, (uint8_t)DST_MAC_5, (uint8_t)DST_MAC_4, (uint8_t)DST_MAC_3, (uint8_t)DST_MAC_2, (uint8_t)DST_MAC_1, (uint8_t)DST_MAC_0, (uint8_t)SRC_MAC_5, (uint8_t)SRC_MAC_4, (uint8_t)SRC_MAC_3, (uint8_t)SRC_MAC_2, (uint8_t)SRC_MAC_1, (uint8_t)SRC_MAC_0, IP_HEAD_LEN + trsp_length ); #if( DEBUG_DEV_NIC_TX & 1) cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] ETH header build / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // 7. move packet to NIC_TX queue (blocking function) dev_nic_tx_move_packet( chdev, k_buf, ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length ); #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] for socket[%x,%d] moved packet to NIC_TX / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, cycle ); #endif } // end dev_nic_tx_build_packet() ///////////////////////////////////////// void dev_nic_tx_server( chdev_t * chdev ) { uint8_t k_buf[NIC_KERNEL_BUF_SIZE]; // buffer for one packet xptr_t queue_root_xp; // extended pointer on sockets list root xptr_t queue_lock_xp; // extended pointer on lock protecting this list xptr_t socket_xp; // extended pointer on on registered socket socket_t * socket_ptr; cxy_t socket_cxy; xptr_t iter_xp; // iterator for loop on registered sockets xlist_entry_t temp_root; // root of temporary list of sockets xptr_t temp_root_xp; // extended pointer on temporary list of sockets uint32_t temp_nr; // number of active registered sockets bool_t cmd_valid; // TX command valid in socket descriptor bool_t r2t_valid; // valid R2T request in socket descriptor thread_t * this = CURRENT_THREAD; #if DEBUG_DEV_NIC_TX uint32_t cycle = (uint32_t)hal_get_cycles(); uint32_t pid; uint32_t fdid; if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] starts / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // check chdev direction and type assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == false) , "illegal chdev type or direction" ); // check thread can yield thread_assert_can_yield( this , __FUNCTION__ ); // build extended pointer on temporary list temp_root_xp = XPTR( local_cxy , &temp_root ); // build extended pointer on client sockets queue (lock & root) queue_lock_xp = XPTR( local_cxy , &chdev->wait_lock ); queue_root_xp = XPTR( local_cxy , &chdev->wait_root ); while( 1 ) // TX server infinite loop { // initialize temporary list of registered sockets as empty xlist_root_init( temp_root_xp ); temp_nr = 0; // take the lock protecting the client sockets queue remote_busylock_acquire( queue_lock_xp ); // build temporary list of all registered sockets if( xlist_is_empty( queue_root_xp ) == false ) { XLIST_FOREACH( queue_root_xp , iter_xp ) { // get client socket cluster and local pointer socket_xp = XLIST_ELEMENT( iter_xp , socket_t , tx_list ); socket_ptr = GET_PTR( socket_xp ); socket_cxy = GET_CXY( socket_xp ); // register socket in temporary list xlist_add_last( temp_root_xp , XPTR( socket_cxy , &socket_ptr->tx_temp )); temp_nr++; } } // release the lock protecting the client sockets queue remote_busylock_release( queue_lock_xp ); if( temp_nr > 0 ) { // loop on temporary list XLIST_FOREACH( temp_root_xp , iter_xp ) { // get client socket cluster and local pointer socket_xp = XLIST_ELEMENT( iter_xp , socket_t , tx_temp ); socket_ptr = GET_PTR( socket_xp ); socket_cxy = GET_CXY( socket_xp ); // get cmd_valid & t2t_valid from socket descriptor cmd_valid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->tx_valid )); // get r2t_valid from socket descriptor r2t_valid = (bool_t)remote_buf_status( XPTR( socket_cxy , &socket_ptr->r2tq )); // test if socket is active if( cmd_valid || r2t_valid ) // active socket { #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); pid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->pid )); fdid = hal_remote_l32( XPTR( socket_cxy , &socket_ptr->fdid )); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] found socket[%x,%d] / cmd_valid %d / r2t_valid %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, pid, fdid, cmd_valid, r2t_valid, cycle ); #endif // build and send one packet/segment for this socket dev_nic_tx_build_packet( cmd_valid, r2t_valid, socket_xp, k_buf, chdev ); #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) dev_nic_packet_display( true, // is_tx this->process->pid, this->trdid, cycle, k_buf ); #endif } else // inactive socket { temp_nr--; } } // end loop on temporary list } // block & deschedule if no active socket found in current iteration if( temp_nr == 0 ) { #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] no active socket => blocks on / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif // block and deschedule thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_CLIENT ); sched_yield( "waiting client" ); #if DEBUG_DEV_NIC_TX cycle = (uint32_t)hal_get_cycles(); if( cycle > DEBUG_DEV_NIC_TX ) printk("\n[%s] thread[%x,%x] resumes / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif } } // end infinite while loop } // end dev_nic_tx_server() ///////////////////////////////////////////// void dev_nic_packet_display( bool_t is_tx, pid_t thread_pid, trdid_t thread_trdid, uint32_t cycle, uint8_t * buf ) { // get ETH header fields uint64_t eth_dst_mac = ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) | ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) | ((uint64_t)buf[1] << 8) | ((uint64_t)buf[0] ) ; uint64_t eth_src_mac = ((uint64_t)buf[11] << 40) | ((uint64_t)buf[10] << 32) | ((uint64_t)buf[9] << 24) | ((uint64_t)buf[8] << 16) | ((uint64_t)buf[7] << 8) | ((uint64_t)buf[6] ) ; uint16_t eth_length = ((uint16_t)buf[12] << 8) | ((uint16_t)buf[13] ) ; // get IP header fields uint8_t ip_version = buf[14]; uint8_t ip_tos = buf[15]; uint16_t ip_length = ((uint16_t)buf[16] << 8) | ((uint16_t)buf[17] ) ; uint16_t ip_ident = ((uint16_t)buf[18] << 8) | ((uint16_t)buf[19] ) ; uint16_t ip_offset = ((uint16_t)buf[20] << 8) | ((uint16_t)buf[21] ) ; uint8_t ip_ttl = buf[22]; uint8_t ip_protocol = buf[23]; uint16_t ip_checksum = ((uint16_t)buf[24] << 8) | ((uint16_t)buf[25] ) ; uint32_t ip_src_addr = ((uint32_t)buf[26] << 24) | ((uint32_t)buf[27] << 16) | ((uint32_t)buf[28] << 8) | ((uint32_t)buf[29] ) ; uint32_t ip_dst_addr = ((uint32_t)buf[30] << 24) | ((uint32_t)buf[31] << 16) | ((uint32_t)buf[32] << 8) | ((uint32_t)buf[33] ) ; // get pointers on TXT0 chdev xptr_t txt0_xp = chdev_dir.txt_tx[0]; cxy_t txt0_cxy = GET_CXY( txt0_xp ); chdev_t * txt0_ptr = GET_PTR( txt0_xp ); // get extended pointer on remote TXT0 chdev lock xptr_t lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock ); // get TXT0 lock remote_busylock_acquire( lock_xp ); if( is_tx ) { nolock_printk("\n*** NIC_TX server thread[%x,%x] send packet / cycle %d\n", thread_pid, thread_trdid, cycle ); } else { nolock_printk("\n*** NIC_RX server thread[%x,%x] get packet / cycle %d\n", thread_pid, thread_trdid, cycle ); } nolock_printk("\n***** ETH header *****\n"); nolock_printk(" - dst_mac [6] = %l\n" , eth_dst_mac ); nolock_printk(" - src_mac [6] = %l\n" , eth_src_mac ); nolock_printk(" - length [2] = %d\n" , (uint32_t)eth_length ); nolock_printk("***** IP header *****\n"); nolock_printk(" - version [1] = %x\n" , (uint32_t)ip_version ); nolock_printk(" - tos [1] = %x\n" , (uint32_t)ip_tos ); nolock_printk(" - length [2] = %d\n" , (uint32_t)ip_length ); nolock_printk(" - ident [2] = %x\n" , (uint32_t)ip_ident ); nolock_printk(" - offset [2] = %x\n" , (uint32_t)ip_offset ); nolock_printk(" - ttl [1] = %x\n" , (uint32_t)ip_ttl ); nolock_printk(" - protocol [1] = %x\n" , (uint32_t)ip_protocol ); nolock_printk(" - checksum [2] = %x\n" , (uint32_t)ip_checksum ); nolock_printk(" - src_addr [4] = %x\n" , (uint32_t)ip_src_addr ); nolock_printk(" - dst_addr [4] = %x\n" , (uint32_t)ip_dst_addr ); // get UDP / TCP fields if ( ip_protocol == PROTOCOL_UDP ) { uint16_t udp_src_port = ((uint16_t)buf[34] << 8) | ((uint16_t)buf[35] ) ; uint16_t udp_dst_port = ((uint16_t)buf[36] << 8) | ((uint16_t)buf[37] ) ; nolock_printk("***** UDP header *****\n"); nolock_printk(" - src_port [2] = %d\n" , (uint32_t)udp_src_port ); nolock_printk(" - dst_port [2] = %d\n" , (uint32_t)udp_dst_port ); } else if( ip_protocol == PROTOCOL_TCP ) { uint16_t tcp_src_port = ((uint16_t)buf[34] << 8) | ((uint16_t)buf[35] ) ; uint16_t tcp_dst_port = ((uint16_t)buf[36] << 8) | ((uint16_t)buf[37] ) ; uint32_t tcp_seq_num = ((uint32_t)buf[38] << 24) | ((uint32_t)buf[39] << 16) | ((uint32_t)buf[40] << 8) | ((uint32_t)buf[41] ) ; uint32_t tcp_ack_num = ((uint32_t)buf[42] << 24) | ((uint32_t)buf[43] << 16) | ((uint32_t)buf[44] << 8) | ((uint32_t)buf[45] ) ; uint8_t tcp_hlen = buf[46]; uint8_t tcp_flags = buf[47]; uint16_t tcp_window = ((uint16_t)buf[48] << 8) | ((uint16_t)buf[49] ) ; uint16_t tcp_checksum = ((uint16_t)buf[50] << 8) | ((uint16_t)buf[51] ) ; uint16_t tcp_urgent = ((uint16_t)buf[52] << 8) | ((uint16_t)buf[53] ) ; nolock_printk("***** TCP header *****\n"); nolock_printk(" - src_port [2] = %x\n" , (uint32_t)tcp_src_port ); nolock_printk(" - dst_port [2] = %x\n" , (uint32_t)tcp_dst_port ); nolock_printk(" - seq_num [4] = %x\n" , (uint32_t)tcp_seq_num ); nolock_printk(" - ack_num [4] = %x\n" , (uint32_t)tcp_ack_num ); nolock_printk(" - hlen [1] = %d\n" , (uint32_t)tcp_hlen ); nolock_printk(" - flags [1] = %x\n" , (uint32_t)tcp_flags ); nolock_printk(" - window [2] = %x\n" , (uint32_t)tcp_window ); nolock_printk(" - checksum [2] = %x\n" , (uint32_t)tcp_checksum ); nolock_printk(" - urgent [2] = %x\n" , (uint32_t)tcp_urgent ); } else { nolock_printk("!!!!! undefined transport protocol !!!!!\n"); } // release TXT0 lock remote_busylock_release( lock_xp ); } // end dev_nic_packet_display()