aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd.network.xml49
-rw-r--r--meson.build3
-rw-r--r--src/basic/missing.h36
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c8
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c32
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h4
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c171
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c10
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/networkd-link.c24
-rw-r--r--src/network/networkd-manager.c192
-rw-r--r--src/network/networkd-manager.h6
-rw-r--r--src/network/networkd-network-gperf.gperf6
-rw-r--r--src/network/networkd-network.c23
-rw-r--r--src/network/networkd-network.h4
-rw-r--r--src/network/networkd-routing-policy-rule.c900
-rw-r--r--src/network/networkd-routing-policy-rule.h83
-rw-r--r--src/network/networkd.c6
-rw-r--r--src/systemd/sd-netlink.h12
19 files changed, 1555 insertions, 16 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 96d381137..ea3bf71ee 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -878,6 +878,55 @@
</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
diff --git a/meson.build b/meson.build
index 5fb90ddea..af1703558 100644
--- a/meson.build
+++ b/meson.build
@@ -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] : ''
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 026906642..653c5b776 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -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
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index e8c8abac2..ac0427f94 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -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);
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 923f7dd10..979dc6824 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -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>
@@ -29,8 +30,11 @@
#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 = {
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index 215af1240..d2fb65120 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -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);
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index 12c51ffe2..586d94f62 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -697,6 +697,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;
}
return -EOPNOTSUPP;
@@ -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;
+}
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index d67244676..77f4d5b63 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -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;
}
diff --git a/src/network/meson.build b/src/network/meson.build
index 35ecd8637..83a837d6c 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -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())
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 2fb1dd67a..6b591271a 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -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)) {
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 45f9b3d35..82b966301 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -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;
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index e2447c223..254aab845 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -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);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 112efd2df..f01e78a35 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -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
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 3b52a8a5b..1694b1e5e 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -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;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 035b57d5f..7af3171ef 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -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
index 000000000..6850135fc
--- /dev/null
+++ b/src/network/networkd-routing-policy-rule.c
@@ -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
index 000000000..d9fd93b9b
--- /dev/null
+++ b/src/network/networkd-routing-policy-rule.h
@@ -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);
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 8efd160aa..d5ba6893e 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -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");
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 3f5a6673c..2289269ee 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -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);