chiark / gitweb /
systemd-detect-virt: detect s390 virtualization
[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_null(unsigned family, 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
43 int in_addr_equal(unsigned family, union in_addr_union *a, union in_addr_union *b) {
44         assert(a);
45         assert(b);
46
47         if (family == AF_INET)
48                 return a->in.s_addr == b->in.s_addr;
49
50         if (family == AF_INET6)
51                 return
52                         a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
53                         a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
54                         a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
55                         a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
56
57         return -EAFNOSUPPORT;
58 }
59
60 int in_addr_prefix_intersect(
61                 unsigned family,
62                 const union in_addr_union *a,
63                 unsigned aprefixlen,
64                 const union in_addr_union *b,
65                 unsigned bprefixlen) {
66
67         unsigned m;
68
69         assert(a);
70         assert(b);
71
72         /* Checks whether there are any addresses that are in both
73          * networks */
74
75         m = MIN(aprefixlen, bprefixlen);
76
77         if (family == AF_INET) {
78                 uint32_t x, nm;
79
80                 x = be32toh(a->in.s_addr ^ b->in.s_addr);
81                 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
82
83                 return (x & nm) == 0;
84         }
85
86         if (family == AF_INET6) {
87                 unsigned i;
88
89                 if (m > 128)
90                         m = 128;
91
92                 for (i = 0; i < 16; i++) {
93                         uint8_t x, nm;
94
95                         x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
96
97                         if (m < 8)
98                                 nm = 0xFF << (8 - m);
99                         else
100                                 nm = 0xFF;
101
102                         if ((x & nm) != 0)
103                                 return 0;
104
105                         if (m > 8)
106                                 m -= 8;
107                         else
108                                 m = 0;
109                 }
110
111                 return 1;
112         }
113
114         return -EAFNOSUPPORT;
115 }
116
117 int in_addr_prefix_next(unsigned family, union in_addr_union *u, unsigned prefixlen) {
118         assert(u);
119
120         /* Increases the network part of an address by one. Returns
121          * positive it that succeeds, or 0 if this overflows. */
122
123         if (prefixlen <= 0)
124                 return 0;
125
126         if (family == AF_INET) {
127                 uint32_t c, n;
128
129                 if (prefixlen > 32)
130                         prefixlen = 32;
131
132                 c = be32toh(u->in.s_addr);
133                 n = c + (1UL << (32 - prefixlen));
134                 if (n < c)
135                         return 0;
136                 n &= 0xFFFFFFFFUL << (32 - prefixlen);
137
138                 u->in.s_addr = htobe32(n);
139                 return 1;
140         }
141
142         if (family == AF_INET6) {
143                 struct in6_addr add = {}, result;
144                 uint8_t overflow = 0;
145                 unsigned i;
146
147                 if (prefixlen > 128)
148                         prefixlen = 128;
149
150                 /* First calculate what we have to add */
151                 add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
152
153                 for (i = 16; i > 0; i--) {
154                         unsigned j = i - 1;
155
156                         result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
157                         overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
158                 }
159
160                 if (overflow)
161                         return 0;
162
163                 u->in6 = result;
164                 return 1;
165         }
166
167         return -EAFNOSUPPORT;
168 }
169
170 int in_addr_to_string(unsigned family, const union in_addr_union *u, char **ret) {
171         char *x;
172         size_t l;
173
174         assert(u);
175         assert(ret);
176
177         if (family == AF_INET)
178                 l = INET_ADDRSTRLEN;
179         else if (family == AF_INET6)
180                 l = INET6_ADDRSTRLEN;
181         else
182                 return -EAFNOSUPPORT;
183
184         x = new(char, l);
185         if (!x)
186                 return -ENOMEM;
187
188         errno = 0;
189         if (!inet_ntop(family, u, x, l)) {
190                 free(x);
191                 return errno ? -errno : -EINVAL;
192         }
193
194         *ret = x;
195         return 0;
196 }
197
198 int in_addr_from_string(unsigned family, const char *s, union in_addr_union *ret) {
199
200         assert(s);
201         assert(ret);
202
203         if (!IN_SET(family, AF_INET, AF_INET6))
204                 return -EAFNOSUPPORT;
205
206         errno = 0;
207         if (inet_pton(family, s, ret) <= 0)
208                 return errno ? -errno : -EINVAL;
209
210         return 0;
211 }
212
213 int in_addr_from_string_auto(const char *s, unsigned *family, union in_addr_union *ret) {
214         int r;
215
216         assert(s);
217         assert(family);
218         assert(ret);
219
220         r = in_addr_from_string(AF_INET, s, ret);
221         if (r >= 0) {
222                 *family = AF_INET;
223                 return 0;
224         }
225
226         r = in_addr_from_string(AF_INET6, s, ret);
227         if (r >= 0) {
228                 *family = AF_INET6;
229                 return 0;
230         }
231
232         return -EINVAL;
233 }
234
235 static const char* const family_table[] = {
236         [AF_UNSPEC] = "unspec",
237         [AF_UNIX] = "unix",
238         [AF_INET] = "inet",
239         [AF_INET6] = "inet6",
240         [AF_NETLINK] = "netlink",
241         [AF_PACKET] = "packet",
242         [AF_BLUETOOTH] = "bluetooth",
243         [AF_NFC] = "nfc",
244 };
245 DEFINE_STRING_TABLE_LOOKUP(family, int);