Message ID | 20230731175117.17376-2-andrea.mayer@uniroma2.it (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | seg6: add NEXT-C-SID support for SRv6 End.X behavior | expand |
On Mon, Jul 31, 2023 at 07:51:16PM +0200, Andrea Mayer wrote: > +/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through > + * the flavors framework. These behaviors must report the subset of (flavor) > + * operations they currently implement. In this way, if a user specifies a > + * flavor combination that is not supported by a given End* behavior, the > + * kernel refuses to instantiate the tunnel reporting the error. > + */ > +static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) > +{ > + switch (action) { > + case SEG6_LOCAL_ACTION_END: > + *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; > + break; > + case SEG6_LOCAL_ACTION_END_X: > + *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > } > ... > @@ -2070,7 +2131,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > { > struct seg6_flavors_info *finfo = &slwt->flv_info; > struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; > - unsigned long fops; > + int action = slwt->action; > + __u32 fops, supp_fops = 0; > int rc; > > rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, > @@ -2086,7 +2148,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > return -EINVAL; > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > - if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { > + rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > + if (rc < 0 || !supp_fops || (fops & ~supp_fops)) { if rc == 0, the supp_fops won't be 0. Thanks Hangbin
Hi Hangbin, thanks for your time. Please see below. On Thu, 3 Aug 2023 17:30:28 +0800 Hangbin Liu <liuhangbin@gmail.com> wrote: > On Mon, Jul 31, 2023 at 07:51:16PM +0200, Andrea Mayer wrote: > > +/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through > > + * the flavors framework. These behaviors must report the subset of (flavor) > > + * operations they currently implement. In this way, if a user specifies a > > + * flavor combination that is not supported by a given End* behavior, the > > + * kernel refuses to instantiate the tunnel reporting the error. > > + */ > > +static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) > > +{ > > + switch (action) { > > + case SEG6_LOCAL_ACTION_END: > > + *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; > > + break; > > + case SEG6_LOCAL_ACTION_END_X: > > + *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + return 0; > > } > > > > ... > > > @@ -2070,7 +2131,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > { > > struct seg6_flavors_info *finfo = &slwt->flv_info; > > struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; > > - unsigned long fops; > > + int action = slwt->action; > > + __u32 fops, supp_fops = 0; > > int rc; > > > > rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, > > @@ -2086,7 +2148,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > return -EINVAL; > > > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > > - if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { > > + rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > > + if (rc < 0 || !supp_fops || (fops & ~supp_fops)) { > > if rc == 0, the supp_fops won't be 0. > Yes, you're right. In this patch, supp_fops is always set properly when rc == 0. Since seg6_flv_supp_ops_by_action() should be extended in the event that other behaviors receive flavors support, I added this check in case the "supp_fops" field was set incorrectly or not set at all. Note that supp_fops == 0 must be considered an inadmissible value. So, I think we have two possibilities: i) remove this "defensive" check, assuming that supp_fops will always be set correctly by seg6_flv_supp_ops_by_action() (when rc == 0, like in this patch); ii) improve the check by explicitly indicating with a pr_warn_once, for example, the condition that is occurring is unexpected. for (ii), something like this: parse_nla_flavors(...) { [...] supp_fops = 0; [...] rc = seg6_flv_supp_ops_by_action(action, &supp_fops); if (!rc && !supp_fops) { /* supported flavors mask cannot be zero as it is considered to * be invalid. */ pr_warn_once("seg6local: invalid Flavor operation(s)"); return -EINVAL; } fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); if (rc < 0 || (fops & ~supp_fops)) { NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); return -EOPNOTSUPP; } finfo->flv_ops = fops; [...] } parse_nla_flavors() is called in the control path so another check would not hit performance. I am more inclined to consider solution (ii). What do you think? > Thanks > Hangbin Ciao, Andrea
On Fri, Aug 04, 2023 at 02:41:18PM +0200, Andrea Mayer wrote: > Hi Hangbin, > thanks for your time. Please see below. > > On Thu, 3 Aug 2023 17:30:28 +0800 > Hangbin Liu <liuhangbin@gmail.com> wrote: > > > On Mon, Jul 31, 2023 at 07:51:16PM +0200, Andrea Mayer wrote: > > > +/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through > > > + * the flavors framework. These behaviors must report the subset of (flavor) > > > + * operations they currently implement. In this way, if a user specifies a > > > + * flavor combination that is not supported by a given End* behavior, the > > > + * kernel refuses to instantiate the tunnel reporting the error. > > > + */ > > > +static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) > > > +{ > > > + switch (action) { > > > + case SEG6_LOCAL_ACTION_END: > > > + *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; > > > + break; > > > + case SEG6_LOCAL_ACTION_END_X: > > > + *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; > > > + break; > > > + default: > > > + return -EOPNOTSUPP; > > > + } > > > + > > > + return 0; > > > } > > > > > > > ... > > > > > @@ -2070,7 +2131,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > > { > > > struct seg6_flavors_info *finfo = &slwt->flv_info; > > > struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; > > > - unsigned long fops; > > > + int action = slwt->action; > > > + __u32 fops, supp_fops = 0; > > > int rc; > > > > > > rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, > > > @@ -2086,7 +2148,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > > return -EINVAL; > > > > > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > > > - if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { > > > + rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > > > + if (rc < 0 || !supp_fops || (fops & ~supp_fops)) { > > > > if rc == 0, the supp_fops won't be 0. > > > > Yes, you're right. > > In this patch, supp_fops is always set properly when rc == 0. > Since seg6_flv_supp_ops_by_action() should be extended in the event that other > behaviors receive flavors support, I added this check in case the "supp_fops" > field was set incorrectly or not set at all. > Note that supp_fops == 0 must be considered an inadmissible value. > > > So, I think we have two possibilities: > i) remove this "defensive" check, assuming that supp_fops will always be set > correctly by seg6_flv_supp_ops_by_action() (when rc == 0, like in this > patch); > ii) improve the check by explicitly indicating with a pr_warn_once, for > example, the condition that is occurring is unexpected. > > for (ii), something like this: > > parse_nla_flavors(...) > { > [...] > supp_fops = 0; > [...] > > rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > if (!rc && !supp_fops) { > /* supported flavors mask cannot be zero as it is considered to > * be invalid. > */ > pr_warn_once("seg6local: invalid Flavor operation(s)"); > return -EINVAL; > } Do you mean there is a possibility *in future* that the supp_fops could be 0 with rc == 0? If yes, this check would make sense(although we can add this check when it's true). If not. I don't see a need to have this check. And some static analysis tool would report warn for this code. Thanks Hangbin > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > if (rc < 0 || (fops & ~supp_fops)) { > NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); > return -EOPNOTSUPP; > } > > finfo->flv_ops = fops; > > [...] > } > > parse_nla_flavors() is called in the control path so another check would not > hit performance. I am more inclined to consider solution (ii). > > What do you think? > > > Thanks > > Hangbin > > Ciao, > Andrea
On Sat, 5 Aug 2023 16:17:03 +0800 Hangbin Liu <liuhangbin@gmail.com> wrote: > On Fri, Aug 04, 2023 at 02:41:18PM +0200, Andrea Mayer wrote: > > Hi Hangbin, > > thanks for your time. Please see below. > > > > On Thu, 3 Aug 2023 17:30:28 +0800 > > Hangbin Liu <liuhangbin@gmail.com> wrote: > > > > > On Mon, Jul 31, 2023 at 07:51:16PM +0200, Andrea Mayer wrote: > > > > +/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through > > > > + * the flavors framework. These behaviors must report the subset of (flavor) > > > > + * operations they currently implement. In this way, if a user specifies a > > > > + * flavor combination that is not supported by a given End* behavior, the > > > > + * kernel refuses to instantiate the tunnel reporting the error. > > > > + */ > > > > +static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) > > > > +{ > > > > + switch (action) { > > > > + case SEG6_LOCAL_ACTION_END: > > > > + *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; > > > > + break; > > > > + case SEG6_LOCAL_ACTION_END_X: > > > > + *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; > > > > + break; > > > > + default: > > > > + return -EOPNOTSUPP; > > > > + } > > > > + > > > > + return 0; > > > > } > > > > > > > > > > ... > > > > > > > @@ -2070,7 +2131,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > > > { > > > > struct seg6_flavors_info *finfo = &slwt->flv_info; > > > > struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; > > > > - unsigned long fops; > > > > + int action = slwt->action; > > > > + __u32 fops, supp_fops = 0; > > > > int rc; > > > > > > > > rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, > > > > @@ -2086,7 +2148,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, > > > > return -EINVAL; > > > > > > > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > > > > - if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { > > > > + rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > > > > + if (rc < 0 || !supp_fops || (fops & ~supp_fops)) { > > > > > > if rc == 0, the supp_fops won't be 0. > > > > > > > Yes, you're right. > > > > In this patch, supp_fops is always set properly when rc == 0. > > Since seg6_flv_supp_ops_by_action() should be extended in the event that other > > behaviors receive flavors support, I added this check in case the "supp_fops" > > field was set incorrectly or not set at all. > > Note that supp_fops == 0 must be considered an inadmissible value. > > > > > > So, I think we have two possibilities: > > i) remove this "defensive" check, assuming that supp_fops will always be set > > correctly by seg6_flv_supp_ops_by_action() (when rc == 0, like in this > > patch); > > ii) improve the check by explicitly indicating with a pr_warn_once, for > > example, the condition that is occurring is unexpected. > > > > for (ii), something like this: > > > > parse_nla_flavors(...) > > { > > [...] > > supp_fops = 0; > > [...] > > > > rc = seg6_flv_supp_ops_by_action(action, &supp_fops); > > if (!rc && !supp_fops) { > > /* supported flavors mask cannot be zero as it is considered to > > * be invalid. > > */ > > pr_warn_once("seg6local: invalid Flavor operation(s)"); > > return -EINVAL; > > } > > Do you mean there is a possibility *in future* that the supp_fops could be 0 > with rc == 0? If yes, this check would make sense(although we can add this > check when it's true). If not. I don't see a need to have this check. > > And some static analysis tool would report warn for this code. > Good points, thanks. There is no possibility at the moment that supp_fops could be 0 with rc == 0. That check is going to be removed in v2. > Thanks > Hangbin Ciao, Andrea > > > > fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); > > if (rc < 0 || (fops & ~supp_fops)) { > > NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); > > return -EOPNOTSUPP; > > } > > > > finfo->flv_ops = fops; > > > > [...] > > } > > > > parse_nla_flavors() is called in the control path so another check would not > > hit performance. I am more inclined to consider solution (ii). > > > > What do you think? > > > > > Thanks > > > Hangbin > > > > Ciao, > > Andrea
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index dd433cc265c8..ea7261872fae 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -109,15 +109,19 @@ struct bpf_lwt_prog { #define next_csid_chk_lcnode_fn_bits(flen) \ next_csid_chk_lcblock_bits(flen) +/* flag indicating that flavors are set up for a given End* behavior */ +#define SEG6_F_LOCAL_FLAVORS SEG6_F_ATTR(SEG6_LOCAL_FLAVORS) + #define SEG6_F_LOCAL_FLV_OP(flvname) BIT(SEG6_LOCAL_FLV_OP_##flvname) +#define SEG6_F_LOCAL_FLV_NEXT_CSID SEG6_F_LOCAL_FLV_OP(NEXT_CSID) #define SEG6_F_LOCAL_FLV_PSP SEG6_F_LOCAL_FLV_OP(PSP) /* Supported RFC8986 Flavor operations are reported in this bitmask */ #define SEG6_LOCAL_FLV8986_SUPP_OPS SEG6_F_LOCAL_FLV_PSP -/* Supported Flavor operations are reported in this bitmask */ -#define SEG6_LOCAL_FLV_SUPP_OPS (SEG6_F_LOCAL_FLV_OP(NEXT_CSID) | \ +#define SEG6_LOCAL_END_FLV_SUPP_OPS (SEG6_F_LOCAL_FLV_NEXT_CSID | \ SEG6_LOCAL_FLV8986_SUPP_OPS) +#define SEG6_LOCAL_END_X_FLV_SUPP_OPS SEG6_F_LOCAL_FLV_NEXT_CSID struct seg6_flavors_info { /* Flavor operations */ @@ -411,9 +415,72 @@ static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) return input_action_end_finish(skb, slwt); } +static int input_action_end_x_finish(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + seg6_lookup_nexthop(skb, &slwt->nh6, 0); + + return dst_input(skb); +} + +static int input_action_end_x_core(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + + return input_action_end_x_finish(skb, slwt); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +static int end_x_next_csid_core(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + const struct seg6_flavors_info *finfo = &slwt->flv_info; + struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; + + if (seg6_next_csid_is_arg_zero(daddr, finfo)) + return input_action_end_x_core(skb, slwt); + + /* update DA */ + seg6_next_csid_advance_arg(daddr, finfo); + + return input_action_end_x_finish(skb, slwt); +} + static bool seg6_next_csid_enabled(__u32 fops) { - return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID); + return fops & SEG6_F_LOCAL_FLV_NEXT_CSID; +} + +/* Processing of SRv6 End, End.X, and End.T behaviors can be extended through + * the flavors framework. These behaviors must report the subset of (flavor) + * operations they currently implement. In this way, if a user specifies a + * flavor combination that is not supported by a given End* behavior, the + * kernel refuses to instantiate the tunnel reporting the error. + */ +static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) +{ + switch (action) { + case SEG6_LOCAL_ACTION_END: + *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; + break; + case SEG6_LOCAL_ACTION_END_X: + *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; + break; + default: + return -EOPNOTSUPP; + } + + return 0; } /* We describe the packet state in relation to the absence/presence of the SRH @@ -746,21 +813,14 @@ static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) /* regular endpoint, and forward to specified nexthop */ static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) { - struct ipv6_sr_hdr *srh; - - srh = get_and_validate_srh(skb); - if (!srh) - goto drop; - - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); - - seg6_lookup_nexthop(skb, &slwt->nh6, 0); + const struct seg6_flavors_info *finfo = &slwt->flv_info; + __u32 fops = finfo->flv_ops; - return dst_input(skb); + /* check for the presence of NEXT-C-SID since it applies first */ + if (seg6_next_csid_enabled(fops)) + return end_x_next_csid_core(skb, slwt); -drop: - kfree_skb(skb); - return -EINVAL; + return input_action_end_x_core(skb, slwt); } static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) @@ -1404,13 +1464,14 @@ static struct seg6_action_desc seg6_action_table[] = { .action = SEG6_LOCAL_ACTION_END, .attrs = 0, .optattrs = SEG6_F_LOCAL_COUNTERS | - SEG6_F_ATTR(SEG6_LOCAL_FLAVORS), + SEG6_F_LOCAL_FLAVORS, .input = input_action_end, }, { .action = SEG6_LOCAL_ACTION_END_X, .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), - .optattrs = SEG6_F_LOCAL_COUNTERS, + .optattrs = SEG6_F_LOCAL_COUNTERS | + SEG6_F_LOCAL_FLAVORS, .input = input_action_end_x, }, { @@ -2070,7 +2131,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, { struct seg6_flavors_info *finfo = &slwt->flv_info; struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; - unsigned long fops; + int action = slwt->action; + __u32 fops, supp_fops = 0; int rc; rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, @@ -2086,7 +2148,8 @@ static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, return -EINVAL; fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); - if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { + rc = seg6_flv_supp_ops_by_action(action, &supp_fops); + if (rc < 0 || !supp_fops || (fops & ~supp_fops)) { NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); return -EOPNOTSUPP; } @@ -2618,6 +2681,11 @@ int __init seg6_local_init(void) */ BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); + /* Check whether the number of defined flavors exceeds the maximum + * allowed value. + */ + BUILD_BUG_ON(SEG6_LOCAL_FLV_OP_MAX + 1 > BITS_PER_TYPE(__u32)); + /* If the default NEXT-C-SID Locator-Block/Node Function lengths (in * bits) have been changed with invalid values, kernel build stops * here.
The NEXT-C-SID mechanism described in [1] offers the possibility of encoding several SRv6 segments within a single 128 bit SID address. Such a SID address is called a Compressed SID (C-SID) container. In this way, the length of the SID List can be drastically reduced. A SID instantiated with the NEXT-C-SID flavor considers an IPv6 address logically structured in three main blocks: i) Locator-Block; ii) Locator-Node Function; iii) Argument. C-SID container +------------------------------------------------------------------+ | Locator-Block |Loc-Node| Argument | | |Function| | +------------------------------------------------------------------+ <--------- B -----------> <- NF -> <------------- A ---------------> (i) The Locator-Block can be any IPv6 prefix available to the provider; (ii) The Locator-Node Function represents the node and the function to be triggered when a packet is received on the node; (iii) The Argument carries the remaining C-SIDs in the current C-SID container. This patch leverages the NEXT-C-SID mechanism previously introduced in the Linux SRv6 subsystem [2] to support SID compression capabilities in the SRv6 End.X behavior [3]. An SRv6 End.X behavior with NEXT-C-SID flavor works as an End.X behavior but it is capable of processing the compressed SID List encoded in C-SID containers. An SRv6 End.X behavior with NEXT-C-SID flavor can be configured to support user-provided Locator-Block and Locator-Node Function lengths. In this implementation, such lengths must be evenly divisible by 8 (i.e. must be byte-aligned), otherwise the kernel informs the user about invalid values with a meaningful error code and message through netlink_ext_ack. If Locator-Block and/or Locator-Node Function lengths are not provided by the user during configuration of an SRv6 End.X behavior instance with NEXT-C-SID flavor, the kernel will choose their default values i.e., 32-bit Locator-Block and 16-bit Locator-Node Function. [1] - https://datatracker.ietf.org/doc/html/draft-ietf-spring-srv6-srh-compression [2] - https://lore.kernel.org/all/20220912171619.16943-1-andrea.mayer@uniroma2.it/ [3] - https://datatracker.ietf.org/doc/html/rfc8986#name-endx-l3-cross-connect Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it> --- net/ipv6/seg6_local.c | 108 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 20 deletions(-)