2013年7月8日月曜日

raw パケットの送信

simple raw packet sender source code for testing.

iptablesでDoSのプロテクトテストをしたくても送信できるプログラムの機能が高すぎて、ソースコードをみても面倒な手続きばかりなので、簡単なものを自作した。Linuxなら比較的簡単にrawパケットを投げることができる。
パケット本体は16進ダンプテキストファイルが必要で、簡単に言えば、そのファイルを読んでEthernetPacketとして、指定のインタフェース(eth0)から送信するというもの。オプションで送信MACアドレスの書換、送信IPアドレスの書換、IPヘッダのチェックサムの書換ができます。
パケット本体の16進ダンプの作り方は、、、Wiresharkでパケットをキャプチャーします。1パケットを選択し、右クリックメニュー、copy,bytes,Hex Streamで16進ダンプをクリップボードに入れて、エディタにペーストします。それをファイルに書き込んで、このプログラムのstdinにリダイレクトします。
1パケットは1行で構成してください。/で始まる行はコメント行、;で始まる行もコメント行となります。1行中に 16進文字コード以外の文字は無視されます。 16進は2つで1バイトですので2文字の間に16進文字以外を入れるのはダメです。 例えば
 00 81 98 2A 87 88 00 82 99 81 28        c0,c6,01,22    
このように空白やカンマは自由に入れて読みやすくしておいて構いません。
コメントは別の行で入れることができます。所謂データの先頭はEthernetHeaderでWiresharkで見えているMACアドレスからです。
ファイルには複数行のパケットが記述できます。指定した間隔で送信します。繰り返し回数を指定すると、それぞれのパケットを回数分送信します。起動指定にかかわらず、入力データストリームで待ち時間、送信回数、インターバル時間を指定できます。
w 1000       1000ミリ秒待ちます。
r    10           10回送信します。
n   0             インターバルを0にします。(待ちを入れないで連続送信します)
あんまりデバッグしていないので、バグがあったらごめんなさい。Wiresharkで動作確認しながら使います。必要に応じてプログラムも改造します。
このプログラムをccでコンパイルします。
$ cc raws.c –o raws
Wiresharkでパケットダンプを取って、それをhexdump.txtに保存したら
# ./raws –i –m –e eth1 < hexdump.txt
などとします。
以下、ソースコード。
/*
* ref = http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/raw.7.html
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ether.h>
#include <linux/if_packet.h>
#include <netdb.h>
char buf[20000];
int sendlen;
extern int h_errno;
const char *filename;
int bIp = 0;    // ip check sum
int bUdp = 0;    // udp check sum
int bMac = 0;    // update own mac
int b4 = 0;        // update own ip
int nRep = 1;    // repeat count
int nInt = 1000;    // interval timer (msec)
const char *pdev = "eth0";    // output device(interface)
int bEOF = 0;        // EOF stdin
int nSeq = 0;        // text number counter
int bV = 0;            // verbos message
char logtimes[128];    // message time-stamp work area
#include <sys/time.h>
#include <time.h>
/*
* YY/MM/DD HH:MM:SS.MMM
*/
const char *logtime()
{
    struct timeval tv;
    struct tm * tm;
    gettimeofday(&tv, 0);
    tm =  (struct tm *)localtime(&tv.tv_sec);
    sprintf(logtimes,"%02d/%02d/%02d %02d:%02d:%02d.%03d ",
        tm->tm_year - 100, tm->tm_mon + 1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec,
        (int)(tv.tv_usec/ 1000));
    return logtimes;
}
/*
* stdin ==> 2 char hex. ---> 1 byte
*/
int getHex(FILE *fin)
{
    char c;
    while(1)
    {
        c = getc(stdin);
        if (c == EOF)
            return c;
        if (c == '\n')
            return c;
        if (c == 'w')
        {
            /* w 1000\n  wait 1sec */
            int nW;
            char szW[1024];
            fgets(szW, 1024, stdin);
            nW = atol(szW);
            printf("%s... sleep %dmsec. now\n", logtime(), nW);
            usleep(nW * 1000);
        }
        if (c == 'r')
        {
            /* r 1000\n  change repeat 1000 times */
            int nW;
            char szW[1024];
            fgets(szW, 1024, stdin);
            nRep = atol(szW);
            printf("%s... change repeat counter %d.\n", logtime(), nRep);
        }
        if (c == 'n')
        {
            /* n 10\n  change interval timer 10msec */
            int nW;
            char szW[1024];
            fgets(szW, 1024, stdin);
            nInt = atol(szW);
            printf("%s... change interval timer %dmsec.\n", logtime(), nRep);
        }
        if ((c >= '0') && (c <= '9'))
            return c;
        if ((c >= 'A') && (c <= 'F'))
            return c;
        if ((c >= 'a') && (c <= 'f'))
            return c;
    }
}
/*
* stdin ---> buf (send data)
*/
int  LoadSendData(void)
{
    int nCnt = 0;
    char *pbuf = buf;
    sendlen = 0;
    while(1)
    {
        int c[2];
        c[0] = getHex(stdin);
        if (c[0] == EOF)
        {
            bEOF = 1;
            if (sendlen == 0)
                return;
        }
        if ((c[0] == '/')    || /* line comment */
            (c[0] == ';'))
        {
            char szW[1024];
            fgets(szW, 1024, stdin);
            continue;
        }
        if (c[0] == '\n')
        {
            if (sendlen)
            {
                /* multi text */
                /* printf("\n multi text\n"); */
                break;
            }
            continue;
        }
        c[1] = getHex(stdin);
        if (c[1] == EOF)
            break;
        c[0] = toupper(c[0]);
        c[1] = toupper(c[1]);
        if (c[0] > '9')
            c[0] = c[0] - 'A' + 10;
        else
            c[0] -= '0';
        if (c[1] > '9')
            c[1] = c[1] - 'A' + 10;
        else
            c[1] -= '0';
        *pbuf = (c[0] << 4) | c[1];
        if (bV) printf("%02X ", *pbuf & 0x0ff);
        pbuf++;
        sendlen++;
        nCnt++;
        if (nCnt == 8)
            if (bV) printf(" ");
        if (nCnt == 16)
        {
            if (bV) printf("\n");
            nCnt = 0;
        }
        if (sendlen >= sizeof(buf))
            return;
    }
    if (bV) printf("\nsend length %d byte...\n", sendlen);
}
/*
* calculate check-sum for ip-header
*/
unsigned short csum(unsigned short *buf, int nwords)
{
       unsigned long sum;
       for(sum=0; nwords>0; nwords--)
    {
           sum += *buf++;
    }
       sum = (sum >> 16) + (sum &0xffff);
       sum = (sum >> 16) + (sum &0xffff);
       return (unsigned short)(~sum);
}
/*
*  modify data, send data.
*  return 1: normal end
*         0: error
*/
int SendRawData(void)
{
    int sockfd;
    struct ifreq if_idx;
    struct ifreq if_mac;
    struct ifreq if_ip;
    struct ether_header *eh = (struct ether_header *) buf;
    struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
    struct udphdr *udph =
        (struct udphdr *) (buf + sizeof(struct iphdr) + sizeof(struct ether_header));
    struct sockaddr_ll socket_address;
    /* Open RAW socket to send on */
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1)
    {
        perror("socket error");
        return 0;
    }
    memset(&if_idx, 0, sizeof(struct ifreq));
    strncpy(if_idx.ifr_name, pdev, IFNAMSIZ-1);   
    if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
    {
            perror("SIOCGIFINDEX error");
        return 0;
    }
    /* get mac */
    memset(&if_mac, 0, sizeof(struct ifreq));
    strncpy(if_mac.ifr_name, pdev, IFNAMSIZ-1);
    if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
    {
            perror("SIOCGIFHWADDR get MAC error");
        return 0;
    }
    /* get ip */
    memset(&if_ip, 0, sizeof(struct ifreq));
    strncpy(if_ip.ifr_name, pdev, IFNAMSIZ-1);
    if (ioctl(sockfd, SIOCGIFADDR, &if_ip) < 0)
    {
        perror("SIOCGIFADDR  get IP error");
        return 0;
    }
    /* OWN mac  addr update ? */
    if (bMac)
    {
        eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0];
        eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1];
        eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2];
        eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3];
        eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4];
        eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5];
        if (bV)
            printf("own MAC=%02x:%02x:%02x:%02x:%02x:%02x \n",
                eh->ether_shost[0] & 0x0ff,
                eh->ether_shost[1] & 0x0ff,
                eh->ether_shost[2] & 0x0ff,
                eh->ether_shost[3] & 0x0ff,
                eh->ether_shost[4] & 0x0ff,
                eh->ether_shost[5] & 0x0ff);
    }
    /* Own IP addr update ? */
    if (b4)
    {
        iph->saddr = inet_addr(inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
        if (bV)
            printf("own ip=%d.%d.%d.%d \n",
                (htonl(iph->saddr) >> 24) & 0x0ff,
                (htonl(iph->saddr) >> 16) & 0x0ff,
                (htonl(iph->saddr) >> 8) & 0x0ff,
                (htonl(iph->saddr)) & 0x0ff);
    }
    /* check sum IP calculate ?*/
    if (bIp)
    {
        iph->check = 0;
        iph->check = csum((unsigned short *)(buf+sizeof(struct ether_header)), sizeof(struct iphdr)/2);
        if (bV) printf("ip check-sum=%04X\n", iph->check);
    }
    /* Index of the network device */
    socket_address.sll_ifindex = if_idx.ifr_ifindex;
    /* Address length*/
    socket_address.sll_halen = ETH_ALEN;
    /* Destination MAC copy from send data */
    socket_address.sll_addr[0] = eh->ether_dhost[0];
    socket_address.sll_addr[1] = eh->ether_dhost[1];
    socket_address.sll_addr[2] = eh->ether_dhost[2];
    socket_address.sll_addr[3] = eh->ether_dhost[3];
    socket_address.sll_addr[4] = eh->ether_dhost[4];
    socket_address.sll_addr[5] = eh->ether_dhost[5];
    if (sendlen)
    {
        int i;
        int nR = 1;
        nSeq++;
        printf("--------------\n");
        for (i = 0; i < nRep; i++)
        {
            if (sendto(sockfd, buf, sendlen,
                    0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
            {
   
                printf("Send error \n");
                return 0;       
            }
            printf("%4d-(%4d) %s ........ sent %dbyte\n",nSeq, nR++,  logtime(), sendlen);
            if (nInt)
                usleep(nInt * 1000);
        }
        printf("--------------\n");
    }
    close(sockfd);
    return 1;
}
/*
* raws (raw packet sender) main
*
*/
int main( int argc, char **argv)
{
    char c;
    while(( c = getopt(argc, argv, "e:ium4n:r:h")) != -1)
    {
        switch(c)
        {
        case 'h':
            printf(" raws (send raw ip packet from file-stream)\n");
            printf(" -e xxx9     :  net-interface [eth0]\n");
            printf(" -i          :  update ip check-sum [no-update]\n");
            printf(" -m          :  update own MAC address \n");
            printf(" -4          :  update own IP address \n");
            printf(" -v          :  verbos \n");
            printf(" -n 9999     :  interval timer between each text[1000] \n");
            printf(" -r 9999     :  repead sending count each text[1] \n");
            printf("  < filename :  hex-dump data from stdin\n");
            printf(" -h          :  this message\n");
            printf(" e.g.\n");
            printf("  raws -i <abc.dat :  send abc.dat to 'eth0' with update c-sum\n");
            printf(" e.g.\n");
            return 0;
        case 'e':
            pdev = optarg;
            break;
        case 'i':
            bIp = 1;
            break;
        case '4':
            b4 = 1;
            break;
        case 'm':
            bMac = 1;
            break;
        case 'v':
            bV = 1;
            break;
        case 'n':
            nInt = atol(optarg);
            break;
        case 'r':
            nRep = atol(optarg);
            break;
        }
    }
    if (bV)
        printf ("e:%s %s %s %s interval=%d repeat=%d stdin\n",
            pdev, bIp ? "IP-check-sum": "", bMac ? "Own-MAC" : "", b4 ? "Own-IP": "", nInt, nRep);
    // load send data
    while(!bEOF)
    {
        LoadSendData();
        if (sendlen)
        {
            if (!SendRawData())
                break;   
        }
        usleep(nInt * 1000);
    }
    if (bV)
        printf(" .... raws program stopped.\n");
    return 1;   
}

0 件のコメント:

コメントを投稿