1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "networkd-address-pool.h"
5 #include "networkd-address.h"
6 #include "networkd-manager.h"
7 #include "set.h"
8 #include "string-util.h"
9
10 #define RANDOM_PREFIX_TRIAL_MAX 1024
11
address_pool_new(Manager * m,int family,const union in_addr_union * u,unsigned prefixlen)12 static int address_pool_new(
13 Manager *m,
14 int family,
15 const union in_addr_union *u,
16 unsigned prefixlen) {
17
18 _cleanup_free_ AddressPool *p = NULL;
19 int r;
20
21 assert(m);
22 assert(u);
23
24 p = new(AddressPool, 1);
25 if (!p)
26 return -ENOMEM;
27
28 *p = (AddressPool) {
29 .manager = m,
30 .family = family,
31 .prefixlen = prefixlen,
32 .in_addr = *u,
33 };
34
35 r = ordered_set_ensure_put(&m->address_pools, NULL, p);
36 if (r < 0)
37 return r;
38
39 TAKE_PTR(p);
40 return 0;
41 }
42
address_pool_new_from_string(Manager * m,int family,const char * p,unsigned prefixlen)43 static int address_pool_new_from_string(
44 Manager *m,
45 int family,
46 const char *p,
47 unsigned prefixlen) {
48
49 union in_addr_union u;
50 int r;
51
52 assert(m);
53 assert(p);
54
55 r = in_addr_from_string(family, p, &u);
56 if (r < 0)
57 return r;
58
59 return address_pool_new(m, family, &u, prefixlen);
60 }
61
address_pool_setup_default(Manager * m)62 int address_pool_setup_default(Manager *m) {
63 int r;
64
65 assert(m);
66
67 /* Add in the well-known private address ranges. */
68 r = address_pool_new_from_string(m, AF_INET6, "fd00::", 8);
69 if (r < 0)
70 return r;
71
72 r = address_pool_new_from_string(m, AF_INET, "192.168.0.0", 16);
73 if (r < 0)
74 return r;
75
76 r = address_pool_new_from_string(m, AF_INET, "172.16.0.0", 12);
77 if (r < 0)
78 return r;
79
80 r = address_pool_new_from_string(m, AF_INET, "10.0.0.0", 8);
81 if (r < 0)
82 return r;
83
84 return 0;
85 }
86
address_pool_prefix_is_taken(AddressPool * p,const union in_addr_union * u,unsigned prefixlen)87 static bool address_pool_prefix_is_taken(
88 AddressPool *p,
89 const union in_addr_union *u,
90 unsigned prefixlen) {
91
92 Link *l;
93 Network *n;
94
95 assert(p);
96 assert(u);
97
98 HASHMAP_FOREACH(l, p->manager->links_by_index) {
99 Address *a;
100
101 /* Don't clash with assigned addresses */
102 SET_FOREACH(a, l->addresses) {
103 if (a->family != p->family)
104 continue;
105
106 if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
107 return true;
108 }
109 }
110
111 /* And don't clash with configured but un-assigned addresses either */
112 ORDERED_HASHMAP_FOREACH(n, p->manager->networks) {
113 Address *a;
114
115 ORDERED_HASHMAP_FOREACH(a, n->addresses_by_section) {
116 if (a->family != p->family)
117 continue;
118
119 if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
120 return true;
121 }
122 }
123
124 return false;
125 }
126
address_pool_acquire_one(AddressPool * p,int family,unsigned prefixlen,union in_addr_union * found)127 static int address_pool_acquire_one(AddressPool *p, int family, unsigned prefixlen, union in_addr_union *found) {
128 union in_addr_union u;
129 int r;
130
131 assert(p);
132 assert(prefixlen > 0);
133 assert(found);
134
135 if (p->family != family)
136 return 0;
137
138 if (p->prefixlen >= prefixlen)
139 return 0;
140
141 u = p->in_addr;
142
143 for (unsigned i = 0; i < RANDOM_PREFIX_TRIAL_MAX; i++) {
144 r = in_addr_random_prefix(p->family, &u, p->prefixlen, prefixlen);
145 if (r <= 0)
146 return r;
147
148 if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
149 if (DEBUG_LOGGING) {
150 _cleanup_free_ char *s = NULL;
151
152 (void) in_addr_prefix_to_string(p->family, &u, prefixlen, &s);
153 log_debug("Found range %s", strna(s));
154 }
155
156 *found = u;
157 return 1;
158 }
159 }
160
161 return 0;
162 }
163
address_pool_acquire(Manager * m,int family,unsigned prefixlen,union in_addr_union * found)164 int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
165 AddressPool *p;
166 int r;
167
168 assert(m);
169 assert(IN_SET(family, AF_INET, AF_INET6));
170 assert(prefixlen > 0);
171 assert(found);
172
173 ORDERED_SET_FOREACH(p, m->address_pools) {
174 r = address_pool_acquire_one(p, family, prefixlen, found);
175 if (r != 0)
176 return r;
177 }
178
179 return 0;
180 }
181