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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
#include "include/ipaddr.h"
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include "acconfig.h"
#ifdef DARWIN
#ifndef s6_addr16
#define s6_addr16 __u6_addr.__u6_addr16
#endif
#ifndef s6_addr32
#define s6_addr32 __u6_addr.__u6_addr32
#endif
#endif
static void netmask_ipv4(const struct in_addr *addr,
unsigned int prefix_len,
struct in_addr *out) {
uint32_t mask;
if (prefix_len >= 32) {
// also handle 32 in this branch, because >>32 is not defined by
// the C standards
mask = ~uint32_t(0);
} else {
mask = htonl(~(~uint32_t(0) >> prefix_len));
}
out->s_addr = addr->s_addr & mask;
}
const struct sockaddr *find_ipv4_in_subnet(const struct ifaddrs *addrs,
const struct sockaddr_in *net,
unsigned int prefix_len) {
struct in_addr want, temp;
netmask_ipv4(&net->sin_addr, prefix_len, &want);
for (; addrs != NULL; addrs = addrs->ifa_next) {
if (addrs->ifa_addr == NULL)
continue;
if (addrs->ifa_addr->sa_family != net->sin_family)
continue;
struct in_addr *cur = &((struct sockaddr_in*)addrs->ifa_addr)->sin_addr;
netmask_ipv4(cur, prefix_len, &temp);
if (temp.s_addr == want.s_addr) {
return addrs->ifa_addr;
}
}
return NULL;
}
static void netmask_ipv6(const struct in6_addr *addr,
unsigned int prefix_len,
struct in6_addr *out) {
if (prefix_len > 128)
prefix_len = 128;
memcpy(out->s6_addr, addr->s6_addr, prefix_len/8);
if (prefix_len < 128)
out->s6_addr[prefix_len/8] = addr->s6_addr[prefix_len/8] & ~( 0xFF >> (prefix_len % 8) );
if (prefix_len/8 < 15)
memset(out->s6_addr+prefix_len/8+1, 0, 16-prefix_len/8-1);
}
const struct sockaddr *find_ipv6_in_subnet(const struct ifaddrs *addrs,
const struct sockaddr_in6 *net,
unsigned int prefix_len) {
struct in6_addr want, temp;
netmask_ipv6(&net->sin6_addr, prefix_len, &want);
for (; addrs != NULL; addrs = addrs->ifa_next) {
if (addrs->ifa_addr == NULL)
continue;
if (addrs->ifa_addr->sa_family != net->sin6_family)
continue;
struct in6_addr *cur = &((struct sockaddr_in6*)addrs->ifa_addr)->sin6_addr;
netmask_ipv6(cur, prefix_len, &temp);
if (memcmp(temp.s6_addr32, want.s6_addr32, sizeof(temp)) == 0)
return addrs->ifa_addr;
}
return NULL;
}
const struct sockaddr *find_ip_in_subnet(const struct ifaddrs *addrs,
const struct sockaddr *net,
unsigned int prefix_len) {
switch (net->sa_family) {
case AF_INET:
return find_ipv4_in_subnet(addrs, (struct sockaddr_in*)net, prefix_len);
case AF_INET6:
return find_ipv6_in_subnet(addrs, (struct sockaddr_in6*)net, prefix_len);
}
return NULL;
}
bool parse_network(const char *s, struct sockaddr *network, unsigned int *prefix_len) {
char *slash = strchr((char*)s, '/');
if (!slash) {
// no slash
return false;
}
if (*(slash+1) == '\0') {
// slash is the last character
return false;
}
char *end;
long int num = strtol(slash+1, &end, 10);
if (*end != '\0') {
// junk after the prefix_len
return false;
}
if (num < 0) {
return false;
}
*prefix_len = num;
// copy the part before slash to get nil termination
char *addr = (char*)alloca(slash-s + 1);
strncpy(addr, s, slash-s);
addr[slash-s] = '\0';
// caller expects ports etc to be zero
memset(network, 0, sizeof(*network));
// try parsing as ipv4
int ok;
ok = inet_pton(AF_INET, addr, &((struct sockaddr_in*)network)->sin_addr);
if (ok) {
network->sa_family = AF_INET;
return true;
}
// try parsing as ipv6
ok = inet_pton(AF_INET6, addr, &((struct sockaddr_in6*)network)->sin6_addr);
if (ok) {
network->sa_family = AF_INET6;
return true;
}
return false;
}
|