.BR skip_sw " | " skip_hw
.R " | { "
.BR dst_mac " | " src_mac " } "
-.IR mac_address " | "
+.IR MASKED_LLADDR " | "
.B vlan_id
.IR VID " | "
.B vlan_prio
.IR ETH_TYPE " } | "
.BR ip_proto " { " tcp " | " udp " | " sctp " | " icmp " | " icmpv6 " | "
.IR IP_PROTO " } | { "
-.BR dst_ip " | " src_ip " } { "
-.IR ipv4_address " | " ipv6_address " } | { "
+.BR dst_ip " | " src_ip " } "
+.IR PREFIX " | { "
.BR dst_port " | " src_port " } "
.IR port_number " } | "
.B enc_key_id
.BR enc_dst_ip " | " enc_src_ip " } { "
.IR ipv4_address " | " ipv6_address " } | "
.B enc_dst_port
-.IR UDP-PORT " | "
+.IR port_number
.SH DESCRIPTION
The
.B flower
.BI skip_hw
Do not process filter by hardware.
.TP
-.BI dst_mac " mac_address"
+.BI dst_mac " MASKED_LLADDR"
.TQ
-.BI src_mac " mac_address"
-Match on source or destination MAC address.
+.BI src_mac " MASKED_LLADDR"
+Match on source or destination MAC address. A mask may be optionally
+provided to limit the bits of the address which are matched. A mask is
+provided by following the address with a slash and then the mask. It may be
+provided in LLADDR format, in which case it is a bitwise mask, or as a
+number of high bits to match. If the mask is missing then a match on all
+bits is assumed.
.TP
.BI vlan_id " VID"
Match on vlan tag id.
.BR tcp ", " udp ", " sctp ", " icmp ", " icmpv6
or an unsigned 8bit value in hexadecimal format.
.TP
-.BI dst_ip " ADDRESS"
+.BI dst_ip " PREFIX"
.TQ
-.BI src_ip " ADDRESS"
+.BI src_ip " PREFIX"
Match on source or destination IP address.
-.I ADDRESS
-must be a valid IPv4 or IPv6 address, depending on
-.BR protocol
-option of tc filter.
+.I PREFIX
+must be a valid IPv4 or IPv6 address, depending on the \fBprotocol\fR
+option to tc filter, optionally followed by a slash and the prefix length.
+If the prefix is missing, \fBtc\fR assumes a full-length host match.
.TP
.BI dst_port " NUMBER"
.TQ
.TP
.BI enc_key_id " NUMBER"
.TQ
-.BI enc_dst_ip " ADDRESS"
+.BI enc_dst_ip " PREFIX"
.TQ
-.BI enc_src_ip " ADDRESS"
+.BI enc_src_ip " PREFIX"
.TQ
.BI enc_dst_port " NUMBER"
Match on IP tunnel metadata. Key id
.I NUMBER
is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
-.I ADDRESS
-must be a valid IPv4 or IPv6 address. Dst port
+.I PREFIX
+must be a valid IPv4 or IPv6 address optionally followed by a slash and the
+prefix length. If the prefix is missing, \fBtc\fR assumes a full-length
+host match. Dst port
.I NUMBER
is a 16 bit UDP dst port.
.SH NOTES
" vlan_id VID |\n"
" vlan_prio PRIORITY |\n"
" vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n"
- " dst_mac MAC-ADDR |\n"
- " src_mac MAC-ADDR |\n"
+ " dst_mac MASKED-LLADDR |\n"
+ " src_mac MASKED-LLADDR |\n"
" ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n"
- " dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
- " src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
+ " dst_ip PREFIX |\n"
+ " src_ip PREFIX |\n"
" dst_port PORT-NUMBER |\n"
" src_port PORT-NUMBER |\n"
" type ICMP-TYPE |\n"
" enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" enc_key_id [ KEY-ID ] |\n"
- " enc_dst_port [ UDP-PORT ] }\n"
+ " matching_flags MATCHING-FLAGS | \n"
+ " enc_dst_port [ port_number ] }\n"
" FILTERID := X:Y:Z\n"
+ " MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n"
" ACTION-SPEC := ... look at individual actions\n"
"\n"
"NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n"
static int flower_parse_eth_addr(char *str, int addr_type, int mask_type,
struct nlmsghdr *n)
{
- int ret;
- char addr[ETH_ALEN];
+ int ret, err = -1;
+ char addr[ETH_ALEN], *slash;
+
+ slash = strchr(str, '/');
+ if (slash)
+ *slash = '\0';
ret = ll_addr_a2n(addr, sizeof(addr), str);
if (ret < 0)
- return -1;
+ goto err;
addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr));
- memset(addr, 0xff, ETH_ALEN);
+
+ if (slash) {
+ unsigned bits;
+
+ if (!get_unsigned(&bits, slash + 1, 10)) {
+ uint64_t mask;
+
+ /* Extra 16 bit shift to push mac address into
+ * high bits of uint64_t
+ */
+ mask = htonll(0xffffffffffffULL << (16 + 48 - bits));
+ memcpy(addr, &mask, ETH_ALEN);
+ } else {
+ ret = ll_addr_a2n(addr, sizeof(addr), slash + 1);
+ if (ret < 0)
+ goto err;
+ }
+ } else {
+ memset(addr, 0xff, ETH_ALEN);
+ }
addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr));
- return 0;
+
+ err = 0;
+err:
+ if (slash)
+ *slash = '/';
+ return err;
}
static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type,
return 0;
}
+static int flower_parse_matching_flags(char *str, int type, int mask_type,
+ struct nlmsghdr *n)
+{
+ __u32 mtf, mtf_mask;
+ char *c;
+
+ c = strchr(str, '/');
+ if (c)
+ *c = '\0';
+
+ if (get_u32(&mtf, str, 0))
+ return -1;
+
+ if (c) {
+ if (get_u32(&mtf_mask, ++c, 0))
+ return -1;
+ } else {
+ mtf_mask = 0xffffffff;
+ }
+
+ addattr32(n, MAX_MSG, type, htonl(mtf));
+ addattr32(n, MAX_MSG, mask_type, htonl(mtf_mask));
+ return 0;
+}
+
static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
__u8 *p_ip_proto, struct nlmsghdr *n)
{
return -1;
}
addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4);
+ } else if (matches(*argv, "matching_flags") == 0) {
+ NEXT_ARG();
+ ret = flower_parse_matching_flags(*argv,
+ TCA_FLOWER_KEY_FLAGS,
+ TCA_FLOWER_KEY_FLAGS_MASK,
+ n);
+ if (ret < 0) {
+ fprintf(stderr, "Illegal \"matching_flags\"\n");
+ return -1;
+ }
} else if (matches(*argv, "skip_hw") == 0) {
flags |= TCA_CLS_FLAGS_SKIP_HW;
} else if (matches(*argv, "skip_sw") == 0) {
*p_ip_proto = ip_proto;
}
+static void flower_print_matching_flags(FILE *f, char *name,
+ struct rtattr *attr,
+ struct rtattr *mask_attr)
+{
+ if (!mask_attr || RTA_PAYLOAD(mask_attr) != 4)
+ return;
+
+ fprintf(f, "\n %s 0x%08x/0x%08x", name, ntohl(rta_getattr_u32(attr)),
+ mask_attr ? ntohl(rta_getattr_u32(mask_attr)) : 0xffffffff);
+}
+
static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type,
struct rtattr *addr4_attr,
struct rtattr *mask4_attr,
flower_print_port(f, "enc_dst_port",
tb[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]);
+ flower_print_matching_flags(f, "matching_flags",
+ tb[TCA_FLOWER_KEY_FLAGS],
+ tb[TCA_FLOWER_KEY_FLAGS_MASK]);
+
if (tb[TCA_FLOWER_FLAGS]) {
__u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]);