Message ID | 1519537482-6861-2-git-send-email-roopa@cumulusnetworks.com |
---|---|
State | Changes Requested, archived |
Delegated to: | David Miller |
Headers | show |
Series | [net-next,1/5] net: fib_rules: support for match on ip_proto, sport and dport | expand |
On 25/02/18 07:44, Roopa Prabhu wrote: > From: Roopa Prabhu <roopa@cumulusnetworks.com> > > uapi for ip_proto, sport and dport range match > in fib rules. > > Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> > --- > include/net/fib_rules.h | 31 +++++++++++++- > include/uapi/linux/fib_rules.h | 8 ++++ > net/core/fib_rules.c | 94 +++++++++++++++++++++++++++++++++++++++++- > 3 files changed, 130 insertions(+), 3 deletions(-) > You should probably update validate_rulemsg() as well, these aren't added in the per-proto policies and nothing validates if the attribute data is actually there. Maybe I'm missing something obvious, but it looks like many other FRA_ attributes don't have such checks. > diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h > index b3d2162..6d99202 100644 > --- a/include/net/fib_rules.h > +++ b/include/net/fib_rules.h > @@ -11,6 +11,11 @@ > #include <net/rtnetlink.h> > #include <net/fib_notifier.h> > > +struct fib_port_range { > + __u16 start; > + __u16 end; > +}; > + > struct fib_kuid_range { > kuid_t start; > kuid_t end; > @@ -27,7 +32,7 @@ struct fib_rule { > u8 action; > u8 l3mdev; > u8 proto; > - /* 1 byte hole, try to use */ > + u8 ip_proto; > u32 target; > __be64 tun_id; > struct fib_rule __rcu *ctarget; > @@ -40,6 +45,8 @@ struct fib_rule { > char iifname[IFNAMSIZ]; > char oifname[IFNAMSIZ]; > struct fib_kuid_range uid_range; > + struct fib_port_range sport_range; > + struct fib_port_range dport_range; > struct rcu_head rcu; > }; > > @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) > return frh->table; > } > > +static inline bool fib_rule_port_inrange(struct fib_port_range *a, > + __be16 port) > +{ > + if (!a->start) > + return true; Can start be == 0 ? IIUC this check is unnecessary because when you're adding the new rule, you do a check for start > 0 so it shouldn't be possible to be 0. > + return ntohs(port) >= a->start && > + ntohs(port) <= a->end; > +} > + > +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) > +{ > + return a->start > 0 && a->end < 0xffff && > + a->start <= a->end; nit: alignment (also can be on a single line) > +} > + > +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, > + struct fib_port_range *b) > +{ > + return a->start == b->start && > + a->end == b->end; nit: alignment (also can be on a single line) > +} > + > struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, > struct net *); > 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 77d90ae..232df14 100644 > --- a/include/uapi/linux/fib_rules.h > +++ b/include/uapi/linux/fib_rules.h > @@ -35,6 +35,11 @@ struct fib_rule_uid_range { > __u32 end; > }; > > +struct fib_rule_port_range { > + __u16 start; > + __u16 end; > +}; > + > enum { > FRA_UNSPEC, > FRA_DST, /* destination address */ > @@ -59,6 +64,9 @@ enum { > FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ > FRA_UID_RANGE, /* UID range */ > FRA_PROTOCOL, /* Originator of the rule */ > + FRA_IP_PROTO, /* ip proto */ > + FRA_SPORT_RANGE, /* sport */ > + FRA_DPORT_RANGE, /* dport */ > __FRA_MAX > }; > > diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c > index a6aea80..5008235 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) > if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || > !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) > return false; > + if (fib_rule_port_range_valid(&rule->sport_range)) > + return false; > + if (fib_rule_port_range_valid(&rule->dport_range)) > + return false; > return true; > } > EXPORT_SYMBOL_GPL(fib_rule_matchall); > @@ -221,6 +225,12 @@ 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); > } > > +static int nla_put_port_range(struct sk_buff *skb, int attrtype, > + struct fib_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, > struct flowi *fl, int flags, > struct fib_lookup_arg *arg) > @@ -425,6 +435,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)) > 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)) > continue; > return 1; > @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, > return 0; > } > > +static int nla_get_port_range(struct nlattr *pattr, > + struct fib_port_range *port_range) > +{ > + const struct fib_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; > +} > + > int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, > struct netlink_ext_ack *extack) > { > @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, > 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) && > rule_exists(ops, frh, tb, rule)) { > err = -EEXIST; > @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > struct fib_rule *rule, *r; > struct nlattr *tb[FRA_MAX+1]; > struct fib_kuid_range range; > + struct fib_port_range *sprange = NULL; > + struct fib_port_range *dprange = NULL; > int err = -EINVAL; > > if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) > @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > range = fib_kuid_range_unset; > } > > + if (tb[FRA_SPORT_RANGE]) { > + sprange = nla_data(tb[FRA_SPORT_RANGE]); > + if (!fib_rule_port_range_valid(sprange)) { > + err = -EINVAL; > + goto errout; > + } > + } > + > + if (tb[FRA_DPORT_RANGE]) { > + dprange = nla_data(tb[FRA_DPORT_RANGE]); > + if (!fib_rule_port_range_valid(dprange)) { > + err = -EINVAL; > + goto errout; > + } > + } > + > list_for_each_entry(rule, &ops->rules_list, list) { > if (tb[FRA_PROTOCOL] && > (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) > @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > !uid_eq(rule->uid_range.end, range.end))) > continue; > > + if (tb[FRA_IP_PROTO] && > + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) nit: no need for the extra braces > + continue; > + > + if (sprange && > + !fib_rule_port_range_compare(&rule->sport_range, sprange)) > + continue; > + > + if (dprange && > + !fib_rule_port_range_compare(&rule->dport_range, dprange)) > + continue; > + > if (!ops->compare(rule, frh, tb)) > continue; > > @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, > + nla_total_size(4) /* FRA_FWMASK */ > + nla_total_size_64bit(8) /* FRA_TUN_ID */ > + 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_port_range)) /* FRA_SPORT_RANGE */ > + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ > > if (ops->nlmsg_payload) > payload += ops->nlmsg_payload(rule); > @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, > (rule->l3mdev && > nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || > (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_valid(&rule->sport_range) && > + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || > + (fib_rule_port_range_valid(&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; > > if (rule->suppress_ifgroup != -1) { >
On 25/02/18 17:04, Nikolay Aleksandrov wrote: > On 25/02/18 07:44, Roopa Prabhu wrote: >> From: Roopa Prabhu <roopa@cumulusnetworks.com> >> >> uapi for ip_proto, sport and dport range match >> in fib rules. >> >> Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> >> --- [snip] >> struct rcu_head rcu; >> }; >> >> @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) >> return frh->table; >> } >> >> +static inline bool fib_rule_port_inrange(struct fib_port_range *a, >> + __be16 port) >> +{ >> + if (!a->start) >> + return true; > > Can start be == 0 ? > IIUC this check is unnecessary because when you're adding the new rule, > you do a check for start > 0 so it shouldn't be possible to be 0. Nevermind this comment, I spoke too soon and saw the match later. :-) > >> + return ntohs(port) >= a->start && >> + ntohs(port) <= a->end; >> +} >> + >> +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) >> +{ >> + return a->start > 0 && a->end < 0xffff && >> + a->start <= a->end; > > nit: alignment (also can be on a single line) > >> +} >> + >> +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, >> + struct fib_port_range *b) >> +{ >> + return a->start == b->start && >> + a->end == b->end; > > nit: alignment (also can be on a single line) > >> +} >> + >> struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, >> struct net *); >> 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 77d90ae..232df14 100644 >> --- a/include/uapi/linux/fib_rules.h >> +++ b/include/uapi/linux/fib_rules.h >> @@ -35,6 +35,11 @@ struct fib_rule_uid_range { >> __u32 end; >> }; >> >> +struct fib_rule_port_range { >> + __u16 start; >> + __u16 end; >> +}; >> + >> enum { >> FRA_UNSPEC, >> FRA_DST, /* destination address */ >> @@ -59,6 +64,9 @@ enum { >> FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ >> FRA_UID_RANGE, /* UID range */ >> FRA_PROTOCOL, /* Originator of the rule */ >> + FRA_IP_PROTO, /* ip proto */ >> + FRA_SPORT_RANGE, /* sport */ >> + FRA_DPORT_RANGE, /* dport */ >> __FRA_MAX >> }; >> >> diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c >> index a6aea80..5008235 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) >> if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || >> !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) >> return false; >> + if (fib_rule_port_range_valid(&rule->sport_range)) >> + return false; >> + if (fib_rule_port_range_valid(&rule->dport_range)) >> + return false; >> return true; >> } >> EXPORT_SYMBOL_GPL(fib_rule_matchall); >> @@ -221,6 +225,12 @@ 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); >> } >> >> +static int nla_put_port_range(struct sk_buff *skb, int attrtype, >> + struct fib_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, >> struct flowi *fl, int flags, >> struct fib_lookup_arg *arg) >> @@ -425,6 +435,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)) >> 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)) >> continue; >> return 1; >> @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, >> return 0; >> } >> >> +static int nla_get_port_range(struct nlattr *pattr, >> + struct fib_port_range *port_range) >> +{ >> + const struct fib_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; >> +} >> + >> int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct netlink_ext_ack *extack) >> { >> @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> 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) && >> rule_exists(ops, frh, tb, rule)) { >> err = -EEXIST; >> @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct fib_rule *rule, *r; >> struct nlattr *tb[FRA_MAX+1]; >> struct fib_kuid_range range; >> + struct fib_port_range *sprange = NULL; >> + struct fib_port_range *dprange = NULL; >> int err = -EINVAL; >> >> if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) >> @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> range = fib_kuid_range_unset; >> } >> >> + if (tb[FRA_SPORT_RANGE]) { >> + sprange = nla_data(tb[FRA_SPORT_RANGE]); >> + if (!fib_rule_port_range_valid(sprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> + if (tb[FRA_DPORT_RANGE]) { >> + dprange = nla_data(tb[FRA_DPORT_RANGE]); >> + if (!fib_rule_port_range_valid(dprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> list_for_each_entry(rule, &ops->rules_list, list) { >> if (tb[FRA_PROTOCOL] && >> (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) >> @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> !uid_eq(rule->uid_range.end, range.end))) >> continue; >> >> + if (tb[FRA_IP_PROTO] && >> + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) > > nit: no need for the extra braces > >> + continue; >> + >> + if (sprange && >> + !fib_rule_port_range_compare(&rule->sport_range, sprange)) >> + continue; >> + >> + if (dprange && >> + !fib_rule_port_range_compare(&rule->dport_range, dprange)) >> + continue; >> + >> if (!ops->compare(rule, frh, tb)) >> continue; >> >> @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, >> + nla_total_size(4) /* FRA_FWMASK */ >> + nla_total_size_64bit(8) /* FRA_TUN_ID */ >> + 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_port_range)) /* FRA_SPORT_RANGE */ >> + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ >> >> if (ops->nlmsg_payload) >> payload += ops->nlmsg_payload(rule); >> @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, >> (rule->l3mdev && >> nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || >> (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_valid(&rule->sport_range) && >> + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || >> + (fib_rule_port_range_valid(&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; >> >> if (rule->suppress_ifgroup != -1) { >> >
On Sun, Feb 25, 2018 at 7:04 AM, Nikolay Aleksandrov <nikolay@cumulusnetworks.com> wrote: > On 25/02/18 07:44, Roopa Prabhu wrote: >> From: Roopa Prabhu <roopa@cumulusnetworks.com> >> >> uapi for ip_proto, sport and dport range match >> in fib rules. >> >> Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> >> --- >> include/net/fib_rules.h | 31 +++++++++++++- >> include/uapi/linux/fib_rules.h | 8 ++++ >> net/core/fib_rules.c | 94 +++++++++++++++++++++++++++++++++++++++++- >> 3 files changed, 130 insertions(+), 3 deletions(-) >> > > You should probably update validate_rulemsg() as well, these aren't added in the per-proto > policies and nothing validates if the attribute data is actually there. Maybe I'm missing > something obvious, but it looks like many other FRA_ attributes don't have such checks. yeah, I added the sport checks there initially and later removed it since I did not see any of the other FRA_* attributes there. and then ended up adding it in the respective rule add and del functions where other attributes were validated for consistency. I can submit a follow on patch to move all FRA_* attribute validations to validate_rulemsg(). I can sure start with the new attribute validation in this series in validate_rulemsg in v2 > >> diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h >> index b3d2162..6d99202 100644 >> --- a/include/net/fib_rules.h >> +++ b/include/net/fib_rules.h >> @@ -11,6 +11,11 @@ >> #include <net/rtnetlink.h> >> #include <net/fib_notifier.h> >> >> +struct fib_port_range { >> + __u16 start; >> + __u16 end; >> +}; >> + >> struct fib_kuid_range { >> kuid_t start; >> kuid_t end; >> @@ -27,7 +32,7 @@ struct fib_rule { >> u8 action; >> u8 l3mdev; >> u8 proto; >> - /* 1 byte hole, try to use */ >> + u8 ip_proto; >> u32 target; >> __be64 tun_id; >> struct fib_rule __rcu *ctarget; >> @@ -40,6 +45,8 @@ struct fib_rule { >> char iifname[IFNAMSIZ]; >> char oifname[IFNAMSIZ]; >> struct fib_kuid_range uid_range; >> + struct fib_port_range sport_range; >> + struct fib_port_range dport_range; >> struct rcu_head rcu; >> }; >> >> @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) >> return frh->table; >> } >> >> +static inline bool fib_rule_port_inrange(struct fib_port_range *a, >> + __be16 port) >> +{ >> + if (!a->start) >> + return true; > > Can start be == 0 ? > IIUC this check is unnecessary because when you're adding the new rule, > you do a check for start > 0 so it shouldn't be possible to be 0. > >> + return ntohs(port) >= a->start && >> + ntohs(port) <= a->end; >> +} >> + >> +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) >> +{ >> + return a->start > 0 && a->end < 0xffff && >> + a->start <= a->end; > > nit: alignment (also can be on a single line) > >> +} >> + >> +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, >> + struct fib_port_range *b) >> +{ >> + return a->start == b->start && >> + a->end == b->end; > > nit: alignment (also can be on a single line) > >> +} >> + >> struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, >> struct net *); >> 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 77d90ae..232df14 100644 >> --- a/include/uapi/linux/fib_rules.h >> +++ b/include/uapi/linux/fib_rules.h >> @@ -35,6 +35,11 @@ struct fib_rule_uid_range { >> __u32 end; >> }; >> >> +struct fib_rule_port_range { >> + __u16 start; >> + __u16 end; >> +}; >> + >> enum { >> FRA_UNSPEC, >> FRA_DST, /* destination address */ >> @@ -59,6 +64,9 @@ enum { >> FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ >> FRA_UID_RANGE, /* UID range */ >> FRA_PROTOCOL, /* Originator of the rule */ >> + FRA_IP_PROTO, /* ip proto */ >> + FRA_SPORT_RANGE, /* sport */ >> + FRA_DPORT_RANGE, /* dport */ >> __FRA_MAX >> }; >> >> diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c >> index a6aea80..5008235 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) >> if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || >> !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) >> return false; >> + if (fib_rule_port_range_valid(&rule->sport_range)) >> + return false; >> + if (fib_rule_port_range_valid(&rule->dport_range)) >> + return false; >> return true; >> } >> EXPORT_SYMBOL_GPL(fib_rule_matchall); >> @@ -221,6 +225,12 @@ 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); >> } >> >> +static int nla_put_port_range(struct sk_buff *skb, int attrtype, >> + struct fib_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, >> struct flowi *fl, int flags, >> struct fib_lookup_arg *arg) >> @@ -425,6 +435,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)) >> 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)) >> continue; >> return 1; >> @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, >> return 0; >> } >> >> +static int nla_get_port_range(struct nlattr *pattr, >> + struct fib_port_range *port_range) >> +{ >> + const struct fib_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; >> +} >> + >> int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct netlink_ext_ack *extack) >> { >> @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> 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) && >> rule_exists(ops, frh, tb, rule)) { >> err = -EEXIST; >> @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct fib_rule *rule, *r; >> struct nlattr *tb[FRA_MAX+1]; >> struct fib_kuid_range range; >> + struct fib_port_range *sprange = NULL; >> + struct fib_port_range *dprange = NULL; >> int err = -EINVAL; >> >> if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) >> @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> range = fib_kuid_range_unset; >> } >> >> + if (tb[FRA_SPORT_RANGE]) { >> + sprange = nla_data(tb[FRA_SPORT_RANGE]); >> + if (!fib_rule_port_range_valid(sprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> + if (tb[FRA_DPORT_RANGE]) { >> + dprange = nla_data(tb[FRA_DPORT_RANGE]); >> + if (!fib_rule_port_range_valid(dprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> list_for_each_entry(rule, &ops->rules_list, list) { >> if (tb[FRA_PROTOCOL] && >> (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) >> @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> !uid_eq(rule->uid_range.end, range.end))) >> continue; >> >> + if (tb[FRA_IP_PROTO] && >> + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) > > nit: no need for the extra braces > >> + continue; >> + >> + if (sprange && >> + !fib_rule_port_range_compare(&rule->sport_range, sprange)) >> + continue; >> + >> + if (dprange && >> + !fib_rule_port_range_compare(&rule->dport_range, dprange)) >> + continue; >> + >> if (!ops->compare(rule, frh, tb)) >> continue; >> >> @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, >> + nla_total_size(4) /* FRA_FWMASK */ >> + nla_total_size_64bit(8) /* FRA_TUN_ID */ >> + 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_port_range)) /* FRA_SPORT_RANGE */ >> + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ >> >> if (ops->nlmsg_payload) >> payload += ops->nlmsg_payload(rule); >> @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, >> (rule->l3mdev && >> nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || >> (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_valid(&rule->sport_range) && >> + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || >> + (fib_rule_port_range_valid(&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; >> >> if (rule->suppress_ifgroup != -1) { >> >
On 2/24/18 10:44 PM, Roopa Prabhu wrote: > From: Roopa Prabhu <roopa@cumulusnetworks.com> > > uapi for ip_proto, sport and dport range match > in fib rules. > > Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> > --- > include/net/fib_rules.h | 31 +++++++++++++- > include/uapi/linux/fib_rules.h | 8 ++++ > net/core/fib_rules.c | 94 +++++++++++++++++++++++++++++++++++++++++- > 3 files changed, 130 insertions(+), 3 deletions(-) > > diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h > index b3d2162..6d99202 100644 > --- a/include/net/fib_rules.h > +++ b/include/net/fib_rules.h > @@ -11,6 +11,11 @@ > #include <net/rtnetlink.h> > #include <net/fib_notifier.h> > > +struct fib_port_range { > + __u16 start; > + __u16 end; u16 for kernel headers; __u16 is for uapi. > +}; > + > struct fib_kuid_range { > kuid_t start; > kuid_t end; > @@ -27,7 +32,7 @@ struct fib_rule { > u8 action; > u8 l3mdev; > u8 proto; > - /* 1 byte hole, try to use */ > + u8 ip_proto; > u32 target; > __be64 tun_id; > struct fib_rule __rcu *ctarget; > @@ -40,6 +45,8 @@ struct fib_rule { > char iifname[IFNAMSIZ]; > char oifname[IFNAMSIZ]; > struct fib_kuid_range uid_range; > + struct fib_port_range sport_range; > + struct fib_port_range dport_range; > struct rcu_head rcu; > }; > > @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) > return frh->table; > } > > +static inline bool fib_rule_port_inrange(struct fib_port_range *a, > + __be16 port) > +{ > + if (!a->start) > + return true; > + return ntohs(port) >= a->start && > + ntohs(port) <= a->end; > +} > + > +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) > +{ > + return a->start > 0 && a->end < 0xffff && > + a->start <= a->end; > +} > + > +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, > + struct fib_port_range *b) > +{ > + return a->start == b->start && > + a->end == b->end; > +} > + > struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, > struct net *); > 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 77d90ae..232df14 100644 > --- a/include/uapi/linux/fib_rules.h > +++ b/include/uapi/linux/fib_rules.h > @@ -35,6 +35,11 @@ struct fib_rule_uid_range { > __u32 end; > }; > > +struct fib_rule_port_range { > + __u16 start; > + __u16 end; > +}; > + > enum { > FRA_UNSPEC, > FRA_DST, /* destination address */ > @@ -59,6 +64,9 @@ enum { > FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ > FRA_UID_RANGE, /* UID range */ > FRA_PROTOCOL, /* Originator of the rule */ > + FRA_IP_PROTO, /* ip proto */ > + FRA_SPORT_RANGE, /* sport */ > + FRA_DPORT_RANGE, /* dport */ > __FRA_MAX > }; > > diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c > index a6aea80..5008235 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) > if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || > !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) > return false; > + if (fib_rule_port_range_valid(&rule->sport_range)) > + return false; > + if (fib_rule_port_range_valid(&rule->dport_range)) > + return false; Seems like that should be a check that start and end are both not 0. Given the uses of fib_rule_port_range_valid, perhaps another helper is needed to make this more readable -- e.g., fib_rule_port_range_set -- which would be used here and fill_rule. > return true; > } > EXPORT_SYMBOL_GPL(fib_rule_matchall); > @@ -221,6 +225,12 @@ 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); > } > > +static int nla_put_port_range(struct sk_buff *skb, int attrtype, > + struct fib_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, > struct flowi *fl, int flags, > struct fib_lookup_arg *arg) > @@ -425,6 +435,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)) > 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)) > continue; > return 1; > @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, > return 0; > } > > +static int nla_get_port_range(struct nlattr *pattr, > + struct fib_port_range *port_range) > +{ > + const struct fib_port_range *pr = nla_data(pattr); Isn't the data struct from userspace fib_rule_port_range? that begs the question of whether a separate struct definition is needed for kernel side. Why not just use the one for both? > + > + if (!fib_rule_port_range_valid(pr)) > + return -EINVAL; > + > + port_range->start = pr->start; > + port_range->end = pr->end; > + > + return 0; > +} > + > int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, > struct netlink_ext_ack *extack) > { > @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, > 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) && > rule_exists(ops, frh, tb, rule)) { > err = -EEXIST; > @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > struct fib_rule *rule, *r; > struct nlattr *tb[FRA_MAX+1]; > struct fib_kuid_range range; > + struct fib_port_range *sprange = NULL; > + struct fib_port_range *dprange = NULL; > int err = -EINVAL; > > if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) > @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > range = fib_kuid_range_unset; > } > > + if (tb[FRA_SPORT_RANGE]) { > + sprange = nla_data(tb[FRA_SPORT_RANGE]); > + if (!fib_rule_port_range_valid(sprange)) { > + err = -EINVAL; > + goto errout; > + } > + } > + > + if (tb[FRA_DPORT_RANGE]) { > + dprange = nla_data(tb[FRA_DPORT_RANGE]); > + if (!fib_rule_port_range_valid(dprange)) { > + err = -EINVAL; > + goto errout; > + } > + } > + > list_for_each_entry(rule, &ops->rules_list, list) { > if (tb[FRA_PROTOCOL] && > (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) > @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, > !uid_eq(rule->uid_range.end, range.end))) > continue; > > + if (tb[FRA_IP_PROTO] && > + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) > + continue; > + > + if (sprange && > + !fib_rule_port_range_compare(&rule->sport_range, sprange)) > + continue; > + > + if (dprange && > + !fib_rule_port_range_compare(&rule->dport_range, dprange)) > + continue; > + > if (!ops->compare(rule, frh, tb)) > continue; > > @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, > + nla_total_size(4) /* FRA_FWMASK */ > + nla_total_size_64bit(8) /* FRA_TUN_ID */ > + 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_port_range)) /* FRA_SPORT_RANGE */ > + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ > > if (ops->nlmsg_payload) > payload += ops->nlmsg_payload(rule); > @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, > (rule->l3mdev && > nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || > (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_valid(&rule->sport_range) && > + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || > + (fib_rule_port_range_valid(&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; > > if (rule->suppress_ifgroup != -1) { >
On Sun, Feb 25, 2018 at 7:08 PM, David Ahern <dsa@cumulusnetworks.com> wrote: > On 2/24/18 10:44 PM, Roopa Prabhu wrote: >> From: Roopa Prabhu <roopa@cumulusnetworks.com> >> >> uapi for ip_proto, sport and dport range match >> in fib rules. >> >> Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> >> --- >> include/net/fib_rules.h | 31 +++++++++++++- >> include/uapi/linux/fib_rules.h | 8 ++++ >> net/core/fib_rules.c | 94 +++++++++++++++++++++++++++++++++++++++++- >> 3 files changed, 130 insertions(+), 3 deletions(-) >> >> diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h >> index b3d2162..6d99202 100644 >> --- a/include/net/fib_rules.h >> +++ b/include/net/fib_rules.h >> @@ -11,6 +11,11 @@ >> #include <net/rtnetlink.h> >> #include <net/fib_notifier.h> >> >> +struct fib_port_range { >> + __u16 start; >> + __u16 end; > > u16 for kernel headers; __u16 is for uapi. > ack, >> +}; >> + >> struct fib_kuid_range { >> kuid_t start; >> kuid_t end; >> @@ -27,7 +32,7 @@ struct fib_rule { >> u8 action; >> u8 l3mdev; >> u8 proto; >> - /* 1 byte hole, try to use */ >> + u8 ip_proto; >> u32 target; >> __be64 tun_id; >> struct fib_rule __rcu *ctarget; >> @@ -40,6 +45,8 @@ struct fib_rule { >> char iifname[IFNAMSIZ]; >> char oifname[IFNAMSIZ]; >> struct fib_kuid_range uid_range; >> + struct fib_port_range sport_range; >> + struct fib_port_range dport_range; >> struct rcu_head rcu; >> }; >> >> @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) >> return frh->table; >> } >> >> +static inline bool fib_rule_port_inrange(struct fib_port_range *a, >> + __be16 port) >> +{ >> + if (!a->start) >> + return true; >> + return ntohs(port) >= a->start && >> + ntohs(port) <= a->end; >> +} >> + >> +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) >> +{ >> + return a->start > 0 && a->end < 0xffff && >> + a->start <= a->end; >> +} >> + >> +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, >> + struct fib_port_range *b) >> +{ >> + return a->start == b->start && >> + a->end == b->end; >> +} >> + >> struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, >> struct net *); >> 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 77d90ae..232df14 100644 >> --- a/include/uapi/linux/fib_rules.h >> +++ b/include/uapi/linux/fib_rules.h >> @@ -35,6 +35,11 @@ struct fib_rule_uid_range { >> __u32 end; >> }; >> >> +struct fib_rule_port_range { >> + __u16 start; >> + __u16 end; >> +}; >> + >> enum { >> FRA_UNSPEC, >> FRA_DST, /* destination address */ >> @@ -59,6 +64,9 @@ enum { >> FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ >> FRA_UID_RANGE, /* UID range */ >> FRA_PROTOCOL, /* Originator of the rule */ >> + FRA_IP_PROTO, /* ip proto */ >> + FRA_SPORT_RANGE, /* sport */ >> + FRA_DPORT_RANGE, /* dport */ >> __FRA_MAX >> }; >> >> diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c >> index a6aea80..5008235 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) >> if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || >> !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) >> return false; >> + if (fib_rule_port_range_valid(&rule->sport_range)) >> + return false; >> + if (fib_rule_port_range_valid(&rule->dport_range)) >> + return false; > > Seems like that should be a check that start and end are both not 0. > Given the uses of fib_rule_port_range_valid, perhaps another helper is > needed to make this more readable -- e.g., fib_rule_port_range_set -- > which would be used here and fill_rule. yeah, was trying to not add two helpers. But, i sure can. > > >> return true; >> } >> EXPORT_SYMBOL_GPL(fib_rule_matchall); >> @@ -221,6 +225,12 @@ 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); >> } >> >> +static int nla_put_port_range(struct sk_buff *skb, int attrtype, >> + struct fib_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, >> struct flowi *fl, int flags, >> struct fib_lookup_arg *arg) >> @@ -425,6 +435,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)) >> 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)) >> continue; >> return 1; >> @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, >> return 0; >> } >> >> +static int nla_get_port_range(struct nlattr *pattr, >> + struct fib_port_range *port_range) >> +{ >> + const struct fib_port_range *pr = nla_data(pattr); > > Isn't the data struct from userspace fib_rule_port_range? that begs the > question of whether a separate struct definition is needed for kernel > side. Why not just use the one for both? yep, will fix. I was following the uid_range code. But, looking back at it, i think uid range has other reasons to have a separate struct. will move to one. > > >> + >> + if (!fib_rule_port_range_valid(pr)) >> + return -EINVAL; >> + >> + port_range->start = pr->start; >> + port_range->end = pr->end; >> + >> + return 0; >> +} >> + >> int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct netlink_ext_ack *extack) >> { >> @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> 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) && >> rule_exists(ops, frh, tb, rule)) { >> err = -EEXIST; >> @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> struct fib_rule *rule, *r; >> struct nlattr *tb[FRA_MAX+1]; >> struct fib_kuid_range range; >> + struct fib_port_range *sprange = NULL; >> + struct fib_port_range *dprange = NULL; >> int err = -EINVAL; >> >> if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) >> @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> range = fib_kuid_range_unset; >> } >> >> + if (tb[FRA_SPORT_RANGE]) { >> + sprange = nla_data(tb[FRA_SPORT_RANGE]); >> + if (!fib_rule_port_range_valid(sprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> + if (tb[FRA_DPORT_RANGE]) { >> + dprange = nla_data(tb[FRA_DPORT_RANGE]); >> + if (!fib_rule_port_range_valid(dprange)) { >> + err = -EINVAL; >> + goto errout; >> + } >> + } >> + >> list_for_each_entry(rule, &ops->rules_list, list) { >> if (tb[FRA_PROTOCOL] && >> (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) >> @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, >> !uid_eq(rule->uid_range.end, range.end))) >> continue; >> >> + if (tb[FRA_IP_PROTO] && >> + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) >> + continue; >> + >> + if (sprange && >> + !fib_rule_port_range_compare(&rule->sport_range, sprange)) >> + continue; >> + >> + if (dprange && >> + !fib_rule_port_range_compare(&rule->dport_range, dprange)) >> + continue; >> + >> if (!ops->compare(rule, frh, tb)) >> continue; >> >> @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, >> + nla_total_size(4) /* FRA_FWMASK */ >> + nla_total_size_64bit(8) /* FRA_TUN_ID */ >> + 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_port_range)) /* FRA_SPORT_RANGE */ >> + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ >> >> if (ops->nlmsg_payload) >> payload += ops->nlmsg_payload(rule); >> @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, >> (rule->l3mdev && >> nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || >> (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_valid(&rule->sport_range) && >> + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || >> + (fib_rule_port_range_valid(&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; >> >> if (rule->suppress_ifgroup != -1) { >> >
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index b3d2162..6d99202 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -11,6 +11,11 @@ #include <net/rtnetlink.h> #include <net/fib_notifier.h> +struct fib_port_range { + __u16 start; + __u16 end; +}; + struct fib_kuid_range { kuid_t start; kuid_t end; @@ -27,7 +32,7 @@ struct fib_rule { u8 action; u8 l3mdev; u8 proto; - /* 1 byte hole, try to use */ + u8 ip_proto; u32 target; __be64 tun_id; struct fib_rule __rcu *ctarget; @@ -40,6 +45,8 @@ struct fib_rule { char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; struct fib_kuid_range uid_range; + struct fib_port_range sport_range; + struct fib_port_range dport_range; struct rcu_head rcu; }; @@ -144,6 +151,28 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) return frh->table; } +static inline bool fib_rule_port_inrange(struct fib_port_range *a, + __be16 port) +{ + if (!a->start) + return true; + return ntohs(port) >= a->start && + ntohs(port) <= a->end; +} + +static inline bool fib_rule_port_range_valid(const struct fib_port_range *a) +{ + return a->start > 0 && a->end < 0xffff && + a->start <= a->end; +} + +static inline bool fib_rule_port_range_compare(struct fib_port_range *a, + struct fib_port_range *b) +{ + return a->start == b->start && + a->end == b->end; +} + struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct net *); 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 77d90ae..232df14 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -35,6 +35,11 @@ struct fib_rule_uid_range { __u32 end; }; +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + enum { FRA_UNSPEC, FRA_DST, /* destination address */ @@ -59,6 +64,9 @@ enum { FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ FRA_UID_RANGE, /* UID range */ FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ __FRA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index a6aea80..5008235 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) if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) return false; + if (fib_rule_port_range_valid(&rule->sport_range)) + return false; + if (fib_rule_port_range_valid(&rule->dport_range)) + return false; return true; } EXPORT_SYMBOL_GPL(fib_rule_matchall); @@ -221,6 +225,12 @@ 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); } +static int nla_put_port_range(struct sk_buff *skb, int attrtype, + struct fib_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, struct flowi *fl, int flags, struct fib_lookup_arg *arg) @@ -425,6 +435,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)) 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)) continue; return 1; @@ -432,6 +453,20 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, return 0; } +static int nla_get_port_range(struct nlattr *pattr, + struct fib_port_range *port_range) +{ + const struct fib_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; +} + int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, 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) && rule_exists(ops, frh, tb, rule)) { err = -EEXIST; @@ -638,6 +690,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, struct fib_rule *rule, *r; struct nlattr *tb[FRA_MAX+1]; struct fib_kuid_range range; + struct fib_port_range *sprange = NULL; + struct fib_port_range *dprange = NULL; int err = -EINVAL; if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) @@ -667,6 +721,22 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, range = fib_kuid_range_unset; } + if (tb[FRA_SPORT_RANGE]) { + sprange = nla_data(tb[FRA_SPORT_RANGE]); + if (!fib_rule_port_range_valid(sprange)) { + err = -EINVAL; + goto errout; + } + } + + if (tb[FRA_DPORT_RANGE]) { + dprange = nla_data(tb[FRA_DPORT_RANGE]); + if (!fib_rule_port_range_valid(dprange)) { + err = -EINVAL; + goto errout; + } + } + list_for_each_entry(rule, &ops->rules_list, list) { if (tb[FRA_PROTOCOL] && (rule->proto != nla_get_u8(tb[FRA_PROTOCOL]))) @@ -712,6 +782,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, !uid_eq(rule->uid_range.end, range.end))) continue; + if (tb[FRA_IP_PROTO] && + (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO]))) + continue; + + if (sprange && + !fib_rule_port_range_compare(&rule->sport_range, sprange)) + continue; + + if (dprange && + !fib_rule_port_range_compare(&rule->dport_range, dprange)) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -790,7 +872,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_FWMASK */ + nla_total_size_64bit(8) /* FRA_TUN_ID */ + 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_port_range)) /* FRA_SPORT_RANGE */ + + nla_total_size(sizeof(struct fib_port_range)); /* FRA_DPORT_RANGE */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -855,7 +940,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, (rule->l3mdev && nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || (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_valid(&rule->sport_range) && + nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) || + (fib_rule_port_range_valid(&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; if (rule->suppress_ifgroup != -1) {