Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

seg6: add NEXT-C-SID support for SRv6 End behavior

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.

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.

This patch introduces the support for flavors in SRv6 End behavior
implementing the NEXT-C-SID one. An SRv6 End behavior with NEXT-C-SID
flavor works as an End behavior but it is capable of processing the
compressed SID List encoded in C-SID containers.

An SRv6 End 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 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://datatracker.ietf.org/doc/html/rfc8986

Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Andrea Mayer and committed by
Paolo Abeni
848f3c0d e2a8ecc4

+356 -3
+24
include/uapi/linux/seg6_local.h
··· 28 28 SEG6_LOCAL_BPF, 29 29 SEG6_LOCAL_VRFTABLE, 30 30 SEG6_LOCAL_COUNTERS, 31 + SEG6_LOCAL_FLAVORS, 31 32 __SEG6_LOCAL_MAX, 32 33 }; 33 34 #define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) ··· 110 109 }; 111 110 112 111 #define SEG6_LOCAL_CNT_MAX (__SEG6_LOCAL_CNT_MAX - 1) 112 + 113 + /* SRv6 End* Flavor attributes */ 114 + enum { 115 + SEG6_LOCAL_FLV_UNSPEC, 116 + SEG6_LOCAL_FLV_OPERATION, 117 + SEG6_LOCAL_FLV_LCBLOCK_BITS, 118 + SEG6_LOCAL_FLV_LCNODE_FN_BITS, 119 + __SEG6_LOCAL_FLV_MAX, 120 + }; 121 + 122 + #define SEG6_LOCAL_FLV_MAX (__SEG6_LOCAL_FLV_MAX - 1) 123 + 124 + /* Designed flavor operations for SRv6 End* Behavior */ 125 + enum { 126 + SEG6_LOCAL_FLV_OP_UNSPEC, 127 + SEG6_LOCAL_FLV_OP_PSP, 128 + SEG6_LOCAL_FLV_OP_USP, 129 + SEG6_LOCAL_FLV_OP_USD, 130 + SEG6_LOCAL_FLV_OP_NEXT_CSID, 131 + __SEG6_LOCAL_FLV_OP_MAX 132 + }; 133 + 134 + #define SEG6_LOCAL_FLV_OP_MAX (__SEG6_LOCAL_FLV_OP_MAX - 1) 113 135 114 136 #endif
+332 -3
net/ipv6/seg6_local.c
··· 73 73 char *name; 74 74 }; 75 75 76 + /* default length values (expressed in bits) for both Locator-Block and 77 + * Locator-Node Function. 78 + * 79 + * Both SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS *must* be: 80 + * i) greater than 0; 81 + * ii) evenly divisible by 8. In other terms, the lengths of the 82 + * Locator-Block and Locator-Node Function must be byte-aligned (we can 83 + * relax this constraint in the future if really needed). 84 + * 85 + * Moreover, a third condition must hold: 86 + * iii) SEG6_LOCAL_LCBLOCK_DBITS + SEG6_LOCAL_LCNODE_FN_DBITS <= 128. 87 + * 88 + * The correctness of SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS 89 + * values are checked during the kernel compilation. If the compilation stops, 90 + * check the value of these parameters to see if they meet conditions (i), (ii) 91 + * and (iii). 92 + */ 93 + #define SEG6_LOCAL_LCBLOCK_DBITS 32 94 + #define SEG6_LOCAL_LCNODE_FN_DBITS 16 95 + 96 + /* The following next_csid_chk_{cntr,lcblock,lcblock_fn}_bits macros can be 97 + * used directly to check whether the lengths (in bits) of Locator-Block and 98 + * Locator-Node Function are valid according to (i), (ii), (iii). 99 + */ 100 + #define next_csid_chk_cntr_bits(blen, flen) \ 101 + ((blen) + (flen) > 128) 102 + 103 + #define next_csid_chk_lcblock_bits(blen) \ 104 + ({ \ 105 + typeof(blen) __tmp = blen; \ 106 + (!__tmp || __tmp > 120 || (__tmp & 0x07)); \ 107 + }) 108 + 109 + #define next_csid_chk_lcnode_fn_bits(flen) \ 110 + next_csid_chk_lcblock_bits(flen) 111 + 112 + /* Supported Flavor operations are reported in this bitmask */ 113 + #define SEG6_LOCAL_FLV_SUPP_OPS (BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID)) 114 + 115 + struct seg6_flavors_info { 116 + /* Flavor operations */ 117 + __u32 flv_ops; 118 + 119 + /* Locator-Block length, expressed in bits */ 120 + __u8 lcblock_bits; 121 + /* Locator-Node Function length, expressed in bits*/ 122 + __u8 lcnode_func_bits; 123 + }; 124 + 76 125 enum seg6_end_dt_mode { 77 126 DT_INVALID_MODE = -EINVAL, 78 127 DT_LEGACY_MODE = 0, ··· 185 136 #ifdef CONFIG_NET_L3_MASTER_DEV 186 137 struct seg6_end_dt_info dt_info; 187 138 #endif 139 + struct seg6_flavors_info flv_info; 140 + 188 141 struct pcpu_seg6_local_counters __percpu *pcpu_counters; 189 142 190 143 int headroom; ··· 322 271 return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 323 272 } 324 273 325 - /* regular endpoint function */ 326 - static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 274 + static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo) 275 + { 276 + return finfo->lcblock_bits >> 3; 277 + } 278 + 279 + static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo) 280 + { 281 + return finfo->lcnode_func_bits >> 3; 282 + } 283 + 284 + static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr, 285 + const struct seg6_flavors_info *finfo) 286 + { 287 + __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 288 + __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 289 + __u8 arg_octects; 290 + int i; 291 + 292 + arg_octects = 16 - blk_octects - fnc_octects; 293 + for (i = 0; i < arg_octects; ++i) { 294 + if (addr->s6_addr[blk_octects + fnc_octects + i] != 0x00) 295 + return false; 296 + } 297 + 298 + return true; 299 + } 300 + 301 + /* assume that DA.Argument length > 0 */ 302 + static void seg6_next_csid_advance_arg(struct in6_addr *addr, 303 + const struct seg6_flavors_info *finfo) 304 + { 305 + __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 306 + __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 307 + 308 + /* advance DA.Argument */ 309 + memmove(&addr->s6_addr[blk_octects], 310 + &addr->s6_addr[blk_octects + fnc_octects], 311 + 16 - blk_octects - fnc_octects); 312 + 313 + memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects); 314 + } 315 + 316 + static int input_action_end_core(struct sk_buff *skb, 317 + struct seg6_local_lwt *slwt) 327 318 { 328 319 struct ipv6_sr_hdr *srh; 329 320 ··· 382 289 drop: 383 290 kfree_skb(skb); 384 291 return -EINVAL; 292 + } 293 + 294 + static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 295 + { 296 + const struct seg6_flavors_info *finfo = &slwt->flv_info; 297 + struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 298 + 299 + if (seg6_next_csid_is_arg_zero(daddr, finfo)) 300 + return input_action_end_core(skb, slwt); 301 + 302 + /* update DA */ 303 + seg6_next_csid_advance_arg(daddr, finfo); 304 + 305 + seg6_lookup_nexthop(skb, NULL, 0); 306 + 307 + return dst_input(skb); 308 + } 309 + 310 + static bool seg6_next_csid_enabled(__u32 fops) 311 + { 312 + return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID); 313 + } 314 + 315 + /* regular endpoint function */ 316 + static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 317 + { 318 + const struct seg6_flavors_info *finfo = &slwt->flv_info; 319 + 320 + if (seg6_next_csid_enabled(finfo->flv_ops)) 321 + return end_next_csid_core(skb, slwt); 322 + 323 + return input_action_end_core(skb, slwt); 385 324 } 386 325 387 326 /* regular endpoint, and forward to specified nexthop */ ··· 1076 951 { 1077 952 .action = SEG6_LOCAL_ACTION_END, 1078 953 .attrs = 0, 1079 - .optattrs = SEG6_F_LOCAL_COUNTERS, 954 + .optattrs = SEG6_F_LOCAL_COUNTERS | 955 + SEG6_F_ATTR(SEG6_LOCAL_FLAVORS), 1080 956 .input = input_action_end, 1081 957 }, 1082 958 { ··· 1258 1132 [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1259 1133 [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 1260 1134 [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1135 + [SEG6_LOCAL_FLAVORS] = { .type = NLA_NESTED }, 1261 1136 }; 1262 1137 1263 1138 static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt, ··· 1678 1551 free_percpu(slwt->pcpu_counters); 1679 1552 } 1680 1553 1554 + static const 1555 + struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] = { 1556 + [SEG6_LOCAL_FLV_OPERATION] = { .type = NLA_U32 }, 1557 + [SEG6_LOCAL_FLV_LCBLOCK_BITS] = { .type = NLA_U8 }, 1558 + [SEG6_LOCAL_FLV_LCNODE_FN_BITS] = { .type = NLA_U8 }, 1559 + }; 1560 + 1561 + /* check whether the lengths of the Locator-Block and Locator-Node Function 1562 + * are compatible with the dimension of a C-SID container. 1563 + */ 1564 + static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len) 1565 + { 1566 + /* Locator-Block and Locator-Node Function cannot exceed 128 bits 1567 + * (i.e. C-SID container lenghts). 1568 + */ 1569 + if (next_csid_chk_cntr_bits(block_len, func_len)) 1570 + return -EINVAL; 1571 + 1572 + /* Locator-Block length must be greater than zero and evenly divisible 1573 + * by 8. There must be room for a Locator-Node Function, at least. 1574 + */ 1575 + if (next_csid_chk_lcblock_bits(block_len)) 1576 + return -EINVAL; 1577 + 1578 + /* Locator-Node Function length must be greater than zero and evenly 1579 + * divisible by 8. There must be room for the Locator-Block. 1580 + */ 1581 + if (next_csid_chk_lcnode_fn_bits(func_len)) 1582 + return -EINVAL; 1583 + 1584 + return 0; 1585 + } 1586 + 1587 + static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb, 1588 + struct seg6_flavors_info *finfo, 1589 + struct netlink_ext_ack *extack) 1590 + { 1591 + __u8 func_len = SEG6_LOCAL_LCNODE_FN_DBITS; 1592 + __u8 block_len = SEG6_LOCAL_LCBLOCK_DBITS; 1593 + int rc; 1594 + 1595 + if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]) 1596 + block_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]); 1597 + 1598 + if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]) 1599 + func_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]); 1600 + 1601 + rc = seg6_chk_next_csid_cfg(block_len, func_len); 1602 + if (rc < 0) { 1603 + NL_SET_ERR_MSG(extack, 1604 + "Invalid Locator Block/Node Function lengths"); 1605 + return rc; 1606 + } 1607 + 1608 + finfo->lcblock_bits = block_len; 1609 + finfo->lcnode_func_bits = func_len; 1610 + 1611 + return 0; 1612 + } 1613 + 1614 + static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1615 + struct netlink_ext_ack *extack) 1616 + { 1617 + struct seg6_flavors_info *finfo = &slwt->flv_info; 1618 + struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; 1619 + unsigned long fops; 1620 + int rc; 1621 + 1622 + rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, 1623 + attrs[SEG6_LOCAL_FLAVORS], 1624 + seg6_local_flavors_policy, NULL); 1625 + if (rc < 0) 1626 + return rc; 1627 + 1628 + /* this attribute MUST always be present since it represents the Flavor 1629 + * operation(s) to be carried out. 1630 + */ 1631 + if (!tb[SEG6_LOCAL_FLV_OPERATION]) 1632 + return -EINVAL; 1633 + 1634 + fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); 1635 + if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { 1636 + NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); 1637 + return -EOPNOTSUPP; 1638 + } 1639 + 1640 + finfo->flv_ops = fops; 1641 + 1642 + if (seg6_next_csid_enabled(fops)) { 1643 + /* Locator-Block and Locator-Node Function lengths can be 1644 + * provided by the user space. Otherwise, default values are 1645 + * applied. 1646 + */ 1647 + rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack); 1648 + if (rc < 0) 1649 + return rc; 1650 + } 1651 + 1652 + return 0; 1653 + } 1654 + 1655 + static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb, 1656 + struct seg6_flavors_info *finfo) 1657 + { 1658 + if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCBLOCK_BITS, finfo->lcblock_bits)) 1659 + return -EMSGSIZE; 1660 + 1661 + if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCNODE_FN_BITS, 1662 + finfo->lcnode_func_bits)) 1663 + return -EMSGSIZE; 1664 + 1665 + return 0; 1666 + } 1667 + 1668 + static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1669 + { 1670 + struct seg6_flavors_info *finfo = &slwt->flv_info; 1671 + __u32 fops = finfo->flv_ops; 1672 + struct nlattr *nest; 1673 + int rc; 1674 + 1675 + nest = nla_nest_start(skb, SEG6_LOCAL_FLAVORS); 1676 + if (!nest) 1677 + return -EMSGSIZE; 1678 + 1679 + if (nla_put_u32(skb, SEG6_LOCAL_FLV_OPERATION, fops)) { 1680 + rc = -EMSGSIZE; 1681 + goto err; 1682 + } 1683 + 1684 + if (seg6_next_csid_enabled(fops)) { 1685 + rc = seg6_fill_nla_next_csid_cfg(skb, finfo); 1686 + if (rc < 0) 1687 + goto err; 1688 + } 1689 + 1690 + return nla_nest_end(skb, nest); 1691 + 1692 + err: 1693 + nla_nest_cancel(skb, nest); 1694 + return rc; 1695 + } 1696 + 1697 + static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a, 1698 + struct seg6_flavors_info *finfo_b) 1699 + { 1700 + if (finfo_a->lcblock_bits != finfo_b->lcblock_bits) 1701 + return 1; 1702 + 1703 + if (finfo_a->lcnode_func_bits != finfo_b->lcnode_func_bits) 1704 + return 1; 1705 + 1706 + return 0; 1707 + } 1708 + 1709 + static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1710 + { 1711 + struct seg6_flavors_info *finfo_a = &a->flv_info; 1712 + struct seg6_flavors_info *finfo_b = &b->flv_info; 1713 + 1714 + if (finfo_a->flv_ops != finfo_b->flv_ops) 1715 + return 1; 1716 + 1717 + if (seg6_next_csid_enabled(finfo_a->flv_ops)) { 1718 + if (seg6_cmp_nla_next_csid_cfg(finfo_a, finfo_b)) 1719 + return 1; 1720 + } 1721 + 1722 + return 0; 1723 + } 1724 + 1725 + static int encap_size_flavors(struct seg6_local_lwt *slwt) 1726 + { 1727 + struct seg6_flavors_info *finfo = &slwt->flv_info; 1728 + int nlsize; 1729 + 1730 + nlsize = nla_total_size(0) + /* nest SEG6_LOCAL_FLAVORS */ 1731 + nla_total_size(4); /* SEG6_LOCAL_FLV_OPERATION */ 1732 + 1733 + if (seg6_next_csid_enabled(finfo->flv_ops)) 1734 + nlsize += nla_total_size(1) + /* SEG6_LOCAL_FLV_LCBLOCK_BITS */ 1735 + nla_total_size(1); /* SEG6_LOCAL_FLV_LCNODE_FN_BITS */ 1736 + 1737 + return nlsize; 1738 + } 1739 + 1681 1740 struct seg6_action_param { 1682 1741 int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1683 1742 struct netlink_ext_ack *extack); ··· 1916 1603 .put = put_nla_counters, 1917 1604 .cmp = cmp_nla_counters, 1918 1605 .destroy = destroy_attr_counters }, 1606 + 1607 + [SEG6_LOCAL_FLAVORS] = { .parse = parse_nla_flavors, 1608 + .put = put_nla_flavors, 1609 + .cmp = cmp_nla_flavors }, 1919 1610 }; 1920 1611 1921 1612 /* call the destroy() callback (if available) for each set attribute in ··· 2233 1916 /* SEG6_LOCAL_CNT_ERRORS */ 2234 1917 nla_total_size_64bit(sizeof(__u64)); 2235 1918 1919 + if (attrs & SEG6_F_ATTR(SEG6_LOCAL_FLAVORS)) 1920 + nlsize += encap_size_flavors(slwt); 1921 + 2236 1922 return nlsize; 2237 1923 } 2238 1924 ··· 2290 1970 * exceeds the allowed value. 2291 1971 */ 2292 1972 BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 1973 + 1974 + /* If the default NEXT-C-SID Locator-Block/Node Function lengths (in 1975 + * bits) have been changed with invalid values, kernel build stops 1976 + * here. 1977 + */ 1978 + BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS, 1979 + SEG6_LOCAL_LCNODE_FN_DBITS)); 1980 + BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS)); 1981 + BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS)); 2293 1982 2294 1983 return lwtunnel_encap_add_ops(&seg6_local_ops, 2295 1984 LWTUNNEL_ENCAP_SEG6_LOCAL);