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