Почему sendto () возвращает «Неверный аргумент»? - PullRequest
0 голосов
/ 28 ноября 2018

Я написал крошечную тестовую программу для отправки собственного пакета через необработанный сокет.
Я бы хотел создать пакет с нуля.
Целевой ОС является FreeBSD / Mac OSX x86_64.
Мой компилятор - gcc с Apple LLVM 10.
Я запускаю программу с правами 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 голосов
/ 11 декабря 2018

После проблем с вычислением контрольных сумм я наконец нашел решение, используя 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-адреса источника.Для этого взгляните на IOCTL из bpf (особенно BIOCGHDRCMPLT и его set аналог).

EthernetКонструкция заголовка

    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;
}
0 голосов
/ 29 ноября 2018

socket(PF_INET, SOCK_RAW, IPPROTO_RAW); дает вам доступ к уровню IP.Чтобы получить доступ к уровню Ethernet, в Linux вам нужно использовать PF_PACKET и получить сокет типа socket(PF_PACKET, SOCK_RAW, SOCK_DATAGRAM);.

Боюсь, вы форматируете пакет Ethernet поверх IP-дейтаграммы.См. package (7) для документации по этому виду сокетов.

Кроме того, проверка на ошибки в каждый системный вызов , безусловно, хорошая идея.Это может быть хорошим способом для вас понять, что происходит под ней.

Кроме того, если вы отправляете вам пакет (адрес Ethernet ether дест совпадает с адресом ethernet источника), вы никогда не получите свой пакетназад к вам.Программное обеспечение должно обнаружить, что адреса назначения и источника одинаковы перед отправкой такого типа пакета.Поскольку все системы стараются не использовать пропускную способность, обычно сетевые карты будут транслировать ваш пакет.Но не так много локальных сетей слушают локальные сети во время отправки пакетов.Вы можете закончить обманутым таким поведением, но это обычный способ действий (не отправляйте пакет, направленный вам).

Наиболее вероятная причина ошибки EINVAL, этовероятно, вы вставляете -1 в качестве параметра сокета, просто потому, что вы предполагаете, что ваш вызов socket(2) прошел нормально, но это не так, и вы думаете, что вам не нужно проверять ошибки там.Но вы форматируете пакет Ethernet в полезной нагрузке IP-устройства.Есть много вещей, которые могут пойти не так с этим.Возможно, sockaddr, который вы передаете интерфейсу сокета, неверен, неправильной длины, неправильного внутреннего формата.Все это происходит из недопустимого семейства протоколов, из-за чего вы получаете неверный тип сокета или, что еще хуже, ошибку.

Другое дело: не стесняйтесь опубликовать complete (со всемииспользуемые вами заголовки) и из коробки проверяемый пример .Это должно следовать.Вы можете использовать неправильные заголовочные файлы, и это может быть источником или ошибками, но мы не узнаем, если вы их обрежете перед публикацией.Лучше, если вы добавите даже Makefile, использованный для построения примера.Таким образом, никто не может сказать Хорошо, я попробовал ваш пример кода, но он даже не компилирует .И вы сохраняете работу для всех нас, которым необходимо сначала преобразовать ваш код в какой-то полезный код, возможно, исправив вашу ошибку.В своем объяснении вы предполагаете, что все сделали правильно, и считаете, что вам не нужно публиковать неинтересные данные, такие как заголовочные файлы и т. П., И вы добавляете ошибки к ошибкам, делая отладку своего кода абсолютно невозможной.

...