]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/ip-address-access.c
core: only warn about BPF/cgroup missing once per runtime (#7319)
[thirdparty/systemd.git] / src / core / ip-address-access.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2016 Daniel Mack
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "alloc-util.h"
24 #include "bpf-firewall.h"
25 #include "extract-word.h"
26 #include "hostname-util.h"
27 #include "ip-address-access.h"
28 #include "parse-util.h"
29 #include "string-util.h"
30
31 int config_parse_ip_address_access(
32 const char *unit,
33 const char *filename,
34 unsigned line,
35 const char *section,
36 unsigned section_line,
37 const char *lvalue,
38 int ltype,
39 const char *rvalue,
40 void *data,
41 void *userdata) {
42
43 IPAddressAccessItem **list = data;
44 const char *p;
45 int r;
46
47 assert(list);
48
49 if (isempty(rvalue)) {
50 *list = ip_address_access_free_all(*list);
51 return 0;
52 }
53
54 p = rvalue;
55
56 for (;;) {
57 _cleanup_free_ IPAddressAccessItem *a = NULL;
58 _cleanup_free_ char *word = NULL;
59
60 r = extract_first_word(&p, &word, NULL, 0);
61 if (r == 0)
62 break;
63 if (r == -ENOMEM)
64 return log_oom();
65 if (r < 0) {
66 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
67 break;
68 }
69
70 a = new0(IPAddressAccessItem, 1);
71 if (!a)
72 return log_oom();
73
74 if (streq(word, "any")) {
75 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
76
77 a->family = AF_INET;
78 LIST_APPEND(items, *list, a);
79
80 a = new0(IPAddressAccessItem, 1);
81 if (!a)
82 return log_oom();
83
84 a->family = AF_INET6;
85
86 } else if (is_localhost(word)) {
87 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
88
89 a->family = AF_INET;
90 a->address.in.s_addr = htobe32(0x7f000000);
91 a->prefixlen = 8;
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) IN6ADDR_LOOPBACK_INIT;
100 a->prefixlen = 128;
101
102 } else if (streq(word, "link-local")) {
103
104 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
105
106 a->family = AF_INET;
107 a->address.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
108 a->prefixlen = 16;
109 LIST_APPEND(items, *list, a);
110
111 a = new0(IPAddressAccessItem, 1);
112 if (!a)
113 return log_oom();
114
115 a->family = AF_INET6;
116 a->address.in6 = (struct in6_addr) {
117 .s6_addr32[0] = htobe32(0xfe800000)
118 };
119 a->prefixlen = 64;
120
121 } else if (streq(word, "multicast")) {
122
123 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
124
125 a->family = AF_INET;
126 a->address.in.s_addr = htobe32((UINT32_C(224) << 24));
127 a->prefixlen = 4;
128 LIST_APPEND(items, *list, a);
129
130 a = new0(IPAddressAccessItem, 1);
131 if (!a)
132 return log_oom();
133
134 a->family = AF_INET6;
135 a->address.in6 = (struct in6_addr) {
136 .s6_addr32[0] = htobe32(0xff000000)
137 };
138 a->prefixlen = 8;
139
140 } else {
141 r = in_addr_prefix_from_string_auto(word, &a->family, &a->address, &a->prefixlen);
142 if (r < 0) {
143 log_syntax(unit, LOG_WARNING, filename, line, r, "Address prefix is invalid, ignoring assignment: %s", word);
144 return 0;
145 }
146 }
147
148 LIST_APPEND(items, *list, a);
149 a = NULL;
150 }
151
152 *list = ip_address_access_reduce(*list);
153
154 if (*list) {
155 r = bpf_firewall_supported();
156 if (r < 0)
157 return r;
158 if (r == 0) {
159 static bool warned = false;
160
161 log_full(warned ? LOG_DEBUG : LOG_WARNING,
162 "File %s:%u configures an IP firewall (%s=%s), but the local system does not support BPF/cgroup based firewalling.\n"
163 "Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
164
165 warned = true;
166 }
167 }
168
169 return 0;
170 }
171
172 IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first) {
173 IPAddressAccessItem *next, *p = first;
174
175 while (p) {
176 next = p->items_next;
177 free(p);
178
179 p = next;
180 }
181
182 return NULL;
183 }
184
185 IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) {
186 IPAddressAccessItem *a, *b, *tmp;
187 int r;
188
189 /* Drops all entries from the list that are covered by another entry in full, thus removing all redundant
190 * entries. */
191
192 LIST_FOREACH_SAFE(items, a, tmp, first) {
193
194 /* Drop irrelevant bits */
195 (void) in_addr_mask(a->family, &a->address, a->prefixlen);
196
197 LIST_FOREACH(items, b, first) {
198
199 if (a == b)
200 continue;
201
202 if (a->family != b->family)
203 continue;
204
205 if (b->prefixlen > a->prefixlen)
206 continue;
207
208 r = in_addr_prefix_covers(b->family,
209 &b->address,
210 b->prefixlen,
211 &a->address);
212 if (r <= 0)
213 continue;
214
215 /* b covers a fully, then let's drop a */
216
217 LIST_REMOVE(items, first, a);
218 free(a);
219 }
220 }
221
222 return first;
223 }