11 #include <gpxe/init.h>
12 #include <gpxe/pkbuff.h>
13 #include <gpxe/netdevice.h>
14 #include <gpxe/tcpip_if.h>
22 * List of registered UDP connections
24 static LIST_HEAD ( udp_conns );
27 * Some utility functions
29 static inline void copy_sockaddr ( struct sockaddr *source, struct sockaddr *dest ) {
30 memcpy ( dest, source, sizeof ( *dest ) );
33 static inline uint16_t * dest_port ( struct sockaddr *sock ) {
34 switch ( sock->sa_family ) {
36 return &sock->sin.sin_port;
38 return &sock->sin6.sin6_port;
46 * @v udphdr UDP header
48 void udp_dump ( struct udp_header *udphdr ) {
50 /* Print UDP header for debugging */
51 DBG ( "UDP header at %#x + %d\n", udphdr, sizeof ( *udphdr ) );
52 DBG ( "\tSource Port = %d\n", ntohs ( udphdr->source_port ) );
53 DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) );
54 DBG ( "\tLength = %d\n", ntohs ( udphdr->len ) );
55 DBG ( "\tChecksum = %x\n", ntohs ( udphdr->chksum ) );
56 DBG ( "\tChecksum located at %#x\n", &udphdr->chksum );
60 * Open a UDP connection
62 * @v conn UDP connection
63 * @v peer Destination socket address
65 * This function stores the socket address within the connection
67 void udp_connect ( struct udp_connection *conn, struct sockaddr *peer ) {
68 copy_sockaddr ( peer, &conn->sin );
70 /* Not sure if this should add the connection to udp_conns; If it does,
71 * uncomment the following code
73 // list_add ( &conn->list, &udp_conns );
77 * Initialize a UDP connection
79 * @v conn UDP connection
80 * @v udp_op UDP operations
82 void udp_init ( struct udp_connection *conn, struct udp_operations *udp_op ) {
85 if ( udp_op != NULL ) {
86 conn->udp_op = udp_op;
91 * Allocate space to the UDP buffer
93 * @v conn UDP connection
94 * @v len Length to allocate
97 * Allocate "len" amount of space in the transmit buffer
99 int udp_buf_alloc ( struct udp_connection *conn, size_t len ) {
100 if ( conn->tx_pkb != NULL ) {
101 free_pkb ( conn->tx_pkb );
104 conn->tx_pkb = alloc_pkb ( len < UDP_MIN_TXPKB ? UDP_MIN_TXPKB : len );
105 return !conn ? -ENOMEM : 0;
109 * User request to send data via a UDP connection
111 * @v conn UDP connection
113 * This function allocates buffer space and invokes the function's senddata()
114 * callback. The callback may use the buffer space
116 int udp_senddata ( struct udp_connection *conn ) {
117 conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB );
118 if ( conn->tx_pkb == NULL ) {
119 DBG ( "Error allocating packet buffer of length %d\n",
123 conn->udp_op->senddata ( conn, conn->tx_pkb, pkb_len ( conn->tx_pkb ) );
129 * Transmit data via a UDP connection
131 * @v conn UDP connection
132 * @v data Data to send
133 * @v len Length of data
135 * This function fills up the UDP headers and sends the data. Discover the
136 * network protocol through the sa_family field in the destination socket
139 int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
140 struct udp_header *udphdr; /* UDP header */
141 struct sockaddr *sock = &conn->sin; /* Destination sockaddr */
145 /* Copy data into packet buffer, if necessary */
146 if ( data != conn->tx_pkb->data ) {
147 /* Allocate space for data and lower layer headers */
148 if ( ( rc = udp_buf_alloc ( conn, len + UDP_MAX_HLEN ) ) != 0 ) {
149 DBG ( "Error allocating buffer" );
153 /* Reserve space for the headers and copy contents */
154 pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
155 memmove ( pkb_put ( conn->tx_pkb, len ), data, len );
161 * Covert all 16- and 32- bit integers into network btye order before
162 * sending it over the network
164 udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
165 if ( (dest = dest_port ( sock ) ) == NULL ) {
166 DBG ( "Network family %d not supported\n", sock->sa_family );
167 return -EAFNOSUPPORT;
169 udphdr->dest_port = *dest;
170 udphdr->source_port = conn->local_port;
171 udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
172 udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) );
176 /* Send it to the next layer for processing */
177 return trans_tx ( conn->tx_pkb, &udp_protocol, sock );
181 * Send data to a specified address
183 * @v conn UDP connection
184 * @v peer Destination address
185 * @v data Data to send
186 * @v len Length of data
188 int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer,
189 const void *data, size_t len ) {
190 struct sockaddr tempsock;
191 copy_sockaddr ( &conn->sin, &tempsock );
192 copy_sockaddr ( peer, &conn->sin );
193 int rc = udp_send ( conn, data, len );
194 copy_sockaddr ( &tempsock, &conn->sin );
199 * Close a UDP connection
201 * @v conn UDP connection
203 void udp_close ( struct udp_connection *conn ) {
204 list_del ( &conn->list );
210 * @v conn UDP connection
211 * @v local_port Local port on which to open connection
213 * This does not support the 0 port option correctly yet
215 int udp_open ( struct udp_connection *conn, uint16_t local_port ) {
216 struct udp_connection *connr;
217 uint16_t min_port = 0xffff;
219 /* Iterate through udp_conns to see if local_port is available */
220 list_for_each_entry ( connr, &udp_conns, list ) {
221 if ( connr->local_port == local_port ) {
224 if ( min_port > connr->local_port ) {
225 min_port = connr->local_port;
228 /* This code is buggy. I will update it soon :) */
229 conn->local_port = local_port == 0 ? min_port > 1024 ? 1024 :
230 min_port + 1 : local_port;
232 /* Add the connection to the list of listening connections */
233 list_add ( &conn->list, &udp_conns );
238 * Process a received packet
240 * @v pkb Packet buffer
241 * @v src_net_addr Source network address
242 * @v dest_net_addr Destination network address
244 void udp_rx ( struct pk_buff *pkb, struct in_addr *src_net_addr __unused,
245 struct in_addr *dest_net_addr __unused ) {
246 struct udp_header *udphdr = pkb->data;
247 struct udp_connection *conn;
253 /* Validate the packet and the UDP length */
254 if ( pkb_len ( pkb ) < sizeof ( *udphdr ) ) {
255 DBG ( "UDP packet too short (%d bytes)\n",
260 ulen = ntohs ( udphdr->len );
261 if ( ulen != pkb_len ( pkb ) ) {
262 DBG ( "Inconsistent UDP packet length (%d bytes)\n",
267 /* Verify the checksum */
268 chksum = calc_chksum ( pkb->data, pkb_len ( pkb ) );
269 if ( chksum != 0xffff ) {
270 DBG ( "Bad checksum %d\n", chksum );
274 /* Todo: Check if it is a broadcast or multicast address */
276 /* Demux the connection */
277 list_for_each_entry ( conn, &udp_conns, list ) {
278 if ( conn->local_port == ntohs ( udphdr->dest_port ) ) {
285 /** Strip off the UDP header */
286 pkb_pull ( pkb, sizeof ( *udphdr ) );
288 /** Allocate max possible buffer space to the tx buffer */
289 conn->tx_pkb = alloc_pkb ( UDP_MAX_TXPKB );
290 pkb_reserve ( conn->tx_pkb, UDP_MAX_HLEN );
292 /** Call the application's callback */
293 conn->udp_op->newdata ( conn, pkb->data, ulen - sizeof ( *udphdr ) );
296 struct tcpip_protocol udp_protocol = {
299 .trans_proto = IP_UDP,
303 TCPIP_PROTOCOL ( udp_protocol );