]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/ip-address-access.c
Merge pull request #16690 from poettering/userdb-group-desc
[thirdparty/systemd.git] / src / core / ip-address-access.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include "alloc-util.h"
7 #include "bpf-firewall.h"
8 #include "extract-word.h"
9 #include "hostname-util.h"
10 #include "ip-address-access.h"
11 #include "parse-util.h"
12 #include "string-util.h"
13
14 int config_parse_ip_address_access(
15 const char *unit,
16 const char *filename,
17 unsigned line,
18 const char *section,
19 unsigned section_line,
20 const char *lvalue,
21 int ltype,
22 const char *rvalue,
23 void *data,
24 void *userdata) {
25
26 IPAddressAccessItem **list = data;
27 const char *p;
28 int r;
29
30 assert(list);
31
32 if (isempty(rvalue)) {
33 *list = ip_address_access_free_all(*list);
34 return 0;
35 }
36
37 p = rvalue;
38
39 for (;;) {
40 _cleanup_free_ IPAddressAccessItem *a = NULL;
41 _cleanup_free_ char *word = NULL;
42
43 r = extract_first_word(&p, &word, NULL, 0);
44 if (r == 0)
45 break;
46 if (r == -ENOMEM)
47 return log_oom();
48 if (r < 0) {
49 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
50 break;
51 }
52
53 a = new0(IPAddressAccessItem, 1);
54 if (!a)
55 return log_oom();
56
57 if (streq(word, "any")) {
58 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
59
60 a->family = AF_INET;
61 LIST_APPEND(items, *list, a);
62
63 a = new0(IPAddressAccessItem, 1);
64 if (!a)
65 return log_oom();
66
67 a->family = AF_INET6;
68
69 } else if (is_localhost(word)) {
70 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
71
72 a->family = AF_INET;
73 a->address.in.s_addr = htobe32(0x7f000000);
74 a->prefixlen = 8;
75 LIST_APPEND(items, *list, a);
76
77 a = new0(IPAddressAccessItem, 1);
78 if (!a)
79 return log_oom();
80
81 a->family = AF_INET6;
82 a->address.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
83 a->prefixlen = 128;
84
85 } else if (streq(word, "link-local")) {
86
87 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
88
89 a->family = AF_INET;
90 a->address.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
91 a->prefixlen = 16;
92 LIST_APPEND(items, *list, a);
93
94 a = new0(IPAddressAccessItem, 1);
95 if (!a)
96 return log_oom();
97
98 a->family = AF_INET6;
99 a->address.in6 = (struct in6_addr) {
100 .s6_addr32[0] = htobe32(0xfe800000)
101 };
102 a->prefixlen = 64;
103
104 } else if (streq(word, "multicast")) {
105
106 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
107
108 a->family = AF_INET;
109 a->address.in.s_addr = htobe32((UINT32_C(224) << 24));
110 a->prefixlen = 4;
111 LIST_APPEND(items, *list, a);
112
113 a = new0(IPAddressAccessItem, 1);
114 if (!a)
115 return log_oom();
116
117 a->family = AF_INET6;
118 a->address.in6 = (struct in6_addr) {
119 .s6_addr32[0] = htobe32(0xff000000)
120 };
121 a->prefixlen = 8;
122
123 } else {
124 r = in_addr_prefix_from_string_auto(word, &a->family, &a->address, &a->prefixlen);
125 if (r < 0) {
126 log_syntax(unit, LOG_WARNING, filename, line, r, "Address prefix is invalid, ignoring assignment: %s", word);
127 return 0;
128 }
129 }
130
131 LIST_APPEND(items, *list, a);
132 a = NULL;
133 }
134
135 *list = ip_address_access_reduce(*list);
136
137 return 0;
138 }
139
140 IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first) {
141 IPAddressAccessItem *next, *p = first;
142
143 while (p) {
144 next = p->items_next;
145 free(p);
146
147 p = next;
148 }
149
150 return NULL;
151 }
152
153 IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) {
154 IPAddressAccessItem *a, *b, *tmp;
155 int r;
156
157 /* Drops all entries from the list that are covered by another entry in full, thus removing all redundant
158 * entries. */
159
160 LIST_FOREACH_SAFE(items, a, tmp, first) {
161
162 /* Drop irrelevant bits */
163 (void) in_addr_mask(a->family, &a->address, a->prefixlen);
164
165 LIST_FOREACH(items, b, first) {
166
167 if (a == b)
168 continue;
169
170 if (a->family != b->family)
171 continue;
172
173 if (b->prefixlen > a->prefixlen)
174 continue;
175
176 r = in_addr_prefix_covers(b->family,
177 &b->address,
178 b->prefixlen,
179 &a->address);
180 if (r > 0) {
181 /* b covers a fully, then let's drop a */
182 LIST_REMOVE(items, first, a);
183 free(a);
184 break;
185 }
186 }
187 }
188
189 return first;
190 }
191
192 bool ip_address_access_item_is_any(IPAddressAccessItem *first) {
193 /* Check for exactly two entries */
194 if (!first || !first->items_next || first->items_next->items_next)
195 return false;
196
197 /* Check both entries cover the full range */
198 if (first->prefixlen != 0 || first->items_next->prefixlen != 0)
199 return false;
200
201 /* Check that one of them is the IPv4 and the other IPv6 */
202 if (!((first->family == AF_INET && first->items_next->family == AF_INET6) ||
203 (first->family == AF_INET6 && first->items_next->family == AF_INET)))
204 return false;
205
206 /* No need to check the actual addresses, they don't matter if the prefix is zero */
207 return true;
208 }