How to Parse a Raw UDP Header in C

Parsing a raw UDP header in C involves capturing a raw network packet buffer, identifying the byte offset where the UDP segment begins, and mapping that memory directly onto a standard UDP header structure. This guide explains the layout of a UDP header, how to map binary packet data into C structures, handle endianness conversions, and access fields such as source and destination ports, payload length, and checksums.

The UDP Header Structure

The User Datagram Protocol (UDP) header is fixed at 8 bytes (64 bits) in length and consists of four 16-bit fields:

  1. Source Port (16 bits): Port number of the sender.
  2. Destination Port (16 bits): Port number of the receiver.
  3. Length (16 bits): Total length of the UDP header plus payload in bytes (minimum 8 bytes).
  4. Checksum (16 bits): Error-checking field covering the header, payload, and an IP pseudo-header.

Defining the UDP Header in C

Standard Linux and Unix environments provide predefined structures in <netinet/udp.h>. The standard structure is struct udphdr:

#include <netinet/udp.h>

/*
struct udphdr {
    u_int16_t uh_sport; // Source port
    u_int16_t uh_dport; // Destination port
    u_int16_t uh_ulen;  // UDP length
    u_int16_t uh_sum;   // Checksum
};
*/

Alternatively, you can define a custom packed structure:

#include <stdint.h>

struct custom_udphdr {
    uint16_t source_port;
    uint16_t dest_port;
    uint16_t length;
    uint16_t checksum;
} __attribute__((packed));

Parsing the Header from a Raw Buffer

When receiving packets via raw sockets (AF_INET, SOCK_RAW, IPPROTO_UDP or AF_PACKET), the raw buffer contains lower-layer protocol headers (like Ethernet and IPv4) preceding the UDP header.

To reach the UDP header: 1. Extract the IP header to determine its length (using the ihl field). 2. Offset the buffer pointer by the IP header size. 3. Cast the resulting pointer to struct udphdr *. 4. Convert 16-bit fields from Network Byte Order (Big Endian) to Host Byte Order using ntohs().

Complete Implementation

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

void parse_udp_packet(const unsigned char *buffer, size_t buffer_len) {
    if (buffer_len < sizeof(struct iphdr)) {
        printf("Packet too short to contain an IP header.\n");
        return;
    }

    // 1. Parse the IPv4 Header
    const struct iphdr *ip_header = (const struct iphdr *)buffer;
    size_t ip_header_len = ip_header->ihl * 4;

    if (buffer_len < ip_header_len + sizeof(struct udphdr)) {
        printf("Packet too short to contain a UDP header.\n");
        return;
    }

    // Ensure the protocol is UDP
    if (ip_header->protocol != IPPROTO_UDP) {
        printf("Not a UDP packet.\n");
        return;
    }

    // 2. Locate and Map the UDP Header
    const struct udphdr *udp_header = (const struct udphdr *)(buffer + ip_header_len);

    // 3. Convert Byte Order and Extract Fields
    uint16_t src_port = ntohs(udp_header->uh_sport);
    uint16_t dst_port = ntohs(udp_header->uh_dport);
    uint16_t udp_len  = ntohs(udp_header->uh_ulen);
    uint16_t checksum = ntohs(udp_header->uh_sum);

    // 4. Access the Payload
    const unsigned char *payload = buffer + ip_header_len + sizeof(struct udphdr);
    size_t payload_len = udp_len - sizeof(struct udphdr);

    // Display Header Information
    printf("--- UDP Header ---\n");
    printf("Source Port:      %u\n", src_port);
    printf("Destination Port: %u\n", dst_port);
    printf("Length:           %u bytes\n", udp_len);
    printf("Checksum:         0x%04X\n", checksum);
    printf("Payload Size:     %zu bytes\n", payload_len);
}

int main() {
    // Simulated raw IP+UDP packet (IPv4 header + UDP header + 4-byte payload)
    unsigned char raw_packet[] = {
        // IPv4 Header (20 bytes)
        0x45, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00,
        0x40, 0x11, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01,
        0x7F, 0x00, 0x00, 0x01,
        // UDP Header (8 bytes): SrcPort=8080, DstPort=9090, Len=12, Checksum=0x0000
        0x1F, 0x90, 0x23, 0x82, 0x00, 0x0C, 0x00, 0x00,
        // Payload (4 bytes: "TEST")
        0x54, 0x45, 0x53, 0x54
    };

    parse_udp_packet(raw_packet, sizeof(raw_packet));
    return 0;
}

Key Considerations