25.3 Sending and Receiving UDP Datagrams

   


The sending of UDP packets, starting from the system call at the socket interface and running all the way until the completed packet is added to the output queue of the network interface, is handled in just one pass, but the receiving of UDP packets requires two separate steps: Once a packet has been received, udp_rcv() first allocates it to a socket in bottom-half context and places it into that socket's receive queue. From there, the packet is fetched via system call of a user process, which is mapped to udp_recvmsg().

Sending UDP Datagrams

udp_sendmsg()

net/ipv4/udp.c


The function udp_sendmsg() is invoked over the socket interface whenever a UDP packet has to be sent: The different kinds of systems calls all lead to this single function's being called. Its parameters are a sock structure with the state of the sending PF_INET socket, a pointer to a msg structure that specifies the receiver and payload, and the payload length in octets.

First, a locally created udpfakehdr structure takes the destination address and the destination port from the msg_name element of the msg structure. The destination doesn't have to be specified explicitly only if a default destination address has previously been assigned to this socket via udp_connect(). In this case, the information from the sock structure is used instead. The source address and the source port always derive from the sock structure. Additionally, they are stored in an ipcm_cookie structure. This structure, which we will not describe here in detail, serves later on to pass the addresses, the device identifier, and the IP options (if applicable) to the Internet Protocol.

Any control messages in the msg_control element of the msghdr structure are processed by calling the function ip_cmsg_send(), and the results are registered in the ipcm_cookie structure. Control messages can be used to modify the source address or to pass IP options, which will then also be registered in the ipcm_cookie structure. If no IP options are specified, then the IP options stored in the sock structure, if applicable, will be used.

A routing cache entry has to be procured, so it is also necessary to handle the source routing IP options beforehand; the address of the first intermediate station might need to be used instead of the destination address.

If the socket had previously obtained a routing cache entry by udp_connect(), and if the corresponding destination address has not yet been changed in the process of udp_sendmsg(), then this routing cache entry is now checked. If this check produces a negative result, or if the destination address was changed, then ip_route_output() is used to procure a new routing cache entry (which is then stored in the sock structure).

Eventually, the transmission of data is initiated by calling ip_build_xmit(), where either udp_getfrag() or udp_getfrag_nosum() is provided as the callback function for getting data, depending on whether the checksum in the packet header should include the payload.[1] The parameters used here also include the udpfakehdr and ipcm structures, the routing cache entry, the flags from the msghdr structure, and the total length of the UDP packet.

[1] A checksum is computed if the flag no_check in the sock structure is null, which is the case by default. This flag can be set via the socket option SO_NO_CHECK on the SOL_SOCKET level.

The following discussion considers only udp_getfrag(), because udp_getfrag_nosum() provides the same functionality, but is simpler for omitting the checksum calculation.

udp_getfrag()

net/ipv4/udp.c


For each IP fragment it generates, ip_build_xmit() invokes the callback function udp_getfrag() to get the required payload. A pointer to the udpfakehdr structure filled by udp_sendmsg() is one of the parameters passed here, in addition to the desired destination address, the fragment offset, and the desired data quantity.

The actual bulk of the work is done by the function csum_partial_copy_fromiovecend(), defined in net/core/iovec.c. This function not only copies the data referenced by the iovec structure directly from the user-address space to the desired location, but also computes the checksum. The result of the checksum calculation is stored in the wcheck element of the updfakehdr structure in each such step. The next step uses this intermediate result as a starting value, so that the checksum eventually extends over the entire data.

The first fragment has to be created last, because it contains the UDP header with the checksum. It is left to ip_build_xmit() to ensure the correct order; udp_getfrag() recognizes the first fragment only by the offset value zero. It extends the checksum calculation to the packet header and the IP pseudo header, and eventually it completes the packet header by inserting the checksum, before it finally copies it to the beginning of the packet.

Receiving UDP Datagrams

udp_rcv()

net/ipv4/udp.c


Once IP has received a UDP packet, it passes this packet in the form of an sk_buff pointer to udp_rcv() for further processing. After the packet length has been checked and the checksum has been computed over the pseudo IP header (if the packet header includes no checksum, or if the checksum was previously computed by the interface hardware, this is registered in the sk_buff), udp_rcv() directly forwards multicast and broadcast packets to udp_mcast_deliver(), which is described further later on.

The most important task is now to allocate the packet to a socket, so that it can be placed into that socket's queue until it is fetched by the user. This task is handled by the function udp_v4_lookup() (or udp_v4_lookup_longway()) invoked by udp_rcv(). This function looks up the udp_hash table and selects the socket with the most specific information with regard to addresses, destination port, and input interface from the sockets linked in the entry and matching the packet.

When a socket has been found, then udp_queue_rcv_skb() is called and in turn invokes sock_queue_rcv_skb(), which inserts the packet into that socket's receive queue. Otherwise, an ICMP error message is generated, provided that the checksum calculation can be completed successfully. No ICMP messages are generated for packets with faulty checksums.

udp_mcast_deliver()

net/ipv4/udp.c


Like udp_v4_lookup(), udp_mcast_deliver() searches for a matching receiver socket. However, it does not select the single socket matching best, but all sockets with a matching destination port and if this information is included with matching addresses and input interface and correspond to the packet. All these sockets receive a copy of the packet. The copies are created by using skb_clone() and delivered as in udp_rcv() by udp_queue_rcv_skb().

udp_recvmsg()

net/ipv4/udp.c


If a user process uses one of the system calls to receive packets, then the socket interface maps it to a call of the function udp_recvmsg(). This function removes an sk_buff structure from the receive queue of the socket passed as a parameter, interprets it as UDP packet, and returns the payload contained (and information from the packet header, if necessary) in the form of an entry in an msghdr structure passed by reference. If the receive queue is currently empty, then either the process has to be set to waiting state, or the call has to be terminated with an appropriate feedback, depending on the specification.

The process of fetching sk_buff structures from receive queues, of waiting for their arrival, and of copying the data are part of not only the UDP, are also required in several other places. Therefore, the file net/core/datagram.c contains generic functions for these purposes, which are used by udp_recv_msg():

  • skb_recv_datagram() serves to fetch an sk_buff. This is done by using skb_dequeue() (or skb_peek(), if the provided flags show that the packet should merely be read, but not be removed). If an sk_buff is actually available, then it is returned; otherwise, if blocking is permitted, the function wait_for_packet() implemented in the same file is invoked. It registers the process as a waiting process with the socket and finally invokes schedule() to temporarily obtain control.

  • skb_copy_datagram_iovec() or skb_copy_and_csum_datagram_iovec() are invoked by udp_recvmsg() to copy the payload from the packet into the msg_iov element of the msghdr structure and to compute and verify the checksum in the process, if applicable.

  • skb_free_datagram() is used to release the sk_buff structure after the address information has been copied in udp_recvmsg() and, for example, IP options have been taken over into a control message (by calling the function ip_cmsg_recv()).


       


    Linux Network Architecture
    Linux Network Architecture
    ISBN: 131777203
    EAN: N/A
    Year: 2004
    Pages: 187

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net