tc: flower: support matching on ICMP type and code

Support matching on ICMP type and code.

Example usage:

tc qdisc add dev eth0 ingress

tc filter add dev eth0 protocol ip parent ffff: flower \
	indev eth0 ip_proto icmp type 8 code 0 action drop

tc filter add dev eth0 protocol ipv6 parent ffff: flower \
	indev eth0 ip_proto icmpv6 type 128 code 0 action drop

Signed-off-by: Simon Horman <simon.horman@netronome.com>
This commit is contained in:
Simon Horman 2016-12-07 14:54:03 +01:00 committed by Stephen Hemminger
parent 6910d65661
commit eb3b5696f1
2 changed files with 106 additions and 10 deletions

View File

@ -29,7 +29,7 @@ flower \- flow based traffic control filter
.IR PRIORITY " | " .IR PRIORITY " | "
.BR vlan_eth_type " { " ipv4 " | " ipv6 " | " .BR vlan_eth_type " { " ipv4 " | " ipv6 " | "
.IR ETH_TYPE " } | " .IR ETH_TYPE " } | "
.BR ip_proto " { " tcp " | " udp " | " sctp " | " .BR ip_proto " { " tcp " | " udp " | " sctp " | " icmp " | " icmpv6 " | "
.IR IP_PROTO " } | { " .IR IP_PROTO " } | { "
.BR dst_ip " | " src_ip " } { " .BR dst_ip " | " src_ip " } { "
.IR ipv4_address " | " ipv6_address " } | { " .IR ipv4_address " | " ipv6_address " } | { "
@ -98,7 +98,7 @@ or an unsigned 16bit value in hexadecimal format.
Match on layer four protocol. Match on layer four protocol.
.I IP_PROTO .I IP_PROTO
may be may be
.BR tcp ", " udp ", " sctp .BR tcp ", " udp ", " sctp ", " icmp ", " icmpv6
or an unsigned 8bit value in hexadecimal format. or an unsigned 8bit value in hexadecimal format.
.TP .TP
.BI dst_ip " ADDRESS" .BI dst_ip " ADDRESS"
@ -117,6 +117,13 @@ Match on layer 4 protocol source or destination port number. Only available for
.BR ip_proto " values " udp ", " tcp " and " sctp .BR ip_proto " values " udp ", " tcp " and " sctp
which have to be specified in beforehand. which have to be specified in beforehand.
.TP .TP
.BI type " NUMBER"
.TQ
.BI code " NUMBER"
Match on ICMP type or code. Only available for
.BR ip_proto " values " icmp " and " icmpv6
which have to be specified in beforehand.
.TP
.BI enc_key_id " NUMBER" .BI enc_key_id " NUMBER"
.TQ .TQ
.BI enc_dst_ip " ADDRESS" .BI enc_dst_ip " ADDRESS"
@ -135,13 +142,16 @@ have no dependency, layer three matches
(\fBip_proto\fR, \fBdst_ip\fR and \fBsrc_ip\fR) (\fBip_proto\fR, \fBdst_ip\fR and \fBsrc_ip\fR)
depend on the depend on the
.B protocol .B protocol
option of tc filter option of tc filter, layer four port matches
and finally layer four matches
(\fBdst_port\fR and \fBsrc_port\fR) (\fBdst_port\fR and \fBsrc_port\fR)
depend on depend on
.B ip_proto .B ip_proto
being set to being set to
.BR tcp ", " udp " or " sctp. .BR tcp ", " udp " or " sctp,
and finally ICMP matches (\fBcode\fR and \fBtype\fR) depend on
.B ip_proto
being set to
.BR icmp " or " icmpv6.
.P .P
There can be only used one mask per one prio. If user needs to specify different There can be only used one mask per one prio. If user needs to specify different
mask, he has to use different prio. mask, he has to use different prio.

View File

@ -28,6 +28,11 @@ enum flower_endpoint {
FLOWER_ENDPOINT_DST FLOWER_ENDPOINT_DST
}; };
enum flower_icmp_field {
FLOWER_ICMP_FIELD_TYPE,
FLOWER_ICMP_FIELD_CODE
};
static void explain(void) static void explain(void)
{ {
fprintf(stderr, fprintf(stderr,
@ -42,11 +47,13 @@ static void explain(void)
" vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n" " vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n"
" dst_mac MAC-ADDR |\n" " dst_mac MAC-ADDR |\n"
" src_mac MAC-ADDR |\n" " src_mac MAC-ADDR |\n"
" ip_proto [tcp | udp | sctp | IP-PROTO ] |\n" " ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n"
" dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" dst_port PORT-NUMBER |\n" " dst_port PORT-NUMBER |\n"
" src_port PORT-NUMBER |\n" " src_port PORT-NUMBER |\n"
" type ICMP-TYPE |\n"
" code ICMP-CODE }\n"
" enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " enc_dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n" " enc_src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n"
" enc_key_id [ KEY-ID ] }\n" " enc_key_id [ KEY-ID ] }\n"
@ -98,16 +105,23 @@ static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
int ret; int ret;
__u8 ip_proto; __u8 ip_proto;
if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) { if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6))
fprintf(stderr, "Illegal \"eth_type\" for ip proto\n"); goto err;
return -1;
}
if (matches(str, "tcp") == 0) { if (matches(str, "tcp") == 0) {
ip_proto = IPPROTO_TCP; ip_proto = IPPROTO_TCP;
} else if (matches(str, "udp") == 0) { } else if (matches(str, "udp") == 0) {
ip_proto = IPPROTO_UDP; ip_proto = IPPROTO_UDP;
} else if (matches(str, "sctp") == 0) { } else if (matches(str, "sctp") == 0) {
ip_proto = IPPROTO_SCTP; ip_proto = IPPROTO_SCTP;
} else if (matches(str, "icmp") == 0) {
if (eth_type != htons(ETH_P_IP))
goto err;
ip_proto = IPPROTO_ICMP;
} else if (matches(str, "icmpv6") == 0) {
if (eth_type != htons(ETH_P_IPV6))
goto err;
ip_proto = IPPROTO_ICMPV6;
} else { } else {
ret = get_u8(&ip_proto, str, 16); ret = get_u8(&ip_proto, str, 16);
if (ret) if (ret)
@ -116,6 +130,10 @@ static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
addattr8(n, MAX_MSG, type, ip_proto); addattr8(n, MAX_MSG, type, ip_proto);
*p_ip_proto = ip_proto; *p_ip_proto = ip_proto;
return 0; return 0;
err:
fprintf(stderr, "Illegal \"eth_type\" for ip proto\n");
return -1;
} }
static int flower_parse_ip_addr(char *str, __be16 eth_type, static int flower_parse_ip_addr(char *str, __be16 eth_type,
@ -171,6 +189,41 @@ static int flower_parse_ip_addr(char *str, __be16 eth_type,
return 0; return 0;
} }
static int flower_icmp_attr_type(__be16 eth_type, __u8 ip_proto,
enum flower_icmp_field field)
{
if (eth_type == htons(ETH_P_IP) && ip_proto == IPPROTO_ICMP)
return field == FLOWER_ICMP_FIELD_CODE ?
TCA_FLOWER_KEY_ICMPV4_CODE :
TCA_FLOWER_KEY_ICMPV4_TYPE;
else if (eth_type == htons(ETH_P_IPV6) && ip_proto == IPPROTO_ICMPV6)
return field == FLOWER_ICMP_FIELD_CODE ?
TCA_FLOWER_KEY_ICMPV6_CODE :
TCA_FLOWER_KEY_ICMPV6_TYPE;
return -1;
}
static int flower_parse_icmp(char *str, __u16 eth_type, __u8 ip_proto,
enum flower_icmp_field field, struct nlmsghdr *n)
{
int ret;
int type;
uint8_t value;
type = flower_icmp_attr_type(eth_type, ip_proto, field);
if (type < 0)
return -1;
ret = get_u8(&value, str, 10);
if (ret)
return -1;
addattr8(n, MAX_MSG, type, value);
return 0;
}
static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint) static int flower_port_attr_type(__u8 ip_proto, enum flower_endpoint endpoint)
{ {
if (ip_proto == IPPROTO_TCP) if (ip_proto == IPPROTO_TCP)
@ -381,6 +434,22 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
fprintf(stderr, "Illegal \"src_port\"\n"); fprintf(stderr, "Illegal \"src_port\"\n");
return -1; return -1;
} }
} else if (matches(*argv, "type") == 0) {
NEXT_ARG();
ret = flower_parse_icmp(*argv, eth_type, ip_proto,
FLOWER_ICMP_FIELD_TYPE, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"icmp type\"\n");
return -1;
}
} else if (matches(*argv, "code") == 0) {
NEXT_ARG();
ret = flower_parse_icmp(*argv, eth_type, ip_proto,
FLOWER_ICMP_FIELD_CODE, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"icmp code\"\n");
return -1;
}
} else if (matches(*argv, "enc_dst_ip") == 0) { } else if (matches(*argv, "enc_dst_ip") == 0) {
NEXT_ARG(); NEXT_ARG();
ret = flower_parse_ip_addr(*argv, 0, ret = flower_parse_ip_addr(*argv, 0,
@ -526,6 +595,10 @@ static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto,
fprintf(f, "udp"); fprintf(f, "udp");
else if (ip_proto == IPPROTO_SCTP) else if (ip_proto == IPPROTO_SCTP)
fprintf(f, "sctp"); fprintf(f, "sctp");
else if (ip_proto == IPPROTO_ICMP)
fprintf(f, "icmp");
else if (ip_proto == IPPROTO_ICMPV6)
fprintf(f, "icmpv6");
else else
fprintf(f, "%02x", ip_proto); fprintf(f, "%02x", ip_proto);
*p_ip_proto = ip_proto; *p_ip_proto = ip_proto;
@ -581,6 +654,12 @@ static void flower_print_key_id(FILE *f, const char *name,
fprintf(f, "\n %s %d", name, rta_getattr_be32(attr)); fprintf(f, "\n %s %d", name, rta_getattr_be32(attr));
} }
static void flower_print_icmp(FILE *f, char *name, struct rtattr *attr)
{
if (attr)
fprintf(f, "\n %s %d", name, rta_getattr_u8(attr));
}
static int flower_print_opt(struct filter_util *qu, FILE *f, static int flower_print_opt(struct filter_util *qu, FILE *f,
struct rtattr *opt, __u32 handle) struct rtattr *opt, __u32 handle)
{ {
@ -649,6 +728,13 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
if (nl_type >= 0) if (nl_type >= 0)
flower_print_port(f, "src_port", tb[nl_type]); flower_print_port(f, "src_port", tb[nl_type]);
nl_type = flower_icmp_attr_type(eth_type, ip_proto, false);
if (nl_type >= 0)
flower_print_icmp(f, "icmp_type", tb[nl_type]);
nl_type = flower_icmp_attr_type(eth_type, ip_proto, true);
if (nl_type >= 0)
flower_print_icmp(f, "icmp_code", tb[nl_type]);
flower_print_ip_addr(f, "enc_dst_ip", flower_print_ip_addr(f, "enc_dst_ip",
tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ? tb[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] ?
htons(ETH_P_IP) : htons(ETH_P_IPV6), htons(ETH_P_IP) : htons(ETH_P_IPV6),