mirror_iproute2/tc/f_flower.c
Phil Sutter d17b136f7d Use C99 style initializers everywhere
This big patch was compiled by vimgrepping for memset calls and changing
to C99 initializer if applicable. One notable exception is the
initialization of union bpf_attr in tc/tc_bpf.c: changing it would break
for older gcc versions (at least <=3.4.6).

Calls to memset for struct rtattr pointer fields for parse_rtattr*()
were just dropped since they are not needed.

The changes here allowed the compiler to discover some unused variables,
so get rid of them, too.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Acked-by: David Ahern <dsa@cumulusnetworks.com>
2016-07-20 12:05:24 -07:00

529 lines
13 KiB
C

/*
* f_flower.c Flower Classifier
*
* This program is free software; you can distribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Jiri Pirko <jiri@resnulli.us>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include "utils.h"
#include "tc_util.h"
#include "rt_names.h"
static void explain(void)
{
fprintf(stderr, "Usage: ... flower [ MATCH-LIST ]\n");
fprintf(stderr, " [ skip_sw | skip_hw ]\n");
fprintf(stderr, " [ action ACTION-SPEC ] [ classid CLASSID ]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n");
fprintf(stderr, " MATCH := { indev DEV-NAME |\n");
fprintf(stderr, " dst_mac MAC-ADDR |\n");
fprintf(stderr, " src_mac MAC-ADDR |\n");
fprintf(stderr, " [ipv4 | ipv6 ] |\n");
fprintf(stderr, " ip_proto [tcp | udp | IP-PROTO ] |\n");
fprintf(stderr, " dst_ip [ IPV4-ADDR | IPV6-ADDR ] |\n");
fprintf(stderr, " src_ip [ IPV4-ADDR | IPV6-ADDR ] |\n");
fprintf(stderr, " dst_port PORT-NUMBER |\n");
fprintf(stderr, " src_port PORT-NUMBER }\n");
fprintf(stderr, " FILTERID := X:Y:Z\n");
fprintf(stderr, " ACTION-SPEC := ... look at individual actions\n");
fprintf(stderr, "\n");
fprintf(stderr, "NOTE: CLASSID, ETH-TYPE, IP-PROTO are parsed as hexadecimal input.\n");
fprintf(stderr, "NOTE: There can be only used one mask per one prio. If user needs\n");
fprintf(stderr, " to specify different mask, he has to use different prio.\n");
}
static int flower_parse_eth_addr(char *str, int addr_type, int mask_type,
struct nlmsghdr *n)
{
int ret;
char addr[ETH_ALEN];
ret = ll_addr_a2n(addr, sizeof(addr), str);
if (ret < 0)
return -1;
addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr));
memset(addr, 0xff, ETH_ALEN);
addattr_l(n, MAX_MSG, mask_type, addr, sizeof(addr));
return 0;
}
static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
__u8 *p_ip_proto, struct nlmsghdr *n)
{
int ret;
__u8 ip_proto;
if (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) {
fprintf(stderr, "Illegal \"eth_type\" for ip proto\n");
return -1;
}
if (matches(str, "tcp") == 0) {
ip_proto = IPPROTO_TCP;
} else if (matches(str, "udp") == 0) {
ip_proto = IPPROTO_UDP;
} else {
ret = get_u8(&ip_proto, str, 16);
if (ret)
return -1;
}
addattr8(n, MAX_MSG, type, ip_proto);
*p_ip_proto = ip_proto;
return 0;
}
static int flower_parse_ip_addr(char *str, __be16 eth_type,
int addr4_type, int mask4_type,
int addr6_type, int mask6_type,
struct nlmsghdr *n)
{
int ret;
inet_prefix addr;
int family;
int bits;
int i;
if (eth_type == htons(ETH_P_IP)) {
family = AF_INET;
} else if (eth_type == htons(ETH_P_IPV6)) {
family = AF_INET6;
} else {
fprintf(stderr, "Illegal \"eth_type\" for ip address\n");
return -1;
}
ret = get_prefix(&addr, str, family);
if (ret)
return -1;
if (addr.family != family)
return -1;
addattr_l(n, MAX_MSG, addr.family == AF_INET ? addr4_type : addr6_type,
addr.data, addr.bytelen);
memset(addr.data, 0xff, addr.bytelen);
bits = addr.bitlen;
for (i = 0; i < addr.bytelen / 4; i++) {
if (!bits) {
addr.data[i] = 0;
} else if (bits / 32 >= 1) {
bits -= 32;
} else {
addr.data[i] <<= 32 - bits;
addr.data[i] = htonl(addr.data[i]);
bits = 0;
}
}
addattr_l(n, MAX_MSG, addr.family == AF_INET ? mask4_type : mask6_type,
addr.data, addr.bytelen);
return 0;
}
static int flower_parse_port(char *str, __u8 ip_port,
int tcp_type, int udp_type, struct nlmsghdr *n)
{
int ret;
int type;
__be16 port;
if (ip_port == IPPROTO_TCP) {
type = tcp_type;
} else if (ip_port == IPPROTO_UDP) {
type = udp_type;
} else {
fprintf(stderr, "Illegal \"ip_proto\" for port\n");
return -1;
}
ret = get_be16(&port, str, 10);
if (ret)
return -1;
addattr16(n, MAX_MSG, type, port);
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);
struct rtattr *tail;
__be16 eth_type = TC_H_MIN(t->tcm_info);
__u8 ip_proto = 0xff;
__u32 flags = 0;
if (handle) {
ret = get_u32(&t->tcm_handle, handle, 0);
if (ret) {
fprintf(stderr, "Illegal \"handle\"\n");
return -1;
}
}
tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len));
addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
if (argc == 0) {
/*at minimal we will match all ethertype packets */
goto parse_done;
}
while (argc > 0) {
if (matches(*argv, "classid") == 0 ||
matches(*argv, "flowid") == 0) {
unsigned int handle;
NEXT_ARG();
ret = get_tc_classid(&handle, *argv);
if (ret) {
fprintf(stderr, "Illegal \"classid\"\n");
return -1;
}
addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4);
} else if (matches(*argv, "skip_hw") == 0) {
flags |= TCA_CLS_FLAGS_SKIP_HW;
} else if (matches(*argv, "skip_sw") == 0) {
flags |= TCA_CLS_FLAGS_SKIP_SW;
} else if (matches(*argv, "indev") == 0) {
char ifname[IFNAMSIZ] = {};
NEXT_ARG();
strncpy(ifname, *argv, sizeof(ifname) - 1);
addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, ifname);
} else if (matches(*argv, "dst_mac") == 0) {
NEXT_ARG();
ret = flower_parse_eth_addr(*argv,
TCA_FLOWER_KEY_ETH_DST,
TCA_FLOWER_KEY_ETH_DST_MASK,
n);
if (ret < 0) {
fprintf(stderr, "Illegal \"dst_mac\"\n");
return -1;
}
} else if (matches(*argv, "src_mac") == 0) {
NEXT_ARG();
ret = flower_parse_eth_addr(*argv,
TCA_FLOWER_KEY_ETH_SRC,
TCA_FLOWER_KEY_ETH_SRC_MASK,
n);
if (ret < 0) {
fprintf(stderr, "Illegal \"src_mac\"\n");
return -1;
}
} else if (matches(*argv, "ip_proto") == 0) {
NEXT_ARG();
ret = flower_parse_ip_proto(*argv, eth_type,
TCA_FLOWER_KEY_IP_PROTO,
&ip_proto, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"ip_proto\"\n");
return -1;
}
} else if (matches(*argv, "dst_ip") == 0) {
NEXT_ARG();
ret = flower_parse_ip_addr(*argv, eth_type,
TCA_FLOWER_KEY_IPV4_DST,
TCA_FLOWER_KEY_IPV4_DST_MASK,
TCA_FLOWER_KEY_IPV6_DST,
TCA_FLOWER_KEY_IPV6_DST_MASK,
n);
if (ret < 0) {
fprintf(stderr, "Illegal \"dst_ip\"\n");
return -1;
}
} else if (matches(*argv, "src_ip") == 0) {
NEXT_ARG();
ret = flower_parse_ip_addr(*argv, eth_type,
TCA_FLOWER_KEY_IPV4_SRC,
TCA_FLOWER_KEY_IPV4_SRC_MASK,
TCA_FLOWER_KEY_IPV6_SRC,
TCA_FLOWER_KEY_IPV6_SRC_MASK,
n);
if (ret < 0) {
fprintf(stderr, "Illegal \"src_ip\"\n");
return -1;
}
} else if (matches(*argv, "dst_port") == 0) {
NEXT_ARG();
ret = flower_parse_port(*argv, ip_proto,
TCA_FLOWER_KEY_TCP_DST,
TCA_FLOWER_KEY_UDP_DST, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"dst_port\"\n");
return -1;
}
} else if (matches(*argv, "src_port") == 0) {
NEXT_ARG();
ret = flower_parse_port(*argv, ip_proto,
TCA_FLOWER_KEY_TCP_SRC,
TCA_FLOWER_KEY_UDP_SRC, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"src_port\"\n");
return -1;
}
} else if (matches(*argv, "action") == 0) {
NEXT_ARG();
ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
if (ret) {
fprintf(stderr, "Illegal \"action\"\n");
return -1;
}
continue;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
} else {
fprintf(stderr, "What is \"%s\"?\n", *argv);
explain();
return -1;
}
argc--; argv++;
}
parse_done:
addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags);
ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type);
if (ret) {
fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n",
ntohs(eth_type));
return -1;
}
tail->rta_len = (((void *)n)+n->nlmsg_len) - (void *)tail;
return 0;
}
static int __mask_bits(char *addr, size_t len)
{
int bits = 0;
bool hole = false;
int i;
int j;
for (i = 0; i < len; i++, addr++) {
for (j = 7; j >= 0; j--) {
if (((*addr) >> j) & 0x1) {
if (hole)
return -1;
bits++;
} else if (bits) {
hole = true;
} else{
return -1;
}
}
}
return bits;
}
static void flower_print_eth_addr(FILE *f, char *name,
struct rtattr *addr_attr,
struct rtattr *mask_attr)
{
SPRINT_BUF(b1);
int bits;
if (!addr_attr || RTA_PAYLOAD(addr_attr) != ETH_ALEN)
return;
fprintf(f, "\n %s %s", name, ll_addr_n2a(RTA_DATA(addr_attr), ETH_ALEN,
0, b1, sizeof(b1)));
if (!mask_attr || RTA_PAYLOAD(mask_attr) != ETH_ALEN)
return;
bits = __mask_bits(RTA_DATA(mask_attr), ETH_ALEN);
if (bits < 0)
fprintf(f, "/%s", ll_addr_n2a(RTA_DATA(mask_attr), ETH_ALEN,
0, b1, sizeof(b1)));
else if (bits < ETH_ALEN * 8)
fprintf(f, "/%d", bits);
}
static void flower_print_eth_type(FILE *f, __be16 *p_eth_type,
struct rtattr *eth_type_attr)
{
__be16 eth_type;
if (!eth_type_attr)
return;
eth_type = rta_getattr_u16(eth_type_attr);
fprintf(f, "\n eth_type ");
if (eth_type == htons(ETH_P_IP))
fprintf(f, "ipv4");
else if (eth_type == htons(ETH_P_IPV6))
fprintf(f, "ipv6");
else
fprintf(f, "%04x", ntohs(eth_type));
*p_eth_type = eth_type;
}
static void flower_print_ip_proto(FILE *f, __u8 *p_ip_proto,
struct rtattr *ip_proto_attr)
{
__u8 ip_proto;
if (!ip_proto_attr)
return;
ip_proto = rta_getattr_u8(ip_proto_attr);
fprintf(f, "\n ip_proto ");
if (ip_proto == IPPROTO_TCP)
fprintf(f, "tcp");
else if (ip_proto == IPPROTO_UDP)
fprintf(f, "udp");
else
fprintf(f, "%02x", ip_proto);
*p_ip_proto = ip_proto;
}
static void flower_print_ip_addr(FILE *f, char *name, __be16 eth_type,
struct rtattr *addr4_attr,
struct rtattr *mask4_attr,
struct rtattr *addr6_attr,
struct rtattr *mask6_attr)
{
struct rtattr *addr_attr;
struct rtattr *mask_attr;
int family;
size_t len;
int bits;
if (eth_type == htons(ETH_P_IP)) {
family = AF_INET;
addr_attr = addr4_attr;
mask_attr = mask4_attr;
len = 4;
} else if (eth_type == htons(ETH_P_IPV6)) {
family = AF_INET6;
addr_attr = addr6_attr;
mask_attr = mask6_attr;
len = 16;
} else {
return;
}
if (!addr_attr || RTA_PAYLOAD(addr_attr) != len)
return;
fprintf(f, "\n %s %s", name, rt_addr_n2a_rta(family, addr_attr));
if (!mask_attr || RTA_PAYLOAD(mask_attr) != len)
return;
bits = __mask_bits(RTA_DATA(mask_attr), len);
if (bits < 0)
fprintf(f, "/%s", rt_addr_n2a_rta(family, mask_attr));
else if (bits < len * 8)
fprintf(f, "/%d", bits);
}
static void flower_print_port(FILE *f, char *name, __u8 ip_proto,
struct rtattr *tcp_attr,
struct rtattr *udp_attr)
{
struct rtattr *attr;
if (ip_proto == IPPROTO_TCP)
attr = tcp_attr;
else if (ip_proto == IPPROTO_UDP)
attr = udp_attr;
else
return;
if (!attr)
return;
fprintf(f, "\n %s %d", name, ntohs(rta_getattr_u16(attr)));
}
static int flower_print_opt(struct filter_util *qu, FILE *f,
struct rtattr *opt, __u32 handle)
{
struct rtattr *tb[TCA_FLOWER_MAX + 1];
__be16 eth_type = 0;
__u8 ip_proto = 0xff;
if (!opt)
return 0;
parse_rtattr_nested(tb, TCA_FLOWER_MAX, opt);
if (handle)
fprintf(f, "handle 0x%x ", handle);
if (tb[TCA_FLOWER_CLASSID]) {
SPRINT_BUF(b1);
fprintf(f, "classid %s ",
sprint_tc_classid(rta_getattr_u32(tb[TCA_FLOWER_CLASSID]),
b1));
}
if (tb[TCA_FLOWER_INDEV]) {
struct rtattr *attr = tb[TCA_FLOWER_INDEV];
fprintf(f, "\n indev %s", rta_getattr_str(attr));
}
flower_print_eth_addr(f, "dst_mac", tb[TCA_FLOWER_KEY_ETH_DST],
tb[TCA_FLOWER_KEY_ETH_DST_MASK]);
flower_print_eth_addr(f, "src_mac", tb[TCA_FLOWER_KEY_ETH_SRC],
tb[TCA_FLOWER_KEY_ETH_SRC_MASK]);
flower_print_eth_type(f, &eth_type, tb[TCA_FLOWER_KEY_ETH_TYPE]);
flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]);
flower_print_ip_addr(f, "dst_ip", eth_type,
tb[TCA_FLOWER_KEY_IPV4_DST],
tb[TCA_FLOWER_KEY_IPV4_DST_MASK],
tb[TCA_FLOWER_KEY_IPV6_DST],
tb[TCA_FLOWER_KEY_IPV6_DST_MASK]);
flower_print_ip_addr(f, "src_ip", eth_type,
tb[TCA_FLOWER_KEY_IPV4_SRC],
tb[TCA_FLOWER_KEY_IPV4_SRC_MASK],
tb[TCA_FLOWER_KEY_IPV6_SRC],
tb[TCA_FLOWER_KEY_IPV6_SRC_MASK]);
flower_print_port(f, "dst_port", ip_proto,
tb[TCA_FLOWER_KEY_TCP_DST],
tb[TCA_FLOWER_KEY_UDP_DST]);
flower_print_port(f, "src_port", ip_proto,
tb[TCA_FLOWER_KEY_TCP_SRC],
tb[TCA_FLOWER_KEY_UDP_SRC]);
if (tb[TCA_FLOWER_FLAGS]) {
__u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]);
if (flags & TCA_CLS_FLAGS_SKIP_HW)
fprintf(f, "\n skip_hw");
if (flags & TCA_CLS_FLAGS_SKIP_SW)
fprintf(f, "\n skip_sw");
}
if (tb[TCA_FLOWER_ACT]) {
tc_print_action(f, tb[TCA_FLOWER_ACT]);
}
return 0;
}
struct filter_util flower_filter_util = {
.id = "flower",
.parse_fopt = flower_parse_opt,
.print_fopt = flower_print_opt,
};