Commit 826136e3 authored by 4ast's avatar 4ast

Merge pull request #27 from plumgrid/bblanco_dev

Convert to using pyroute2 for tc
parents 7249beaf bb7200ce
......@@ -34,7 +34,6 @@ bc
kexec-tools
cmake
clang
libmnl-devel
libstdc++-static
python-netaddr
%end
......
......@@ -35,8 +35,6 @@ lib.bpf_open_raw_sock.restype = ct.c_int
lib.bpf_open_raw_sock.argtypes = [ct.c_char_p]
lib.bpf_attach_socket.restype = ct.c_int
lib.bpf_attach_socket.argtypes = [ct.c_int, ct.c_int]
lib.bpf_attach_filter.restype = ct.c_int
lib.bpf_attach_filter.argtypes = [ct.c_int, ct.c_char_p, ct.c_uint, ct.c_ubyte, ct.c_uint]
lib.bpf_prog_load.restype = ct.c_int
lib.bpf_prog_load.argtypes = [ct.c_int, ct.c_void_p, ct.c_size_t,
ct.c_char_p, ct.c_uint]
......@@ -163,16 +161,6 @@ class BPF(object):
% (dev, errstr))
fn.sock = sock
@staticmethod
def attach_classifier(fn, ifname, prio=10, classid=1):
with open("/sys/class/net/%s/ifindex" % ifname) as f:
ifindex = int(f.read())
if not isinstance(fn, BPF.Function):
raise Exception("arg 1 must be of type BPF.Function")
res = lib.bpf_attach_filter(fn.fd, fn.name.encode("ascii"), ifindex, prio, classid)
if res < 0:
raise Exception("Failed to filter with BPF")
@staticmethod
def attach_kprobe(fn, event, pid=-1, cpu=0, group_fd=-1):
if not isinstance(fn, BPF.Function):
......
......@@ -23,4 +23,4 @@ set(clang_libs ${libclangFrontend} ${libclangSerialization} ${libclangDriver} ${
${libclangAST} ${libclangLex} ${libclangBasic})
# Link against LLVM libraries
target_link_libraries(bpfprog ${clang_libs} ${llvm_libs} LLVMBPFCodeGen mnl)
target_link_libraries(bpfprog ${clang_libs} ${llvm_libs} LLVMBPFCodeGen)
......@@ -187,7 +187,7 @@ bool BTypeVisitor::VisitImplicitCastExpr(ImplicitCastExpr *E) {
uint64_t ofs = C.getFieldOffset(F);
uint64_t sz = F->isBitField() ? F->getBitWidthValue(C) : C.getTypeSize(F->getType());
string base = rewriter_.getRewrittenText(SourceRange(Base->getLocStart(), Base->getLocEnd()));
string text = "bpf_dext_pkt(skb, (u64)" + base + "+" + to_string(ofs >> 3)
string text = "bpf_dext_pkt(skb, _parse_base + (u64)" + base + "+" + to_string(ofs >> 3)
+ ", " + to_string(ofs & 0x7) + ", " + to_string(sz) + ")";
rewriter_.ReplaceText(SourceRange(E->getLocStart(), E->getLocEnd()), text);
}
......
......@@ -2,6 +2,8 @@
#define __BPF_HELPERS_H
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <linux/version.h>
/* helper macro to place programs, maps, license in
......@@ -30,6 +32,7 @@ struct _name##_table_t _name
BPF_EXPORT(name) int _##name(struct __sk_buff *skb)
#define BEGIN(next) \
u64 _parse_cursor = 0; \
u64 _parse_base = skb->pkt_type == PACKET_OUTGOING ? 0 : BPF_LL_OFF; \
goto next
#define PROTO(name) \
......
......@@ -1024,7 +1024,7 @@ StatusTuple CodegenLLVM::visit_integer_variable_decl_stmt_node(IntegerVariableDe
StatusTuple CodegenLLVM::visit_struct_decl_stmt_node(StructDeclStmtNode *n) {
++indent_;
StructType *struct_type = StructType::create(ctx(), "struct." + n->id_->name_);
StructType *struct_type = StructType::create(ctx(), "_struct." + n->id_->name_);
vector<Type *> fields;
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it)
fields.push_back(B.getIntNTy((*it)->bit_width_));
......@@ -1089,9 +1089,9 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
else
return mkstatus_(n, "Table type %s not implemented", n->type_id()->name_.c_str());
StructType *decl_struct = mod_->getTypeByName("struct." + n->id_->name_);
StructType *decl_struct = mod_->getTypeByName("_struct." + n->id_->name_);
if (!decl_struct)
decl_struct = StructType::create(ctx(), "struct." + n->id_->name_);
decl_struct = StructType::create(ctx(), "_struct." + n->id_->name_);
if (decl_struct->isOpaque())
decl_struct->setBody(std::vector<Type *>({Type::getInt32Ty(ctx()), Type::getInt32Ty(ctx()),
Type::getInt32Ty(ctx()), Type::getInt32Ty(ctx())}),
......@@ -1156,7 +1156,7 @@ StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
StructType *stype;
//TRY2(lookup_struct_type(formal, &stype));
auto var = (StructVariableDeclStmtNode *)formal;
stype = mod_->getTypeByName("struct." + var->struct_id_->name_);
stype = mod_->getTypeByName("_struct." + var->struct_id_->name_);
if (!stype) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str());
formals.push_back(PointerType::getUnqual(stype));
} else {
......
......@@ -2,7 +2,6 @@
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <libmnl/libmnl.h>
#include <linux/bpf.h>
#include <linux/if_packet.h>
#include <linux/pkt_cls.h>
......@@ -12,9 +11,11 @@
#include <linux/version.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "libbpf.h"
......@@ -135,88 +136,6 @@ int bpf_attach_socket(int sock, int prog) {
return setsockopt(sock, SOL_SOCKET, 50 /*SO_ATTACH_BPF*/, &prog, sizeof(prog));
}
static int cb(const struct nlmsghdr *nlh, void *data) {
struct nlmsgerr *err;
if (nlh->nlmsg_type == NLMSG_ERROR) {
err = mnl_nlmsg_get_payload(nlh);
if (err->error != 0) {
fprintf(stderr, "bpf tc netlink command failed (%d): %s\n",
err->error, strerror(-1 * err->error));
return -1;
} else {
return 0;
}
} else {
return -1;
}
}
int bpf_attach_filter(int progfd, const char *prog_name,
uint32_t ifindex, uint8_t prio, uint32_t classid) {
int rc = -1;
char buf[1024];
struct nlmsghdr *nlh;
struct tcmsg *tc;
struct nlattr *opt;
struct mnl_socket *nl = NULL;
unsigned int portid;
ssize_t bytes;
int seq = getpid();
memset(buf, 0, sizeof(buf));
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWTFILTER;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL;
nlh->nlmsg_seq = seq;
tc = mnl_nlmsg_put_extra_header(nlh, sizeof(*tc));
tc->tcm_family = AF_UNSPEC;
tc->tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL));
tc->tcm_ifindex = ifindex;
mnl_attr_put_strz(nlh, TCA_KIND, "bpf");
opt = mnl_attr_nest_start(nlh, TCA_OPTIONS);
mnl_attr_put_u32(nlh, TCA_BPF_FD, progfd);
mnl_attr_put_strz(nlh, TCA_BPF_NAME, prog_name);
mnl_attr_put_u32(nlh, TCA_BPF_CLASSID, classid);
mnl_attr_nest_end(nlh, opt);
nl = mnl_socket_open(NETLINK_ROUTE);
if (!nl || (uintptr_t)nl == (uintptr_t)-1) {
perror("mnl_socket_open");
goto cleanup;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
goto cleanup;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
goto cleanup;
}
if ((bytes = mnl_socket_recvfrom(nl, buf, sizeof(buf))) < 0) {
perror("mnl_socket_recvfrom");
goto cleanup;
}
if (mnl_cb_run(buf, bytes, seq, portid, cb, NULL) < 0) {
perror("mnl_cb_run");
goto cleanup;
}
rc = 0;
cleanup:
if (nl && (uintptr_t)nl != (uintptr_t)-1)
if (mnl_socket_close(nl) < 0)
perror("mnl_socket_close");
return rc;
}
static int bpf_attach_tracing_event(int progfd, const char *event_path, pid_t pid, int cpu, int group_fd)
{
int efd = -1, rc = -1, pfd = -1;
......
......@@ -13,6 +13,18 @@ struct dot1q_t {
u16 type;
} __attribute__((packed));
struct arp_t {
u16 htype;
u16 ptype;
u8 hlen;
u8 plen;
u16 oper;
u64 sha:48;
u64 spa:32;
u64 tha:48;
u32 tpa;
} __attribute__((packed));
struct ip_t {
u8 ver:4; // byte 0
u8 hlen:4;
......
......@@ -21,8 +21,6 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int insn_len,
const char *license, unsigned kern_version);
int bpf_attach_socket(int sockfd, int progfd);
int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex,
uint8_t prio, uint32_t classid);
/* create RAW socket and bind to interface 'name' */
int bpf_open_raw_sock(const char *name);
......
......@@ -2,8 +2,8 @@ add_test(NAME py_test_stat1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_stat1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_stat1.py test_stat1.b proto.b)
add_test(NAME py_test_stat1_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_stat1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_stat1.py test_stat1.c)
add_test(NAME py_test_xlate1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_xlate1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_xlate1.py test_xlate1.b proto.b)
#add_test(NAME py_test_xlate1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# COMMAND ${TEST_WRAPPER} py_xlate1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_xlate1.py test_xlate1.b proto.b)
add_test(NAME py_test_xlate1_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_xlate1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_xlate1.py test_xlate1.c)
add_test(NAME py_test_call1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
......
......@@ -24,7 +24,7 @@ int parse_ether(struct __sk_buff *skb) {
case 0x0806: jump.call(skb, S_ARP);
}
jump.call(skb, S_EOP);
return 0;
return 1;
}
BPF_EXPORT(parse_arp)
......@@ -37,7 +37,7 @@ int parse_arp(struct __sk_buff *skb) {
if (leaf) (*leaf)++;
jump.call(skb, S_EOP);
return 0;
return 1;
}
BPF_EXPORT(parse_ip)
......@@ -50,7 +50,7 @@ int parse_ip(struct __sk_buff *skb) {
if (leaf) (*leaf)++;
jump.call(skb, S_EOP);
return 0;
return 1;
}
BPF_EXPORT(eop)
......@@ -58,5 +58,5 @@ int eop(struct __sk_buff *skb) {
int key = S_EOP;
u64 *leaf = stats.lookup(&key);
if (leaf) (*leaf)++;
return 0;
return 1;
}
......@@ -3,6 +3,7 @@
from ctypes import c_ushort, c_int, c_ulonglong
from netaddr import IPAddress
from bpf import BPF
from pyroute2 import IPRoute
from socket import socket, AF_INET, SOCK_DGRAM
import sys
from time import sleep
......@@ -22,7 +23,10 @@ class TestBPFSocket(TestCase):
arp_fn = b.load_func("parse_arp", BPF.SCHED_CLS)
ip_fn = b.load_func("parse_ip", BPF.SCHED_CLS)
eop_fn = b.load_func("eop", BPF.SCHED_CLS)
BPF.attach_classifier(ether_fn, "eth0")
ip = IPRoute()
ifindex = ip.link_lookup(ifname="eth0")[0]
ip.tc("add-filter", "bpf", ifindex, ":1", fd=ether_fn.fd,
name=ether_fn.name, parent="0:", action="ok", classid=1)
self.jump = b.get_table("jump", c_int, c_int)
self.jump.update(c_int(S_ARP), c_int(arp_fn.fd))
self.jump.update(c_int(S_IP), c_int(ip_fn.fd))
......@@ -32,6 +36,7 @@ class TestBPFSocket(TestCase):
def test_jumps(self):
udp = socket(AF_INET, SOCK_DGRAM)
udp.sendto(b"a" * 10, ("172.16.1.1", 5000))
udp.close()
self.assertGreater(self.stats.lookup(c_int(S_IP)).value, 0)
self.assertGreater(self.stats.lookup(c_int(S_ARP)).value, 0)
self.assertGreater(self.stats.lookup(c_int(S_EOP)).value, 1)
......
......@@ -7,21 +7,23 @@ struct IPKey {
struct IPLeaf {
u32 xdip;
u32 xsip;
u64 xlated_pkts;
u64 ip_xlated_pkts;
u64 arp_xlated_pkts;
};
BPF_TABLE("hash", struct IPKey, struct IPLeaf, xlate, 1024);
BPF_EXPORT(on_packet)
int on_packet(struct __sk_buff *skb) {
BEGIN(ethernet);
u32 orig_dip = 0;
u32 orig_sip = 0;
struct IPLeaf *xleaf;
BEGIN(ethernet);
PROTO(ethernet) {
switch (ethernet->type) {
case 0x0800: goto ip;
case 0x0806: goto arp;
case 0x8100: goto dot1q;
}
goto EOP;
......@@ -29,10 +31,23 @@ int on_packet(struct __sk_buff *skb) {
PROTO(dot1q) {
switch (dot1q->type) {
case 0x0806: goto arp;
case 0x0800: goto ip;
}
goto EOP;
}
PROTO(arp) {
orig_dip = arp->tpa;
orig_sip = arp->spa;
struct IPKey key = {.dip=orig_dip, .sip=orig_sip};
xleaf = xlate.lookup(&key);
if (xleaf) {
arp->tpa = xleaf->xdip;
arp->spa = xleaf->xsip;
lock_xadd(&xleaf->arp_xlated_pkts, 1);
}
goto EOP;
}
PROTO(ip) {
orig_dip = ip->dst;
......@@ -44,7 +59,7 @@ int on_packet(struct __sk_buff *skb) {
incr_cksum_l3(&ip->hchecksum, orig_dip, xleaf->xdip);
ip->src = xleaf->xsip;
incr_cksum_l3(&ip->hchecksum, orig_sip, xleaf->xsip);
lock_xadd(&xleaf->xlated_pkts, 1);
lock_xadd(&xleaf->ip_xlated_pkts, 1);
}
switch (ip->nextp) {
case 6: goto tcp;
......@@ -70,5 +85,5 @@ int on_packet(struct __sk_buff *skb) {
}
EOP:
return 0;
return 1;
}
......@@ -3,7 +3,9 @@
from ctypes import c_uint, c_ulonglong, Structure
from netaddr import IPAddress
from bpf import BPF
from pyroute2 import IPRoute
from socket import socket, AF_INET, SOCK_DGRAM
from subprocess import call
import sys
from time import sleep
from unittest import main, TestCase
......@@ -19,24 +21,36 @@ class Key(Structure):
class Leaf(Structure):
_fields_ = [("xdip", c_uint),
("xsip", c_uint),
("xlated_pkts", c_ulonglong)]
("ip_xlated_pkts", c_ulonglong),
("arp_xlated_pkts", c_ulonglong)]
class TestBPFSocket(TestCase):
class TestBPFFilter(TestCase):
def setUp(self):
b = BPF(arg1, arg2, debug=1)
b = BPF(arg1, arg2, debug=0)
fn = b.load_func("on_packet", BPF.SCHED_CLS)
BPF.attach_classifier(fn, "eth0")
ip = IPRoute()
ifindex = ip.link_lookup(ifname="eth0")[0]
ip.addr("del", index=ifindex, address="172.16.1.2", mask=24)
ip.addr("add", index=ifindex, address="192.168.1.2", mask=24)
ip.tc("add", "ingress", ifindex, "ffff:")
ip.tc("add-filter", "bpf", ifindex, ":1", fd=fn.fd, name=fn.name, parent="ffff:", action="ok", classid=1)
ip.tc("add-filter", "bpf", ifindex, ":2", fd=fn.fd, name=fn.name, parent="0:", action="ok", classid=1)
self.xlate = b.get_table("xlate", Key, Leaf)
def test_xlate(self):
key = Key(IPAddress("172.16.1.1").value, IPAddress("172.16.1.2").value)
leaf = Leaf(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value, 0)
self.xlate.update(key, leaf)
udp = socket(AF_INET, SOCK_DGRAM)
udp.sendto(b"a" * 10, ("172.16.1.1", 5000))
leaf = self.xlate.lookup(key)
self.assertGreater(leaf.xlated_pkts, 0)
udp.close()
key1 = Key(IPAddress("172.16.1.2").value, IPAddress("172.16.1.1").value)
leaf1 = Leaf(IPAddress("192.168.1.2").value, IPAddress("192.168.1.1").value, 0, 0)
self.xlate.update(key1, leaf1)
key2 = Key(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value)
leaf2 = Leaf(IPAddress("172.16.1.1").value, IPAddress("172.16.1.2").value, 0, 0)
self.xlate.update(key2, leaf2)
call(["ping", "-c1", "192.168.1.1"])
leaf = self.xlate.lookup(key1)
self.assertGreater(leaf.ip_xlated_pkts, 0)
self.assertGreater(leaf.arp_xlated_pkts, 0)
leaf = self.xlate.lookup(key2)
self.assertGreater(leaf.ip_xlated_pkts, 0)
self.assertGreater(leaf.arp_xlated_pkts, 0)
if __name__ == "__main__":
main()
......@@ -25,7 +25,6 @@ function ns_run() {
sudo ip link add $ns.in type veth peer name $ns.out
sudo ip link set $ns.in netns $ns
sudo ip netns exec $ns ip link set $ns.in name eth0
sudo ip netns exec $ns tc qdisc add dev eth0 root prio
sudo ip netns exec $ns ip addr add dev eth0 172.16.1.2/24
sudo ip netns exec $ns ip link set eth0 up
sudo ip netns exec $ns ethtool -K eth0 tx off
......
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