Commit bfff4862 authored by Roopa Prabhu's avatar Roopa Prabhu Committed by David S. Miller

net: fib_rules: support for match on ip_proto, sport and dport

uapi for ip_proto, sport and dport range match
in fib rules.
Signed-off-by: default avatarRoopa Prabhu <roopa@cumulusnetworks.com>
Acked-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 29274991
...@@ -27,7 +27,7 @@ struct fib_rule { ...@@ -27,7 +27,7 @@ struct fib_rule {
u8 action; u8 action;
u8 l3mdev; u8 l3mdev;
u8 proto; u8 proto;
/* 1 byte hole, try to use */ u8 ip_proto;
u32 target; u32 target;
__be64 tun_id; __be64 tun_id;
struct fib_rule __rcu *ctarget; struct fib_rule __rcu *ctarget;
...@@ -40,6 +40,8 @@ struct fib_rule { ...@@ -40,6 +40,8 @@ struct fib_rule {
char iifname[IFNAMSIZ]; char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ]; char oifname[IFNAMSIZ];
struct fib_kuid_range uid_range; struct fib_kuid_range uid_range;
struct fib_rule_port_range sport_range;
struct fib_rule_port_range dport_range;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) ...@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla)
return frh->table; return frh->table;
} }
static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range)
{
return range->start != 0 && range->end != 0;
}
static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a,
__be16 port)
{
return ntohs(port) >= a->start &&
ntohs(port) <= a->end;
}
static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a)
{
return a->start != 0 && a->end != 0 && a->end < 0xffff &&
a->start <= a->end;
}
static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a,
struct fib_rule_port_range *b)
{
return a->start == b->start &&
a->end == b->end;
}
static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
{
return rule->ip_proto ||
fib_rule_port_range_set(&rule->sport_range) ||
fib_rule_port_range_set(&rule->dport_range);
}
struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
struct net *); struct net *);
void fib_rules_unregister(struct fib_rules_ops *); void fib_rules_unregister(struct fib_rules_ops *);
......
...@@ -35,6 +35,11 @@ struct fib_rule_uid_range { ...@@ -35,6 +35,11 @@ struct fib_rule_uid_range {
__u32 end; __u32 end;
}; };
struct fib_rule_port_range {
__u16 start;
__u16 end;
};
enum { enum {
FRA_UNSPEC, FRA_UNSPEC,
FRA_DST, /* destination address */ FRA_DST, /* destination address */
...@@ -59,6 +64,9 @@ enum { ...@@ -59,6 +64,9 @@ enum {
FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
FRA_UID_RANGE, /* UID range */ FRA_UID_RANGE, /* UID range */
FRA_PROTOCOL, /* Originator of the rule */ FRA_PROTOCOL, /* Originator of the rule */
FRA_IP_PROTO, /* ip proto */
FRA_SPORT_RANGE, /* sport */
FRA_DPORT_RANGE, /* dport */
__FRA_MAX __FRA_MAX
}; };
......
...@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule) ...@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule)
if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) ||
!uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
return false; return false;
if (fib_rule_port_range_set(&rule->sport_range))
return false;
if (fib_rule_port_range_set(&rule->dport_range))
return false;
return true; return true;
} }
EXPORT_SYMBOL_GPL(fib_rule_matchall); EXPORT_SYMBOL_GPL(fib_rule_matchall);
...@@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range) ...@@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
} }
static int nla_get_port_range(struct nlattr *pattr,
struct fib_rule_port_range *port_range)
{
const struct fib_rule_port_range *pr = nla_data(pattr);
if (!fib_rule_port_range_valid(pr))
return -EINVAL;
port_range->start = pr->start;
port_range->end = pr->end;
return 0;
}
static int nla_put_port_range(struct sk_buff *skb, int attrtype,
struct fib_rule_port_range *range)
{
return nla_put(skb, attrtype, sizeof(*range), range);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
struct flowi *fl, int flags, struct flowi *fl, int flags,
struct fib_lookup_arg *arg) struct fib_lookup_arg *arg)
...@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, ...@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh,
!uid_eq(r->uid_range.end, rule->uid_range.end)) !uid_eq(r->uid_range.end, rule->uid_range.end))
continue; continue;
if (r->ip_proto != rule->ip_proto)
continue;
if (!fib_rule_port_range_compare(&r->sport_range,
&rule->sport_range))
continue;
if (!fib_rule_port_range_compare(&r->dport_range,
&rule->dport_range))
continue;
if (!ops->compare(r, frh, tb)) if (!ops->compare(r, frh, tb))
continue; continue;
return 1; return 1;
...@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
rule->uid_range = fib_kuid_range_unset; rule->uid_range = fib_kuid_range_unset;
} }
if (tb[FRA_IP_PROTO])
rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
if (tb[FRA_SPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
&rule->sport_range);
if (err)
goto errout_free;
}
if (tb[FRA_DPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
&rule->dport_range);
if (err)
goto errout_free;
}
if ((nlh->nlmsg_flags & NLM_F_EXCL) && if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
rule_exists(ops, frh, tb, rule)) { rule_exists(ops, frh, tb, rule)) {
err = -EEXIST; err = -EEXIST;
...@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct fib_rule_hdr *frh = nlmsg_data(nlh); struct fib_rule_hdr *frh = nlmsg_data(nlh);
struct fib_rule_port_range sprange = {0, 0};
struct fib_rule_port_range dprange = {0, 0};
struct fib_rules_ops *ops = NULL; struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *r; struct fib_rule *rule, *r;
struct nlattr *tb[FRA_MAX+1]; struct nlattr *tb[FRA_MAX+1];
...@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
range = fib_kuid_range_unset; range = fib_kuid_range_unset;
} }
if (tb[FRA_SPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_SPORT_RANGE],
&sprange);
if (err)
goto errout;
}
if (tb[FRA_DPORT_RANGE]) {
err = nla_get_port_range(tb[FRA_DPORT_RANGE],
&dprange);
if (err)
goto errout;
}
list_for_each_entry(rule, &ops->rules_list, list) { list_for_each_entry(rule, &ops->rules_list, list) {
if (tb[FRA_PROTOCOL] && if (tb[FRA_PROTOCOL] &&
(rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) (rule->proto != nla_get_u8(tb[FRA_PROTOCOL])))
...@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
!uid_eq(rule->uid_range.end, range.end))) !uid_eq(rule->uid_range.end, range.end)))
continue; continue;
if (tb[FRA_IP_PROTO] &&
(rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO])))
continue;
if (fib_rule_port_range_set(&sprange) &&
!fib_rule_port_range_compare(&rule->sport_range, &sprange))
continue;
if (fib_rule_port_range_set(&dprange) &&
!fib_rule_port_range_compare(&rule->dport_range, &dprange))
continue;
if (!ops->compare(rule, frh, tb)) if (!ops->compare(rule, frh, tb))
continue; continue;
...@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, ...@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
+ nla_total_size(4) /* FRA_FWMASK */ + nla_total_size(4) /* FRA_FWMASK */
+ nla_total_size_64bit(8) /* FRA_TUN_ID */ + nla_total_size_64bit(8) /* FRA_TUN_ID */
+ nla_total_size(sizeof(struct fib_kuid_range)) + nla_total_size(sizeof(struct fib_kuid_range))
+ nla_total_size(1); /* FRA_PROTOCOL */ + nla_total_size(1) /* FRA_PROTOCOL */
+ nla_total_size(1) /* FRA_IP_PROTO */
+ nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */
+ nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */
if (ops->nlmsg_payload) if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule); payload += ops->nlmsg_payload(rule);
...@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, ...@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
(rule->l3mdev && (rule->l3mdev &&
nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
(uid_range_set(&rule->uid_range) && (uid_range_set(&rule->uid_range) &&
nla_put_uid_range(skb, &rule->uid_range))) nla_put_uid_range(skb, &rule->uid_range)) ||
(fib_rule_port_range_set(&rule->sport_range) &&
nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) ||
(fib_rule_port_range_set(&rule->dport_range) &&
nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) ||
(rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto)))
goto nla_put_failure; goto nla_put_failure;
if (rule->suppress_ifgroup != -1) { if (rule->suppress_ifgroup != -1) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment