return -1;
}
+static const char *seg6_flavor_names[SEG6_LOCAL_FLV_OP_MAX + 1] = {
+ [SEG6_LOCAL_FLV_OP_PSP] = "psp",
+ [SEG6_LOCAL_FLV_OP_USP] = "usp",
+ [SEG6_LOCAL_FLV_OP_USD] = "usd",
+ [SEG6_LOCAL_FLV_OP_NEXT_CSID] = "next-csid"
+};
+
+static int read_seg6_local_flv_type(const char *name)
+{
+ int i;
+
+ for (i = 1; i < SEG6_LOCAL_FLV_OP_MAX + 1; ++i) {
+ if (!seg6_flavor_names[i])
+ continue;
+
+ if (strcasecmp(seg6_flavor_names[i], name) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+static int parse_seg6local_flavors(const char *buf, __u32 *flv_mask)
+{
+ unsigned char flavor_ok[SEG6_LOCAL_FLV_OP_MAX + 1] = { 0, };
+ char *wbuf;
+ __u32 mask = 0;
+ int index;
+ char *s;
+
+ /* strtok changes first parameter, so we need to make a local copy */
+ wbuf = strdupa(buf);
+
+ if (strlen(wbuf) == 0)
+ return -1;
+
+ for (s = strtok(wbuf, ","); s; s = strtok(NULL, ",")) {
+ index = read_seg6_local_flv_type(s);
+ if (index < 0 || index > SEG6_LOCAL_FLV_OP_MAX)
+ return -1;
+ /* we check for duplicates */
+ if (flavor_ok[index]++)
+ return -1;
+
+ mask |= (1 << index);
+ }
+
+ *flv_mask = mask;
+ return 0;
+}
+
+static void print_flavors(FILE *fp, __u32 flavors)
+{
+ int i, fnumber = 0;
+ char *flv_name;
+
+ if (is_json_context())
+ open_json_array(PRINT_JSON, "flavors");
+ else
+ print_string(PRINT_FP, NULL, "flavors ", NULL);
+
+ for (i = 0; i < SEG6_LOCAL_FLV_OP_MAX + 1; ++i) {
+ if (flavors & (1 << i)) {
+ flv_name = (char *) seg6_flavor_names[i];
+ if (!flv_name)
+ continue;
+
+ if (is_json_context())
+ print_string(PRINT_JSON, NULL, NULL, flv_name);
+ else {
+ if (fnumber++ == 0)
+ print_string(PRINT_FP, NULL, "%s", flv_name);
+ else
+ print_string(PRINT_FP, NULL, ",%s", flv_name);
+ }
+ }
+ }
+
+ if (is_json_context())
+ close_json_array(PRINT_JSON, NULL);
+ else
+ print_string(PRINT_FP, NULL, " ", NULL);
+}
+
+static void print_flavors_attr(FILE *fp, const char *key, __u32 value)
+{
+ if (is_json_context()) {
+ print_u64(PRINT_JSON, key, NULL, value);
+ } else {
+ print_string(PRINT_FP, NULL, "%s ", key);
+ print_num(fp, 1, value);
+ }
+}
+
static void print_encap_seg6(FILE *fp, struct rtattr *encap)
{
struct rtattr *tb[SEG6_IPTUNNEL_MAX+1];
}
}
+static void print_seg6_local_flavors(FILE *fp, struct rtattr *encap)
+{
+ struct rtattr *tb[SEG6_LOCAL_FLV_MAX + 1];
+ __u8 lbl = 0, nfl = 0;
+ __u32 flavors = 0;
+
+ parse_rtattr_nested(tb, SEG6_LOCAL_FLV_MAX, encap);
+
+ if (tb[SEG6_LOCAL_FLV_OPERATION]) {
+ flavors = rta_getattr_u32(tb[SEG6_LOCAL_FLV_OPERATION]);
+ print_flavors(fp, flavors);
+ }
+
+ if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]) {
+ lbl = rta_getattr_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]);
+ print_flavors_attr(fp, "lblen", lbl);
+ }
+
+ if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]) {
+ nfl = rta_getattr_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]);
+ print_flavors_attr(fp, "nflen", nfl);
+ }
+}
+
static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
{
struct rtattr *tb[SEG6_LOCAL_MAX + 1];
if (tb[SEG6_LOCAL_COUNTERS] && show_stats)
print_seg6_local_counters(fp, tb[SEG6_LOCAL_COUNTERS]);
+
+ if (tb[SEG6_LOCAL_FLAVORS])
+ print_seg6_local_flavors(fp, tb[SEG6_LOCAL_FLAVORS]);
}
static void print_encap_mpls(FILE *fp, struct rtattr *encap)
return 0;
}
+static int seg6local_parse_flavors(struct rtattr *rta, size_t len,
+ int *argcp, char ***argvp, int attr)
+{
+ int lbl_ok = 0, nfl_ok = 0;
+ __u8 lbl = 0, nfl = 0;
+ struct rtattr *nest;
+ __u32 flavors = 0;
+ int ret;
+
+ char **argv = *argvp;
+ int argc = *argcp;
+
+ nest = rta_nest(rta, len, attr);
+
+ ret = parse_seg6local_flavors(*argv, &flavors);
+ if (ret < 0)
+ return ret;
+
+ ret = rta_addattr32(rta, len, SEG6_LOCAL_FLV_OPERATION, flavors);
+ if (ret < 0)
+ return ret;
+
+ if (flavors & (1 << SEG6_LOCAL_FLV_OP_NEXT_CSID)) {
+ NEXT_ARG_FWD();
+ if (strcmp(*argv, "lblen") == 0){
+ NEXT_ARG();
+ if (lbl_ok++)
+ duparg2("lblen", *argv);
+ if (get_u8(&lbl, *argv, 0))
+ invarg("\"locator-block length\" value is invalid\n", *argv);
+ ret = rta_addattr8(rta, len, SEG6_LOCAL_FLV_LCBLOCK_BITS, lbl);
+ NEXT_ARG_FWD();
+ }
+
+ if (strcmp(*argv, "nflen") == 0){
+ NEXT_ARG();
+ if (nfl_ok++)
+ duparg2("nflen", *argv);
+ if (get_u8(&nfl, *argv, 0))
+ invarg("\"locator-node function length\" value is invalid\n", *argv);
+ ret = rta_addattr8(rta, len, SEG6_LOCAL_FLV_LCNODE_FN_BITS, nfl);
+ NEXT_ARG_FWD();
+ }
+ PREV_ARG();
+ }
+
+ rta_nest_end(rta, nest);
+
+ *argcp = argc;
+ *argvp = argv;
+
+ return 0;
+}
+
static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
char ***argvp)
{
+ int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0, flavors_ok = 0;
int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
int action_ok = 0, srh_ok = 0, bpf_ok = 0, counters_ok = 0;
- int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0;
__u32 action = 0, table, vrftable, iif, oif;
struct ipv6_sr_hdr *srh;
char **argv = *argvp;
duparg2("count", *argv);
ret = seg6local_fill_counters(rta, len,
SEG6_LOCAL_COUNTERS);
+ } else if (strcmp(*argv, "flavors") == 0) {
+ NEXT_ARG();
+ if (flavors_ok++)
+ duparg2("flavors", *argv);
+
+ if (seg6local_parse_flavors(rta, len, &argc, &argv,
+ SEG6_LOCAL_FLAVORS))
+ invarg("invalid \"flavors\" attribute\n",
+ *argv);
} else if (strcmp(*argv, "srh") == 0) {
NEXT_ARG();
if (srh_ok++)
The following actions are currently supported (\fBLinux 4.14+ only\fR).
.in +2
-.B End
+.BR End " [ " flavors
+.IR FLAVORS " ] "
- Regular SRv6 processing as intermediate segment endpoint.
This action only accepts packets with a non-zero Segments Left
-value. Other matching packets are dropped.
+value. Other matching packets are dropped. The presence of flavors
+can change the regular processing of an End behavior according to
+the user-provided Flavor operations and information carried in the packet.
+See \fBFlavors parameters\fR section.
.B End.X nh6
.I NEXTHOP
followed by the specified SRH. The destination address of the outer IPv6
header is set to the first segment of the new SRH. The source
address is set as described in \fBip-sr\fR(8).
+
+.B Flavors parameters
+
+The flavors represent additional operations that can modify or extend a
+subset of the existing behaviors.
+.in +2
+
+.B flavors
+.IR OPERATION "[," OPERATION "] [" ATTRIBUTES "]"
+.in +2
+
+.IR OPERATION " := { "
+.BR psp " | "
+.BR usp " | "
+.BR usd " | "
+.BR next-csid " }"
+
+.IR ATTRIBUTES " := {"
+.IR "KEY VALUE" " } ["
+.IR ATTRIBUTES " ]"
+
+.IR KEY " := { "
+.BR lblen " | "
+.BR nflen " } "
.in -2
+.B psp
+- Penultimate Segment Pop of the SRH (not yet supported in kernel)
+
+.B usp
+- Ultimate Segment Pop of the SRH (not yet supported in kernel)
+
+.B usd
+- Ultimate Segment Decapsulation (not yet supported in kernel)
+
+.B next-csid
+- The NEXT-C-SID mechanism offers the possibility of encoding
+several SRv6 segments within a single 128 bit SID address. The NEXT-C-SID
+flavor can be configured to support user-provided Locator-Block and
+Locator-Node Function lengths. 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 default value is 32-bit for
+Locator-Block and 16-bit for Locator-Node Function.
+
+.BI lblen " VALUE "
+- defines the Locator-Block length for NEXT-C-SID flavor.
+The Locator-Block length must be greater than 0 and evenly divisible by 8. This
+attribute can be used only with NEXT-C-SID flavor.
+
+.BI nflen " VALUE "
+- defines the Locator-Node Function length for NEXT-C-SID
+flavors. The Locator-Node Function length must be greater than 0 and evenly
+divisible by 8. This attribute can be used only with NEXT-C-SID flavor.
+.in -4
+
.B ioam6
.in +2
.B freq K/N
Adds an IPv6 route with SRv6 decapsulation and forward with lookup in VRF table.
.RE
.PP
+ip -6 route add 2001:db8:1::/64 encap seg6local action End flavors next-csid dev eth0
+.RS 4
+Adds an IPv6 route with SRv6 End behavior with next-csid flavor enabled.
+.RE
+.PP
+ip -6 route add 2001:db8:1::/64 encap seg6local action End flavors next-csid lblen 48 nflen 16 dev eth0
+.RS 4
+Adds an IPv6 route with SRv6 End behavior with next-csid flavor enabled and user-provided Locator-Block and Locator-Node Function lengths.
+.RE
+.PP
ip -6 route add 2001:db8:1::/64 encap ioam6 freq 2/5 mode encap tundst 2001:db8:42::1 trace prealloc type 0x800000 ns 1 size 12 dev eth0
.RS 4
Adds an IPv6 route with an IOAM Pre-allocated Trace encapsulation (ip6ip6) that only includes the hop limit and the node id, configured for the IOAM namespace 1 and a pre-allocated data block of 12 octets (will be injected in 2 packets every 5 packets).