.IR PRIORITY " | "
.BR cvlan_ethtype " { " ipv4 " | " ipv6 " | "
.IR ETH_TYPE " } | "
+.B mpls
+.IR LSE_LIST " | "
.B mpls_label
.IR LABEL " | "
.B mpls_tc
}
.IR OPTIONS " | "
.BR ip_flags
-.IR IP_FLAGS
+.IR IP_FLAGS " }"
+
+.ti -8
+.IR LSE_LIST " := [ " LSE_LIST " ] " LSE
+
+.ti -8
+.IR LSE " := "
+.B lse depth
+.IR DEPTH " { "
+.B label
+.IR LABEL " | "
+.B tc
+.IR TC " | "
+.B bos
+.IR BOS " | "
+.B ttl
+.IR TTL " }"
+
.SH DESCRIPTION
The
.B flower
may be either
.BR ipv4 ", " ipv6
or an unsigned 16bit value in hexadecimal format.
+
+.TP
+.BI mpls " LSE_LIST"
+Match on the MPLS label stack.
+.I LSE_LIST
+is a list of Label Stack Entries, each introduced by the
+.BR lse " keyword."
+This option can't be used together with the standalone
+.BR mpls_label ", " mpls_tc ", " mpls_bos " and " mpls_ttl " options."
+.RS
+.TP
+.BI lse " LSE_OPTIONS"
+Match on an MPLS Label Stack Entry.
+.I LSE_OPTIONS
+is a list of options that describe the properties of the LSE to match.
+.RS
+.TP
+.BI depth " DEPTH"
+The depth of the Label Stack Entry to consider. Depth starts at 1 (the
+outermost Label Stack Entry). The maximum usable depth may be limited by the
+kernel. This option is mandatory.
+.I DEPTH
+is an unsigned 8 bit value in decimal format.
+.TP
+.BI label " LABEL"
+Match on the MPLS Label field at the specified
+.BR depth .
+.I LABEL
+is an unsigned 20 bit value in decimal format.
+.TP
+.BI tc " TC"
+Match on the MPLS Traffic Class field at the specified
+.BR depth .
+.I TC
+is an unsigned 3 bit value in decimal format.
+.TP
+.BI bos " BOS"
+Match on the MPLS Bottom Of Stack field at the specified
+.BR depth .
+.I BOS
+is a 1 bit value in decimal format.
+.TP
+.BI ttl " TTL"
+Match on the MPLS Time To Live field at the specified
+.BR depth .
+.I TTL
+is an unsigned 8 bit value in decimal format.
+.RE
+.RE
+
.TP
.BI mpls_label " LABEL"
Match the label id in the outermost MPLS label stack entry.
(\fBindev\fR, \fBdst_mac\fR and \fBsrc_mac\fR)
have no dependency,
MPLS and layer three matches
-(\fBmpls_label\fR, \fBmpls_tc\fR, \fBmpls_bos\fR, \fBmpls_ttl\fR,
+(\fBmpls\fR, \fBmpls_label\fR, \fBmpls_tc\fR, \fBmpls_bos\fR, \fBmpls_ttl\fR,
\fBip_proto\fR, \fBdst_ip\fR, \fBsrc_ip\fR, \fBarp_tip\fR, \fBarp_sip\fR,
\fBarp_op\fR, \fBarp_tha\fR, \fBarp_sha\fR and \fBip_flags\fR)
depend on the
" ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n"
" ip_tos MASKED-IP_TOS |\n"
" ip_ttl MASKED-IP_TTL |\n"
+ " mpls LSE-LIST |\n"
" mpls_label LABEL |\n"
" mpls_tc TC |\n"
" mpls_bos BOS |\n"
" ct_label MASKED_CT_LABEL |\n"
" ct_mark MASKED_CT_MARK |\n"
" ct_zone MASKED_CT_ZONE }\n"
+ " LSE-LIST := [ LSE-LIST ] LSE\n"
+ " LSE := lse depth DEPTH { label LABEL | tc TC | bos BOS | ttl TTL }\n"
" FILTERID := X:Y:Z\n"
" MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n"
" MASKED_CT_STATE := combination of {+|-} and flags trk,est,new\n"
return 0;
}
+static int flower_parse_mpls_lse(int *argc_p, char ***argv_p,
+ struct nlmsghdr *nlh)
+{
+ struct rtattr *lse_attr;
+ char **argv = *argv_p;
+ int argc = *argc_p;
+ __u8 depth = 0;
+ int ret;
+
+ lse_attr = addattr_nest(nlh, MAX_MSG,
+ TCA_FLOWER_KEY_MPLS_OPTS_LSE | NLA_F_NESTED);
+
+ while (argc > 0) {
+ if (matches(*argv, "depth") == 0) {
+ NEXT_ARG();
+ ret = get_u8(&depth, *argv, 10);
+ if (ret < 0 || depth < 1) {
+ fprintf(stderr, "Illegal \"depth\"\n");
+ return -1;
+ }
+ addattr8(nlh, MAX_MSG,
+ TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH, depth);
+ } else if (matches(*argv, "label") == 0) {
+ __u32 label;
+
+ NEXT_ARG();
+ ret = get_u32(&label, *argv, 10);
+ if (ret < 0 ||
+ label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) {
+ fprintf(stderr, "Illegal \"label\"\n");
+ return -1;
+ }
+ addattr32(nlh, MAX_MSG,
+ TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL, label);
+ } else if (matches(*argv, "tc") == 0) {
+ __u8 tc;
+
+ NEXT_ARG();
+ ret = get_u8(&tc, *argv, 10);
+ if (ret < 0 ||
+ tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) {
+ fprintf(stderr, "Illegal \"tc\"\n");
+ return -1;
+ }
+ addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_TC,
+ tc);
+ } else if (matches(*argv, "bos") == 0) {
+ __u8 bos;
+
+ NEXT_ARG();
+ ret = get_u8(&bos, *argv, 10);
+ if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) {
+ fprintf(stderr, "Illegal \"bos\"\n");
+ return -1;
+ }
+ addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS,
+ bos);
+ } else if (matches(*argv, "ttl") == 0) {
+ __u8 ttl;
+
+ NEXT_ARG();
+ ret = get_u8(&ttl, *argv, 10);
+ if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) {
+ fprintf(stderr, "Illegal \"ttl\"\n");
+ return -1;
+ }
+ addattr8(nlh, MAX_MSG, TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL,
+ ttl);
+ } else {
+ break;
+ }
+ argc--; argv++;
+ }
+
+ if (!depth) {
+ missarg("depth");
+ return -1;
+ }
+
+ addattr_nest_end(nlh, lse_attr);
+
+ *argc_p = argc;
+ *argv_p = argv;
+
+ return 0;
+}
+
+static int flower_parse_mpls(int *argc_p, char ***argv_p, struct nlmsghdr *nlh)
+{
+ struct rtattr *mpls_attr;
+ char **argv = *argv_p;
+ int argc = *argc_p;
+
+ mpls_attr = addattr_nest(nlh, MAX_MSG,
+ TCA_FLOWER_KEY_MPLS_OPTS | NLA_F_NESTED);
+
+ while (argc > 0) {
+ if (matches(*argv, "lse") == 0) {
+ NEXT_ARG();
+ if (flower_parse_mpls_lse(&argc, &argv, nlh) < 0)
+ return -1;
+ } else {
+ break;
+ }
+ }
+
+ addattr_nest_end(nlh, mpls_attr);
+
+ *argc_p = argc;
+ *argv_p = argv;
+
+ return 0;
+}
+
static int flower_parse_opt(struct filter_util *qu, char *handle,
int argc, char **argv, struct nlmsghdr *n)
{
int ret;
struct tcmsg *t = NLMSG_DATA(n);
+ bool mpls_format_old = false;
+ bool mpls_format_new = false;
struct rtattr *tail;
__be16 eth_type = TC_H_MIN(t->tcm_info);
__be16 vlan_ethtype = 0;
&cvlan_ethtype, n);
if (ret < 0)
return -1;
+ } else if (matches(*argv, "mpls") == 0) {
+ NEXT_ARG();
+ if (eth_type != htons(ETH_P_MPLS_UC) &&
+ eth_type != htons(ETH_P_MPLS_MC)) {
+ fprintf(stderr,
+ "Can't set \"mpls\" if ethertype isn't MPLS\n");
+ return -1;
+ }
+ if (mpls_format_old) {
+ fprintf(stderr,
+ "Can't set \"mpls\" if \"mpls_label\", \"mpls_tc\", \"mpls_bos\" or \"mpls_ttl\" is set\n");
+ return -1;
+ }
+ mpls_format_new = true;
+ if (flower_parse_mpls(&argc, &argv, n) < 0)
+ return -1;
+ continue;
} else if (matches(*argv, "mpls_label") == 0) {
__u32 label;
"Can't set \"mpls_label\" if ethertype isn't MPLS\n");
return -1;
}
+ if (mpls_format_new) {
+ fprintf(stderr,
+ "Can't set \"mpls_label\" if \"mpls\" is set\n");
+ return -1;
+ }
+ mpls_format_old = true;
ret = get_u32(&label, *argv, 10);
if (ret < 0 || label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) {
fprintf(stderr, "Illegal \"mpls_label\"\n");
"Can't set \"mpls_tc\" if ethertype isn't MPLS\n");
return -1;
}
+ if (mpls_format_new) {
+ fprintf(stderr,
+ "Can't set \"mpls_tc\" if \"mpls\" is set\n");
+ return -1;
+ }
+ mpls_format_old = true;
ret = get_u8(&tc, *argv, 10);
if (ret < 0 || tc & ~(MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT)) {
fprintf(stderr, "Illegal \"mpls_tc\"\n");
"Can't set \"mpls_bos\" if ethertype isn't MPLS\n");
return -1;
}
+ if (mpls_format_new) {
+ fprintf(stderr,
+ "Can't set \"mpls_bos\" if \"mpls\" is set\n");
+ return -1;
+ }
+ mpls_format_old = true;
ret = get_u8(&bos, *argv, 10);
if (ret < 0 || bos & ~(MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT)) {
fprintf(stderr, "Illegal \"mpls_bos\"\n");
"Can't set \"mpls_ttl\" if ethertype isn't MPLS\n");
return -1;
}
+ if (mpls_format_new) {
+ fprintf(stderr,
+ "Can't set \"mpls_ttl\" if \"mpls\" is set\n");
+ return -1;
+ }
+ mpls_format_old = true;
ret = get_u8(&ttl, *argv, 10);
if (ret < 0 || ttl & ~(MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT)) {
fprintf(stderr, "Illegal \"mpls_ttl\"\n");
print_uint(PRINT_ANY, name, namefrm, rta_getattr_u32(attr));
}
+static void flower_print_mpls_opt_lse(const char *name, struct rtattr *lse)
+{
+ struct rtattr *tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1];
+ struct rtattr *attr;
+
+ if (lse->rta_type != (TCA_FLOWER_KEY_MPLS_OPTS_LSE | NLA_F_NESTED)) {
+ fprintf(stderr, "rta_type 0x%x, expecting 0x%x (0x%x & 0x%x)\n",
+ lse->rta_type,
+ TCA_FLOWER_KEY_MPLS_OPTS_LSE & NLA_F_NESTED,
+ TCA_FLOWER_KEY_MPLS_OPTS_LSE, NLA_F_NESTED);
+ return;
+ }
+
+ parse_rtattr(tb, TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX, RTA_DATA(lse),
+ RTA_PAYLOAD(lse));
+
+ print_nl();
+ open_json_array(PRINT_ANY, name);
+ attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH];
+ if (attr)
+ print_hhu(PRINT_ANY, "depth", " depth %u",
+ rta_getattr_u8(attr));
+ attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL];
+ if (attr)
+ print_uint(PRINT_ANY, "label", " label %u",
+ rta_getattr_u32(attr));
+ attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TC];
+ if (attr)
+ print_hhu(PRINT_ANY, "tc", " tc %u", rta_getattr_u8(attr));
+ attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS];
+ if (attr)
+ print_hhu(PRINT_ANY, "bos", " bos %u", rta_getattr_u8(attr));
+ attr = tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL];
+ if (attr)
+ print_hhu(PRINT_ANY, "ttl", " ttl %u", rta_getattr_u8(attr));
+ close_json_array(PRINT_JSON, NULL);
+}
+
+static void flower_print_mpls_opts(const char *name, struct rtattr *attr)
+{
+ struct rtattr *lse;
+ int rem;
+
+ if (!attr || !(attr->rta_type & NLA_F_NESTED))
+ return;
+
+ print_nl();
+ open_json_array(PRINT_ANY, name);
+ rem = RTA_PAYLOAD(attr);
+ lse = RTA_DATA(attr);
+ while (RTA_OK(lse, rem)) {
+ flower_print_mpls_opt_lse(" lse", lse);
+ lse = RTA_NEXT(lse, rem);
+ };
+ if (rem)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n",
+ rem, lse->rta_len);
+ close_json_array(PRINT_JSON, NULL);
+}
+
static void flower_print_arp_op(const char *name,
struct rtattr *op_attr,
struct rtattr *mask_attr)
flower_print_ip_attr("ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL],
tb[TCA_FLOWER_KEY_IP_TTL_MASK]);
+ flower_print_mpls_opts(" mpls", tb[TCA_FLOWER_KEY_MPLS_OPTS]);
flower_print_u32("mpls_label", tb[TCA_FLOWER_KEY_MPLS_LABEL]);
flower_print_u8("mpls_tc", tb[TCA_FLOWER_KEY_MPLS_TC]);
flower_print_u8("mpls_bos", tb[TCA_FLOWER_KEY_MPLS_BOS]);