1 /* SPDX-License-Identifier: LGPL-2.1+ */
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"
14 int config_parse_ip_address_access(
19 unsigned section_line
,
26 IPAddressAccessItem
**list
= data
;
32 if (isempty(rvalue
)) {
33 *list
= ip_address_access_free_all(*list
);
40 _cleanup_free_ IPAddressAccessItem
*a
= NULL
;
41 _cleanup_free_
char *word
= NULL
;
43 r
= extract_first_word(&p
, &word
, NULL
, 0);
49 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
53 a
= new0(IPAddressAccessItem
, 1);
57 if (streq(word
, "any")) {
58 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
61 LIST_APPEND(items
, *list
, a
);
63 a
= new0(IPAddressAccessItem
, 1);
69 } else if (is_localhost(word
)) {
70 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
73 a
->address
.in
.s_addr
= htobe32(0x7f000000);
75 LIST_APPEND(items
, *list
, a
);
77 a
= new0(IPAddressAccessItem
, 1);
82 a
->address
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
85 } else if (streq(word
, "link-local")) {
87 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
90 a
->address
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
92 LIST_APPEND(items
, *list
, a
);
94 a
= new0(IPAddressAccessItem
, 1);
99 a
->address
.in6
= (struct in6_addr
) {
100 .s6_addr32
[0] = htobe32(0xfe800000)
104 } else if (streq(word
, "multicast")) {
106 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
109 a
->address
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
111 LIST_APPEND(items
, *list
, a
);
113 a
= new0(IPAddressAccessItem
, 1);
117 a
->family
= AF_INET6
;
118 a
->address
.in6
= (struct in6_addr
) {
119 .s6_addr32
[0] = htobe32(0xff000000)
124 r
= in_addr_prefix_from_string_auto(word
, &a
->family
, &a
->address
, &a
->prefixlen
);
126 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Address prefix is invalid, ignoring assignment: %s", word
);
131 LIST_APPEND(items
, *list
, a
);
135 *list
= ip_address_access_reduce(*list
);
140 IPAddressAccessItem
* ip_address_access_free_all(IPAddressAccessItem
*first
) {
141 IPAddressAccessItem
*next
, *p
= first
;
144 next
= p
->items_next
;
153 IPAddressAccessItem
* ip_address_access_reduce(IPAddressAccessItem
*first
) {
154 IPAddressAccessItem
*a
, *b
, *tmp
;
157 /* Drops all entries from the list that are covered by another entry in full, thus removing all redundant
160 LIST_FOREACH_SAFE(items
, a
, tmp
, first
) {
162 /* Drop irrelevant bits */
163 (void) in_addr_mask(a
->family
, &a
->address
, a
->prefixlen
);
165 LIST_FOREACH(items
, b
, first
) {
170 if (a
->family
!= b
->family
)
173 if (b
->prefixlen
> a
->prefixlen
)
176 r
= in_addr_prefix_covers(b
->family
,
181 /* b covers a fully, then let's drop a */
182 LIST_REMOVE(items
, first
, a
);
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
)
197 /* Check both entries cover the full range */
198 if (first
->prefixlen
!= 0 || first
->items_next
->prefixlen
!= 0)
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
)))
206 /* No need to check the actual addresses, they don't matter if the prefix is zero */