Search code examples
csocketsbsd

Raw Sockets on BSD Operating Systems


I've been writing some sockets code in C. I need modify packet headers and control how they're sent out, so I took the raw sockets approach. However, the code I wrote will not compile on BSD systems (Mac OS X/Darwin, FreeBSD, etc.)

I've done a bunch of research on this and have found that BSD systems can't handle raw sockets the way Linux (or even Windows) does. From what I've read, it seems I need to use bpf (berkley packet filter), but I can't figure out how bpf works or how I would go about using it with raw sockets.

If someone could shed some light on this one, I'd be very excited :D

P.S. I'll even be happy with some source code showing how raw sockets are handled in a BSD environment. It doesn't have to be a guide or explanation. I just want to see how it works.


Solution

  • Using raw sockets isn't hard but it's not entirely portable. For instance, both in BSD and in Linux you can send whatever you want, but in BSD you can't receive anything that has a handler (like TCP and UDP).

    Here is an example program that sends a SYN.

    #include <sys/socket.h>
    #include <sys/types.h>
    
    #include <netdb.h>
    #include <netinet/in.h>
    #include <netinet/tcp.h>
    #include <arpa/inet.h>
    
    #include <err.h>
    #include <stdio.h>
    #include <string.h>
    #include <sysexits.h>
    
    int
    main(int argc, char *argv[])
    {
        int s, rc;
        struct protoent *p;
        struct sockaddr_in sin;
        struct tcphdr tcp;
    
        if (argc != 2)
            errx(EX_USAGE, "%s addr", argv[0]);
    
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = 0;
    
        /* Parse command line address. */
        if (inet_pton(AF_INET, argv[1], &sin.sin_addr) <= 0)
            err(EX_USAGE, "Parse address");
    
        /* Look up tcp although it's 6. */
        p = getprotobyname("tcp");
        if (p == NULL)
            err(EX_UNAVAILABLE, "getprotobyname");
    
        /* Make a new shiny (Firefly) socket. */
        s = socket(AF_INET, SOCK_RAW, p->p_proto);
        if (s < 0)
            err(EX_OSERR, "socket");
    
        memset(&tcp, 0, sizeof(tcp));
    
        /* Fill in some random stuff. */
        tcp.th_sport = htons(4567);
        tcp.th_dport = htons(80);
        tcp.th_seq = 4; /* Chosen by fair dice roll. */
        tcp.th_ack = 0;
        tcp.th_off = 5;
        tcp.th_flags = TH_SYN;
        tcp.th_win = htonl(65535);
    
        rc = sendto(s, &tcp, sizeof(tcp), 0, (struct sockaddr *)&sin,
            sizeof(sin));
    
        printf("Wrote %d bytes\n", rc);
    
        return 0;
    }
    

    Of course, more BSD-specific solutions are available. For instance you could use divert(4) to intercept packets as they traverse your system and alter them.