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