kexec-tools
cmake
clang
-libmnl-devel
libstdc++-static
python-netaddr
%end
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]
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):
raise Exception("arg 1 must be of type BPF.Function")
${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)
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);
}
#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
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) \
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_));
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())}),
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 {
#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>
#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"
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;
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;
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);
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}
case 0x0806: jump.call(skb, S_ARP);
}
jump.call(skb, S_EOP);
- return 0;
+ return 1;
}
BPF_EXPORT(parse_arp)
if (leaf) (*leaf)++;
jump.call(skb, S_EOP);
- return 0;
+ return 1;
}
BPF_EXPORT(parse_ip)
if (leaf) (*leaf)++;
jump.call(skb, S_EOP);
- return 0;
+ return 1;
}
BPF_EXPORT(eop)
int key = S_EOP;
u64 *leaf = stats.lookup(&key);
if (leaf) (*leaf)++;
- return 0;
+ return 1;
}
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
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))
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)
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;
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;
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;
}
EOP:
- return 0;
+ return 1;
}
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
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()
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