+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;
+}
+