Post

Raw Sockets

Tips for using raw sockets in C.

Raw Sockets

I began learning about raw sockets in C recently. Here’s a simple raw socket proram in C with comments explaning what’s going on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/** This code shows how to create raw socket in C that listens for any
 * incoming packet of any protocol. Comments added anywhere someone 
 * familiar with C socket programming (but not raw sockets) may have
 * questions.
 * 
 * Assumes you're already familiar with
 * socket programming. For info/intro on socket programming, check out:
 * https://beej.us/guide/bgnet/
 * 
 * Reccomended reading for raw socket programming:
 * - man 7 ip
 * - man packet
 * 
 * The _DEFAULT_SOURCE macro help vscode's intellisense work properly;
 * very handy when working with large struct in network programming.
 * See https://stackoverflow.com/questions/74823127/vscode-c-c-intellisense-issue-with-netinet-ip-h
 */
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif // _DEFAULT_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>

void print_packet_info(const unsigned char *packet, int packet_len) {
    // struct ether_header defined in <netinet/ethernet.h>
    struct ether_header *eth_header = (struct ether_header *)packet;
    // struct ip defined in <netinet/ip.h>

    struct ip *ip_header; // newer version of up struct is the iphdr struct

    // Print MAC addresses
    printf("Source MAC: %s\n", ether_ntoa((struct ether_addr *)eth_header->ether_shost));
    printf("Destination MAC: %s\n", ether_ntoa((struct ether_addr *)eth_header->ether_dhost));

    // Check if the packet is an IP packet
    if (ntohs(eth_header->ether_type) == ETHERTYPE_IP) {
        ip_header = (struct ip *)(packet + sizeof(struct ether_header));
        printf("Source IP: %s\n", inet_ntoa(ip_header->ip_src));
        printf("Destination IP: %s\n", inet_ntoa(ip_header->ip_dst));
    }

    printf("\n");
}

int main() {
    int sock;
    struct sockaddr saddr;
    int saddr_len = sizeof(saddr);
    unsigned char buffer[65536]; // Buffer to hold the packet

    /** Create a raw socket. 
     * `man packet` has helpful info about these socket() params
     * 
     * AF_PACKET:   Specifices socket will be raw.
     * SOCK_RAW:    Packets are not changed before receipt (eg stripped of
     *              link layer header).
     * ETH_P_ALL:   Specifies that all protocols should be received on this
     *              socket (not just UDP, TCP, etc)
     */
    sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock < 0) {
        perror("Socket creation failed");
        return 1;
    }

    // Listen for incoming packets
    while (1) {
        int packet_len = recvfrom(sock, buffer, 65536, 0, &saddr, (socklen_t *)&saddr_len);
        if (packet_len < 0) {
            perror("Packet receive failed");
            close(sock);
            return 1;
        }
        print_packet_info(buffer, packet_len);
    }

    // Cleanup
    close(sock);
    return 0;
}

Resources

If you’re new to C socket programming, I would highly reccomend Beej’s Guide to Network Programming.

To learn more about raw socket programing, read these man pages:

This post is licensed under CC BY 4.0 by the author.