2015-12-16 82 views
2

我想檢測傳入的Internet數據包的IP選項。這是我的代碼。設置setsockopt檢測ip選項

//#define IPPROTO_IP 0 
//#define IP_OPTIONS 68 
#define SENDER_PORT_NUM 53 
#define SENDER_IP_ADDR "127.0.0.1" 
#define true 1 

static void bail(const char *error) 
{ 
    printf("%s: %s\n", error, strerror(errno)); 
    exit(1); 
} 

struct ip_datagram{ 
    unsigned char ver_ihl; 
    unsigned char tos; 
    unsigned short totlen; 
    unsigned short id; 
    unsigned short flags_offs; 
    unsigned char ttl; 
    unsigned char proto; 
    unsigned short checksum; 
    unsigned long src; 
    unsigned long dst; 
    unsigned char payload[1500]; 
};  
int main(){  

    int s,b,sso,gso, i; 
    int contatore = 5; 
    int enabled = 1; 
    char *data, *buffer; 
    int addrlen; 
    struct sockaddr_in addr;  
    struct ip_datagram *ip, *ip_reply; 
    struct icmphdr* icmp;  
    //char *src_addr="93.34.229.111"; 
    char *dst_addr="127.0.0.1"; 

    data = malloc(sizeof(struct ip_datagram)+ sizeof(struct icmphdr)); 
    buffer = malloc(sizeof(struct ip_datagram)+ sizeof(struct icmphdr)); 
    ip = (struct ip_datagram *) (data); 
    icmp = (struct icmphdr*) (data + sizeof(struct ip_datagram)); 

    ip->ver_ihl = 45; 
    //ip->ihl = 15; 
    ip->totlen = sizeof(struct ip_datagram); 
    ip->proto = IPPROTO_ICMP; 
    //ip->src =(inet_addr(src_addr)); 
    ip->dst = inet_addr(dst_addr); 
    ip->checksum = in_cksum((unsigned short *)ip, sizeof(struct  ip_datagram)); 
    icmp->type  = ICMP_ECHO; 
    icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr)); 

    s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
    if(s<0) 
     printf("errore socket\n"); 

    sso = setsockopt(s, IPPROTO_IP, IP_OPTIONS, &enabled, sizeof(enabled)); 
    if(sso<0) 
     bail("errore setsockoptions\n"); 

    gso = (s, IPPROTO_IP, IP_OPTIONS, &enabled, sizeof(enabled)); 
    if(gso<0) 
     bail("errore getsockoptions\n"); 
    else 
     printf("IP_OPTIONS VALUE :%d\n", enabled); 

    addr.sin_family = AF_INET; 
    addr.sin_addr.s_addr = ip->dst; 

    while(contatore>0){ 
     int sent_bytes = sendto(s, (const char *)data, ip->totlen, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)); 

     if(sent_bytes <0){ 
     printf("failed to send packets\n"); 
     return 3; 
     } 
     //printf("Sent %d byte packet from %s to %s\n", ip->totlen, src_addr, dst_addr); 
     printf("Sent %d byte packet to %s\n", ip->totlen, dst_addr); 

     addrlen = sizeof(addr); 

     int bytes = recvfrom(s, buffer, sizeof(struct ip_datagram) + sizeof(struct icmphdr), 0, (struct sockaddr*)&addr, &addrlen); 

     if(bytes<=0){ 
     printf("errore recvfrom\n"); 
     break; 
     } 
     else{ 
     char *cp; 
     ip_reply = (struct ip_datagram*) buffer; 
     cp = (char *)&ip_reply->src; 
     printf("Received %d byte reply from %u.%u.%u.%u:\n", ntohs(ip_reply->totlen), cp[0]&0xff,cp[1]&0xff,cp[2]&0xff,cp[3]&0xff); 
     printf("ID: %d\n", ntohs(ip_reply->id)); 
     printf("TTL: %d\n", ip_reply->ttl); 
     printf("type of service: %.1X\n", ip_reply->tos);   
     printf("IP ADDRESS SOURCE: %.4X\n", htonl(ip_reply->src)); 
     printf("IP ADDRESS DESTINATION: %.4X\n", htonl(ip_reply->dst)); 
     printf("versione + header length: %.1X\n", ip_reply->ver_ihl); 
     contatore--;  
     printf("-----------------\n");   
     printf("****************\n"); 
     } 
    }  
} 

問題是標頭長度是6,但長度應該是20,用於檢測所有ip選項。在setsockopt中,也許啓用變量的類型應該是char,但是當我運行該程序時,我遇到了一些問題:實際上,對於一個char類型,它給了我一個無效參數的錯誤。我也想知道如何使用getsockoption打印這些選項。有什麼問題?我希望我的問題是明確的:)

+0

請正確縮進代碼,以便閱讀。 – dbush

+0

如何使用IP版本並將頭部長度合併爲一個字節?你需要將它們分成4位,它們是有用的。 – Michael

回答

1

您爲您的IP標頭定義的結構可能與IP數據包標頭廣泛兼容,但它不夠好,也不容易使用。

在我的Linux系統中的系統報頭/usr/include/linux/ip.h定義IP報頭是這樣的:

struct iphdr { 
#if defined(__LITTLE_ENDIAN_BITFIELD) 
    __u8 ihl:4, 
      version:4; 
#elif defined (__BIG_ENDIAN_BITFIELD) 
    __u8 version:4, 
      ihl:4; 
#else 
#error "Please fix <asm/byteorder.h>" 
#endif 
    __u8 tos; 
    __be16 tot_len; 
    __be16 id; 
    __be16 frag_off; 
    __u8 ttl; 
    __u8 protocol; 
    __sum16 check; 
    __be32 saddr; 
    __be32 daddr; 
    /*The options start here. */ 
}; 

正如你所看到的,這個適當打破了IP版本和頭部長度,並且爲大或小端目標編譯更安全。

通過這樣的結構定義,您可以在sizeof(struct iphdr)字節之後找到IP選項的開頭,整個IP標頭的大小將爲ip.ihl * 4,其中ip爲struct iphdr

由於ihl是構成IP頭的32位字的數量,而不是頭本身的字節數,所以需要乘以上述4。

您可以查看IPv4 (Wikipedia),其中包含有關IP標頭選項及其格式的一些常規信息,以及IANAs list of IP Options的完整列表,並鏈接到描述每個選項的詳細信息的各種RFC。

+0

好的,我做了修改。現在標題長度是24,我怎樣才能看到ip選項? setsockopt seeting是否適合檢測這些選項? – EmanueleRiso

+0

好的,這意味着你有4個字節的IP選項。不,getsockopt/setsockopt用於套接字,與IP標頭選項無關。我在答案的底部添加了一段文字,其中包含一些鏈接,提供了有關IP標題選項的鏈接,但是我擔心你不得不用手處理數據,我不會覺得有一個簡單的圖書館爲你AFAIK。 – Michael