networkd: add support to configure IP Rule (#5725)

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
This commit is contained in:
Susant Sahani 2017-09-14 19:51:39 +00:00 committed by Lennart Poettering
parent f1e24a259c
commit bce67bbee3
19 changed files with 1555 additions and 16 deletions

View File

@ -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

View 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] : ''

View 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

View 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);

View 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>
@ -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 = {

View 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);

View 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;
}

View 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;
}

View 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())

View 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)) {

View 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;

View 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);

View 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

View 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;

View 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;

View File

@ -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++;
}
}
}

View File

@ -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);

View 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");

View 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);