From 9a4320107a2b72a7d1213145f87f221fb648e7cc Mon Sep 17 00:00:00 2001 From: Shinae Woo Date: Sun, 15 Jul 2018 02:00:28 -0700 Subject: [PATCH 1/3] Update temp file patterns in gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b72f844ad..1b37f2a8d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ core/extra.mk core/extra.dpdk.mk core/kmod/.cache.mk *.retry +.mypy_cache +*.cache.mk # Vagrant stores temporary files in env/.vagrant env/.vagrant From 2de244fcf527751ed0bfc68001eabacdd9bcd55b Mon Sep 17 00:00:00 2001 From: Shinae Woo Date: Sun, 15 Jul 2018 01:53:18 -0700 Subject: [PATCH 2/3] Add BESSCTL CLI for listing gatehook class - show gatehookclass - show gatehookclass GATEHOOKCLASS --- bessctl/commands.py | 47 +++++++++++++++++++++++++++++++++++++- core/bessctl.cc | 40 ++++++++++++++++++++++++++++++++ core/gate.cc | 6 +++-- core/gate.h | 24 +++++++++++++++---- core/gate_hooks/pcapng.cc | 4 ++-- core/gate_hooks/tcpdump.cc | 4 ++-- core/gate_hooks/track.cc | 4 ++-- protobuf/bess_msg.proto | 23 ++++++++++++++++--- protobuf/service.proto | 8 ++++++- pybess/bess.py | 14 +++++++++--- 10 files changed, 153 insertions(+), 21 deletions(-) diff --git a/bessctl/commands.py b/bessctl/commands.py index fb0c82ae7..b438f51d3 100644 --- a/bessctl/commands.py +++ b/bessctl/commands.py @@ -360,6 +360,22 @@ def get_var_attrs(cli, var_token, partial_word): var_type = 'gate' var_desc = 'input gate of a module (default 0)' + elif var_token == 'GATEHOOKCLASS': + var_type = 'name' + var_desc = 'name of a gatehook class' + try: + var_candidates = cli.bess.list_gatehook_classes().names + except: + pass + + elif var_token == 'GATEHOOKCLASS...': + var_type = 'name+' + var_desc = 'one or more gatehook class names' + try: + var_candidates = cli.bess.list_gatehook_classes().names + except: + pass + elif var_token == '[ENV_VARS...]': var_type = 'map' var_desc = 'Environmental variables for configuration' @@ -1521,6 +1537,35 @@ def show_mclass_list(cli, cls_names): _show_mclass(cli, cls_name, True) +def _show_gatehook_class(cli, cls_name, detail): + info = cli.bess.get_gatehook_class_info(cls_name) + cli.fout.write('%-16s %s\n' % (info.name, info.help)) + + if detail: + if len(info.cmds) > 0: + cli.fout.write('\t\t commands: %s\n' % + (', '.join(map(lambda cmd, msg: "%s(%s)" + % (cmd, msg), + info.cmds, + info.cmd_args)))) + else: + cli.fout.write('\t\t (no commands)\n') + + +@cmd('show gatehookclass', 'Show all gatehook classes') +def show_gatahook_class_all(cli): + gatehook_classes = cli.bess.list_gatehook_classes().names + for cls_name in gatehook_classes: + _show_gatehook_class(cli, cls_name, False) + + +@cmd('show gatehookclass GATEHOOKCLASS...', + 'Show the details of specified gate classes') +def show_gatehook_class_list(cli, cls_names): + for cls_name in cls_names: + _show_gatehook_class(cli, cls_name, True) + + @cmd('import plugin PLUGIN_FILE', 'Import the specified plugin (*.so)') def import_plugin(cli, plugin): cli.bess.pause_all() @@ -1937,7 +1982,7 @@ def track_module_bits(cli, flag, module_name, direction, gate): @cmd('track reset MODULE DIRECTION GATE', 'Reset counts of packets, batches, and bits on specified gate') def track_reset(cli, module_name, direction, gate): - cli.bess.run_gate_command('track', module_name, direction, gate, 'reset', + cli.bess.run_gate_command('Track', module_name, direction, gate, 'reset', 'EmptyArg', {}) diff --git a/core/bessctl.cc b/core/bessctl.cc index e7356da98..b25798567 100644 --- a/core/bessctl.cc +++ b/core/bessctl.cc @@ -1367,6 +1367,46 @@ class BESSControlImpl final : public BESSControl::Service { return Status::OK; } + Status ListGateHookClass(ServerContext*, const EmptyRequest*, + ListGateHookClassResponse* response) override { + std::lock_guard lock(mutex_); + + for (const auto& pair : bess::GateHookFactory::all_gate_hook_factories()) { + const auto& factory = pair.second; + response->add_names(factory.hook_name()); + } + return Status::OK; + } + + Status GetGateHookClassInfo(ServerContext*, + const GetGateHookClassInfoRequest* request, + GetGateHookClassInfoResponse* response) override { + std::lock_guard lock(mutex_); + + VLOG(1) << "GetGateHookClassInfo from client:" << std::endl + << request->DebugString(); + if (!request->name().length()) { + return return_with_error(response, EINVAL, + "Argument must be a name in str"); + } + + const std::string& cls_name = request->name(); + const auto& it = bess::GateHookFactory::all_gate_hook_factories().find(cls_name); + if (it == bess::GateHookFactory::all_gate_hook_factories().end()) { + return return_with_error(response, ENOENT, "No gatehook class '%s' found", + cls_name.c_str()); + } + const bess::GateHookFactory* cls = &it->second; + + response->set_name(cls->hook_name()); + response->set_help(cls->help_text()); + for (const auto& cmd : cls->cmds()) { + response->add_cmds(cmd.first); + response->add_cmd_args(cmd.second); + } + return Status::OK; + } + Status ListGateHooks(ServerContext*, const EmptyRequest*, ListGateHooksResponse* response) override { std::lock_guard lock(mutex_); diff --git a/core/gate.cc b/core/gate.cc index 2b4bdd5df..fbac18198 100644 --- a/core/gate.cc +++ b/core/gate.cc @@ -45,10 +45,12 @@ const GateHookCommands GateHook::cmds; bool GateHookFactory::RegisterGateHook(GateHook::constructor_t constructor, const GateHookCommands &cmds, GateHook::init_func_t init_func, - const std::string &hook_name) { + const std::string &hook_name, + const std::string &help_text) { return all_gate_hook_factories_holder() .emplace(std::piecewise_construct, std::forward_as_tuple(hook_name), - std::forward_as_tuple(constructor, cmds, init_func, hook_name)) + std::forward_as_tuple(constructor, cmds, init_func, hook_name, + help_text)) .second; } diff --git a/core/gate.h b/core/gate.h index 90bf1577d..f820ef4e6 100644 --- a/core/gate.h +++ b/core/gate.h @@ -119,16 +119,18 @@ class GateHookFactory { public: GateHookFactory(GateHook::constructor_t constructor, const GateHookCommands &cmds, GateHook::init_func_t init_func, - const std::string &hook_name) + const std::string &hook_name, const std::string &help_text) : hook_constructor_(constructor), cmds_(cmds), hook_init_func_(init_func), - hook_name_(hook_name) {} + hook_name_(hook_name), + help_text_(help_text){} static bool RegisterGateHook(GateHook::constructor_t constructor, const GateHookCommands &cmds, GateHook::init_func_t init_func, - const std::string &hook_name); + const std::string &hook_name, + const std::string &help_text); static std::map &all_gate_hook_factories_holder( bool reset = false); @@ -136,6 +138,16 @@ class GateHookFactory { static const std::map &all_gate_hook_factories(); + const std::string &hook_name() const { return hook_name_; } + const std::string &help_text() const { return help_text_; } + + const std::vector> cmds() const { + std::vector> ret; + for (auto &cmd : cmds_) + ret.push_back(std::make_pair(cmd.cmd, cmd.arg_type)); + return ret; + } + CommandResponse RunCommand(GateHook *hook, const std::string &user_cmd, const google::protobuf::Any &arg) const; @@ -146,6 +158,7 @@ class GateHookFactory { const GateHookCommands cmds_; GateHook::init_func_t hook_init_func_; std::string hook_name_; + std::string help_text_; }; inline CommandResponse GateHook::RunCommand(const std::string &cmd, @@ -277,9 +290,10 @@ static inline bess::GateHook::init_func_t InitGateHookWithGenericArg( }; } -#define ADD_GATE_HOOK(_HOOK) \ +#define ADD_GATE_HOOK(_HOOK, _HELP) \ bool __gate_hook__##_HOOK = bess::GateHookFactory::RegisterGateHook( \ std::function([]() { return new _HOOK(); }), \ - _HOOK::cmds, InitGateHookWithGenericArg(&_HOOK::Init), _HOOK::kName); + _HOOK::cmds, InitGateHookWithGenericArg(&_HOOK::Init), _HOOK::kName, \ + _HELP); #endif // BESS_GATE_H_ diff --git a/core/gate_hooks/pcapng.cc b/core/gate_hooks/pcapng.cc index edc07f0d4..da1336ab1 100644 --- a/core/gate_hooks/pcapng.cc +++ b/core/gate_hooks/pcapng.cc @@ -84,7 +84,7 @@ void BytesToHexDump(const void *src, size_t len, char *dst) { } // namespace -const std::string Pcapng::kName = "pcapng"; +const std::string Pcapng::kName = "PcapNg"; Pcapng::Pcapng() : bess::GateHook(Pcapng::kName, Pcapng::kPriority), @@ -247,4 +247,4 @@ void Pcapng::ProcessBatch(const bess::PacketBatch *batch) { } } -ADD_GATE_HOOK(Pcapng) +ADD_GATE_HOOK(Pcapng, "metadata-dump-able packet dump") diff --git a/core/gate_hooks/tcpdump.cc b/core/gate_hooks/tcpdump.cc index 04530c2c0..ae98c7deb 100644 --- a/core/gate_hooks/tcpdump.cc +++ b/core/gate_hooks/tcpdump.cc @@ -41,7 +41,7 @@ #include "../utils/pcap.h" #include "../utils/time.h" -const std::string Tcpdump::kName = "tcpdump"; +const std::string Tcpdump::kName = "TcpDump"; bool TcpdumpOpener::InitFifo(int fd) { static const struct pcap_hdr hdr = { @@ -104,4 +104,4 @@ void Tcpdump::ProcessBatch(const bess::PacketBatch *batch) { } } -ADD_GATE_HOOK(Tcpdump) +ADD_GATE_HOOK(Tcpdump, "dump traffic on a network") diff --git a/core/gate_hooks/track.cc b/core/gate_hooks/track.cc index e34201bae..a8bd164e4 100644 --- a/core/gate_hooks/track.cc +++ b/core/gate_hooks/track.cc @@ -35,7 +35,7 @@ // Ethernet overhead in bytes static const size_t kEthernetOverhead = 24; -const std::string Track::kName = "track"; +const std::string Track::kName = "Track"; const GateHookCommands Track::cmds = {{"reset", "EmptyArg", GATE_HOOK_CMD_FUNC(&Track::CommandReset), @@ -74,4 +74,4 @@ void Track::ProcessBatch(const bess::PacketBatch *batch) { } } -ADD_GATE_HOOK(Track) +ADD_GATE_HOOK(Track, "count the packets and batches") diff --git a/protobuf/bess_msg.proto b/protobuf/bess_msg.proto index 0dcad0040..1644257ef 100644 --- a/protobuf/bess_msg.proto +++ b/protobuf/bess_msg.proto @@ -442,7 +442,7 @@ message ConnectModulesRequest { uint64 ogate = 3; /// m1's output gate ID uint64 igate = 4; /// m2's input gate ID /// If true do not attach default hooks at the input/output gate. - /// (Currently, the only default hook is the "track" hook at the ogate) + /// (Currently, the only default hook is the "Track" hook at the ogate) bool skip_default_hooks = 5; } @@ -490,9 +490,26 @@ message CommandResponse { // Gate hooks // ------------------------------------------------------------------------- -/// Enable/Disable the "track" hook on a gate (or all gates) +message ListGateHookClassResponse { + Error error = 1; + repeated string names = 2; /// List of gatehook types +} + +message GetGateHookClassInfoRequest { + string name = 1; /// Name of gatehook type +} + +message GetGateHookClassInfoResponse { + Error error = 1; + string name = 2; /// Name of gatehook type + string help = 3; /// 1=line description of the gatehook type + repeated string cmds = 4; /// List of commands supported by the gatehook + repeated string cmd_args = 5; /// Corresponding Protobuf message types +} + +/// Enable/Disable the "Track" hook on a gate (or all gates) /// -/// "track" hook accumulates the number of total packets, batches and bits +/// "Track" hook accumulates the number of total packets, batches and bits /// passing through a gate. This incurs some amount of CPU overheads. While /// the cost is very small, remember that the delay adds up at every gate. /// diff --git a/protobuf/service.proto b/protobuf/service.proto index 6ef09d1ab..a82e2218c 100644 --- a/protobuf/service.proto +++ b/protobuf/service.proto @@ -291,10 +291,16 @@ service BESSControl { // Gate hooks // ------------------------------------------------------------------------- + /// Enumerate all gatehook types available + rpc ListGateHookClass (EmptyRequest) returns (ListGateHookClassResponse) {} + + /// Query detailed information of a gatehook type + rpc GetGateHookClassInfo (GetGateHookClassInfoRequest) returns (GetGateHookClassInfoResponse) {} + /// Enable/Disable a gate hook. rpc ConfigureGateHook (ConfigureGateHookRequest) returns (CommandResponse) {} - /// Enable/Disable a gate hook. + /// Enumerate all gatehook installed rpc ListGateHooks (EmptyRequest) returns (ListGateHooksResponse) {} /// Send command to gate hook instance. diff --git a/pybess/bess.py b/pybess/bess.py index 8760ae245..87a593b8f 100644 --- a/pybess/bess.py +++ b/pybess/bess.py @@ -405,6 +405,14 @@ def list_mclasses(self): def list_modules(self): return self._request('ListModules') + def list_gatehook_classes(self): + return self._request('ListGateHookClass') + + def get_gatehook_class_info(self, name): + request = bess_msg.GetGateHookClassInfoRequest() + request.name = name + return self._request('GetGateHookClassInfo', request) + def get_mclass_info(self, name): request = bess_msg.GetMclassInfoRequest() request.name = name @@ -560,20 +568,20 @@ def tcpdump(self, enable, m, direction='out', gate=0, fifo=None): arg = bess_msg.TcpdumpArg() if fifo is not None: arg.fifo = fifo - return self._configure_gate_hook('tcpdump', m, arg, enable, direction, + return self._configure_gate_hook('TcpDump', m, arg, enable, direction, gate) def track_module(self, m, enable, bits=False, direction='out', gate=-1): arg = bess_msg.TrackArg() arg.bits = bits - return self._configure_gate_hook('track', m, arg, enable, direction, + return self._configure_gate_hook('Track', m, arg, enable, direction, gate) def pcapng(self, enable, m, direction='out', gate=0, fifo=None): arg = bess_msg.PcapngArg() if fifo is not None: arg.fifo = fifo - return self._configure_gate_hook('pcapng', m, arg, enable, direction, + return self._configure_gate_hook('PcapNg', m, arg, enable, direction, gate) def list_workers(self): From 2160ed397db880ecebe3ca13dda1b5ce78dd8f09 Mon Sep 17 00:00:00 2001 From: Shinae Woo Date: Sun, 15 Jul 2018 18:47:21 -0700 Subject: [PATCH 3/3] Show active gatehooks in CLI - add command `show gatehook` - add per-gate gatehook info with `show module` --- bessctl/commands.py | 30 ++++++++++++++++++++++++------ core/bessctl.cc | 8 ++++++++ protobuf/bess_msg.proto | 2 ++ pybess/bess.py | 3 +++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/bessctl/commands.py b/bessctl/commands.py index b438f51d3..e1eaf3c6e 100644 --- a/bessctl/commands.py +++ b/bessctl/commands.py @@ -1463,27 +1463,29 @@ def _show_module(cli, module_name): for gate in info.igates: track_str = 'batches N/A packets N/A' try: - track_str = 'batches %-16d packets %-16d' % (gate.cnt, + track_str = 'batches %-11d packets %-12d' % (gate.cnt, gate.pkts) except: pass - cli.fout.write(' %5d: %s %s\n' % + cli.fout.write(' %3d: %s %s\t%s\n' % (gate.igate, track_str, ', '.join('%s:%d ->' % (g.name, g.ogate) - for g in gate.ogates))) + for g in gate.ogates), + ', '.join(gate.hook_name))) if len(info.ogates) > 0: cli.fout.write(' Output gates:\n') for gate in info.ogates: track_str = 'batches N/A packets N/A' try: - track_str = 'batches %-16d packets %-16d' % (gate.cnt, + track_str = 'batches %-11d packets %-12d' % (gate.cnt, gate.pkts) except: pass cli.fout.write( - ' %5d: %s -> %d:%s\n' % - (gate.ogate, track_str, gate.igate, gate.name)) + ' %3d: %s -> %d:%s\t%s\n' % + (gate.ogate, track_str, gate.igate, gate.name, + ', '.join(gate.hook_name))) if hasattr(info, 'dump'): dump_str = pprint.pformat(info.dump, width=74) @@ -1537,6 +1539,22 @@ def show_mclass_list(cli, cls_names): _show_mclass(cli, cls_name, True) +@cmd('show gatehook', 'Show the status of all gatehook') +def show_gatehook_all(cli): + gatehooks = cli.bess.list_gatehooks().hooks + + if not gatehooks: + raise cli.CommandError('There is no active gatehook to show.') + + for gatehook in gatehooks: + if gatehook.HasField('igate'): + cli.fout.write('%-16s %d:%s\n' % (gatehook.hook_name, gatehook.igate, + gatehook.module_name)) + else: + cli.fout.write('%-16s %s:%d\n' % (gatehook.hook_name, + gatehook.module_name, gatehook.ogate)) + + def _show_gatehook_class(cli, cls_name, detail): info = cli.bess.get_gatehook_class_info(cls_name) cli.fout.write('%-16s %s\n' % (info.name, info.help)) diff --git a/core/bessctl.cc b/core/bessctl.cc index b25798567..da62d6c89 100644 --- a/core/bessctl.cc +++ b/core/bessctl.cc @@ -195,6 +195,10 @@ static int collect_igates(Module* m, GetModuleInfoResponse* response) { ogate->set_ogate(og->gate_idx()); ogate->set_name(og->module()->name()); } + + for (const auto& hook : g->hooks()) { + igate->add_hook_name(hook->name()); + } } return 0; @@ -218,6 +222,10 @@ static int collect_ogates(Module* m, GetModuleInfoResponse* response) { } ogate->set_name(g->igate()->module()->name()); ogate->set_igate(g->igate()->gate_idx()); + + for (const auto& hook : g->hooks()) { + ogate->add_hook_name(hook->name()); + } } return 0; diff --git a/protobuf/bess_msg.proto b/protobuf/bess_msg.proto index 1644257ef..10d8c7486 100644 --- a/protobuf/bess_msg.proto +++ b/protobuf/bess_msg.proto @@ -411,6 +411,7 @@ message GetModuleInfoResponse { uint64 pkts = 4; /// # of packets seen uint64 bytes = 5; /// # of bytes seen double timestamp = 6; /// The time that cnt/pkts counters were read + repeated string hook_name = 7; /// List of hooks per gate } message OGate { uint64 ogate = 1; /// Output gate ID @@ -420,6 +421,7 @@ message GetModuleInfoResponse { double timestamp = 5; /// The time thatcnt/pkts counters were read string name = 6; /// Name of the "next" module it connects to uint64 igate = 7; /// Input gate ID of the "next" module + repeated string hook_name = 8; /// List of hooks per gate } message Attribute { string name = 1; /// Name of per-packet metadata attribute diff --git a/pybess/bess.py b/pybess/bess.py index 87a593b8f..8064bae94 100644 --- a/pybess/bess.py +++ b/pybess/bess.py @@ -408,6 +408,9 @@ def list_modules(self): def list_gatehook_classes(self): return self._request('ListGateHookClass') + def list_gatehooks(self): + return self._request('ListGateHooks') + def get_gatehook_class_info(self, name): request = bess_msg.GetGateHookClassInfoRequest() request.name = name