Commit cd5cb41e authored by Brenden Blanco's avatar Brenden Blanco

Initial commit

Signed-off-by: default avatarBrenden Blanco <bblanco@plumgrid.com>
parent a94bd931
*.swp
*.swo
cmake_minimum_required(VERSION 2.8.7)
project(bpf-tools)
set(CMAKE_BUILD_TYPE Debug)
enable_testing()
find_package(BISON)
find_package(FLEX)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM: ${LLVM_INCLUDE_DIRS}")
find_program(XXD xxd)
if (${XXD} STREQUAL "XXD-NOTFOUND")
message(FATAL_ERROR "program xxd not found, install vim-common")
......@@ -13,8 +17,15 @@ find_program(CLANG clang)
if (${CLANG} STREQUAL "CLANG-NOTFOUND")
message(FATAL_ERROR "program clang not found, install clang with bpf support")
endif()
execute_process(COMMAND ${CLANG} --version OUTPUT_VARIABLE CLANG_VERSION_RAW)
string(REGEX MATCH "[0-9]+[.][0-9]+[.][0-9]+" CLANG_VERSION ${CLANG_VERSION_RAW})
message(STATUS "Found CLANG: ${CLANG} (found version \"${CLANG_VERSION}\")")
if (CLANG_VERSION VERSION_LESS 3.6.0)
message(FATAL_ERROR "requires clang version >= 3.6.0, ${CLANG_VERSION} found")
endif()
set(CMAKE_C_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
add_subdirectory(jit)
add_subdirectory(src)
add_subdirectory(tests)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fno-rtti")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat/include)
add_subdirectory(src)
/*
* =====================================================================
* Copyright (c) 2012, PLUMgrid, http://plumgrid.com
*
* This source is subject to the PLUMgrid License.
* All rights reserved.
*
* THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
* PLUMgrid confidential information, delete if you are not the
* intended recipient.
*
* =====================================================================
*/
#include <set>
#include <algorithm>
#include <sstream>
#include <assert.h>
#include "exception.h"
#include "cc/codegen_c.h"
#include "cc/lexer.h"
#include "cc/type_helper.h"
namespace ebpf {
namespace cc {
using std::set;
using std::for_each;
using std::pair;
using std::stringstream;
template <typename... Args>
void CodegenC::emitln(const char *fmt, Args&&... params) {
fprintf(out_, fmt, std::forward<Args>(params)...);
fprintf(out_, "\n%*s", indent_ * 2, "");
}
void CodegenC::emitln(const char *s) {
fprintf(out_, "%s", s);
fprintf(out_, "\n%*s", indent_ * 2, "");
}
template <typename... Args>
void CodegenC::emit(const char *fmt, Args&&... params) {
fprintf(out_, fmt, std::forward<Args>(params)...);
}
void CodegenC::emit(const char *s) {
fprintf(out_, "%s", s);
}
template <typename... Args>
void CodegenC::lnemit(const char *fmt, Args&&... params) {
fprintf(out_, "\n%*s", indent_ * 2, "");
fprintf(out_, fmt, std::forward<Args>(params)...);
}
void CodegenC::lnemit(const char *s) {
fprintf(out_, "\n%*s", indent_ * 2, "");
fprintf(out_, "%s", s);
}
void CodegenC::indent() {
fprintf(out_, "%*s", indent_ * 2, "");
}
void CodegenC::emit_comment(Node* n) {
// if (!n->text_.empty()) {
// emitln("/* %s */", n->text_.c_str());
// }
}
void CodegenC::visit_block_stmt_node(BlockStmtNode* n) {
++indent_;
emit("{");
// enter scope
auto scope = scopes_->current_var();
if (n->scope_) {
scopes_->set_current(n->scope_);
}
if (!n->stmts_.empty()) {
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
emitln("");
(*it)->accept(this);
}
}
// exit scope
scopes_->set_current(scope);
--indent_;
emitln("");
emit("}");
}
void CodegenC::visit_version_stmt_node(VersionStmtNode* n) {
uint32_t version;
version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
emit("static const uint32_t plumlet_version __attribute__"
"((section (\".version\"), used)) = 0x%x;\n", version);
}
void CodegenC::visit_if_stmt_node(IfStmtNode* n) {
emit_comment(n);
emit("if (");
n->cond_->accept(this);
emit(") ");
n->true_block_->accept(this);
if (n->false_block_) {
emit(" else ");
n->false_block_->accept(this);
}
}
void CodegenC::visit_onvalid_stmt_node(OnValidStmtNode* n) {
auto sdecl = static_cast<StructVariableDeclStmtNode*>(n->cond_->decl_);
emit_comment(n);
// cheat a little not using n->cond_->accept(this) to prevent the dereference
emit("if (%s%s) ", sdecl->scope_id(), sdecl->id_->c_str());
n->block_->accept(this);
if (n->else_block_) {
emit(" else ");
n->else_block_->accept(this);
}
}
void CodegenC::visit_switch_stmt_node(SwitchStmtNode* n) {
emit_comment(n);
emit("switch (");
n->cond_->accept(this);
emit(") ");
n->block_->accept(this);
}
void CodegenC::visit_case_stmt_node(CaseStmtNode* n) {
if (n->value_) {
emit("case ");
n->value_->accept(this);
} else {
emit("default");
}
emit(": ");
++indent_;
n->block_->accept(this);
emitln("");
emit("break;");
--indent_;
}
void CodegenC::visit_ident_expr_node(IdentExprNode* n) {
if (!n->decl_)
throw CompilerException("variable lookup failed: %s", n->name_.c_str());
if (n->decl_->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) {
if (n->sub_name_.size()) {
if (n->bitop_) {
// ident is holding a host endian number, don't use dext
if (n->flags_[ExprNode::IS_LHS]) {
emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
} else {
emit("(((%s%s->%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str(),
n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_);
}
} else {
if (n->struct_type_->id_->name_ == "_Packet" && n->sub_name_.substr(0, 3) == "arg") {
// convert arg1~arg8 into args[0]~args[7] assuming type_check verified the range already
auto arg_num = stoi(n->sub_name_.substr(3, 3));
if (arg_num < 5) {
emit("%s%s->args_lo[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 1);
} else {
emit("%s%s->args_hi[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 5);
}
} else {
emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
}
}
} else {
emit("*%s%s", n->decl_->scope_id(), n->c_str());
}
} else {
if (n->sub_name_.size()) {
emit("%s%s.%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
} else {
if (n->bitop_) {
// ident is holding a host endian number, don't use dext
if (n->flags_[ExprNode::IS_LHS]) {
assert(0);
} else {
emit("(((%s%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(),
n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_);
}
} else {
emit("%s%s", n->decl_->scope_id(), n->c_str());
}
}
}
}
void CodegenC::visit_assign_expr_node(AssignExprNode* n) {
if (n->bitop_) {
n->id_->accept(this);
emit(" = (");
n->id_->accept(this);
emit(" & ~((((%s)1 << %d) - 1) << %d)) | (", bits_to_uint(n->id_->bit_width_),
n->bitop_->bit_width_, n->bitop_->bit_offset_);
n->rhs_->accept(this);
emit(" << %d)", n->bitop_->bit_offset_);
} else {
if (n->id_->flags_[ExprNode::PROTO]) {
auto f = n->id_->struct_type_->field(n->id_->sub_name_);
emit("bpf_dins(%s%s + %zu, %zu, %zu, ", n->id_->decl_->scope_id(), n->id_->c_str(),
f->bit_offset_ >> 3, f->bit_offset_ & 0x7, f->bit_width_);
n->rhs_->accept(this);
emit(")");
} else {
n->id_->accept(this);
emit(" = ");
n->rhs_->accept(this);
}
}
}
void CodegenC::visit_packet_expr_node(PacketExprNode* n) {
auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
if (p) {
auto f = p->field(n->id_->sub_name_);
if (f) {
size_t bit_offset = f->bit_offset_;
size_t bit_width = f->bit_width_;
if (n->bitop_) {
bit_offset += f->bit_width_ - (n->bitop_->bit_offset_ + n->bitop_->bit_width_);
bit_width = std::min(bit_width - n->bitop_->bit_offset_, n->bitop_->bit_width_);
}
if (n->flags_[ExprNode::IS_LHS])
emit("bpf_dins_pkt(pkt, %s + %zu, %zu, %zu, ", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
else
emit("bpf_dext_pkt(pkt, %s + %zu, %zu, %zu)", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
} else {
emit("pkt->start + pkt->offset + %s", n->id_->c_str());
}
}
}
void CodegenC::visit_integer_expr_node(IntegerExprNode* n) {
emit("%s", n->val_.c_str());
}
void CodegenC::visit_binop_expr_node(BinopExprNode* n) {
n->lhs_->accept(this);
switch (n->op_) {
case Tok::TCEQ: emit(" == "); break;
case Tok::TCNE: emit(" != "); break;
case Tok::TXOR: emit(" ^ "); break;
case Tok::TAND: emit(" && "); break;
case Tok::TOR: emit(" || "); break;
case Tok::TMOD: emit("%"); break;
case Tok::TCLT: emit(" < "); break;
case Tok::TCLE: emit(" <= "); break;
case Tok::TCGT: emit(" > "); break;
case Tok::TCGE: emit(" >= "); break;
case Tok::TPLUS: emit(" + "); break;
case Tok::TMINUS: emit(" - "); break;
case Tok::TLAND: emit(" & "); break;
case Tok::TLOR: emit(" | "); break;
default: emit(" ?%d? ", n->op_); break;
}
n->rhs_->accept(this);
}
void CodegenC::visit_unop_expr_node(UnopExprNode* n) {
const char* s = "";
switch (n->op_) {
case Tok::TNOT: s = "!"; break;
case Tok::TCMPL: s = "~"; break;
default: {}
}
emit("%s", s);
n->expr_->accept(this);
}
void CodegenC::visit_bitop_expr_node(BitopExprNode* n) {
}
void CodegenC::visit_goto_expr_node(GotoExprNode* n) {
if (n->id_->name_ == "DONE") {
for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii)
for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj)
emitln("%s;", jj->c_str());
emit("goto DONE");
return;
}
string jump_label;
// when dealing with multistates, goto statements may be overridden
auto rewrite_it = proto_rewrites_.find(n->id_->full_name());
auto default_it = proto_rewrites_.find("");
if (rewrite_it != proto_rewrites_.end()) {
jump_label = rewrite_it->second;
} else if (default_it != proto_rewrites_.end()) {
jump_label = default_it->second;
} else {
auto state = scopes_->current_state()->lookup(n->id_->full_name(), false);
if (state) {
jump_label = state->scoped_name();
if (n->is_continue_) {
jump_label += "_continue";
}
} else {
state = scopes_->current_state()->lookup("EOP", false);
if (state) {
jump_label = state->scoped_name();
}
}
}
for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii)
for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj)
emitln("%s;", jj->c_str());
emit("goto %s", jump_label.c_str());
}
void CodegenC::emit_table_lookup(MethodCallExprNode* n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
stringstream free_inst;
IdentExprNode* arg1;
StructVariableDeclStmtNode* arg1_type;
emitln("{ if (unlikely(pkt->capture)) {");
emitln(" bpf_capture(pkt, BPF_CAP_TABLE_LOOKUP, TABLE_ID_%s, 0);", n->id_->c_str());
emitln("} }");
emit("%s* %s_key = &", table->key_id()->c_str(), n->id_->c_str());
arg0->accept(this);
emitln(";");
emitln("%s *%s_element = (%s*)",
table->leaf_id()->c_str(), n->id_->c_str(), table->leaf_id()->c_str());
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED" ||
table->type_id()->name_ == "LPM") {
emit(" bpf_table_lookup(pkt, TABLE_ID_%s, %s_key)", n->id_->c_str(), n->id_->c_str());
if (n->args_.size() == 2) {
arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
arg1_type = static_cast<StructVariableDeclStmtNode*>(arg1->decl_);
if (table->leaf_id()->name_ != arg1_type->struct_id_->name_) {
throw CompilerException("lookup pointer type mismatch %s != %s", table->leaf_id()->c_str(),
arg1_type->struct_id_->c_str());
}
emitln(";");
// cheat a little not using arg1->accept(this) to prevent the dereference
emit("%s%s = %s_element", arg1_type->scope_id(), arg1_type->id_->c_str(), n->id_->c_str());
}
} else {
throw CompilerException("lookup in table type %s unsupported", table->type_id()->c_str());
}
free_instructions_.back().push_back(free_inst.str());
}
void CodegenC::emit_table_update(MethodCallExprNode* n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
IdentExprNode* arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
IdentExprNode* type0 = table->templates_.at(0).get();
emit("%s* %s_ukey = &", type0->c_str(), n->id_->c_str());
arg0->accept(this);
emitln(";");
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
emit("bpf_table_update(pkt, TABLE_ID_%s, %s_ukey", n->id_->c_str(), n->id_->c_str());
emit(", &");
arg1->accept(this);
emitln(");");
} else if (table->type_id()->name_ == "LPM") {
}
}
void CodegenC::emit_table_delete(MethodCallExprNode* n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
IdentExprNode* type0 = table->templates_.at(0).get();
emit("%s* %s_dkey = &", type0->c_str(), n->id_->c_str());
arg0->accept(this);
emitln(";");
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
emit("bpf_table_delete(pkt, TABLE_ID_%s, %s_dkey", n->id_->c_str(), n->id_->c_str());
emitln(");");
} else if (table->type_id()->name_ == "LPM") {
}
}
void CodegenC::emit_channel_push_generic(MethodCallExprNode* n) {
/* computation of orig_length of packet:
* orig_lenth = pkt->length - (orig_offset - pkt->offset)
* push_header(N) does pkt->length += N; pkt->offset -= N;
* pop_header(N) does pg_may_access(N); pkt->length -=N; pkt->offset +=N;
*
* therefore push_header(); pop_header(); sequence is currently broken, ticket #930
*/
emit("bpf_channel_push_packet(pkt");
emit(")");
}
void CodegenC::emit_channel_push(MethodCallExprNode* n) {
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
StructVariableDeclStmtNode* arg0_type = static_cast<StructVariableDeclStmtNode*>(arg0->decl_);
emit("bpf_channel_push_struct(pkt, STRUCTID_%s, &", arg0_type->struct_id_->c_str());
arg0->accept(this);
emit(", sizeof(");
arg0->accept(this);
emit("))");
}
void CodegenC::emit_log(MethodCallExprNode* n) {
emitln("{ if (unlikely(pkt->capture)) {");
emit(" bpf_capture(pkt, BPF_CAP_LOG, %d, ", n->line_);
n->args_[0]->accept(this);
emit("); } }");
}
void CodegenC::emit_packet_forward(MethodCallExprNode* n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_forward(pkt, ");
n->args_[0]->accept(this);
emit(")");
}
void CodegenC::emit_packet_replicate(MethodCallExprNode*n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_replicate(pkt, ");
n->args_[0]->accept(this);
emit(",", n->id_->c_str());
n->args_[1]->accept(this);
emit(")");
}
void CodegenC::emit_packet_clone_forward(MethodCallExprNode* n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_clone_forward(pkt, ");
n->args_[0]->accept(this);
emit(")");
}
void CodegenC::emit_packet_forward_self(MethodCallExprNode* n) {
emit("bpf_forward_self(pkt, ");
n->args_[0]->accept(this);
emit(")");
}
void CodegenC::emit_packet_drop(MethodCallExprNode* n) {
emit("bpf_drop(pkt)");
}
void CodegenC::emit_packet_push_header(MethodCallExprNode* n) {
emit("if (unlikely(bpf_push_header(pkt, ");
n->args_[0]->accept(this);
if (n->args_.size() == 1) {
emit(", %zu, 0) != 0)) goto ERROR", n->args_[0]->struct_type_->bit_width_ >> 3);
} else {
emit(", %zu, ", n->args_[0]->struct_type_->bit_width_ >> 3);
n->args_[1]->accept(this);
emit(") != 0)) goto ERROR");
}
}
void CodegenC::emit_packet_pop_header(MethodCallExprNode* n) {
emit("if (unlikely(bpf_pop_header(pkt, ");
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
} else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
n->args_[0]->accept(this);
}
emit(", 0/*todo*/) != 0)) goto ERROR");
}
void CodegenC::emit_packet_push_vlan(MethodCallExprNode* n) {
emit("if (unlikely(bpf_push_vlan(pkt, bpf_htons(0x8100/*ETH_P_8021Q*/), ");
n->args_[0]->accept(this);
emit(") != 0)) goto ERROR");
}
void CodegenC::emit_packet_pop_vlan(MethodCallExprNode* n) {
emit("if (unlikely(bpf_pop_vlan(pkt) != 0)) goto ERROR");
}
void CodegenC::emit_packet_rewrite_field(MethodCallExprNode* n) {
n->args_[0]->accept(this);
n->args_[1]->accept(this);
emit(")");
}
void CodegenC::emit_atomic_add(MethodCallExprNode* n) {
emit("__sync_fetch_and_add(&");
n->args_[0]->accept(this);
emit(", ");
n->args_[1]->accept(this);
emit(")");
}
void CodegenC::emit_cksum(MethodCallExprNode* n) {
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
auto v = n->args_[0]->struct_type_;
size_t bit_width = v->bit_width_ >> 3;
auto p = proto_scopes_->top_struct()->lookup(v->id_->name_, true);
if (p) {
/* should we do store_half directly? */
if (!n->args_[0]->flags_[ExprNode::PROTO]) {
emit("bpf_ntohs(bpf_checksum_pkt(pkt, %s, %zu))", v->id_->c_str(), bit_width);
} else {
emit("bpf_ntohs(bpf_checksum(");
n->args_[0]->accept(this);
emit(", %zu))", bit_width);
}
} else {
throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_);
}
/** emit("pg_cksum(");
n->args_[0]->accept(this);
emit(", %zu)", n->args_[0]->struct_type_->bit_width_ >> 3);**/
} else {
throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_);
}
}
void CodegenC::emit_incr_cksum_u16(MethodCallExprNode* n) {
if (n->args_.size() == 3) {
/* ip checksum */
emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htons(");
n->args_[1]->accept(this);
emit("), bpf_htons(");
n->args_[2]->accept(this);
emit(")))");
} else {
/* L4 checksum */
emit("(");
/* part of pseudo header */
n->args_[3]->accept(this);
emit(" ? ");
emit("((pkt->hw_csum == 1) ? ");
/* CHECKSUM_PARTIAL update pseudo only */
emit("bpf_ntohs(bpf_pseudo_csum_replace2(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htons(");
n->args_[1]->accept(this);
emit("), bpf_htons(");
n->args_[2]->accept(this);
emit(")))");
emit(" : ");
/* CHECKSUM_NONE update normally */
emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htons(");
n->args_[1]->accept(this);
emit("), bpf_htons(");
n->args_[2]->accept(this);
emit(")))");
emit(")");
emit(" : ");
/* not part of pseudo */
emit("((pkt->hw_csum != 1) ? ");
/* CHECKSUM_NONE update normally */
emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htons(");
n->args_[1]->accept(this);
emit("), bpf_htons(");
n->args_[2]->accept(this);
emit(")))");
emit(" : ");
/* CHECKSUM_PARTIAL no-op */
n->args_[0]->accept(this);
emit("))");
}
}
void CodegenC::emit_incr_cksum_u32(MethodCallExprNode* n) {
if (n->args_.size() == 3) {
/* ip checksum */
emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htonl(");
n->args_[1]->accept(this);
emit("), bpf_htonl(");
n->args_[2]->accept(this);
emit(")))");
} else {
/* L4 checksum */
emit("(");
/* part of pseudo header */
n->args_[3]->accept(this);
emit(" ? ");
emit("((pkt->hw_csum == 1) ? ");
/* CHECKSUM_PARTIAL update pseudo only */
emit("bpf_ntohs(bpf_pseudo_csum_replace4(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htonl(");
n->args_[1]->accept(this);
emit("), bpf_htonl(");
n->args_[2]->accept(this);
emit(")))");
emit(" : ");
/* CHECKSUM_NONE update normally */
emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htonl(");
n->args_[1]->accept(this);
emit("), bpf_htonl(");
n->args_[2]->accept(this);
emit(")))");
emit(")");
emit(" : ");
/* not part of pseudo */
emit("((pkt->hw_csum != 1) ? ");
/* CHECKSUM_NONE updata normally */
emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
n->args_[0]->accept(this);
emit("), bpf_htonl(");
n->args_[1]->accept(this);
emit("), bpf_htonl(");
n->args_[2]->accept(this);
emit(")))");
emit(" : ");
/* CHECKSUM_PARTIAL no-op */
n->args_[0]->accept(this);
emit("))");
}
}
void CodegenC::emit_lb_hash(MethodCallExprNode* n) {
emit("pg_lb_hash(");
n->args_[0]->accept(this);
emit(", ");
n->args_[1]->accept(this);
emit(")");
}
void CodegenC::emit_sizeof(MethodCallExprNode* n) {
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
if (n->args_[0]->struct_type_->id_->name_ == "_Packet") {
emit("PG_SIZEOF(pkt)");
} else {
emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
}
} else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
emit("%zu", n->args_[0]->bit_width_ >> 3);
}
}
void CodegenC::emit_get_usec_time(MethodCallExprNode* n) {
emit("bpf_get_usec_time()");
}
void CodegenC::emit_forward_to_vnf(MethodCallExprNode*n) {
emitln("pkt->arg1 |= 1;");
emit("pkt->arg2 = ");
n->args_[0]->accept(this);
emitln(";");
emit("bpf_forward_to_plum(pkt, ");
n->args_[1]->accept(this);
emit(")");
}
void CodegenC::emit_forward_to_group(MethodCallExprNode *n) {
emit("pkt->arg2 = ");
n->args_[0]->accept(this);
emitln(";");
emitln("pkt->arg3 = pkt->plum_id;");
emit("bpf_forward_to_plum(pkt, ");
emit("1/*TUNNEL_PLUM_ID*/");
emit(")");
}
void CodegenC::visit_method_call_expr_node(MethodCallExprNode* n) {
free_instructions_.push_back(vector<string>());
if (!n->stmts_.empty()) {
++indent_;
emitln("{");
}
if (n->id_->sub_name_.size()) {
if (n->id_->sub_name_ == "lookup") {
emit_table_lookup(n);
} else if (n->id_->sub_name_ == "update") {
emit_table_update(n);
} else if (n->id_->sub_name_ == "delete") {
emit_table_delete(n);
} else if (n->id_->sub_name_ == "replicate" && n->id_->name_ == "pkt") {
emit_packet_replicate(n);
} else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
emit_packet_forward(n);
} else if (n->id_->sub_name_ == "forward_self" && n->id_->name_ == "pkt") {
emit_packet_forward_self(n);
} else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
emit_packet_push_header(n);
} else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
emit_packet_pop_header(n);
} else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
emit_packet_push_vlan(n);
} else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
emit_packet_pop_vlan(n);
} else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
emit_packet_rewrite_field(n);
} else if (n->id_->sub_name_ == "clone_forward" && n->id_->name_ == "pkt") {
emit_packet_clone_forward(n);
}
} else if (n->id_->name_ == "atomic_add") {
emit_atomic_add(n);
} else if (n->id_->name_ == "log") {
emit_log(n);
} else if (n->id_->name_ == "cksum") {
emit_cksum(n);
} else if (n->id_->name_ == "incr_cksum_u16") {
emit_incr_cksum_u16(n);
} else if (n->id_->name_ == "incr_cksum_u32") {
emit_incr_cksum_u32(n);
} else if (n->id_->name_ == "lb_hash") {
emit_lb_hash(n);
} else if (n->id_->name_ == "sizeof") {
emit_sizeof(n);
} else if (n->id_->name_ == "get_usec_time") {
emit_get_usec_time(n);
} else if (n->id_->name_ == "channel_push") {
emit_channel_push(n);
} else if (n->id_->name_ == "channel_push_generic") {
emit_channel_push_generic(n);
} else if (n->id_->name_ == "forward_to_vnf") {
emit_forward_to_vnf(n);
} else if (n->id_->name_ == "forward_to_group") {
emit_forward_to_group(n);
} else {
n->id_->accept(this);
emit("(");
for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
(*it)->accept(this);
if (it + 1 != n->args_.end()) {
emit(", ");
}
}
emit(")");
}
if (!n->stmts_.empty()) {
emit(";");
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
lnemit("");
(*it)->accept(this);
}
for (auto it = free_instructions_.back().rbegin(); it != free_instructions_.back().rend(); ++it) {
lnemit("%s;", it->c_str());
}
--indent_;
lnemit("}");
}
free_instructions_.pop_back();
}
/// on_match
void CodegenC::visit_match_decl_stmt_node(MatchDeclStmtNode* n) {
if (n->formals_.size() != 2)
throw CompilerException("on_match expected 2 arguments, %zu given", n->formals_.size());
StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
StructVariableDeclStmtNode* leaf_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(1).get());
if (!key_n || !leaf_n)
throw CompilerException("invalid parameter type");
++indent_;
emitln("if (%s_element) {", n->id_->c_str());
emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(), key_n->scope_id(),
key_n->id_->c_str(), n->id_->c_str());
emitln("%s* %s%s = %s_element;", leaf_n->struct_id_->c_str(), leaf_n->scope_id(),
leaf_n->id_->c_str(), n->id_->c_str());
n->block_->accept(this);
--indent_;
emitln("");
emit("}");
}
/// on_miss
void CodegenC::visit_miss_decl_stmt_node(MissDeclStmtNode* n) {
if (n->formals_.size() != 1)
throw CompilerException("on_match expected 1 argument, %zu given", n->formals_.size());
StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
++indent_;
emitln("if (!%s_element) {", n->id_->c_str());
emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
n->block_->accept(this);
--indent_;
emitln("");
emit("}");
}
void CodegenC::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) {
if (n->formals_.size() != 1)
throw CompilerException("on_failure expected 1 argument, %zu given", n->formals_.size());
StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
++indent_;
emitln("/*if ((unsigned long)%s_element >= (unsigned long)-4095) {", n->id_->name_.c_str());
emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
n->block_->accept(this);
--indent_;
emitln("");
emit("}*/");
}
void CodegenC::visit_expr_stmt_node(ExprStmtNode* n) {
emit_comment(n);
n->expr_->accept(this);
emit(";");
}
void CodegenC::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode* n) {
if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') {
return;
}
emit_comment(n);
if (n->struct_id_->scope_name_ == "proto") {
auto p = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true);
if (p) {
string var = n->scope_id() + n->id_->name_;
/* zero initialize array to be filled in with packet header */
emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;",
var.c_str(), ((p->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
auto asn = static_cast<AssignExprNode*>(it->get());
if (auto f = p->field(asn->id_->sub_name_)) {
size_t bit_offset = f->bit_offset_;
size_t bit_width = f->bit_width_;
if (asn->bitop_) {
bit_offset += f->bit_width_ - (asn->bitop_->bit_offset_ + asn->bitop_->bit_width_);
bit_width = std::min(bit_width - asn->bitop_->bit_offset_, asn->bitop_->bit_width_);
}
emit(" bpf_dins(%s + %zu, %zu, %zu, ", var.c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
asn->rhs_->accept(this);
emit(");");
}
}
}
} else {
/* all structs must be initialized with zeros, since they're alocated on stack,
* if struct doesn't have gaps between fields, gcc will be smart enough to avoid redundant zeroing */
if (n->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) {
emit("%s* %s%s = 0;", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
} else {
emit("%s %s%s = {};", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
if (!n->init_.empty()) {
for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
emit(" ");
(*it)->accept(this);
emit(";");
}
}
}
}
}
void CodegenC::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode* n) {
if (n->id_->name_ == "timer_delay" || n->id_->name_ == "parsed_bytes")
return;
emit_comment(n);
emit("%s %s%s", bits_to_uint(n->bit_width_), n->scope_id(), n->id_->c_str());
if (!n->scope_id_.empty())
emit(" = 0");
if (!n->init_.empty()) {
emit("; ");
n->init_[0]->accept(this);
}
emit(";");
}
void CodegenC::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
emit("typedef struct {\n");
++indent_;
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
indent();
(*it)->accept(this);
emit("\n");
}
--indent_;
indent();
emit("} __attribute__((aligned(4))) ");
emit("%s", n->id_->c_str());
}
void CodegenC::visit_parser_state_stmt_node(ParserStateStmtNode* n) {
string jump_label = n->scoped_name() + "_continue";
emit("%s: {", jump_label.c_str());
++indent_;
lnemit("PG_TRACE(%.14s);", jump_label.c_str());
if (n->next_state_) {
lnemit("");
n->next_state_->accept(this);
}
--indent_;
lnemit("}");
}
void CodegenC::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
auto scope = scopes_->current_state();
scopes_->set_current(n->scope_);
n->block_->accept(this);
scopes_->set_current(scope);
}
void CodegenC::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
if (!n->id_) {
return;
}
string jump_label = n->scoped_name();
++indent_;
emitln("JUMP_GUARD; %s: {", jump_label.c_str());
emitln("PG_TRACE(%.14s);", jump_label.c_str());
if (auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true)) {
emitln("%s = parsed_bytes; /* remember the offset of this header */", n->id_->c_str());
emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
//emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
}
// collect the protocols used in this state scope and declare them
set<string> protos;
for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) {
if (!it->scope_) {
continue;
}
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
for (auto it2 = scopes_->current_state()->obegin(); it2 != scopes_->current_state()->oend(); ++it2) {
if (proto_scopes_->top_struct()->lookup((*it2)->id_->name_, true)) {
protos.insert((*it2)->id_->name_);
}
for (auto it3 = (*it2)->subs_.begin(); it3 != (*it2)->subs_.end(); ++it3) {
if (proto_scopes_->top_struct()->lookup(it3->id_->name_, true)) {
protos.insert(it3->id_->name_);
}
}
}
scopes_->set_current(scope);
}
for (auto it = protos.begin(); it != protos.end(); ++it) {
emitln("uint32_t %s = 0; /* header offset */", it->c_str());
}
auto it = n->subs_.begin();
if (n->subs_.size() == 1 && it->id_->name_ == "") {
// this is not a multistate protocol, emit everything and finish
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
it->block_->accept(this);
if (n->parser_) {
emitln("");
n->parser_->accept(this);
}
scopes_->set_current(scope);
} else {
if (n->parser_) {
for (auto it2 = n->subs_.begin(); it2 != n->subs_.end(); ++it2) {
proto_rewrites_[it2->id_->full_name()] = n->scoped_name() + "_" + it2->id_->name_;
}
n->parser_->accept(this);
proto_rewrites_.clear();
emitln("");
}
for (; it != n->subs_.end(); ++it) {
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
string jump_label = n->scoped_name() + "_" + it->id_->name_;
++indent_;
emitln("JUMP_GUARD; %s: {", jump_label.c_str());
emitln("PG_TRACE(%.14s);", jump_label.c_str());
if (auto p = proto_scopes_->top_struct()->lookup(it->id_->name_, true)) {
emitln("%s = pkt->offset + parsed_bytes; /* remember the offset of this header */", it->id_->c_str());
emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
}
it->block_->accept(this);
if (it->parser_) {
emitln("");
it->parser_->accept(this);
}
--indent_;
emitln("");
emitln("}");
scopes_->set_current(scope);
}
}
--indent_;
emitln("");
emit("}");
}
void CodegenC::visit_table_decl_stmt_node(TableDeclStmtNode* n) {
if (n->table_type_->name_ == "Table"
|| n->table_type_->name_ == "SharedTable") {
if (n->templates_.size() != 4)
throw CompilerException("%s expected 4 arguments, %zu given", n->table_type_->c_str(), n->templates_.size());
const char *key_type = n->key_id()->c_str();
const char *leaf_type = n->leaf_id()->c_str();
char buf[128];
if (n->type_id()->name_ == "FIXED_MATCH" || n->type_id()->name_ == "INDEXED") {
//emitln("struct %s_Element {", n->id_->c_str());
//emitln(" PG_HASH_TABLE_ELEMENT_COMMON");
//emitln(" %s key;", key_type);
//emitln(" %s leaf;", leaf_type);
//emitln("} __attribute__((aligned(8)));");
//emitln("static struct PGHashTable %s;", n->id_->c_str());
//emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_);
//emitln("PG_HASH_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s)",
// table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str());
emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size());
snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_HASH, sizeof(%s), sizeof(%s), %zd, 0}, // %s",
table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_, n->id_->c_str());
} else if (n->type_id()->name_ == "LPM") {
//emitln("struct %s_Element {", n->id_->c_str());
//emitln(" PG_LPM_TABLE_ELEMENT_COMMON");
//emitln(" %s key;", key_type);
//emitln(" %s leaf;", leaf_type);
//emitln("} __attribute__((aligned(8)));");
//emitln("static struct PGLpmTable %s;", n->id_->c_str());
//emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_);
//emitln("PG_LPM_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s, %u)",
// table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str(),
// n->key_id()->bit_width_);
emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size());
snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_LPM, sizeof(%s), sizeof(%s), %zd, %zd}, // %s",
table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_,
n->key_id()->bit_width_, n->id_->c_str());
} else {
throw CompilerException("table type \"%s\" unknown", n->type_id()->c_str());
}
//table_inits_.push_back(n->id_->name_);
table_inits_.push_back(buf);
}
}
int CodegenC::visit(Node* root) {
BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
scopes_->set_current(scopes_->top_state());
scopes_->set_current(scopes_->top_var());
print_header();
b->ver_.accept(this);
for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) {
(*it)->accept(this);
emit("\n");
}
print_parser();
print_footer();
return 0;
}
void CodegenC::print_timer() {
// visit timers
++indent_;
emitln("PG_PARSE_DECL(timer) {");
emitln("uint32_t timer_delay = 0;");
// visit function scoped variables
for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
(*it)->accept(this);
emitln("");
}
for (auto it = scopes_->top_timer()->obegin(); it != scopes_->top_timer()->oend(); ++it) {
(*it)->accept(this);
emitln("");
}
++indent_;
emitln("DONE: {");
emitln("PG_TRACE(DONE);");
emitln("pg_timer_forward(pkt, timer_delay);");
--indent_;
emitln("return;");
emitln("}");
++indent_;
emitln("ERROR: {");
emitln("PG_TRACE(ERROR);");
emitln("pg_drop(pkt);");
emitln("pg_timer_forward(pkt, timer_delay);");
--indent_;
emitln("return;");
--indent_;
emitln("}");
emitln("}");
}
void CodegenC::print_parser() {
++indent_;
emitln("PG_PARSE_DECL(parse) {");
/* emitln("uint8_t *pp;"); */
emitln("uint32_t parsed_bytes = 0;");
emitln("uint16_t orig_offset = 0;/*pkt->offset;*/");
// visit function scoped variables
for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
(*it)->accept(this);
emitln("");
}
for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
if (proto_scopes_->top_struct()->lookup((*it)->id_->name_, true)) {
emitln("uint32_t %s = 0; /* header offset */", (*it)->id_->c_str());
}
}
/* emitln("pp = pkt->start + pkt->offset;"); */
emitln("goto s1_INIT;");
// finally, visit the states
for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
(*it)->accept(this);
emitln("");
}
++indent_;
emitln("ERROR: {");
emitln("PG_TRACE(ERROR);");
--indent_;
emitln("goto CLEANUP;");
emitln("}");
++indent_;
emitln("DONE: {");
emitln("PG_TRACE(DONE);");
--indent_;
emitln("goto CLEANUP;");
emitln("}");
++indent_;
emitln("CLEANUP: {");
--indent_;
emitln("/* cleanup is done by PE */;");
--indent_;
emitln("}");
emitln("}");
//print_timer();
}
void CodegenC::print_header() {
if (use_pre_header_) {
//emit("%s", PRE_HEADER.c_str());
emitln("");
} else {
emitln("#include <stdint.h>");
emitln("#include \"../dp/linux/filter.h\"");
emitln("#include \"container/pg_api.h\"");
emitln("#include \"container/pg_defs.h\"");
}
emitln("#define JUMP_GUARD goto DONE");
emitln("#define PG_SIZEOF(_pkt) ((int)_pkt->length - (int)pkt->offset + orig_offset)");
int i = 0;
// declare structures
for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) {
if ((*it)->id_->name_ == "_Packet")
continue;
(*it)->accept(this);
emit(";\n");
emitln("#define STRUCTID_%s %d", (*it)->id_->c_str(), i++);
}
emitln("#define STRUCTID_generic %d", i);
}
void CodegenC::print_footer() {
//emitln("#define EXPAND_TABLES(E) \\");
emitln("struct bpf_table plum_tables[] = {");
for (auto it = table_inits_.begin(); it != table_inits_.end(); ++it) {
//emit("E(%s) ", it->c_str());
emitln(" %s", it->c_str());
}
emitln(" {0,0,0,0,0,0} // last table marker");
emitln("};");
emitln("");
emitln("PG_INIT");
emitln("PG_CLEANUP");
}
} // namespace cc
} // namespace ebpf
/*
* =====================================================================
* Copyright (c) 2012, PLUMgrid, http://plumgrid.com
*
* This source is subject to the PLUMgrid License.
* All rights reserved.
*
* THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
* PLUMgrid confidential information, delete if you are not the
* intended recipient.
*
* =====================================================================
*/
#pragma once
#include <stdio.h>
#include <vector>
#include <string>
#include <set>
#include "cc/node.h"
#include "cc/scope.h"
namespace ebpf {
namespace cc {
using std::vector;
using std::string;
using std::set;
class CodegenC : public Visitor {
public:
CodegenC(FILE* out, Scopes::Ptr scopes, Scopes::Ptr proto_scopes, bool use_pre_header)
: out_(out), indent_(0), tmp_reg_index_(0), scopes_(scopes),
proto_scopes_(proto_scopes), use_pre_header_(use_pre_header) {}
#define VISIT(type, func) virtual void visit_##func(type* n);
EXPAND_NODES(VISIT)
#undef VISIT
virtual int visit(Node* n);
void emit_table_lookup(MethodCallExprNode* n);
void emit_table_update(MethodCallExprNode* n);
void emit_table_delete(MethodCallExprNode* n);
void emit_channel_push(MethodCallExprNode* n);
void emit_channel_push_generic(MethodCallExprNode* n);
void emit_log(MethodCallExprNode* n);
void emit_packet_forward(MethodCallExprNode* n);
void emit_packet_replicate(MethodCallExprNode* n);
void emit_packet_clone_forward(MethodCallExprNode* n);
void emit_packet_forward_self(MethodCallExprNode* n);
void emit_packet_drop(MethodCallExprNode* n);
void emit_packet_broadcast(MethodCallExprNode* n);
void emit_packet_multicast(MethodCallExprNode* n);
void emit_packet_push_header(MethodCallExprNode* n);
void emit_packet_pop_header(MethodCallExprNode* n);
void emit_packet_push_vlan(MethodCallExprNode* n);
void emit_packet_pop_vlan(MethodCallExprNode* n);
void emit_packet_rewrite_field(MethodCallExprNode* n);
void emit_atomic_add(MethodCallExprNode* n);
void emit_cksum(MethodCallExprNode* n);
void emit_incr_cksum_u16(MethodCallExprNode* n);
void emit_incr_cksum_u32(MethodCallExprNode* n);
void emit_lb_hash(MethodCallExprNode* n);
void emit_sizeof(MethodCallExprNode* n);
void emit_get_usec_time(MethodCallExprNode* n);
void emit_forward_to_vnf(MethodCallExprNode* n);
void emit_forward_to_group(MethodCallExprNode* n);
void print_parser();
void print_timer();
void print_header();
void print_footer();
private:
void indent();
template <typename... Args> void emitln(const char *fmt, Args&&... params);
template <typename... Args> void lnemit(const char *fmt, Args&&... params);
template <typename... Args> void emit(const char *fmt, Args&&... params);
void emitln(const char *s);
void lnemit(const char *s);
void emit(const char *s);
void emit_comment(Node* n);
FILE* out_;
int indent_;
int tmp_reg_index_;
Scopes::Ptr scopes_;
Scopes::Ptr proto_scopes_;
bool use_pre_header_;
vector<vector<string> > free_instructions_;
vector<string> table_inits_;
map<string, string> proto_rewrites_;
};
} // namespace cc
} // namespace ebpf
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
......
import ctypes as ct
import os
import pyroute2 as pr
lib = ct.cdll.LoadLibrary("libbpfprog.so")
......@@ -35,35 +34,45 @@ lib.bpf_attach_filter.argtypes = [ct.c_int, ct.c_char_p, ct.c_uint, ct.c_ubyte,
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]
lib.bpf_attach_kprobe.restype = ct.c_int
lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_int, ct.c_int, ct.c_int]
class BPF(object):
BPF_PROG_TYPE_SOCKET_FILTER = 1
BPF_PROG_TYPE_SCHED_CLS = 2
BPF_PROG_TYPE_SCHED_ACT = 3
BPF_PROG_TYPE_KPROBE = 2
BPF_PROG_TYPE_SCHED_CLS = 3
BPF_PROG_TYPE_SCHED_ACT = 4
def __init__(self, name, dp_file, dph_file,
prog_type=BPF_PROG_TYPE_SOCKET_FILTER,
debug=0):
self.debug = debug
self.name = name
self.prog_type = prog_type
self.fd = {}
self.prog = lib.bpf_program_create(dp_file.encode("ascii"),
dph_file.encode("ascii"), self.debug)
if self.prog == ct.c_void_p(None):
if self.prog == None:
raise Exception("Failed to compile BPF program %s" % dp_file)
if lib.bpf_program_start(self.prog,
self.name.encode("ascii")) == ct.c_void_p(None):
if prog_type == BPF.BPF_PROG_TYPE_KPROBE:
return
self.load(self.name)
def load(self, prog_name):
if lib.bpf_program_start(self.prog, prog_name.encode("ascii")) == None:
raise Exception("Unknown program %s" % self.name)
self.fd = lib.bpf_prog_load(prog_type,
lib.bpf_program_start(self.prog, self.name.encode("ascii")),
lib.bpf_program_size(self.prog, self.name.encode("ascii")),
self.fd[prog_name] = lib.bpf_prog_load(self.prog_type,
lib.bpf_program_start(self.prog, prog_name.encode("ascii")),
lib.bpf_program_size(self.prog, prog_name.encode("ascii")),
lib.bpf_program_license(self.prog))
if self.fd < 0:
if self.fd[prog_name] < 0:
print((ct.c_char * 65536).in_dll(lib, "bpf_log_buf").value)
#print(ct.c_char_p.in_dll(lib, "bpf_log_buf").value)
raise Exception("Failed to load BPF program %s" % dp_file)
raise Exception("Failed to load BPF program %s" % self.name)
class Table(object):
def __init__(self, bpf, map_fd, keytype, leaftype):
......@@ -126,20 +135,39 @@ class BPF(object):
raise Exception("Failed to find BPF Table %s" % name)
return BPF.Table(self, map_fd, keytype, leaftype)
def attach(self, dev):
def attach(self, dev, prog_name=None):
prog_name = prog_name or self.name
self.sock = lib.bpf_open_raw_sock(dev.encode("ascii"))
if self.sock < 0:
errstr = os.strerror(ct.get_errno())
raise Exception("Failed to open raw device %s: %s" % (dev, errstr))
res = lib.bpf_attach_socket(self.sock, self.fd)
res = lib.bpf_attach_socket(self.sock, self.fd[prog_name])
if res < 0:
errstr = os.strerror(ct.get_errno())
raise Exception("Failed to attach BPF to device %s: %s"
% (dev, errstr))
def attach_filter(self, ifindex, prio, classid):
res = lib.bpf_attach_filter(self.fd, self.name.encode("ascii"), ifindex, prio, classid)
def attach_filter(self, ifindex, prio, classid, prog_name=None):
prog_name = prog_name or self.name
res = lib.bpf_attach_filter(self.fd[prog_name], self.name.encode("ascii"), ifindex, prio, classid)
if res < 0:
raise Exception("Failed to filter with BPF")
def attach_kprobe(self, event, prog_name, pid=-1, cpu=0, group_fd=-1):
ev_name = "p_" + event.replace("+", "_")
desc = "p:kprobes/%s %s" % (ev_name, event)
res = lib.bpf_attach_kprobe(self.fd[prog_name], ev_name.encode("ascii"),
desc.encode("ascii"), pid, cpu, group_fd)
if res < 0:
raise Exception("Failed to attach BPF to kprobe")
return res
def attach_kretprobe(self, event, prog_name, pid=-1, cpu=0, group_fd=-1):
ev_name = "r_" + event.replace("+", "_")
desc = "r:kprobes/%s %s" % (ev_name, event)
res = lib.bpf_attach_kprobe(self.fd[prog_name], ev_name.encode("ascii"),
desc.encode("ascii"), pid, cpu, group_fd)
if res < 0:
raise Exception("Failed to attach BPF to kprobe")
return res
......@@ -10,9 +10,9 @@ ADD_FLEX_BISON_DEPENDENCY(Lexer Parser)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bitops.bc
COMMAND ${CLANG}
ARGS -O3 -emit-llvm -S -o bitops.bc -I${CMAKE_SOURCE_DIR}/jit/compat/include
ARGS -O3 -emit-llvm -o bitops.bc -I${CMAKE_SOURCE_DIR}/jit/compat/include
-c ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c ${CMAKE_CURRENT_SOURCE_DIR}/bpf_helpers.h
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating bitops IR")
add_custom_command(
......
......@@ -15,6 +15,12 @@ static int (*bpf_map_update_elem)(void *map, void *key, void *value,
(void *) BPF_FUNC_map_update_elem;
static int (*bpf_map_delete_elem)(void *map, void *key) =
(void *) BPF_FUNC_map_delete_elem;
static int (*bpf_probe_read)(void *dst, unsigned long long size, void *unsafe_ptr) =
(void *) BPF_FUNC_probe_read;
static unsigned long long (*bpf_ktime_get_ns)(void) =
(void *) BPF_FUNC_ktime_get_ns;
static int (*bpf_trace_printk)(const char *fmt, unsigned long long fmt_size, ...) =
(void *) BPF_FUNC_trace_printk;
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
......
......@@ -85,11 +85,17 @@ int BPFProgram::parse() {
proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename_);
rc = proto_parser_->parse();
if (rc) return rc;
if (rc) {
fprintf(stderr, "In file: %s\n", filename_.c_str());
return rc;
}
parser_ = make_unique<ebpf::cc::Parser>(filename_);
rc = parser_->parse();
if (rc) return rc;
if (rc) {
fprintf(stderr, "In file: %s\n", filename_.c_str());
return rc;
}
//ebpf::cc::Printer printer(stderr);
//printer.visit(parser_->root_node_);
......@@ -101,10 +107,7 @@ int BPFProgram::parse() {
exit(1);
}
codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(),
proto_parser_->scopes_.get(),
/*use_pre_header*/false,
parser_->pragma("name"));
codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(), proto_parser_->scopes_.get());
ret = codegen_->visit(parser_->root_node_);
if (get<0>(ret) != 0 || get<1>(ret).size()) {
fprintf(stderr, "Codegen error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
......@@ -182,7 +185,7 @@ int BPFProgram::finalize() {
}
uint8_t * BPFProgram::start(const string &name) const {
auto section = sections_.find(name);
auto section = sections_.find("." + name);
if (section == sections_.end())
return nullptr;
......@@ -190,7 +193,7 @@ uint8_t * BPFProgram::start(const string &name) const {
}
size_t BPFProgram::size(const string &name) const {
auto section = sections_.find(name);
auto section = sections_.find("." + name);
if (section == sections_.end())
return 0;
......
......@@ -44,8 +44,6 @@
extern "C"
int bpf_create_map(int map_type, int key_size, int value_size, int max_entries);
#define ENABLE_RELOCATIONS 0
namespace ebpf {
namespace cc {
......@@ -64,6 +62,7 @@ using std::vector;
// parameters), so cast it instead :(
#define B (*((IRBuilder<> *)this->b_))
// Helper class to push/pop the insert block
class BlockStack {
public:
explicit BlockStack(CodegenLLVM *cc, BasicBlock *bb)
......@@ -81,6 +80,7 @@ class BlockStack {
CodegenLLVM *cc_;
};
// Helper class to push/pop switch statement insert block
class SwitchStack {
public:
explicit SwitchStack(CodegenLLVM *cc, SwitchInst *sw)
......@@ -95,29 +95,15 @@ class SwitchStack {
CodegenLLVM *cc_;
};
CodegenLLVM::CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes,
bool use_pre_header, const string &section)
CodegenLLVM::CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes)
: out_(stdout), mod_(mod), indent_(0), tmp_reg_index_(0), scopes_(scopes),
proto_scopes_(proto_scopes), use_pre_header_(use_pre_header),
section_(section), expr_(nullptr) {
proto_scopes_(proto_scopes), expr_(nullptr) {
b_ = new IRBuilder<>(ctx());
}
CodegenLLVM::~CodegenLLVM() {
delete b_;
}
template <typename... Args>
void CodegenLLVM::emitln(const char *fmt, Args&&... params) {
//fprintf(out_, fmt, std::forward<Args>(params)...);
//fprintf(out_, "\n%*s", indent_ * 2, "");
//fflush(out_);
}
void CodegenLLVM::emitln(const char *s) {
//fprintf(out_, "%s", s);
//fprintf(out_, "\n%*s", indent_ * 2, "");
//fflush(out_);
}
template <typename... Args>
void CodegenLLVM::emit(const char *fmt, Args&&... params) {
//fprintf(out_, fmt, std::forward<Args>(params)...);
......@@ -128,52 +114,20 @@ void CodegenLLVM::emit(const char *s) {
//fflush(out_);
}
template <typename... Args>
void CodegenLLVM::lnemit(const char *fmt, Args&&... params) {
//fprintf(out_, "\n%*s", indent_ * 2, "");
//fprintf(out_, fmt, std::forward<Args>(params)...);
//fflush(out_);
}
void CodegenLLVM::lnemit(const char *s) {
//fprintf(out_, "\n%*s", indent_ * 2, "");
//fprintf(out_, "%s", s);
//fflush(out_);
}
void CodegenLLVM::indent() {
//fprintf(out_, "%*s", indent_ * 2, "");
//fflush(out_);
}
void CodegenLLVM::emit_comment(Node *n) {
// if (!n->text_.empty()) {
// emitln("/* %s */", n->text_.c_str());
// }
}
StatusTuple CodegenLLVM::visit_block_stmt_node(BlockStmtNode *n) {
// enter scope
auto scope = scopes_->current_var();
if (n->scope_) {
scopes_->set_current(n->scope_);
}
if (n->scope_)
scopes_->push_var(n->scope_);
if (!n->stmts_.empty()) {
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it)
TRY2((*it)->accept(this));
}
// exit scope
scopes_->set_current(scope);
return mkstatus(0);
}
if (n->scope_)
scopes_->pop_var();
StatusTuple CodegenLLVM::visit_version_stmt_node(VersionStmtNode *n) {
uint32_t version;
version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
emit("static const uint32_t plumlet_version __attribute__"
"((section (\".version\"), used)) = 0x%x;\n", version);
return mkstatus(0);
}
......@@ -184,11 +138,12 @@ StatusTuple CodegenLLVM::visit_if_stmt_node(IfStmtNode *n) {
BasicBlock *label_end = BasicBlock::Create(ctx(), "if.end", parent);
TRY2(n->cond_->accept(this));
Value *is_not_null = B.CreateIsNotNull(pop_expr());
if (n->false_block_)
B.CreateCondBr(pop_expr(), label_then, label_else);
B.CreateCondBr(is_not_null, label_then, label_else);
else
B.CreateCondBr(pop_expr(), label_then, label_end);
B.CreateCondBr(is_not_null, label_then, label_end);
{
BlockStack bstack(this, label_then);
......@@ -256,8 +211,10 @@ StatusTuple CodegenLLVM::visit_switch_stmt_node(SwitchStmtNode *n) {
TRY2(n->block_->accept(this));
}
// if other cases are terminal, erase the end label
if (pred_empty(label_end))
if (pred_empty(label_end)) {
B.SetInsertPoint(resolve_label("DONE"));
label_end->eraseFromParent();
}
return mkstatus(0);
}
......@@ -320,11 +277,9 @@ StatusTuple CodegenLLVM::visit_ident_expr_node(IdentExprNode *n) {
}
}
} else {
emit("*%s%s", n->decl_->scope_id(), n->c_str());
auto it = vars_.find(n->decl_);
if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str());
LoadInst *load_1 = B.CreateAlignedLoad(it->second, 4);
expr_ = load_1;
expr_ = n->is_lhs() ? it->second : (Value *)B.CreateLoad(it->second);
}
} else {
if (n->sub_name_.size()) {
......@@ -377,11 +332,13 @@ StatusTuple CodegenLLVM::visit_assign_expr_node(AssignExprNode *n) {
emit(")");
return mkstatus_(n, "unsupported");
} else {
TRY2(n->rhs_->accept(this));
Value *rhs = pop_expr();
TRY2(n->id_->accept(this));
Value *lhs = pop_expr();
TRY2(n->rhs_->accept(this));
expr_ = B.CreateIntCast(expr_, cast<PointerType>(lhs->getType())->getElementType(), false);
B.CreateStore(pop_expr(), lhs);
if (!n->rhs_->is_ref())
rhs = B.CreateIntCast(rhs, cast<PointerType>(lhs->getType())->getElementType(), false);
B.CreateStore(rhs, lhs);
}
}
return mkstatus(0);
......@@ -389,7 +346,7 @@ StatusTuple CodegenLLVM::visit_assign_expr_node(AssignExprNode *n) {
StatusTuple CodegenLLVM::lookup_var(Node *n, const string &name, Scopes::VarScope *scope,
VariableDeclStmtNode **decl, Value **mem) const {
*decl = scope->lookup(name, false);
*decl = scope->lookup(name, SCOPE_GLOBAL);
if (!*decl) return mkstatus_(n, "cannot find %s variable", name.c_str());
auto it = vars_.find(*decl);
if (it == vars_.end()) return mkstatus_(n, "unable to find %s memory location", name.c_str());
......@@ -401,7 +358,7 @@ StatusTuple CodegenLLVM::visit_packet_expr_node(PacketExprNode *n) {
auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
VariableDeclStmtNode *offset_decl, *skb_decl;
Value *offset_mem, *skb_mem;
TRY2(lookup_var(n, "skb", scopes_->top_var(), &skb_decl, &skb_mem));
TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem));
TRY2(lookup_var(n, "$" + n->id_->name_, scopes_->current_var(), &offset_decl, &offset_mem));
if (p) {
......@@ -458,6 +415,17 @@ StatusTuple CodegenLLVM::visit_integer_expr_node(IntegerExprNode *n) {
return mkstatus(0);
}
StatusTuple CodegenLLVM::visit_string_expr_node(StringExprNode *n) {
if (n->is_lhs()) return mkstatus_(n, "cannot assign to a string");
Value *global = B.CreateGlobalString(n->val_);
Value *ptr = new AllocaInst(B.getInt8Ty(), B.getInt64(n->val_.size() + 1), "", resolve_entry_stack());
B.CreateMemCpy(ptr, global, n->val_.size() + 1, 1);
expr_ = ptr;
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_short_circuit_and(BinopExprNode *n) {
Function *parent = B.GetInsertBlock()->getParent();
BasicBlock *label_start = B.GetInsertBlock();
......@@ -586,7 +554,8 @@ StatusTuple CodegenLLVM::visit_goto_expr_node(GotoExprNode *n) {
StatusTuple CodegenLLVM::visit_return_expr_node(ReturnExprNode *n) {
TRY2(n->expr_->accept(this));
Value *cast_1 = B.CreateIntCast(pop_expr(), cast<PointerType>(retval_->getType())->getElementType(), true);
Function *parent = B.GetInsertBlock()->getParent();
Value *cast_1 = B.CreateIntCast(pop_expr(), parent->getReturnType(), true);
B.CreateStore(cast_1, retval_);
B.CreateBr(resolve_label("DONE"));
return mkstatus(0);
......@@ -595,7 +564,6 @@ StatusTuple CodegenLLVM::visit_return_expr_node(ReturnExprNode *n) {
StatusTuple CodegenLLVM::emit_table_lookup(MethodCallExprNode *n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
stringstream free_inst;
IdentExprNode* arg1;
StructVariableDeclStmtNode* arg1_type;
......@@ -639,7 +607,6 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
IdentExprNode* arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
IdentExprNode* type0 = table->templates_.at(0).get();
auto table_fd_it = table_fds_.find(table);
if (table_fd_it == table_fds_.end())
......@@ -653,20 +620,16 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
B.getInt64(table_fd_it->second));
Value *pseudo_map_fd = pseudo_call;
emit("%s* %s_ukey = &", type0->c_str(), n->id_->c_str());
TRY2(arg0->accept(this));
Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
emitln(";");
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
emit("bpf_table_update(pkt, TABLE_ID_%s, %s_ukey", n->id_->c_str(), n->id_->c_str());
emit(", &");
TRY2(arg1->accept(this));
Value *value_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
expr_ = B.CreateCall4(update_fn, pseudo_map_fd, key_ptr, value_ptr, B.getInt64(BPF_ANY));
emitln(");");
} else if (table->type_id()->name_ == "LPM") {
} else {
return mkstatus_(n, "unsupported");
}
return mkstatus(0);
}
......@@ -674,129 +637,48 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
StatusTuple CodegenLLVM::emit_table_delete(MethodCallExprNode *n) {
TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
IdentExprNode* type0 = table->templates_.at(0).get();
emit("%s* %s_dkey = &", type0->c_str(), n->id_->c_str());
TRY2(arg0->accept(this));
emitln(";");
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
emit("bpf_table_delete(pkt, TABLE_ID_%s, %s_dkey", n->id_->c_str(), n->id_->c_str());
emitln(");");
} else if (table->type_id()->name_ == "LPM") {
}
return mkstatus(0);
}
auto table_fd_it = table_fds_.find(table);
if (table_fd_it == table_fds_.end())
return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str());
Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist");
Function *update_fn = mod_->getFunction("bpf_map_update_elem_");
if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined");
StatusTuple CodegenLLVM::emit_channel_push_generic(MethodCallExprNode *n) {
/* computation of orig_length of packet:
* orig_lenth = pkt->length - (orig_offset - pkt->offset)
* push_header(N) does pkt->length += N; pkt->offset -= N;
* pop_header(N) does pg_may_access(N); pkt->length -=N; pkt->offset +=N;
*
* therefore push_header(); pop_header(); sequence is currently broken, ticket #930
*/
emit("bpf_channel_push_packet(pkt");
emit(")");
return mkstatus(0);
}
CallInst *pseudo_call = B.CreateCall2(pseudo_fn, B.getInt64(BPF_PSEUDO_MAP_FD),
B.getInt64(table_fd_it->second));
Value *pseudo_map_fd = pseudo_call;
StatusTuple CodegenLLVM::emit_channel_push(MethodCallExprNode *n) {
IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
StructVariableDeclStmtNode* arg0_type = static_cast<StructVariableDeclStmtNode*>(arg0->decl_);
emit("bpf_channel_push_struct(pkt, STRUCTID_%s, &", arg0_type->struct_id_->c_str());
TRY2(arg0->accept(this));
emit(", sizeof(");
TRY2(arg0->accept(this));
emit("))");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_log(MethodCallExprNode *n) {
emitln("{ if (unlikely(pkt->capture)) {");
emit(" bpf_capture(pkt, BPF_CAP_LOG, %d, ", n->line_);
TRY2(n->args_[0]->accept(this));
emit("); } }");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_forward(MethodCallExprNode *n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_forward(pkt, ");
TRY2(n->args_[0]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_replicate(MethodCallExprNode*n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_replicate(pkt, ");
TRY2(n->args_[0]->accept(this));
emit(",", n->id_->c_str());
TRY2(n->args_[1]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_clone_forward(MethodCallExprNode *n) {
emitln("pkt->arg1 &= ~1;");
emit("bpf_clone_forward(pkt, ");
TRY2(n->args_[0]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_forward_self(MethodCallExprNode *n) {
emit("bpf_forward_self(pkt, ");
TRY2(n->args_[0]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_drop(MethodCallExprNode *n) {
emit("bpf_drop(pkt)");
return mkstatus(0);
}
Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
StatusTuple CodegenLLVM::emit_packet_push_header(MethodCallExprNode *n) {
emit("if (unlikely(bpf_push_header(pkt, ");
TRY2(n->args_[0]->accept(this));
if (n->args_.size() == 1) {
emit(", %zu, 0) != 0)) goto ERROR", n->args_[0]->struct_type_->bit_width_ >> 3);
if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
expr_ = B.CreateCall2(update_fn, pseudo_map_fd, key_ptr);
} else {
emit(", %zu, ", n->args_[0]->struct_type_->bit_width_ >> 3);
TRY2(n->args_[1]->accept(this));
emit(") != 0)) goto ERROR");
return mkstatus_(n, "unsupported");
}
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_pop_header(MethodCallExprNode *n) {
emit("if (unlikely(bpf_pop_header(pkt, ");
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
} else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
TRY2(n->args_[0]->accept(this));
StatusTuple CodegenLLVM::emit_log(MethodCallExprNode *n) {
vector<Value *> args;
auto arg = n->args_.begin();
TRY2((*arg)->accept(this));
args.push_back(pop_expr());
args.push_back(B.getInt64(((*arg)->bit_width_ >> 3) + 1));
++arg;
for (; arg != n->args_.end(); ++arg) {
TRY2((*arg)->accept(this));
args.push_back(pop_expr());
}
emit(", 0/*todo*/) != 0)) goto ERROR");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_push_vlan(MethodCallExprNode *n) {
emit("if (unlikely(bpf_push_vlan(pkt, bpf_htons(0x8100/*ETH_P_8021Q*/), ");
TRY2(n->args_[0]->accept(this));
emit(") != 0)) goto ERROR");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_packet_pop_vlan(MethodCallExprNode *n) {
emit("if (unlikely(bpf_pop_vlan(pkt) != 0)) goto ERROR");
return mkstatus(0);
}
// int bpf_trace_printk(fmt, sizeof(fmt), ...)
FunctionType *printk_fn_type = FunctionType::get(B.getInt32Ty(), vector<Type *>({B.getInt8PtrTy(), B.getInt64Ty()}), true);
Value *printk_fn = B.CreateIntToPtr(B.getInt64(BPF_FUNC_trace_printk),
PointerType::getUnqual(printk_fn_type));
StatusTuple CodegenLLVM::emit_packet_rewrite_field(MethodCallExprNode *n) {
TRY2(n->args_[1]->accept(this));
TRY2(n->args_[0]->accept(this));
emit(")");
expr_ = B.CreateCall(printk_fn, args);
return mkstatus(0);
}
......@@ -810,32 +692,6 @@ StatusTuple CodegenLLVM::emit_atomic_add(MethodCallExprNode *n) {
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_cksum(MethodCallExprNode *n) {
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
auto v = n->args_[0]->struct_type_;
size_t bit_width = v->bit_width_ >> 3;
auto p = proto_scopes_->top_struct()->lookup(v->id_->name_, true);
if (p) {
/* should we do store_half directly? */
if (!n->args_[0]->flags_[ExprNode::PROTO]) {
emit("bpf_ntohs(bpf_checksum_pkt(pkt, %s, %zu))", v->id_->c_str(), bit_width);
} else {
emit("bpf_ntohs(bpf_checksum(");
TRY2(n->args_[0]->accept(this));
emit(", %zu))", bit_width);
}
} else {
return mkstatus_(n, "cannot pg_cksum %d", n->args_[0]->typeof_);
}
/** emit("pg_cksum(");
TRY2(n->args_[0]->accept(this));
emit(", %zu)", n->args_[0]->struct_type_->bit_width_ >> 3);**/
} else {
return mkstatus_(n, "cannot pg_cksum %d", n->args_[0]->typeof_);
}
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_incr_cksum(MethodCallExprNode *n, size_t sz) {
Value *is_pseudo;
string csum_fn_str;
......@@ -865,132 +721,19 @@ StatusTuple CodegenLLVM::emit_incr_cksum(MethodCallExprNode *n, size_t sz) {
VariableDeclStmtNode *skb_decl;
Value *skb_mem;
TRY2(lookup_var(n, "skb", scopes_->top_var(), &skb_decl, &skb_mem));
TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem));
LoadInst *skb_ptr = B.CreateLoad(skb_mem);
Value *skb_ptr8 = B.CreateBitCast(skb_ptr, B.getInt8PtrTy());
expr_ = B.CreateCall5(csum_fn, skb_ptr8, offset, old_val, new_val, flags);
// if (n->args_.size() == 3) {
// /* ip checksum */
// emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
// TRY2(n->args_[0]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[1]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[2]->accept(this));
// emit(")))");
// } else {
// /* L4 checksum */
// emit("(");
// /* part of pseudo header */
// TRY2(n->args_[3]->accept(this));
// emit(" ? ");
// emit("((pkt->hw_csum == 1) ? ");
// /* CHECKSUM_PARTIAL update pseudo only */
// emit("bpf_ntohs(bpf_pseudo_csum_replace4(bpf_htons(");
// TRY2(n->args_[0]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[1]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[2]->accept(this));
// emit(")))");
// emit(" : ");
// /* CHECKSUM_NONE update normally */
// emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
// TRY2(n->args_[0]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[1]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[2]->accept(this));
// emit(")))");
// emit(")");
// emit(" : ");
// /* not part of pseudo */
// emit("((pkt->hw_csum != 1) ? ");
// /* CHECKSUM_NONE updata normally */
// emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
// TRY2(n->args_[0]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[1]->accept(this));
// emit("), bpf_htonl(");
// TRY2(n->args_[2]->accept(this));
// emit(")))");
// emit(" : ");
// /* CHECKSUM_PARTIAL no-op */
// TRY2(n->args_[0]->accept(this));
// emit("))");
// }
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_lb_hash(MethodCallExprNode *n) {
emit("pg_lb_hash(");
TRY2(n->args_[0]->accept(this));
emit(", ");
TRY2(n->args_[1]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_sizeof(MethodCallExprNode *n) {
if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
if (n->args_[0]->struct_type_->id_->name_ == "_Packet") {
//emit("PG_SIZEOF(pkt)");
emit("(int)pkt->length");
} else {
emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
expr_ = B.getInt64(n->args_[0]->struct_type_->bit_width_ >> 3);
}
} else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
if (n->args_[0]->struct_type_) {
expr_ = B.getInt64(n->args_[0]->struct_type_->bit_width_ >> 3);
} else {
emit("%zu", n->args_[0]->bit_width_ >> 3);
expr_ = B.getInt64(n->args_[0]->bit_width_ >> 3);
}
}
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_get_usec_time(MethodCallExprNode *n) {
emit("bpf_get_usec_time()");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_forward_to_vnf(MethodCallExprNode*n) {
emitln("pkt->arg1 |= 1;");
emit("pkt->arg2 = ");
TRY2(n->args_[0]->accept(this));
emitln(";");
emit("bpf_forward_to_plum(pkt, ");
TRY2(n->args_[1]->accept(this));
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::emit_forward_to_group(MethodCallExprNode *n) {
emit("pkt->arg2 = ");
TRY2(n->args_[0]->accept(this));
emitln(";");
emitln("pkt->arg3 = pkt->plum_id;");
emit("bpf_forward_to_plum(pkt, ");
emit("1/*TUNNEL_PLUM_ID*/");
emit(")");
return mkstatus(0);
}
StatusTuple CodegenLLVM::visit_method_call_expr_node(MethodCallExprNode *n) {
free_instructions_.push_back(vector<string>());
if (!n->block_->stmts_.empty()) {
++indent_;
emitln("{");
}
if (n->id_->sub_name_.size()) {
if (n->id_->sub_name_ == "lookup") {
TRY2(emit_table_lookup(n));
......@@ -998,62 +741,125 @@ StatusTuple CodegenLLVM::visit_method_call_expr_node(MethodCallExprNode *n) {
TRY2(emit_table_update(n));
} else if (n->id_->sub_name_ == "delete") {
TRY2(emit_table_delete(n));
} else if (n->id_->sub_name_ == "replicate" && n->id_->name_ == "pkt") {
TRY2(emit_packet_replicate(n));
} else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
TRY2(emit_packet_forward(n));
} else if (n->id_->sub_name_ == "forward_self" && n->id_->name_ == "pkt") {
TRY2(emit_packet_forward_self(n));
} else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
TRY2(emit_packet_push_header(n));
} else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
TRY2(emit_packet_pop_header(n));
} else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
TRY2(emit_packet_push_vlan(n));
} else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
TRY2(emit_packet_pop_vlan(n));
} else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
TRY2(emit_packet_rewrite_field(n));
} else if (n->id_->sub_name_ == "clone_forward" && n->id_->name_ == "pkt") {
TRY2(emit_packet_clone_forward(n));
}
} else if (n->id_->name_ == "atomic_add") {
TRY2(emit_atomic_add(n));
} else if (n->id_->name_ == "log") {
TRY2(emit_log(n));
} else if (n->id_->name_ == "cksum") {
TRY2(emit_cksum(n));
} else if (n->id_->name_ == "incr_cksum_u16") {
TRY2(emit_incr_cksum(n, 2));
} else if (n->id_->name_ == "incr_cksum_u32") {
TRY2(emit_incr_cksum(n, 4));
} else if (n->id_->name_ == "incr_cksum") {
TRY2(emit_incr_cksum(n));
} else if (n->id_->name_ == "lb_hash") {
TRY2(emit_lb_hash(n));
} else if (n->id_->name_ == "sizeof") {
TRY2(emit_sizeof(n));
} else if (n->id_->name_ == "get_usec_time") {
TRY2(emit_get_usec_time(n));
} else if (n->id_->name_ == "channel_push") {
TRY2(emit_channel_push(n));
} else if (n->id_->name_ == "channel_push_generic") {
TRY2(emit_channel_push_generic(n));
} else if (n->id_->name_ == "forward_to_vnf") {
TRY2(emit_forward_to_vnf(n));
} else if (n->id_->name_ == "forward_to_group") {
TRY2(emit_forward_to_group(n));
} else {
TRY2(n->id_->accept(this));
emit("(");
for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
TRY2((*it)->accept(this));
if (it + 1 != n->args_.end()) {
emit(", ");
return mkstatus_(n, "unsupported");
}
TRY2(n->block_->accept(this));
return mkstatus(0);
}
/* result = lookup(key)
* if (!result) {
* update(key, {0}, BPF_NOEXIST)
* result = lookup(key)
* }
*/
StatusTuple CodegenLLVM::visit_table_index_expr_node(TableIndexExprNode *n) {
auto table_fd_it = table_fds_.find(n->table_);
if (table_fd_it == table_fds_.end())
return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str());
Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist");
Function *update_fn = mod_->getFunction("bpf_map_update_elem_");
if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined");
Function *lookup_fn = mod_->getFunction("bpf_map_lookup_elem_");
if (!lookup_fn) return mkstatus_(n, "bpf_map_lookup_elem_ undefined");
StructType *leaf_type;
TRY2(lookup_struct_type(n->table_->leaf_type_, &leaf_type));
PointerType *leaf_ptype = PointerType::getUnqual(leaf_type);
CallInst *pseudo_call = B.CreateCall2(pseudo_fn, B.getInt64(BPF_PSEUDO_MAP_FD),
B.getInt64(table_fd_it->second));
Value *pseudo_map_fd = pseudo_call;
TRY2(n->index_->accept(this));
Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
// result = lookup(key)
Value *lookup1 = B.CreateBitCast(B.CreateCall2(lookup_fn, pseudo_map_fd, key_ptr), leaf_ptype);
Value *result;
if (n->table_->policy_id()->name_ == "AUTO") {
Function *parent = B.GetInsertBlock()->getParent();
BasicBlock *label_start = B.GetInsertBlock();
BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[].then", parent);
BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[].end", parent);
Value *eq_zero = B.CreateIsNull(lookup1);
B.CreateCondBr(eq_zero, label_then, label_end);
B.SetInsertPoint(label_then);
// var Leaf leaf {0}
Value *leaf_ptr = B.CreateBitCast(new AllocaInst(leaf_type, "", resolve_entry_stack()), B.getInt8PtrTy());
B.CreateMemSet(leaf_ptr, B.getInt8(0), B.getInt64(n->table_->leaf_id()->bit_width_ >> 3), 1);
// update(key, leaf)
B.CreateCall4(update_fn, pseudo_map_fd, key_ptr, leaf_ptr, B.getInt64(BPF_NOEXIST));
// result = lookup(key)
Value *lookup2 = B.CreateBitCast(B.CreateCall2(lookup_fn, pseudo_map_fd, key_ptr), leaf_ptype);
B.CreateBr(label_end);
B.SetInsertPoint(label_end);
PHINode *phi = B.CreatePHI(leaf_ptype, 2);
phi->addIncoming(lookup1, label_start);
phi->addIncoming(lookup2, label_then);
result = phi;
} else if (n->table_->policy_id()->name_ == "NONE") {
result = lookup1;
}
if (n->is_lhs()) {
if (n->sub_decl_) {
Type *ptr_type = PointerType::getUnqual(B.getIntNTy(n->sub_decl_->bit_width_));
// u64 *errval -> uN *errval
Value *err_cast = B.CreateBitCast(errval_, ptr_type);
// if valid then &field, else &errval
Function *parent = B.GetInsertBlock()->getParent();
BasicBlock *label_start = B.GetInsertBlock();
BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.then", parent);
BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.end", parent);
if (1) {
// the PHI implementation of this doesn't load, maybe eBPF limitation?
B.CreateCondBr(B.CreateIsNull(result), label_then, label_end);
B.SetInsertPoint(label_then);
B.CreateStore(B.getInt32(2), retval_);
B.CreateBr(resolve_label("DONE"));
B.SetInsertPoint(label_end);
vector<Value *> indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)});
expr_ = B.CreateInBoundsGEP(result, indices);
} else {
B.CreateCondBr(B.CreateIsNotNull(result), label_then, label_end);
B.SetInsertPoint(label_then);
vector<Value *> indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)});
Value *field = B.CreateInBoundsGEP(result, indices);
B.CreateBr(label_end);
B.SetInsertPoint(label_end);
PHINode *phi = B.CreatePHI(ptr_type, 2);
phi->addIncoming(err_cast, label_start);
phi->addIncoming(field, label_then);
expr_ = phi;
}
} else {
return mkstatus_(n, "unsupported");
}
} else {
expr_ = result;
}
TRY2(n->block_->accept(this));
return mkstatus(0);
}
......@@ -1119,18 +925,7 @@ StatusTuple CodegenLLVM::visit_miss_decl_stmt_node(MissDeclStmtNode *n) {
}
StatusTuple CodegenLLVM::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) {
if (n->formals_.size() != 1)
return mkstatus_(n, "on_failure expected 1 argument, %zu given", n->formals_.size());
StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
++indent_;
emitln("/*if ((unsigned long)%s_element >= (unsigned long)-4095) {", n->id_->name_.c_str());
emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
TRY2(n->block_->accept(this));
--indent_;
emitln("");
emit("}*/");
return mkstatus(0);
return mkstatus_(n, "unsupported");
}
StatusTuple CodegenLLVM::visit_expr_stmt_node(ExprStmtNode *n) {
......@@ -1143,16 +938,27 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') {
return mkstatus(0);
}
StructType *stype;
StructDeclStmtNode *decl;
TRY2(lookup_struct_type(n, &stype, &decl));
Type *ptr_stype = n->is_pointer() ? PointerType::getUnqual(stype) : (PointerType *)stype;
AllocaInst *ptr_a = new AllocaInst(ptr_stype, "", resolve_entry_stack());
vars_[n] = ptr_a;
if (n->struct_id_->scope_name_ == "proto") {
auto p = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true);
if (p) {
if (n->is_pointer()) {
ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(ptr_stype));
B.CreateStore(const_null, ptr_a);
} else {
string var = n->scope_id() + n->id_->name_;
/* zero initialize array to be filled in with packet header */
emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;",
var.c_str(), ((p->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
var.c_str(), ((decl->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
auto asn = static_cast<AssignExprNode*>(it->get());
if (auto f = p->field(asn->id_->sub_name_)) {
if (auto f = decl->field(asn->id_->sub_name_)) {
size_t bit_offset = f->bit_offset_;
size_t bit_width = f->bit_width_;
if (asn->bitop_) {
......@@ -1166,32 +972,20 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
}
}
} else {
StructDeclStmtNode *decl = scopes_->top_struct()->lookup(n->struct_id_->name_);
if (!decl) return mkstatus_(n, "Cannot find struct %s decl", n->id_->c_str());
auto it = structs_.find(decl);
if (it == structs_.end()) return mkstatus_(n, "Cannot find struct %s decl", n->id_->c_str());
Type *stype = n->is_pointer() ? PointerType::get(it->second, 0) : (PointerType *)it->second;
AllocaInst *ptr_a = new AllocaInst(stype, nullptr, "", entry_bb_);
vars_[n] = ptr_a;
if (n->is_pointer()) {
if (n->id_->name_ == "_result") {
// special case for capturing the return value of a previous method call
Value *cast_1 = B.CreateBitCast(pop_expr(), stype);
Value *cast_1 = B.CreateBitCast(pop_expr(), ptr_stype);
B.CreateStore(cast_1, ptr_a);
} else {
ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(stype));
ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(ptr_stype));
B.CreateStore(const_null, ptr_a);
}
} else {
B.CreateMemSet(ptr_a, B.getInt8(0), B.getInt64(decl->bit_width_ >> 3), 1);
emit("%s %s%s = {};", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
if (!n->init_.empty()) {
for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
emit(" ");
for (auto it = n->init_.begin(); it != n->init_.end(); ++it)
TRY2((*it)->accept(this));
emit(";");
}
}
}
}
......@@ -1201,23 +995,14 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
StatusTuple CodegenLLVM::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) {
if (!B.GetInsertBlock())
return mkstatus(0);
if (n->id_->name_ == "timer_delay")
return mkstatus(0);
emit_comment(n);
emit("%s %s%s", bits_to_uint(n->bit_width_), n->scope_id(), n->id_->c_str());
// uintX var = init
AllocaInst *ptr_a = new AllocaInst(B.getIntNTy(n->bit_width_), nullptr, n->id_->name_, entry_bb_);
AllocaInst *ptr_a = new AllocaInst(B.getIntNTy(n->bit_width_), n->id_->name_, resolve_entry_stack());
vars_[n] = ptr_a;
// todo
if (!n->scope_id_.empty())
emit(" = 0");
if (!n->init_.empty()) {
emit("; ");
if (!n->init_.empty())
TRY2(n->init_[0]->accept(this));
}
emit(";");
return mkstatus(0);
}
......@@ -1241,29 +1026,19 @@ StatusTuple CodegenLLVM::visit_parser_state_stmt_node(ParserStateStmtNode *n) {
return mkstatus(0);
}
StatusTuple CodegenLLVM::visit_timer_decl_stmt_node(TimerDeclStmtNode *n) {
auto scope = scopes_->current_state();
scopes_->set_current(n->scope_);
TRY2(n->block_->accept(this));
scopes_->set_current(scope);
return mkstatus(0);
}
StatusTuple CodegenLLVM::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
if (!n->id_) {
if (!n->id_)
return mkstatus(0);
}
string jump_label = n->scoped_name();
BasicBlock *label_entry = resolve_label(jump_label);
B.SetInsertPoint(label_entry);
auto it = n->subs_.begin();
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
scopes_->push_state(it->scope_);
for (auto in = n->init_.begin(); in != n->init_.end(); ++in) {
for (auto in = n->init_.begin(); in != n->init_.end(); ++in)
TRY2((*in)->accept(this));
}
if (n->subs_.size() == 1 && it->id_->name_ == "") {
// this is not a multistate protocol, emit everything and finish
......@@ -1274,45 +1049,9 @@ StatusTuple CodegenLLVM::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
}
} else {
return mkstatus_(n, "unsupported");
if (n->parser_) {
for (auto it2 = n->subs_.begin(); it2 != n->subs_.end(); ++it2) {
proto_rewrites_[it2->id_->full_name()] = n->scoped_name() + "_" + it2->id_->name_;
}
TRY2(n->parser_->accept(this));
proto_rewrites_.clear();
emitln("");
}
for (; it != n->subs_.end(); ++it) {
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
string jump_label = n->scoped_name() + "_" + it->id_->name_;
++indent_;
emitln("JUMP_GUARD; %s: {", jump_label.c_str());
emitln("PG_TRACE(%.14s);", jump_label.c_str());
if (auto p = proto_scopes_->top_struct()->lookup(it->id_->name_, true)) {
emitln("%s = pkt->offset + parsed_bytes; /* remember the offset of this header */", it->id_->c_str());
emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
}
TRY2(it->block_->accept(this));
if (it->parser_) {
emitln("");
TRY2(it->parser_->accept(this));
}
--indent_;
emitln("");
emitln("}");
scopes_->set_current(scope);
}
}
scopes_->set_current(scope);
--indent_;
emitln("");
emit("}");
scopes_->pop_state();
return mkstatus(0);
}
......@@ -1344,7 +1083,6 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
GlobalVariable *decl_gvar = new GlobalVariable(*mod_, decl_struct, false,
GlobalValue::ExternalLinkage, 0, n->id_->name_);
decl_gvar->setSection("maps");
decl_gvar->setAlignment(4);
vector<Constant *> struct_init = { B.getInt32(map_type), B.getInt32(key->bit_width_ / 8),
B.getInt32(leaf->bit_width_ / 8), B.getInt32(n->size_)};
Constant *const_struct = ConstantStruct::get(decl_struct, struct_init);
......@@ -1352,7 +1090,7 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
tables_[n] = decl_gvar;
int map_fd = bpf_create_map(map_type, key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_);
if (map_fd >= 0 || !ENABLE_RELOCATIONS)
if (map_fd >= 0)
table_fds_[n] = map_fd;
} else {
return mkstatus_(n, "Table %s not implemented", n->table_type_->name_.c_str());
......@@ -1360,182 +1098,133 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
return mkstatus(0);
}
StatusTuple CodegenLLVM::visit(Node* root) {
BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
StatusTuple CodegenLLVM::lookup_struct_type(StructDeclStmtNode *decl, StructType **stype) const {
auto struct_it = structs_.find(decl);
if (struct_it == structs_.end())
return mkstatus_(decl, "could not find IR for type %s", decl->id_->c_str());
*stype = struct_it->second;
return mkstatus(0);
}
scopes_->set_current(scopes_->top_state());
scopes_->set_current(scopes_->top_var());
StatusTuple CodegenLLVM::lookup_struct_type(VariableDeclStmtNode *n, StructType **stype,
StructDeclStmtNode **decl) const {
if (!n->is_struct())
return mkstatus_(n, "attempt to search for struct with a non-struct type %s", n->id_->c_str());
TRY2(print_header());
auto var = (StructVariableDeclStmtNode *)n;
StructDeclStmtNode *type;
if (var->struct_id_->scope_name_ == "proto")
type = proto_scopes_->top_struct()->lookup(var->struct_id_->name_, true);
else
type = scopes_->top_struct()->lookup(var->struct_id_->name_, true);
TRY2(b->ver_.accept(this));
if (!type) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str());
for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) {
TRY2((*it)->accept(this));
emit("\n");
}
TRY2(lookup_struct_type(type, stype));
TRY2(print_parser());
if (decl)
*decl = type;
return mkstatus(0);
}
StatusTuple CodegenLLVM::print_timer() {
// visit timers
++indent_;
emitln("PG_PARSE_DECL(timer) {");
emitln("uint32_t timer_delay = 0;");
// visit function scoped variables
for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
TRY2((*it)->accept(this));
emitln("");
}
for (auto it = scopes_->top_timer()->obegin(); it != scopes_->top_timer()->oend(); ++it) {
TRY2((*it)->accept(this));
emitln("");
}
++indent_;
emitln("DONE: {");
emitln("PG_TRACE(DONE);");
emitln("pg_timer_forward(pkt, timer_delay);");
--indent_;
emitln("return;");
emitln("}");
++indent_;
emitln("ERROR: {");
emitln("PG_TRACE(ERROR);");
emitln("pg_drop(pkt);");
emitln("pg_timer_forward(pkt, timer_delay);");
--indent_;
emitln("return;");
--indent_;
emitln("}");
emitln("}");
return mkstatus(0);
}
StatusTuple CodegenLLVM::print_parser() {
auto skbuff_decl = scopes_->top_struct()->lookup("_skbuff");
if (!skbuff_decl) return mkstatus(-1, "could not find built-in struct decl _skbuff");
auto struct_it = structs_.find(skbuff_decl);
if (struct_it == structs_.end()) return mkstatus(-1, "could not find built-in type _skbuff");
// int parse(struct sk_buff *skb)
FunctionType *parse_fn_type = FunctionType::get(B.getInt32Ty(),
vector<Type *>({PointerType::get(struct_it->second, 0)}),
/*isVarArg=*/false);
Function *prog = mod_->getFunction("main");
if (!prog) {
prog = Function::Create(parse_fn_type, GlobalValue::ExternalLinkage, "main", mod_);
if (section_.empty()) return mkstatus(-1, "Empty section pragma");
prog->setSection(section_);
StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
if (n->formals_.size() != 1)
return mkstatus_(n, "Functions must have exactly 1 argument, %zd given", n->formals_.size());
vector<Type *> formals;
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
VariableDeclStmtNode *formal = it->get();
if (formal->is_struct()) {
StructType *stype;
TRY2(lookup_struct_type(formal, &stype));
formals.push_back(PointerType::getUnqual(stype));
} else {
formals.push_back(B.getIntNTy(formal->bit_width_));
}
}
FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), formals, /*isVarArg=*/false);
entry_bb_ = BasicBlock::Create(ctx(), "entry", prog);
labels_["entry"] = entry_bb_;
B.SetInsertPoint(entry_bb_);
auto args = prog->arg_begin();
Value *skb_arg = args++;
skb_arg->setName("skb");
auto skb = scopes_->top_var()->lookup("skb", true);
if (!skb) return mkstatus(-1, "unable to find declaration of built-in skb");
Type *stype = PointerType::get(struct_it->second, 0);
AllocaInst *ptr_skb = new AllocaInst(stype, nullptr, "skb", entry_bb_);
ptr_skb->setAlignment(4);
B.CreateStore(skb_arg, ptr_skb);
retval_ = new AllocaInst(B.getInt32Ty(), nullptr, "ret", entry_bb_);
B.CreateStore(B.getInt32(0), retval_);
vars_[skb] = ptr_skb;
Function *fn = mod_->getFunction(n->id_->name_);
if (fn) return mkstatus_(n, "Function %s already defined", n->id_->c_str());
fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, n->id_->name_, mod_);
fn->setSection("." + n->id_->name_);
BasicBlock *label_entry = BasicBlock::Create(ctx(), "entry", fn);
B.SetInsertPoint(label_entry);
string scoped_entry_label = std::to_string((uintptr_t)fn) + "::entry";
labels_[scoped_entry_label] = label_entry;
BasicBlock *label_return = resolve_label("DONE");
++indent_;
emitln("PG_PARSE_DECL(parse) {");
/* emitln("uint8_t *pp;"); */
emitln("uint32_t parsed_bytes = 0;");
//emitln("uint16_t orig_offset = 0;/*pkt->offset;*/");
retval_ = new AllocaInst(fn->getReturnType(), "ret", label_entry);
B.CreateStore(B.getInt32(0), retval_);
errval_ = new AllocaInst(B.getInt64Ty(), "err", label_entry);
B.CreateStore(B.getInt64(0), errval_);
auto formal = n->formals_.begin();
for (auto arg = fn->arg_begin(); arg != fn->arg_end(); ++arg, ++formal) {
TRY2((*formal)->accept(this));
Value *ptr = vars_[formal->get()];
if (!ptr) return mkstatus_(n, "cannot locate memory location for arg %s", (*formal)->id_->c_str());
B.CreateStore(arg, ptr);
// Type *ptype;
// if ((*formal)->is_struct()) {
// StructType *type;
// TRY2(lookup_struct_type(formal->get(), &type));
// ptype = PointerType::getUnqual(type);
// } else {
// ptype = PointerType::getUnqual(B.getIntNTy((*formal)->bit_width_));
// }
// arg->setName((*formal)->id_->name_);
// AllocaInst *ptr = new AllocaInst(ptype, nullptr, (*formal)->id_->name_, label_entry);
// B.CreateStore(arg, ptr);
// vars_[formal->get()] = ptr;
}
// visit function scoped variables
{
BlockStack bstack(this, entry_bb_);
B.SetInsertPoint(entry_bb_);
scopes_->push_state(n->scope_);
for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it)
TRY2((*it)->accept(this));
}
for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
if (proto_scopes_->top_struct()->lookup((*it)->id_->name_, true)) {
emitln("uint32_t %s = 0; /* header offset */", (*it)->id_->c_str());
}
}
TRY2(n->block_->accept(this));
/* emitln("pp = pkt->start + pkt->offset;"); */
emitln("goto s1_INIT;");
scopes_->pop_state();
if (!B.GetInsertBlock()->getTerminator())
B.CreateBr(resolve_label("DONE"));
// finally, visit the states
for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
emitln("");
TRY2((*it)->accept(this));
// always return something
B.SetInsertPoint(label_return);
B.CreateRet(B.CreateLoad(retval_));
}
B.SetInsertPoint(entry_bb_);
B.CreateBr(resolve_label("s1_INIT"));
B.SetInsertPoint(label_return);
expr_ = B.CreateLoad(retval_);
B.CreateRet(pop_expr());
return mkstatus(0);
}
++indent_;
emitln("ERROR: {");
emitln("PG_TRACE(ERROR);");
--indent_;
emitln("goto CLEANUP;");
emitln("}");
StatusTuple CodegenLLVM::visit(Node* root) {
scopes_->set_current(scopes_->top_state());
scopes_->set_current(scopes_->top_var());
++indent_;
emitln("DONE: {");
emitln("PG_TRACE(DONE);");
--indent_;
emitln("goto CLEANUP;");
emitln("}");
TRY2(print_header());
++indent_;
emitln("CLEANUP: {");
--indent_;
emitln("/* cleanup is done by PE */;");
--indent_;
emitln("}");
for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it)
TRY2((*it)->accept(this));
emitln("}");
for (auto it = scopes_->top_func()->obegin(); it != scopes_->top_func()->oend(); ++it)
TRY2((*it)->accept(this));
//TRY2(print_parser());
//print_timer();
return mkstatus(0);
}
StatusTuple CodegenLLVM::print_header() {
if (use_pre_header_) {
//emit("%s", PRE_HEADER.c_str());
emitln("");
} else {
emitln("#include <stdint.h>");
emitln("#include \"../dp/linux/filter.h\"");
emitln("#include \"container/pg_api.h\"");
emitln("#include \"container/pg_defs.h\"");
}
emitln("#define JUMP_GUARD goto DONE");
emitln("#define PG_SIZEOF(_pkt) ((int)_pkt->length - (int)_pkt->offset + 0/*orig_offset*/)");
GlobalVariable *gvar_license = new GlobalVariable(*mod_, ArrayType::get(Type::getInt8Ty(ctx()), 4),
false, GlobalValue::ExternalLinkage, 0, "_license");
gvar_license->setSection("license");
gvar_license->setAlignment(1);
gvar_license->setInitializer(ConstantDataArray::getString(ctx(), "GPL", true));
Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
......@@ -1545,16 +1234,17 @@ StatusTuple CodegenLLVM::print_header() {
GlobalValue::ExternalLinkage, "llvm.bpf.pseudo", mod_);
}
int i = 0;
// declare structures
for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) {
if ((*it)->id_->name_ == "_Packet")
continue;
TRY2((*it)->accept(this));
emit(";\n");
emitln("#define STRUCTID_%s %d", (*it)->id_->c_str(), i++);
}
emitln("#define STRUCTID_generic %d", i);
for (auto it = proto_scopes_->top_struct()->obegin(); it != proto_scopes_->top_struct()->oend(); ++it) {
if ((*it)->id_->name_ == "_Packet")
continue;
TRY2((*it)->accept(this));
}
return mkstatus(0);
}
......@@ -1585,13 +1275,19 @@ Value * CodegenLLVM::pop_expr() {
}
BasicBlock * CodegenLLVM::resolve_label(const string &label) {
auto it = labels_.find(label);
if (it != labels_.end()) return it->second;
Function *parent = B.GetInsertBlock()->getParent();
string scoped_label = std::to_string((uintptr_t)parent) + "::" + label;
auto it = labels_.find(scoped_label);
if (it != labels_.end()) return it->second;
BasicBlock *label_new = BasicBlock::Create(ctx(), label, parent);
labels_[label] = label_new;
labels_[scoped_label] = label_new;
return label_new;
}
Instruction * CodegenLLVM::resolve_entry_stack() {
BasicBlock *label_entry = resolve_label("entry");
return &label_entry->back();
}
} // namespace cc
} // namespace ebpf
......@@ -53,8 +53,7 @@ class CodegenLLVM : public Visitor {
friend class BlockStack;
friend class SwitchStack;
public:
CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes,
bool use_pre_header, const std::string &section);
CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes);
virtual ~CodegenLLVM();
#define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n);
......@@ -94,25 +93,21 @@ class CodegenLLVM : public Visitor {
STATUS_RETURN emit_get_usec_time(MethodCallExprNode* n);
STATUS_RETURN emit_forward_to_vnf(MethodCallExprNode* n);
STATUS_RETURN emit_forward_to_group(MethodCallExprNode* n);
STATUS_RETURN print_parser();
STATUS_RETURN print_timer();
STATUS_RETURN print_header();
void indent();
llvm::LLVMContext & ctx() const;
llvm::Constant * const_int(uint64_t val, unsigned bits = 64, bool is_signed = false);
llvm::Value * pop_expr();
llvm::BasicBlock * resolve_label(const string &label);
llvm::Instruction * resolve_entry_stack();
StatusTuple lookup_var(Node *n, const std::string &name, Scopes::VarScope *scope,
VariableDeclStmtNode **decl, llvm::Value **mem) const;
StatusTuple lookup_struct_type(StructDeclStmtNode *decl, llvm::StructType **stype) const;
StatusTuple lookup_struct_type(VariableDeclStmtNode *n, llvm::StructType **stype,
StructDeclStmtNode **decl = nullptr) const;
template <typename... Args> void emitln(const char *fmt, Args&&... params);
template <typename... Args> void lnemit(const char *fmt, Args&&... params);
template <typename... Args> void emit(const char *fmt, Args&&... params);
void emitln(const char *s);
void lnemit(const char *s);
void emit(const char *s);
void emit_comment(Node* n);
FILE* out_;
llvm::Module* mod_;
......@@ -121,8 +116,6 @@ class CodegenLLVM : public Visitor {
int tmp_reg_index_;
Scopes *scopes_;
Scopes *proto_scopes_;
bool use_pre_header_;
std::string section_;
vector<vector<string> > free_instructions_;
vector<string> table_inits_;
map<string, string> proto_rewrites_;
......@@ -131,10 +124,10 @@ class CodegenLLVM : public Visitor {
map<VariableDeclStmtNode *, llvm::Value *> vars_;
map<StructDeclStmtNode *, llvm::StructType *> structs_;
map<string, llvm::BasicBlock *> labels_;
llvm::BasicBlock *entry_bb_;
llvm::SwitchInst *cur_switch_;
llvm::Value *expr_;
llvm::AllocaInst *retval_;
llvm::AllocaInst *errval_;
};
} // namespace cc
......
......@@ -82,7 +82,8 @@ class Lexer : public yyFlexLexer {
case Tok::TRBRACK:
case Tok::TTRUE:
case Tok::TFALSE:
return true;
// uncomment to add implicit semicolons
//return true;
default:
break;
}
......
......@@ -31,14 +31,14 @@ std::string tmp_str_cc;
%x STRING_
%%
\' {BEGIN STRING_;}
<STRING_>\' { BEGIN 0;
\" {BEGIN STRING_;}
<STRING_>\" { BEGIN 0;
yylval_->string = new std::string(tmp_str_cc);
tmp_str_cc = "";
return Tok::TSTRING;
}
<STRING_>\\n {tmp_str_cc += "\n"; }
<STRING_>. {tmp_str_cc += *yytext; }
<STRING_>\n {tmp_str_cc += "\n"; }
......@@ -59,10 +59,13 @@ std::string tmp_str_cc;
"}" return save(Tok::TRBRACE);
"[" return save(Tok::TLBRACK);
"]" return save(Tok::TRBRACK);
"->" return save(Tok::TARROW);
"." return save(Tok::TDOT);
"," return save(Tok::TCOMMA);
"+" return save(Tok::TPLUS);
"++" return save(Tok::TINCR);
"-" return save(Tok::TMINUS);
"--" return save(Tok::TDECR);
"*" return save(Tok::TMUL);
"/" return save(Tok::TDIV);
"%" return save(Tok::TMOD);
......@@ -79,30 +82,31 @@ std::string tmp_str_cc;
"|" return save(Tok::TLOR);
"@" return save(Tok::TAT);
"const" return save(Tok::TCONST);
"struct" return save(Tok::TSTRUCT);
"var" return save(Tok::TVAR);
"state" return save(Tok::TSTATE);
"timer" return save(Tok::TTIMER);
"goto" return save(Tok::TGOTO);
"case" return save(Tok::TCASE);
"continue" return save(Tok::TCONTINUE);
"else" return save(Tok::TELSE);
"false" return save(Tok::TFALSE);
"goto" return save(Tok::TGOTO);
"if" return save(Tok::TIF);
"next" return save(Tok::TNEXT);
"on_match" return save(Tok::TMATCH);
"on_miss" return save(Tok::TMISS);
"on_failure" return save(Tok::TFAILURE);
"on_valid" return save(Tok::TVALID);
"true" return save(Tok::TTRUE);
"false" return save(Tok::TFALSE);
"if" return save(Tok::TIF);
"else" return save(Tok::TELSE);
"switch" return save(Tok::TSWITCH);
"case" return save(Tok::TCASE);
"return" return save(Tok::TRETURN);
"state" return save(Tok::TSTATE);
"struct" return save(Tok::TSTRUCT);
"switch" return save(Tok::TSWITCH);
"true" return save(Tok::TTRUE);
"u8" return save(Tok::TU8);
"u16" return save(Tok::TU16);
"u32" return save(Tok::TU32);
"u64" return save(Tok::TU64);
[a-zA-Z][a-zA-Z0-9_]* return save(Tok::TIDENTIFIER);
[0-9]+ return save(Tok::TINTEGER);
0x[0-9a-fA-F]+ return save(Tok::THEXINTEGER);
. printf("Unknown token\n"); yyterminate();
. printf("Unknown token \"%s\"\n", yytext); yyterminate();
%%
/* eBPF mini library */
#include <stdlib.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <unistd.h>
#include <string.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/bpf.h>
#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/perf_event.h>
#include <linux/rtnetlink.h>
#include <linux/unistd.h>
#include <linux/version.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <linux/if_packet.h>
#include <linux/pkt_sched.h>
#include <arpa/inet.h>
#include <libmnl/libmnl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "libbpf.h"
static __u64 ptr_to_u64(void *ptr)
......@@ -82,8 +84,8 @@ int bpf_get_next_key(int fd, void *key, void *next_key)
char bpf_log_buf[LOG_BUF_SIZE];
int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int prog_len,
const char *license)
const struct bpf_insn *insns, int prog_len,
const char *license)
{
union bpf_attr attr = {
.prog_type = prog_type,
......@@ -95,9 +97,14 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
.log_level = 1,
};
attr.kern_version = LINUX_VERSION_CODE;
bpf_log_buf[0] = 0;
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
if (ret < 0) {
fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
}
return ret;
}
int bpf_open_raw_sock(const char *name)
......@@ -144,8 +151,8 @@ static int cb(const struct nlmsghdr *nlh, void *data) {
}
}
int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex, uint8_t prio, uint32_t classid)
{
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;
......@@ -169,9 +176,9 @@ int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex, uint8
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, 6 /*TCA_BPF_FD*/, progfd);
mnl_attr_put_strz(nlh, 7 /*TCP_BPF_NAME*/, prog_name);
mnl_attr_put_u32(nlh, 3 /*TCA_BPF_CLASSID*/, classid);
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);
......@@ -209,3 +216,82 @@ cleanup:
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;
ssize_t bytes = -1;
char buf[256];
struct perf_event_attr attr = {};
snprintf(buf, sizeof(buf), "%s/id", event_path);
efd = open(buf, O_RDONLY, 0);
if (efd < 0) {
fprintf(stderr, "open(%s): %s\n", buf, strerror(errno));
goto cleanup;
}
bytes = read(efd, buf, sizeof(buf));
if (bytes <= 0 || bytes >= sizeof(buf)) {
fprintf(stderr, "read(%s): %s\n", buf, strerror(errno));
goto cleanup;
}
buf[bytes] = '\0';
attr.config = strtol(buf, NULL, 0);
attr.type = PERF_TYPE_TRACEPOINT;
attr.sample_type = PERF_SAMPLE_RAW;
attr.sample_period = 1;
attr.wakeup_events = 1;
pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
if (pfd < 0) {
perror("perf_event_open");
goto cleanup;
}
if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) {
perror("ioctl(PERF_EVENT_IOC_SET_BPF)");
goto cleanup;
}
if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
perror("ioctl(PERF_EVENT_IOC_ENABLE)");
goto cleanup;
}
rc = pfd;
pfd = -1;
cleanup:
if (efd >= 0)
close(efd);
if (pfd >= 0)
close(pfd);
return rc;
}
int bpf_attach_kprobe(int progfd, const char *event,
const char *event_desc, pid_t pid,
int cpu, int group_fd) {
int rc = -1, kfd = -1;
char buf[256];
kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
if (kfd < 0) {
perror("open(kprobe_events)");
goto cleanup;
}
if (write(kfd, event_desc, strlen(event_desc)) < 0) {
perror("write(kprobe_events)");
goto cleanup;
}
snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/kprobes/%s", event);
rc = bpf_attach_tracing_event(progfd, buf, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/);
cleanup:
if (kfd >= 0)
close(kfd);
return rc;
}
......@@ -67,12 +67,14 @@ typedef unique_ptr<string> String;
EXPAND(AssignExprNode, assign_expr_node) \
EXPAND(PacketExprNode, packet_expr_node) \
EXPAND(IntegerExprNode, integer_expr_node) \
EXPAND(StringExprNode, string_expr_node) \
EXPAND(BinopExprNode, binop_expr_node) \
EXPAND(UnopExprNode, unop_expr_node) \
EXPAND(BitopExprNode, bitop_expr_node) \
EXPAND(GotoExprNode, goto_expr_node) \
EXPAND(ReturnExprNode, return_expr_node) \
EXPAND(MethodCallExprNode, method_call_expr_node)
EXPAND(MethodCallExprNode, method_call_expr_node) \
EXPAND(TableIndexExprNode, table_index_expr_node)
#define NODE_STATEMENTS(EXPAND) \
EXPAND(ExprStmtNode, expr_stmt_node) \
......@@ -85,13 +87,12 @@ typedef unique_ptr<string> String;
EXPAND(IntegerVariableDeclStmtNode, integer_variable_decl_stmt_node) \
EXPAND(StructDeclStmtNode, struct_decl_stmt_node) \
EXPAND(StateDeclStmtNode, state_decl_stmt_node) \
EXPAND(TimerDeclStmtNode, timer_decl_stmt_node) \
EXPAND(ParserStateStmtNode, parser_state_stmt_node) \
EXPAND(MatchDeclStmtNode, match_decl_stmt_node) \
EXPAND(MissDeclStmtNode, miss_decl_stmt_node) \
EXPAND(FailureDeclStmtNode, failure_decl_stmt_node) \
EXPAND(TableDeclStmtNode, table_decl_stmt_node) \
EXPAND(VersionStmtNode, version_stmt_node)
EXPAND(FuncDeclStmtNode, func_decl_stmt_node)
#define EXPAND_NODES(EXPAND) \
NODE_EXPRESSIONS(EXPAND) \
......@@ -148,7 +149,7 @@ class ExprNode : public Node {
public:
typedef unique_ptr<ExprNode> Ptr;
virtual StatusTuple accept(Visitor* v) = 0;
enum expr_type { STRUCT, INTEGER, VOID, UNKNOWN };
enum expr_type { STRUCT, INTEGER, STRING, VOID, UNKNOWN };
enum prop_flag { READ = 0, WRITE, PROTO, IS_LHS, IS_REF, LAST };
expr_type typeof_;
StructDeclStmtNode *struct_type_;
......@@ -259,6 +260,17 @@ class PacketExprNode : public ExprNode {
explicit PacketExprNode(IdentExprNode::Ptr id) : id_(move(id)) {}
};
class StringExprNode : public ExprNode {
public:
DECLARE(StringExprNode)
string val_;
explicit StringExprNode(string *val) : val_(move(*val)) {
delete val;
}
explicit StringExprNode(const string &val) : val_(val) {}
};
class IntegerExprNode : public ExprNode {
public:
DECLARE(IntegerExprNode)
......@@ -318,25 +330,15 @@ class ReturnExprNode : public ExprNode {
: expr_(move(expr)) {}
};
class VersionStmtNode : public StmtNode {
public:
DECLARE(VersionStmtNode)
VersionStmtNode(const int major, const int minor, const int rev)
: major_(major), minor_(minor), rev_(rev) {}
uint32_t major_, minor_, rev_;
};
class BlockStmtNode : public StmtNode {
public:
DECLARE(BlockStmtNode)
explicit BlockStmtNode(StmtNodeList stmts = StmtNodeList())
: stmts_(move(stmts)), scope_(NULL), ver_(0, 0, 0) {}
: stmts_(move(stmts)), scope_(NULL) {}
~BlockStmtNode() { delete scope_; }
StmtNodeList stmts_;
Scopes::VarScope* scope_;
VersionStmtNode ver_;
};
class MethodCallExprNode : public ExprNode {
......@@ -352,6 +354,20 @@ class MethodCallExprNode : public ExprNode {
}
};
class TableIndexExprNode : public ExprNode {
public:
DECLARE(TableIndexExprNode)
IdentExprNode::Ptr id_;
IdentExprNode::Ptr sub_;
ExprNode::Ptr index_;
TableDeclStmtNode *table_;
VariableDeclStmtNode *sub_decl_;
TableIndexExprNode(IdentExprNode::Ptr id, ExprNode::Ptr index)
: id_(move(id)), index_(move(index)), table_(nullptr), sub_decl_(nullptr)
{}
};
class ExprStmtNode : public StmtNode {
public:
DECLARE(ExprStmtNode)
......@@ -532,16 +548,6 @@ class StateDeclStmtNode : public StmtNode {
}
};
class TimerDeclStmtNode : public StateDeclStmtNode {
public:
DECLARE(TimerDeclStmtNode)
BlockStmtNode::Ptr block_;
Scopes::StateScope* scope_;
explicit TimerDeclStmtNode(BlockStmtNode::Ptr block): block_(move(block)) {
}
};
class MatchDeclStmtNode : public StmtNode {
public:
DECLARE(MatchDeclStmtNode)
......@@ -582,6 +588,8 @@ class TableDeclStmtNode : public StmtNode {
IdentExprNode::Ptr table_type_;
IdentExprNodeList templates_;
IdentExprNode::Ptr id_;
StructDeclStmtNode *key_type_;
StructDeclStmtNode *leaf_type_;
IdentExprNode * key_id() { return templates_.at(0).get(); }
IdentExprNode * leaf_id() { return templates_.at(1).get(); }
IdentExprNode * type_id() { return templates_.at(2).get(); }
......@@ -590,11 +598,23 @@ class TableDeclStmtNode : public StmtNode {
TableDeclStmtNode(IdentExprNode::Ptr table_type, IdentExprNodeList&& templates,
IdentExprNode::Ptr id, string* size)
: table_type_(move(table_type)), templates_(move(templates)), id_(move(id)),
size_(strtoul(size->c_str(), NULL, 0)) {
key_type_(nullptr), leaf_type_(nullptr), size_(strtoul(size->c_str(), NULL, 0)) {
delete size;
}
};
class FuncDeclStmtNode : public StmtNode {
public:
DECLARE(FuncDeclStmtNode)
IdentExprNode::Ptr id_;
FormalList formals_;
BlockStmtNode::Ptr block_;
Scopes::StateScope* scope_;
FuncDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block)
: id_(move(id)), formals_(move(formals)), block_(move(block)), scope_(NULL) {}
};
class Visitor {
public:
typedef StatusTuple Ret;
......
......@@ -30,47 +30,43 @@ using std::move;
using std::string;
using std::unique_ptr;
bool Parser::variable_exists(VariableDeclStmtNode* decl, bool search_local) {
if (scopes_->current_var()->lookup(decl->id_->name_, search_local) == NULL) {
bool Parser::variable_exists(VariableDeclStmtNode *decl) const {
if (scopes_->current_var()->lookup(decl->id_->name_, SCOPE_LOCAL) == NULL) {
return false;
}
return true;
}
VariableDeclStmtNode* Parser::variable_add(VariableDeclStmtNode* decl) {
VariableDeclStmtNode *Parser::variable_add(vector<int> *types, VariableDeclStmtNode *decl) {
if (variable_exists(decl, true)) {
if (variable_exists(decl)) {
fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str());
assert(0 && "redeclaration of variable");
// how to raise a bison error from here?
return decl;
return nullptr;
}
decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
scopes_->current_var()->add(decl->id_->name_, decl);
return decl;
}
VariableDeclStmtNode* Parser::variable_add(VariableDeclStmtNode* decl, ExprNode* init_expr) {
VariableDeclStmtNode *Parser::variable_add(vector<int> *types, VariableDeclStmtNode *decl, ExprNode *init_expr) {
AssignExprNode::Ptr assign(new AssignExprNode(decl->id_->copy(), ExprNode::Ptr(init_expr)));
decl->init_.push_back(move(assign));
if (variable_exists(decl, true)) {
if (variable_exists(decl)) {
fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str());
assert(0 && "redeclaration of variable");
// how to raise a bison error from here?
return decl;
return nullptr;
}
decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
scopes_->current_var()->add(decl->id_->name_, decl);
return decl;
}
StructVariableDeclStmtNode* Parser::variable_add(StructVariableDeclStmtNode* decl, ExprNodeList* args, bool is_kv) {
StructVariableDeclStmtNode *Parser::variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv) {
if (is_kv) {
// annotate the init expressions with the declared id
for (auto arg = args->begin(); arg != args->end(); ++arg) {
// decorate with the name of this decl
auto n = static_cast<AssignExprNode*>(arg->get());
auto n = static_cast<AssignExprNode *>(arg->get());
n->id_->prepend_dot(decl->id_->name_);
}
} else {
......@@ -81,32 +77,17 @@ StructVariableDeclStmtNode* Parser::variable_add(StructVariableDeclStmtNode* dec
decl->init_ = move(*args);
delete args;
if (variable_exists(decl, true)) {
if (variable_exists(decl)) {
fprintf(stderr, "ccpg: warning: redeclaration of variable '%s'\n", decl->id_->name_.c_str());
// how to raise a bison error from here?
return decl;
return nullptr;
}
decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
scopes_->current_var()->add(decl->id_->name_, decl);
return decl;
}
StmtNode* Parser::timer_add(Scopes::StateScope* scope, BlockStmtNode* body) {
if (scopes_->top_timer()->lookup("timer", true)) {
fprintf(stderr, "redeclaration of timer. Only one timer supported per plum");
assert(0 && "redeclaration of timer. Only one timer supported per plum");
}
auto timer = new TimerDeclStmtNode(BlockStmtNode::Ptr(body));
timer->scope_ = scope;
scopes_->top_timer()->add("timer", timer);
return timer;
}
StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id, BlockStmtNode* body) {
if (scopes_->current_state()->lookup(id->full_name(), true)) {
StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id, BlockStmtNode *body) {
if (scopes_->current_state()->lookup(id->full_name(), SCOPE_LOCAL)) {
fprintf(stderr, "redeclaration of state %s\n", id->full_name().c_str());
// redeclaration
return NULL;
......@@ -122,8 +103,8 @@ StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id, BlockS
return state;
}
StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id1, IdentExprNode* id2, BlockStmtNode* body) {
auto state = scopes_->current_state()->lookup(id1->full_name(), true);
StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body) {
auto state = scopes_->current_state()->lookup(id1->full_name(), SCOPE_LOCAL);
if (!state) {
state = new StateDeclStmtNode(IdentExprNode::Ptr(id1), IdentExprNode::Ptr(id2), BlockStmtNode::Ptr(body));
// add a reference to the lower scope
......@@ -146,15 +127,15 @@ StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id1, Ident
}
}
bool Parser::table_exists(TableDeclStmtNode* decl, bool search_local) {
bool Parser::table_exists(TableDeclStmtNode *decl, bool search_local) {
if (scopes_->top_table()->lookup(decl->id_->name_, search_local) == NULL) {
return false;
}
return true;
}
StmtNode* Parser::table_add(IdentExprNode* type, IdentExprNodeList* templates,
IdentExprNode* id, string* size) {
StmtNode *Parser::table_add(IdentExprNode *type, IdentExprNodeList *templates,
IdentExprNode *id, string *size) {
auto table = new TableDeclStmtNode(IdentExprNode::Ptr(type),
move(*templates),
IdentExprNode::Ptr(id), size);
......@@ -166,9 +147,9 @@ StmtNode* Parser::table_add(IdentExprNode* type, IdentExprNodeList* templates,
return table;
}
StmtNode* Parser::struct_add(IdentExprNode* type, FormalList* formals) {
StmtNode * Parser::struct_add(IdentExprNode *type, FormalList *formals) {
auto struct_decl = new StructDeclStmtNode(IdentExprNode::Ptr(type), move(*formals));
if (scopes_->top_struct()->lookup(type->name_, true) != NULL) {
if (scopes_->top_struct()->lookup(type->name_, SCOPE_LOCAL) != NULL) {
fprintf(stderr, "redeclaration of struct %s\n", type->name_.c_str());
return struct_decl;
}
......@@ -192,11 +173,7 @@ StmtNode* Parser::struct_add(IdentExprNode* type, FormalList* formals) {
return struct_decl;
}
StmtNode* Parser::result_add(int token, IdentExprNode* id, FormalList* formals, BlockStmtNode* body) {
// result arguments are pass-by-reference instead of value
for (auto it = formals->begin(); it != formals->end(); ++it) {
(*it)->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
}
StmtNode * Parser::result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body) {
StmtNode *stmt = NULL;
switch (token) {
case Tok::TMATCH:
......@@ -214,6 +191,24 @@ StmtNode* Parser::result_add(int token, IdentExprNode* id, FormalList* formals,
return stmt;
}
StmtNode * Parser::func_add(vector<int> *types, Scopes::StateScope *scope,
IdentExprNode *id, FormalList *formals, BlockStmtNode *body) {
auto decl = new FuncDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body));
if (scopes_->top_func()->lookup(decl->id_->name_, SCOPE_LOCAL)) {
fprintf(stderr, "redeclaration of func %s\n", id->name_.c_str());
return decl;
}
auto cur_scope = scopes_->current_var();
scopes_->set_current(scope);
for (auto it = formals->begin(); it != formals->end(); ++it)
if (!variable_add(nullptr, it->get()))
return nullptr;
scopes_->set_current(cur_scope);
decl->scope_ = scope;
scopes_->top_func()->add(id->name_, decl);
return decl;
}
void Parser::set_loc(Node *n, const BisonParser::location_type &loc) const {
n->line_ = loc.begin.line;
n->column_ = loc.begin.column;
......@@ -222,7 +217,7 @@ void Parser::set_loc(Node *n, const BisonParser::location_type &loc) const {
string Parser::pragma(const string &name) const {
auto it = pragmas_.find(name);
if (it == pragmas_.end()) return "";
if (it == pragmas_.end()) return "main";
return it->second;
}
......
......@@ -26,8 +26,9 @@
namespace ebpf {
namespace cc {
using std::string;
using std::pair;
using std::string;
using std::vector;
class Parser {
public:
......@@ -40,22 +41,23 @@ class Parser {
return parser.parse();
}
VariableDeclStmtNode* variable_add(VariableDeclStmtNode* decl);
VariableDeclStmtNode* variable_add(VariableDeclStmtNode* decl, ExprNode* init_expr);
StructVariableDeclStmtNode* variable_add(StructVariableDeclStmtNode* decl, ExprNodeList* args, bool is_kv);
StmtNode* state_add(Scopes::StateScope* scope, IdentExprNode* id1, BlockStmtNode* body);
StmtNode* timer_add(Scopes::StateScope* scope, BlockStmtNode* body);
StmtNode* state_add(Scopes::StateScope* scope, IdentExprNode* id1, IdentExprNode* id2, BlockStmtNode* body);
StmtNode* table_add(IdentExprNode* type, IdentExprNodeList* templates, IdentExprNode* id, string* size);
StmtNode* struct_add(IdentExprNode* type, FormalList* formals);
StmtNode* result_add(int token, IdentExprNode* id, FormalList* formals, BlockStmtNode* body);
bool variable_exists(VariableDeclStmtNode* decl, bool search_local = true);
bool table_exists(TableDeclStmtNode* decl, bool search_local = true);
VariableDeclStmtNode * variable_add(vector<int> *types, VariableDeclStmtNode *decl);
VariableDeclStmtNode * variable_add(vector<int> *types, VariableDeclStmtNode *decl, ExprNode *init_expr);
StructVariableDeclStmtNode * variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv);
StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, BlockStmtNode *body);
StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body);
StmtNode * func_add(std::vector<int> *types, Scopes::StateScope *scope,
IdentExprNode *id, FormalList *formals, BlockStmtNode *body);
StmtNode * table_add(IdentExprNode *type, IdentExprNodeList *templates, IdentExprNode *id, string *size);
StmtNode * struct_add(IdentExprNode *type, FormalList *formals);
StmtNode * result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body);
bool variable_exists(VariableDeclStmtNode *decl) const;
bool table_exists(TableDeclStmtNode *decl, bool search_local = true);
void add_pragma(const std::string& pr, const std::string& v) { pragmas_[pr] = v; }
void set_loc(Node *n, const BisonParser::location_type &loc) const;
std::string pragma(const std::string &name) const;
Node* root_node_;
Node *root_node_;
Scopes::Ptr scopes_;
std::map<std::string, std::string> pragmas_;
private:
......
......@@ -68,17 +68,19 @@
FormalList *formals;
VariableDeclStmtNode *decl;
StructVariableDeclStmtNode *type_decl;
TableIndexExprNode *table_index;
std::vector<int> *type_specifiers;
std::string* string;
int token;
VersionStmtNode *ver;
}
/* Define the terminal symbols. */
%token <string> TIDENTIFIER TINTEGER THEXINTEGER TPRAGMA TSTRING
%token <token> TU8 TU16 TU32 TU64
%token <token> TEQUAL TCEQ TCNE TCLT TCLE TCGT TCGE TAND TOR
%token <token> TLPAREN TRPAREN TLBRACE TRBRACE TLBRACK TRBRACK
%token <token> TDOT TCOMMA TPLUS TMINUS TMUL TDIV TMOD TXOR TDOLLAR TCOLON TSCOPE TNOT TSEMI TCMPL TLAND TLOR
%token <token> TCONST TSTRUCT TVAR TSTATE TTIMER TGOTO TCONTINUE TNEXT TTRUE TFALSE TRETURN
%token <token> TDOT TARROW TCOMMA TPLUS TMINUS TMUL TDIV TMOD TXOR TDOLLAR TCOLON TSCOPE TNOT TSEMI TCMPL TLAND TLOR
%token <token> TSTRUCT TSTATE TFUNC TGOTO TCONTINUE TNEXT TTRUE TFALSE TRETURN
%token <token> TIF TELSE TSWITCH TCASE
%token <token> TMATCH TMISS TFAILURE TVALID
%token <token> TAT
......@@ -88,24 +90,26 @@
%type <expr> expr assign_expr return_expr init_arg_kv
%type <numeric> numeric
%type <bitop> bitop
%type <args> call_args init_args init_args_kv
%type <args> call_args /*init_args*/ init_args_kv
%type <ident_args> table_decl_args
%type <formals> struct_decl_stmts formals
%type <block> program block prog_decls
%type <decl> decl_stmt int_decl ref_stmt
%type <type_decl> type_decl ptr_decl
%type <stmt> stmt prog_decl var_decl struct_decl state_decl timer_decl
%type <stmt> stmt prog_decl var_decl struct_decl state_decl func_decl
%type <stmt> table_decl table_result_stmt if_stmt switch_stmt case_stmt onvalid_stmt
%type <var_scope> enter_varscope exit_varscope
%type <state_scope> enter_statescope exit_statescope
%type <stmts> stmts table_result_stmts case_stmts
%type <call> call_expr
%type <table_index> table_index_expr
%type <type_specifiers> type_specifiers
%type <stmt> pragma_decl
%type <ver> version_stmt
%type <token> type_specifier
/* taken from C++ operator precedence wiki page */
%nonassoc TSCOPE
%left TDOT TLBRACK TLBRACE TLPAREN
%left TDOT TLBRACK TLBRACE TLPAREN TINCR TDECR
%right TNOT TCMPL
%left TMUL
%left TDIV
......@@ -127,17 +131,10 @@
%%
program
: enter_statescope enter_varscope version_stmt prog_decls exit_varscope exit_statescope
{ parser.root_node_ = $4; $4->scope_ = $2; $4->ver_ = (*$3); delete $3;}
: enter_statescope enter_varscope prog_decls exit_varscope exit_statescope
{ parser.root_node_ = $3; $3->scope_ = $2; }
;
version_stmt
: TAT TINTEGER TDOT TINTEGER TDOT TINTEGER TSEMI
{
$$ = new VersionStmtNode(atoi($2->c_str()), atoi($4->c_str()), atoi($6->c_str()));
}
;
/* program is a list of declarations */
prog_decls
: prog_decl
......@@ -156,10 +153,10 @@ prog_decls
prog_decl
: var_decl TSEMI
| struct_decl TSEMI
| state_decl TSEMI
| timer_decl TSEMI
| state_decl
| table_decl TSEMI
| pragma_decl TSEMI
| pragma_decl
| func_decl
;
pragma_decl
......@@ -194,12 +191,12 @@ stmt
| call_expr TLBRACE TRBRACE TSEMI // support empty curly braces
{ $$ = new ExprStmtNode(ExprNode::Ptr($1));
parser.set_loc($$, @$); }
| if_stmt TSEMI
| switch_stmt TSEMI
| if_stmt
| switch_stmt
| var_decl TSEMI
{ $$ = $1; }
| state_decl TSEMI
| onvalid_stmt TSEMI
| state_decl
| onvalid_stmt
;
call_expr
......@@ -229,10 +226,10 @@ struct_decl
;
struct_decl_stmts
: decl_stmt TSEMI
{ $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr($1)); }
| struct_decl_stmts decl_stmt TSEMI
{ $1->push_back(VariableDeclStmtNode::Ptr($2)); }
: type_specifiers decl_stmt TSEMI
{ $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr($2)); }
| struct_decl_stmts type_specifiers decl_stmt TSEMI
{ $1->push_back(VariableDeclStmtNode::Ptr($3)); }
;
table_decl
......@@ -251,18 +248,22 @@ table_decl_args
state_decl
: TSTATE scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope
{ $$ = parser.state_add($3, $2, $5); $5->scope_ = $4;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TSTATE scoped_ident TCOMMA TMUL enter_statescope enter_varscope block exit_varscope exit_statescope
{ $$ = parser.state_add($5, $2, new IdentExprNode(""), $7); $7->scope_ = $6;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TSTATE scoped_ident TCOMMA scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope
{ $$ = parser.state_add($5, $2, $4, $7); $7->scope_ = $6;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
;
timer_decl
: TTIMER enter_statescope enter_varscope block exit_varscope exit_statescope
{ $$ = parser.timer_add($2, $4); $4->scope_ = $3;
func_decl
: type_specifiers ident enter_statescope enter_varscope TLPAREN formals TRPAREN block exit_varscope exit_statescope
{ $$ = parser.func_add($1, $3, $2, $6, $8); $8->scope_ = $4;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
;
......@@ -276,37 +277,56 @@ table_result_stmts
table_result_stmt
: TMATCH ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI
{ $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TMISS ident enter_varscope TLPAREN TRPAREN block exit_varscope TSEMI
{ $$ = parser.result_add($1, $2, new FormalList, $6); $6->scope_ = $3;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TFAILURE ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI
{ $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3;
if (!$$) YYERROR;
parser.set_loc($$, @$); }
;
formals
: TVAR ptr_decl
{ $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr(parser.variable_add($2))); }
| formals TCOMMA TVAR ptr_decl
{ $1->push_back(VariableDeclStmtNode::Ptr(parser.variable_add($4))); }
: TSTRUCT ptr_decl
{ $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $2))); }
| formals TCOMMA TSTRUCT ptr_decl
{ $1->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $4))); }
;
type_specifier
: TU8
| TU16
| TU32
| TU64
;
type_specifiers
: type_specifier { $$ = new std::vector<int>; $$->push_back($1); }
| type_specifiers type_specifier { $$->push_back($2); }
;
var_decl
: TVAR decl_stmt
{ $$ = parser.variable_add($2);
: type_specifiers decl_stmt
{ $$ = parser.variable_add($1, $2);
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TVAR int_decl TEQUAL expr
{ $$ = parser.variable_add($2, $4);
| type_specifiers int_decl TEQUAL expr
{ $$ = parser.variable_add($1, $2, $4);
if (!$$) YYERROR;
parser.set_loc($$, @$); }
| TVAR type_decl TLBRACE init_args_kv TRBRACE
{ $$ = parser.variable_add($2, $4, true);
| TSTRUCT type_decl TEQUAL TLBRACE init_args_kv TRBRACE
{ $$ = parser.variable_add($2, $5, true);
if (!$$) YYERROR;
parser.set_loc($$, @$); }
/*| TVAR type_decl TLBRACE init_args TRBRACE
{ $$ = parser.variable_add($2, $4, false);
/*| TSTRUCT type_decl TEQUAL TLBRACE init_args TRBRACE
{ $$ = parser.variable_add($2, $5, false);
parser.set_loc($$, @$); }*/
| TVAR ref_stmt
{ $$ = parser.variable_add($2);
| TSTRUCT ref_stmt
{ $$ = parser.variable_add(nullptr, $2);
if (!$$) YYERROR;
parser.set_loc($$, @$); }
;
......@@ -331,10 +351,10 @@ ptr_decl : scoped_ident TMUL ident
;
/* normal initializer */
init_args
/* init_args
: expr { $$ = new ExprNodeList; $$->push_back(ExprNode::Ptr($1)); }
| init_args TCOMMA expr { $$->push_back(ExprNode::Ptr($3)); }
;
;*/
/* one or more of "field" = "expr" */
init_args_kv
......@@ -342,11 +362,11 @@ init_args_kv
| init_args_kv TCOMMA init_arg_kv { $$->push_back(ExprNode::Ptr($3)); }
;
init_arg_kv
: ident TEQUAL expr
{ $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($3));
: TDOT ident TEQUAL expr
{ $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($4));
parser.set_loc($$, @$); }
| ident bitop TEQUAL expr
{ $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($4)); $$->bitop_ = BitopExprNode::Ptr($2);
| TDOT ident bitop TEQUAL expr
{ $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($5)); $$->bitop_ = BitopExprNode::Ptr($3);
parser.set_loc($$, @$); }
;
......@@ -354,7 +374,7 @@ if_stmt
: TIF expr enter_varscope block exit_varscope
{ $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4));
$4->scope_ = $3;
parser.set_loc($$, @$); }
parser.set_loc($$, @$); }
| TIF expr enter_varscope block exit_varscope TELSE enter_varscope block exit_varscope
{ $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4), StmtNode::Ptr($8));
$4->scope_ = $3; $8->scope_ = $7;
......@@ -439,6 +459,10 @@ expr
{ $$ = $1; }
| call_expr bitop
{ $$ = $1; $$->bitop_ = BitopExprNode::Ptr($2); }
| table_index_expr
{ $$ = $1; }
| table_index_expr TDOT ident
{ $$ = $1; $1->sub_ = IdentExprNode::Ptr($3); }
| any_ident
{ $$ = $1; }
| TAT dotted_ident
......@@ -464,6 +488,9 @@ expr
{ $$ = $2; }
| TLPAREN expr TRPAREN bitop
{ $$ = $2; $$->bitop_ = BitopExprNode::Ptr($4); }
| TSTRING
{ $$ = new StringExprNode($1);
parser.set_loc($$, @$); }
| numeric
{ $$ = $1; }
| numeric bitop
......@@ -544,6 +571,12 @@ bitop
parser.set_loc($$, @$); }
;
table_index_expr
: dotted_ident TLBRACK ident TRBRACK
{ $$ = new TableIndexExprNode(IdentExprNode::Ptr($1), IdentExprNode::Ptr($3));
parser.set_loc($$, @$); }
;
scoped_ident
: ident
{ $$ = $1; }
......@@ -561,6 +594,8 @@ dotted_ident
any_ident
: ident
{ $$ = $1; }
| dotted_ident TARROW TIDENTIFIER
{ $$->append_dot(*$3); delete $3; }
| dotted_ident TDOT TIDENTIFIER
{ $$->append_dot(*$3); delete $3; }
| scoped_ident TSCOPE TIDENTIFIER
......
......@@ -30,7 +30,6 @@ void Printer::print_indent() {
StatusTuple Printer::visit_block_stmt_node(BlockStmtNode* n) {
fprintf(out_, "{\n");
TRY2(n->ver_.accept(this));
if (!n->stmts_.empty()) {
++indent_;
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
......@@ -44,14 +43,6 @@ StatusTuple Printer::visit_block_stmt_node(BlockStmtNode* n) {
return mkstatus(0);
}
StatusTuple Printer::visit_version_stmt_node(VersionStmtNode* n) {
uint32_t version;
version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
fprintf(out_, "static const uint32_t plumlet_version __attribute__"
"((section (\".version\"), used)) = 0x%x;\n", version);
return mkstatus(0);
}
StatusTuple Printer::visit_if_stmt_node(IfStmtNode* n) {
fprintf(out_, "if ");
TRY2(n->cond_->accept(this));
......@@ -124,6 +115,11 @@ StatusTuple Printer::visit_integer_expr_node(IntegerExprNode* n) {
return mkstatus(0);
}
StatusTuple Printer::visit_string_expr_node(StringExprNode *n) {
fprintf(out_, "%s", n->val_.c_str());
return mkstatus(0);
}
StatusTuple Printer::visit_binop_expr_node(BinopExprNode* n) {
TRY2(n->lhs_->accept(this));
fprintf(out_, "%d", n->op_);
......@@ -186,6 +182,13 @@ StatusTuple Printer::visit_method_call_expr_node(MethodCallExprNode* n) {
return mkstatus(0);
}
StatusTuple Printer::visit_table_index_expr_node(TableIndexExprNode *n) {
fprintf(out_, "%s[", n->id_->c_str());
TRY2(n->index_->accept(this));
fprintf(out_, "]");
return mkstatus(0);
}
StatusTuple Printer::visit_expr_stmt_node(ExprStmtNode* n) {
TRY2(n->expr_->accept(this));
return mkstatus(0);
......@@ -235,15 +238,6 @@ StatusTuple Printer::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple Printer::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
if (!n->id_) {
return mkstatus(0);
}
fprintf(out_, "timer ");
TRY2(n->id_->accept(this));
return mkstatus(0);
}
StatusTuple Printer::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
if (!n->id_) {
return mkstatus(0);
......@@ -324,5 +318,20 @@ StatusTuple Printer::visit_table_decl_stmt_node(TableDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple Printer::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
fprintf(out_, "func ");
TRY2(n->id_->accept(this));
fprintf(out_, "(");
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
TRY2((*it)->accept(this));
if (it + 1 != n->formals_.end()) {
fprintf(out_, ", ");
}
}
fprintf(out_, ") ");
TRY2(n->block_->accept(this));
return mkstatus(0);
}
} // namespace cc
} // namespace ebpf
......@@ -33,28 +33,30 @@ using std::pair;
using std::unique_ptr;
class StateDeclStmtNode;
class TimerDeclStmtNode;
class VariableDeclStmtNode;
class TableDeclStmtNode;
class StructDeclStmtNode;
class FuncDeclStmtNode;
enum search_type { SCOPE_LOCAL, SCOPE_GLOBAL };
template <typename T>
class Scope {
public:
Scope() {}
Scope(Scope<T>* scope, int id) : parent_(scope), id_(id) {}
enum search_type { LOCAL, GLOBAL };
T* lookup(const string& name, bool search_local = true) {
T* lookup(const string &name, bool search_local = true) {
return lookup(name, search_local ? SCOPE_LOCAL : SCOPE_GLOBAL);
}
T * lookup(const string &name, search_type stype) {
auto it = elems_.find(name);
if (it != elems_.end()) {
if (it != elems_.end())
return it->second;
}
if (search_local || !parent_) {
return NULL;
}
return parent_->lookup(name, search_local);
if (stype == SCOPE_LOCAL || !parent_)
return nullptr;
return parent_->lookup(name, stype);
}
void add(const string& name, T* n) {
elems_[name] = n;
......@@ -80,23 +82,51 @@ class Scopes {
typedef unique_ptr<Scopes> Ptr;
typedef Scope<StructDeclStmtNode> StructScope;
typedef Scope<StateDeclStmtNode> StateScope;
typedef Scope<TimerDeclStmtNode> TimerScope;
typedef Scope<VariableDeclStmtNode> VarScope;
typedef Scope<TableDeclStmtNode> TableScope;
typedef Scope<FuncDeclStmtNode> FuncScope;
Scopes() : var_id__(0), state_id_(0), var_id_(0),
current_var_scope_(NULL), top_var_scope_(NULL),
current_state_scope_(NULL), top_state_scope_(NULL),
top_timer_scope_(new TimerScope(NULL, 1)),
top_struct_scope_(new StructScope(NULL, 1)),
top_table_scope_(new TableScope(NULL, 1)) {}
current_var_scope_(nullptr), top_var_scope_(nullptr),
current_state_scope_(nullptr), top_state_scope_(nullptr),
top_struct_scope_(new StructScope(nullptr, 1)),
top_table_scope_(new TableScope(nullptr, 1)),
top_func_scope_(new FuncScope(nullptr, 1)) {}
~Scopes() {
delete top_timer_scope_;
delete top_func_scope_;
delete top_struct_scope_;
delete top_table_scope_;
delete top_state_scope_;
}
void push_var(VarScope *scope) {
if (scope == top_var_scope_)
return;
scope->parent_ = current_var_scope_;
current_var_scope_ = scope;
}
void pop_var() {
if (current_var_scope_ == top_var_scope_)
return;
VarScope *old = current_var_scope_;
current_var_scope_ = old->parent_;
old->parent_ = nullptr;
}
void push_state(StateScope *scope) {
if (scope == top_state_scope_)
return;
scope->parent_ = current_state_scope_;
current_state_scope_ = scope;
}
void pop_state() {
if (current_state_scope_ == top_state_scope_)
return;
StateScope *old = current_state_scope_;
current_state_scope_ = old->parent_;
old->parent_ = nullptr;
}
/// While building the AST, allocate a new scope
VarScope* enter_var_scope() {
current_var_scope_ = new VarScope(current_var_scope_, next_var_id());
......@@ -125,18 +155,17 @@ class Scopes {
}
void set_current(VarScope* s) { current_var_scope_ = s; }
VarScope* current_var() { return current_var_scope_; }
VarScope* top_var() { return top_var_scope_; }
VarScope* current_var() const { return current_var_scope_; }
VarScope* top_var() const { return top_var_scope_; }
void set_current(StateScope* s) { current_state_scope_ = s; }
StateScope* current_state() { return current_state_scope_; }
StateScope* top_state() { return top_state_scope_; }
TimerScope* top_timer() { return top_timer_scope_; }
StateScope* current_state() const { return current_state_scope_; }
StateScope* top_state() const { return top_state_scope_; }
StructScope* top_struct() { return top_struct_scope_; }
StructScope* top_struct() const { return top_struct_scope_; }
TableScope* top_table() { return top_table_scope_; }
TableScope* top_table() const { return top_table_scope_; }
FuncScope* top_func() const { return top_func_scope_; }
int next_id() { return ++var_id__; }
int next_state_id() { return ++state_id_; }
......@@ -149,9 +178,9 @@ class Scopes {
VarScope* top_var_scope_;
StateScope* current_state_scope_;
StateScope* top_state_scope_;
TimerScope* top_timer_scope_;
StructScope* top_struct_scope_;
TableScope* top_table_scope_;
FuncScope* top_func_scope_;
};
} // namespace cc
......
......@@ -28,36 +28,24 @@ namespace cc {
using std::for_each;
using std::set;
StatusTuple TypeCheck::visit_block_stmt_node(BlockStmtNode* n) {
StatusTuple TypeCheck::visit_block_stmt_node(BlockStmtNode *n) {
// enter scope
auto scope = scopes_->current_var();
if (n->scope_) {
scopes_->set_current(n->scope_);
}
n->ver_.accept(this);
if (n->scope_)
scopes_->push_var(n->scope_);
if (!n->stmts_.empty()) {
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
//try {
TRY2((*it)->accept(this));
//} catch (CompilerException& e) {
// errors_.push_back(e.what());
//}
}
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it)
TRY2((*it)->accept(this));
}
// exit scope
scopes_->set_current(scope);
if (n->scope_)
scopes_->pop_var();
return mkstatus(0);
}
StatusTuple TypeCheck::visit_version_stmt_node(VersionStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode* n) {
StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode *n) {
TRY2(n->cond_->accept(this));
if (n->cond_->typeof_ != ExprNode::INTEGER)
return mkstatus_(n, "If condition must be a numeric type");
//if (n->cond_->typeof_ != ExprNode::INTEGER)
// return mkstatus_(n, "If condition must be a numeric type");
TRY2(n->true_block_->accept(this));
if (n->false_block_) {
TRY2(n->false_block_->accept(this));
......@@ -65,7 +53,7 @@ StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode* n) {
StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode *n) {
TRY2(n->cond_->accept(this));
auto sdecl = static_cast<StructVariableDeclStmtNode*>(n->cond_->decl_);
if (sdecl->storage_type_ != StructVariableDeclStmtNode::STRUCT_REFERENCE)
......@@ -77,7 +65,7 @@ StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode* n) {
StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode *n) {
TRY2(n->cond_->accept(this));
if (n->cond_->typeof_ != ExprNode::INTEGER)
return mkstatus_(n, "Switch condition must be a numeric type");
......@@ -88,7 +76,7 @@ StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode* n) {
StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode *n) {
if (n->value_) {
TRY2(n->value_->accept(this));
if (n->value_->typeof_ != ExprNode::INTEGER)
......@@ -98,8 +86,8 @@ StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode* n) {
n->decl_ = scopes_->current_var()->lookup(n->name_, false);
StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode *n) {
n->decl_ = scopes_->current_var()->lookup(n->name_, SCOPE_GLOBAL);
if (!n->decl_)
return mkstatus_(n, "Variable %s lookup failed", n->c_str());
......@@ -148,7 +136,7 @@ StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode* n) {
StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode *n) {
/// @todo check lhs is assignable
TRY2(n->id_->accept(this));
if (n->id_->typeof_ == ExprNode::STRUCT) {
......@@ -168,7 +156,7 @@ StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode* n) {
StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode *n) {
StructDeclStmtNode *struct_type = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
if (!struct_type)
return mkstatus_(n, "Undefined packet header %s", n->id_->c_str());
......@@ -189,13 +177,20 @@ StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_integer_expr_node(IntegerExprNode* n) {
StatusTuple TypeCheck::visit_integer_expr_node(IntegerExprNode *n) {
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = n->bits_;
return mkstatus(0);
}
StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode* n) {
StatusTuple TypeCheck::visit_string_expr_node(StringExprNode *n) {
n->typeof_ = ExprNode::STRING;
n->flags_[ExprNode::IS_REF] = true;
n->bit_width_ = n->val_.size() << 3;
return mkstatus(0);
}
StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode *n) {
TRY2(n->lhs_->accept(this));
if (n->lhs_->typeof_ != ExprNode::INTEGER)
return mkstatus_(n, "Left-hand side of binary expression must be a numeric type");
......@@ -217,7 +212,7 @@ StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode* n) {
StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode *n) {
TRY2(n->expr_->accept(this));
if (n->expr_->typeof_ != ExprNode::INTEGER)
return mkstatus_(n, "Unary operand must be a numeric type");
......@@ -225,26 +220,26 @@ StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_bitop_expr_node(BitopExprNode* n) {
StatusTuple TypeCheck::visit_bitop_expr_node(BitopExprNode *n) {
if (n->expr_->typeof_ != ExprNode::INTEGER)
return mkstatus_(n, "Bitop [] can only operate on numeric types");
n->typeof_ = ExprNode::INTEGER;
return mkstatus(0);
}
StatusTuple TypeCheck::visit_goto_expr_node(GotoExprNode* n) {
StatusTuple TypeCheck::visit_goto_expr_node(GotoExprNode *n) {
//n->id_->accept(this);
n->typeof_ = ExprNode::VOID;
return mkstatus(0);
}
StatusTuple TypeCheck::visit_return_expr_node(ReturnExprNode* n) {
StatusTuple TypeCheck::visit_return_expr_node(ReturnExprNode *n) {
TRY2(n->expr_->accept(this));
n->typeof_ = ExprNode::VOID;
return mkstatus(0);
}
StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode* n, size_t num, size_t num_def_args = 0) {
StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode *n, size_t num, size_t num_def_args = 0) {
if (num_def_args == 0) {
if (n->args_.size() != num)
return mkstatus_(n, "%s expected %d argument%s, %zu given", n->id_->sub_name_.c_str(),
......@@ -257,7 +252,7 @@ StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode* n, size_t num, size
return mkstatus(0);
}
StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode* n) {
StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode *n) {
auto table = scopes_->top_table()->lookup(n->id_->name_);
if (!table)
return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
......@@ -273,7 +268,7 @@ StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::check_update_method(MethodCallExprNode* n) {
StatusTuple TypeCheck::check_update_method(MethodCallExprNode *n) {
auto table = scopes_->top_table()->lookup(n->id_->name_);
if (!table)
return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
......@@ -284,7 +279,7 @@ StatusTuple TypeCheck::check_update_method(MethodCallExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::check_delete_method(MethodCallExprNode* n) {
StatusTuple TypeCheck::check_delete_method(MethodCallExprNode *n) {
auto table = scopes_->top_table()->lookup(n->id_->name_);
if (!table)
return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
......@@ -295,7 +290,7 @@ StatusTuple TypeCheck::check_delete_method(MethodCallExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode* n) {
StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode *n) {
// be sure to visit those child nodes ASAP, so their properties can
// be propagated up to this node and be ready to be used
for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
......@@ -310,51 +305,23 @@ StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode* n) {
TRY2(check_update_method(n));
} else if (n->id_->sub_name_ == "delete") {
TRY2(check_delete_method(n));
} else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 1));
} else if (n->id_->sub_name_ == "drop" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 0));
} else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 2, 1));
} else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 1));
} else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 1));
} else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 0));
} else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
TRY2(expect_method_arg(n, 2));
n->args_[0]->flags_[ExprNode::IS_LHS] = true;
}
} else if (n->id_->name_ == "channel_push") {
TRY2(expect_method_arg(n, 1));
} else if (n->id_->name_ == "log") {
TRY2(expect_method_arg(n, 1));
if (n->args_.size() < 1)
return mkstatus_(n, "%s expected at least 1 argument", n->id_->c_str());
if (n->args_[0]->typeof_ != ExprNode::STRING)
return mkstatus_(n, "%s expected a string for argument 1", n->id_->c_str());
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 32;
} else if (n->id_->name_ == "atomic_add") {
TRY2(expect_method_arg(n, 2));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = n->args_[0]->bit_width_;
n->args_[0]->flags_[ExprNode::IS_LHS] = true;
} else if (n->id_->name_ == "cksum") {
TRY2(expect_method_arg(n, 1));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 16;
} else if (n->id_->name_ == "incr_cksum_u16") {
TRY2(expect_method_arg(n, 4, 1));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 16;
} else if (n->id_->name_ == "incr_cksum_u32") {
TRY2(expect_method_arg(n, 4, 1));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 16;
} else if (n->id_->name_ == "incr_cksum") {
TRY2(expect_method_arg(n, 4, 1));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 16;
} else if (n->id_->name_ == "lb_hash") {
TRY2(expect_method_arg(n, 3, 1));
n->typeof_ = ExprNode::INTEGER;
n->bit_width_ = 8;
} else if (n->id_->name_ == "sizeof") {
TRY2(expect_method_arg(n, 1));
n->typeof_ = ExprNode::INTEGER;
......@@ -373,12 +340,32 @@ StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_expr_stmt_node(ExprStmtNode* n) {
StatusTuple TypeCheck::visit_table_index_expr_node(TableIndexExprNode *n) {
n->table_ = scopes_->top_table()->lookup(n->id_->name_);
if (!n->table_) return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
TRY2(n->index_->accept(this));
if (n->index_->struct_type_ != n->table_->key_type_)
return mkstatus_(n, "Key to table %s lookup must be of type %s", n->id_->c_str(), n->table_->key_id()->c_str());
if (n->sub_) {
n->sub_decl_ = n->table_->leaf_type_->field(n->sub_->name_);
if (!n->sub_decl_)
return mkstatus_(n, "Field %s is not a member of %s", n->sub_->c_str(), n->table_->leaf_id()->c_str());
n->typeof_ = ExprNode::INTEGER;
} else {
n->typeof_ = ExprNode::STRUCT;
n->flags_[ExprNode::IS_REF] = true;
n->struct_type_ = n->table_->leaf_type_;
}
return mkstatus(0);
}
StatusTuple TypeCheck::visit_expr_stmt_node(ExprStmtNode *n) {
TRY2(n->expr_->accept(this));
return mkstatus(0);
}
StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode* n) {
StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode *n) {
//TRY2(n->struct_id_->accept(this));
//TRY2(n->id_->accept(this));
if (!n->init_.empty()) {
......@@ -412,7 +399,7 @@ StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclSt
return mkstatus(0);
}
StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode* n) {
StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) {
//TRY2(n->id_->accept(this));
if (!n->init_.empty()) {
TRY2(n->init_[0]->accept(this));
......@@ -420,7 +407,7 @@ StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDecl
return mkstatus(0);
}
StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode *n) {
//TRY2(n->id_->accept(this));
for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
TRY2((*it)->accept(this));
......@@ -428,20 +415,11 @@ StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_parser_state_stmt_node(ParserStateStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
auto timer_delay = make_unique<IntegerVariableDeclStmtNode>(make_unique<IdentExprNode>("timer_delay"), "32");
scopes_->current_var()->add("timer_delay", timer_delay.get());
n->block_->stmts_.push_back(move(timer_delay));
TRY2(n->block_->accept(this));
StatusTuple TypeCheck::visit_parser_state_stmt_node(ParserStateStmtNode *n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
if (!n->id_) {
return mkstatus(0);
}
......@@ -476,8 +454,7 @@ StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
}
for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) {
auto scope = scopes_->current_state();
scopes_->set_current(it->scope_);
scopes_->push_state(it->scope_);
TRY2(it->block_->accept(this));
......@@ -497,12 +474,12 @@ StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
}
}
scopes_->set_current(scope);
scopes_->pop_state();
}
return mkstatus(0);
}
StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode* n) {
StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode *n) {
//TRY2(n->id_->accept(this));
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
TRY2((*it)->accept(this));
......@@ -511,7 +488,7 @@ StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode* n) {
StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode *n) {
//TRY2(n->id_->accept(this));
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
TRY2((*it)->accept(this));
......@@ -520,7 +497,7 @@ StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) {
StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) {
//TRY2(n->id_->accept(this));
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
TRY2((*it)->accept(this));
......@@ -529,96 +506,61 @@ StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) {
return mkstatus(0);
}
StatusTuple TypeCheck::visit_table_decl_stmt_node(TableDeclStmtNode* n) {
auto key_type = scopes_->top_struct()->lookup(n->key_id()->name_, true);
if (!key_type)
StatusTuple TypeCheck::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
n->key_type_ = scopes_->top_struct()->lookup(n->key_id()->name_, true);
if (!n->key_type_)
return mkstatus_(n, "Table key type %s undefined", n->key_id()->c_str());
n->key_id()->bit_width_ = key_type->bit_width_;
auto leaf_type = scopes_->top_struct()->lookup(n->leaf_id()->name_, true);
if (!leaf_type)
n->key_id()->bit_width_ = n->key_type_->bit_width_;
n->leaf_type_ = scopes_->top_struct()->lookup(n->leaf_id()->name_, true);
if (!n->leaf_type_)
return mkstatus_(n, "Table leaf type %s undefined", n->leaf_id()->c_str());
n->leaf_id()->bit_width_ = leaf_type->bit_width_;
n->leaf_id()->bit_width_ = n->leaf_type_->bit_width_;
if (n->type_id()->name_ == "INDEXED" && n->policy_id()->name_ != "AUTO") {
fprintf(stderr, "Table %s is INDEXED, policy should be AUTO\n", n->id_->c_str());
n->policy_id()->name_ = "AUTO";
}
if (n->policy_id()->name_ != "AUTO" && n->policy_id()->name_ != "NONE")
return mkstatus_(n, "Unsupported policy type %s", n->policy_id()->c_str());
return mkstatus(0);
}
StatusTuple TypeCheck::visit(Node* root) {
BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
StatusTuple TypeCheck::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
VariableDeclStmtNode *var = it->get();
TRY2(var->accept(this));
if (var->is_struct()) {
if (!var->is_pointer())
return mkstatus_(n, "Only struct references allowed in function definitions");
}
}
scopes_->push_state(n->scope_);
TRY2(n->block_->accept(this));
scopes_->pop_state();
return mkstatus(0);
}
if (pragmas_.count("name") == 0)
return mkstatus(-1, "#name <PackageName> must be defined");
StatusTuple TypeCheck::visit(Node *root) {
BlockStmtNode *b = static_cast<BlockStmtNode*>(root);
scopes_->set_current(scopes_->top_state());
scopes_->set_current(scopes_->top_var());
// add builtin types and identifiers
if (scopes_->top_struct()->lookup("_Packet", true)) {
return mkstatus(-1, "_Packet already defined");
}
auto pkt_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_Packet"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("port_id"), "8"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("length"), "16"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("parsed_bytes"), "16"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("vlan_tag"), "16"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg8"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg7"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg6"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg5"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg4"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg3"), "32")); // originator_plum
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg2"), "32")); // gid
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("arg1"), "32")); // from_fabric
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.crypto_proto"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.crypto_hr"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.crypto_mark"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.crypto_spi"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.src_ip"), "32"));
pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
make_unique<IdentExprNode>("tun_key.dst_ip"), "32"));
scopes_->top_struct()->add("_Packet", pkt_type.get());
b->stmts_.push_back(move(pkt_type));
if (scopes_->current_var()->lookup("pkt", true)) {
return mkstatus(-1, "pkt already defined");
}
auto pkt = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_Packet"),
make_unique<IdentExprNode>("pkt"));
pkt->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
scopes_->current_var()->add("pkt", pkt.get());
b->stmts_.push_back(move(pkt));
// packet data in bpf socket
if (scopes_->top_struct()->lookup("_skbuff", true)) {
return mkstatus(-1, "_skbuff already defined");
}
auto skb_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"));
scopes_->top_struct()->add("_skbuff", skb_type.get());
b->stmts_.push_back(move(skb_type));
if (scopes_->current_var()->lookup("skb", true)) {
return mkstatus(-1, "skb already defined");
}
auto skb = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"),
make_unique<IdentExprNode>("skb"));
skb->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
scopes_->current_var()->add("skb", skb.get());
b->stmts_.push_back(move(skb));
// // packet data in bpf socket
// if (scopes_->top_struct()->lookup("_skbuff", true)) {
// return mkstatus(-1, "_skbuff already defined");
// }
// auto skb_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"));
// scopes_->top_struct()->add("_skbuff", skb_type.get());
// b->stmts_.push_back(move(skb_type));
// if (scopes_->current_var()->lookup("skb", true)) {
// return mkstatus(-1, "skb already defined");
// }
// auto skb = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"),
// make_unique<IdentExprNode>("skb"));
// skb->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
// scopes_->current_var()->add("skb", skb.get());
// b->stmts_.push_back(move(skb));
// offset counter
auto parsed_bytes = make_unique<IntegerVariableDeclStmtNode>(
......
......@@ -118,6 +118,7 @@ enum bpf_map_type {
enum bpf_prog_type {
BPF_PROG_TYPE_UNSPEC,
BPF_PROG_TYPE_SOCKET_FILTER,
BPF_PROG_TYPE_KPROBE,
BPF_PROG_TYPE_SCHED_CLS,
BPF_PROG_TYPE_SCHED_ACT,
};
......@@ -155,6 +156,7 @@ union bpf_attr {
__u32 log_level; /* verbosity level of verifier */
__u32 log_size; /* size of user buffer */
__aligned_u64 log_buf; /* user supplied buffer */
__u32 kern_version; /* checked when prog_type=kprobe */
};
} __attribute__((aligned(8)));
......@@ -166,13 +168,16 @@ enum bpf_func_id {
BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */
BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */
BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */
BPF_FUNC_ktime_get_ns, /* u64 bpf_ktime_get_ns(void) */
BPF_FUNC_trace_printk, /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */
BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */
BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */
/**
* skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet
* @skb: pointer to skb
* @offset: offset within packet from skb->data
* @offset: offset within packet from skb->mac_header
* @from: pointer where to copy bytes from
* @len: number of bytes to store into packet
* @flags: bit 0 - if true, recompute skb->csum
......
#ifndef __LINUX_BPF_COMMON_H__
#define __LINUX_BPF_COMMON_H__
#ifndef _UAPI__LINUX_BPF_COMMON_H__
#define _UAPI__LINUX_BPF_COMMON_H__
/* Instruction classes */
#define BPF_CLASS(code) ((code) & 0x07)
......@@ -52,4 +52,4 @@
#define BPF_MAXINSNS 4096
#endif
#endif /* __LINUX_BPF_COMMON_H__ */
#endif /* _UAPI__LINUX_BPF_COMMON_H__ */
......@@ -27,6 +27,8 @@ int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex,
/* create RAW socket and bind to interface 'name' */
int bpf_open_raw_sock(const char *name);
int bpf_attach_kprobe(int progfd, const char *event, const char *event_desc, pid_t pid, int cpu, int group_fd);
#define LOG_BUF_SIZE 65536
extern char bpf_log_buf[LOG_BUF_SIZE];
......
configure_file(wrapper.sh.in "${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh" @ONLY)
set(TEST_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh)
add_subdirectory(jit)
add_test(NAME py_test1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_test1 ${CMAKE_CURRENT_SOURCE_DIR}/test1.py namespace)
add_test(NAME py_test2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_test2 ${CMAKE_CURRENT_SOURCE_DIR}/test2.py namespace)
add_test(NAME py_trace1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_trace1 ${CMAKE_CURRENT_SOURCE_DIR}/trace1.py sudo)
add_test(NAME py_trace2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_trace2 ${CMAKE_CURRENT_SOURCE_DIR}/trace2.py sudo)
#packed "false"
struct pt_regs {
u64 r15:64;
u64 r14:64;
u64 r13:64;
u64 r12:64;
u64 bp:64;
u64 bx:64;
u64 r11:64;
u64 r10:64;
u64 r9:64;
u64 r8:64;
u64 ax:64;
u64 cx:64;
u64 dx:64;
u64 si:64;
u64 di:64;
};
#!/usr/bin/env python
import sys
from src.bpf import BPF
from bpf import BPF
prog = BPF(sys.argv[1], sys.argv[2], sys.argv[3],
prog_type=int(sys.argv[4]), debug=int(sys.argv[5]))
#packed "true"
struct skbuff {
u32 len:32;
u32 pkt_type:32;
u32 mark:32;
u32 queue_mapping:32;
u32 protocol:32;
u32 vlan_present:32;
u32 vlan_tci:32;
u32 vlan_proto:32;
u32 priority:32;
};
struct ethernet {
u64 dst:48;
u64 src:48;
u32 type:16;
};
state ethernet {
switch $ethernet.type {
case 0x0800 {
next proto::ip;
};
case 0x8100 {
next proto::dot1q;
};
case * {
goto EOP;
};
}
}
struct dot1q {
u32 pri:3;
u32 cfi:1;
u32 vlanid:12;
u32 type:16;
};
state dot1q {
switch $dot1q.type {
case 0x0800 {
next proto::ip;
};
case * {
goto EOP;
};
}
}
struct ip {
u32 ver:4;
u32 hlen:4;
u32 tos:8;
u32 tlen:16;
u32 identification:16;
u32 ffo_unused:1;
u32 df:1;
u32 mf:1;
u32 foffset:13;
u32 ttl:8;
u32 nextp:8;
u32 hchecksum:16;
u32 src:32;
u32 dst:32;
};
state ip {
switch $ip.nextp {
case 6 {
next proto::tcp;
};
case 17 {
next proto::udp;
};
case 47 {
next proto::gre;
};
case * {
goto EOP;
};
}
}
struct udp {
u32 sport:16;
u32 dport:16;
u32 length:16;
u32 crc:16;
};
state udp {
switch $udp.dport {
case 8472 {
next proto::vxlan;
};
case * {
goto EOP;
};
}
}
struct tcp {
u16 src_port:16;
u16 dst_port:16;
u32 seq_num:32;
u32 ack_num:32;
u8 offset:4;
u8 reserved:4;
u8 flag_cwr:1;
u8 flag_ece:1;
u8 flag_urg:1;
u8 flag_ack:1;
u8 flag_psh:1;
u8 flag_rst:1;
u8 flag_syn:1;
u8 flag_fin:1;
u16 rcv_wnd:16;
u16 cksum:16;
u16 urg_ptr:16;
};
state tcp {
goto EOP;
}
struct vxlan {
u32 rsv1:4;
u32 iflag:1;
u32 rsv2:3;
u32 rsv3:24;
u32 key:24;
u32 rsv4:8;
};
state vxlan {
goto EOP;
}
struct gre {
u32 cflag:1;
u32 rflag:1;
u32 kflag:1;
u32 snflag:1;
u32 srflag:1;
u32 recurflag:3;
u32 reserved:5;
u32 vflag:3;
u32 protocol:16;
u32 key:32;
};
state gre {
switch $gre.protocol {
case * {
goto EOP;
};
}
}
@1.0.0
#packed 'true'
struct ethernet {
dst:48
src:48
type:16
}
state ethernet {
switch $ethernet.type {
case 0x0800 {
next proto::ip
}
case 0x8100 {
next proto::dot1q
}
case * {
goto EOP
}
}
}
struct dot1q {
pri:3
cfi:1
vlanid:12
type:16
}
state dot1q {
switch $dot1q.type {
case 0x0800 {
next proto::ip
}
case * {
goto EOP
}
}
}
struct ip {
ver:4
hlen:4
tos:8
tlen:16
identification:16
ffo_unused:1
df:1
mf:1
foffset:13
ttl:8
nextp:8
hchecksum:16
src:32
dst:32
}
state ip {
switch $ip.nextp {
case 6 {
next proto::tcp
}
case 17 {
next proto::udp
}
case 47 {
next proto::gre
}
case * {
goto EOP
}
}
}
struct udp {
sport:16
dport:16
length:16
crc:16
}
state udp {
switch $udp.dport {
case 8472 {
next proto::vxlan
}
case * {
goto EOP
}
}
}
struct tcp {
src_port:16
dst_port:16
seq_num:32
ack_num:32
offset:4
reserved:4
flag_cwr:1
flag_ece:1
flag_urg:1
flag_ack:1
flag_psh:1
flag_rst:1
flag_syn:1
flag_fin:1
rcv_wnd:16
cksum:16
urg_ptr:16
}
state tcp {
goto EOP
}
struct vxlan {
rsv1:4
iflag:1
rsv2:3
rsv3:24
key:24
rsv4:8
}
state vxlan {
goto EOP
}
struct gre {
cflag:1
rflag:1
kflag:1
snflag:1
srflag:1
recurflag:3
reserved:5
vflag:3
protocol:16
key:32
}
state gre {
switch $gre.protocol {
case * {
goto EOP
}
}
}
@1.0.0
#packed 'true'
struct ethernet {
dst:48
src:48
type:16
}
state ethernet {
switch $ethernet.type {
case * {
goto EOP
}
}
}
#packed "false"
struct IPKey {
u32 dip:32;
u32 sip:32;
};
struct IPLeaf {
u32 rx_pkts:64;
u32 tx_pkts:64;
};
Table<IPKey, IPLeaf, FIXED_MATCH, AUTO> stats(1024);
u32 main(struct proto::skbuff *skb) {
u32 ret:32 = 0;
goto proto::ethernet;
state proto::ethernet {
}
state proto::dot1q {
}
state proto::ip {
u32 rx:32 = 0;
u32 tx:32 = 0;
u32 IPKey key;
if $ip.dst > $ip.src {
key.dip = $ip.dst;
key.sip = $ip.src;
rx = 1;
// test arbitrary return stmt
if false {
return 3;
}
} else {
key.dip = $ip.src;
key.sip = $ip.dst;
tx = 1;
ret = 1;
}
struct IPLeaf *leaf;
leaf = stats[key];
on_valid(leaf) {
atomic_add(leaf.rx_pkts, rx);
atomic_add(leaf.tx_pkts, tx);
}
}
state proto::udp {
}
state proto::vxlan {
}
state proto::gre {
}
state EOP {
return ret;
}
}
@1.0.0
#name test1
struct SomeKey {
foo:3
foo1:9
foo2:63
}
struct SomeLeaf {
bar:64
}
Table<SomeKey, SomeLeaf, FIXED_MATCH, LRU> stats(8)
state INIT {
goto proto::ethernet
}
var ret:32 = 0
state proto::ethernet {
var SomeKey sk2{foo = 2}
stats.lookup(sk2) {
on_match stats (var SomeLeaf *l21) {
atomic_add(l21.bar, 1)
}
on_miss stats () {
ret = 1
}
}
var SomeKey sk3{foo = 3}
stats.lookup(sk3) {
}
var SomeKey sk4{foo = 4}
var SomeLeaf sl4{bar = 1}
stats.update(sk4, sl4) { }
var SomeKey sk5{foo = 5}
var SomeLeaf sl5{bar = 1}
stats.update(sk5, sl5) {
on_failure stats (var SomeKey *k21) {}
}
}
state EOP {
return ret
}
#!/usr/bin/env python
# test program to count the packets sent to a device in a .5
# second period
from ctypes import c_uint, c_ulong, Structure
from netaddr import IPAddress
from bpf import BPF
from subprocess import check_call
from unittest import main, TestCase
class Key(Structure):
_fields_ = [("dip", c_uint),
("sip", c_uint)]
class Leaf(Structure):
_fields_ = [("rx_pkts", c_ulong),
("tx_pkts", c_ulong)]
class TestBPFSocket(TestCase):
def setUp(self):
self.prog = BPF("main", "test1.b", "proto.b", debug=0)
self.prog.attach("eth0")
self.stats = self.prog.table("stats", Key, Leaf)
def test_ping(self):
cmd = ["ping", "-f", "-c", "100", "172.16.1.1"]
check_call(cmd)
#for key in self.stats.iter():
# leaf = self.stats.get(key)
# print(IPAddress(key.sip), "=>", IPAddress(key.dip),
# "rx", leaf.rx_pkts, "tx", leaf.tx_pkts)
key = Key(IPAddress("172.16.1.2").value, IPAddress("172.16.1.1").value)
leaf = self.stats.get(key)
self.assertEqual(leaf.rx_pkts, 100)
self.assertEqual(leaf.tx_pkts, 100)
if __name__ == "__main__":
main()
// test for packet modification
#packed "false"
struct IPKey {
u32 dip:32;
u32 sip:32;
};
struct IPLeaf {
u32 xdip:32;
u32 xsip:32;
u64 xlated_pkts:64;
};
Table<IPKey, IPLeaf, FIXED_MATCH, NONE> xlate(1024);
u32 main (struct proto::skbuff *skb) {
u32 ret:32 = 1;
u32 orig_dip:32 = 0;
u32 orig_sip:32 = 0;
struct IPLeaf *xleaf;
goto proto::ethernet;
state proto::ethernet {
}
state proto::dot1q {
}
state proto::ip {
orig_dip = $ip.dst;
orig_sip = $ip.src;
struct IPKey key = {.dip=orig_dip, .sip=orig_sip};
xlate.lookup(key, xleaf) {};
on_valid(xleaf) {
incr_cksum(@ip.hchecksum, orig_dip, xleaf.xdip);
incr_cksum(@ip.hchecksum, orig_sip, xleaf.xsip);
pkt.rewrite_field($ip.dst, xleaf.xdip);
pkt.rewrite_field($ip.src, xleaf.xsip);
atomic_add(xleaf.xlated_pkts, 1);
}
}
state proto::udp {
on_valid(xleaf) {
incr_cksum(@udp.crc, orig_dip, xleaf.xdip, 1);
incr_cksum(@udp.crc, orig_sip, xleaf.xsip, 1);
}
}
state proto::tcp {
on_valid(xleaf) {
incr_cksum(@tcp.cksum, orig_dip, xleaf.xdip, 1);
incr_cksum(@tcp.cksum, orig_sip, xleaf.xsip, 1);
}
}
state proto::vxlan {
}
state proto::gre {
}
state EOP {
return ret;
}
}
@1.0.0
#name 'socket1'
#packed 'false'
struct IPKey {
dip:32
sip:32
}
struct IPLeaf {
rx_pkts:64
tx_pkts:64
}
Table<IPKey, IPLeaf, FIXED_MATCH, LRU> stats(1024)
var ret:32 = 0
state INIT {
goto proto::ethernet
}
state proto::ethernet {
}
state proto::dot1q {
}
state proto::ip {
var rx:32 = 0
var tx:32 = 0
var IPKey key
if $ip.dst > $ip.src {
key.dip = $ip.dst
key.sip = $ip.src
rx = 1
if false {
return 3
}
} else {
key.dip = $ip.src
key.sip = $ip.dst
tx = 1
ret = 1
}
var IPLeaf *leaf
stats.lookup(key, leaf) {}
on_valid(leaf) {
atomic_add(leaf.rx_pkts, rx)
atomic_add(leaf.tx_pkts, tx)
} else {
var IPLeaf newleaf{rx_pkts = rx, tx_pkts = tx}
stats.update(key, newleaf) {}
}
}
state proto::udp {
}
state proto::vxlan {
}
state proto::gre {
}
state EOP {
return ret
}
#!/usr/bin/env python
# test program to count the packets sent to a device in a .5
# second period
import time
import netaddr
from ctypes import *
from src.bpf import BPF
prog = BPF("socket1", "tests/test2.dp", "tests/proto.dph")
from ctypes import c_uint, c_ulonglong, Structure
from netaddr import IPAddress
from bpf import BPF
from socket import socket, AF_INET, SOCK_DGRAM
from time import sleep
from unittest import main, TestCase
class Key(Structure):
_fields_ = [("dip", c_uint),
("sip", c_uint)]
class Leaf(Structure):
_fields_ = [("rx_pkts", c_ulong),
("tx_pkts", c_ulong)]
_fields_ = [("xdip", c_uint),
("xsip", c_uint),
("xlated_pkts", c_ulonglong)]
prog.attach("eth0")
stats = prog.table("stats", Key, Leaf)
class TestBPFSocket(TestCase):
def setUp(self):
self.prog = BPF("main", "test2.b", "proto.b",
BPF.BPF_PROG_TYPE_SCHED_CLS, debug=0)
with open("/sys/class/net/eth0/ifindex") as f:
ifindex = int(f.read())
self.prog.attach_filter(ifindex, 10, 1)
time.sleep(0.5)
def test_xlate(self):
xlate = self.prog.table("xlate", Key, Leaf)
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)
xlate.put(key, leaf)
udp = socket(AF_INET, SOCK_DGRAM)
udp.sendto(b"a" * 10, ("172.16.1.1", 5000))
leaf = xlate.get(key)
self.assertGreater(leaf.xlated_pkts, 0)
for key in stats.iter():
leaf = stats.get(key)
print(netaddr.IPAddress(key.sip), "=>", netaddr.IPAddress(key.dip),
"rx", leaf.rx_pkts, "tx", leaf.tx_pkts)
if __name__ == "__main__":
main()
// test for packet modification
@1.0.0
#name 'classifier'
#packed 'false'
struct IPKey {
dip:32
sip:32
}
struct IPLeaf {
xdip:32
xsip:32
xlated_pkts:64
}
Table<IPKey, IPLeaf, FIXED_MATCH, LRU> xlate(1024)
var ret:32 = 1
var orig_dip:32 = 0
var orig_sip:32 = 0
var IPLeaf *xleaf;
state INIT {
goto proto::ethernet
}
state proto::ethernet {
}
state proto::dot1q {
}
state proto::ip {
orig_dip = $ip.dst
orig_sip = $ip.src
var IPKey key{dip=orig_dip, sip=orig_sip}
xlate.lookup(key, xleaf) {}
on_valid(xleaf) {
incr_cksum(@ip.hchecksum, orig_dip, xleaf.xdip)
incr_cksum(@ip.hchecksum, orig_sip, xleaf.xsip)
pkt.rewrite_field($ip.dst, xleaf.xdip)
pkt.rewrite_field($ip.src, xleaf.xsip)
atomic_add(xleaf.xlated_pkts, 1)
}
}
state proto::udp {
on_valid(xleaf) {
incr_cksum(@udp.crc, orig_dip, xleaf.xdip, 1)
incr_cksum(@udp.crc, orig_sip, xleaf.xsip, 1)
}
}
state proto::tcp {
on_valid(xleaf) {
incr_cksum(@tcp.cksum, orig_dip, xleaf.xdip, 1)
incr_cksum(@tcp.cksum, orig_sip, xleaf.xsip, 1)
}
}
state proto::vxlan {
}
state proto::gre {
}
state EOP {
return ret
}
#!/usr/bin/env python
import time
from netaddr import IPAddress
from ctypes import *
from src.bpf import BPF
prog = BPF("classifier", "tests/test3.dp", "tests/proto.dph",
BPF.BPF_PROG_TYPE_SCHED_CLS, debug=1)
class Key(Structure):
_fields_ = [("dip", c_uint),
("sip", c_uint)]
class Leaf(Structure):
_fields_ = [("xdip", c_uint),
("xsip", c_uint),
("xlated_pkts", c_ulonglong)]
prog.attach_filter(4, 10, 1)
xlate = prog.table("xlate", Key, Leaf)
xlate.put(Key(IPAddress("172.16.2.1").value, IPAddress("172.16.2.2").value),
Leaf(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value, 0))
while True:
print("==============================")
for key in xlate.iter():
leaf = xlate.get(key)
print(IPAddress(key.sip), "=>", IPAddress(key.dip),
"xlated_pkts", leaf.xlated_pkts)
time.sleep(1)
struct Ptr {
u64 ptr:64;
};
struct Counters {
u64 stat1:64;
u64 stat2:64;
};
Table<Ptr, Counters, FIXED_MATCH, AUTO> stats(1024);
// example with on_valid syntax
u32 sys_wr (struct proto::pt_regs *ctx) {
struct Ptr key = {.ptr=ctx->di};
struct Counters *leaf;
leaf = stats[key];
if leaf {
atomic_add(leaf->stat2, 1);
}
log("sys_wr: %p\n", ctx->di);
return 0;
}
// example with smallest available syntax
// note: if stats[key] fails, program returns early
u32 sys_rd (struct proto::pt_regs *ctx) {
struct Ptr key = {.ptr=ctx->di};
atomic_add(stats[key].stat1, 1);
}
// example with if/else case
u32 sys_bpf (struct proto::pt_regs *ctx) {
struct Ptr key = {.ptr=ctx->di};
struct Counters *leaf;
leaf = stats[key];
if leaf {
atomic_add(leaf->stat1, 1);
} else {
log("update %llx failed\n", ctx->di);
}
return 0;
}
#!/usr/bin/env python
from ctypes import c_uint, c_ulong, Structure
from bpf import BPF
import os
from time import sleep
from unittest import main, TestCase
class Key(Structure):
_fields_ = [("fd", c_ulong)]
class Leaf(Structure):
_fields_ = [("stat1", c_ulong),
("stat2", c_ulong)]
class TestKprobe(TestCase):
def setUp(self):
self.prog = BPF("trace1", "trace1.b", "kprobe.b",
prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
self.prog.load("sys_wr")
self.prog.load("sys_rd")
self.prog.load("sys_bpf")
self.stats = self.prog.table("stats", Key, Leaf)
self.prog.attach_kprobe("sys_write", "sys_wr", 0, -1)
self.prog.attach_kprobe("sys_read", "sys_rd", 0, -1)
self.prog.attach_kprobe("htab_map_get_next_key", "sys_bpf")
def test_trace1(self):
with open("/dev/null", "a") as f:
for i in range(0, 100):
os.write(f.fileno(), b"")
with open("/etc/services", "r") as f:
for i in range(0, 200):
os.read(f.fileno(), 1)
for key in self.stats.iter():
leaf = self.stats.get(key)
print("fd %x:" % key.fd, "stat1 %d" % leaf.stat1, "stat2 %d" % leaf.stat2)
if __name__ == "__main__":
main()
#include "kprobe.b"
struct Ptr { u64 ptr:64; };
struct Counters { u64 stat1:64; };
Table<Ptr, Counters, FIXED_MATCH, AUTO> stats(1024);
u32 count_sched (struct proto::pt_regs *ctx) {
struct Ptr key = {.ptr=ctx->bx};
atomic_add(stats[key].stat1, 1);
}
#!/usr/bin/env python
from ctypes import c_uint, c_ulong, Structure
from bpf import BPF
from time import sleep
from unittest import main, TestCase
class Ptr(Structure):
_fields_ = [("ptr", c_ulong)]
class Counters(Structure):
_fields_ = [("stat1", c_ulong)]
tracing = "/sys/kernel/debug/tracing"
class TestTracingEvent(TestCase):
def setUp(self):
self.prog = BPF("trace2", "trace2.b", "kprobe.b",
prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
self.prog.load("count_sched")
self.stats = self.prog.table("stats", Ptr, Counters)
self.prog.attach_kprobe("schedule+50", "count_sched", 0, -1)
def test_sched1(self):
for i in range(0, 100):
sleep(0.01)
for key in self.stats.iter():
leaf = self.stats.get(key)
print("ptr %x:" % key.ptr, "stat1 %x" % leaf.stat1)
if __name__ == "__main__":
main()
#!/bin/bash
#set -x
name=$1; shift
cmd=$1; shift
kind=$1; shift
PYTHONPATH=@CMAKE_SOURCE_DIR@/src
LD_LIBRARY_PATH=@CMAKE_BINARY_DIR@:@CMAKE_BINARY_DIR@/src/cc
ns=$name
function cleanup() {
trap - EXIT
if [[ "$kind" = "namespace" ]]; then
sudo ip netns delete $ns
fi
}
trap cleanup EXIT
function ns_run() {
sudo ip netns add $ns
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 ip addr add dev eth0 172.16.1.2/24
sudo ip netns exec $ns ip link set eth0 up
sudo ip addr add dev $ns.out 172.16.1.1/24
sudo ip link set $ns.out up
sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH ip netns exec $ns $cmd $@"
return $?
}
function sudo_run() {
sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $@"
return $?
}
case $kind in
namespace)
ns_run $@
;;
sudo)
sudo_run $@
;;
*)
echo "Invalid kind $kind"
exit 1
;;
esac
[[ $? -ne 0 ]] && { echo "Failed"; exit 1; }
exit 0
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