diff options
author | Roopa Prabhu <roopa@cumulusnetworks.com> | 2018-02-28 22:40:16 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-02-28 22:44:43 -0500 |
commit | bfff4862653bb96001ab57c1edd6d03f48e5f035 (patch) | |
tree | 463387f66425eff78e5aff19a5c3965f85c6c082 | |
parent | 292749915743758013e290c994f41de196d47498 (diff) |
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: Roopa Prabhu <roopa@cumulusnetworks.com>
Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/fib_rules.h | 36 | ||||
-rw-r--r-- | include/uapi/linux/fib_rules.h | 8 | ||||
-rw-r--r-- | net/core/fib_rules.c | 92 |
3 files changed, 133 insertions, 3 deletions
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index b3d216249240..6dd0a00653ae 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h | |||
@@ -27,7 +27,7 @@ struct fib_rule { | |||
27 | u8 action; | 27 | u8 action; |
28 | u8 l3mdev; | 28 | u8 l3mdev; |
29 | u8 proto; | 29 | u8 proto; |
30 | /* 1 byte hole, try to use */ | 30 | u8 ip_proto; |
31 | u32 target; | 31 | u32 target; |
32 | __be64 tun_id; | 32 | __be64 tun_id; |
33 | struct fib_rule __rcu *ctarget; | 33 | struct fib_rule __rcu *ctarget; |
@@ -40,6 +40,8 @@ struct fib_rule { | |||
40 | char iifname[IFNAMSIZ]; | 40 | char iifname[IFNAMSIZ]; |
41 | char oifname[IFNAMSIZ]; | 41 | char oifname[IFNAMSIZ]; |
42 | struct fib_kuid_range uid_range; | 42 | struct fib_kuid_range uid_range; |
43 | struct fib_rule_port_range sport_range; | ||
44 | struct fib_rule_port_range dport_range; | ||
43 | struct rcu_head rcu; | 45 | struct rcu_head rcu; |
44 | }; | 46 | }; |
45 | 47 | ||
@@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) | |||
144 | return frh->table; | 146 | return frh->table; |
145 | } | 147 | } |
146 | 148 | ||
149 | static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range) | ||
150 | { | ||
151 | return range->start != 0 && range->end != 0; | ||
152 | } | ||
153 | |||
154 | static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a, | ||
155 | __be16 port) | ||
156 | { | ||
157 | return ntohs(port) >= a->start && | ||
158 | ntohs(port) <= a->end; | ||
159 | } | ||
160 | |||
161 | static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a) | ||
162 | { | ||
163 | return a->start != 0 && a->end != 0 && a->end < 0xffff && | ||
164 | a->start <= a->end; | ||
165 | } | ||
166 | |||
167 | static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a, | ||
168 | struct fib_rule_port_range *b) | ||
169 | { | ||
170 | return a->start == b->start && | ||
171 | a->end == b->end; | ||
172 | } | ||
173 | |||
174 | static inline bool fib_rule_requires_fldissect(struct fib_rule *rule) | ||
175 | { | ||
176 | return rule->ip_proto || | ||
177 | fib_rule_port_range_set(&rule->sport_range) || | ||
178 | fib_rule_port_range_set(&rule->dport_range); | ||
179 | } | ||
180 | |||
147 | struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, | 181 | struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, |
148 | struct net *); | 182 | struct net *); |
149 | void fib_rules_unregister(struct fib_rules_ops *); | 183 | void fib_rules_unregister(struct fib_rules_ops *); |
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 77d90ae38114..232df14e1287 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h | |||
@@ -35,6 +35,11 @@ struct fib_rule_uid_range { | |||
35 | __u32 end; | 35 | __u32 end; |
36 | }; | 36 | }; |
37 | 37 | ||
38 | struct fib_rule_port_range { | ||
39 | __u16 start; | ||
40 | __u16 end; | ||
41 | }; | ||
42 | |||
38 | enum { | 43 | enum { |
39 | FRA_UNSPEC, | 44 | FRA_UNSPEC, |
40 | FRA_DST, /* destination address */ | 45 | FRA_DST, /* destination address */ |
@@ -59,6 +64,9 @@ enum { | |||
59 | FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ | 64 | FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ |
60 | FRA_UID_RANGE, /* UID range */ | 65 | FRA_UID_RANGE, /* UID range */ |
61 | FRA_PROTOCOL, /* Originator of the rule */ | 66 | FRA_PROTOCOL, /* Originator of the rule */ |
67 | FRA_IP_PROTO, /* ip proto */ | ||
68 | FRA_SPORT_RANGE, /* sport */ | ||
69 | FRA_DPORT_RANGE, /* dport */ | ||
62 | __FRA_MAX | 70 | __FRA_MAX |
63 | }; | 71 | }; |
64 | 72 | ||
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index a6aea805a0a2..f6f04fc0f629 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c | |||
@@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule) | |||
33 | if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || | 33 | if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || |
34 | !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) | 34 | !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) |
35 | return false; | 35 | return false; |
36 | if (fib_rule_port_range_set(&rule->sport_range)) | ||
37 | return false; | ||
38 | if (fib_rule_port_range_set(&rule->dport_range)) | ||
39 | return false; | ||
36 | return true; | 40 | return true; |
37 | } | 41 | } |
38 | EXPORT_SYMBOL_GPL(fib_rule_matchall); | 42 | 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 | return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); | 225 | return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); |
222 | } | 226 | } |
223 | 227 | ||
228 | static int nla_get_port_range(struct nlattr *pattr, | ||
229 | struct fib_rule_port_range *port_range) | ||
230 | { | ||
231 | const struct fib_rule_port_range *pr = nla_data(pattr); | ||
232 | |||
233 | if (!fib_rule_port_range_valid(pr)) | ||
234 | return -EINVAL; | ||
235 | |||
236 | port_range->start = pr->start; | ||
237 | port_range->end = pr->end; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static int nla_put_port_range(struct sk_buff *skb, int attrtype, | ||
243 | struct fib_rule_port_range *range) | ||
244 | { | ||
245 | return nla_put(skb, attrtype, sizeof(*range), range); | ||
246 | } | ||
247 | |||
224 | static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, | 248 | static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, |
225 | struct flowi *fl, int flags, | 249 | struct flowi *fl, int flags, |
226 | struct fib_lookup_arg *arg) | 250 | struct fib_lookup_arg *arg) |
@@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, | |||
425 | !uid_eq(r->uid_range.end, rule->uid_range.end)) | 449 | !uid_eq(r->uid_range.end, rule->uid_range.end)) |
426 | continue; | 450 | continue; |
427 | 451 | ||
452 | if (r->ip_proto != rule->ip_proto) | ||
453 | continue; | ||
454 | |||
455 | if (!fib_rule_port_range_compare(&r->sport_range, | ||
456 | &rule->sport_range)) | ||
457 | continue; | ||
458 | |||
459 | if (!fib_rule_port_range_compare(&r->dport_range, | ||
460 | &rule->dport_range)) | ||
461 | continue; | ||
462 | |||
428 | if (!ops->compare(r, frh, tb)) | 463 | if (!ops->compare(r, frh, tb)) |
429 | continue; | 464 | continue; |
430 | return 1; | 465 | return 1; |
@@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
569 | rule->uid_range = fib_kuid_range_unset; | 604 | rule->uid_range = fib_kuid_range_unset; |
570 | } | 605 | } |
571 | 606 | ||
607 | if (tb[FRA_IP_PROTO]) | ||
608 | rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); | ||
609 | |||
610 | if (tb[FRA_SPORT_RANGE]) { | ||
611 | err = nla_get_port_range(tb[FRA_SPORT_RANGE], | ||
612 | &rule->sport_range); | ||
613 | if (err) | ||
614 | goto errout_free; | ||
615 | } | ||
616 | |||
617 | if (tb[FRA_DPORT_RANGE]) { | ||
618 | err = nla_get_port_range(tb[FRA_DPORT_RANGE], | ||
619 | &rule->dport_range); | ||
620 | if (err) | ||
621 | goto errout_free; | ||
622 | } | ||
623 | |||
572 | if ((nlh->nlmsg_flags & NLM_F_EXCL) && | 624 | if ((nlh->nlmsg_flags & NLM_F_EXCL) && |
573 | rule_exists(ops, frh, tb, rule)) { | 625 | rule_exists(ops, frh, tb, rule)) { |
574 | err = -EEXIST; | 626 | err = -EEXIST; |
@@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
634 | { | 686 | { |
635 | struct net *net = sock_net(skb->sk); | 687 | struct net *net = sock_net(skb->sk); |
636 | struct fib_rule_hdr *frh = nlmsg_data(nlh); | 688 | struct fib_rule_hdr *frh = nlmsg_data(nlh); |
689 | struct fib_rule_port_range sprange = {0, 0}; | ||
690 | struct fib_rule_port_range dprange = {0, 0}; | ||
637 | struct fib_rules_ops *ops = NULL; | 691 | struct fib_rules_ops *ops = NULL; |
638 | struct fib_rule *rule, *r; | 692 | struct fib_rule *rule, *r; |
639 | struct nlattr *tb[FRA_MAX+1]; | 693 | struct nlattr *tb[FRA_MAX+1]; |
@@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
667 | range = fib_kuid_range_unset; | 721 | range = fib_kuid_range_unset; |
668 | } | 722 | } |
669 | 723 | ||
724 | if (tb[FRA_SPORT_RANGE]) { | ||
725 | err = nla_get_port_range(tb[FRA_SPORT_RANGE], | ||
726 | &sprange); | ||
727 | if (err) | ||
728 | goto errout; | ||
729 | } | ||
730 | |||
731 | if (tb[FRA_DPORT_RANGE]) { | ||
732 | err = nla_get_port_range(tb[FRA_DPORT_RANGE], | ||
733 | &dprange); | ||
734 | if (err) | ||
735 | goto errout; | ||
736 | } | ||
737 | |||
670 | list_for_each_entry(rule, &ops->rules_list, list) { | 738 | list_for_each_entry(rule, &ops->rules_list, list) { |
671 | if (tb[FRA_PROTOCOL] && | 739 | if (tb[FRA_PROTOCOL] && |
672 | (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) | 740 | (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) |
@@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
712 | !uid_eq(rule->uid_range.end, range.end))) | 780 | !uid_eq(rule->uid_range.end, range.end))) |
713 | continue; | 781 | continue; |
714 | 782 | ||
783 | if (tb[FRA_IP_PROTO] && | ||
784 | (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) | ||
785 | continue; | ||
786 | |||
787 | if (fib_rule_port_range_set(&sprange) && | ||
788 | !fib_rule_port_range_compare(&rule->sport_range, &sprange)) | ||
789 | continue; | ||
790 | |||
791 | if (fib_rule_port_range_set(&dprange) && | ||
792 | !fib_rule_port_range_compare(&rule->dport_range, &dprange)) | ||
793 | continue; | ||
794 | |||
715 | if (!ops->compare(rule, frh, tb)) | 795 | if (!ops->compare(rule, frh, tb)) |
716 | continue; | 796 | continue; |
717 | 797 | ||
@@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, | |||
790 | + nla_total_size(4) /* FRA_FWMASK */ | 870 | + nla_total_size(4) /* FRA_FWMASK */ |
791 | + nla_total_size_64bit(8) /* FRA_TUN_ID */ | 871 | + nla_total_size_64bit(8) /* FRA_TUN_ID */ |
792 | + nla_total_size(sizeof(struct fib_kuid_range)) | 872 | + nla_total_size(sizeof(struct fib_kuid_range)) |
793 | + nla_total_size(1); /* FRA_PROTOCOL */ | 873 | + nla_total_size(1) /* FRA_PROTOCOL */ |
874 | + nla_total_size(1) /* FRA_IP_PROTO */ | ||
875 | + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */ | ||
876 | + nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */ | ||
794 | 877 | ||
795 | if (ops->nlmsg_payload) | 878 | if (ops->nlmsg_payload) |
796 | payload += ops->nlmsg_payload(rule); | 879 | payload += ops->nlmsg_payload(rule); |
@@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, | |||
855 | (rule->l3mdev && | 938 | (rule->l3mdev && |
856 | nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || | 939 | nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || |
857 | (uid_range_set(&rule->uid_range) && | 940 | (uid_range_set(&rule->uid_range) && |
858 | nla_put_uid_range(skb, &rule->uid_range))) | 941 | nla_put_uid_range(skb, &rule->uid_range)) || |
942 | (fib_rule_port_range_set(&rule->sport_range) && | ||
943 | nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || | ||
944 | (fib_rule_port_range_set(&rule->dport_range) && | ||
945 | nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) || | ||
946 | (rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto))) | ||
859 | goto nla_put_failure; | 947 | goto nla_put_failure; |
860 | 948 | ||
861 | if (rule->suppress_ifgroup != -1) { | 949 | if (rule->suppress_ifgroup != -1) { |