summaryrefslogtreecommitdiff
path: root/zebra/rule_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/rule_netlink.c')
-rw-r--r--zebra/rule_netlink.c135
1 files changed, 116 insertions, 19 deletions
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index c9699c7d95..a5a605f27e 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -174,11 +174,40 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule)
}
/*
+ * Update specified rule for a specific interface.
+ */
+enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule)
+{
+ int ret = 0;
+
+ /* Add the new, updated one */
+ ret = netlink_rule_update(RTM_NEWRULE, new_rule);
+
+ /**
+ * Delete the old one.
+ *
+ * Don't care about this result right?
+ */
+ netlink_rule_update(RTM_DELRULE, old_rule);
+
+ kernel_pbr_rule_add_del_status(new_rule,
+ (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS
+ : ZEBRA_DPLANE_INSTALL_FAILURE);
+
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
+}
+
+/*
* Handle netlink notification informing a rule add or delete.
* Handling of an ADD is TBD.
* DELs are notified up, if other attributes indicate it may be a
* notification of interest. The expectation is that if this corresponds
* to a PBR rule added by FRR, it will be readded.
+ *
+ * If startup and we see a rule we created, delete it as its leftover
+ * from a previous instance and should have been removed on shutdown.
+ *
*/
int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
@@ -190,15 +219,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
struct zebra_pbr_rule rule = {};
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
+ uint8_t proto = 0;
/* Basic validation followed by extracting attributes. */
if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE)
return 0;
- /* TBD */
- if (h->nlmsg_type == RTM_NEWRULE)
- return 0;
-
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
if (len < 0) {
zlog_err(
@@ -222,19 +248,6 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memset(tb, 0, sizeof(tb));
netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len);
- /* TBD: We don't care about rules not specifying an IIF. */
- if (tb[FRA_IFNAME] == NULL)
- return 0;
-
- ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
- zns = zebra_ns_lookup(ns_id);
-
- /* If we don't know the interface, we don't care. */
- if (!if_lookup_by_name_per_ns(zns, ifname))
- return 0;
-
- strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
-
if (tb[FRA_PRIORITY])
rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
@@ -246,6 +259,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(&rule.rule.filter.src_ip.u.prefix6,
RTA_DATA(tb[FRA_SRC]), 16);
rule.rule.filter.src_ip.prefixlen = frh->src_len;
+ rule.rule.filter.src_ip.family = frh->family;
rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
}
@@ -257,6 +271,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(&rule.rule.filter.dst_ip.u.prefix6,
RTA_DATA(tb[FRA_DST]), 16);
rule.rule.filter.dst_ip.prefixlen = frh->dst_len;
+ rule.rule.filter.dst_ip.family = frh->family;
rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
}
@@ -265,6 +280,49 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
else
rule.rule.action.table = frh->table;
+ /* TBD: We don't care about rules not specifying an IIF. */
+ if (tb[FRA_IFNAME] == NULL)
+ return 0;
+
+ if (tb[FRA_PROTOCOL])
+ proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]);
+
+ ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
+ strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
+
+ if (h->nlmsg_type == RTM_NEWRULE) {
+ /*
+ * If we see a rule at startup we created, delete it now.
+ * It should have been flushed on a previous shutdown.
+ */
+ if (startup && proto == RTPROT_ZEBRA) {
+ int ret;
+
+ ret = netlink_rule_update(RTM_DELRULE, &rule);
+
+ zlog_debug(
+ "%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
+ __func__,
+ ((ret == 0) ? "Removed" : "Failed to remove"),
+ nl_family_to_str(frh->family), rule.ifname,
+ rule.rule.ifindex, rule.rule.priority,
+ prefix2str(&rule.rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule.rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule.rule.action.table);
+ }
+
+ /* TBD */
+ return 0;
+ }
+
+ zns = zebra_ns_lookup(ns_id);
+
+ /* If we don't know the interface, we don't care. */
+ if (!if_lookup_by_name_per_ns(zns, ifname))
+ return 0;
+
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
@@ -281,12 +339,51 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
}
/*
+ * Request rules from the kernel
+ */
+static int netlink_request_rules(struct zebra_ns *zns, int family, int type)
+{
+ struct {
+ struct nlmsghdr n;
+ struct fib_rule_hdr frh;
+ char buf[NL_PKT_BUF_SIZE];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
+ req.frh.family = family;
+
+ return netlink_request(&zns->netlink_cmd, &req);
+}
+
+/*
* Get to know existing PBR rules in the kernel - typically called at startup.
- * TBD.
*/
int netlink_rules_read(struct zebra_ns *zns)
{
- return 0;
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true);
+
+ ret = netlink_request_rules(zns, AF_INET, RTM_GETRULE);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_request_rules(zns, AF_INET6, RTM_GETRULE);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+ return ret;
}
#endif /* HAVE_NETLINK */