简体   繁体   English

快速比较IP地址的最聪明方法?

[英]Smartest way to compare IP addresses quickly?

I have a list of IP addresses, stored like this: 我有一个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 */
    ...
};

I get the IP address from the sniffed packet (casting it to struct iphdr *iph = (struct iphdr *)(packet + sizeof(struct ether_header)); I convert it in a character string using inet_ntop ; finally, I compare the IP address from the packet with the ones in the list with the following code: 我从嗅探到的数据包中获得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;
}

I'd like to make this more fast, without using strtok , strcmp , malloc or free . 我想使它更快,而不使用strtokstrcmpmallocfree In /usr/include/netinet/ip.h I see that addresses are /usr/include/netinet/ip.h我看到地址是

u_int32_t saddr;
u_int32_t daddr;

is it possible to compare without even using inet_ntop first, maybe just comparing the two addresses while they still are u_int32_t ? 是否有可能甚至inet_ntop先使用inet_ntop进行比较,也可能只是在两个地址仍然为u_int32_t时进行比较?

EDIT: here's a solution example for whoever will read this question. 编辑:这是解决方案示例,适合任何人阅读此问题。

#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;
}

Thanks to iharob too for the bsearch hint. 也感谢iharob提供的bsearch提示。

I would avoid converting the binary data to strings. 我会避免将二进制数据转换为字符串。 If you keep them binary then it's quite easy to compare: 如果将它们保留为二进制,则比较起来很容易:

match = (ip & listed_mask) == listed_ip;

"/24" is a mask. “ / 24”是掩码。 Means inly 24 highest bits are relevant. 意味着只有24个最高位是相关的。 You convert it to binary mask as follows: 您将其转换为二进制掩码,如下所示:

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

The performance issues have nothing to do with strcmp() , malloc() is unnecessary though. 性能问题与strcmp()无关,尽管malloc()是不必要的。

If you are only using IPv4 addresses you only need 16 characters to store it so you can remove malloc() and declare the temporary storage as an array. 如果仅使用IPv4地址,则只需要16个字符即可存储它,因此可以删除malloc()并将临时存储声明为数组。

But there is an important improvement if there are going to be many ip addresses in the list. 但是,如果列表中将有许多ip地址,则有一个重要的改进。

First you need to sort the list of IP addresses, and then use bsearch() to search for the right IP. 首先,您需要对IP地址列表进行排序,然后使用bsearch()搜索正确的IP。 This way the code will run in O(log(2n)) time which is a lot faster than O(N) , specially for large N 这样,代码将以O(log(2n))的时间运行,这比O(N)快很多,特别是对于较大的N

My approach here would be: 我的方法是:

  1. simply use a strncat with ".0" to build valid IPv4 addresses. 只需使用带有".0"strncat即可构建有效的IPv4地址。
  2. use getaddrinfo with constant values for socket type etc to build a addrinfo struct 使用具有套接字类型等常量值的getaddrinfo来构建addrinfo结构
  3. compare the relevant fields of the addrinfo . 比较addrinfo的相关字段。

Basically, the example from man getaddrinfo does all this . 基本上,来自man getaddrinfo的示例完成了所有这一切

The fastest way would be to store addresses in a dictionary, see this link 最快的方法是将地址存储在字典中,请参阅此链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM