2015-10-09 48 views
2

我有IP地址的列表,存储的是这样的:快速比较IP地址的最佳方法?

char IP_addresses_list[] = { 
    "157.55.130", /* 157.55.130.0/24 */ 
    "157.56.52", /* 157.56.52.0/24 */ 
    "157.12.53", /* 157.12.53.0/24 */ 
    ... 
}; 

我从嗅探到的数据包中的IP地址(铸造struct iphdr *iph = (struct iphdr *)(packet + sizeof(struct ether_header));我把它转换使用inet_ntop的字符串;最后,我比较从列表中的那些用下面的代码数据包的IP地址:

/* 
* input: IP address to search in the list 
* output: 1 if IP address is found in the list, 0 otherwise 
*/ 
int find_IP_addr(char *server) { 
    int ret = 0; 
    int i, string_size1, string_size2; 
    char *copied_server, *copied_const_char; 
    char *save_ptr1, *save_ptr2; 
    char dot[2] = "."; 
    /* Here I store the IP address from the packet */ 
    char first_IPaddr_pkt[4], second_IPaddr_pkt[4], third_IPaddr_pkt[4]; 
    /* Here I store the IP address from the list */ 
    char first_IPaddr_list[4], second_IPaddr_list[4], third_IPaddr_list[4]; 

    string_size1 = strlen(server)+1; 
    copied_server = (char *)malloc(string_size1 * sizeof(char)); 
    strcpy(copied_server, server); 

    /* I store and compare the first three bits of the IP address */ 
    strcpy(first_IPaddr_pkt, strtok_r(copied_server, dot, &save_ptr1)); 
    strcpy(second_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1)); 
    strcpy(third_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1)); 
    printf("tokenized %s, %s and %s\n", first_IPaddr_pkt, second_IPaddr_pkt, third_IPaddr_pkt); 

    /* Now I scan the list */ 
    for (i=0; i<LIST_LENGTH; i++) { 
     /* I copy an address from the list */ 
     string_size2 = strlen(IP_addresses_list[i])+1; // +1 for null character 
     copied_const_char = (char *)malloc(string_size2 * sizeof(char)); 
     strcpy(copied_const_char, IP_addresses_list[i]); 
     /* Let's split the address from the list */ 
     strcpy(first_IPaddr_list, strtok_r(copied_const_char, dot, &save_ptr2)); 
     strcpy(second_IPaddr_list, strtok_r(NULL, dot, &save_ptr2)); 
     strcpy(third_IPaddr_list, strtok_r(NULL, dot, &save_ptr2)); 
     printf("tokenized %s, %s and %s\n", first_IPaddr_list, second_IPaddr_list, third_IPaddr_list); 
     /* I compare the first byte of the address from the packet I got and 
     the first byte of the address from the list: 
     if they are different, there's no reason to continue comparing 
     the other bytes of the addresses */ 
     if (strcmp(first_IPaddr_pkt, first_IPaddr_list) != 0) { 
      continue; 
     } 
     else { 
      if (strcmp(second_IPaddr_pkt, second_IPaddr_list) != 0) { 
       continue; 
     } 
     else { 
      if (strcmp(third_IPaddr_pkt, third_IPaddr_list) != 0) { 
       continue; 
      } 
      else 
       /* All the bytes are the same! */ 
       ret = 1; 
      } 
     } 
     free(copied_const_char); 
    } 
    free(copied_server); 
    return ret; 
} 

我想使这更快捷,无需使用strtokstrcmpmallocfree/usr/include/netinet/ip.h我看到地址是

u_int32_t saddr; 
u_int32_t daddr; 

是否有可能甚至不需要使用inet_ntop第一,也许仅仅比较这两个地址,但他们仍然是u_int32_t比较?

编辑:这里有一个解决方案的例子,谁会读这个问题。

#include <stdio.h> 
#include <sys/types.h> 

int main() { 

    // In the list I have: 104.40.0.0./13 
    int cidr = 13; 
    u_int32_t ipaddr_from_pkt = 1747488105;  // pkt coming from 104.40.141.105 
    u_int32_t ipaddr_from_list = 1747451904; // 104.40.0.0 
    int mask = (-1) << (32 - cidr); 

    if ((ipaddr_from_pkt & mask) == ipaddr_from_list) 
     printf("IP address belongs to the given range!!!\n"); 
    else printf ("failure\n"); 

    return 0; 
} 

感谢iharob也为bsearch提示。

+0

这些都不是有效的IPv4地址,所以我首先将它们转换成的东西,看起来像一个,然后只用标准的网络库 –

+2

这是一个很大的代码仅用于ip地址比较!是的,不要使用'malloc()''free()',没有合理的理由需要'strtok()'。 –

+0

“是否可以比较,甚至不用'inet_ntop'先比较这两个地址,但仍然是'u_int32_t'?为什么**不会**与32位无符号的'int'值相比有效? –

回答

3

我会避免将二进制数据转换为字符串。如果将它们保留为二进制,则很容易比较:

match = (ip & listed_mask) == listed_ip; 

“/ 24”是一个掩码。意味着最高24位是相关的。将其转换为二进制掩码如下:

listed_mask = (-1) << (32 - 24); 
+0

我不擅长面具和逻辑运算符:请问你能解释一下你的代码吗? – elmazzun

+1

/24是一个面具。你用它来计算“listed_mask” - 参见上文。然后你使用掩码来掩盖用&操作的ip不相关的8位。这用零代替无关位。您将结果从您的列表(已被屏蔽)列表中列出到列表中,如果相同,则表示匹配。 – PineForestRanch

1

性能问题与strcmp()无关,malloc()是不必要的。

如果您只使用IPv4地址,则只需要个字符来存储它,以便您可以删除malloc()并将临时存储声明为数组。

但是如果在列表中会有很多的IP地址,这会有一个重要的改进。

首先,您需要对IP地址列表进行排序,然后使用bsearch()来搜索正确的IP。这样的代码将在O(日志(2N))时间比O(N)快得多运行,专为大型N

+0

我的列表确实很长,超过150个IP地址。 'bsearch'可能是一个很好的策略! – elmazzun

-1

最快的方法是将存储地址在字典中,看到this link

+2

[tag:c]中没有词典。只有链接的答案是不鼓励的。 –

+0

呃但链接描述如何实现字典? –

+0

您不会留下链接作为答案。这就是我的答案。 –

0

我在这里的做法是:

  1. 只需使用strncat".0"建立有效的IPv4地址。
  2. 使用getaddrinfo与插座等类型的常数值打造addrinfo结构
  3. 比较addrinfo的相关领域。

基本上,从man getaddrinfo does all this的例子。

+0

我写了“157.55.130”,因为我的意思是说“157.55.130.0/24”:我不想将整个范围的地址从157.55.130.0写到157.55.130.255。如果我得到来自157.55.130.45的数据包,我只比较地址的前三个字节(157.55.130),并且该字符串在列表中。 – elmazzun

+1

你不必遵循我的建议,但让标准的库函数完成你的工作与格式良好的地址肯定是我喜欢你的长手比较解决方案之上。我认为使用专为工作而设计的*工具*在这种情况下*使用旨在理解代表IP地址的字符串的标准库函数*比编写自己的,可能会出错的版本要聪明得多。假设你不必每秒钟解析数百万个地址,那么在我能想到的任何机器上,开销都是可以忽略的。 –