Receiving a Packet in UDP - Linux

we saw how protocols register with the AF_ INET family by calling the inet_add_protocol function in linux /net /ipv4 /protoco l.c. This initialization step happens when the AF_ INET initialization function, inet_ init, in the file linux /net /ipv4 /af_ inet.c, runs at kernel startup time. In the inet_ protocol structure for UDP, the value in the protocol field is IPPPROTO_ UDP, and the function defined in the handler field is udp_ rcv. This is the function that gets called for all incoming UDP packets passed up to us by IP.

UDP Receive Handler Function, udp_rcv

Udp_rcv in file linux /net /ipv4 /udp.c is the first function in UDP that sees a UDP input packet after IP is done with it. This function executed when the protocol field in the IP header is IPPPROTO_ UDP, or the value 17.

int udp_rcv(struct sk_buff *skb) {

The variable, sk, will point to the socket that gets this packet if there is a socket open on this port. Uh points to the UDP header. Rt gets the routing table entry from the destination cache.

The function pskb_ may_ pull is called to confirm that there is sufficient space in the socket buffer to hold the UDP header. Uh points to the UDP header, and ulen is the value of the length field in the UDP header.

We check to ensure that the skb is sufficiently long to contain a complete UDP header. Pskb_trim checks the packet length in the process of setting tail to point to the end of the UDP header.

The UDP checksum is begun. The ip_summed field in skb is set depending on whether it is necessary to calculate the checksum.

The routing table entry for this incoming packet is checked to see if it was sent to a broadcast or multicast address. If so, we call udp_v4_mcast_deliver to complete the processing.

if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) return udp_v4_mcast_deliver(skb, uh, saddr, daddr);Udp_v4_lookup determines if there is an open socket on the UDP port in this packet’s header by searching the UDP hash table. If there is an open socket, the packet is passed on to the socket’s receive queue and we are done.

If the return value is greater than zero, we must tell the caller to resubmit the input packet.

If there is no socket for this packet, the checksum calculation is completed. The packet is dropped silently if there is a checksum error. If it is a valid packet but has been sent to a port for which there is no open socket, it is processed as a port- unreachable packet. The UdpNoPorts count is incremented, an ICMP port- unreachable message is sent to the sending machine, and the packet is freed. We are done.

At this point, all that is left is to process the bad packet errors detected earlier.

Even though the packet is discarded silently, we still increment the UDP error statistics.

Receiving Multicast and Broadcast Packets in UDP

Multicast and broadcast packets are sent to multiple destinations. In fact, there may be multiple destinations in the same machine. When UDP receives a multicast or broadcast packet, it checks to see if there are multiple open sockets that should receive the packet. When the routing table entry for the incoming packet has the multicast or broadcast flags set, the UDP receive function, udp_ rcv, calls the UDP multicast receive function, udp_ v4_ mcast_deliver in fil linux /net /ipv4 /udp.,c to distribute the incoming packet to all valid listening sockets.

First, we lock the UDP hash table. Then, we select the first socket in the hash table that is open on a port matching the destination port in the header of the incoming packet.

We loop through the hash table checking each potential matching entry. When an appropriate listening socket is found, the socket buffer, skb, is cloned and the new buffer is put on the receive queue of the listening socket by calling udp_ queue_ rcv_ skb, which is the UDP backlog receive function. If there is no match, the packet is silently discarded. There is no statistics counter to increment for discarded multicast and broadcast received packets.

The comments say that we should be reprocessing packets instead of dropping them here. However, as of this kernel revision, we are not.

UDP Hash Table

As we know from earlier chapters, an application opens a socket of type SOCK_DGRAM to receive UDP packets. This type of socket may listen on a variety of address and port combinations. For example, it may want to receive all packets sent from any address but with a particular destination port, or it may want to receive packets sent to multicast or broadcast addresses. A packet arriving in the udp_rcv function may be passed on to multiple listening sockets. It is not sufficient for UDP to determine which socket or sockets should receive the packet solely by matching the destination port. To determine which socket should get an incoming packet, Linux uses a UDP hash table, udp_hash, defined in the file udp.c.

struct sock *udp_hash[UDP_HTABLE_SIZE];

The hash table can contain up to 128 slots, and each location in the hash table points to a list of sock structures. The hash value used for the table index is calculated from the lowest 7 bits of the UDP port number.

In linux/net/ipv4/udp.c, Udp_v4_lookup is a utility function that looks up a socket in the hash table based on matching source and destination ports, source and destination addresses, and network interface. It locks the hash table and calls udp_v4_lookup_longway to do the real work.

The next function, Udp_v4_lookup_longway, is called from udp_v4_lookup. It is declared in the file linux /net /ipv4 /udp.c. It tries to find the socket as best it can. Minimally, it matches the destination port number. Then, it tries to refine the choice further by matching the source address, destination address, and the incoming network interface.

Our idea here is to find the best match of the incoming packet with an open socket. First, we check to make sure that it is not an IPv6-only socket. As discussed in, “Linux Sockets,” IPv6 sockets may generally be used for IPv4. In this section of code, we find the first matched socket, and this is the one that gets the packet.

Now we check for a matching destination address.

We check for a matching port. Finally, we check to see if the input network interface matches (but only if it is bound). UDP Backlog Receive

The function, udp_queue_rcv_skb, in the file linux/net/ipv4/udp.c is the backlog receive function for UDP, called from the "bottom half" when the socket is held by the user and can’t accept any more data. This function is initialized at compile time into the backlog_rcv field of the UDP proto structure, udp_prot. As we saw, udp_queue_rcv_skb is called by udp_rcv to complete input packet processing, and its purpose is mainly to place incoming packets on the socket’s receive queue and increment the UDP input statistics.

If we have an encapsulated socket, the incoming packet must be encapsulated. If it is, we transform the input packet. If not, we assume it is an ordinary UDP packet and we fall through.

Here, we process the ESP packet.

Now, we finish calculating the checksum.

Next, we call the socket-level receive function, sock_queue_rcv_skb, which places the packet on the socket’s receive queue and wakes up the socket so the application can read the data. If there is insufficient room on the receive queue, the error statistics are incremented and the packet is silently discarded.

if (sock_queue_rcv_skb(sk,skb)<0) {

If the socket returned an error, we increment the statistics and get out.

Once we increment the counter, we are done. At this point, the socket-level processing will allow the user-level read to complete.

All rights reserved © 2020 Wisdom IT Services India Pvt. Ltd Protection Status

Linux Topics