diff --git a/src/include/gpxe/udp.h b/src/include/gpxe/udp.h index ed45c183..faad6c2d 100644 --- a/src/include/gpxe/udp.h +++ b/src/include/gpxe/udp.h @@ -18,7 +18,6 @@ * UDP constants */ -#define UDP_HLEN 8 #define UDP_MAX_HLEN 72 #define UDP_MAX_TXPKB ETH_MAX_MTU #define UDP_MIN_TXPKB ETH_ZLEN @@ -29,10 +28,10 @@ typedef uint16_t port_t; * A UDP header */ struct udp_header { - port_t source_port; - port_t dest_port; - uint16_t len; - uint16_t chksum; + port_t source_port; + port_t dest_port; + uint16_t len; + uint16_t chksum; }; struct udp_connection; @@ -59,15 +58,15 @@ struct udp_operations { */ struct udp_connection { /** Address of the remote end of the connection */ - struct sockaddr sin; - /** Local port on which the connection receives packets */ - port_t local_port; - /** Transmit buffer */ - struct pk_buff *tx_pkb; - /** List of registered connections */ - struct list_head list; - /** Operations table for this connection */ - struct udp_operations *udp_op; + struct sockaddr sin; + /** Local port on which the connection receives packets */ + port_t local_port; + /** Transmit buffer */ + struct pk_buff *tx_pkb; + /** List of registered connections */ + struct list_head list; + /** Operations table for this connection */ + struct udp_operations *udp_op; }; /** diff --git a/src/net/udp.c b/src/net/udp.c index 9a6898d7..abfa7595 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -17,7 +17,40 @@ * UDP protocol */ -void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ); +static inline void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ) { + memcpy ( dest, source, sizeof ( *dest ) ); +} + +static inline uint16_t dest_port ( struct sockaddr *sock, uint16_t *dest ) { + switch ( sock->sa_family ) { + case AF_INET: + dest = &sock->sin.sin_port; + break; + case AF_INET6: + dest = &sock->sin6.sin6_port; + break; + default: + DBG ( "Network family %d not supported\n", sock->sa_family ); + return -EAFNOSUPPORT; + } + return 0; +} + +/** + * Dump the UDP header + * + * @v udphdr UDP header + */ +void udp_dump ( struct udp_header *udphdr ) { + + /* Print UDP header for debugging */ + DBG ( "UDP header at %#x + %d\n", udphdr, sizeof ( *udphdr ) ); + DBG ( "\tSource Port = %d\n", ntohs ( udphdr->source_port ) ); + DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) ); + DBG ( "\tLength = %d\n", ntohs ( udphdr->len ) ); + DBG ( "\tChecksum = %d\n", ntohs ( udphdr->chksum ) ); + DBG ( "\tChecksum located at %#x\n", &udphdr->chksum ); +} /** * Open a UDP connection @@ -28,10 +61,11 @@ void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ); * This function stores the socket address within the connection */ void udp_connect ( struct udp_connection *conn, struct sockaddr *peer ) { - copy_sockaddr ( peer, &conn->sin ); - /** - * Not sure if this should add the connection to udp_conns; If it does, uncomment the following code - */ + copy_sockaddr ( peer, &conn->sin ); + + /* Not sure if this should add the connection to udp_conns; If it does, + * uncomment the following code + */ // list_add ( &conn->list, &udp_conns ); } @@ -42,11 +76,11 @@ void udp_connect ( struct udp_connection *conn, struct sockaddr *peer ) { * @v udp_op UDP operations */ void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op ) { - conn->local_port = 0; - conn->tx_pkb = NULL; - if ( udp_op != NULL ) { - conn->udp_op = udp_op; - } + conn->local_port = 0; + conn->tx_pkb = NULL; + if ( udp_op != NULL ) { + conn->udp_op = udp_op; + } } /** @@ -59,12 +93,12 @@ void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op ) { * Allocate "len" amount of space in the transmit buffer */ int udp_buf_alloc ( struct udp_connection *conn, size_t len ) { - if ( conn->tx_pkb != NULL ) { - free_pkb ( conn->tx_pkb ); - conn->tx_pkb = NULL; - } - conn->tx_pkb = alloc_pkb ( len < UDP_MIN_TXPKB ? UDP_MIN_TXPKB : len ); - return !conn ? -ENOMEM : 0; + if ( conn->tx_pkb != NULL ) { + free_pkb ( conn->tx_pkb ); + conn->tx_pkb = NULL; + } + conn->tx_pkb = alloc_pkb ( len < UDP_MIN_TXPKB ? UDP_MIN_TXPKB : len ); + return !conn ? -ENOMEM : 0; } /** @@ -74,45 +108,47 @@ int udp_buf_alloc ( struct udp_connection *conn, size_t len ) { * @v data Data to send * @v len Length of data * - * This function fills up the UDP headers and sends the data. Discover the network protocol to - * use through the sa_family field in the destination socket address. + * This function fills up the UDP headers and sends the data. Discover the + * network protocol through the sa_family field in the destination socket + * address. */ int udp_send ( struct udp_connection *conn, const void *data, size_t len ) { - struct udp_header *udphdr; /* UDP header */ - struct sockaddr *sock = &conn->sin; /* Destination sockaddr */ - int rc; + struct udp_header *udphdr; /* UDP header */ + struct sockaddr *sock = &conn->sin; /* Destination sockaddr */ + uint16_t *dest; + int rc; - /* Copy data into packet buffer if necessary */ - if ( data != conn->tx_pkb->data ) { - /* Allocate a buffer */ - if ( ( rc = udp_buf_alloc ( conn, len + UDP_MAX_HLEN ) ) != 0 ) { - DBG ( "Error allocating buffer" ); - return rc; - } + /* Copy data into packet buffer, if necessary */ + if ( data != conn->tx_pkb->data ) { + /* Allocate space for data and lower layer headers */ + if ( ( rc = udp_buf_alloc ( conn, len + UDP_MAX_HLEN ) ) != 0 ) { + DBG ( "Error allocating buffer" ); + return rc; + } - /* Reserve space for the headers and copy contents */ - pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN ); - memcpy ( pkb_put ( conn->tx_pkb, len ), data, len ); - } + /* Reserve space for the headers and copy contents */ + pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN ); + memcpy ( pkb_put ( conn->tx_pkb, len ), data, len ); + } - /* Add the UDP header */ - udphdr = pkb_push ( conn->tx_pkb, UDP_HLEN ); - if ( sock->sa_family == AF_INET ) - udphdr->dest_port = sock->sin.sin_port; - else - udphdr->dest_port = sock->sin6.sin6_port; - udphdr->source_port = conn->local_port; - udphdr->len = htons ( pkb_len ( conn->tx_pkb ) ); - udphdr->chksum = calc_chksum ( udphdr, UDP_HLEN ); + /* + * Add the UDP header + * + * Covert all 16- and 32- bit integers into network btye order before + * sending it over the network + */ + udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) ); + if ( (rc = dest_port ( sock, dest ) ) != 0 ) { + return rc; + } + udphdr->dest_port = htons ( *dest ); + udphdr->source_port = htons ( conn->local_port ); + udphdr->len = htons ( pkb_len ( conn->tx_pkb ) ); + udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) ); - /* Print UDP header for debugging */ - DBG ( "UDP header at %#x + %d\n", udphdr, UDP_HDR_LEN ); - DBG ( "\tSource Port = %d\n", udphdr->source_port ); - DBG ( "\tDestination Port = %d\n", udphdr->dest_port ); - DBG ( "\tLength = %d\n", udphdr->len ); - DBG ( "\tChecksum = %d\n", udphdr->chksum ); - DBG ( "\tChecksum located at %#x\n", &udphdr->chksum ); + udp_dump ( udphdr ); + /* Send it to the next layer for processing */ return trans_tx ( conn->tx_pkb, IP_UDP, sock ); } @@ -125,13 +161,13 @@ int udp_send ( struct udp_connection *conn, const void *data, size_t len ) { * @v len Length of data */ int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer, - const void *data, size_t len ) { - struct sockaddr tempsock; - copy_sockaddr ( &conn->sin, &tempsock ); - copy_sockaddr ( peer, &conn->sin ); - int rc = udp_send ( conn, data, len ); - copy_sockaddr ( &tempsock, &conn->sin ); - return rc; + const void *data, size_t len ) { + struct sockaddr tempsock; + copy_sockaddr ( &conn->sin, &tempsock ); + copy_sockaddr ( peer, &conn->sin ); + int rc = udp_send ( conn, data, len ); + copy_sockaddr ( &tempsock, &conn->sin ); + return rc; } /** @@ -140,7 +176,7 @@ int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer, * @v conn UDP connection */ void udp_close ( struct udp_connection *conn ) { - list_del ( &conn->list ); + list_del ( &conn->list ); } /** @@ -152,74 +188,90 @@ void udp_close ( struct udp_connection *conn ) { * This does not support the 0 port option correctly yet */ int udp_open ( struct udp_connection *conn, uint16_t local_port ) { - struct udp_connection *connr; - uint16_t min_port = 0xffff; - list_for_each_entry ( connr, &udp_conns, list ) { - if ( connr->local_port == local_port ) { - return -EISCONN; /* CHECK THE ERROR NUMBER */ - } - if ( min_port > connr->local_port ) { - min_port = connr->local_port; - } - } - conn->local_port = local_port == 0 ? min_port > 1024 ? 1024 : min_port + 1 : local_port; // FAULTY!!! - list_add ( &conn->list, &udp_conns ); - return 0; + struct udp_connection *connr; + uint16_t min_port = 0xffff; + + /* Iterate through udp_conns to see if local_port is available */ + list_for_each_entry ( connr, &udp_conns, list ) { + if ( connr->local_port == local_port ) { + return -EISCONN; + } + if ( min_port > connr->local_port ) { + min_port = connr->local_port; + } + } + /* This code is buggy. I will update it soon :) */ + conn->local_port = local_port == 0 ? min_port > 1024 ? 1024 : + min_port + 1 : local_port; + + /* Add the connection to the list of listening connections */ + list_add ( &conn->list, &udp_conns ); + return 0; } /** * Process a received packet * - * @v pkb Packet buffer + * @v pkb Packet buffer * @v src_net_addr Source network address * @v dest_net_addr Destination network address */ void udp_rx ( struct pk_buff *pkb, struct in_addr *src_net_addr __unused, - struct in_addr *dest_net_addr __unused ) { - struct udp_header *udphdr = pkb->data; - struct udp_connection *conn; + struct in_addr *dest_net_addr __unused ) { + struct udp_header *udphdr = pkb->data; + struct udp_connection *conn; + uint16_t ulen; + uint16_t chksum; - /* todo: Compute and verify checksum */ + udp_dump ( udphdr ); - /* Print UDP header for debugging */ - DBG ( "UDP header at %#x + %d\n", udphdr, UDP_HDR_LEN ); - DBG ( "\tSource Port = %d\n", udphdr->source_port ); - DBG ( "\tDestination Port = %d\n", udphdr->dest_port ); - DBG ( "\tLength = %d\n", udphdr->len ); - DBG ( "\tChecksum = %d\n", udphdr->chksum ); - DBG ( "\tChecksum located at %#x\n", &udphdr->chksum ); + /* Validate the packet and the UDP length */ + if ( pkb_len ( pkb ) < sizeof ( *udphdr ) ) { + DBG ( "UDP packet too short (%d bytes)\n", + pkb_len ( pkb ) ); + return; + } - /* Demux the connection */ - list_for_each_entry ( conn, &udp_conns, list ) { - if ( conn->local_port == udphdr->dest_port ) { - goto conn; - } - } - return; + ulen = ntohs ( udphdr->len ); + if ( ulen != pkb_len ( pkb ) ) { + DBG ( "Inconsistent UDP packet length (%d bytes)\n", + pkb_len ( pkb ) ); + return; + } - conn: - /** Strip off the UDP header */ - pkb_pull ( pkb, UDP_HLEN ); + /* Verify the checksum */ + chksum = calc_chksum ( pkb->data, pkb_len ( pkb ) ); + if ( chksum != 0xffff ) { + DBG ( "Bad checksum %d\n", chksum ); + return; + } - /** Allocate max possible buffer space to the tx buffer */ - conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB ); - pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN ); + /* Todo: Check if it is a broadcast or multicast address */ - /** Call the application's callback */ - conn->udp_op->newdata ( conn, pkb->data, ntohs ( udphdr->len ) - UDP_HLEN ); + /* Demux the connection */ + list_for_each_entry ( conn, &udp_conns, list ) { + if ( conn->local_port == ntohs ( udphdr->dest_port ) ) { + goto conn; + } + } + return; + + conn: + /** Strip off the UDP header */ + pkb_pull ( pkb, sizeof ( *udphdr ) ); + + /** Allocate max possible buffer space to the tx buffer */ + conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB ); + pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN ); + + /** Call the application's callback */ + conn->udp_op->newdata ( conn, pkb->data, ulen - sizeof ( *udphdr ) ); } struct trans_protocol udp_protocol = { - .name = "UDP", - .rx = udp_rx, - .trans_proto = IP_UDP, + .name = "UDP", + .rx = udp_rx, + .trans_proto = IP_UDP, }; TRANS_PROTOCOL ( udp_protocol ); - -/** - * Internal functions - */ -void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ) { - memcpy ( dest, source, sizeof ( struct sockaddr ) ); -}