]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
b36672e0 DM |
2 | |
3 | #include <stdio.h> | |
4 | #include <stdlib.h> | |
5 | ||
6 | #include "alloc-util.h" | |
078ba556 | 7 | #include "bpf-firewall.h" |
b36672e0 DM |
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) { | |
5a10b4d6 | 100 | .s6_addr32[0] = htobe32(0xfe800000) |
b36672e0 DM |
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) { | |
5a10b4d6 | 119 | .s6_addr32[0] = htobe32(0xff000000) |
b36672e0 DM |
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 | ||
1274b6c6 LP |
135 | *list = ip_address_access_reduce(*list); |
136 | ||
b36672e0 DM |
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 | } | |
1274b6c6 LP |
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); | |
8ed7742a LP |
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 | } | |
1274b6c6 LP |
186 | } |
187 | } | |
188 | ||
189 | return first; | |
190 | } | |
4c1567f2 AZ |
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 | } |