]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: add support to configure ip rule port range and protocol. 10948/head
authorSusant Sahani <ssahani@gmail.com>
Tue, 27 Nov 2018 05:28:54 +0000 (10:58 +0530)
committerSusant Sahani <ssahani@gmail.com>
Wed, 28 Nov 2018 14:36:28 +0000 (20:06 +0530)
Please see:

iprule: support for ip_proto, sport and dport match options
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=f686f764682745daf6a93b0a6330ba42a961f858

Closes 10622

17 files changed:
man/systemd.network.xml
meson.build
src/basic/missing.h
src/basic/parse-util.c
src/basic/parse-util.h
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-network-gperf.gperf
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
src/systemd/sd-netlink.h
test/fuzz/fuzz-network-parser/25-fibrule-port-range.network [new file with mode: 0644]
test/fuzz/fuzz-network-parser/directives.network
test/test-network/conf/25-fibrule-port-range.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index 1bffedd00c673389ed5da7ae2dde6b5d20df206a..1b6a6d44dd8b77661fb0d1f4541c3d301f21393f 100644 (file)
             <para>Specifies the outgoing device to match. The outgoing interface is only available for packets originating from local sockets that are bound to a device.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>SourcePort=</varname></term>
+          <listitem>
+            <para>Specifies the source IP port or IP port range match in forwarding information base (FIB) rules.
+            A port range is specified by the lower and upper port separated by a dash. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>DestinationPort=</varname></term>
+          <listitem>
+            <para>Specifies the destination IP port or IP port range match in forwarding information base (FIB) rules.
+            A port range is specified by the lower and upper port separated by a dash. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>Protocol=</varname></term>
+          <listitem>
+            <para>Specifies the protocol to match in forwarding information base (FIB) rules. Accepted values are tcp, udp and sctp.
+            Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index 86e1c8932bc7c90d1bcc25e5bc6affa89e03a123..acccc22f6745e92c159f10166c021b143ce3e620 100644 (file)
@@ -426,6 +426,7 @@ decl_headers = '''
 foreach decl : ['char16_t',
                 'char32_t',
                 'struct fib_rule_uid_range',
+                'struct fib_rule_port_range',
                 'struct statx',
                ]
 
@@ -471,7 +472,7 @@ foreach decl : [['IFLA_INET6_ADDR_GEN_MODE',                'linux/if_link.h'],
                 ['IPVLAN_F_PRIVATE',                        'linux/if_link.h'],
                 ['NDA_IFINDEX',                             'linux/neighbour.h'],
                 ['IFA_FLAGS',                               'linux/if_addr.h'],
-                ['FRA_UID_RANGE',                           'linux/fib_rules.h'],
+                ['FRA_DPORT_RANGE',                         'linux/fib_rules.h'],
                 ['LO_FLAGS_PARTSCAN',                       'linux/loop.h'],
                 ['VXCAN_INFO_PEER',                         'linux/can/vxcan.h'],
                 ['FOU_ATTR_REMCSUM_NOPARTIAL',              'linux/fou.h'],
index c0c1d4066564f040be297ff345f569990322e153..99a463b7b6e305edd680f8c4534c3fc12f785825 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/falloc.h>
+#include <linux/fib_rules.h>
 #include <linux/if_link.h>
 #include <linux/input.h>
 #include <linux/loop.h>
@@ -988,7 +989,7 @@ struct input_mask {
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
 #endif
 
-#if !HAVE_FRA_UID_RANGE
+#if !HAVE_FRA_DPORT_RANGE
 #define FRA_UNSPEC 0
 #define FRA_DST 1
 #define FRA_SRC 2
@@ -1010,7 +1011,11 @@ struct input_mask {
 #define FRA_PAD 18
 #define FRA_L3MDEV 19
 #define FRA_UID_RANGE 20
-#define __FRA_MAX 12
+#define FRA_PROTOCOL 21
+#define FRA_IP_PROTO 22
+#define FRA_SPORT_RANGE 23
+#define FRA_DPORT_RANGE 24
+#define __FRA_MAX 25
 
 #define FRA_MAX (__FRA_MAX - 1)
 #endif
@@ -1311,6 +1316,15 @@ struct fib_rule_uid_range {
 
 #endif
 
+#if ! HAVE_STRUCT_FIB_RULE_PORT_RANGE
+
+struct fib_rule_port_range {
+        __u16 start;
+        __u16 end;
+};
+
+#endif
+
 #endif
 
 #ifndef SOL_ALG
index a9085348b555b64c733e42b10c858d9073660649..ce8bb12670bea1cb2f584670bb97b49b4515d78a 100644 (file)
@@ -710,6 +710,26 @@ int parse_ip_port(const char *s, uint16_t *ret) {
         return 0;
 }
 
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+        unsigned l, h;
+        int r;
+
+        r = parse_range(s, &l, &h);
+        if (r < 0)
+                return r;
+
+        if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+                return -EINVAL;
+
+        if (h < l)
+                return -EINVAL;
+
+        *low = l;
+        *high = h;
+
+        return 0;
+}
+
 int parse_dev(const char *s, dev_t *ret) {
         unsigned x, y;
         dev_t d;
index f3267f4cfeae85b357bd1ae934e7f2bd86d5a77f..e47641b429544de390efebd0e4a72e7585b08f49 100644 (file)
@@ -115,5 +115,6 @@ int parse_permille(const char *p);
 int parse_nice(const char *p, int *ret);
 
 int parse_ip_port(const char *s, uint16_t *ret);
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
 
 int parse_oom_score_adjust(const char *s, int *ret);
index 487a58ca7322c6edf741f156bc307b16da23108d..b0b25639f44e71d11597c42978726a9e84e24da3 100644 (file)
@@ -562,6 +562,25 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
         return RTA_PAYLOAD(rta);
 }
 
+int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) {
+        void *attr_data;
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        if (r < 0)
+                return r;
+
+        if ((size_t) r < size)
+                return -EIO;
+
+        if (data)
+                memcpy(data, attr_data, size);
+
+        return 0;
+}
+
 int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
         int r;
         void *attr_data;
index 307b5aa34349f556f55ad7e9aaaf58cd7f89e6a0..823e654d11fad23c6d1bbca0a27279952b655fda 100644 (file)
@@ -641,6 +641,10 @@ static const NLType rtnl_routing_policy_rule_types[] = {
         [FRA_PAD]                 = { .type = NETLINK_TYPE_U32 },
         [FRA_L3MDEV]              = { .type = NETLINK_TYPE_U64 },
         [FRA_UID_RANGE]           = { .size = sizeof(struct fib_rule_uid_range) },
+        [FRA_PROTOCOL]            = { .type = NETLINK_TYPE_U8 },
+        [FRA_IP_PROTO]            = { .type = NETLINK_TYPE_U8 },
+        [FRA_SPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
+        [FRA_DPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
 };
 
 static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
index 4af1a559b23465270d73b3f3a53ceb0051a748e7..02b102a47f6c6ef2ca826555506bcb8aec2bb96d 100644 (file)
@@ -794,7 +794,8 @@ static int link_set_routing_policy_rule(Link *link) {
 
         LIST_FOREACH(rules, rule, link->network->rules) {
                 r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
-                                            rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, &rrule);
+                                            rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif,
+                                            rule->protocol, &rule->sport, &rule->dport, &rrule);
                 if (r == 0) {
                         (void) routing_policy_rule_make_local(link->manager, rrule);
                         continue;
index fe481f1c0258b1bb0817102375cd98f2296d754d..3b1ae0b41fcf60371d7d39d2889e15953ed0e30a 100644 (file)
@@ -697,7 +697,8 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
 }
 
 int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+        uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0;
+        struct fib_rule_port_range sport = {}, dport = {};
         union in_addr_union to = {}, from = {};
         RoutingPolicyRule *rule = NULL;
         uint32_t fwmark = 0, table = 0;
@@ -829,12 +830,30 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 return 0;
         }
 
-        (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
+        r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &protocol);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(sport), (void *) &sport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(dport), (void *) &dport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
+
+        (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
 
         switch (type) {
         case RTM_NEWRULE:
                 if (!rule) {
-                        r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
+                        r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
                         if (r < 0) {
                                 log_warning_errno(r, "Could not add rule, ignoring: %m");
                                 return 0;
index 8bbdd290addd19ae37356b25dace125e0e799009..378b63af8c0ce36774a333af8b3b778710072ca0 100644 (file)
@@ -101,6 +101,9 @@ RoutingPolicyRule.From,                 config_parse_routing_policy_rule_prefix,
 RoutingPolicyRule.To,                   config_parse_routing_policy_rule_prefix,        0,                             0
 RoutingPolicyRule.IncomingInterface,    config_parse_routing_policy_rule_device,        0,                             0
 RoutingPolicyRule.OutgoingInterface,    config_parse_routing_policy_rule_device,        0,                             0
+RoutingPolicyRule.Protocol,             config_parse_routing_policy_rule_protocol,      0,                             0
+RoutingPolicyRule.SourcePort,           config_parse_routing_policy_rule_port_range,    0,                             0
+RoutingPolicyRule.DestinationPort,      config_parse_routing_policy_rule_port_range,    0,                             0
 Route.Gateway,                          config_parse_gateway,                           0,                             0
 Route.Destination,                      config_parse_destination,                       0,                             0
 Route.Source,                           config_parse_destination,                       0,                             0
index 683c166289e102d6b61c571e95eb47b5571fc72b..1b2222cada6dca371d7b2b888e77815d0518b2b3 100644 (file)
@@ -10,6 +10,7 @@
 #include "netlink-util.h"
 #include "networkd-manager.h"
 #include "parse-util.h"
+#include "socket-protocol-list.h"
 #include "socket-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -76,6 +77,10 @@ static void routing_policy_rule_hash_func(const void *b, struct siphash *state)
                 siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
                 siphash24_compress(&rule->table, sizeof(rule->table), state);
 
+                siphash24_compress(&rule->protocol, sizeof(rule->protocol), state);
+                siphash24_compress(&rule->sport, sizeof(rule->sport), state);
+                siphash24_compress(&rule->dport, sizeof(rule->dport), state);
+
                 if (rule->iif)
                         siphash24_compress(rule->iif, strlen(rule->iif), state);
 
@@ -128,6 +133,18 @@ static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
                 if (!r)
                         return r;
 
+                r = CMP(a->protocol, b->protocol);
+                if (r != 0)
+                        return r;
+
+                r = memcmp(&a->sport, &b->sport, sizeof(a->sport));
+                if (r != 0)
+                        return r;
+
+                r = memcmp(&a->dport, &b->dport, sizeof(a->dport));
+                if (r != 0)
+                        return r;
+
                 r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
                 if (r != 0)
                         return r;
@@ -156,6 +173,9 @@ int routing_policy_rule_get(Manager *m,
                             uint32_t table,
                             const char *iif,
                             const char *oif,
+                            uint8_t protocol,
+                            struct fib_rule_port_range *sport,
+                            struct fib_rule_port_range *dport,
                             RoutingPolicyRule **ret) {
 
         RoutingPolicyRule rule, *existing;
@@ -172,7 +192,10 @@ int routing_policy_rule_get(Manager *m,
                 .fwmark = fwmark,
                 .table = table,
                 .iif = (char*) iif,
-                .oif = (char*) oif
+                .oif = (char*) oif,
+                .protocol = protocol,
+                .sport = *sport,
+                .dport = *dport,
         };
 
         existing = set_get(m->rules, &rule);
@@ -222,6 +245,9 @@ static int routing_policy_rule_add_internal(Manager *m,
                                             uint32_t table,
                                             const char *_iif,
                                             const char *_oif,
+                                            uint8_t protocol,
+                                            const struct fib_rule_port_range *sport,
+                                            const struct fib_rule_port_range *dport,
                                             RoutingPolicyRule **ret) {
 
         _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
@@ -257,6 +283,9 @@ static int routing_policy_rule_add_internal(Manager *m,
         rule->table = table;
         rule->iif = iif;
         rule->oif = oif;
+        rule->protocol = protocol;
+        rule->sport = *sport;
+        rule->dport = *dport;
 
         r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
         if (r < 0)
@@ -286,9 +315,12 @@ int routing_policy_rule_add(Manager *m,
                             uint32_t table,
                             const char *iif,
                             const char *oif,
+                            uint8_t protocol,
+                            const struct fib_rule_port_range *sport,
+                            const struct fib_rule_port_range *dport,
                             RoutingPolicyRule **ret) {
 
-        return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+        return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
 }
 
 int routing_policy_rule_add_foreign(Manager *m,
@@ -302,8 +334,11 @@ int routing_policy_rule_add_foreign(Manager *m,
                                     uint32_t table,
                                     const char *iif,
                                     const char *oif,
+                                    uint8_t protocol,
+                                    const struct fib_rule_port_range *sport,
+                                    const struct fib_rule_port_range *dport,
                                     RoutingPolicyRule **ret) {
-        return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+        return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
 }
 
 static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
@@ -544,6 +579,22 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlin
                         return log_error_errno(r, "Could not append FRA_OIFNAME attribute: %m");
         }
 
+        r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->protocol);
+        if (r < 0)
+                return log_error_errno(r, "Could not append FRA_IP_PROTO attribute: %m");
+
+        if (rule->sport.start != 0 || rule->sport.end != 0) {
+                r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport));
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_SPORT_RANGE attribute: %m");
+        }
+
+        if (rule->dport.start != 0 || rule->dport.end != 0) {
+                r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport));
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_DPORT_RANGE attribute: %m");
+        }
+
         rule->link = link;
 
         r = sd_netlink_call_async(link->manager->rtnl, NULL, m, callback,
@@ -554,7 +605,7 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlin
         link_ref(link);
 
         r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
-                                    rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, NULL);
+                                    rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, rule->protocol, &rule->sport, &rule->dport, NULL);
         if (r < 0)
                 return log_error_errno(r, "Could not add rule: %m");
 
@@ -836,6 +887,95 @@ int config_parse_routing_policy_rule_device(
         return 0;
 }
 
+int config_parse_routing_policy_rule_port_range(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
+        Network *network = userdata;
+        uint16_t low, high;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = routing_policy_rule_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = parse_ip_port_range(rvalue, &low, &high);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "SourcePort")) {
+                n->sport.start = low;
+                n->sport.end = high;
+        } else {
+                n->dport.start = low;
+                n->dport.end = high;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_protocol(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
+        Network *network = userdata;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = routing_policy_rule_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = socket_protocol_from_name(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule protocol, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!IN_SET(r, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid protocol '%s'. Protocol should be tcp/udp/sctp, ignoring", rvalue);
+                return 0;
+        }
+
+        n->protocol = r;
+
+        n = NULL;
+
+        return 0;
+}
+
 static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
         _cleanup_free_ char *s = NULL;
         size_t size;
@@ -918,6 +1058,27 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
                         space = true;
                 }
 
+                if (rule->protocol != 0) {
+                        fprintf(f, "%sprotocol=%hhu",
+                                space ? " " : "",
+                                rule->protocol);
+                        space = true;
+                }
+
+                if (rule->sport.start != 0 || rule->sport.end != 0) {
+                        fprintf(f, "%ssourcesport=%hhu-%hhu",
+                                space ? " " : "",
+                                rule->sport.start, rule->sport.end);
+                        space = true;
+                }
+
+                if (rule->dport.start != 0 || rule->dport.end != 0) {
+                        fprintf(f, "%sdestinationport=%hhu-%hhu",
+                                space ? " " : "",
+                                rule->dport.start, rule->dport.end);
+                        space = true;
+                }
+
                 fprintf(f, "%stable=%"PRIu32 "\n",
                         space ? " " : "",
                         rule->table);
@@ -929,6 +1090,7 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
 int routing_policy_load_rules(const char *state_file, Set **rules) {
         _cleanup_strv_free_ char **l = NULL;
         _cleanup_free_ char *data = NULL;
+        uint16_t low = 0, high = 0;
         const char *p;
         char **i;
         int r;
@@ -1023,6 +1185,33 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
 
                                 if (free_and_strdup(&rule->oif, b) < 0)
                                         return log_oom();
+                        } else if (streq(a, "protocol")) {
+                                r = safe_atou8(b, &rule->protocol);
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to parse RPDB rule protocol, ignoring: %s", b);
+                                        continue;
+                                }
+                        } else if (streq(a, "sourceport")) {
+
+                                r = parse_ip_port_range(b, &low, &high);
+                                if (r < 0) {
+                                        log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment:'%s'", b);
+                                        continue;
+                                }
+
+                                rule->sport.start = low;
+                                rule->sport.end = high;
+
+                        } else if (streq(a, "destinationport")) {
+
+                                r = parse_ip_port_range(b, &low, &high);
+                                if (r < 0) {
+                                        log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment:'%s'", b);
+                                        continue;
+                                }
+
+                                rule->dport.start = low;
+                                rule->dport.end = high;
                         }
                 }
 
index 2e7474ef56e4629f2cbc69987d13111ce75cacae..3bb3b4cd8110447e77dc4506541935c8ced98691 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <inttypes.h>
+#include <linux/fib_rules.h>
 #include <stdbool.h>
 
 #include "in-addr-util.h"
@@ -24,6 +25,7 @@ struct RoutingPolicyRule {
         NetworkConfigSection *section;
 
         uint8_t tos;
+        uint8_t protocol;
 
         uint32_t table;
         uint32_t fwmark;
@@ -40,6 +42,9 @@ struct RoutingPolicyRule {
         union in_addr_union to;
         union in_addr_union from;
 
+        struct fib_rule_port_range sport;
+        struct fib_rule_port_range dport;
+
         LIST_FIELDS(RoutingPolicyRule, rules);
 };
 
@@ -54,11 +59,14 @@ int link_routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message
 int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
 
 int routing_policy_rule_add(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
-                            uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, RoutingPolicyRule **ret);
+                            uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
+                            const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
 int routing_policy_rule_add_foreign(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
-                                    uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, RoutingPolicyRule **ret);
+                                    uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
+                                    const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
 int routing_policy_rule_get(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, uint8_t tos,
-                            uint32_t fwmark, uint32_t table, const char *iif, const char *oif, RoutingPolicyRule **ret);
+                            uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, struct fib_rule_port_range *sport,
+                            struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
 int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule);
 int routing_policy_serialize_rules(Set *rules, FILE *f);
 int routing_policy_load_rules(const char *state_file, Set **rules);
@@ -70,3 +78,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_fwmark_mask);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_prefix);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_priority);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_device);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_port_range);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_protocol);
index 276953eb8ea5129cc249d00065fbf4690609ad96..e1b89559a8bf095d582b219a15566cd86efd03b9 100644 (file)
@@ -85,6 +85,7 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
 int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key);
 int sd_netlink_message_close_container(sd_netlink_message *m);
 
+int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
 int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
 int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data);
 int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data);
diff --git a/test/fuzz/fuzz-network-parser/25-fibrule-port-range.network b/test/fuzz/fuzz-network-parser/25-fibrule-port-range.network
new file mode 100644 (file)
index 0000000..b050217
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+SourcePort = 1123-1150
+DestinationPort = 3224-3290
+Protocol = tcp
index 766c76008fc08a4cb1ccb4776458a684d57ceb4a..7508f693ddbec6cce301010ee7828bd357648ad4 100644 (file)
@@ -150,6 +150,9 @@ From=
 TypeOfService=
 Priority=
 FirewallMark=
+SourcePort=
+DestinationPort=
+Protocol=
 [IPv6PrefixDelegation]
 RouterPreference=
 DNSLifetimeSec=
diff --git a/test/test-network/conf/25-fibrule-port-range.network b/test/test-network/conf/25-fibrule-port-range.network
new file mode 100644 (file)
index 0000000..b050217
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+SourcePort = 1123-1150
+DestinationPort = 3224-3290
+Protocol = tcp
index 73ecf6f73f869cd1404e49c613082809d1efe84b..f9372d27ee613e507cb2e487ff6fe4f522b56f3f 100755 (executable)
@@ -427,9 +427,9 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities):
     units = ['12-dummy.netdev', 'test-static.network', 'configure-without-carrier.network', '11-dummy.netdev',
              '23-primary-slave.network', '23-test1-bond199.network', '11-dummy.netdev', '23-bond199.network',
              '25-bond-active-backup-slave.netdev', '12-dummy.netdev', '23-active-slave.network',
-             'routing-policy-rule.network', '25-address-section.network', '25-address-section-miscellaneous.network',
-             '25-route-section.network', '25-route-type.network', '25-route-tcp-window-settings.network',
-             '25-route-gateway.network', '25-route-gateway-on-link.network',
+             'routing-policy-rule.network', '25-fibrule-port-range.network', '25-address-section.network',
+             '25-address-section-miscellaneous.network', '25-route-section.network', '25-route-type.network',
+             '25-route-tcp-window-settings.network', '25-route-gateway.network', '25-route-gateway-on-link.network',
              '25-address-link-section.network', '25-ipv6-address-label-section.network', '25-link-section-unmanaged.network',
              '25-sysctl.network']
 
@@ -496,6 +496,20 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'oif test1')
         self.assertRegex(output, 'lookup 7')
 
+    def test_routing_policy_rule_port_range(self):
+        self.copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
+        self.start_networkd()
+
+        self.assertTrue(self.link_exits('test1'))
+        output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+        print(output)
+        self.assertRegex(output, '111')
+        self.assertRegex(output, 'from 192.168.100.18')
+        self.assertRegex(output, '1123-1150')
+        self.assertRegex(output, '3224-3290')
+        self.assertRegex(output, 'tcp')
+        self.assertRegex(output, 'lookup 7')
+
     def test_address_preferred_lifetime_zero_ipv6(self):
         self.copy_unit_to_networkd_unit_path('25-address-section-miscellaneous.network', '12-dummy.netdev')
         self.start_networkd()