使用原始套接字通过组播将欺骗性数据包发送到不同子网时无输出

时间:2014-07-17 23:45:32

标签: c sockets multicast packet subnet

我的应用程序是一个专用的用户空间UDP路由器,它使用原始套接字在发送的数据包的IP数据包标头中生成具有实际或欺骗源地址的单播和多播输出。

简而言之,输出的一个排列无法到达某些目标主机。

我的问题:Linux堆栈中是否有一些微妙的逻辑可以阻止某些路由排列,或者我使用的输出函数是否缺少某些内容?

我无法创建一个UDP数据包写程序,成功发送(1)包含欺骗源地址的数据包,(2)到多播地址,(3)如果发送主机在子网A上,(4)如果(有效)欺骗源地址在子网A上,则接收主机位于子网B上。

子网A = 10.200.xx.nn 子网B = 10.200.yy.nn

数据包中的源地址=发送主机

                           Subnet                
Src addr = sender addr  A   A   B   B   
Receiver                A   B   A   B   
Result                  Yes Yes Yes Yes 

数据包中的源地址!=发送主机

                                 Subnet                
Sender                  A   A   A   A   B   B   B   B    
Spoofed src addr        A   A   B   B   A   A   B   B
Receiver                A   B   A   B   A   B   A   B
Result                  Yes Yes Yes No  No  Yes Yes Yes

欺骗地址是指现有的网络设备。

在失败目的地上运行的Wireshark显示没有UDP数据。

我的原始套接字类与我在Stack Overflow上发现的示例程序非常接近,该程序由用户Peter O.在2012年3月11日 - 问题3737612"在Linux上使用C" 。我附上了我的版本 几个增强/修复。我在RedHat 5,gcc 4.1.2上编译并运行它。

我检查了可能相关的/etc/sysctl.conf条目,并将以下内容设置为无可识别的效果:

  sudo /sbin/sysctl -w net.ipv4.conf.default.rp_filter=0
  sudo /sbin/sysctl -w net.ipv4.conf.eth2.rp_filter=0
  sudo /sbin/sysctl -w net.ipv4.conf.lo.rp_filter=0
  sudo /sbin/sysctl -w net.ipv4.conf.all.rp_filter=0

  sudo /sbin/sysctl -w net.ipv4.ip_forward=1

  sudo /sbin/sysctl -w net.ipv4.conf.default.accept_source_route=1
  sudo /sbin/sysctl -w net.ipv4.conf.all.accept_source_route=1

附加了3737612中示例的修改版本。

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <time.h>

//The packet length in byes
#define PCKT_LEN 50

//Date size in bytes
#define DATA_SIZE 15

//PseudoHeader struct used to calculate UDP checksum.
typedef struct PseudoHeader{
    unsigned long int source_ip;
    unsigned long int dest_ip;
    unsigned char reserved;
    unsigned char protocol;
    unsigned short int udp_length;
}PseudoHeader;

// Ripped from Richard Stevans Book
unsigned short ComputeChecksum(unsigned char *data, int len)
{
    long sum = 0;  /* assume 32 bit long, 16 bit short */
    unsigned short *temp = (unsigned short *)data;

    while(len > 1){
    sum += *temp++;
    if(sum & 0x80000000)   /* if high order bit set, fold */
        sum = (sum & 0xFFFF) + (sum >> 16);
    len -= 2;
    }

    if(len)   /* take care of left over byte */
    sum += (unsigned short) *((unsigned char *)temp);

    while(sum>>16)
    sum = (sum & 0xFFFF) + (sum >> 16);

    return ~sum;
}

int BindRawSocketToInterface(int rawsock, char *addr, short int port)
{
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = inet_addr(addr);
    s_addr.sin_port = htons(port);

    if((bind(rawsock, (struct sockaddr *)&s_addr, sizeof(s_addr)))== -1)
    {
    perror("Error binding raw socket to interface\n");
    exit(-1);
    }

    return 1;
}

// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
struct ip *CreateIPHeader(char *srcip, char *destip)
{
    struct ip *ip_header;

    ip_header = (struct ip *)malloc(sizeof(struct ip));

    ip_header->ip_v = 4;
    ip_header->ip_hl = 5;
    ip_header->ip_tos = 0;
    ip_header->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
    ip_header->ip_id = htons(111);
    ip_header->ip_off = 0;
    ip_header->ip_ttl = 111;
    ip_header->ip_p = IPPROTO_UDP;
    ip_header->ip_sum = 0; /* We will calculate the checksum later */
    inet_pton(AF_INET, srcip, &ip_header->ip_src);
    inet_pton(AF_INET, destip, &ip_header->ip_dst);

    /* Calculate the IP checksum now :
    The IP Checksum is only over the IP header */
    ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);

    return (ip_header);
}

// Creates a the UDP header.
struct udphdr *CreateUdpHeader(char *srcport, char *destport )
{
    struct udphdr *udp_header;

    /* Check netinet/udp.h for header definiation */

    udp_header = (struct udphdr *)malloc(sizeof(struct udphdr));

    udp_header->source = htons(atoi(srcport));
    udp_header->dest = htons(atoi(destport));
    udp_header->len = htons(sizeof(struct udphdr) + DATA_SIZE); //TODO: need to specify this
    udp_header->check = htons(0);

    return (udp_header);
}

void CreatePseudoHeaderAndComputeUdpChecksum(struct udphdr *udp_header, struct ip *ip_header, unsigned char *data)
{

    /*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/

    /* Find the size of the TCP Header + Data */
    int segment_len = ntohs(ip_header->ip_len) - ip_header->ip_hl*4;

    /* Total length over which TCP checksum will be computed */
    int header_len = sizeof(PseudoHeader) + segment_len;

    /* Allocate the memory */

    unsigned char *hdr = (unsigned char *)malloc(header_len);

    /* Fill in the pseudo header first */

    PseudoHeader *pseudo_header = (PseudoHeader *)hdr;

    pseudo_header->source_ip = ip_header->ip_src.s_addr;
    pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
    pseudo_header->reserved = 0;
    pseudo_header->protocol = ip_header->ip_p;
    pseudo_header->udp_length = htons(segment_len);


    /* Now copy TCP */

    memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);

    /* Now copy the Data */

    memcpy((hdr + sizeof(PseudoHeader) + 8), data, DATA_SIZE);

    /* Calculate the Checksum */

    udp_header->check = ComputeChecksum(hdr, header_len);

    /* Free the PseudoHeader */
    free(hdr);
}

// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
    int sd, ix;
    char buffer[PCKT_LEN];
    char data[DATA_SIZE];
    int one = 1;
    char interface[100];
    struct ifreq ifr;
    time_t the_time;

    // Source and destination addresses: IP and port
    struct sockaddr_in to_addr;
    const int *val = &one;

    printf("IP Header Size: %u \n", sizeof(struct ip));
    printf("UDP Header Size: %u \n", sizeof(struct udphdr));
    printf("Data Size: %d\n", DATA_SIZE);
    printf("IP Total: %u \n", sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);

    memset(buffer, 0, PCKT_LEN);

    if(argc != 5 && argc != 6)
    {
    printf("- Invalid parameters!!!\n");
    printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port> [interface]\n", argv[0]);
    exit(-1);
    }
    if (argc == 6)
      strcpy(interface, argv[5]);
    else
      strcpy(interface, "eth0");
    printf("Interface:  %s\n", interface);

    // bind() fails in BindRawSocketToInterface() above if it is passed a source address that
    // is not the actual host sending (i.e. spoofing):  "Cannot assign requested address"
    #if 0
    // Create a raw socket with UDP protocol
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
    if(sd < 0)
    {
    perror("socket() error");
    exit(-1);
    }
    else
    printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");

    //Bind the socket to the source address and port.
    BindRawSocketToInterface(sd, argv[1], atoi(argv[2]));
    #else
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
    if(sd < 0)
    {
    perror("socket() error");
    exit(-1);
    }
    else
    printf("socket() - Using SOCK_RAW socket and RAW protocol is OK.\n");
    memset (&ifr, 0, sizeof (ifr));
    snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
    if (ioctl(sd, SIOCGIFINDEX, &ifr))
    {
    perror("ioctl() error ");
    exit(-1);
    }
    if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0)
    {
    perror("setsockopt() error (#2)");
    exit(-1);
    }
    #endif
    // Inform the kernel do not fill up the packet structure. we will build our own...
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) < 0)
    {
    perror("setsockopt() error");
    close(sd);
    exit(-1);
    }
    else
    printf("setsockopt() is OK.\n");

    // The source is redundant, may be used later if needed
    // The address family
    to_addr.sin_family = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr(argv[3]);
    to_addr.sin_port = htons(atoi(argv[4]));

    //Create the IP header.
    struct ip *ip_header = CreateIPHeader(argv[1], argv[3]);
    //Create the UDP header.
    struct udphdr *udp_header = CreateUdpHeader(argv[2], argv[4]);

    printf("Using raw socket and UDP protocol\n");
    printf("Using Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));


    // include a timestamp
    memset(data, ' ', sizeof(data));
    the_time = htonl(time(NULL));
    memcpy(data, &the_time, sizeof(the_time));

    //Compute UDP checksum
    CreatePseudoHeaderAndComputeUdpChecksum(udp_header, ip_header, (unsigned char*)data);

    for (ix = 0; ix < 1000000; ++ix)
    {

      //Copy IP header, UDP header, and data to the packet buffer.
      memcpy(buffer, ip_header, sizeof(struct ip));
      memcpy(buffer + sizeof(struct ip), udp_header, 8 /*sizeof(struct udphdr)*/);
      memcpy(buffer + sizeof(struct ip) + 8, data, DATA_SIZE);

    // length must include header sizes
    #if 0
      if(sendto(sd, buffer, 20/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
    #else
      if(sendto(sd, buffer, sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE, 0, 
            (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
    #endif
      {
    perror("sendto() error");
    break;
      }
      else
      {
          printf("sendto() is OK.\n");
      }
      sleep(1);
    } // loop
    free(ip_header);
    free(udp_header);
    close(sd);
    return 0;
}

0 个答案:

没有答案