Commit 3a641f53 authored by Stephen Hemminger's avatar Stephen Hemminger

Merge branch 'net-next'

parents 46d364fe 95ce04bc
......@@ -16,4 +16,5 @@ extern int preferred_family;
extern int show_stats;
extern int show_details;
extern int timestamp;
extern int compress_vlans;
extern struct rtnl_handle rth;
......@@ -21,6 +21,7 @@ int resolve_hosts;
int oneline = 0;
int show_stats;
int show_details;
int compress_vlans;
int timestamp;
char * _SL_ = NULL;
......@@ -32,7 +33,8 @@ static void usage(void)
"Usage: bridge [ OPTIONS ] OBJECT { COMMAND | help }\n"
"where OBJECT := { link | fdb | mdb | vlan | monitor }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n"
" -o[neline] | -t[imestamp] | -n[etns] name }\n");
" -o[neline] | -t[imestamp] | -n[etns] name |\n"
" -c[ompressvlans] }\n");
exit(-1);
}
......@@ -117,6 +119,8 @@ main(int argc, char **argv)
NEXT_ARG();
if (netns_switch(argv[1]))
exit(-1);
} else if (matches(opt, "-compressvlans") == 0) {
++compress_vlans;
} else {
fprintf(stderr, "Option \"%s\" is unknown, try \"bridge help\".\n", opt);
exit(-1);
......
......@@ -32,6 +32,7 @@ static int vlan_modify(int cmd, int argc, char **argv)
} req;
char *d = NULL;
short vid = -1;
short vid_end = -1;
struct rtattr *afspec;
struct bridge_vlan_info vinfo;
unsigned short flags = 0;
......@@ -49,8 +50,18 @@ static int vlan_modify(int cmd, int argc, char **argv)
NEXT_ARG();
d = *argv;
} else if (strcmp(*argv, "vid") == 0) {
char *p;
NEXT_ARG();
vid = atoi(*argv);
p = strchr(*argv, '-');
if (p) {
*p = '\0';
p++;
vid = atoi(*argv);
vid_end = atoi(p);
vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
} else {
vid = atoi(*argv);
}
} else if (strcmp(*argv, "self") == 0) {
flags |= BRIDGE_FLAGS_SELF;
} else if (strcmp(*argv, "master") == 0) {
......@@ -83,15 +94,40 @@ static int vlan_modify(int cmd, int argc, char **argv)
return -1;
}
vinfo.vid = vid;
if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
vid, vid_end);
return -1;
}
if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
fprintf(stderr,
"pvid cannot be configured for a vlan range\n");
return -1;
}
}
afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
if (flags)
addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
sizeof(vinfo));
vinfo.vid = vid;
if (vid_end != -1) {
/* send vlan range start */
addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
sizeof(vinfo));
vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
/* Now send the vlan range end */
vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
vinfo.vid = vid_end;
addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
sizeof(vinfo));
} else {
addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
sizeof(vinfo));
}
addattr_nest_end(&req.n, afspec);
......@@ -146,7 +182,12 @@ static int print_vlan(const struct sockaddr_nl *who,
continue;
vinfo = RTA_DATA(i);
fprintf(fp, "\t %hu", vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)
fprintf(fp, "-%hu", vinfo->vid);
else
fprintf(fp, "\t %hu", vinfo->vid);
if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
continue;
if (vinfo->flags & BRIDGE_VLAN_INFO_PVID)
fprintf(fp, " PVID");
if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED)
......@@ -182,7 +223,9 @@ static int vlan_show(int argc, char **argv)
}
if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
RTEXT_FILTER_BRVLAN) < 0) {
(compress_vlans ?
RTEXT_FILTER_BRVLAN_COMPRESSED :
RTEXT_FILTER_BRVLAN)) < 0) {
perror("Cannont send dump request");
exit(1);
}
......
......@@ -125,6 +125,8 @@ enum {
#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */
#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */
#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */
#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */
#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
struct bridge_vlan_info {
__u16 flags;
......
......@@ -146,6 +146,7 @@ enum {
IFLA_PHYS_PORT_ID,
IFLA_CARRIER_CHANGES,
IFLA_PHYS_SWITCH_ID,
IFLA_LINK_NETNSID,
__IFLA_MAX
};
......@@ -368,6 +369,9 @@ enum {
IFLA_VXLAN_UDP_CSUM,
IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
IFLA_VXLAN_REMCSUM_TX,
IFLA_VXLAN_REMCSUM_RX,
IFLA_VXLAN_GBP,
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
......
......@@ -176,5 +176,6 @@ enum l2tp_seqmode {
*/
#define L2TP_GENL_NAME "l2tp"
#define L2TP_GENL_VERSION 0x1
#define L2TP_GENL_MCGROUP "l2tp"
#endif /* _LINUX_L2TP_H_ */
......@@ -70,6 +70,8 @@
#define __UAPI_DEF_IPV6_MREQ 0
#define __UAPI_DEF_IPPROTO_V6 0
#define __UAPI_DEF_IPV6_OPTIONS 0
#define __UAPI_DEF_IN6_PKTINFO 0
#define __UAPI_DEF_IP6_MTUINFO 0
#else
......@@ -84,6 +86,8 @@
#define __UAPI_DEF_IPV6_MREQ 1
#define __UAPI_DEF_IPPROTO_V6 1
#define __UAPI_DEF_IPV6_OPTIONS 1
#define __UAPI_DEF_IN6_PKTINFO 1
#define __UAPI_DEF_IP6_MTUINFO 1
#endif /* _NETINET_IN_H */
......@@ -106,6 +110,8 @@
#define __UAPI_DEF_IPV6_MREQ 1
#define __UAPI_DEF_IPPROTO_V6 1
#define __UAPI_DEF_IPV6_OPTIONS 1
#define __UAPI_DEF_IN6_PKTINFO 1
#define __UAPI_DEF_IP6_MTUINFO 1
/* Definitions for xattr.h */
#define __UAPI_DEF_XATTR 1
......
......@@ -25,6 +25,7 @@ enum {
NDA_VNI,
NDA_IFINDEX,
NDA_MASTER,
NDA_LINK_NETNSID,
__NDA_MAX
};
......
......@@ -132,6 +132,11 @@ enum {
RTM_GETMDB = 86,
#define RTM_GETMDB RTM_GETMDB
RTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSID
RTM_GETNSID = 90,
#define RTM_GETNSID RTM_GETNSID
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
......@@ -389,6 +394,8 @@ enum {
#define RTAX_INITRWND RTAX_INITRWND
RTAX_QUICKACK,
#define RTAX_QUICKACK RTAX_QUICKACK
RTAX_CC_ALGO,
#define RTAX_CC_ALGO RTAX_CC_ALGO
__RTAX_MAX
};
......@@ -632,6 +639,7 @@ struct tcamsg {
/* New extended info filters for IFLA_EXT_MASK */
#define RTEXT_FILTER_VF (1 << 0)
#define RTEXT_FILTER_BRVLAN (1 << 1)
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
/* End of information exported to user level */
......
/*
* Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
*
* This program is free software; you can redistribute 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.
*/
#ifndef __LINUX_TC_BPF_H
#define __LINUX_TC_BPF_H
#include <linux/pkt_cls.h>
#define TCA_ACT_BPF 13
struct tc_act_bpf {
tc_gen;
};
enum {
TCA_ACT_BPF_UNSPEC,
TCA_ACT_BPF_TM,
TCA_ACT_BPF_PARMS,
TCA_ACT_BPF_OPS_LEN,
TCA_ACT_BPF_OPS,
__TCA_ACT_BPF_MAX,
};
#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
#endif
......@@ -30,6 +30,7 @@ static void print_explain(FILE *f)
fprintf(f, " [ [no]l2miss ] [ [no]l3miss ]\n");
fprintf(f, " [ ageing SECONDS ] [ maxaddress NUMBER ]\n");
fprintf(f, " [ [no]udpcsum ] [ [no]udp6zerocsumtx ] [ [no]udp6zerocsumrx ]\n");
fprintf(f, " [ gbp ]\n");
fprintf(f, "\n");
fprintf(f, "Where: VNI := 0-16777215\n");
fprintf(f, " ADDR := { IP_ADDRESS | any }\n");
......@@ -68,6 +69,7 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
__u8 udpcsum = 0;
__u8 udp6zerocsumtx = 0;
__u8 udp6zerocsumrx = 0;
__u8 gbp = 0;
int dst_port_set = 0;
struct ifla_vxlan_port_range range = { 0, 0 };
......@@ -197,6 +199,8 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
udp6zerocsumrx = 1;
} else if (!matches(*argv, "noudp6zerocsumrx")) {
udp6zerocsumrx = 0;
} else if (!matches(*argv, "gbp")) {
gbp = 1;
} else if (matches(*argv, "help") == 0) {
explain();
return -1;
......@@ -268,6 +272,10 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
if (dstport)
addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport));
if (gbp)
addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0);
return 0;
}
......@@ -398,6 +406,9 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
fputs("udp6zerocsumrx ", f);
if (tb[IFLA_VXLAN_GBP])
fputs("gbp ", f);
}
static void vxlan_print_help(struct link_util *lu, int argc, char **argv,
......
......@@ -53,6 +53,7 @@ static const char *mx_names[RTAX_MAX+1] = {
[RTAX_RTO_MIN] = "rto_min",
[RTAX_INITRWND] = "initrwnd",
[RTAX_QUICKACK] = "quickack",
[RTAX_CC_ALGO] = "congctl",
};
static void usage(void) __attribute__((noreturn));
......@@ -80,8 +81,7 @@ static void usage(void)
fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n");
fprintf(stderr, " [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n");
fprintf(stderr, " [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n");
fprintf(stderr, " [ features FEATURES ]\n");
fprintf(stderr, " [ quickack BOOL ]\n");
fprintf(stderr, " [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n");
fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n");
fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n");
fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n");
......@@ -536,7 +536,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]);
for (i=2; i<= RTAX_MAX; i++) {
unsigned val;
__u32 val;
if (mxrta[i] == NULL)
continue;
......@@ -545,10 +545,12 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
fprintf(fp, " %s", mx_names[i]);
else
fprintf(fp, " metric %d", i);
if (mxlock & (1<<i))
fprintf(fp, " lock");
if (i != RTAX_CC_ALGO)
val = rta_getattr_u32(mxrta[i]);
val = *(unsigned*)RTA_DATA(mxrta[i]);
switch (i) {
case RTAX_FEATURES:
print_rtax_features(fp, val);
......@@ -573,6 +575,10 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
fprintf(fp, " %gs", val/1e3);
else
fprintf(fp, " %ums", val);
break;
case RTAX_CC_ALGO:
fprintf(fp, " %s", rta_getattr_str(mxrta[i]));
break;
}
}
}
......@@ -925,6 +931,14 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
if (quickack != 1 && quickack != 0)
invarg("\"quickack\" value should be 0 or 1\n", *argv);
rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
} else if (matches(*argv, "congctl") == 0) {
NEXT_ARG();
if (strcmp(*argv, "lock") == 0) {
mxlock |= 1 << RTAX_CC_ALGO;
NEXT_ARG();
}
rta_addattr_l(mxrta, sizeof(mxbuf), RTAX_CC_ALGO, *argv,
strlen(*argv));
} else if (matches(*argv, "rttvar") == 0) {
unsigned win;
NEXT_ARG();
......
......@@ -276,6 +276,8 @@ the following additional arguments are supported:
.BI ageing " SECONDS "
.R " ] [ "
.BI maxaddress " NUMBER "
.R " ] [ "
.B gbp
.R " ]"
.in +8
......@@ -348,6 +350,49 @@ are entered into the VXLAN device forwarding database.
.BI maxaddress " NUMBER"
- specifies the maximum number of FDB entries.
.sp
.B gbp
- enables the Group Policy extension (VXLAN-GBP).
.in +4
Allows to transport group policy context across VXLAN network peers.
If enabled, includes the mark of a packet in the VXLAN header for outgoing
packets and fills the packet mark based on the information found in the
VXLAN header for incomming packets.
Format of upper 16 bits of packet mark (flags);
.in +2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.br
|-|-|-|-|-|-|-|-|-|D|-|-|A|-|-|-|
.br
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.B D :=
Don't Learn bit. When set, this bit indicates that the egress
VTEP MUST NOT learn the source address of the encapsulated frame.
.B A :=
Indicates that the group policy has already been applied to
this packet. Policies MUST NOT be applied by devices when the A bit is set.
.in -2
Format of lower 16 bits of packet mark (policy ID):
.in +2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.br
| Group Policy ID |
.br
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.in -2
Example:
iptables -A OUTPUT [...] -j MARK --set-mark 0x800FF
.in -4
.in -8
.TP
......
......@@ -116,7 +116,9 @@ replace " } "
.B features
.IR FEATURES " ] [ "
.B quickack
.IR BOOL " ]"
.IR BOOL " ] [ "
.B congctl
.IR NAME " ]"
.ti -8
.IR TYPE " := [ "
......@@ -432,6 +434,21 @@ sysctl is set to 0.
.BI quickack " BOOL " "(3.11+ only)"
Enable or disable quick ack for connections to this destination.
.TP
.BI congctl " NAME " "(3.20+ only)"
.TP
.BI "congctl lock" " NAME " "(3.20+ only)"
Sets a specific TCP congestion control algorithm only for a given destination.
If not specified, Linux keeps the current global default TCP congestion control
algorithm, or the one set from the application. If the modifier
.B lock
is not used, an application may nevertheless overwrite the suggested congestion
control algorithm for that destination. If the modifier
.B lock
is used, then an application is not allowed to overwrite the specified congestion
control algorithm for that destination, thus it will be enforced/guaranteed to
use the proposed algorithm.
.TP
.BI advmss " NUMBER " "(2.3.15+ only)"
the MSS ('Maximal Segment Size') to advertise to these
......
......@@ -84,6 +84,9 @@ context of the creating process, however the context shown will reflect
any policy role, type and/or range transition rules applied,
and is therefore a useful reference.
.TP
.B \-N NSNAME, \-\-net=NSNAME
Switch to the specified network namespace name.
.TP
.B \-b, \-\-bpf
Show socket BPF filters (only administrators are allowed to get these information).
.TP
......
......@@ -10,6 +10,10 @@ ifeq ($(HAVE_SELINUX),y)
CFLAGS += $(shell pkg-config --cflags libselinux) -DHAVE_SELINUX
endif
ifeq ($(IP_CONFIG_SETNS),y)
CFLAGS += -DHAVE_SETNS
endif
all: $(TARGETS)
ss: $(SSOBJ)
......
......@@ -31,6 +31,7 @@
#include "rt_names.h"
#include "ll_map.h"
#include "libnetlink.h"
#include "namespace.h"
#include "SNAPSHOT.h"
#include <linux/tcp.h>
......@@ -3246,6 +3247,7 @@ static void _usage(FILE *dest)
" -b, --bpf show bpf filter socket information\n"
" -Z, --context display process SELinux security contexts\n"
" -z, --contexts display process and socket SELinux security contexts\n"
" -N, --net switch to the specified network namespace name\n"
"\n"
" -4, --ipv4 display only IP version 4 sockets\n"
" -6, --ipv6 display only IP version 6 sockets\n"
......@@ -3345,6 +3347,7 @@ static const struct option long_opts[] = {
{ "help", 0, 0, 'h' },
{ "context", 0, 0, 'Z' },
{ "contexts", 0, 0, 'z' },
{ "net", 1, 0, 'N' },
{ 0 }
};
......@@ -3360,7 +3363,7 @@ int main(int argc, char *argv[])
struct filter dbs_filter = {};
int state_filter = 0;
while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vVzZ",
while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vVzZN:",
long_opts, NULL)) != EOF) {
switch(ch) {
case 'n':
......@@ -3532,6 +3535,10 @@ int main(int argc, char *argv[])
show_proc_ctx++;
user_ent_hash_build();
break;
case 'N':
if (netns_switch(optarg))
exit(1);
break;
case 'h':
case '?':
help();
......
TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o \
tc_monitor.o m_police.o m_estimator.o m_action.o \
tc_monitor.o tc_bpf.o m_police.o m_estimator.o m_action.o \
m_ematch.o emp_ematch.yacc.o emp_ematch.lex.o
include ../Config
......@@ -46,6 +46,7 @@ TCMODULES += m_skbedit.o
TCMODULES += m_csum.o
TCMODULES += m_simple.o
TCMODULES += m_vlan.o
TCMODULES += m_bpf.o
TCMODULES += p_ip.o
TCMODULES += p_icmp.o
TCMODULES += p_tcp.o
......
......@@ -26,6 +26,7 @@
#include "utils.h"
#include "tc_util.h"
#include "tc_bpf.h"
static void explain(void)
{
......@@ -44,130 +45,6 @@ static void explain(void)
fprintf(stderr, "NOTE: CLASSID is parsed as hexadecimal input.\n");
}
static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
char **bpf_string, bool *need_release,
const char separator)
{
char sp;
if (from_file) {
size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
char *tmp_string;
FILE *fp;
tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
tmp_string = malloc(tmp_len);
if (tmp_string == NULL)
return -ENOMEM;
memset(tmp_string, 0, tmp_len);
fp = fopen(arg, "r");
if (fp == NULL) {
perror("Cannot fopen");
free(tmp_string);
return -ENOENT;
}
if (!fgets(tmp_string, tmp_len, fp)) {
free(tmp_string);
fclose(fp);
return -EIO;
}
fclose(fp);
*need_release = true;
*bpf_string = tmp_string;
} else {
*need_release = false;
*bpf_string = arg;
}
if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 ||
sp != separator) {
if (*need_release)
free(*bpf_string);
return -EINVAL;
}
return 0;
}
static int bpf_parse_ops(int argc, char **argv, struct nlmsghdr *n,
bool from_file)
{
char *bpf_string, *token, separator = ',';
struct sock_filter bpf_ops[BPF_MAXINSNS];
int ret = 0, i = 0;
bool need_release;
__u16 bpf_len = 0;
if (argc < 1)
return -EINVAL;
if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string,
&need_release, separator))
return -EINVAL;
if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) {
ret = -EINVAL;
goto out;
}
token = bpf_string;
while ((token = strchr(token, separator)) && (++token)[0]) {
if (i >= bpf_len) {
fprintf(stderr, "Real program length exceeds encoded "
"length parameter!\n");
ret = -EINVAL;
goto out;
}
if (sscanf(token, "%hu %hhu %hhu %u,",
&bpf_ops[i].code, &bpf_ops[i].jt,
&bpf_ops[i].jf, &bpf_ops[i].k) != 4) {
fprintf(stderr, "Error at instruction %d!\n", i);
ret = -EINVAL;
goto out;
}
i++;
}
if (i != bpf_len) {
fprintf(stderr, "Parsed program length is less than encoded"
"length parameter!\n");
ret = -EINVAL;
goto out;
}
addattr_l(n, MAX_MSG, TCA_BPF_OPS_LEN, &bpf_len, sizeof(bpf_len));
addattr_l(n, MAX_MSG, TCA_BPF_OPS, &bpf_ops,
bpf_len * sizeof(struct sock_filter));
out:
if (need_release)
free(bpf_string);
return ret;
}
static void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len)
{
struct sock_filter *ops = (struct sock_filter *) RTA_DATA(bpf_ops);
int i;
if (len == 0)
return;
fprintf(f, "bytecode \'%u,", len);
for (i = 0; i < len - 1; i++)
fprintf(f, "%hu %hhu %hhu %u,", ops[i].code, ops[i].jt,
ops[i].jf, ops[i].k);
fprintf(f, "%hu %hhu %hhu %u\'\n", ops[i].code, ops[i].jt,
ops[i].jf, ops[i].k);
}
static int bpf_parse_opt(struct filter_util *qu, char *handle,
int argc, char **argv, struct nlmsghdr *n)
{
......@@ -195,6 +72,10 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
while (argc > 0) {
if (matches(*argv, "run") == 0) {
bool from_file;
struct sock_filter bpf_ops[BPF_MAXINSNS];
__u16 bpf_len;
int ret;
NEXT_ARG();
if (strcmp(*argv, "bytecode-file") == 0) {
from_file = true;
......@@ -206,10 +87,15 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
return -1;
}
NEXT_ARG();
if (bpf_parse_ops(argc, argv, n, from_file)) {
ret = bpf_parse_ops(argc, argv, bpf_ops, from_file);
if (ret < 0) {
fprintf(stderr, "Illegal \"bytecode\"\n");
return -1;
}
bpf_len = ret;
addattr16(n, MAX_MSG, TCA_BPF_OPS_LEN, bpf_len);
addattr_l(n, MAX_MSG, TCA_BPF_OPS, &bpf_ops,
bpf_len * sizeof(struct sock_filter));
} else if (matches(*argv, "classid") == 0 ||
strcmp(*argv, "flowid") == 0) {
unsigned handle;
......
/*
* m_bpf.c BFP based action module
*
* This program is free software; you can redistribute 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 <string.h>
#include <stdbool.h>
#include <linux/tc_act/tc_bpf.h>
#include "utils.h"
#include "rt_names.h"
#include "tc_util.h"
#include "tc_bpf.h"
static void explain(void)
{
fprintf(stderr, "Usage: ... bpf ...\n");
fprintf(stderr, "\n");
fprintf(stderr, " [inline]: run bytecode BPF_BYTECODE\n");
fprintf(stderr, " [from file]: run bytecode-file FILE\n");
fprintf(stderr, "\n");
fprintf(stderr, "Where BPF_BYTECODE := \'s,c t f k,c t f k,c t f k,...\'\n");
fprintf(stderr, " c,t,f,k and s are decimals; s denotes number of 4-tuples\n");
fprintf(stderr, "Where FILE points to a file containing the BPF_BYTECODE string\n");
fprintf(stderr, "\nACTION_SPEC := ... look at individual actions\n");
fprintf(stderr, "NOTE: CLASSID is parsed as hexadecimal input.\n");
}
static void usage(void)
{
explain();
exit(-1);
}
static int parse_bpf(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n)
{
int argc = *argc_p;
char **argv = *argv_p;
struct rtattr *tail;
struct tc_act_bpf parm = { 0 };
struct sock_filter bpf_ops[BPF_MAXINSNS];
__u16 bpf_len = 0;
if (matches(*argv, "bpf") != 0)
return -1;
NEXT_ARG();
while (argc > 0) {
if (matches(*argv, "run") == 0) {
bool from_file;
int ret;
NEXT_ARG();
if (strcmp(*argv, "bytecode-file") == 0) {
from_file = true;
} else if (strcmp(*argv, "bytecode") == 0) {
from_file = false;
} else {
fprintf(stderr, "unexpected \"%s\"\n", *argv);
explain();
return -1;
}
NEXT_ARG();
ret = bpf_parse_ops(argc, argv, bpf_ops, from_file);
if (ret < 0) {
fprintf(stderr, "Illegal \"bytecode\"\n");
return -1;
}
bpf_len = ret;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
break;
}
argc--;
argv++;
}
parm.action = TC_ACT_PIPE;
if (argc) {
if (matches(*argv, "reclassify") == 0) {
parm.action = TC_ACT_RECLASSIFY;
NEXT_ARG();
} else if (matches(*argv, "pipe") == 0) {
parm.action = TC_ACT_PIPE;
NEXT_ARG();
} else if (matches(*argv, "drop") == 0 ||
matches(*argv, "shot") == 0) {
parm.action = TC_ACT_SHOT;
NEXT_ARG();
} else if (matches(*argv, "continue") == 0) {
parm.action = TC_ACT_UNSPEC;
NEXT_ARG();
} else if (matches(*argv, "pass") == 0) {
parm.action = TC_ACT_OK;
NEXT_ARG();
}
}
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&parm.index, *argv, 10)) {
fprintf(stderr, "bpf: Illegal \"index\"\n");
return -1;
}
argc--;
argv++;
}
}
if (!bpf_len) {
fprintf(stderr, "bpf: Bytecode needs to be passed\n");
explain();
return -1;
}
tail = NLMSG_TAIL(n);
addattr_l(n, MAX_MSG, tca_id, NULL, 0);
addattr_l(n, MAX_MSG, TCA_ACT_BPF_PARMS, &parm, sizeof(parm));
addattr16(n, MAX_MSG, TCA_ACT_BPF_OPS_LEN, bpf_len);
addattr_l(n, MAX_MSG, TCA_ACT_BPF_OPS, &bpf_ops,
bpf_len * sizeof(struct sock_filter));
tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
*argc_p = argc;
*argv_p = argv;
return 0;
}
static int print_bpf(struct action_util *au, FILE *f, struct rtattr *arg)
{
struct rtattr *tb[TCA_ACT_BPF_MAX + 1];
struct tc_act_bpf *parm;
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, TCA_ACT_BPF_MAX, arg);
if (!tb[TCA_ACT_BPF_PARMS]) {
fprintf(f, "[NULL bpf parameters]");
return -1;
}
parm = RTA_DATA(tb[TCA_ACT_BPF_PARMS]);
fprintf(f, " bpf ");
if (tb[TCA_ACT_BPF_OPS] && tb[TCA_ACT_BPF_OPS_LEN])
bpf_print_ops(f, tb[TCA_ACT_BPF_OPS],
rta_getattr_u16(tb[TCA_ACT_BPF_OPS_LEN]));
fprintf(f, "\n\tindex %d ref %d bind %d", parm->index, parm->refcnt,
parm->bindcnt);
if (show_stats) {
if (tb[TCA_ACT_BPF_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_ACT_BPF_TM]);
print_tm(f, tm);
}
}
fprintf(f, "\n ");
return 0;
}
struct action_util bpf_action_util = {
.id = "bpf",
.parse_aopt = parse_bpf,
.print_aopt = print_bpf,
};
/*
* tc_bpf.c BPF common code
*
* 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: Daniel Borkmann <dborkman@redhat.com>
* Jiri Pirko <jiri@resnulli.us>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <linux/filter.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include "utils.h"
#include "tc_util.h"
#include "tc_bpf.h"
int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
char **bpf_string, bool *need_release,
const char separator)
{
char sp;
if (from_file) {
size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
char *tmp_string;
FILE *fp;
tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
tmp_string = malloc(tmp_len);
if (tmp_string == NULL)
return -ENOMEM;
memset(tmp_string, 0, tmp_len);
fp = fopen(arg, "r");
if (fp == NULL) {
perror("Cannot fopen");
free(tmp_string);
return -ENOENT;
}
if (!fgets(tmp_string, tmp_len, fp)) {
free(tmp_string);
fclose(fp);
return -EIO;
}
fclose(fp);
*need_release = true;
*bpf_string = tmp_string;
} else {
*need_release = false;
*bpf_string = arg;
}
if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 ||
sp != separator) {
if (*need_release)
free(*bpf_string);
return -EINVAL;
}
return 0;
}
int bpf_parse_ops(int argc, char **argv, struct sock_filter *bpf_ops,
bool from_file)
{
char *bpf_string, *token, separator = ',';
int ret = 0, i = 0;
bool need_release;
__u16 bpf_len = 0;
if (argc < 1)
return -EINVAL;
if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string,
&need_release, separator))
return -EINVAL;
if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) {
ret = -EINVAL;
goto out;
}
token = bpf_string;
while ((token = strchr(token, separator)) && (++token)[0]) {
if (i >= bpf_len) {
fprintf(stderr, "Real program length exceeds encoded "
"length parameter!\n");
ret = -EINVAL;
goto out;
}
if (sscanf(token, "%hu %hhu %hhu %u,",
&bpf_ops[i].code, &bpf_ops[i].jt,
&bpf_ops[i].jf, &bpf_ops[i].k) != 4) {
fprintf(stderr, "Error at instruction %d!\n", i);
ret = -EINVAL;
goto out;
}
i++;
}
if (i != bpf_len) {
fprintf(stderr, "Parsed program length is less than encoded"
"length parameter!\n");
ret = -EINVAL;
goto out;
}
ret = bpf_len;
out:
if (need_release)
free(bpf_string);
return ret;
}
void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len)
{
struct sock_filter *ops = (struct sock_filter *) RTA_DATA(bpf_ops);
int i;
if (len == 0)
return;
fprintf(f, "bytecode \'%u,", len);
for (i = 0; i < len - 1; i++)
fprintf(f, "%hu %hhu %hhu %u,", ops[i].code, ops[i].jt,
ops[i].jf, ops[i].k);
fprintf(f, "%hu %hhu %hhu %u\'\n", ops[i].code, ops[i].jt,
ops[i].jf, ops[i].k);
}
/*
* tc_bpf.h BPF common code
*
* 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: Daniel Borkmann <dborkman@redhat.com>
* Jiri Pirko <jiri@resnulli.us>
*/
#ifndef _TC_BPF_H_
#define _TC_BPF_H_ 1
#include <stdio.h>
#include <linux/filter.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
char **bpf_string, bool *need_release,
const char separator);
int bpf_parse_ops(int argc, char **argv, struct sock_filter *bpf_ops,
bool from_file);
void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment