c - 这是检查 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
解决方案
从您的问题中不清楚,但是如果您将 ip 地址作为字符串或char
数组提供,您可以将 ip 的每个“十进制值”存储到一个unsigned char
. 您无需将其转换为 32 位值,您只需检查每个单独的字符,然后对其进行操作。例如,如果您有10.252.1.154
ip,则将 10、252、1 和 154 存储在变量bits31_24
、bits23_16
、bits15_8
、 中,然后从左bits7_0
数1
每个连续 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 \0
to\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
这就是我在算法中使用的,也许我最初并不清楚。
推荐阅读
- docker - Docker-compose 错误“无法关闭容器”
- sql - 组合查询以正确顺序获取所有匹配的搜索文本
- python - 在数据框中创建滚动平均值直到设定点
- python - 如何在 Python 3 中使用类型注释重载不同接口的相同命名方法
- angular - 根据搜索值过滤子对象
- amazon-web-services - 如何提供实例身份文件以使用 AWS Javascript SDK 将容器实例注册到 AWS ECS 集群?
- r - R中的Urbanmapr包
- javascript - 每天在特定时间运行 Node.js 脚本
- python - python scrapy FormRequest.FormResponse没有给出任何输出
- html - 当标签也在输入中时向占位符添加填充