C中实现判断ipv4或ipv6地址是单播、组播、广播地址

在 C 中我们需要实现输入一个字符集(单播、组播、广播地址类型),来判断某 ipv4ipv6 地址的类型是否在指定的类型集内。

为方便用户输入,类型的字符集我们定义为:
1:单播地址
2:组播地址
3:广播地址

可以输入多个类型,以英文逗号分隔,如:1,2,3

首先定义处理字符串类型匹配的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @brief 判断目标数字是否在字符串中
*
* @param target
* @param data
* @return true
* @return false
*/
bool pp_in_string(int target, char *data) {
bool found = false;
if (data == NULL) goto end;

char str[128];
// 避免字符串拷贝溢出
strncpy(str, data, sizeof(data) - 1);
str[sizeof(str) - 1] = '\0';

char *token = strtok(str, ",");
while (token != NULL) {
if (atoi(token) == target) {
found = true;
break;
}
token = strtok(NULL, ",");
}

end:
return found;
}

定义地址类型判断方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @brief 检查地址是否在指定范围,判断是否为单播、组播、广播地址
*
* @param family
* @param ip ipv4 或 ipv6 地址,分别以“struct in_addr”、"struct in6_addr"的形式传入,统一类型为__u32
* @param range_type
* @return true
* @return false
*/
bool address_check_range_type(int family, unsigned int *ip, char *range_type) {
if (range_type[0] == '\0') {
return true;
}

// 默认为单播1单播,2组播,3广播
int result = 1;

if (family == AF_INET) {
if (ntohl(ip[0]) == 0xFFFFFFFF) {
result = 3;
} else if ((ntohl(ip[0]) & 0xF0000000) == ((uint32_t)(0xE0 << 24))) {
result = 2;
}
} else if (family == AF_INET6) {
// ipv6的格式是分8段,首段是16位,共128位的值,组播需要判断其前8位需要全是1
// unsigned int是32位的,不能直接使用ip[0]==oxFF去判断,需要转换后去判断
struct in6_addr *addr_v6 = (struct in6_addr *)ip;
if (addr_v6->s6_addr[0] == 0xFF) {
result = 2;
// ipv6的广播使用组播实现
if (pp_in_string(3, range_type)) {
result = 3;
}
}
}

return pp_in_string(result, range_type);
}

上面的在 ipv6 格式下比较,需要转换一下再去判断。

ipv6 的地址结构体为:

1
2
3
4
5
6
7
struct in6_addr {
union {
uint8_t s6_addr[16]; // 128-bit address
uint16_t s6_addr16[8]; // access with uint16_t
uint32_t s6_addr32[4]; // access with uint32_t
} __u6_addr;
}

所以我们需要使用 in6_addr 结构体中的 s6_addr 去判断,正好是 128 位的 ipv6 地址表示值,而且分了 16 个数组,所以每组即 8 位,正使满足我们比较前 8 位需要全是 1 的条件。而如果我们使用的 s6_addr32,那么每组是 32 位,共 4 组,不能满足判断前 8 位的条件。

ipv6 的地址数据格式为:AA22:BB11:1122:CDED:1234:AA99:7654:7410