为什么sendto()返回“无效参数”?

时间:2018-11-28 17:35:07

标签: c sockets networking packet bsd

我编写了一个微型测试程序,以便通过原始套接字发送自己的数据包。
我想从头开始创建数据包。
目标操作系统是FreeBSD / Mac OSX x86_64。
我的编译器是带有Apple LLVM 10的gcc。
我使用sudo权限运行该程序。

由于某种原因,sendto()总是返回“无效参数”错误,我不知道为什么。我很想解决这个问题。

我设置IP_HDRINCL标志并通过bind调用绑定到特定的网络接口。但是,sendto似乎并​​不满意它作为数据包接收到的内容。

无论如何,这是到目前为止我的代码的样子:

#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;
}

不用担心错误检查,由于这段代码已经很大,所以我省去了。

这是结果之一:

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

编辑

我正在将sockaddr_dl结构转换为sockaddr。它们都在其头文件中定义,如下所示:

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 */
};

2 个答案:

答案 0 :(得分:1)

socket(PF_INET, SOCK_RAW, IPPROTO_RAW);使您可以访问IP级别。要访问以太网级别,在Linux中,您需要使用PF_PACKET并获得类型为socket(PF_PACKET, SOCK_RAW, SOCK_DATAGRAM);的套接字。

恐怕您是在IP数据报的顶部格式化以太网数据包。有关此类套接字的文档,请参见 packet(7)

此外,检查每个系统调用中的错误绝对是一个好主意。这可能是您了解底层情况的好方法。

此外,如果您向您发送数据包(目标以太网地址与源以太网地址相同),您将永远无法将数据包退还给您。在发送此类数据包之前,软件应检测到目标地址和源地址相同。由于所有系统都试图不占用带宽,因此通常以太网卡会广播您的数据包。但是,没有太多的以太网在发送数据包时侦听以太网。您可以通过这种行为来欺骗自己,但那是做事的常见方式(不要发送指向您的数据包)。

最可能导致EINVAL错误的原因可能是您将-1作为套接字参数,只是因为您假设您的socket(2)调用正常,但是没有成功t,您认为您不需要在那里检查错误。但是,您正在格式化IP pdu的有效负载中的以太网数据包。有很多事情可能会出错。您传递给套接字接口的sockaddr可能无效,长度错误,内部格式错误。所有这些都脱离了无效的协议系列,这使您收到错误的套接字类型,甚至更糟的是错误。

另一件事:不要犹豫发布complete (with all the headers you used) and out of the box verifiable example。这是必须遵循的。您可能使用了错误的头文件,并且可能是来源或错误,但我们不知道是否在发布前将其剪切了。如果甚至添加用于构建示例的Makefile,也会更好。这样没人会说好吧,我尝试了您的示例代码,但它甚至没有编译。您可以将工作保存给我们所有人,这些工作必须首先将您的代码修复为某种可用的代码,可能会以这种方式纠正您的错误。您在解释中假设自己做对了所有事情,因此您认为不需要发布无用的数据(例如头文件等),并且在错误之上添加了错误,从而完全不可能调试代码。

答案 1 :(得分:1)

在计算校验和有问题之后,我终于找到了使用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

校验和功能

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;
}

我们将在主要功能中创建数据包。

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);

我们现在搜索同时可用可读可写的可用 bpf 设备。

    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);
    }

现在,还可以禁用源MAC地址的自动报头完成。为此,请查看 bpf IOCTL (尤其是BIOCGHDRCMPLT,它是 set 的对应对象)。

以太网头结构

    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.

当时,这仅适用于本地地址。我们不检查指定的IP地址是否是子网的一部分。

    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标头构造

    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标头构造

    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 = 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;
}