]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bpf: add ip proto matching to socket-bind prog
authorJulia Kartseva <hex@fb.com>
Sat, 12 Jun 2021 01:27:27 +0000 (18:27 -0700)
committerJulia Kartseva <hex@fb.com>
Tue, 29 Jun 2021 21:37:07 +0000 (14:37 -0700)
Lookup ip protocol in a socket address to allow or deny binding a socket
to the address.
Matching rule is extended with 'protocol' field. If its value is 0
(IPPROTO_IP) ip protocol comparison is omitted and matching is passed to
the next token which is ip ports.

Documentation is updated.

src/core/bpf-socket-bind.c
src/core/bpf/socket_bind/socket-bind-api.bpf.h
src/core/bpf/socket_bind/socket-bind.bpf.c

index 66c82d54699753a7396fd6da2d0c49da863dc6ca..9983691575579f8ed8f29b58be59362b8362ab8e 100644 (file)
@@ -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,
                 };
index 418889955bbcf9093ef90979f0c6473a9df241f5..277b9bbde25714882e993ba55b1277f270d60a73 100644 (file)
 /*
  * 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;
 };
index 474808d82449aecf873feb58ec03330a791eac41..8004400e6ca1cfca4671d8ad8e6a020dcb870748 100644 (file)
@@ -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;
         }