]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: add support to configure IP Rule (#5725)
authorSusant Sahani <ssahani@users.noreply.github.com>
Thu, 14 Sep 2017 19:51:39 +0000 (19:51 +0000)
committerLennart Poettering <lennart@poettering.net>
Thu, 14 Sep 2017 19:51:39 +0000 (21:51 +0200)
Routing Policy rule manipulates rules in the routing policy database control the
route selection algorithm.

This work supports to configure Rule
```
[RoutingPolicyRule]
TypeOfService=0x08
Table=7
From= 192.168.100.18

```

```
ip rule show
0: from all lookup local
0: from 192.168.100.18 tos 0x08 lookup 7
```

V2 changes:

1. Added logic to handle duplicate rules.
2. If rules are changed or deleted and networkd restarted
   then those are deleted when networkd restarts next time

V3:

1. Add parse_fwmark_fwmask

19 files changed:
man/systemd.network.xml
meson.build
src/basic/missing.h
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-netlink/rtnl-message.c
src/libsystemd/sd-netlink/sd-netlink.c
src/network/meson.build
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-routing-policy-rule.c [new file with mode: 0644]
src/network/networkd-routing-policy-rule.h [new file with mode: 0644]
src/network/networkd.c
src/systemd/sd-netlink.h

index 96d38113721ef713949a7e450377f4cee327cc12..ea3bf71eea4d372de162ac9a4a7a00c82755d025 100644 (file)
       </variablelist>
   </refsect1>
 
+ <refsect1>
+    <title>[RoutingPolicyRule] Section Options</title>
+
+      <para>An <literal>[RoutingPolicyRule]</literal> section accepts the
+      following keys. Specify several <literal>[RoutingPolicyRule]</literal>
+      sections to configure several rules.</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>TypeOfService=</varname></term>
+          <listitem>
+            <para>Specifies the type of service to match a number between 0 to 255.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>From=</varname></term>
+          <listitem>
+            <para>Specifies the source address prefix to match. Possibly followed by a slash and the prefix length.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>To=</varname></term>
+          <listitem>
+            <para>Specifies the destination address prefix to match. Possibly followed by a slash and the prefix length.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>FirewallMark=</varname></term>
+          <listitem>
+            <para>Specifies the iptables firewall mark value to match (a number between 1 and 4294967295).</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>Table=</varname></term>
+          <listitem>
+            <para>Specifies the routing table identifier to lookup if the rule
+            selector matches. The table identifier for a route (a number between 1 and 4294967295).</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>Priority=</varname></term>
+          <listitem>
+            <para>Specifies the priority of this rule. <varname>Priority=</varname> is an unsigned
+            integer. Higher number means lower priority, and rules get processed in order of increasing number.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[Route] Section Options</title>
       <para>The <literal>[Route]</literal> section accepts the
index 5fb90ddea331df6627ba925ece443e1b25865222..af17035586693a73f0e7a259a1f6e42fb770f88a 100644 (file)
@@ -374,6 +374,7 @@ conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h
 decl_headers = '''
 #include <uchar.h>
 #include <linux/ethtool.h>
+#include <linux/fib_rules.h>
 '''
 # FIXME: key_serial_t is only defined in keyutils.h, this is bound to fail
 
@@ -381,6 +382,7 @@ foreach decl : ['char16_t',
                 'char32_t',
                 'key_serial_t',
                 'struct ethtool_link_settings',
+                'struct fib_rule_uid_range',
                ]
 
         # We get -1 if the size cannot be determined
@@ -409,6 +411,7 @@ foreach decl : [['IFLA_INET6_ADDR_GEN_MODE',         'linux/if_link.h'],
                 ['IFLA_BR_VLAN_DEFAULT_PVID',        'linux/if_link.h'],
                 ['NDA_IFINDEX',                      'linux/neighbour.h'],
                 ['IFA_FLAGS',                        'linux/if_addr.h'],
+                ['FRA_UID_RANGE',                    'linux/fib_rules.h'],
                 ['LO_FLAGS_PARTSCAN',                'linux/loop.h'],
                ]
         prefix = decl.length() > 2 ? decl[2] : ''
index 0269066427cc2762127da470e3549c0b17eaaa60..653c5b7766e6ecc98cf219e5c56cd8da162bd57e 100644 (file)
@@ -920,6 +920,33 @@ struct input_mask {
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
 #endif
 
+#if !HAVE_DECL_FRA_UID_RANGE
+#define FRA_UNSPEC 0
+#define FRA_DST 1
+#define FRA_SRC 2
+#define FRA_IIFNAME 3
+#define FRA_GOTO 4
+#define FRA_UNUSED2 5
+#define FRA_PRIORITY 6
+#define FRA_UNUSED3 7
+#define FRA_UNUSED4 8
+#define FRA_UNUSED5 9
+#define FRA_FWMARK 10
+#define FRA_FLOW 11
+#define FRA_TUN_ID 12
+#define FRA_SUPPRESS_IFGROUP 13
+#define FRA_SUPPRESS_PREFIXLEN 14
+#define FRA_TABLE 15
+#define FRA_FWMASK 16
+#define FRA_OIFNAME 17
+#define FRA_PAD 18
+#define FRA_L3MDEV 19
+#define FRA_UID_RANGE 20
+#define __FRA_MAX 12
+
+#define FRA_MAX (__FRA_MAX - 1)
+#endif
+
 #if !HAVE_DECL_IFLA_BRPORT_PROXYARP
 #define IFLA_BRPORT_PROXYARP 10
 #endif
@@ -1216,6 +1243,15 @@ struct ethtool_link_settings {
 
 #endif
 
+#ifndef HAVE_STRUCT_FIB_RULE_UID_RANGE
+
+struct fib_rule_uid_range {
+        __u32 start;
+        __u32 end;
+};
+
+#endif
+
 #endif
 
 #ifndef SOL_ALG
index e8c8abac2ae352efe29623c0610a5e56686943a7..ac0427f94939af6f412ce3cb93be14e152050b30 100644 (file)
@@ -101,12 +101,8 @@ int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
 int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(m->hdr->nlmsg_type == RTM_GETLINK  ||
-                      m->hdr->nlmsg_type == RTM_GETADDR  ||
-                      m->hdr->nlmsg_type == RTM_GETROUTE ||
-                      m->hdr->nlmsg_type == RTM_GETNEIGH ||
-                      m->hdr->nlmsg_type == RTM_GETADDRLABEL ,
-                      -EINVAL);
+
+        assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL);
 
         SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);
 
index 923f7dd10c9b1af413e26c9181afb98bbabba62b..979dc6824f340a5619392bc76256461306f348c8 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/can/netlink.h>
+#include <linux/fib_rules.h>
 #include <linux/in6.h>
 #include <linux/veth.h>
 #include <linux/if_bridge.h>
 #include <linux/if_addrlabel.h>
 #include <linux/if.h>
 #include <linux/ip.h>
+#include <linux/if_addr.h>
+#include <linux/if_bridge.h>
 #include <linux/if_link.h>
 #include <linux/if_tunnel.h>
+#include <linux/fib_rules.h>
 
 #include "macro.h"
 #include "missing.h"
@@ -597,6 +601,31 @@ static const NLTypeSystem rtnl_addrlabel_type_system = {
         .types = rtnl_addrlabel_types,
 };
 
+static const NLType rtnl_routing_policy_rule_types[] = {
+        [FRA_DST]                 = { .type = NETLINK_TYPE_IN_ADDR },
+        [FRA_SRC]                 = { .type = NETLINK_TYPE_IN_ADDR },
+        [FRA_IIFNAME]             = { .type = NETLINK_TYPE_STRING },
+        [RTA_OIF]                 = { .type = NETLINK_TYPE_U32 },
+        [RTA_GATEWAY]             = { .type = NETLINK_TYPE_IN_ADDR },
+        [FRA_PRIORITY]            = { .type = NETLINK_TYPE_U32 },
+        [FRA_FWMARK]              = { .type = NETLINK_TYPE_U32 },
+        [FRA_FLOW]                = { .type = NETLINK_TYPE_U32 },
+        [FRA_TUN_ID]              = { .type = NETLINK_TYPE_U32 },
+        [FRA_SUPPRESS_IFGROUP]    = { .type = NETLINK_TYPE_U32 },
+        [FRA_SUPPRESS_PREFIXLEN]  = { .type = NETLINK_TYPE_U32 },
+        [FRA_TABLE]               = { .type = NETLINK_TYPE_U32 },
+        [FRA_FWMASK]              = { .type = NETLINK_TYPE_U32 },
+        [FRA_OIFNAME]             = { .type = NETLINK_TYPE_STRING },
+        [FRA_PAD]                 = { .type = NETLINK_TYPE_U32 },
+        [FRA_L3MDEV]              = { .type = NETLINK_TYPE_U64 },
+        [FRA_UID_RANGE]           = { .size = sizeof(struct fib_rule_uid_range) },
+};
+
+static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
+        .count = ELEMENTSOF(rtnl_routing_policy_rule_types),
+        .types = rtnl_routing_policy_rule_types,
+};
+
 static const NLType rtnl_types[] = {
         [NLMSG_DONE]       = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
         [NLMSG_ERROR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
@@ -616,6 +645,9 @@ static const NLType rtnl_types[] = {
         [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
         [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
         [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
+        [RTM_NEWRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
+        [RTM_DELRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
+        [RTM_GETRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
 };
 
 const NLTypeSystem type_system_root = {
index 215af124063baa6fb67b88fa1cd146390f5f90ec..d2fb65120430db4856038087ccc77a18557123b2 100644 (file)
@@ -47,6 +47,10 @@ static inline bool rtnl_message_type_is_addrlabel(uint16_t type) {
         return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL);
 }
 
+static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
+        return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
+}
+
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
 int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu);
 
index 12c51ffe2e6728a5126a25fa75b7f9b847701ee8..586d94f624a35273e1640844b19014578a02e551 100644 (file)
@@ -696,6 +696,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) {
 
                 *family = ifa->ifa_family;
 
+                return 0;
+        } else if (rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type)) {
+                struct rtmsg *rtm;
+
+                rtm = NLMSG_DATA(m->hdr);
+
+                *family = rtm->rtm_family;
+
                 return 0;
         }
 
@@ -754,3 +762,166 @@ int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char
 
         return 0;
 }
+
+int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family) {
+        struct rtmsg *rtm;
+        int r;
+
+        assert_return(rtnl_message_type_is_routing_policy_rule(nlmsg_type), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = message_new(rtnl, ret, nlmsg_type);
+        if (r < 0)
+                return r;
+
+        if (nlmsg_type == RTM_NEWRULE)
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+
+        rtm = NLMSG_DATA((*ret)->hdr);
+        rtm->rtm_family = ifal_family;
+        rtm->rtm_protocol = RTPROT_BOOT;
+        rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+        rtm->rtm_type = RTN_UNICAST;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        routing_policy_rule->rtm_tos = tos;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_tos(sd_netlink_message *m, unsigned char *tos) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        *tos = routing_policy_rule->rtm_tos;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        routing_policy_rule->rtm_table = table;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_table(sd_netlink_message *m, unsigned char *table) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        *table = routing_policy_rule->rtm_table;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        routing_policy_rule->rtm_type = type;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsigned char *type) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        *type =  routing_policy_rule->rtm_type;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        routing_policy_rule->rtm_dst_len = len;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char *len) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        *len = routing_policy_rule->rtm_dst_len;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        routing_policy_rule->rtm_src_len = len;
+
+        return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(sd_netlink_message *m, unsigned char *len) {
+        struct rtmsg *routing_policy_rule;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+        routing_policy_rule = NLMSG_DATA(m->hdr);
+
+        *len = routing_policy_rule->rtm_src_len;
+
+        return 0;
+}
index d67244676c27c453dfcdba80c8438ca47ba83f97..77f4d5b635738046701a832a3e102e363cba6f93 100644 (file)
@@ -894,6 +894,16 @@ int sd_netlink_add_match(sd_netlink *rtnl,
                         if (r < 0)
                                 return r;
                         break;
+                case RTM_NEWRULE:
+                case RTM_DELRULE:
+                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
+                        if (r < 0)
+                                return r;
+
+                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
+                        if (r < 0)
+                                return r;
+                        break;
                 default:
                         return -EOPNOTSUPP;
         }
index 35ecd86379b53d967dc8e863e24d7f8f9d8f12eb..83a837d6c9c640510170477f11f16c2aa645afaf 100644 (file)
@@ -61,6 +61,8 @@ sources = files('''
         networkd-network.h
         networkd-route.c
         networkd-route.h
+        networkd-routing-policy-rule.c
+        networkd-routing-policy-rule.h
         networkd-util.c
         networkd-util.h
 '''.split())
index 2fb1dd67abec7459910db3cfd0251f45cf51940b..6b591271a02178f885410f4971f824925dfc56f8 100644 (file)
@@ -33,6 +33,7 @@
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-radv.h"
+#include "networkd-routing-policy-rule.h"
 #include "set.h"
 #include "socket-util.h"
 #include "stdio-util.h"
@@ -497,8 +498,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
 
 static void link_free(Link *link) {
         Address *address;
-        Iterator i;
         Link *carrier;
+        Iterator i;
 
         if (!link)
                 return;
@@ -1014,6 +1015,7 @@ static int link_set_bridge_fdb(Link *link) {
 }
 
 static int link_enter_set_addresses(Link *link) {
+        RoutingPolicyRule *rule, *rrule = NULL;
         AddressLabel *label;
         Address *ad;
         int r;
@@ -1050,6 +1052,26 @@ static int link_enter_set_addresses(Link *link) {
                 link->link_messages++;
         }
 
+        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, &rrule);
+                if (r == 1) {
+                        (void) routing_policy_rule_make_local(link->manager, rrule);
+                        continue;
+                }
+
+                r = routing_policy_rule_configure(rule, link, link_routing_policy_rule_handler, false);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
+                        link_enter_failed(link);
+                        return r;
+                }
+
+                link->link_messages++;
+        }
+
+        routing_policy_rule_purge(link->manager, link);
+
         /* now that we can figure out a default address for the dhcp server,
            start it */
         if (link_dhcp4_server_enabled(link)) {
index 45f9b3d359b3cd406e0974ca0c3a4389729b6cba..82b9663019a6fd896331ab093d3373ee63d942dd 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <sys/socket.h>
 #include <linux/if.h>
+#include <linux/fib_rules.h>
 
 #include "sd-daemon.h"
 #include "sd-netlink.h"
@@ -718,6 +719,113 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
         return 1;
 }
 
+int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+        uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+        RoutingPolicyRule *rule = NULL;
+        union in_addr_union to, from;
+        uint32_t fwmark = 0, table = 0;
+        Manager *m = userdata;
+        uint16_t type;
+        int family;
+        int r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_warning_errno(r, "rtnl: failed to receive rule: %m");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
+                log_warning("rtnl: received unexpected message type '%u' when processing rule.", type);
+                return 0;
+        }
+
+        r = sd_rtnl_message_get_family(message, &family);
+        if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+                log_warning_errno(r, "rtnl: received address with invalid family type %u, ignoring.", type);
+                return 0;
+        }
+
+        switch (family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in);
+                if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+                        if (r < 0)
+                                log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+                }
+
+                r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in);
+                if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+                        if (r < 0)
+                                log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+                }
+
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6);
+                if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+                        if (r < 0)
+                                log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6);
+                if (r >= 0) {
+                        r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+                        if (r < 0)
+                                log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+                }
+
+                break;
+
+        default:
+                log_debug("rtnl: ignoring unsupported rule family: %d", family);
+        }
+
+        if (from_prefixlen == 0 && to_prefixlen == 0)
+                return 0;
+
+        (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark);
+        (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table);
+        (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos);
+
+        (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &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, &rule);
+                        if (r < 0) {
+                                log_warning_errno(r, "Could not add rule: %m");
+                                return 0;
+                        }
+                }
+                break;
+        case RTM_DELRULE:
+                routing_policy_rule_free(rule);
+
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
+}
+
 static int systemd_netlink_fd(void) {
         int n, fd, rtnl_fd = -EINVAL;
 
@@ -782,6 +890,14 @@ static int manager_connect_rtnl(Manager *m) {
         if (r < 0)
                 return r;
 
+        r = sd_netlink_add_match(m->rtnl, RTM_NEWRULE, &manager_rtnl_process_rule, m);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_add_match(m->rtnl, RTM_DELRULE, &manager_rtnl_process_rule, m);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -875,6 +991,8 @@ static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
 
 static int manager_save(Manager *m) {
         _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
+        RoutingPolicyRule *rule = NULL;
+        bool space = false;
         Link *link;
         Iterator i;
         _cleanup_free_ char *temp_path = NULL;
@@ -999,6 +1117,28 @@ static int manager_save(Manager *m) {
         print_string_set(f, "DOMAINS=", search_domains);
         print_string_set(f, "ROUTE_DOMAINS=", route_domains);
 
+        SET_FOREACH(rule, m->rules, i) {
+                _cleanup_free_ char *from_str = NULL, *to_str = NULL;
+                fputs("RULE=", f);
+
+                if (!in_addr_is_null(rule->family, &rule->from)) {
+                        r = in_addr_to_string(rule->family, &rule->from, &from_str);
+                        if (r < 0)
+                                goto fail;
+                }
+
+                if (!in_addr_is_null(rule->family, &rule->to)) {
+                        r = in_addr_to_string(rule->family, &rule->to, &to_str);
+                        if (r < 0)
+                                goto fail;
+                }
+
+                fprintf(f, "from=%s%s/%hhu to=%s%s/%hhu tos=%hhu fwmark=%"PRIu32"/%"PRIu32" table=%hhu", space ? " " : "", from_str,
+                        rule->from_prefixlen, space ? " " : "", to_str, rule->to_prefixlen, rule->tos, rule->fwmark, rule->fwmask, rule->table);
+
+                fputc('\n', f);
+        }
+
         r = fflush_and_check(f);
         if (r < 0)
                 goto fail;
@@ -1084,6 +1224,8 @@ int manager_new(Manager **ret, sd_event *event) {
 
         m->duid.type = DUID_TYPE_EN;
 
+        (void) routing_policy_rule_load(m);
+
         *ret = m;
         m = NULL;
 
@@ -1091,6 +1233,7 @@ int manager_new(Manager **ret, sd_event *event) {
 }
 
 void manager_free(Manager *m) {
+        RoutingPolicyRule *rule;
         Network *network;
         NetDev *netdev;
         Link *link;
@@ -1101,13 +1244,13 @@ void manager_free(Manager *m) {
 
         free(m->state_file);
 
+        while ((network = m->networks))
+                network_free(network);
+
         while ((link = hashmap_first(m->links)))
                 link_unref(link);
         hashmap_free(m->links);
 
-        while ((network = m->networks))
-                network_free(network);
-
         hashmap_free(m->networks_by_name);
 
         while ((netdev = hashmap_first(m->netdevs)))
@@ -1117,6 +1260,14 @@ void manager_free(Manager *m) {
         while ((pool = m->address_pools))
                 address_pool_free(pool);
 
+        set_free(m->rules);
+        set_free(m->rules_foreign);
+
+        while ((rule = set_steal_first(m->rules_saved)))
+                free(rule);
+
+        set_free(m->rules_saved);
+
         sd_netlink_unref(m->rtnl);
         sd_event_unref(m->event);
 
@@ -1277,6 +1428,41 @@ int manager_rtnl_enumerate_routes(Manager *m) {
         return r;
 }
 
+int manager_rtnl_enumerate_rules(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        sd_netlink_message *rule;
+        int r;
+
+        assert(m);
+        assert(m->rtnl);
+
+        r = sd_rtnl_message_new_routing_policy_rule(m->rtnl, &req, RTM_GETRULE, 0);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_request_dump(req, true);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(m->rtnl, req, 0, &reply);
+        if (r < 0)
+                return r;
+
+        for (rule = reply; rule; rule = sd_netlink_message_next(rule)) {
+                int k;
+
+                m->enumerating = true;
+
+                k = manager_rtnl_process_rule(m->rtnl, rule, m);
+                if (k < 0)
+                        r = k;
+
+                m->enumerating = false;
+        }
+
+        return r;
+}
+
 int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
         AddressPool *p;
         int r;
index e2447c2230ea38cc3971eb9f45c408cbc2a01c63..254aab845273b2860e5340cd83f2b126efbb4883 100644 (file)
@@ -65,6 +65,10 @@ struct Manager {
         DUID duid;
         char* dynamic_hostname;
         char* dynamic_timezone;
+
+        Set *rules;
+        Set *rules_foreign;
+        Set *rules_saved;
 };
 
 static inline const DUID* link_duid(const Link *link) {
@@ -88,9 +92,11 @@ bool manager_should_reload(Manager *m);
 int manager_rtnl_enumerate_links(Manager *m);
 int manager_rtnl_enumerate_addresses(Manager *m);
 int manager_rtnl_enumerate_routes(Manager *m);
+int manager_rtnl_enumerate_rules(Manager *m);
 
 int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 
 int manager_send_changed(Manager *m, const char *property, ...) _sentinel_;
 void manager_dirty(Manager *m);
index 112efd2dfb8da62274a3aa2533dff7aa790981f3..f01e78a356406fe186aac3677c721c5e7c217906 100644 (file)
@@ -84,6 +84,12 @@ Address.AutoJoin,                       config_parse_address_flags,
 Address.Scope,                          config_parse_address_scope,                     0,                             0
 IPv6AddressLabel.Prefix,                config_parse_address_label_prefix,              0,                             0
 IPv6AddressLabel.Label,                 config_parse_address_label,                     0,                             0
+RoutingPolicyRule.TypeOfService,        config_parse_routing_policy_rule_tos,           0,                             0
+RoutingPolicyRule.Priority,             config_parse_routing_policy_rule_priority,      0,                             0
+RoutingPolicyRule.Table,                config_parse_routing_policy_rule_table,         0,                             0
+RoutingPolicyRule.FirewallMark,         config_parse_routing_policy_rule_fwmark_mask,   0,                             0
+RoutingPolicyRule.From,                 config_parse_routing_policy_rule_prefix,        0,                             0
+RoutingPolicyRule.To,                   config_parse_routing_policy_rule_prefix,        0,                             0
 Route.Gateway,                          config_parse_gateway,                           0,                             0
 Route.Destination,                      config_parse_destination,                       0,                             0
 Route.Source,                           config_parse_destination,                       0,                             0
index 3b52a8a5b96f97d19d03f3f1e7bbcb855181cd4c..1694b1e5eb53f7444ec140cf58affe6ca1930ae9 100644 (file)
@@ -77,7 +77,7 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi
 }
 
 void network_config_section_free(NetworkConfigSection *cs) {
-          free(cs);
+        free(cs);
 }
 
 /* Set defaults following RFC7844 */
@@ -157,6 +157,7 @@ static int network_load_one(Manager *manager, const char *filename) {
         LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
         LIST_HEAD_INIT(network->address_labels);
         LIST_HEAD_INIT(network->static_prefixes);
+        LIST_HEAD_INIT(network->rules);
 
         network->stacked_netdevs = hashmap_new(&string_hash_ops);
         if (!network->stacked_netdevs)
@@ -182,6 +183,10 @@ static int network_load_one(Manager *manager, const char *filename) {
         if (!network->prefixes_by_section)
                 return log_oom();
 
+        network->rules_by_section = hashmap_new(&network_config_hash_ops);
+        if (!network->rules_by_section)
+                return log_oom();
+
         network->filename = strdup(filename);
         if (!network->filename)
                 return log_oom();
@@ -258,6 +263,7 @@ static int network_load_one(Manager *manager, const char *filename) {
                               "Network\0"
                               "Address\0"
                               "IPv6AddressLabel\0"
+                              "RoutingPolicyRule\0"
                               "Route\0"
                               "DHCP\0"
                               "DHCPv4\0" /* compat */
@@ -336,13 +342,14 @@ int network_load(Manager *manager) {
 }
 
 void network_free(Network *network) {
-        NetDev *netdev;
-        Route *route;
-        Address *address;
-        FdbEntry *fdb_entry;
         IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
+        RoutingPolicyRule *rule;
+        FdbEntry *fdb_entry;
         AddressLabel *label;
         Prefix *prefix;
+        Address *address;
+        NetDev *netdev;
+        Route *route;
         Iterator i;
 
         if (!network)
@@ -396,11 +403,15 @@ void network_free(Network *network) {
         while ((prefix = network->static_prefixes))
                 prefix_free(prefix);
 
+        while ((rule = network->rules))
+                routing_policy_rule_free(rule);
+
         hashmap_free(network->addresses_by_section);
         hashmap_free(network->routes_by_section);
         hashmap_free(network->fdb_entries_by_section);
         hashmap_free(network->address_labels_by_section);
         hashmap_free(network->prefixes_by_section);
+        hashmap_free(network->rules_by_section);
 
         if (network->manager) {
                 if (network->manager->networks)
@@ -746,7 +757,7 @@ int config_parse_tunnel(const char *unit,
             netdev->kind != NETDEV_KIND_VTI &&
             netdev->kind != NETDEV_KIND_VTI6 &&
             netdev->kind != NETDEV_KIND_IP6TNL
-            ) {
+        ) {
                 log_syntax(unit, LOG_ERR, filename, line, 0,
                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
                 return 0;
index 035b57d5f264d723d5e4a34a06921d2354f9c1e2..7af3171ef9a46a2cc3c8e9a14cc229e20ffc34db 100644 (file)
@@ -34,6 +34,7 @@
 #include "networkd-lldp-tx.h"
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-route.h"
+#include "networkd-routing-policy-rule.h"
 #include "networkd-util.h"
 #include "netdev/netdev.h"
 
@@ -218,6 +219,7 @@ struct Network {
         LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
         LIST_HEAD(AddressLabel, address_labels);
         LIST_HEAD(Prefix, static_prefixes);
+        LIST_HEAD(RoutingPolicyRule, rules);
 
         unsigned n_static_addresses;
         unsigned n_static_routes;
@@ -225,12 +227,14 @@ struct Network {
         unsigned n_ipv6_proxy_ndp_addresses;
         unsigned n_address_labels;
         unsigned n_static_prefixes;
+        unsigned n_rules;
 
         Hashmap *addresses_by_section;
         Hashmap *routes_by_section;
         Hashmap *fdb_entries_by_section;
         Hashmap *address_labels_by_section;
         Hashmap *prefixes_by_section;
+        Hashmap *rules_by_section;
 
         struct in_addr_data *dns;
         unsigned n_dns;
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
new file mode 100644 (file)
index 0000000..6850135
--- /dev/null
@@ -0,0 +1,900 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2017 Susant Sahani
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/if.h>
+#include <linux/fib_rules.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "fileio.h"
+#include "networkd-routing-policy-rule.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+
+int routing_policy_rule_new(RoutingPolicyRule **ret) {
+        RoutingPolicyRule *rule;
+
+        rule = new0(RoutingPolicyRule, 1);
+        if (!rule)
+                return -ENOMEM;
+
+        rule->family = AF_INET;
+        rule->table = RT_TABLE_MAIN;
+
+        *ret = rule;
+        return 0;
+}
+
+void routing_policy_rule_free(RoutingPolicyRule *rule) {
+
+        if (!rule)
+                return;
+
+        if (rule->network) {
+                LIST_REMOVE(rules, rule->network->rules, rule);
+                assert(rule->network->n_rules > 0);
+                rule->network->n_rules--;
+
+                if (rule->section) {
+                        hashmap_remove(rule->network->rules_by_section, rule->section);
+                        network_config_section_free(rule->section);
+                }
+
+                if (rule->network->manager) {
+                        set_remove(rule->network->manager->rules, rule);
+                        set_remove(rule->network->manager->rules_foreign, rule);
+                }
+        }
+
+        free(rule);
+}
+
+static void routing_policy_rule_hash_func(const void *b, struct siphash *state) {
+        const RoutingPolicyRule *rule = b;
+
+        assert(rule);
+
+        siphash24_compress(&rule->family, sizeof(rule->family), state);
+
+        switch (rule->family) {
+        case AF_INET:
+        case AF_INET6:
+
+                siphash24_compress(&rule->from, FAMILY_ADDRESS_SIZE(rule->family), state);
+                siphash24_compress(&rule->from_prefixlen, sizeof(rule->from_prefixlen), state);
+
+                siphash24_compress(&rule->to, FAMILY_ADDRESS_SIZE(rule->family), state);
+                siphash24_compress(&rule->to_prefixlen, sizeof(rule->to_prefixlen), state);
+
+                siphash24_compress(&rule->tos, sizeof(rule->tos), state);
+                siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
+                siphash24_compress(&rule->table, sizeof(rule->table), state);
+
+                break;
+        default:
+                /* treat any other address family as AF_UNSPEC */
+                break;
+        }
+}
+
+static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
+        const RoutingPolicyRule *a = _a, *b = _b;
+        int r;
+
+        if (a->family < b->family)
+                return -1;
+        if (a->family > b->family)
+                return 1;
+
+        switch (a->family) {
+        case AF_INET:
+        case AF_INET6:
+                if (a->from_prefixlen < b->from_prefixlen)
+                        return -1;
+                if (a->from_prefixlen > b->from_prefixlen)
+                        return 1;
+
+                if (a->to_prefixlen < b->to_prefixlen)
+                        return -1;
+                if (a->to_prefixlen > b->to_prefixlen)
+                        return 1;
+
+                if (a->tos < b->tos)
+                        return -1;
+                if (a->tos > b->tos)
+                        return 1;
+
+                if (a->fwmask < b->fwmark)
+                        return -1;
+                if (a->fwmask > b->fwmark)
+                        return 1;
+
+                if (a->table < b->table)
+                        return -1;
+                if (a->table > b->table)
+                        return 1;
+
+                r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
+                if (r != 0)
+                        return r;
+
+                return memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
+
+        default:
+                /* treat any other address family as AF_UNSPEC */
+                return 0;
+        }
+}
+
+const struct hash_ops routing_policy_rule_hash_ops = {
+        .hash = routing_policy_rule_hash_func,
+        .compare = routing_policy_rule_compare_func
+};
+
+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,
+                            RoutingPolicyRule **ret) {
+
+        RoutingPolicyRule rule, *existing;
+
+        assert_return(m, -1);
+
+        rule = (RoutingPolicyRule) {
+                .family = family,
+                .from = *from,
+                .from_prefixlen = from_prefixlen,
+                .to = *to,
+                .to_prefixlen = to_prefixlen,
+                .tos = tos,
+                .fwmark = fwmark,
+                .table = table,
+        };
+
+        if (m->rules) {
+                existing = set_get(m->rules, &rule);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 1;
+                }
+        }
+
+        if (m->rules_foreign) {
+                existing = set_get(m->rules_foreign, &rule);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 1;
+                }
+        }
+
+        return -ENOENT;
+}
+
+int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
+        int r;
+
+        assert(m);
+
+        if (set_contains(m->rules_foreign, rule)) {
+                set_remove(m->rules_foreign, rule);
+
+                r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops);
+                if (r < 0)
+                        return r;
+
+                return set_put(m->rules, rule);
+        }
+
+        return -ENOENT;
+}
+
+static int routing_policy_rule_add_internal(Set **rules,
+                                            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,
+                                            RoutingPolicyRule **ret) {
+
+        _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL;
+        int r;
+
+        assert_return(rules, -EINVAL);
+
+        r = routing_policy_rule_new(&rule);
+        if (r < 0)
+                return r;
+
+        rule->family = family;
+        rule->from = *from;
+        rule->from_prefixlen = from_prefixlen;
+        rule->to = *to;
+        rule->to_prefixlen = to_prefixlen;
+        rule->tos = tos;
+        rule->fwmark = fwmark;
+        rule->table = table;
+
+        r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = set_put(*rules, rule);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = rule;
+
+        rule = NULL;
+
+        return 0;
+}
+
+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,
+                            RoutingPolicyRule **ret) {
+
+        return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, 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,
+                                    RoutingPolicyRule **ret) {
+        return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, ret);
+}
+
+static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        _cleanup_link_unref_ Link *link = userdata;
+        int r;
+
+        assert(m);
+        assert(link);
+        assert(link->ifname);
+
+        link->link_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Could not drop routing policy rule: %m");
+
+        return 1;
+}
+
+int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
+        assert(routing_policy_rule);
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+        assert(IN_SET(routing_policy_rule->family, AF_INET, AF_INET6));
+
+        r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, routing_policy_rule->family);
+        if (r < 0)
+                return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m");
+
+        if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from)) {
+                if (routing_policy_rule->family == AF_INET)
+                        r = sd_netlink_message_append_in_addr(m, FRA_SRC, &routing_policy_rule->from.in);
+                else
+                        r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &routing_policy_rule->from.in6);
+
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, routing_policy_rule->from_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set source prefix length: %m");
+        }
+
+        if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to)) {
+                if (routing_policy_rule->family == AF_INET)
+                        r = sd_netlink_message_append_in_addr(m, FRA_DST, &routing_policy_rule->to.in);
+                else
+                        r = sd_netlink_message_append_in6_addr(m, FRA_DST, &routing_policy_rule->to.in6);
+
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_DST attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, routing_policy_rule->to_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set destination prefix length: %m");
+        }
+
+        r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        return 0;
+}
+
+static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
+        _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL;
+        _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(!!filename == (section_line > 0));
+
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        rule = hashmap_get(network->rules_by_section, n);
+        if (rule) {
+                *ret = rule;
+                rule = NULL;
+
+                return 0;
+        }
+
+        r = routing_policy_rule_new(&rule);
+        if (r < 0)
+                return r;
+
+        rule->section = n;
+        rule->network = network;
+        n = NULL;
+
+        r = hashmap_put(network->rules_by_section, rule->section, rule);
+        if (r < 0)
+                return r;
+
+        LIST_APPEND(rules, network->rules, rule);
+        network->n_rules++;
+
+        *ret = rule;
+        rule = NULL;
+
+        return 0;
+}
+
+int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        _cleanup_link_unref_ Link *link = userdata;
+        int r;
+
+        assert(rtnl);
+        assert(m);
+        assert(link);
+        assert(link->ifname);
+        assert(link->link_messages > 0);
+
+        link->link_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST)
+                log_link_warning_errno(link, r, "Could not add routing policy rule: %m");
+
+        if (link->link_messages == 0)
+                log_link_debug(link, "Routing policy rule configured");
+
+        return 1;
+}
+
+int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlink_message_handler_t callback, bool update) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
+        assert(rule);
+        assert(link);
+        assert(link->ifindex > 0);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+
+        r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
+        if (r < 0)
+                return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m");
+
+        if (!in_addr_is_null(rule->family, &rule->from)) {
+                if (rule->family == AF_INET)
+                        r = sd_netlink_message_append_in_addr(m, FRA_SRC, &rule->from.in);
+                else
+                        r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &rule->from.in6);
+
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set source prefix length: %m");
+        }
+
+        if (!in_addr_is_null(rule->family, &rule->to)) {
+                if (rule->family == AF_INET)
+                        r = sd_netlink_message_append_in_addr(m, FRA_DST, &rule->to.in);
+                else
+                        r = sd_netlink_message_append_in6_addr(m, FRA_DST, &rule->to.in6);
+
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_DST attribute: %m");
+
+                r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set destination prefix length: %m");
+        }
+
+        r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
+        if (r < 0)
+                return log_error_errno(r, "Could not append FRA_PRIORITY attribute: %m");
+
+        if (rule->tos > 0) {
+                r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set ip rule tos: %m");
+        }
+
+        if (rule->table < 256) {
+                r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set ip rule table: %m");
+        } else {
+                r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set ip rule table: %m");
+
+                r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_TABLE attribute: %m");
+        }
+
+        if (rule->fwmark > 0) {
+                r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_FWMARK attribute: %m");
+        }
+
+        if (rule->fwmask > 0) {
+                r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append FRA_FWMASK attribute: %m");
+        }
+
+        rule->link = link;
+
+        r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not send rtnetlink message: %m");
+
+        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, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not add rule : %m");
+
+        return 0;
+}
+
+static int parse_fwmark_fwmask(const char *s, uint32_t *fwmark, uint32_t *fwmask) {
+        _cleanup_free_ char *f = NULL;
+        char *p;
+        int r;
+
+        assert(s);
+
+        f = strdup(s);
+        if (!f)
+                return -ENOMEM;
+
+        p = strchr(f, '/');
+        if (p)
+                *p++ = '\0';
+
+        r = safe_atou32(f, fwmark);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse RPDB rule firewall mark, ignoring: %s", f);
+
+        if (p) {
+                r = safe_atou32(p, fwmask);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse RPDB rule mask, ignoring: %s", f);
+        }
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_tos(
+                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_free_ 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 = safe_atou8(rvalue, &n->tos);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_priority(
+                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_free_ 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 = safe_atou32(rvalue, &n->priority);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_table(
+                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_free_ 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 = safe_atou32(rvalue, &n->table);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_fwmark_mask(
+                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_free_ RoutingPolicyRule *n = NULL;
+        _cleanup_free_ char *fwmark = 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 = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_prefix(
+                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_free_ RoutingPolicyRule *n = NULL;
+        Network *network = userdata;
+        union in_addr_union buffer;
+        uint8_t prefixlen;
+        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 = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
+        if (r < 0) {
+                r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+
+                n->family = AF_INET6;
+        } else
+                n->family = AF_INET;
+
+        if (streq(lvalue, "To")) {
+                n->to = buffer;
+                n->to_prefixlen = prefixlen;
+        } else {
+                n->from = buffer;
+                n->from_prefixlen = prefixlen;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
+static int routing_policy_rule_read_full_file(char *state_file, char **ret) {
+        _cleanup_free_ char *s = NULL, *p = NULL;
+        size_t size;
+        int r;
+
+        assert(state_file);
+
+        r = read_full_file(state_file, &s, &size);
+        if (r == -ENOENT)
+                return -ENODATA;
+        if (r < 0)
+                return r;
+        if (size <= 0)
+                return -ENODATA;
+
+        *ret = s;
+        s = NULL;
+
+        return size;
+}
+
+int routing_policy_rule_load(Manager *m) {
+        _cleanup_strv_free_ char **l = NULL;
+        _cleanup_free_ char *data = NULL;
+        const char *p;
+        char **i;
+        int r;
+
+        assert(m);
+
+        r = routing_policy_rule_read_full_file(m->state_file, &data);
+        if (r <= 0)
+                return r;
+
+        l = strv_split_newlines(data);
+        if (!l)
+                return -ENOMEM;
+
+        r = set_ensure_allocated(&m->rules_saved, &routing_policy_rule_hash_ops);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, l) {
+                _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL;
+
+                p = startswith(*i, "RULE=");
+                if (!p)
+                        continue;
+
+                p = strchr(*i, '=');
+                p++;
+
+                r = routing_policy_rule_new(&rule);
+                if (r < 0)
+                        return r;
+
+                for (;;) {
+                        _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL;
+                        union in_addr_union buffer;
+                        uint8_t prefixlen;
+
+                        r = extract_first_word(&p, &word, NULL, 0);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                break;
+
+                        r = split_pair(word, "=", &a, &b);
+                        if (r < 0)
+                                continue;
+
+                        if (STR_IN_SET(a, "from", "to")) {
+
+                                r = in_addr_prefix_from_string(b, AF_INET, &buffer, &prefixlen);
+                                if (r < 0) {
+                                        r = in_addr_prefix_from_string(b, AF_INET6, &buffer, &prefixlen);
+                                        if (r < 0) {
+                                                log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b);
+                                                continue;
+                                        }
+
+                                        rule->family = AF_INET6;
+                                } else
+                                        rule->family = AF_INET;
+
+                                if (streq(a, "to")) {
+                                        rule->to = buffer;
+                                        rule->to_prefixlen = prefixlen;
+                                } else {
+                                        rule->from = buffer;
+                                        rule->from_prefixlen = prefixlen;
+                                }
+                        } else if (streq(a, "tos")) {
+                                r = safe_atou8(b, &rule->tos);
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b);
+                                        continue;
+                                }
+                        } else if (streq(a, "table")) {
+                                r = safe_atou32(b, &rule->table);
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to parse RPDB rule table, ignoring: %s", b);
+                                        continue;
+                                }
+                        } else if (streq(a, "fwmark")) {
+
+                                r = parse_fwmark_fwmask(a, &rule->fwmark, &rule->fwmask);
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a);
+                                        continue;
+                                }
+                        }
+                }
+
+                r = set_put(m->rules_saved, rule);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p);
+                        continue;
+                }
+
+                rule = NULL;
+        }
+
+        return 0;
+}
+
+void routing_policy_rule_purge(Manager *m, Link *link) {
+        RoutingPolicyRule *rule, *existing;
+        Iterator i;
+        int r;
+
+        assert(m);
+        assert(link);
+
+        SET_FOREACH(rule, m->rules_saved, i) {
+                existing = set_get(m->rules_foreign, rule);
+                if (existing) {
+
+                        r = routing_policy_rule_remove(rule, link, routing_policy_rule_remove_handler);
+                        if (r < 0) {
+                                log_warning_errno(r, "Could not remove routing policy rules: %m");
+                                continue;
+                        }
+
+                        link->link_messages++;
+                }
+        }
+}
diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h
new file mode 100644 (file)
index 0000000..d9fd93b
--- /dev/null
@@ -0,0 +1,83 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2017 Susant Sahani
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "in-addr-util.h"
+
+typedef struct RoutingPolicyRule RoutingPolicyRule;
+
+#include "networkd-link.h"
+#include "networkd-network.h"
+
+typedef struct Network Network;
+typedef struct Link Link;
+typedef struct NetworkConfigSection NetworkConfigSection;
+
+struct RoutingPolicyRule {
+        Network *network;
+        Link *link;
+        NetworkConfigSection *section;
+
+        uint8_t tos;
+
+        uint32_t table;
+        uint32_t fwmark;
+        uint32_t fwmask;
+        uint32_t priority;
+
+        int family;
+        unsigned char to_prefixlen;
+        unsigned char from_prefixlen;
+
+        union in_addr_union to;
+        union in_addr_union from;
+
+        LIST_FIELDS(RoutingPolicyRule, rules);
+};
+
+int routing_policy_rule_new(RoutingPolicyRule **ret);
+void routing_policy_rule_free(RoutingPolicyRule *rule);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RoutingPolicyRule*, routing_policy_rule_free);
+#define _cleanup_routing_policy_rule_free_ _cleanup_(routing_policy_rule_freep)
+
+int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, sd_netlink_message_handler_t callback, bool update);
+int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback);
+int link_routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+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, 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, 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, RoutingPolicyRule **ret);
+int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule);
+int routing_policy_rule_load(Manager *m);
+void routing_policy_rule_purge(Manager *m, Link *link);
+
+int config_parse_routing_policy_rule_tos(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);
+int config_parse_routing_policy_rule_table(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);
+int config_parse_routing_policy_rule_fwmark_mask(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);
+int config_parse_routing_policy_rule_prefix(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);
+int config_parse_routing_policy_rule_priority(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);
index 8efd160aaaa972302bf05fd59413ef2d3027392c..d5ba6893e339bd76cc68dd7ffe7f0a1e580e1877 100644 (file)
@@ -132,6 +132,12 @@ int main(int argc, char *argv[]) {
                 goto out;
         }
 
+        r = manager_rtnl_enumerate_rules(m);
+        if (r < 0) {
+                log_error_errno(r, "Could not enumerate rules: %m");
+                goto out;
+        }
+
         r = manager_start(m);
         if (r < 0) {
                 log_error_errno(r, "Could not start manager: %m");
index 3f5a6673cf54fe41087c7f32cc05c32c3db0b698..2289269eef9b0d182b342fffc598bad168ad1bc7 100644 (file)
@@ -159,6 +159,18 @@ int sd_rtnl_message_new_addrlabel(sd_netlink *rtnl, sd_netlink_message **ret, ui
 int sd_rtnl_message_addrlabel_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
 int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen);
 
+int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family);
+int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos);
+int sd_rtnl_message_routing_policy_rule_get_tos(sd_netlink_message *m, unsigned char *tos);
+int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table);
+int sd_rtnl_message_routing_policy_rule_get_table(sd_netlink_message *m, unsigned char *table);
+int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len);
+int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(sd_netlink_message *m, unsigned char *len);
+int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len);
+int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char *len);
+int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type);
+int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsigned char *type);
+
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);