chiark / gitweb /
machined: Move image discovery logic into src/shared, so that we can make use of...
[elogind.git] / src / shared / in-addr-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <arpa/inet.h>
23
24 #include "in-addr-util.h"
25
26 int in_addr_is_null(int family, const union in_addr_union *u) {
27         assert(u);
28
29         if (family == AF_INET)
30                 return u->in.s_addr == 0;
31
32         if (family == AF_INET6)
33                 return
34                         u->in6.s6_addr32[0] == 0 &&
35                         u->in6.s6_addr32[1] == 0 &&
36                         u->in6.s6_addr32[2] == 0 &&
37                         u->in6.s6_addr32[3] == 0;
38
39         return -EAFNOSUPPORT;
40 }
41
42 int in_addr_is_link_local(int family, const union in_addr_union *u) {
43         assert(u);
44
45         if (family == AF_INET)
46                 return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
47
48         if (family == AF_INET6)
49                 return IN6_IS_ADDR_LINKLOCAL(&u->in6);
50
51         return -EAFNOSUPPORT;
52 }
53
54 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
55         assert(a);
56         assert(b);
57
58         if (family == AF_INET)
59                 return a->in.s_addr == b->in.s_addr;
60
61         if (family == AF_INET6)
62                 return
63                         a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
64                         a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
65                         a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
66                         a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
67
68         return -EAFNOSUPPORT;
69 }
70
71 int in_addr_prefix_intersect(
72                 int family,
73                 const union in_addr_union *a,
74                 unsigned aprefixlen,
75                 const union in_addr_union *b,
76                 unsigned bprefixlen) {
77
78         unsigned m;
79
80         assert(a);
81         assert(b);
82
83         /* Checks whether there are any addresses that are in both
84          * networks */
85
86         m = MIN(aprefixlen, bprefixlen);
87
88         if (family == AF_INET) {
89                 uint32_t x, nm;
90
91                 x = be32toh(a->in.s_addr ^ b->in.s_addr);
92                 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
93
94                 return (x & nm) == 0;
95         }
96
97         if (family == AF_INET6) {
98                 unsigned i;
99
100                 if (m > 128)
101                         m = 128;
102
103                 for (i = 0; i < 16; i++) {
104                         uint8_t x, nm;
105
106                         x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
107
108                         if (m < 8)
109                                 nm = 0xFF << (8 - m);
110                         else
111                                 nm = 0xFF;
112
113                         if ((x & nm) != 0)
114                                 return 0;
115
116                         if (m > 8)
117                                 m -= 8;
118                         else
119                                 m = 0;
120                 }
121
122                 return 1;
123         }
124
125         return -EAFNOSUPPORT;
126 }
127
128 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
129         assert(u);
130
131         /* Increases the network part of an address by one. Returns
132          * positive it that succeeds, or 0 if this overflows. */
133
134         if (prefixlen <= 0)
135                 return 0;
136
137         if (family == AF_INET) {
138                 uint32_t c, n;
139
140                 if (prefixlen > 32)
141                         prefixlen = 32;
142
143                 c = be32toh(u->in.s_addr);
144                 n = c + (1UL << (32 - prefixlen));
145                 if (n < c)
146                         return 0;
147                 n &= 0xFFFFFFFFUL << (32 - prefixlen);
148
149                 u->in.s_addr = htobe32(n);
150                 return 1;
151         }
152
153         if (family == AF_INET6) {
154                 struct in6_addr add = {}, result;
155                 uint8_t overflow = 0;
156                 unsigned i;
157
158                 if (prefixlen > 128)
159                         prefixlen = 128;
160
161                 /* First calculate what we have to add */
162                 add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
163
164                 for (i = 16; i > 0; i--) {
165                         unsigned j = i - 1;
166
167                         result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
168                         overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
169                 }
170
171                 if (overflow)
172                         return 0;
173
174                 u->in6 = result;
175                 return 1;
176         }
177
178         return -EAFNOSUPPORT;
179 }
180
181 int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
182         char *x;
183         size_t l;
184
185         assert(u);
186         assert(ret);
187
188         if (family == AF_INET)
189                 l = INET_ADDRSTRLEN;
190         else if (family == AF_INET6)
191                 l = INET6_ADDRSTRLEN;
192         else
193                 return -EAFNOSUPPORT;
194
195         x = new(char, l);
196         if (!x)
197                 return -ENOMEM;
198
199         errno = 0;
200         if (!inet_ntop(family, u, x, l)) {
201                 free(x);
202                 return errno ? -errno : -EINVAL;
203         }
204
205         *ret = x;
206         return 0;
207 }
208
209 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
210
211         assert(s);
212         assert(ret);
213
214         if (!IN_SET(family, AF_INET, AF_INET6))
215                 return -EAFNOSUPPORT;
216
217         errno = 0;
218         if (inet_pton(family, s, ret) <= 0)
219                 return errno ? -errno : -EINVAL;
220
221         return 0;
222 }
223
224 int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
225         int r;
226
227         assert(s);
228         assert(family);
229         assert(ret);
230
231         r = in_addr_from_string(AF_INET, s, ret);
232         if (r >= 0) {
233                 *family = AF_INET;
234                 return 0;
235         }
236
237         r = in_addr_from_string(AF_INET6, s, ret);
238         if (r >= 0) {
239                 *family = AF_INET6;
240                 return 0;
241         }
242
243         return -EINVAL;
244 }
245
246 unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
247         assert(addr);
248
249         return 32 - u32ctz(be32toh(addr->s_addr));
250 }
251
252 int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
253         uint8_t msb_octet = *(uint8_t*) addr;
254
255         /* addr may not be aligned, so make sure we only access it byte-wise */
256
257         assert(addr);
258         assert(prefixlen);
259
260         if (msb_octet < 128)
261                 /* class A, leading bits: 0 */
262                 *prefixlen = 8;
263         else if (msb_octet < 192)
264                 /* class B, leading bits 10 */
265                 *prefixlen = 16;
266         else if (msb_octet < 224)
267                 /* class C, leading bits 110 */
268                 *prefixlen = 24;
269         else
270                 /* class D or E, no default prefixlen */
271                 return -ERANGE;
272
273         return 0;
274 }
275
276 int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
277         unsigned char prefixlen;
278         int r;
279
280         assert(addr);
281         assert(mask);
282
283         r = in_addr_default_prefixlen(addr, &prefixlen);
284         if (r < 0)
285                 return r;
286
287         assert(prefixlen > 0 && prefixlen < 32);
288
289         mask->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
290
291         return 0;
292 }