]> git.ipfire.org Git - location/libloc.git/blame - src/libloc/address.h
Refactor parsing IP addresses
[location/libloc.git] / src / libloc / address.h
CommitLineData
0b1fef38
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2022 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#ifndef LIBLOC_ADDRESS_H
18#define LIBLOC_ADDRESS_H
19
20#ifdef LIBLOC_PRIVATE
21
22#include <errno.h>
23#include <netinet/in.h>
24
25/*
26 All of these functions are private and for internal use only
27*/
28
0a0a289a 29const char* loc_address_str(const struct in6_addr* address);
95b6a8e4 30int loc_address_parse(struct in6_addr* address, unsigned int* prefix, const char* string);
0a0a289a 31
0b1fef38
MT
32static inline int loc_address_family(const struct in6_addr* address) {
33 if (IN6_IS_ADDR_V4MAPPED(address))
34 return AF_INET;
35 else
36 return AF_INET6;
37}
38
1fd09d0b
MT
39static inline unsigned int loc_address_family_bit_length(const int family) {
40 switch (family) {
41 case AF_INET6:
42 return 128;
43
44 case AF_INET:
45 return 32;
46
47 default:
48 return 0;
49 }
50}
51
52/*
53 Checks whether prefix is valid for the given address
54*/
55static inline int loc_address_valid_prefix(const struct in6_addr* address, unsigned int prefix) {
56 const int family = loc_address_family(address);
57
58 // What is the largest possible prefix?
59 const unsigned int bit_length = loc_address_family_bit_length(family);
60
61 if (prefix <= bit_length)
62 return 1;
63
64 return 0;
65}
66
9c1e2943 67static inline int loc_address_cmp(const struct in6_addr* a1, const struct in6_addr* a2) {
0b1fef38
MT
68 for (unsigned int i = 0; i < 16; i++) {
69 if (a1->s6_addr[i] > a2->s6_addr[i])
70 return 1;
71
72 else if (a1->s6_addr[i] < a2->s6_addr[i])
73 return -1;
74 }
75
76 return 0;
77}
78
25612764
MT
79#define foreach_octet_in_address(octet, address) \
80 for (octet = (IN6_IS_ADDR_V4MAPPED(address) ? 12 : 0); octet <= 15; octet++)
81
8bcd0712
MT
82#define foreach_octet_in_address_reverse(octet, address) \
83 for (octet = 15; octet >= (IN6_IS_ADDR_V4MAPPED(address) ? 12 : 0); octet--)
84
25612764
MT
85static inline int loc_address_all_zeroes(const struct in6_addr* address) {
86 int octet = 0;
87
88 foreach_octet_in_address(octet, address) {
89 if (address->s6_addr[octet])
90 return 0;
91 }
92
93 return 1;
94}
95
96static inline int loc_address_all_ones(const struct in6_addr* address) {
97 int octet = 0;
98
99 foreach_octet_in_address(octet, address) {
100 if (address->s6_addr[octet] < 255)
101 return 0;
102 }
103
104 return 1;
105}
106
d698ca09 107static inline int loc_address_get_bit(const struct in6_addr* address, unsigned int i) {
0b1fef38
MT
108 return ((address->s6_addr[i / 8] >> (7 - (i % 8))) & 1);
109}
110
d698ca09 111static inline void loc_address_set_bit(struct in6_addr* address, unsigned int i, unsigned int val) {
0b1fef38
MT
112 address->s6_addr[i / 8] ^= (-val ^ address->s6_addr[i / 8]) & (1 << (7 - (i % 8)));
113}
114
115static inline struct in6_addr loc_prefix_to_bitmask(const unsigned int prefix) {
116 struct in6_addr bitmask;
117
118 for (unsigned int i = 0; i < 16; i++)
119 bitmask.s6_addr[i] = 0;
120
121 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
122 if (i >= 8)
123 bitmask.s6_addr[j] = 0xff;
124 else
125 bitmask.s6_addr[j] = 0xff << (8 - i);
126 }
127
128 return bitmask;
129}
130
8bcd0712
MT
131static inline unsigned int loc_address_bit_length(const struct in6_addr* address) {
132 int octet = 0;
133 foreach_octet_in_address(octet, address) {
134 if (address->s6_addr[octet])
135 return (15 - octet) * 8 + 32 - __builtin_clz(address->s6_addr[octet]);
0b1fef38
MT
136 }
137
8bcd0712 138 return 0;
0b1fef38
MT
139}
140
141static inline int loc_address_reset(struct in6_addr* address, int family) {
142 switch (family) {
143 case AF_INET6:
9820c51f
MT
144 address->s6_addr32[0] = 0x00000000;
145 address->s6_addr32[1] = 0x00000000;
146 address->s6_addr32[2] = 0x00000000;
147 address->s6_addr32[3] = 0x00000000;
0b1fef38
MT
148 return 0;
149
150 case AF_INET:
9820c51f
MT
151 address->s6_addr32[0] = 0x00000000;
152 address->s6_addr32[1] = 0x00000000;
0b1fef38 153 address->s6_addr32[2] = htonl(0xffff);
9820c51f 154 address->s6_addr32[3] = 0x00000000;
0b1fef38
MT
155 return 0;
156 }
157
158 return -1;
159}
160
161static inline int loc_address_reset_last(struct in6_addr* address, int family) {
162 switch (family) {
163 case AF_INET6:
9820c51f
MT
164 address->s6_addr32[0] = 0xffffffff;
165 address->s6_addr32[1] = 0xffffffff;
166 address->s6_addr32[2] = 0xffffffff;
167 address->s6_addr32[3] = 0xffffffff;
0b1fef38
MT
168 return 0;
169
170 case AF_INET:
9820c51f
MT
171 address->s6_addr32[0] = 0x00000000;
172 address->s6_addr32[1] = 0x00000000;
0b1fef38 173 address->s6_addr32[2] = htonl(0xffff);
9820c51f 174 address->s6_addr32[3] = 0xffffffff;
0b1fef38
MT
175 return 0;
176 }
177
178 return -1;
179}
180
181static inline struct in6_addr loc_address_and(
182 const struct in6_addr* address, const struct in6_addr* bitmask) {
183 struct in6_addr a;
184
185 // Perform bitwise AND
186 for (unsigned int i = 0; i < 4; i++)
187 a.s6_addr32[i] = address->s6_addr32[i] & bitmask->s6_addr32[i];
188
189 return a;
190}
191
192static inline struct in6_addr loc_address_or(
193 const struct in6_addr* address, const struct in6_addr* bitmask) {
194 struct in6_addr a;
195
196 // Perform bitwise OR
197 for (unsigned int i = 0; i < 4; i++)
198 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask->s6_addr32[i];
199
200 return a;
201}
202
0b1fef38
MT
203static inline int loc_address_sub(struct in6_addr* result,
204 const struct in6_addr* address1, const struct in6_addr* address2) {
dd599427
MT
205 int family1 = loc_address_family(address1);
206 int family2 = loc_address_family(address2);
0b1fef38
MT
207
208 // Address family must match
209 if (family1 != family2) {
210 errno = EINVAL;
211 return 1;
212 }
213
214 // Clear result
215 int r = loc_address_reset(result, family1);
216 if (r)
217 return r;
218
8bcd0712
MT
219 int octet = 0;
220 int remainder = 0;
0b1fef38 221
8bcd0712
MT
222 foreach_octet_in_address_reverse(octet, address1) {
223 int x = address1->s6_addr[octet] - address2->s6_addr[octet] + remainder;
0b1fef38 224
8bcd0712
MT
225 // Store remainder for the next iteration
226 remainder = (x >> 8);
227
228 result->s6_addr[octet] = x & 0xff;
0b1fef38 229 }
8bcd0712
MT
230
231 return 0;
0b1fef38
MT
232}
233
5b72642c 234static inline void loc_address_increment(struct in6_addr* address) {
25612764
MT
235 // Prevent overflow when everything is ones
236 if (loc_address_all_ones(address))
237 return;
238
8bcd0712
MT
239 int octet = 0;
240 foreach_octet_in_address_reverse(octet, address) {
5b72642c
MT
241 if (address->s6_addr[octet] < 255) {
242 address->s6_addr[octet]++;
0b1fef38
MT
243 break;
244 } else {
5b72642c 245 address->s6_addr[octet] = 0;
0b1fef38
MT
246 }
247 }
0b1fef38
MT
248}
249
5b72642c 250static inline void loc_address_decrement(struct in6_addr* address) {
25612764
MT
251 // Prevent underflow when everything is ones
252 if (loc_address_all_zeroes(address))
253 return;
254
8bcd0712
MT
255 int octet = 0;
256 foreach_octet_in_address_reverse(octet, address) {
5b72642c
MT
257 if (address->s6_addr[octet] > 0) {
258 address->s6_addr[octet]--;
0b1fef38 259 break;
a27c2f72
MT
260 } else {
261 address->s6_addr[octet] = 255;
0b1fef38
MT
262 }
263 }
0b1fef38
MT
264}
265
0b1fef38
MT
266static inline int loc_address_count_trailing_zero_bits(const struct in6_addr* address) {
267 int zeroes = 0;
268
8bcd0712
MT
269 int octet = 0;
270 foreach_octet_in_address_reverse(octet, address) {
0b1fef38
MT
271 if (address->s6_addr[octet]) {
272 zeroes += __builtin_ctz(address->s6_addr[octet]);
273 break;
274 } else
275 zeroes += 8;
276 }
277
278 return zeroes;
279}
280
281#endif /* LIBLOC_PRIVATE */
282
283#endif /* LIBLOC_ADDRESS_H */