Line data Source code
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 0 : 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 0 : IPAddressAccessItem **list = data;
27 : const char *p;
28 : int r;
29 :
30 0 : assert(list);
31 :
32 0 : if (isempty(rvalue)) {
33 0 : *list = ip_address_access_free_all(*list);
34 0 : return 0;
35 : }
36 :
37 0 : p = rvalue;
38 :
39 0 : for (;;) {
40 0 : _cleanup_free_ IPAddressAccessItem *a = NULL;
41 0 : _cleanup_free_ char *word = NULL;
42 :
43 0 : r = extract_first_word(&p, &word, NULL, 0);
44 0 : if (r == 0)
45 0 : break;
46 0 : if (r == -ENOMEM)
47 0 : return log_oom();
48 0 : if (r < 0) {
49 0 : log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
50 0 : break;
51 : }
52 :
53 0 : a = new0(IPAddressAccessItem, 1);
54 0 : if (!a)
55 0 : return log_oom();
56 :
57 0 : if (streq(word, "any")) {
58 : /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
59 :
60 0 : a->family = AF_INET;
61 0 : LIST_APPEND(items, *list, a);
62 :
63 0 : a = new0(IPAddressAccessItem, 1);
64 0 : if (!a)
65 0 : return log_oom();
66 :
67 0 : a->family = AF_INET6;
68 :
69 0 : } else if (is_localhost(word)) {
70 : /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
71 :
72 0 : a->family = AF_INET;
73 0 : a->address.in.s_addr = htobe32(0x7f000000);
74 0 : a->prefixlen = 8;
75 0 : LIST_APPEND(items, *list, a);
76 :
77 0 : a = new0(IPAddressAccessItem, 1);
78 0 : if (!a)
79 0 : return log_oom();
80 :
81 0 : a->family = AF_INET6;
82 0 : a->address.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
83 0 : a->prefixlen = 128;
84 :
85 0 : } else if (streq(word, "link-local")) {
86 :
87 : /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
88 :
89 0 : a->family = AF_INET;
90 0 : a->address.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
91 0 : a->prefixlen = 16;
92 0 : LIST_APPEND(items, *list, a);
93 :
94 0 : a = new0(IPAddressAccessItem, 1);
95 0 : if (!a)
96 0 : return log_oom();
97 :
98 0 : a->family = AF_INET6;
99 0 : a->address.in6 = (struct in6_addr) {
100 0 : .s6_addr32[0] = htobe32(0xfe800000)
101 : };
102 0 : a->prefixlen = 64;
103 :
104 0 : } else if (streq(word, "multicast")) {
105 :
106 : /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
107 :
108 0 : a->family = AF_INET;
109 0 : a->address.in.s_addr = htobe32((UINT32_C(224) << 24));
110 0 : a->prefixlen = 4;
111 0 : LIST_APPEND(items, *list, a);
112 :
113 0 : a = new0(IPAddressAccessItem, 1);
114 0 : if (!a)
115 0 : return log_oom();
116 :
117 0 : a->family = AF_INET6;
118 0 : a->address.in6 = (struct in6_addr) {
119 0 : .s6_addr32[0] = htobe32(0xff000000)
120 : };
121 0 : a->prefixlen = 8;
122 :
123 : } else {
124 0 : r = in_addr_prefix_from_string_auto(word, &a->family, &a->address, &a->prefixlen);
125 0 : if (r < 0) {
126 0 : log_syntax(unit, LOG_WARNING, filename, line, r, "Address prefix is invalid, ignoring assignment: %s", word);
127 0 : return 0;
128 : }
129 : }
130 :
131 0 : LIST_APPEND(items, *list, a);
132 0 : a = NULL;
133 : }
134 :
135 0 : *list = ip_address_access_reduce(*list);
136 :
137 0 : return 0;
138 : }
139 :
140 1178 : IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first) {
141 1178 : IPAddressAccessItem *next, *p = first;
142 :
143 1178 : while (p) {
144 0 : next = p->items_next;
145 0 : free(p);
146 :
147 0 : p = next;
148 : }
149 :
150 1178 : return NULL;
151 : }
152 :
153 0 : 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 0 : LIST_FOREACH_SAFE(items, a, tmp, first) {
161 :
162 : /* Drop irrelevant bits */
163 0 : (void) in_addr_mask(a->family, &a->address, a->prefixlen);
164 :
165 0 : LIST_FOREACH(items, b, first) {
166 :
167 0 : if (a == b)
168 0 : continue;
169 :
170 0 : if (a->family != b->family)
171 0 : continue;
172 :
173 0 : if (b->prefixlen > a->prefixlen)
174 0 : continue;
175 :
176 0 : r = in_addr_prefix_covers(b->family,
177 0 : &b->address,
178 0 : b->prefixlen,
179 0 : &a->address);
180 0 : if (r > 0) {
181 : /* b covers a fully, then let's drop a */
182 0 : LIST_REMOVE(items, first, a);
183 0 : free(a);
184 0 : break;
185 : }
186 : }
187 : }
188 :
189 0 : return first;
190 : }
191 :
192 0 : bool ip_address_access_item_is_any(IPAddressAccessItem *first) {
193 : /* Check for exactly two entries */
194 0 : if (!first || !first->items_next || first->items_next->items_next)
195 0 : return false;
196 :
197 : /* Check both entries cover the full range */
198 0 : if (first->prefixlen != 0 || first->items_next->prefixlen != 0)
199 0 : return false;
200 :
201 : /* Check that one of them is the IPv4 and the other IPv6 */
202 0 : if (!((first->family == AF_INET && first->items_next->family == AF_INET6) ||
203 0 : (first->family == AF_INET6 && first->items_next->family == AF_INET)))
204 0 : return false;
205 :
206 : /* No need to check the actual addresses, they don't matter if the prefix is zero */
207 0 : return true;
208 : }
|