forked from Minki/linux
Merge branch 'seg6-add-next-c-sid-support-for-srv6-end-behavior'
Andrea Mayer says: ==================== seg6: add NEXT-C-SID support for SRv6 End behavior The Segment Routing (SR) architecture is based on loose source routing. A list of instructions, called segments, can be added to the packet headers to influence the forwarding and processing of the packets in an SR enabled network. In SRv6 (Segment Routing over IPv6 data plane) [1], the segment identifiers (SIDs) are IPv6 addresses (128 bits) and the segment list (SID List) is carried in the Segment Routing Header (SRH). A segment may correspond to a "behavior" that is executed by a node when the packet is received. The Linux kernel currently supports a large subset of the behaviors described in [2] (e.g., End, End.X, End.T and so on). Some SRv6 scenarios (i.e.: traffic-engineering, fast-rerouting, VPN, mobile network backhaul, etc.) may require a large number of segments (i.e. up to 15). Therefore, reducing the size of the SID List is useful to minimize the impact on MTU (Maximum Transfer Unit) and to enable SRv6 on legacy hardware devices with limited processing power that can suffer from long IPv6 headers. Draft-ietf-spring-srv6-srh-compression [3] extends the SRv6 architecture by providing different mechanisms for the efficient representation (i.e. compression) of the SID List. The NEXT-C-SID mechanism described in [3] offers the possibility of encoding several SRv6 segments within a single 128 bit SID address. Such a SID address is called a Compressed SID Container. In this way, the length of the SID List can be drastically reduced. In some cases, the SRH can be omitted, as the IPv6 Destination Address can carry the whole Segment List, using its compressed representation. The NEXT-C-SID mechanism relies on the "flavors" framework defined in [2]. The flavors represent additional operations that can modify or extend a subset of the existing behaviors. In this patchset we extend the SRv6 Subsystem in order to support the NEXT-C-SID mechanism. In details the patchset is made of: - patch 1/3: add netlink_ext_ack support in parsing SRv6 behavior attributes; - patch 2/3: add NEXT-C-SID support for SRv6 End behavior; - patch 3/3: add selftest for NEXT-C-SID in SRv6 End behavior. The corresponding iproute2 patch for supporting the NEXT-C-SID in SRv6 End behavior is provided in a separated patchset. Comments, improvements and suggestions are always appreciated. [1] - https://datatracker.ietf.org/doc/html/rfc8754 [2] - https://datatracker.ietf.org/doc/html/rfc8986 [3] - https://datatracker.ietf.org/doc/html/draft-ietf-spring-srv6-srh-compression ==================== Link: https://lore.kernel.org/r/20220912171619.16943-1-andrea.mayer@uniroma2.it Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
cec9d59e89
@ -28,6 +28,7 @@ enum {
|
||||
SEG6_LOCAL_BPF,
|
||||
SEG6_LOCAL_VRFTABLE,
|
||||
SEG6_LOCAL_COUNTERS,
|
||||
SEG6_LOCAL_FLAVORS,
|
||||
__SEG6_LOCAL_MAX,
|
||||
};
|
||||
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
|
||||
@ -110,4 +111,27 @@ enum {
|
||||
|
||||
#define SEG6_LOCAL_CNT_MAX (__SEG6_LOCAL_CNT_MAX - 1)
|
||||
|
||||
/* SRv6 End* Flavor attributes */
|
||||
enum {
|
||||
SEG6_LOCAL_FLV_UNSPEC,
|
||||
SEG6_LOCAL_FLV_OPERATION,
|
||||
SEG6_LOCAL_FLV_LCBLOCK_BITS,
|
||||
SEG6_LOCAL_FLV_LCNODE_FN_BITS,
|
||||
__SEG6_LOCAL_FLV_MAX,
|
||||
};
|
||||
|
||||
#define SEG6_LOCAL_FLV_MAX (__SEG6_LOCAL_FLV_MAX - 1)
|
||||
|
||||
/* Designed flavor operations for SRv6 End* Behavior */
|
||||
enum {
|
||||
SEG6_LOCAL_FLV_OP_UNSPEC,
|
||||
SEG6_LOCAL_FLV_OP_PSP,
|
||||
SEG6_LOCAL_FLV_OP_USP,
|
||||
SEG6_LOCAL_FLV_OP_USD,
|
||||
SEG6_LOCAL_FLV_OP_NEXT_CSID,
|
||||
__SEG6_LOCAL_FLV_OP_MAX
|
||||
};
|
||||
|
||||
#define SEG6_LOCAL_FLV_OP_MAX (__SEG6_LOCAL_FLV_OP_MAX - 1)
|
||||
|
||||
#endif
|
||||
|
@ -73,6 +73,55 @@ struct bpf_lwt_prog {
|
||||
char *name;
|
||||
};
|
||||
|
||||
/* default length values (expressed in bits) for both Locator-Block and
|
||||
* Locator-Node Function.
|
||||
*
|
||||
* Both SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS *must* be:
|
||||
* i) greater than 0;
|
||||
* ii) evenly divisible by 8. In other terms, the lengths of the
|
||||
* Locator-Block and Locator-Node Function must be byte-aligned (we can
|
||||
* relax this constraint in the future if really needed).
|
||||
*
|
||||
* Moreover, a third condition must hold:
|
||||
* iii) SEG6_LOCAL_LCBLOCK_DBITS + SEG6_LOCAL_LCNODE_FN_DBITS <= 128.
|
||||
*
|
||||
* The correctness of SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS
|
||||
* values are checked during the kernel compilation. If the compilation stops,
|
||||
* check the value of these parameters to see if they meet conditions (i), (ii)
|
||||
* and (iii).
|
||||
*/
|
||||
#define SEG6_LOCAL_LCBLOCK_DBITS 32
|
||||
#define SEG6_LOCAL_LCNODE_FN_DBITS 16
|
||||
|
||||
/* The following next_csid_chk_{cntr,lcblock,lcblock_fn}_bits macros can be
|
||||
* used directly to check whether the lengths (in bits) of Locator-Block and
|
||||
* Locator-Node Function are valid according to (i), (ii), (iii).
|
||||
*/
|
||||
#define next_csid_chk_cntr_bits(blen, flen) \
|
||||
((blen) + (flen) > 128)
|
||||
|
||||
#define next_csid_chk_lcblock_bits(blen) \
|
||||
({ \
|
||||
typeof(blen) __tmp = blen; \
|
||||
(!__tmp || __tmp > 120 || (__tmp & 0x07)); \
|
||||
})
|
||||
|
||||
#define next_csid_chk_lcnode_fn_bits(flen) \
|
||||
next_csid_chk_lcblock_bits(flen)
|
||||
|
||||
/* Supported Flavor operations are reported in this bitmask */
|
||||
#define SEG6_LOCAL_FLV_SUPP_OPS (BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID))
|
||||
|
||||
struct seg6_flavors_info {
|
||||
/* Flavor operations */
|
||||
__u32 flv_ops;
|
||||
|
||||
/* Locator-Block length, expressed in bits */
|
||||
__u8 lcblock_bits;
|
||||
/* Locator-Node Function length, expressed in bits*/
|
||||
__u8 lcnode_func_bits;
|
||||
};
|
||||
|
||||
enum seg6_end_dt_mode {
|
||||
DT_INVALID_MODE = -EINVAL,
|
||||
DT_LEGACY_MODE = 0,
|
||||
@ -136,6 +185,8 @@ struct seg6_local_lwt {
|
||||
#ifdef CONFIG_NET_L3_MASTER_DEV
|
||||
struct seg6_end_dt_info dt_info;
|
||||
#endif
|
||||
struct seg6_flavors_info flv_info;
|
||||
|
||||
struct pcpu_seg6_local_counters __percpu *pcpu_counters;
|
||||
|
||||
int headroom;
|
||||
@ -271,8 +322,50 @@ int seg6_lookup_nexthop(struct sk_buff *skb,
|
||||
return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false);
|
||||
}
|
||||
|
||||
/* regular endpoint function */
|
||||
static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
|
||||
static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo)
|
||||
{
|
||||
return finfo->lcblock_bits >> 3;
|
||||
}
|
||||
|
||||
static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo)
|
||||
{
|
||||
return finfo->lcnode_func_bits >> 3;
|
||||
}
|
||||
|
||||
static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr,
|
||||
const struct seg6_flavors_info *finfo)
|
||||
{
|
||||
__u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo);
|
||||
__u8 blk_octects = seg6_flv_lcblock_octects(finfo);
|
||||
__u8 arg_octects;
|
||||
int i;
|
||||
|
||||
arg_octects = 16 - blk_octects - fnc_octects;
|
||||
for (i = 0; i < arg_octects; ++i) {
|
||||
if (addr->s6_addr[blk_octects + fnc_octects + i] != 0x00)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* assume that DA.Argument length > 0 */
|
||||
static void seg6_next_csid_advance_arg(struct in6_addr *addr,
|
||||
const struct seg6_flavors_info *finfo)
|
||||
{
|
||||
__u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo);
|
||||
__u8 blk_octects = seg6_flv_lcblock_octects(finfo);
|
||||
|
||||
/* advance DA.Argument */
|
||||
memmove(&addr->s6_addr[blk_octects],
|
||||
&addr->s6_addr[blk_octects + fnc_octects],
|
||||
16 - blk_octects - fnc_octects);
|
||||
|
||||
memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects);
|
||||
}
|
||||
|
||||
static int input_action_end_core(struct sk_buff *skb,
|
||||
struct seg6_local_lwt *slwt)
|
||||
{
|
||||
struct ipv6_sr_hdr *srh;
|
||||
|
||||
@ -291,6 +384,38 @@ drop:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int end_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_core(skb, slwt);
|
||||
|
||||
/* update DA */
|
||||
seg6_next_csid_advance_arg(daddr, finfo);
|
||||
|
||||
seg6_lookup_nexthop(skb, NULL, 0);
|
||||
|
||||
return dst_input(skb);
|
||||
}
|
||||
|
||||
static bool seg6_next_csid_enabled(__u32 fops)
|
||||
{
|
||||
return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID);
|
||||
}
|
||||
|
||||
/* regular endpoint function */
|
||||
static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
|
||||
{
|
||||
const struct seg6_flavors_info *finfo = &slwt->flv_info;
|
||||
|
||||
if (seg6_next_csid_enabled(finfo->flv_ops))
|
||||
return end_next_csid_core(skb, slwt);
|
||||
|
||||
return input_action_end_core(skb, slwt);
|
||||
}
|
||||
|
||||
/* regular endpoint, and forward to specified nexthop */
|
||||
static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
|
||||
{
|
||||
@ -951,7 +1076,8 @@ static struct seg6_action_desc seg6_action_table[] = {
|
||||
{
|
||||
.action = SEG6_LOCAL_ACTION_END,
|
||||
.attrs = 0,
|
||||
.optattrs = SEG6_F_LOCAL_COUNTERS,
|
||||
.optattrs = SEG6_F_LOCAL_COUNTERS |
|
||||
SEG6_F_ATTR(SEG6_LOCAL_FLAVORS),
|
||||
.input = input_action_end,
|
||||
},
|
||||
{
|
||||
@ -1132,9 +1258,11 @@ static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
|
||||
[SEG6_LOCAL_OIF] = { .type = NLA_U32 },
|
||||
[SEG6_LOCAL_BPF] = { .type = NLA_NESTED },
|
||||
[SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED },
|
||||
[SEG6_LOCAL_FLAVORS] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ipv6_sr_hdr *srh;
|
||||
int len;
|
||||
@ -1191,7 +1319,8 @@ static void destroy_attr_srh(struct seg6_local_lwt *slwt)
|
||||
kfree(slwt->srh);
|
||||
}
|
||||
|
||||
static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]);
|
||||
|
||||
@ -1225,7 +1354,8 @@ seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt)
|
||||
}
|
||||
|
||||
static int parse_nla_vrftable(struct nlattr **attrs,
|
||||
struct seg6_local_lwt *slwt)
|
||||
struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt);
|
||||
|
||||
@ -1261,7 +1391,8 @@ static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]),
|
||||
sizeof(struct in_addr));
|
||||
@ -1287,7 +1418,8 @@ static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
|
||||
return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]),
|
||||
sizeof(struct in6_addr));
|
||||
@ -1313,7 +1445,8 @@ static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
|
||||
return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr));
|
||||
}
|
||||
|
||||
static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]);
|
||||
|
||||
@ -1336,7 +1469,8 @@ static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]);
|
||||
|
||||
@ -1366,7 +1500,8 @@ static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
|
||||
.len = MAX_PROG_NAME },
|
||||
};
|
||||
|
||||
static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
|
||||
struct bpf_prog *p;
|
||||
@ -1444,7 +1579,8 @@ nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = {
|
||||
};
|
||||
|
||||
static int parse_nla_counters(struct nlattr **attrs,
|
||||
struct seg6_local_lwt *slwt)
|
||||
struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct pcpu_seg6_local_counters __percpu *pcounters;
|
||||
struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1];
|
||||
@ -1542,8 +1678,195 @@ static void destroy_attr_counters(struct seg6_local_lwt *slwt)
|
||||
free_percpu(slwt->pcpu_counters);
|
||||
}
|
||||
|
||||
static const
|
||||
struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] = {
|
||||
[SEG6_LOCAL_FLV_OPERATION] = { .type = NLA_U32 },
|
||||
[SEG6_LOCAL_FLV_LCBLOCK_BITS] = { .type = NLA_U8 },
|
||||
[SEG6_LOCAL_FLV_LCNODE_FN_BITS] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
/* check whether the lengths of the Locator-Block and Locator-Node Function
|
||||
* are compatible with the dimension of a C-SID container.
|
||||
*/
|
||||
static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len)
|
||||
{
|
||||
/* Locator-Block and Locator-Node Function cannot exceed 128 bits
|
||||
* (i.e. C-SID container lenghts).
|
||||
*/
|
||||
if (next_csid_chk_cntr_bits(block_len, func_len))
|
||||
return -EINVAL;
|
||||
|
||||
/* Locator-Block length must be greater than zero and evenly divisible
|
||||
* by 8. There must be room for a Locator-Node Function, at least.
|
||||
*/
|
||||
if (next_csid_chk_lcblock_bits(block_len))
|
||||
return -EINVAL;
|
||||
|
||||
/* Locator-Node Function length must be greater than zero and evenly
|
||||
* divisible by 8. There must be room for the Locator-Block.
|
||||
*/
|
||||
if (next_csid_chk_lcnode_fn_bits(func_len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb,
|
||||
struct seg6_flavors_info *finfo,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
__u8 func_len = SEG6_LOCAL_LCNODE_FN_DBITS;
|
||||
__u8 block_len = SEG6_LOCAL_LCBLOCK_DBITS;
|
||||
int rc;
|
||||
|
||||
if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS])
|
||||
block_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]);
|
||||
|
||||
if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS])
|
||||
func_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]);
|
||||
|
||||
rc = seg6_chk_next_csid_cfg(block_len, func_len);
|
||||
if (rc < 0) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"Invalid Locator Block/Node Function lengths");
|
||||
return rc;
|
||||
}
|
||||
|
||||
finfo->lcblock_bits = block_len;
|
||||
finfo->lcnode_func_bits = func_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct seg6_flavors_info *finfo = &slwt->flv_info;
|
||||
struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1];
|
||||
unsigned long fops;
|
||||
int rc;
|
||||
|
||||
rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX,
|
||||
attrs[SEG6_LOCAL_FLAVORS],
|
||||
seg6_local_flavors_policy, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* this attribute MUST always be present since it represents the Flavor
|
||||
* operation(s) to be carried out.
|
||||
*/
|
||||
if (!tb[SEG6_LOCAL_FLV_OPERATION])
|
||||
return -EINVAL;
|
||||
|
||||
fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]);
|
||||
if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) {
|
||||
NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
finfo->flv_ops = fops;
|
||||
|
||||
if (seg6_next_csid_enabled(fops)) {
|
||||
/* Locator-Block and Locator-Node Function lengths can be
|
||||
* provided by the user space. Otherwise, default values are
|
||||
* applied.
|
||||
*/
|
||||
rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb,
|
||||
struct seg6_flavors_info *finfo)
|
||||
{
|
||||
if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCBLOCK_BITS, finfo->lcblock_bits))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCNODE_FN_BITS,
|
||||
finfo->lcnode_func_bits))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt)
|
||||
{
|
||||
struct seg6_flavors_info *finfo = &slwt->flv_info;
|
||||
__u32 fops = finfo->flv_ops;
|
||||
struct nlattr *nest;
|
||||
int rc;
|
||||
|
||||
nest = nla_nest_start(skb, SEG6_LOCAL_FLAVORS);
|
||||
if (!nest)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(skb, SEG6_LOCAL_FLV_OPERATION, fops)) {
|
||||
rc = -EMSGSIZE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (seg6_next_csid_enabled(fops)) {
|
||||
rc = seg6_fill_nla_next_csid_cfg(skb, finfo);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return nla_nest_end(skb, nest);
|
||||
|
||||
err:
|
||||
nla_nest_cancel(skb, nest);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a,
|
||||
struct seg6_flavors_info *finfo_b)
|
||||
{
|
||||
if (finfo_a->lcblock_bits != finfo_b->lcblock_bits)
|
||||
return 1;
|
||||
|
||||
if (finfo_a->lcnode_func_bits != finfo_b->lcnode_func_bits)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
|
||||
{
|
||||
struct seg6_flavors_info *finfo_a = &a->flv_info;
|
||||
struct seg6_flavors_info *finfo_b = &b->flv_info;
|
||||
|
||||
if (finfo_a->flv_ops != finfo_b->flv_ops)
|
||||
return 1;
|
||||
|
||||
if (seg6_next_csid_enabled(finfo_a->flv_ops)) {
|
||||
if (seg6_cmp_nla_next_csid_cfg(finfo_a, finfo_b))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encap_size_flavors(struct seg6_local_lwt *slwt)
|
||||
{
|
||||
struct seg6_flavors_info *finfo = &slwt->flv_info;
|
||||
int nlsize;
|
||||
|
||||
nlsize = nla_total_size(0) + /* nest SEG6_LOCAL_FLAVORS */
|
||||
nla_total_size(4); /* SEG6_LOCAL_FLV_OPERATION */
|
||||
|
||||
if (seg6_next_csid_enabled(finfo->flv_ops))
|
||||
nlsize += nla_total_size(1) + /* SEG6_LOCAL_FLV_LCBLOCK_BITS */
|
||||
nla_total_size(1); /* SEG6_LOCAL_FLV_LCNODE_FN_BITS */
|
||||
|
||||
return nlsize;
|
||||
}
|
||||
|
||||
struct seg6_action_param {
|
||||
int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
|
||||
int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
|
||||
int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
|
||||
|
||||
@ -1593,6 +1916,10 @@ static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
|
||||
.put = put_nla_counters,
|
||||
.cmp = cmp_nla_counters,
|
||||
.destroy = destroy_attr_counters },
|
||||
|
||||
[SEG6_LOCAL_FLAVORS] = { .parse = parse_nla_flavors,
|
||||
.put = put_nla_flavors,
|
||||
.cmp = cmp_nla_flavors },
|
||||
};
|
||||
|
||||
/* call the destroy() callback (if available) for each set attribute in
|
||||
@ -1636,7 +1963,8 @@ static void destroy_attrs(struct seg6_local_lwt *slwt)
|
||||
}
|
||||
|
||||
static int parse_nla_optional_attrs(struct nlattr **attrs,
|
||||
struct seg6_local_lwt *slwt)
|
||||
struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct seg6_action_desc *desc = slwt->desc;
|
||||
unsigned long parsed_optattrs = 0;
|
||||
@ -1652,7 +1980,7 @@ static int parse_nla_optional_attrs(struct nlattr **attrs,
|
||||
*/
|
||||
param = &seg6_action_params[i];
|
||||
|
||||
err = param->parse(attrs, slwt);
|
||||
err = param->parse(attrs, slwt, extack);
|
||||
if (err < 0)
|
||||
goto parse_optattrs_err;
|
||||
|
||||
@ -1705,7 +2033,8 @@ static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt)
|
||||
ops->destroy_state(slwt);
|
||||
}
|
||||
|
||||
static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct seg6_action_param *param;
|
||||
struct seg6_action_desc *desc;
|
||||
@ -1749,14 +2078,14 @@ static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
||||
|
||||
param = &seg6_action_params[i];
|
||||
|
||||
err = param->parse(attrs, slwt);
|
||||
err = param->parse(attrs, slwt, extack);
|
||||
if (err < 0)
|
||||
goto parse_attrs_err;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the optional attributes, if any */
|
||||
err = parse_nla_optional_attrs(attrs, slwt);
|
||||
err = parse_nla_optional_attrs(attrs, slwt, extack);
|
||||
if (err < 0)
|
||||
goto parse_attrs_err;
|
||||
|
||||
@ -1800,7 +2129,7 @@ static int seg6_local_build_state(struct net *net, struct nlattr *nla,
|
||||
slwt = seg6_local_lwtunnel(newts);
|
||||
slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
|
||||
|
||||
err = parse_nla_action(tb, slwt);
|
||||
err = parse_nla_action(tb, slwt, extack);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
@ -1904,6 +2233,9 @@ static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
|
||||
/* SEG6_LOCAL_CNT_ERRORS */
|
||||
nla_total_size_64bit(sizeof(__u64));
|
||||
|
||||
if (attrs & SEG6_F_ATTR(SEG6_LOCAL_FLAVORS))
|
||||
nlsize += encap_size_flavors(slwt);
|
||||
|
||||
return nlsize;
|
||||
}
|
||||
|
||||
@ -1959,6 +2291,15 @@ int __init seg6_local_init(void)
|
||||
*/
|
||||
BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long));
|
||||
|
||||
/* If the default NEXT-C-SID Locator-Block/Node Function lengths (in
|
||||
* bits) have been changed with invalid values, kernel build stops
|
||||
* here.
|
||||
*/
|
||||
BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS,
|
||||
SEG6_LOCAL_LCNODE_FN_DBITS));
|
||||
BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS));
|
||||
BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS));
|
||||
|
||||
return lwtunnel_encap_add_ops(&seg6_local_ops,
|
||||
LWTUNNEL_ENCAP_SEG6_LOCAL);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ TEST_PROGS += srv6_end_dt4_l3vpn_test.sh
|
||||
TEST_PROGS += srv6_end_dt6_l3vpn_test.sh
|
||||
TEST_PROGS += srv6_hencap_red_l3vpn_test.sh
|
||||
TEST_PROGS += srv6_hl2encap_red_l2vpn_test.sh
|
||||
TEST_PROGS += srv6_end_next_csid_l3vpn_test.sh
|
||||
TEST_PROGS += vrf_strict_mode_test.sh
|
||||
TEST_PROGS += arp_ndisc_evict_nocarrier.sh
|
||||
TEST_PROGS += ndisc_unsolicited_na_test.sh
|
||||
|
1145
tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh
Executable file
1145
tools/testing/selftests/net/srv6_end_next_csid_l3vpn_test.sh
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user