Why does sendto() return 'Invalid argument'?

1

I wrote a tiny test program in order to send an own packet over a raw socket.
I'd like to create the packet from scratch.
The target OS is FreeBSD/Mac OSX x86_64.
My compiler is gcc with Apple LLVM 10.
I run the program with sudo rights.

For some reason, sendto() always returns an "Invalid argument" error and I don't know why. I'd love to fix that issue.

I set the IP_HDRINCL flag and bound to a specific network interface with a bind call. However, sendto does not seems to be happy with what it receives as packet.

Anyways, this is how my piece of code looks so far:

#include <stdio.h>
#include <stdlib.h>         // EXIT_FAILURE EXIT_SUCCESS
#include <stdbool.h>        // bool
#include <string.h>         // strlen(), memcpy()
#include <sys/socket.h>     // socket()
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>     // IPPROTO_TCP
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <netinet/if_ether.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <netinet/udp.h>


#define DESTMAC     "ed:5b:b6:29:43:d5" 
#define DESTIP      "192.168.178.25" 
#define DESTPORT    23452
#define SRCPORT     23451
#define PKT_SIZ     64

#ifndef AF_PACKET
#   ifdef PF_LINK
#       define AF_PACKET PF_LINK
#   elif defined (AF_LINK)
#       define AF_PACKET AF_LINK
#   endif
#endif



typedef int in_socket_t;

uint16_t udp_checksum(struct udphdr *p_udp_header, size_t len, uint32_t src_addr, uint32_t dest_addr)
{
    const uint16_t *buf = (const uint16_t*)p_udp_header;
    uint16_t *ip_src = (void*)&src_addr, *ip_dst = (void*)&dest_addr;
    uint32_t sum;
    size_t length = len;

    // Calculate the sum
    sum = 0;
    while (len > 1)
    {
        sum += *buf++;
        if (sum & 0x80000000)
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }

    if (len & 1)
        // Add the padding if the packet lenght is odd
        sum += *((uint8_t*)buf);

    // Add the pseudo-header
    sum += *(ip_src++);
    sum += *ip_src;

    sum += *(ip_dst++);
    sum += *ip_dst;

    sum += htons(IPPROTO_UDP);
    sum += htons(length);

    // Add the carries
    while (sum >> 16)
        sum = (sum & 0xFFFF) + (sum >> 16);

    // Return the one's complement of sum
    return (uint16_t)~sum;
}

unsigned short checksum(unsigned short *buf, int _16bitword)
{
    unsigned long sum; 
    for (sum = 0; _16bitword > 0; _16bitword--)
        sum += htons(*(buf)++);
    sum = ((sum >> 16) + (sum & 0xFFFF));
    sum += (sum >> 16);
    return (unsigned short)~sum;
}

int main(int argc, const char **argv)
{
    in_socket_t sock_r;
    struct ifreq ifreq_i = { 0 }, ifreq_c = { 0 }, ifreq_ip = { 0 };
    unsigned char *packet = NULL;
    struct ether_header *eth = NULL;
    struct ifaddrs *ifaddr = NULL;
    unsigned int if_c = 0, pckt_len = 0;
    struct ether_addr *eth_daddr;
    ssize_t send_len;
    const int on_f = 1;

    if ((packet = (unsigned char *) malloc(PKT_SIZ)) == NULL)
    {
        perror("Could not allocate packet memory");
        exit(EXIT_FAILURE);
    }
    memset(packet, 0, PKT_SIZ);

    /// 1. Ethernet Header Construction
    puts("PHASE 1: Ethernet Header Construction");
    eth = (struct ether_header *)packet;

    if ((sock_r = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
    {
        perror("Could not create socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(sock_r, IPPROTO_IP, IP_HDRINCL, &on_f, sizeof(on_f)) == -1)
    {
        perror("Could not request manually including header within data");
        exit(EXIT_FAILURE);
    }

    // Get IF Index
    strncpy(ifreq_i.ifr_name, "en7", IFNAMSIZ - 1);
#if !defined(SIOCGIFNAME)
    if (!(ifreq_i.ifr_intval = if_nametoindex(ifreq_i.ifr_name)))
    {
        fprintf(stderr, "Could not get interface name for interface %s: %s\n", ifreq_i.ifr_name, strerror(errno));
        exit(EXIT_FAILURE);
    }
#else 
#error "Not yet implemented."
#endif

    // Get IF MAC Address.
    strncpy(ifreq_c.ifr_name, ifreq_i.ifr_name, IFNAMSIZ - 1);
#ifndef SIOCGIFHWADDR
    if (getifaddrs(&ifaddr) == -1)
    {
        perror("Could not get interface address");
        exit(EXIT_FAILURE);
    }
    for (; ifaddr->ifa_next; ifaddr = ifaddr->ifa_next, if_c++)
    {
        if (!strcmp(ifaddr->ifa_name, ifreq_c.ifr_name) && ifaddr->ifa_addr && ifaddr->ifa_addr->sa_family == AF_PACKET)
        {
            // Copy the Source (local) NIC MAC address to the packet (ethernet header). It's already in network format.
            memcpy(eth->ether_shost, (unsigned char *) LLADDR((struct sockaddr_dl *) ifaddr->ifa_addr), ETHER_ADDR_LEN);
        }
    }
    freeifaddrs(ifaddr - if_c);
#elif defined(SIOCGIFHWADDR)
    if (ioctl(sock_r, SIOCGIFHWADDR, &ifreq_c) == -1)
    {
        fprintf(stderr, "Could not get MAC address for interface %s: %s\n", ifreq_c.ifr_name, strerror(errno));
        exit(EXIT_FAILURE);
    }
#else
#error "Not yet implemented."
#endif

    // Get IF assigned IP Address.
    strncpy(ifreq_ip.ifr_name, ifreq_c.ifr_name, IFNAMSIZ - 1);
#if defined(SIOCGIFADDR)
    if (ioctl(sock_r, SIOCGIFADDR, &ifreq_ip) == -1)
    {
        fprintf(stderr, "Could not get IP address for interface %s: %s\n", ifreq_ip.ifr_name, strerror(errno));
        exit(EXIT_FAILURE);
    }
#else
#error "Not yet implemented."
#endif
    // Copy the destination NIC MAC address to the packet (ethernet header). 
    // ether_aton converts a human-readable NIC MAC to network format.
    // TODO: Remember that we don't want to have a fixed destination NIC MAC, we'll probably receive it with an ARP request in the future. 
    if ((eth_daddr = ether_aton(DESTMAC)) == NULL)
    {
        perror("Could not convert destination NIC MAC address to network format");
        exit(EXIT_FAILURE);
    }
    memcpy(eth->ether_dhost, eth_daddr->octet, ETHER_ADDR_LEN);

    eth->ether_type = htons(ETHERTYPE_IP);

    // Calculate total packet length. 
    pckt_len += sizeof(*eth);

    printf("Source Host: %s\n", ether_ntoa((const struct ether_addr *)eth->ether_shost));
    printf("Desti. Host: %s\n", ether_ntoa((const struct ether_addr *)eth->ether_dhost));
    printf("Ether. Type: 0x%0x%s\n", ntohs(eth->ether_type), ntohs(eth->ether_type) == 0x800 ? " (IP)" : "");


    if (bind(sock_r, (struct sockaddr *)ifaddr->ifa_addr, sizeof(struct sockaddr)) == -1) 
    {
        perror("Could not bind to specific interface");
        exit(EXIT_FAILURE);
    }


    /// 2. IP Header Construction
    puts("\nPHASE 2: IP Header Construction");
    struct ip *iph = (struct ip *)(packet + pckt_len);
    // printf("IP Header %p (eth hdr siz: %i) begins at %p.\n", packet, pckt_len, packet + pckt_len);
    iph->ip_hl = sizeof(struct ip) >> 2;
    iph->ip_v = IPVERSION;      // 4
    iph->ip_tos = 16;
    iph->ip_id = htons(10201);  // any unique ID.
    iph->ip_ttl = 64;
    iph->ip_p = IPPROTO_UDP;     // UDP (User datagram protocol)
    iph->ip_src.s_addr = ((struct sockaddr_in *)&ifreq_ip.ifr_ifru.ifru_addr)->sin_addr.s_addr;
    if (!inet_aton(DESTIP, &iph->ip_dst)) 
    {
        perror("Could not interpret destination IP address");
        exit(EXIT_FAILURE);
    }

    // Calculate total packet length. 
    pckt_len += sizeof(*iph);

    printf("Header length: %i\n", iph->ip_hl);
    printf("Version      : %i%s\n", iph->ip_v, iph->ip_v == IPVERSION ? " (IPv4)" : "");
    printf("Type of Serv.: %i\n", iph->ip_tos);
    printf("Identificati.: %i\n", ntohs(iph->ip_id));
    printf("Time to live : %i\n", iph->ip_ttl);
    printf("Protocol     : %i%s\n", iph->ip_p, iph->ip_p == IPPROTO_UDP ? " (UDP)" : "");
    printf("Source Addre.: %s%s\n", inet_ntoa(iph->ip_src), " (local IP)");
    printf("Dest. Address: %s\n", inet_ntoa(iph->ip_dst));



    /// 3. UDP Header Construction
    struct udphdr *udph = (struct udphdr *)(packet + pckt_len);
    udph->uh_sport = htons(SRCPORT);
    udph->uh_dport = htons(DESTPORT);
    udph->uh_sum = 0;

    // Calculate total packet length. 
    pckt_len += sizeof(*udph);

    // Actual UDP Payload: 
    packet[pckt_len++] = 0xAA;
    packet[pckt_len++] = 0xBB;
    packet[pckt_len++] = 0xCC;
    packet[pckt_len++] = 0xDD;
    packet[pckt_len++] = 0xEE;

    // Fill out remaining length header fields: 
    // UDP length field
    udph->uh_ulen = htons(pckt_len - sizeof(*iph) - sizeof(*eth));
    // IP length field
    iph->ip_len = htons(pckt_len - sizeof(*eth));

    // Finally, calculate the checksum.
    iph->ip_sum = checksum((unsigned short *)(packet + sizeof(*eth)), sizeof(*iph) / 2);
    udph->uh_sum = udp_checksum(udph, pckt_len, iph->ip_src.s_addr, iph->ip_dst.s_addr);

    struct sockaddr_dl saddr_dl = { 0 };
    memset(&saddr_dl, 0, sizeof(struct sockaddr_dl));
    saddr_dl.sdl_index = ifreq_i.ifr_intval;
    saddr_dl.sdl_family = AF_LINK;
    saddr_dl.sdl_type = IFRTYPE_FUNCTIONAL_WIRED; // APPLE_IF_FAM_ETHERNET
    saddr_dl.sdl_nlen = strlen(ifreq_i.ifr_name);
    saddr_dl.sdl_len = sizeof(struct sockaddr_dl);
    saddr_dl.sdl_alen = ETHER_ADDR_LEN;
    memcpy(saddr_dl.sdl_data, eth_daddr->octet, ETHER_ADDR_LEN);

    puts("\nPHASE 4: SENDING PACKET");
    printf("Packet length: %i\n", pckt_len);
    printf("Interface index: %i; %i\n", saddr_dl.sdl_index, ifreq_i.ifr_intval);

    // Send the packet.
    if ((send_len = sendto(sock_r, packet, PKT_SIZ, 0, (const struct sockaddr *)&saddr_dl, sizeof(saddr_dl))) == -1) 
    {
        perror("Could not send packet");
        exit(EXIT_FAILURE);
    }
    printf("Successfully sent packet with data length: %lu\n", send_len);

    return 0;
}

Don't worry about error checking, I left that out due to the piece of code already being very large.

This is one of the results:

PHASE 1: Ethernet Header Construction
Source Host: 5E:F1:28:36:5E:DB
Desti. Host: ED:5B:B6:29:43:D5
Ether. Type: 0x800

PHASE 2: IP Header Construction
IP Header 0x5fcde63019a0 (eth hdr siz: 14) begins at 0x5fcde63019ae.
Header length: 5
Version      : 4 (IPv4)
Type of Serv.: 16
Identificati.: 10201
Time to live : 68
Protocol     : 17 (UDP)
Source Addre.: 192.168.178.21 (local IP)
Dest. Address: 192.168.178.25

PHASE 4: SENDING PACKET
Interface Index: 7
Packet length: 47
Destination mac: ed:5b:b6:29:43:d5
Could not send packet: Invalid argument

Edit

I'm casting a sockaddr_dl structure to a sockaddr. They're both defined in their header files like following:

struct sockaddr {
    __uint8_t   sa_len;     /* total length */
    sa_family_t sa_family;  /* [XSI] address family */
    char        sa_data[14];    /* [XSI] addr value (actually larger) */
};
struct sockaddr_dl {
    u_char  sdl_len;    /* Total length of sockaddr */
    u_char  sdl_family; /* AF_LINK */
    u_short sdl_index;  /* if != 0, system given index for interface */
    u_char  sdl_type;   /* interface type */
    u_char  sdl_nlen;   /* interface name length, no trailing 0 reqd. */
    u_char  sdl_alen;   /* link level address length */
    u_char  sdl_slen;   /* link layer selector length */
    char    sdl_data[12];   /* minimum work area, can be larger;
                   contains both if name and ll address */
};
c
sockets
networking
packet
bsd
asked on Stack Overflow Nov 28, 2018 by T.Meyer • edited Nov 29, 2018 by T.Meyer

2 Answers

1

socket(PF_INET, SOCK_RAW, IPPROTO_RAW); gives you access to IP level. To access the ethernet level, in linux, you need to use PF_PACKET and get a socket of type socket(PF_PACKET, SOCK_RAW, SOCK_DATAGRAM);.

I'm afraid you are formatting an ethernet packet on top of an IP datagram. See packet(7) for documentation on this kind of sockets.

Also, checking for errors in every system call is definitely a good idea. This can be a good way for you to get what is going on underneath.

Also, if you send a packet to you (dest ethernet address is the same as source ethernet address) you'll never get your packet back to you. The software should detect that the target and source addresses are the same before sending this kind of packet. As all systems try to not consume bandwidth, normally ethernet cards will broadcast your packet out. But not so many ethernets listen to the ethernet while they are sending packets. You can finish deceived by such a behaviour, but that's the common way of doing things (don't send out a packet that is directed to you).

The most probable cause for the EINVAL error, is probably that you are pasing -1 as the socket parameter, just because you assume your socket(2) call went fine, but it didn't, and you think you don't need to check errors there. But you are formatting an ethernet packet in the payload of an IP pdu. There are many things that can go wrong with that. Probably the sockaddr you are passing to the socket interface are invalid, wrong length, wrong internal format. All of this departs from an invalid protocol family, that makes you to receive a wrong socket type, or even worse, an error.

Another thing: don't hesitate to post a complete (with all the headers you used) and out of the box verifiable example. This is a must to follow. You can have used the wrong header files, and that can be a source or errors, but we will not know, if you cut them before posting. Better if you add even the Makefile used to build the example. This way no one can say Well, I tried your example code, but it doesn't even compile. And you save work to all of us, that have first to repair your code into some kind of usable code, probably correcting your mistake in the way. You are assuming in your explanation that you have done all things right, so you think you don't need to post uninteresting data, like header files, or the like, and you are adding mistakes over mistakes, making debugging your code completely impossible.

answered on Stack Overflow Nov 29, 2018 by Luis Colorado • edited Nov 29, 2018 by Luis Colorado
1

After having problems with calculating the checksums, I finally found a solution using Berkeley Packet Filters.

#include <stdio.h>
#include <stdlib.h>         
#include <stdbool.h>        
#include <string.h>         
#include <stdint.h>
#include <sys/socket.h>    
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>    
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <netinet/if_ether.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <netinet/udp.h>

#define INTERFACE   "en0"
#define DESTMAC     "ED:5B:B6:29:43:D5"
#define SRCMAC      "5E:F1:28:36:5E:DB"
#define DESTIP      "192.168.178.27" 
#define DESTPORT    23455
#define SRCPORT     23456

Checksum functions

uint32_t crc32(const void *m, size_t len) 
{
    const unsigned char *message = m;
    size_t i;
    int j;
    unsigned int byte, crc, mask;

    i = 0;
    crc = 0xFFFFFFFF;
    while (i < len) {
        byte = message[i];            // Get next byte.
        crc = crc ^ byte;
        for (j = 7; j >= 0; j--) {    // Do eight times.
            mask = -(crc & 1);
            crc = (crc >> 1) ^ (0xEDB88320 & mask);
        }
        i = i + 1;
    }
    return ~crc;
}
uint32_t eth_cks(const void *data, size_t frame_len)
{
    if (data == NULL) return 0;
    const unsigned char * const eth_frame = data;
    char str[(frame_len - 4) * 2 + 1];      // +1 for null terminator.
    memset(str, 0, (frame_len - 4) * 2 + 1);
    for (int i = 0; i < (frame_len - 4) * 2; i += 2)
        sprintf(&str[i], "%02x", eth_frame[i / 2]);
    size_t len = strlen(str), len2 = (len + 1) / 2;
    unsigned char arr2[len2];
    for (size_t i = 0; i < len; i += 2)
        arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
    return crc32(arr2, len2);
}

uint16_t ip4_cks(const void *vdata, size_t length) 
{
    // Cast the data pointer to one that can be indexed.
    char* data = (char *)vdata;

    // Initialise the accumulator.
    uint64_t acc = 0xffff;

    // Handle any partial block at the start of the data.
    unsigned int offset = ((uintptr_t)data)&3;
    if (offset) 
    {
        size_t count = 4 - offset;
        if (count > length) count = length;
        uint32_t word = 0;
        memcpy(offset + (char *)&word, data, count);
        acc += ntohl(word);
        data += count;
        length -= count;
    }

    // Handle any complete 32-bit blocks.
    char* data_end = data + (length &~3);
    while (data != data_end) 
    {
        uint32_t word;
        memcpy(&word, data, 4);
        acc += ntohl(word);
        data += 4;
    }
    length &= 3;

    // Handle any partial block at the end of the data.
    if (length) 
    {
        uint32_t word = 0;
        memcpy(&word, data, length);
        acc += ntohl(word);
    }

    // Handle deferred carries.
    acc = (acc & 0xffffffff) + (acc >> 32);
    while (acc >> 16) 
        acc = (acc & 0xffff) + (acc >> 16);

    // If the data began at an odd byte address
    // then reverse the byte order to compensate.
    if (offset&1)
        acc = ((acc & 0xff00) >> 8) | ((acc & 0x00ff) << 8);

    // Return the checksum in network byte order.
    return htons(~acc);
}

uint16_t udp_cks(struct udphdr *p_udp_header, size_t len, uint32_t src_addr, uint32_t dest_addr)
{
    const uint16_t *buf = (const uint16_t *)p_udp_header;
    uint16_t *ip_src = (void *)&src_addr, *ip_dst = (void *)&dest_addr;
    uint32_t sum;
    size_t length = len;

    // Calculate the sum
    sum = 0;
    while (len > 1)
    {
        sum += *buf++;
        if (sum & 0x80000000)
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }

    if (len & 1)
        // Add the padding if the packet lenght is odd
        sum += *((uint8_t*)buf);

    // Add the pseudo-header
    sum += *(ip_src++);
    sum += *ip_src;

    sum += *(ip_dst++);
    sum += *ip_dst;

    sum += htons(IPPROTO_UDP);
    sum += htons(length);

    // Add the carries
    while (sum >> 16)
        sum = (sum & 0xFFFF) + (sum >> 16);

    // Return the one's complement of sum
    return (uint16_t)~sum;
}

We'll create the packet in our main function.

int main(int argc, char **argv)
{
    int bpf_f = 0;
    struct ifreq ifreq_ip = { {0} }, ifreq_bpf = { {0} };
    unsigned char *packet;
    struct ether_header *eth;
    struct ifaddrs *ifaddr = NULL;
    unsigned int pckt_len = 0, if_c = 0;
    uint32_t *fcs;

    if (!(packet = malloc(PKT_SIZ)))
    {
        perror("Could not allocate memory for packet");
        exit(EXIT_FAILURE);
    }
    memset(packet, 0, PKT_SIZ);
    fcs = (uint32_t *)(packet + PKT_SIZ - SIZEOF_UINT32_T);

We now search for an available bpf device that is both readable and writable.

    char buf[11] = { 0 };
    for (int ext = 1; ext < 99; ++ext)
    {
        sprintf(buf, "/dev/bpf%i", ext);
        if ((bpf_f = open(buf, O_RDWR)) != -1)
            break;
    }
    if (!bpf_f) 
    {
        fprintf(stderr, "Could not open BPF device.\n");
        exit(EXIT_FAILURE);
    }

    // Bind BPF to physical device.
    // Copy the interface name to the request.
    strcpy(ifreq_bpf.ifr_name, ifaddr->ifa_name);
    if (ioctl(bpf_f, BIOCSETIF, &ifreq_bpf) == -1) 
    {
        perror("Could not bind BPF to physical device");
        exit(EXIT_FAILURE);
    }

Now, one could also disable the automatic header completion of the source MAC address. In order to do so, have a look at the IOCTLs of bpf (especially BIOCGHDRCMPLT and it's set counterpart).

Ethernet Header Construction

    eth = (struct ether_header *)packet;

    // Copy the Source (local) NIC MAC address to the packet (ethernet header). It's already in network format. 
    memcpy(eth->ether_shost, ether_aton(SRCMAC)->octet, ETHER_ADDR_LEN);

    // Copy the destination NIC MAC address to the packet (ethernet header). 
    // ether_aton converts a human-readable NIC MAC to network format.

At the time, this only works for local addresses. We don't check whether the specified IP address is part of the subnet.

    memcpy(eth->ether_dhost, ether_aton(DESTMAC)->octet, ETHER_ADDR_LEN);

    // Specifiy the ethernet overlaying type.
    eth->ether_type = htons(ETHERTYPE_IP);

    printf("Source MAC: %s\n", ether_ntoa((const struct ether_addr *)eth->ether_shost));
    printf("Desti. MAC: %s\n", ether_ntoa((const struct ether_addr *)eth->ether_dhost));
    printf("Eth Type: 0x%x%s\n", ntohs(eth->ether_type), ntohs(eth->ether_type) == ETHERTYPE_IP ? " (IP)" : "");

    // Get IF assigned IP address. 
    strncpy(ifreq_ip.ifr_name, ifreq_bpf.ifr_name, IFNAMSIZ - 1);

    in_socket_t sock; 
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    {
        perror("Could not create socket");
        exit(EXIT_FAILURE);
    }
#if defined(SIOCGIFADDR)
    if (ioctl(sock, SIOCGIFADDR, &ifreq_ip) == -1)
    {
        fprintf(stderr, "Could not get IP address for interface %s: %s\n", ifreq_ip.ifr_name, strerror(errno));
    }
#else 
#error "Not yet implemented"
#endif
    close(sock);

    pckt_len += sizeof(*eth);

IP Header Construction

    struct ip *iph = (struct ip *)(packet + pckt_len);
    iph->ip_hl = sizeof(struct ip) >> 2;
    iph->ip_v = IPVERSION;      // 4
    iph->ip_tos = 16;
    iph->ip_id = htons(10201);  // any unique ID.
    iph->ip_ttl = 64;
    iph->ip_p = IPPROTO_UDP;     // UDP (User datagram protocol)
    iph->ip_src.s_addr = ((struct sockaddr_in *)&ifreq_ip.ifr_ifru.ifru_addr)->sin_addr.s_addr;
    if (!inet_aton(DESTIP, &iph->ip_dst)) 
    {
        perror("Could not interpret destination IP address");
        exit(EXIT_FAILURE);
    }


    // Calculate total packet length. 
    pckt_len += sizeof(*iph);

    printf("Header length: %i\n", iph->ip_hl);
    printf("Version      : %i%s\n", iph->ip_v, iph->ip_v == IPVERSION ? " (IPv4)" : "");
    printf("Type of Serv.: %i\n", iph->ip_tos);
    printf("Identificati.: %i\n", ntohs(iph->ip_id));
    printf("Time to live : %i\n", iph->ip_ttl);
    printf("Protocol     : %i%s\n", iph->ip_p, iph->ip_p == IPPROTO_UDP ? " (UDP)" : "");
    printf("Source Addre.: %s%s\n", inet_ntoa(iph->ip_src), " (local IP)");
    printf("Dest. Address: %s\n", inet_ntoa(iph->ip_dst));

UDP Header Construction

    struct udphdr *udph = (struct udphdr *)(packet + pckt_len);
    udph->uh_sport = htons(SRCPORT);
    udph->uh_dport = htons(DESTPORT);
    udph->uh_sum = 0;

    // Calculate total packet length. 
    pckt_len += sizeof(*udph);

    // Actual UDP Payload: 
    packet[pckt_len++] = 0xAA;
    packet[pckt_len++] = 0xBB;
    packet[pckt_len++] = 0xCC;
    packet[pckt_len++] = 0xDD;
    packet[pckt_len++] = 0xEE;

    // Fill out remaining length header fields: 
    // UDP length field
    udph->uh_ulen = htons(pckt_len - sizeof(*iph) - sizeof(*eth));
    // IP length field
    iph->ip_len = htons(pckt_len - sizeof(*eth));

Checksum calculations

    // Finally, calculate the checksum.
    iph->ip_sum = ip4_cks(packet + sizeof(*eth), sizeof(*iph));
    udph->uh_sum = udp_cks(udph, ntohs(udph->uh_ulen), iph->ip_src.s_addr, iph->ip_dst.s_addr);
    *fcs = eth_cks(packet, PKT_SIZ);

    if (write(bpf_f, packet, PKT_SIZ) == -1) 
    {
        perror("Could not write to bpf");
        exit(EXIT_FAILURE);
    }

    free(packet);
    packet = NULL;
    close(bpf_f);
    return 0;
}
answered on Stack Overflow Dec 11, 2018 by T.Meyer

User contributions licensed under CC BY-SA 3.0