]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/in-addr-util.c
man: tmpfiles.d - recommend using b! and c!
[thirdparty/systemd.git] / src / shared / in-addr-util.c
CommitLineData
3b653205
LP
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
af93291c 26int in_addr_is_null(int family, const union in_addr_union *u) {
3b653205
LP
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
af93291c
LP
42int 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}
3b653205 53
623a4c97 54int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
3b653205
LP
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
71int in_addr_prefix_intersect(
0dd25fb9 72 int family,
3b653205
LP
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
0dd25fb9 128int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
3b653205
LP
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
0dd25fb9 181int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
3b653205
LP
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
0dd25fb9 209int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
3b653205
LP
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}
74b2466e 223
0dd25fb9 224int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
74b2466e
LP
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}
44e7b949
LP
245
246unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
247 assert(addr);
248
249 return 32 - u32ctz(be32toh(addr->s_addr));
250}