On Fri, Jun 19, 2020 at 12:51 PM Guillaume Nault <gna...@redhat.com> wrote: > > Add the new "mpls" keyword that can be used to match MPLS fields in > arbitrary Label Stack Entries. > LSEs are introduced by the "lse" keyword and followed by LSE options: > "depth", "label", "tc", "bos" and "ttl". The depth is manadtory, the > other options are optionals. > > For example, the following filter drops MPLS packets having two labels, > where the first label is 21 and has TTL 64 and the second label is 22: > > $ tc filter add dev ethX ingress proto mpls_uc flower mpls \ > lse depth 1 label 21 ttl 64 \ > lse depth 2 label 22 bos 1 \ > action drop > > Signed-off-by: Guillaume Nault <gna...@redhat.com> > --- > man/man8/tc-flower.8 | 73 +++++++++++++- > tc/f_flower.c | 221 +++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 292 insertions(+), 2 deletions(-) > > diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 > index 4d32ff1b..693be571 100644 > --- a/man/man8/tc-flower.8 > +++ b/man/man8/tc-flower.8 > @@ -46,6 +46,8 @@ flower \- flow based traffic control filter > .IR PRIORITY " | " > .BR cvlan_ethtype " { " ipv4 " | " ipv6 " | " > .IR ETH_TYPE " } | " > +.B mpls > +.IR LSE_LIST " | " > .B mpls_label > .IR LABEL " | " > .B mpls_tc > @@ -96,7 +98,24 @@ flower \- flow based traffic control filter > } > .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 > @@ -182,6 +201,56 @@ Match on QinQ layer three protocol. > 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 limitted by the
limited > +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. > @@ -393,7 +462,7 @@ on the matches of the next lower layer. Precisely, layer > one and two matches > (\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 > diff --git a/tc/f_flower.c b/tc/f_flower.c > index fc136911..00c919fd 100644 > --- a/tc/f_flower.c > +++ b/tc/f_flower.c > @@ -59,6 +59,7 @@ static void explain(void) > " 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" > @@ -89,6 +90,8 @@ static void explain(void) > " 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" > @@ -1199,11 +1202,127 @@ static int flower_parse_enc_opts_erspan(char *str, > struct nlmsghdr *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; > + } > + } This can probably be simplified to: while (argc > 0 && matches(*argv, "lse") == 0) { NEXT_ARG(); if (flower_parse_mpls_lse(&argc, &argv, nlh) < 0) return -1; } > + > + 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; > @@ -1381,6 +1500,23 @@ static int flower_parse_opt(struct filter_util *qu, > char *handle, > &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; > > @@ -1391,6 +1527,12 @@ static int flower_parse_opt(struct filter_util *qu, > char *handle, > "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"); > @@ -1407,6 +1549,12 @@ static int flower_parse_opt(struct filter_util *qu, > char *handle, > "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"); > @@ -1423,6 +1571,12 @@ static int flower_parse_opt(struct filter_util *qu, > char *handle, > "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"); > @@ -1439,6 +1593,12 @@ static int flower_parse_opt(struct filter_util *qu, > char *handle, > "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"); > @@ -2316,6 +2476,66 @@ static void flower_print_u32(const char *name, struct > rtattr *attr) > 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) > @@ -2430,6 +2650,7 @@ static int flower_print_opt(struct filter_util *qu, > FILE *f, > 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]); > -- > 2.21.3 >