Merge pull request #20021 from wat-ze-hex/socket-bind-ip-proto-2021-06-23
core/bpf: support IP protocol in SocketBind{Allow|Deny}=
This commit is contained in:
@@ -772,26 +772,32 @@ BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook
|
||||
<para>Allow or deny binding a socket address to a socket by matching it with the <replaceable>bind-rule</replaceable> and
|
||||
applying a corresponding action if there is a match.</para>
|
||||
|
||||
<para><replaceable>bind-rule</replaceable> describes socket properties such as <replaceable>address-family</replaceable>
|
||||
and <replaceable>ip-ports</replaceable>.</para>
|
||||
<para><replaceable>bind-rule</replaceable> describes socket properties such as <replaceable>address-family</replaceable>,
|
||||
<replaceable>transport-protocol</replaceable> and <replaceable>ip-ports</replaceable>.</para>
|
||||
|
||||
<para><replaceable>bind-rule</replaceable> := [<replaceable>address-family</replaceable><constant>:</constant>]<replaceable>ip-ports</replaceable></para>
|
||||
<para><replaceable>bind-rule</replaceable> :=
|
||||
{ [<replaceable>address-family</replaceable><constant>:</constant>][<replaceable>transport-protocol</replaceable><constant>:</constant>][<replaceable>ip-ports</replaceable>] | <constant>any</constant> }</para>
|
||||
|
||||
<para><replaceable>address-family</replaceable> := { <constant>ipv4</constant> | <constant>ipv6</constant> }</para>
|
||||
|
||||
<para><replaceable>ip-ports</replaceable> := { <replaceable>ip-port</replaceable> | <replaceable>ip-port-range</replaceable> |
|
||||
<constant>any</constant> }</para>
|
||||
<para><replaceable>transport-protocol</replaceable> := { <constant>tcp</constant> | <constant>udp</constant> }</para>
|
||||
|
||||
<para>An optional <replaceable>address-family</replaceable> expects <constant>IPv4</constant> or <constant>IPv6</constant> values.
|
||||
If not specified, a rule will be matched for both IPv4 and IPv6 addresses and applied depending on other socket fields, e.g.
|
||||
<para><replaceable>ip-ports</replaceable> := { <replaceable>ip-port</replaceable> | <replaceable>ip-port-range</replaceable> }</para>
|
||||
|
||||
<para>An optional <replaceable>address-family</replaceable> expects <constant>ipv4</constant> or <constant>ipv6</constant> values.
|
||||
If not specified, a rule will be matched for both IPv4 and IPv6 addresses and applied depending on other socket fields, e.g. <replaceable>transport-protocol</replaceable>,
|
||||
<replaceable>ip-port</replaceable>.</para>
|
||||
|
||||
<para><replaceable>ip-port</replaceable> value must lie within 1…65535 interval inclusively, i.e.
|
||||
<para>An optional <replaceable>transport-protocol</replaceable> expects <constant>tcp</constant> or <constant>udp</constant> transport protocol names.
|
||||
If not specified, a rule will be matched for any transport protocol.</para>
|
||||
|
||||
<para>An optional <replaceable>ip-port</replaceable> value must lie within 1…65535 interval inclusively, i.e.
|
||||
dynamic port <constant>0</constant> is not allowed. A range of sequential ports is described by
|
||||
<replaceable>ip-port-range</replaceable> := <replaceable>ip-port-low</replaceable><constant>-</constant><replaceable>ip-port-high</replaceable>,
|
||||
where <replaceable>ip-port-low</replaceable> is smaller than or equal to <replaceable>ip-port-high</replaceable>
|
||||
and both are within 1…65535 inclusively. A special value <constant>any</constant>
|
||||
should be used to apply a rule to any port with a positive value.</para>
|
||||
and both are within 1…65535 inclusively.</para>
|
||||
|
||||
<para>A special value <constant>any</constant> can be used to apply a rule to any address family, transport protocol and any port with a positive value.</para>
|
||||
|
||||
<para>To allow multiple rules assign <varname>SocketBindAllow=</varname> or <varname>SocketBindDeny=</varname> multiple times.
|
||||
To clear the existing assignments pass an empty <varname>SocketBindAllow=</varname> or <varname>SocketBindDeny=</varname>
|
||||
@@ -825,11 +831,26 @@ SocketBindDeny=any
|
||||
…
|
||||
# Deny binding IPv6 socket addresses.
|
||||
[Service]
|
||||
SocketBindDeny=ipv6:any
|
||||
SocketBindDeny=ipv6
|
||||
…
|
||||
# Deny binding IPv4 and IPv6 socket addresses.
|
||||
[Service]
|
||||
SocketBindDeny=any
|
||||
…
|
||||
# Allow binding only over TCP
|
||||
[Service]
|
||||
SocketBindAllow=tcp
|
||||
SocketBindDeny=any
|
||||
…
|
||||
# Allow binding only over IPv6/TCP
|
||||
[Service]
|
||||
SocketBindAllow=ipv6:tcp
|
||||
SocketBindDeny=any
|
||||
…
|
||||
# Allow binding ports within 10000-65535 range over IPv4/UDP.
|
||||
[Service]
|
||||
SocketBindAllow=ipv4:udp:10000-65535
|
||||
SocketBindDeny=any
|
||||
…</programlisting></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -35,6 +35,7 @@ static int update_rules_map(
|
||||
LIST_FOREACH(socket_bind_items, item, head) {
|
||||
struct socket_bind_rule val = {
|
||||
.address_family = (uint32_t) item->address_family,
|
||||
.protocol = item->ip_protocol,
|
||||
.nr_ports = item->nr_ports,
|
||||
.port_min = item->port_min,
|
||||
};
|
||||
|
||||
@@ -11,35 +11,39 @@
|
||||
/*
|
||||
* Bind rule is matched with socket fields accessible to cgroup/bind{4,6} hook
|
||||
* through bpf_sock_addr struct.
|
||||
* address_family is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
|
||||
* 'address_family' is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
|
||||
* Matching by family is bypassed for rules with AF_UNSPEC set, which makes the
|
||||
* rest of a rule applicable for both IPv4 and IPv6 addresses.
|
||||
* If matching by family is either successful or bypassed, a rule and a socket
|
||||
* are matched by ports.
|
||||
* nr_ports and port_min fields specify a set of ports to match a user port
|
||||
* are matched by ip protocol.
|
||||
* If 'protocol' is 0, matching is bypassed.
|
||||
* 'nr_ports' and 'port_min' fields specify a set of ports to match a user port
|
||||
* with.
|
||||
* If nr_ports is 0, matching by port is bypassed, making that rule applicable
|
||||
* If 'nr_ports' is 0, matching by port is bypassed, making that rule applicable
|
||||
* for all possible ports, e.g. [1, 65535] range. Thus a rule with
|
||||
* address_family and nr_ports equal to AF_UNSPEC and 0 correspondingly forms
|
||||
* 'allow any' or 'deny any' cases.
|
||||
* For positive nr_ports, a user_port lying in a range from port_min to
|
||||
* port_min + nr_ports exclusively is considered to be a match. nr_ports
|
||||
* 'address_family', 'protocol' and 'nr_ports' equal to AF_UNSPEC, 0 and 0
|
||||
* correspondingly forms 'allow any' or 'deny any' cases.
|
||||
* For positive 'nr_ports', a user_port lying in a range from 'port_min' to'
|
||||
* 'port_min' + 'nr_ports' exclusively is considered to be a match. 'nr_ports'
|
||||
* equalling to 1 forms a rule for a single port.
|
||||
* Ports are in host order.
|
||||
*
|
||||
* Examples:
|
||||
* AF_UNSPEC, 1, 7777: match IPv4 and IPv6 addresses with 7777 user port;
|
||||
* AF_UNSPEC, 1, 0, 7777: match IPv4 and IPv6 addresses with 7777 user port;
|
||||
*
|
||||
* AF_INET, 1023, 1: match IPv4 addresses with user port in [1, 1023]
|
||||
* AF_INET, 1023, 0, 1: match IPv4 addresses with user port in [1, 1023]
|
||||
* range inclusively;
|
||||
*
|
||||
* AF_INET6, 0, 0: match IPv6 addresses;
|
||||
* AF_INET6, 0, 0, 0: match IPv6 addresses;
|
||||
*
|
||||
* AF_UNSPEC, 0, 0: match IPv4 and IPv6 addresses.
|
||||
* AF_UNSPEC, 0, 0, 0: match IPv4 and IPv6 addresses;
|
||||
*
|
||||
* AF_INET6, IPPROTO_TCP, 0, 0: match IPv6/TCP addresses.
|
||||
*/
|
||||
|
||||
struct socket_bind_rule {
|
||||
__u32 address_family;
|
||||
__u32 protocol;
|
||||
__u16 nr_ports;
|
||||
__u16 port_min;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,11 @@ static __always_inline bool match_af(
|
||||
return r->address_family == AF_UNSPEC || address_family == r->address_family;
|
||||
}
|
||||
|
||||
static __always_inline bool match_protocol(
|
||||
__u32 protocol, const struct socket_bind_rule *r) {
|
||||
return r->protocol == 0 || r->protocol == protocol;
|
||||
}
|
||||
|
||||
static __always_inline bool match_user_port(
|
||||
__u16 port, const struct socket_bind_rule *r) {
|
||||
return r->nr_ports == 0 ||
|
||||
@@ -47,9 +52,12 @@ static __always_inline bool match_user_port(
|
||||
|
||||
static __always_inline bool match(
|
||||
__u8 address_family,
|
||||
__u32 protocol,
|
||||
__u16 port,
|
||||
const struct socket_bind_rule *r) {
|
||||
return match_af(address_family, r) && match_user_port(port, r);
|
||||
return match_af(address_family, r) &&
|
||||
match_protocol(protocol, r) &&
|
||||
match_user_port(port, r);
|
||||
}
|
||||
|
||||
static __always_inline bool match_rules(
|
||||
@@ -67,7 +75,7 @@ static __always_inline bool match_rules(
|
||||
if (!rule)
|
||||
break;
|
||||
|
||||
if (match(ctx->user_family, port, rule))
|
||||
if (match(ctx->user_family, ctx->protocol, port, rule))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -20,6 +20,7 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "limits-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
@@ -593,18 +594,24 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
}
|
||||
|
||||
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f) {
|
||||
const char *family, *colon;
|
||||
const char *family, *colon1, *protocol = "", *colon2 = "";
|
||||
|
||||
family = strempty(af_to_ipv4_ipv6(item->address_family));
|
||||
colon = isempty(family) ? "" : ":";
|
||||
colon1 = isempty(family) ? "" : ":";
|
||||
|
||||
if (item->ip_protocol != 0) {
|
||||
protocol = ip_protocol_to_tcp_udp(item->ip_protocol);
|
||||
colon2 = ":";
|
||||
}
|
||||
|
||||
if (item->nr_ports == 0)
|
||||
fprintf(f, " %s%sany", family, colon);
|
||||
fprintf(f, " %s%s%s%sany", family, colon1, protocol, colon2);
|
||||
else if (item->nr_ports == 1)
|
||||
fprintf(f, " %s%s%" PRIu16, family, colon, item->port_min);
|
||||
fprintf(f, " %s%s%s%s%" PRIu16, family, colon1, protocol, colon2, item->port_min);
|
||||
else {
|
||||
uint16_t port_max = item->port_min + item->nr_ports - 1;
|
||||
fprintf(f, " %s%s%" PRIu16 "-%" PRIu16, family, colon, item->port_min, port_max);
|
||||
fprintf(f, " %s%s%s%s%" PRIu16 "-%" PRIu16, family, colon1, protocol, colon2,
|
||||
item->port_min, port_max);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -104,8 +104,8 @@ struct CGroupBPFForeignProgram {
|
||||
|
||||
struct CGroupSocketBindItem {
|
||||
LIST_FIELDS(CGroupSocketBindItem, socket_bind_items);
|
||||
int32_t address_family;
|
||||
int32_t ip_protocol;
|
||||
int address_family;
|
||||
int ip_protocol;
|
||||
uint16_t nr_ports;
|
||||
uint16_t port_min;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "limits-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -1895,8 +1896,8 @@ int bus_cgroup_set_property(
|
||||
if (!IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects INET or INET6 family, if specified.", name);
|
||||
|
||||
if (ip_protocol != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects ip protocol equals to 0, for the time being.", name);
|
||||
if (!IN_SET(ip_protocol, 0, IPPROTO_TCP, IPPROTO_UDP))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects TCP or UDP protocol, if specified.", name);
|
||||
|
||||
if (port_min + (uint32_t) nr_ports > (1 << 16))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects maximum port value lesser than 65536.", name);
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "log.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
@@ -5633,62 +5634,30 @@ int config_parse_cgroup_socket_bind(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
_cleanup_free_ CGroupSocketBindItem *item = NULL;
|
||||
const char *user_port;
|
||||
uint16_t nr_ports = 0, port_min = 0;
|
||||
CGroupSocketBindItem **head = data;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
int af, r;
|
||||
uint16_t nr_ports, port_min;
|
||||
int af, ip_protocol, r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
cgroup_context_remove_socket_bind(head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = extract_first_word(&rvalue, &word, ":", 0);
|
||||
r = parse_socket_bind_item(rvalue, &af, &ip_protocol, &nr_ports, &port_min);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r <= 0) {
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Unable to parse %s= assignment, ignoring: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rvalue) {
|
||||
af = af_from_ipv4_ipv6(word);
|
||||
if (af == AF_UNSPEC) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Only \"ipv4\" and \"ipv6\" protocols are supported, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
user_port = rvalue;
|
||||
} else {
|
||||
af = AF_UNSPEC;
|
||||
user_port = word;
|
||||
}
|
||||
|
||||
if (!streq(user_port, "any")) {
|
||||
uint16_t port_max;
|
||||
|
||||
r = parse_ip_port_range(user_port, &port_min, &port_max);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid port or port range, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nr_ports = 1 + port_max - port_min;
|
||||
}
|
||||
|
||||
item = new(CGroupSocketBindItem, 1);
|
||||
if (!item)
|
||||
return log_oom();
|
||||
*item = (CGroupSocketBindItem) {
|
||||
.address_family = af,
|
||||
/* No ip protocol specified for now. */
|
||||
.ip_protocol = 0,
|
||||
.ip_protocol = ip_protocol,
|
||||
.nr_ports = nr_ports,
|
||||
.port_min = port_min,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "mountpoint-util.h"
|
||||
#include "nsflags.h"
|
||||
#include "numa-util.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
@@ -868,42 +869,17 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 0);
|
||||
else {
|
||||
/* No ip protocol specified for now. */
|
||||
int32_t family = AF_UNSPEC, ip_protocol = 0;
|
||||
const char *address_family, *user_port;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
int32_t family, ip_protocol;
|
||||
uint16_t nr_ports, port_min;
|
||||
|
||||
r = extract_first_word(&eq, &word, ":", 0);
|
||||
r = parse_socket_bind_item(eq, &family, &ip_protocol, &nr_ports, &port_min);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s: %m", field);
|
||||
return log_error_errno(r, "Failed to parse %s", field);
|
||||
|
||||
address_family = eq ? word : NULL;
|
||||
if (address_family) {
|
||||
family = af_from_ipv4_ipv6(address_family);
|
||||
if (family == AF_UNSPEC)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Only \"ipv4\" and \"ipv6\" protocols are supported");
|
||||
}
|
||||
|
||||
user_port = eq ? eq : word;
|
||||
if (streq(user_port, "any")) {
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, 0, 0);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
} else {
|
||||
uint16_t port_min, port_max;
|
||||
|
||||
r = parse_ip_port_range(user_port, &port_min, &port_max);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Invalid port or port range: %s", user_port);
|
||||
|
||||
r = sd_bus_message_append(
|
||||
m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, port_max - port_min + 1, port_min);
|
||||
}
|
||||
r = sd_bus_message_append(
|
||||
m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, nr_ports, port_min);
|
||||
}
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
@@ -65,3 +65,13 @@ int parse_ip_protocol(const char *s) {
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
const char *ip_protocol_to_tcp_udp(int id) {
|
||||
return IN_SET(id, IPPROTO_TCP, IPPROTO_UDP) ?
|
||||
ip_protocol_to_name(id) : NULL;
|
||||
}
|
||||
|
||||
int ip_protocol_from_tcp_udp(const char *ip_protocol) {
|
||||
int id = ip_protocol_from_name(ip_protocol);
|
||||
return IN_SET(id, IPPROTO_TCP, IPPROTO_UDP) ? id : -EINVAL;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,6 @@
|
||||
const char *ip_protocol_to_name(int id);
|
||||
int ip_protocol_from_name(const char *name);
|
||||
int parse_ip_protocol(const char *s);
|
||||
|
||||
const char *ip_protocol_to_tcp_udp(int id);
|
||||
int ip_protocol_from_tcp_udp(const char *ip_protocol);
|
||||
|
||||
@@ -225,6 +225,8 @@ shared_sources = files('''
|
||||
pager.h
|
||||
parse-argument.c
|
||||
parse-argument.h
|
||||
parse-socket-bind-item.c
|
||||
parse-socket-bind-item.h
|
||||
pe-header.h
|
||||
pkcs11-util.c
|
||||
pkcs11-util.h
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "af-list.h"
|
||||
#include "extract-word.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
static int parse_af_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
int af;
|
||||
|
||||
assert(token);
|
||||
assert(family);
|
||||
|
||||
af = af_from_ipv4_ipv6(token);
|
||||
if (af == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
*family = af;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ip_protocol_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
int proto;
|
||||
|
||||
assert(token);
|
||||
assert(ip_protocol);
|
||||
|
||||
proto = ip_protocol_from_tcp_udp(token);
|
||||
if (proto < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*ip_protocol = proto;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ip_ports_token(
|
||||
const char *token,
|
||||
int *family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
assert(token);
|
||||
assert(nr_ports);
|
||||
assert(port_min);
|
||||
|
||||
if (streq(token, "any"))
|
||||
*nr_ports = *port_min = 0;
|
||||
else {
|
||||
uint16_t mn = 0, mx = 0;
|
||||
int r = parse_ip_port_range(token, &mn, &mx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*nr_ports = mx - mn + 1;
|
||||
*port_min = mn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*parse_token_f)(
|
||||
const char *,
|
||||
int *,
|
||||
int *,
|
||||
uint16_t *,
|
||||
uint16_t *);
|
||||
|
||||
int parse_socket_bind_item(
|
||||
const char *str,
|
||||
int *address_family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min) {
|
||||
/* Order of token parsers is important. */
|
||||
const parse_token_f parsers[] = {
|
||||
&parse_af_token,
|
||||
&parse_ip_protocol_token,
|
||||
&parse_ip_ports_token,
|
||||
};
|
||||
parse_token_f const *parser_ptr = parsers;
|
||||
int af = AF_UNSPEC, proto = 0, r;
|
||||
uint16_t nr = 0, mn = 0;
|
||||
const char *p = str;
|
||||
|
||||
assert(str);
|
||||
assert(address_family);
|
||||
assert(ip_protocol);
|
||||
assert(nr_ports);
|
||||
assert(port_min);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *token = NULL;
|
||||
|
||||
r = extract_first_word(&p, &token, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(token))
|
||||
return -EINVAL;
|
||||
|
||||
while (parser_ptr != parsers + ELEMENTSOF(parsers)) {
|
||||
r = (*parser_ptr)(token, &af, &proto, &nr, &mn);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
|
||||
++parser_ptr;
|
||||
/* Continue to next token if parsing succeeded,
|
||||
* otherwise apply next parser to the same token.
|
||||
*/
|
||||
if (r >= 0)
|
||||
break;
|
||||
}
|
||||
if (parser_ptr == parsers + ELEMENTSOF(parsers))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Failed to parse a token. */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Parsers applied succesfully, but end of the string not reached. */
|
||||
if (p)
|
||||
return -EINVAL;
|
||||
|
||||
*address_family = af;
|
||||
*ip_protocol = proto;
|
||||
*nr_ports = nr;
|
||||
*port_min = mn;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int parse_socket_bind_item(
|
||||
const char *str,
|
||||
int *address_family,
|
||||
int *ip_protocol,
|
||||
uint16_t *nr_ports,
|
||||
uint16_t *port_min);
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "journal-file.h"
|
||||
#include "list.h"
|
||||
#include "locale-util.h"
|
||||
@@ -1718,19 +1719,27 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
while ((r = sd_bus_message_read(m, "(iiqq)", &af, &ip_protocol, &nr_ports, &port_min)) > 0) {
|
||||
const char *family, *colon;
|
||||
const char *family, *colon1, *protocol = "", *colon2 = "";
|
||||
|
||||
family = strempty(af_to_ipv4_ipv6(af));
|
||||
colon = isempty(family) ? "" : ":";
|
||||
colon1 = isempty(family) ? "" : ":";
|
||||
|
||||
if (ip_protocol != 0) {
|
||||
protocol = ip_protocol_to_tcp_udp(ip_protocol);
|
||||
colon2 = "";
|
||||
}
|
||||
|
||||
if (nr_ports == 0)
|
||||
bus_print_property_valuef(name, expected_value, flags, "%s%sany", family, colon);
|
||||
bus_print_property_valuef(name, expected_value, flags, "%s%s%s%sany",
|
||||
family, colon1, protocol, colon2);
|
||||
else if (nr_ports == 1)
|
||||
bus_print_property_valuef(
|
||||
name, expected_value, flags, "%s%s%hu", family, colon, port_min);
|
||||
name, expected_value, flags, "%s%s%s%s%hu",
|
||||
family, colon1, protocol, colon2, port_min);
|
||||
else
|
||||
bus_print_property_valuef(
|
||||
name, expected_value, flags, "%s%s%hu-%hu", family, colon, port_min,
|
||||
name, expected_value, flags, "%s%s%s%s%hu-%hu",
|
||||
family, colon1, protocol, colon2, port_min,
|
||||
(uint16_t) (port_min + nr_ports - 1));
|
||||
}
|
||||
if (r < 0)
|
||||
|
||||
@@ -242,6 +242,8 @@ tests += [
|
||||
|
||||
[['src/test/test-parse-argument.c']],
|
||||
|
||||
[['src/test/test-parse-socket-bind-item.c']],
|
||||
|
||||
[['src/test/test-parse-util.c']],
|
||||
|
||||
[['src/test/test-sysctl-util.c']],
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "parse-socket-bind-item.h"
|
||||
|
||||
static void test_valid_item(
|
||||
const char *str,
|
||||
int expected_af,
|
||||
int expected_ip_protocol,
|
||||
uint16_t expected_nr_ports,
|
||||
uint16_t expected_port_min) {
|
||||
uint16_t nr_ports, port_min;
|
||||
int af, ip_protocol;
|
||||
|
||||
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) >= 0);
|
||||
assert_se(af == expected_af);
|
||||
assert_se(ip_protocol == expected_ip_protocol);
|
||||
assert_se(nr_ports == expected_nr_ports);
|
||||
assert_se(port_min == expected_port_min);
|
||||
|
||||
log_info("%s: \"%s\" ok", __func__, str);
|
||||
}
|
||||
|
||||
static void test_invalid_item(const char *str) {
|
||||
uint16_t nr_ports, port_min;
|
||||
int af, ip_protocol;
|
||||
|
||||
assert_se(parse_socket_bind_item(str, &af, &ip_protocol, &nr_ports, &port_min) == -EINVAL);
|
||||
|
||||
log_info("%s: \"%s\" ok", __func__, str);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_valid_item("any", AF_UNSPEC, 0, 0, 0);
|
||||
test_valid_item("ipv4", AF_INET, 0, 0, 0);
|
||||
test_valid_item("ipv6", AF_INET6, 0, 0, 0);
|
||||
test_valid_item("ipv4:any", AF_INET, 0, 0, 0);
|
||||
test_valid_item("ipv6:any", AF_INET6, 0, 0, 0);
|
||||
test_valid_item("tcp", AF_UNSPEC, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("udp", AF_UNSPEC, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("tcp:any", AF_UNSPEC, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("udp:any", AF_UNSPEC, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("6666", AF_UNSPEC, 0, 1, 6666);
|
||||
test_valid_item("6666-6667", AF_UNSPEC, 0, 2, 6666);
|
||||
test_valid_item("65535", AF_UNSPEC, 0, 1, 65535);
|
||||
test_valid_item("1-65535", AF_UNSPEC, 0, 65535, 1);
|
||||
test_valid_item("ipv4:tcp", AF_INET, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("ipv4:udp", AF_INET, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("ipv6:tcp", AF_INET6, IPPROTO_TCP, 0, 0);
|
||||
test_valid_item("ipv6:udp", AF_INET6, IPPROTO_UDP, 0, 0);
|
||||
test_valid_item("ipv4:6666", AF_INET, 0, 1, 6666);
|
||||
test_valid_item("ipv6:6666", AF_INET6, 0, 1, 6666);
|
||||
test_valid_item("tcp:6666", AF_UNSPEC, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("udp:6666", AF_UNSPEC, IPPROTO_UDP, 1, 6666);
|
||||
test_valid_item("ipv4:tcp:6666", AF_INET, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("ipv6:tcp:6666", AF_INET6, IPPROTO_TCP, 1, 6666);
|
||||
test_valid_item("ipv6:udp:6666-6667", AF_INET6, IPPROTO_UDP, 2, 6666);
|
||||
test_valid_item("ipv6:tcp:any", AF_INET6, IPPROTO_TCP, 0, 0);
|
||||
|
||||
test_invalid_item("");
|
||||
test_invalid_item(":");
|
||||
test_invalid_item("::");
|
||||
test_invalid_item("any:");
|
||||
test_invalid_item("meh");
|
||||
test_invalid_item("zupa:meh");
|
||||
test_invalid_item("zupa:meh:eh");
|
||||
test_invalid_item("ip");
|
||||
test_invalid_item("dccp");
|
||||
test_invalid_item("ipv6meh");
|
||||
test_invalid_item("ipv6::");
|
||||
test_invalid_item("ipv6:ipv6");
|
||||
test_invalid_item("ipv6:icmp");
|
||||
test_invalid_item("ipv6:tcp:0");
|
||||
test_invalid_item("65536");
|
||||
test_invalid_item("0-65535");
|
||||
test_invalid_item("ipv6:tcp:6666-6665");
|
||||
test_invalid_item("ipv6:tcp:6666-100000");
|
||||
test_invalid_item("ipv6::6666");
|
||||
test_invalid_item("ipv6:tcp:any:");
|
||||
test_invalid_item("ipv6:tcp:any:ipv6");
|
||||
test_invalid_item("ipv6:tcp:6666:zupa");
|
||||
test_invalid_item("ipv6:tcp:6666:any");
|
||||
test_invalid_item("ipv6:tcp:6666 zupa");
|
||||
test_invalid_item("ipv6:tcp:6666: zupa");
|
||||
test_invalid_item("ipv6:tcp:6666\n zupa");
|
||||
return 0;
|
||||
}
|
||||
@@ -146,6 +146,11 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("6667", "6668", ""), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "7777", STRV_MAKE_EMPTY, STRV_MAKE_EMPTY) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "8888", STRV_MAKE("any"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "8888", STRV_MAKE("ipv6:tcp:8888-8889"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "10000", STRV_MAKE("ipv6:udp:9999-10000"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("ipv4:tcp:6666"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("ipv4:udp:6666"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("tcp:6666"), STRV_MAKE("any")) >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user