linux下用RAW socket發送syn包

源碼編譯方法:gcc -o syn syn.c


結果:在centos6上成功運行,用tcpdump抓包分析,發送的對端有syn,ack包返回,一切正常。

過程:寫代碼時忘記了對tcph->protocol賦值,計算出得checksum老不對,數據包是成功發出去了,但是對端沒syn,ack包回,查了幾個小時,鬱悶死我~~~

疑問:ip->check爲0是內核會計算ip頭的checksum,但是計算出得結果和我用ip_fast_csum得到的結果不一致,這是爲何?標記一下,有結論再附上。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

#include <netinet/ip.h>
#include <netinet/tcp.h>

/* 數據包最大長度,無負載 */
#define MAX_PKG_SIZE 80 /* ip header = 20 ~ 40, tcp header = 20 ~ 40; max = 80 */
/* 計算TCP校驗和的最大使用長度 */
#define MAX_PSD_SIZE 52 /* psd header = 12, tcp header = 20 ~ 40; max = 52 */
/* 數據包構造參數 */
struct st_args{
    struct sockaddr_in saddr; /* 源地址 */
    struct sockaddr_in daddr; /* 目的地址 */
};

/* 用於計算TCP校驗和的僞頭部 */
struct psdhdr{
    uint32_t saddr; /* ip頭部源地址 */
    uint32_t daddr; /* ip頭部目的地址 */
    uint8_t mbz; /* 補全字段,需爲0 */
    uint8_t protocol; /* ip頭部協議 */
    uint16_t tcpl; /* tcp長度,包括頭部和數據部分 */
};


/*
 * 採用彙編計算ip頭部校驗和
 * @param[in]: iph, ip頭指針; 
 * @param[in]: ihl, ip頭長度(4的倍數)
 *
 * @return 16位校驗和
 * */
static inline uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
{
    unsigned int sum;

    asm("  movl (%1), %0\n"
        "  subl $4, %2\n"
        "  jbe 2f\n"
        "  addl 4(%1), %0\n"
        "  adcl 8(%1), %0\n"
        "  adcl 12(%1), %0\n"
        "1: adcl 16(%1), %0\n"
        "  lea 4(%1), %1\n"
        "  decl %2\n"
        "  jne  1b\n"
        "  adcl $0, %0\n"
        "  movl %0, %2\n"
        "  shrl $16, %0\n"
        "  addw %w2, %w0\n"
        "  adcl $0, %0\n"
        "  notl %0\n"
        "2:"
    /* Since the input registers which are loaded with iph and ihl
       are modified, we must also specify them as outputs, or gcc
       will assume they contain their original values. */
        : "=r" (sum), "=r" (iph), "=r" (ihl)
        : "1" (iph), "2" (ihl)
        : "memory");
    return (uint16_t)sum;
}

/*
 * 計算校驗和
 * @param[in]: buffer, 待計算數據指針
 * @param[in]: size, 數據長度
 *
 * @return 校驗和
 * */
uint16_t csum(uint16_t *buffer, int size)
{
    unsigned long cksum = 0;

    while(size>1)
    {
        cksum += *buffer++;
        size -= sizeof(uint16_t);
    }

    if(size)
    {
        cksum += *(unsigned char*)buffer;
    }

    cksum = (cksum>>16) + (cksum&0xffff);
    cksum += (cksum>>16); 
    
    return (uint16_t)(~cksum);
}
/*
 * 調試用的函數,用於輸出數據
 * */
void data_dump(uint8_t *pdata, int len)
{
    int i = 0;
    printf("len = %d\n", len);
    for(i = 0;  i < len; ++i)
    {
        printf("%02X ", *(pdata + i));
        if((i + 1) % 4 == 0)
            printf("\n");
    }
}

/*
 * 數據包發送函數,只構造了ip頭+tcp頭大小的長度;
 * ip頭和tcp都無選項部分,tcp無負載數據
 * @param[in]: parg, 構造數據包是採用的一些參數
 * 
 * @return -1, 發送失敗;0, 發送成功
 * */
int send_pkg(struct st_args* parg)
{
    uint8_t datagram[MAX_PKG_SIZE] = {0};
    uint8_t psdheader[MAX_PSD_SIZE] = {0};
    
    struct iphdr *iph = (struct iphdr*)datagram;
    struct tcphdr *tcph = (struct tcphdr*)(datagram + sizeof(struct iphdr));
    struct tcp_options *tcpopt = (struct tcp_options*)(datagram + sizeof(struct iphdr)
                                 + sizeof(struct tcphdr));
    struct psdhdr *psdh = (struct psdhdr*)psdheader;
    struct tcphdr *tcph_psd = (struct tcphdr*)(psdheader + sizeof(struct psdhdr));
    
    int sockfd = -1, ret = 0;
    int optval = 1;
    const int *poptval = &optval;
    
    sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sockfd < 0)
    {
        perror("create socket failed!\n");
        goto err_out;
    }
    
    iph->ihl = 5; /* header length, 5 * 4 = 20 Bytes */
    iph->version = 4;   /* version, ipv4 */
    iph->tos = 0; /* type of service, gernarel */
    iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); /* total length, iph + tcph = 32 Bytes */
    iph->id = htons(54321); /* identifcation */
    iph->frag_off = htons(0x02 << 13); /* fragment offset field */
    iph->ttl = 64; /* time to live */
    iph->protocol = 6; /* protocol, tcp */
    iph->check = 0; /* checksum */
    iph->saddr = parg->saddr.sin_addr.s_addr; /* source address */
    iph->daddr = parg->daddr.sin_addr.s_addr; /* dest address */
    
    tcph->source = parg->saddr.sin_port; /* source port */
    tcph->dest = parg->daddr.sin_port; /* dest port */
    tcph->seq = random(); /* current sended packet sequence number */
    tcph->ack_seq = 0; /* expect received next packet sequence number */
    tcph->doff = sizeof(struct tcphdr) / 4; /* data position in the packet */
    tcph->syn = 1; /* syn packet */
    tcph->window = htons(65535); /* size of tcp window, FreeBSD uses this value */
    tcph->check = 0; /* checksum */
    tcph->urg_ptr = 0; /* urgent data position */
    
    psdh->saddr = iph->saddr;
    psdh->daddr = iph->daddr;
    psdh->mbz = 0;
    psdh->protocol = iph->protocol;
    psdh->tcpl = htons(sizeof(struct tcphdr));
    //data_dump(psdheader, sizeof(struct psdhdr));

    memcpy(tcph_psd, tcph, sizeof(struct tcphdr));
    
    tcph->check = csum((uint16_t*)psdheader, sizeof(struct psdhdr) + sizeof(struct tcphdr));
    /* iph->check == 0時, 內核會自動計算校驗和 */
    //iph->check = ip_fast_csum(datagram, iph->ihl);

    if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, poptval, sizeof(optval)) < 0)
    {
        perror("setsockopt failed!");
        goto err_out;
    }
    
    ret = sendto(sockfd, datagram, iph->tot_len, 0, 
            (struct sockaddr*)&(parg->daddr), sizeof(parg->daddr));
    if(ret < 0)
    {
        perror("sendto socket failed!");
        goto err_out;
    }

    //data_dump(datagram, 40);
    close(sockfd);
    return 0;
    
err_out:
    if(sockfd != -1)
        close(sockfd);
    return -1;
}

int main(int argc, char **argv)
{
    struct st_args args;
    
#define MAX_IP_SIZE 16
    uint8_t sip[MAX_IP_SIZE] = "192.168.1.107";
    uint8_t dip[MAX_IP_SIZE] = "192.168.1.1";
    uint16_t sport = 55555;
    uint16_t dport = 80;
    int8_t arg = 0;

    struct option lopts[] = {
        {"saddr", required_argument, 0, 's'},
        {"sport", required_argument, 0, 'p'},
        {"daddr", required_argument, 0, 'd'},
        {"dport", required_argument, 0, 'f'}
    };
    while((arg = getopt_long(argc, argv, "s:p:d:f:", lopts, NULL)) != -1)
    {
        switch(arg)
        {
        case 's':
            memcpy(sip, optarg, MAX_IP_SIZE);
            break;
        case 'p':
            sport = atoi(optarg);
            break;
        case 'd':
            memcpy(dip, optarg, MAX_IP_SIZE);
            break;
        case 'f':
            dport = atoi(optarg);
            break;
        default:
            break;
        }
    }
    
    memset(&args, 0, sizeof(struct st_args));
    inet_pton(AF_INET, sip, (void*)&args.saddr.sin_addr);
    args.saddr.sin_port = htons(sport);

    inet_pton(AF_INET, dip, (void*)&args.daddr.sin_addr);
    args.daddr.sin_port = htons(dport);

    send_pkg(&args);
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章