首页 > 解决方案 > 这是检查 IP(字符串)是否属于 CIDR(字符串)的最快方法吗?

问题描述

想要检查 IP(作为字符串)是否在 CIDR 范围内(作为字符串)

我知道还有一些网络功能可以检查它,但决定使用这种方法并且到目前为止有这个代码:

    int cidr_to_ip_and_mask(const char *cidr, uint32_t *ip, uint32_t *mask)
    {
        uint8_t a, b, c, d, bits;
        if (sscanf(cidr, "%hhu.%hhu.%hhu.%hhu/%hhu", a, b, c, d, bits) < 5) {
            return -1; /* didn't convert enough of CIDR */
        }
        if (bits > 32) {
            return -1; /* Invalid bit count */
        }
        *ip =
            (a << 24UL) |
            (b << 16UL) |
            (c << 8UL) |
            (d);
        *mask = (0xFFFFFFFFUL << (32 - bits)) & 0xFFFFFFFFUL;
    }
    
    uint32_t netip;
    uint32_t netmask;
    if (cidr_to_ip_and_mask("10.0.0.0/16", &netip, &netmask) < 0) {
        /* return some failure */
    } 
    uint8_t a, b, c, d;
    
    char *ipstr= "10.0.0.1" // value to check
    uint32_t ip = 0;
    if (sscanf(ipstr, "%hhu.%hhu.%hhu.%hhu",  a, b, c, d) < 4) {
            return -1; /* didn't convert enough of CIDR */
        }
       
    ip = (a << 24UL) |
      (b << 16UL) |
      (c << 8UL) |
      (d);
            
    uint32_t netstart = (netip & netmask); // first ip in subnet
    uint32_t netend = (netstart | ~netmask); // last ip in subnet
    if ((ip >= netstart) && (ip <= netend))
        // is in subnet range...
    else
        // not in subnet range...

这个对吗?

这是实现它的最快方法吗?

更新

我使用这两个代码做了一些实验和测量。不得不稍微修改建议的答案代码。

我使用的最终代码:

t1.c

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

int cidr_to_ip_and_mask(const char *cidr, uint32_t *ip, uint32_t *mask)
{
    uint8_t a, b, c, d, bits;
    if (sscanf(cidr, "%hhu.%hhu.%hhu.%hhu/%hhu", &a, &b, &c, &d, &bits) < 5) {
        return -1; /* didn't convert enough of CIDR */
    }
    if (bits > 32) {
        return -1; /* Invalid bit count */
    }
    *ip =
        (a << 24UL) |
        (b << 16UL) |
        (c << 8UL) |
        (d);
    *mask = (0xFFFFFFFFUL << (32 - bits)) & 0xFFFFFFFFUL;
}

void main()
{    
    uint32_t netip;
    uint32_t netmask;


    for(int i=0;i<1000000;i++)
    {
        if (cidr_to_ip_and_mask("10.1.0.0/16", &netip, &netmask) < 0) {
            /* return some failure */
        } 
        uint8_t a, b, c, d;
        uint32_t ip;

        char *ipstr= "10.2.13.1"; // value to check
        if (sscanf(ipstr, "%hhu.%hhu.%hhu.%hhu",  &a, &b, &c, &d) < 4) {
        }

        ip = (a << 24UL) |
            (b << 16UL) |
            (c << 8UL) |
            (d);

        uint32_t netstart = (netip & netmask); // first ip in subnet
        uint32_t netend = (netstart | ~netmask); // last ip in subnet
        if ((ip >= netstart) && (ip <= netend))
            printf("in");
        else
            printf("out");
    }
}

t2.c

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

void main(){

    for(int i=0;i<1000000;i++)
    {


        char ipAsString[16]="10.2.13.1";
        char cidrAsString[20]="10.1.0.0/16";
        char *ipSplit[4];
        unsigned char ipAddr[4];
        for(int i=0;i<4;i++){
            if(i==0)  
                ipSplit[i] =  (char *)strtok(ipAsString, ".");
            else 
                ipSplit[i] = (char *)strtok(NULL, ".");
        }
        ipAddr[0] = atoi(ipSplit[0]);
        ipAddr[1] = atoi(ipSplit[1]);
        ipAddr[2] = atoi(ipSplit[2]);
        ipAddr[3] = atoi(ipSplit[3]);
       
        int i=0;
        for(i=0;i<=16;i++)
        {
            if(cidrAsString[i]=='/')
                break;
        }
        char c[3];
        strncpy(c,cidrAsString+(i+1),2);

        int cirdIntVal = atoi(c);
        int numOfSetBits = 0;
        if (ipAddr[0] < 255) {
            // find number of set bits (1s) in ipAddr[0] and add them to numOfSetBits
        } else if (ipAddr[1] <= 255) {
            numOfSetBits += 8;
            // find number of set bits (1s) in ipAddr[1] and add them to numOfSetBits
        } else if (ipAddr[2] <= 255) {
            numOfSetBits += 16;
            // find number of set bits (1s) in ipAddr[2] and add them to numOfSetBits
        } else {
            numOfSetBits += 24;
            // find number of set bits (1s) in ipAddr[1] and add them to numOfSetBits
        }
        if (numOfSetBits == cirdIntVal) // will return true if ip is in cird
            printf("in");
        else
            printf("out");
    }
}

性能结果:

time ./t1     
./t1  0,00s user 0,00s system 80% cpu 0,001 total
time ./t2
./t2  0,00s user 0,00s system 81% cpu 0,001 total

Above is with return value ie return 1 or 0

Below is with printf()s

time ./t1  0,66s user 0,02s system 78% cpu 0,856 total

time ./t2  0,21s user 0,00s system 52% cpu 0,406 total


Second approch is ca 47% faster and easier on CPU

标签: c

解决方案


从您的问题中不清楚,但是如果您将 ip 地址作为字符串或char数组提供,您可以将 ip 的每个“十进制值”存储到一个unsigned char. 您无需将其转换为 32 位值,您只需检查每个单独的字符,然后对其进行操作。例如,如果您有10.252.1.154ip,则将 10、252、1 和 154 存储在变量bits31_24bits23_16bits15_8、 中,然后从左bits7_01每个连续 s 的数量。unsigned char

int getCIDR (char *ipAsString, char* cird) {
    unsigned char ipAddr[4];
    char ipSplit[4][3] = strtok(ipAsString, '.');
    ipAddr[0] = atoi(ipSplit[0]);
    ipAddr[1] = atoi(ipSplit[1]);
    ipAddr[2] = atoi(ipSplit[2]);
    ipAddr[3] = atoi(ipSplit[3]);

    int numOfSetBits = 0;
    if (ipAddr[0] < 255) {
        // find number of set bits (1s) till first 0 in ipAddr[0] and add them to numOfSetBits
        return numOfSetBits;
    } else if (ipAddr[1] <= 255) {
        numOfSetBits += 8;
        // find number of set bits (1s) till first 0 in ipAddr[1] and add them to numOfSetBits
        return numOfSetBits;
    } else if (ipAddr[2] <= 255) {
        numOfSetBits += 16;
        // find number of set bits (1s) till first 0 in ipAddr[2] and add them to numOfSetBits
        return numOfSetBits;
    } else {
        numOfSetBits += 24;
        // find number of set bits (1s) in till first 0 ipAddr[1] and add them to numOfSetBits
        return numOfSetBits;
    }
}

上面的代码将返回每个 CRDL from \0to\32

而不是将其转换回字符串:

char* CLDR = malloc (sizeof(char) * (numOfDigits(numOfSetBits) + 1)); // 1 for \ character
CLDR = itoa (numberOfSetBits);

编辑:每条atoi减少函数调用的评论可以重写为

ipAddr[0] = (ipSplit[0][0] - '0') * 100 + (ipSplit[0][1] - '0') * 10 + ipSplit[0][0] - '0';

编辑:为了更好地解决问题并且不打扰原始代码,这里是修改后的代码。

int getCIDR (char *ipAsString, char* cidr) {
    unsigned char ipAddr[4];
    char ipSplit[4][3] = strtok(ipAsString, '.');
    ipAddr[0] = atoi(ipSplit[0]);
    ipAddr[1] = atoi(ipSplit[1]);
    ipAddr[2] = atoi(ipSplit[2]);
    ipAddr[3] = atoi(ipSplit[3]);
    ++cidr; // to skip initial /
    int cirdIntVal = atoi(cidr);

    int numOfSetBits = 0;
    if (ipAddr[0] < 255) {
        // find number of set bits (1s) in ipAddr[0] and add them to numOfSetBits
    } else if (ipAddr[1] <= 255) {
        numOfSetBits += 8;
        // find number of set bits (1s) in ipAddr[1] and add them to numOfSetBits
    } else if (ipAddr[2] <= 255) {
        numOfSetBits += 16;
        // find number of set bits (1s) in ipAddr[2] and add them to numOfSetBits
    } else {
        numOfSetBits += 24;
        // find number of set bits (1s) in ipAddr[1] and add them to numOfSetBits
    }

    return (numberOfSetBits == cirdIntVal); // will return true if ip is in cird
}

您也可以使用范围而不是进行return (numberOfSetBits == cirdIntVal);范围检查。我假设 CIRD 是从 1 到 32 之间的数字值(不是整数,而是字符串表示)。

CIRD也是一回事

\0 => 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\1 => 10xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\2 => 110xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\3 => 1110xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
... // and so on

这就是我在算法中使用的,也许我最初并不清楚。

CIRD


推荐阅读