mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00
Merge remote-tracking branch 'origin/master' into topic/johanna/imap-starttls
This commit is contained in:
commit
3669b6aa9c
626 changed files with 13981 additions and 3994 deletions
|
@ -61,7 +61,7 @@ if (NOT SED_EXE)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
FindRequiredPackage(Perl)
|
FindRequiredPackage(PythonInterp)
|
||||||
FindRequiredPackage(FLEX)
|
FindRequiredPackage(FLEX)
|
||||||
FindRequiredPackage(BISON)
|
FindRequiredPackage(BISON)
|
||||||
FindRequiredPackage(PCAP)
|
FindRequiredPackage(PCAP)
|
||||||
|
@ -88,7 +88,7 @@ endif ()
|
||||||
|
|
||||||
include_directories(BEFORE
|
include_directories(BEFORE
|
||||||
${PCAP_INCLUDE_DIR}
|
${PCAP_INCLUDE_DIR}
|
||||||
${OpenSSL_INCLUDE_DIR}
|
${OPENSSL_INCLUDE_DIR}
|
||||||
${BIND_INCLUDE_DIR}
|
${BIND_INCLUDE_DIR}
|
||||||
${BinPAC_INCLUDE_DIR}
|
${BinPAC_INCLUDE_DIR}
|
||||||
${ZLIB_INCLUDE_DIR}
|
${ZLIB_INCLUDE_DIR}
|
||||||
|
@ -141,7 +141,7 @@ endif ()
|
||||||
set(brodeps
|
set(brodeps
|
||||||
${BinPAC_LIBRARY}
|
${BinPAC_LIBRARY}
|
||||||
${PCAP_LIBRARY}
|
${PCAP_LIBRARY}
|
||||||
${OpenSSL_LIBRARIES}
|
${OPENSSL_LIBRARIES}
|
||||||
${BIND_LIBRARY}
|
${BIND_LIBRARY}
|
||||||
${ZLIB_LIBRARY}
|
${ZLIB_LIBRARY}
|
||||||
${JEMALLOC_LIBRARIES}
|
${JEMALLOC_LIBRARIES}
|
||||||
|
@ -170,8 +170,8 @@ include(RequireCXX11)
|
||||||
# Tell the plugin code that we're building as part of the main tree.
|
# Tell the plugin code that we're building as part of the main tree.
|
||||||
set(BRO_PLUGIN_INTERNAL_BUILD true CACHE INTERNAL "" FORCE)
|
set(BRO_PLUGIN_INTERNAL_BUILD true CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/bro-config.h.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
${CMAKE_CURRENT_BINARY_DIR}/bro-config.h)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
@ -233,6 +233,7 @@ message(
|
||||||
"\nCPP: ${CMAKE_CXX_COMPILER}"
|
"\nCPP: ${CMAKE_CXX_COMPILER}"
|
||||||
"\n"
|
"\n"
|
||||||
"\nBroker: ${ENABLE_BROKER}"
|
"\nBroker: ${ENABLE_BROKER}"
|
||||||
|
"\nBroker Python: ${BROKER_PYTHON_BINDINGS}"
|
||||||
"\nBroccoli: ${INSTALL_BROCCOLI}"
|
"\nBroccoli: ${INSTALL_BROCCOLI}"
|
||||||
"\nBroctl: ${INSTALL_BROCTL}"
|
"\nBroctl: ${INSTALL_BROCTL}"
|
||||||
"\nAux. Tools: ${INSTALL_AUX_TOOLS}"
|
"\nAux. Tools: ${INSTALL_AUX_TOOLS}"
|
||||||
|
|
100
NEWS
100
NEWS
|
@ -16,6 +16,102 @@ New Dependencies
|
||||||
- Bro now requires the C++ Actor Framework, CAF, which must be
|
- Bro now requires the C++ Actor Framework, CAF, which must be
|
||||||
installed first. See http://actor-framework.org.
|
installed first. See http://actor-framework.org.
|
||||||
|
|
||||||
|
- Bro now requires Python instead of Perl to compile the source code.
|
||||||
|
|
||||||
|
- The pcap buffer size can set through the new option Pcap::bufsize.
|
||||||
|
|
||||||
|
New Functionality
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- Bro now includes the NetControl framework. The framework allows for easy
|
||||||
|
interaction of Bro with hard- and software switches, firewalls, etc.
|
||||||
|
|
||||||
|
- There is a new file entropy analyzer for files.
|
||||||
|
|
||||||
|
- Bro now supports the remote framebuffer protocol (RFB) that is used by
|
||||||
|
VNC servers for remote graphical displays.
|
||||||
|
|
||||||
|
- Bro now supports the Radiotap header for 802.11 frames.
|
||||||
|
|
||||||
|
- Bro now tracks VLAN IDs. To record them inside the connection log,
|
||||||
|
load protocols/conn/vlan-logging.bro.
|
||||||
|
|
||||||
|
- A new dns_CAA_reply event gives access to DNS Certification Authority
|
||||||
|
Authorization replies.
|
||||||
|
|
||||||
|
- A new per-packet event raw_packet() provides access to layer 2
|
||||||
|
information. Use with care, generating events per packet is
|
||||||
|
expensive.
|
||||||
|
|
||||||
|
- A new built-in function, decode_base64_conn() for Base64 decoding.
|
||||||
|
It works like decode_base64() but receives an additional connection
|
||||||
|
argument that will be used for decoding errors into weird.log
|
||||||
|
(instead of reporter.log).
|
||||||
|
|
||||||
|
- A new get_current_packet_header bif returns the headers of the current
|
||||||
|
packet.
|
||||||
|
|
||||||
|
- Two new built-in functions for handling set[subnet] and table[subnet]:
|
||||||
|
|
||||||
|
- check_subnet(subnet, table) checks if a specific subnet is a member
|
||||||
|
of a set/table. This is different from the "in" operator, which always
|
||||||
|
performs a longest prefix match.
|
||||||
|
|
||||||
|
- matching_subnets(subnet, table) returns all subnets of the set or table
|
||||||
|
that contain the given subnet.
|
||||||
|
|
||||||
|
- filter_subnet_table(subnet, table) works like check_subnet, but returns
|
||||||
|
a table containing all matching entries.
|
||||||
|
|
||||||
|
- Several built-in functions for handling IP addresses and subnets were added:
|
||||||
|
|
||||||
|
- is_v4_subnet(subnet) checks whether a subnet specification is IPv4.
|
||||||
|
|
||||||
|
- is_v6_subnet(subnet) checks whether a subnet specification is IPv6.
|
||||||
|
|
||||||
|
- addr_to_subnet(addr) converts an IP address to a /32 subnet.
|
||||||
|
|
||||||
|
- subnet_to_addr(subnet) returns the IP address part of a subnet.
|
||||||
|
|
||||||
|
- subnet_width(subnet) returns the width of a subnet.
|
||||||
|
|
||||||
|
- The IRC analyzer now recognizes StartTLS sessions and enable the SSL
|
||||||
|
analyzer for them.
|
||||||
|
|
||||||
|
- New Bro plugins in aux/plugins:
|
||||||
|
|
||||||
|
- af_packet: Native AF_PACKET support.
|
||||||
|
- kafka : Log writer interfacing to Kafka.
|
||||||
|
- myricom: Native Myricom SNF v3 support.
|
||||||
|
- pf_ring: Native PF_RING support.
|
||||||
|
- redis: An experimental log writer for Redis.
|
||||||
|
- tcprs: An TCP-level analyzer detecting retransmissions, reordering, and more.
|
||||||
|
|
||||||
|
Changed Functionality
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
- The BrokerComm and BrokerStore namespaces were renamed to Broker.
|
||||||
|
|
||||||
|
- ``SSH::skip_processing_after_detection`` was removed. The functionality was
|
||||||
|
replaced by ``SSH::disable_analyzer_after_detection``.
|
||||||
|
|
||||||
|
- Some script-level identifier have changed their names:
|
||||||
|
|
||||||
|
snaplen -> Pcap::snaplen
|
||||||
|
precompile_pcap_filter() -> Pcap::precompile_pcap_filter()
|
||||||
|
install_pcap_filter() -> Pcap::install_pcap_filter()
|
||||||
|
pcap_error() -> Pcap::pcap_error()
|
||||||
|
|
||||||
|
|
||||||
|
Deprecated Functionality
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- The built-in functions decode_base64_custom() and
|
||||||
|
encode_base64_custom() are no longer needed and will be removed
|
||||||
|
in the future. Their functionality is now provided directly by
|
||||||
|
decode_base64() and encode_base64(), which take an optional
|
||||||
|
parameter to change the Base64 alphabet.
|
||||||
|
|
||||||
Bro 2.4
|
Bro 2.4
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -176,8 +272,8 @@ Changed Functionality
|
||||||
- The SSH changes come with a few incompatibilities. The following
|
- The SSH changes come with a few incompatibilities. The following
|
||||||
events have been renamed:
|
events have been renamed:
|
||||||
|
|
||||||
* ``SSH::heuristic_failed_login`` to ``SSH::ssh_auth_failed``
|
* ``SSH::heuristic_failed_login`` to ``ssh_auth_failed``
|
||||||
* ``SSH::heuristic_successful_login`` to ``SSH::ssh_auth_successful``
|
* ``SSH::heuristic_successful_login`` to ``ssh_auth_successful``
|
||||||
|
|
||||||
The ``SSH::Info`` status field has been removed and replaced with
|
The ``SSH::Info`` status field has been removed and replaced with
|
||||||
the ``auth_success`` field. This field has been changed from a
|
the ``auth_success`` field. This field has been changed from a
|
||||||
|
|
1
README.rst
Symbolic link
1
README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
README
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.4-46
|
2.4-471
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4f33233aef5539ae4f12c6d0e4338247833c3900
|
Subproject commit 424d40c1e8d5888311b50c0e5a9dfc9c5f818b66
|
|
@ -1 +1 @@
|
||||||
Subproject commit 07af9748f40dc47d3a2b3290db494a90dcbddbdc
|
Subproject commit 105dfe4ad6c4ae4563b21cb0466ee350f0af0d43
|
|
@ -1 +1 @@
|
||||||
Subproject commit 74bb4bbd949e61e099178f8a97499d3f1355de8b
|
Subproject commit f83038b17fc83788415a58d77f75ad182ca6a9b7
|
|
@ -1 +1 @@
|
||||||
Subproject commit 54377d4746e2fd3ba7b7ca97e4a6ceccbd2cc236
|
Subproject commit 583f3a3ff1847cf96a87f865d5cf0f36fae9dd67
|
|
@ -1 +1 @@
|
||||||
Subproject commit d25efc7d5f495c30294b11180c1857477078f2d6
|
Subproject commit 6684ab5109f526fb535013760f17a4c8dff093ae
|
|
@ -1 +1 @@
|
||||||
Subproject commit a89cd0fda0f17f69b96c935959cae89145b92927
|
Subproject commit 4bea8fa948be2bc86ff92399137131bc1c029b08
|
|
@ -1 +1 @@
|
||||||
Subproject commit 98ad8a5b97f601a3ec9a773d87582438212b8290
|
Subproject commit ab61be0c4f128c976f72dfa5a09a87cd842f387a
|
2
cmake
2
cmake
|
@ -1 +1 @@
|
||||||
Subproject commit 6406fb79d30df8d7956110ce65a97d18e4bc8c3b
|
Subproject commit 0a2b36874ad5c1a22829135f8aeeac534469053f
|
19
configure
vendored
19
configure
vendored
|
@ -47,6 +47,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
--disable-auxtools don't build or install auxiliary tools
|
--disable-auxtools don't build or install auxiliary tools
|
||||||
--disable-perftools don't try to build with Google Perftools
|
--disable-perftools don't try to build with Google Perftools
|
||||||
--disable-python don't try to build python bindings for broccoli
|
--disable-python don't try to build python bindings for broccoli
|
||||||
|
--disable-pybroker don't try to build python bindings for broker
|
||||||
|
|
||||||
Required Packages in Non-Standard Locations:
|
Required Packages in Non-Standard Locations:
|
||||||
--with-openssl=PATH path to OpenSSL install root
|
--with-openssl=PATH path to OpenSSL install root
|
||||||
|
@ -55,7 +56,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
--with-binpac=PATH path to BinPAC install root
|
--with-binpac=PATH path to BinPAC install root
|
||||||
--with-flex=PATH path to flex executable
|
--with-flex=PATH path to flex executable
|
||||||
--with-bison=PATH path to bison executable
|
--with-bison=PATH path to bison executable
|
||||||
--with-perl=PATH path to perl executable
|
--with-python=PATH path to Python executable
|
||||||
--with-libcaf=PATH path to C++ Actor Framework installation
|
--with-libcaf=PATH path to C++ Actor Framework installation
|
||||||
(a required Broker dependency)
|
(a required Broker dependency)
|
||||||
|
|
||||||
|
@ -63,7 +64,6 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
--with-geoip=PATH path to the libGeoIP install root
|
--with-geoip=PATH path to the libGeoIP install root
|
||||||
--with-perftools=PATH path to Google Perftools install root
|
--with-perftools=PATH path to Google Perftools install root
|
||||||
--with-jemalloc=PATH path to jemalloc install root
|
--with-jemalloc=PATH path to jemalloc install root
|
||||||
--with-python=PATH path to Python interpreter
|
|
||||||
--with-python-lib=PATH path to libpython
|
--with-python-lib=PATH path to libpython
|
||||||
--with-python-inc=PATH path to Python headers
|
--with-python-inc=PATH path to Python headers
|
||||||
--with-ruby=PATH path to ruby interpreter
|
--with-ruby=PATH path to ruby interpreter
|
||||||
|
@ -122,6 +122,7 @@ append_cache_entry PY_MOD_INSTALL_DIR PATH $prefix/lib/broctl
|
||||||
append_cache_entry BRO_SCRIPT_INSTALL_PATH STRING $prefix/share/bro
|
append_cache_entry BRO_SCRIPT_INSTALL_PATH STRING $prefix/share/bro
|
||||||
append_cache_entry BRO_ETC_INSTALL_DIR PATH $prefix/etc
|
append_cache_entry BRO_ETC_INSTALL_DIR PATH $prefix/etc
|
||||||
append_cache_entry BROKER_PYTHON_HOME PATH $prefix
|
append_cache_entry BROKER_PYTHON_HOME PATH $prefix
|
||||||
|
append_cache_entry BROKER_PYTHON_BINDINGS BOOL false
|
||||||
append_cache_entry ENABLE_DEBUG BOOL false
|
append_cache_entry ENABLE_DEBUG BOOL false
|
||||||
append_cache_entry ENABLE_PERFTOOLS BOOL false
|
append_cache_entry ENABLE_PERFTOOLS BOOL false
|
||||||
append_cache_entry ENABLE_PERFTOOLS_DEBUG BOOL false
|
append_cache_entry ENABLE_PERFTOOLS_DEBUG BOOL false
|
||||||
|
@ -218,11 +219,14 @@ while [ $# -ne 0 ]; do
|
||||||
--disable-python)
|
--disable-python)
|
||||||
append_cache_entry DISABLE_PYTHON_BINDINGS BOOL true
|
append_cache_entry DISABLE_PYTHON_BINDINGS BOOL true
|
||||||
;;
|
;;
|
||||||
|
--disable-pybroker)
|
||||||
|
append_cache_entry DISABLE_PYBROKER BOOL true
|
||||||
|
;;
|
||||||
--enable-ruby)
|
--enable-ruby)
|
||||||
append_cache_entry DISABLE_RUBY_BINDINGS BOOL false
|
append_cache_entry DISABLE_RUBY_BINDINGS BOOL false
|
||||||
;;
|
;;
|
||||||
--with-openssl=*)
|
--with-openssl=*)
|
||||||
append_cache_entry OpenSSL_ROOT_DIR PATH $optarg
|
append_cache_entry OPENSSL_ROOT_DIR PATH $optarg
|
||||||
;;
|
;;
|
||||||
--with-bind=*)
|
--with-bind=*)
|
||||||
append_cache_entry BIND_ROOT_DIR PATH $optarg
|
append_cache_entry BIND_ROOT_DIR PATH $optarg
|
||||||
|
@ -239,9 +243,6 @@ while [ $# -ne 0 ]; do
|
||||||
--with-bison=*)
|
--with-bison=*)
|
||||||
append_cache_entry BISON_EXECUTABLE PATH $optarg
|
append_cache_entry BISON_EXECUTABLE PATH $optarg
|
||||||
;;
|
;;
|
||||||
--with-perl=*)
|
|
||||||
append_cache_entry PERL_EXECUTABLE PATH $optarg
|
|
||||||
;;
|
|
||||||
--with-geoip=*)
|
--with-geoip=*)
|
||||||
append_cache_entry LibGeoIP_ROOT_DIR PATH $optarg
|
append_cache_entry LibGeoIP_ROOT_DIR PATH $optarg
|
||||||
;;
|
;;
|
||||||
|
@ -275,8 +276,12 @@ while [ $# -ne 0 ]; do
|
||||||
--with-swig=*)
|
--with-swig=*)
|
||||||
append_cache_entry SWIG_EXECUTABLE PATH $optarg
|
append_cache_entry SWIG_EXECUTABLE PATH $optarg
|
||||||
;;
|
;;
|
||||||
|
--with-caf=*)
|
||||||
|
append_cache_entry CAF_ROOT_DIR PATH $optarg
|
||||||
|
;;
|
||||||
--with-libcaf=*)
|
--with-libcaf=*)
|
||||||
append_cache_entry LIBCAF_ROOT_DIR PATH $optarg
|
echo "warning: --with-libcaf deprecated, use --with-caf instead"
|
||||||
|
append_cache_entry CAF_ROOT_DIR PATH $optarg
|
||||||
;;
|
;;
|
||||||
--with-rocksdb=*)
|
--with-rocksdb=*)
|
||||||
append_cache_entry ROCKSDB_ROOT_DIR PATH $optarg
|
append_cache_entry ROCKSDB_ROOT_DIR PATH $optarg
|
||||||
|
|
1
doc/components/bro-plugins/af_packet/README.rst
Symbolic link
1
doc/components/bro-plugins/af_packet/README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../aux/plugins/af_packet/README
|
|
@ -1 +0,0 @@
|
||||||
../../../../aux/plugins/dataseries/README
|
|
1
doc/components/bro-plugins/myricom/README.rst
Symbolic link
1
doc/components/bro-plugins/myricom/README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../aux/plugins/myricom/README
|
1
doc/components/bro-plugins/pf_ring/README.rst
Symbolic link
1
doc/components/bro-plugins/pf_ring/README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../aux/plugins/pf_ring/README
|
1
doc/components/bro-plugins/redis/README.rst
Symbolic link
1
doc/components/bro-plugins/redis/README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../aux/plugins/redis/README
|
1
doc/components/bro-plugins/tcprs/README.rst
Symbolic link
1
doc/components/bro-plugins/tcprs/README.rst
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../aux/plugins/tcprs/README
|
|
@ -66,7 +66,7 @@ master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'Bro'
|
project = u'Bro'
|
||||||
copyright = u'2013, The Bro Project'
|
copyright = u'2016, The Bro Project'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
|
|
@ -209,8 +209,15 @@ directory. With the skeleton, ``<base>`` corresponds to ``build/``.
|
||||||
"@load"ed.
|
"@load"ed.
|
||||||
|
|
||||||
``scripts``/__load__.bro
|
``scripts``/__load__.bro
|
||||||
A Bro script that will be loaded immediately when the plugin gets
|
A Bro script that will be loaded when the plugin gets activated.
|
||||||
activated. See below for more information on activating plugins.
|
When this script executes, any BiF elements that the plugin
|
||||||
|
defines will already be available. See below for more information
|
||||||
|
on activating plugins.
|
||||||
|
|
||||||
|
``scripts``/__preload__.bro
|
||||||
|
A Bro script that will be loaded when the plugin gets activated,
|
||||||
|
but before any BiF elements become available. See below for more
|
||||||
|
information on activating plugins.
|
||||||
|
|
||||||
``lib/bif/``
|
``lib/bif/``
|
||||||
Directory with auto-generated Bro scripts that declare the plugin's
|
Directory with auto-generated Bro scripts that declare the plugin's
|
||||||
|
@ -279,7 +286,9 @@ Activating a plugin will:
|
||||||
1. Load the dynamic module
|
1. Load the dynamic module
|
||||||
2. Make any bif items available
|
2. Make any bif items available
|
||||||
3. Add the ``scripts/`` directory to ``BROPATH``
|
3. Add the ``scripts/`` directory to ``BROPATH``
|
||||||
4. Load ``scripts/__load__.bro``
|
4. Load ``scripts/__preload__.bro``
|
||||||
|
5. Make BiF elements available to scripts.
|
||||||
|
6. Load ``scripts/__load__.bro``
|
||||||
|
|
||||||
By default, Bro will automatically activate all dynamic plugins found
|
By default, Bro will automatically activate all dynamic plugins found
|
||||||
in its search path ``BRO_PLUGIN_PATH``. However, in bare mode (``bro
|
in its search path ``BRO_PLUGIN_PATH``. However, in bare mode (``bro
|
||||||
|
|
|
@ -9,10 +9,7 @@ Broker-Enabled Communication Framework
|
||||||
|
|
||||||
Bro can now use the `Broker Library
|
Bro can now use the `Broker Library
|
||||||
<../components/broker/README.html>`_ to exchange information with
|
<../components/broker/README.html>`_ to exchange information with
|
||||||
other Bro processes. To enable it run Bro's ``configure`` script
|
other Bro processes.
|
||||||
with the ``--enable-broker`` option. Note that a C++11 compatible
|
|
||||||
compiler (e.g. GCC 4.8+ or Clang 3.3+) is required as well as the
|
|
||||||
`C++ Actor Framework <http://actor-framework.org/>`_.
|
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
@ -20,35 +17,35 @@ Connecting to Peers
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Communication via Broker must first be turned on via
|
Communication via Broker must first be turned on via
|
||||||
:bro:see:`BrokerComm::enable`.
|
:bro:see:`Broker::enable`.
|
||||||
|
|
||||||
Bro can accept incoming connections by calling :bro:see:`BrokerComm::listen`
|
Bro can accept incoming connections by calling :bro:see:`Broker::listen`
|
||||||
and then monitor connection status updates via
|
and then monitor connection status updates via the
|
||||||
:bro:see:`BrokerComm::incoming_connection_established` and
|
:bro:see:`Broker::incoming_connection_established` and
|
||||||
:bro:see:`BrokerComm::incoming_connection_broken`.
|
:bro:see:`Broker::incoming_connection_broken` events.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-listener.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-listener.bro
|
||||||
|
|
||||||
Bro can initiate outgoing connections by calling :bro:see:`BrokerComm::connect`
|
Bro can initiate outgoing connections by calling :bro:see:`Broker::connect`
|
||||||
and then monitor connection status updates via
|
and then monitor connection status updates via the
|
||||||
:bro:see:`BrokerComm::outgoing_connection_established`,
|
:bro:see:`Broker::outgoing_connection_established`,
|
||||||
:bro:see:`BrokerComm::outgoing_connection_broken`, and
|
:bro:see:`Broker::outgoing_connection_broken`, and
|
||||||
:bro:see:`BrokerComm::outgoing_connection_incompatible`.
|
:bro:see:`Broker::outgoing_connection_incompatible` events.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-connector.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/connecting-connector.bro
|
||||||
|
|
||||||
Remote Printing
|
Remote Printing
|
||||||
===============
|
===============
|
||||||
|
|
||||||
To receive remote print messages, first use
|
To receive remote print messages, first use the
|
||||||
:bro:see:`BrokerComm::subscribe_to_prints` to advertise to peers a topic
|
:bro:see:`Broker::subscribe_to_prints` function to advertise to peers a
|
||||||
prefix of interest and then create an event handler for
|
topic prefix of interest and then create an event handler for
|
||||||
:bro:see:`BrokerComm::print_handler` to handle any print messages that are
|
:bro:see:`Broker::print_handler` to handle any print messages that are
|
||||||
received.
|
received.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-listener.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-listener.bro
|
||||||
|
|
||||||
To send remote print messages, just call :bro:see:`BrokerComm::print`.
|
To send remote print messages, just call :bro:see:`Broker::print`.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-connector.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/printing-connector.bro
|
||||||
|
|
||||||
|
@ -71,17 +68,17 @@ the Broker message format is simply:
|
||||||
Remote Events
|
Remote Events
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Receiving remote events is similar to remote prints. Just use
|
Receiving remote events is similar to remote prints. Just use the
|
||||||
:bro:see:`BrokerComm::subscribe_to_events` and possibly define any new events
|
:bro:see:`Broker::subscribe_to_events` function and possibly define any
|
||||||
along with handlers that peers may want to send.
|
new events along with handlers that peers may want to send.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-listener.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-listener.bro
|
||||||
|
|
||||||
To send events, there are two choices. The first is to use call
|
There are two different ways to send events. The first is to call the
|
||||||
:bro:see:`BrokerComm::event` directly. The second option is to use
|
:bro:see:`Broker::event` function directly. The second option is to call
|
||||||
:bro:see:`BrokerComm::auto_event` to make it so a particular event is
|
the :bro:see:`Broker::auto_event` function where you specify a
|
||||||
automatically sent to peers whenever it is called locally via the normal
|
particular event that will be automatically sent to peers whenever the
|
||||||
event invocation syntax.
|
event is called locally via the normal event invocation syntax.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-connector.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/events-connector.bro
|
||||||
|
|
||||||
|
@ -98,7 +95,7 @@ the Broker message format is:
|
||||||
broker::message{std::string{}, ...};
|
broker::message{std::string{}, ...};
|
||||||
|
|
||||||
The first parameter is the name of the event and the remaining ``...``
|
The first parameter is the name of the event and the remaining ``...``
|
||||||
are its arguments, which are any of the support Broker data types as
|
are its arguments, which are any of the supported Broker data types as
|
||||||
they correspond to the Bro types for the event named in the first
|
they correspond to the Bro types for the event named in the first
|
||||||
parameter of the message.
|
parameter of the message.
|
||||||
|
|
||||||
|
@ -107,23 +104,23 @@ Remote Logging
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/testlog.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/testlog.bro
|
||||||
|
|
||||||
Use :bro:see:`BrokerComm::subscribe_to_logs` to advertise interest in logs
|
Use the :bro:see:`Broker::subscribe_to_logs` function to advertise interest
|
||||||
written by peers. The topic names that Bro uses are implicitly of the
|
in logs written by peers. The topic names that Bro uses are implicitly of the
|
||||||
form "bro/log/<stream-name>".
|
form "bro/log/<stream-name>".
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-listener.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-listener.bro
|
||||||
|
|
||||||
To send remote logs either use :bro:see:`Log::enable_remote_logging` or
|
To send remote logs either redef :bro:see:`Log::enable_remote_logging` or
|
||||||
:bro:see:`BrokerComm::enable_remote_logs`. The former allows any log stream
|
use the :bro:see:`Broker::enable_remote_logs` function. The former
|
||||||
to be sent to peers while the later toggles remote logging for
|
allows any log stream to be sent to peers while the latter enables remote
|
||||||
particular streams.
|
logging for particular streams.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-connector.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/logs-connector.bro
|
||||||
|
|
||||||
Message Format
|
Message Format
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
For other applications that want to exchange logs messages with Bro,
|
For other applications that want to exchange log messages with Bro,
|
||||||
the Broker message format is:
|
the Broker message format is:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
@ -132,7 +129,7 @@ the Broker message format is:
|
||||||
|
|
||||||
The enum value corresponds to the stream's :bro:see:`Log::ID` value, and
|
The enum value corresponds to the stream's :bro:see:`Log::ID` value, and
|
||||||
the record corresponds to a single entry of that log's columns record,
|
the record corresponds to a single entry of that log's columns record,
|
||||||
in this case a ``Test::INFO`` value.
|
in this case a ``Test::Info`` value.
|
||||||
|
|
||||||
Tuning Access Control
|
Tuning Access Control
|
||||||
=====================
|
=====================
|
||||||
|
@ -140,23 +137,24 @@ Tuning Access Control
|
||||||
By default, endpoints do not restrict the message topics that it sends
|
By default, endpoints do not restrict the message topics that it sends
|
||||||
to peers and do not restrict what message topics and data store
|
to peers and do not restrict what message topics and data store
|
||||||
identifiers get advertised to peers. These are the default
|
identifiers get advertised to peers. These are the default
|
||||||
:bro:see:`BrokerComm::EndpointFlags` supplied to :bro:see:`BrokerComm::enable`.
|
:bro:see:`Broker::EndpointFlags` supplied to :bro:see:`Broker::enable`.
|
||||||
|
|
||||||
If not using the ``auto_publish`` flag, one can use the
|
If not using the ``auto_publish`` flag, one can use the
|
||||||
:bro:see:`BrokerComm::publish_topic` and :bro:see:`BrokerComm::unpublish_topic`
|
:bro:see:`Broker::publish_topic` and :bro:see:`Broker::unpublish_topic`
|
||||||
functions to manipulate the set of message topics (must match exactly)
|
functions to manipulate the set of message topics (must match exactly)
|
||||||
that are allowed to be sent to peer endpoints. These settings take
|
that are allowed to be sent to peer endpoints. These settings take
|
||||||
precedence over the per-message ``peers`` flag supplied to functions
|
precedence over the per-message ``peers`` flag supplied to functions
|
||||||
that take a :bro:see:`BrokerComm::SendFlags` such as :bro:see:`BrokerComm::print`,
|
that take a :bro:see:`Broker::SendFlags` such as :bro:see:`Broker::print`,
|
||||||
:bro:see:`BrokerComm::event`, :bro:see:`BrokerComm::auto_event` or
|
:bro:see:`Broker::event`, :bro:see:`Broker::auto_event` or
|
||||||
:bro:see:`BrokerComm::enable_remote_logs`.
|
:bro:see:`Broker::enable_remote_logs`.
|
||||||
|
|
||||||
If not using the ``auto_advertise`` flag, one can use the
|
If not using the ``auto_advertise`` flag, one can use the
|
||||||
:bro:see:`BrokerComm::advertise_topic` and :bro:see:`BrokerComm::unadvertise_topic`
|
:bro:see:`Broker::advertise_topic` and
|
||||||
to manupulate the set of topic prefixes that are allowed to be
|
:bro:see:`Broker::unadvertise_topic` functions
|
||||||
advertised to peers. If an endpoint does not advertise a topic prefix,
|
to manipulate the set of topic prefixes that are allowed to be
|
||||||
the only way a peers can send messages to it is via the ``unsolicited``
|
advertised to peers. If an endpoint does not advertise a topic prefix, then
|
||||||
flag of :bro:see:`BrokerComm::SendFlags` and choosing a topic with a matching
|
the only way peers can send messages to it is via the ``unsolicited``
|
||||||
|
flag of :bro:see:`Broker::SendFlags` and choosing a topic with a matching
|
||||||
prefix (i.e. full topic may be longer than receivers prefix, just the
|
prefix (i.e. full topic may be longer than receivers prefix, just the
|
||||||
prefix needs to match).
|
prefix needs to match).
|
||||||
|
|
||||||
|
@ -172,7 +170,7 @@ specific type of frontend, but a standalone frontend can also exist to
|
||||||
e.g. query and modify the contents of a remote master store without
|
e.g. query and modify the contents of a remote master store without
|
||||||
actually "owning" any of the contents itself.
|
actually "owning" any of the contents itself.
|
||||||
|
|
||||||
A master data store can be be cloned from remote peers which may then
|
A master data store can be cloned from remote peers which may then
|
||||||
perform lightweight, local queries against the clone, which
|
perform lightweight, local queries against the clone, which
|
||||||
automatically stays synchronized with the master store. Clones cannot
|
automatically stays synchronized with the master store. Clones cannot
|
||||||
modify their content directly, instead they send modifications to the
|
modify their content directly, instead they send modifications to the
|
||||||
|
@ -181,7 +179,7 @@ all clones.
|
||||||
|
|
||||||
Master and clone stores get to choose what type of storage backend to
|
Master and clone stores get to choose what type of storage backend to
|
||||||
use. E.g. In-memory versus SQLite for persistence. Note that if clones
|
use. E.g. In-memory versus SQLite for persistence. Note that if clones
|
||||||
are used, data store sizes should still be able to fit within memory
|
are used, then data store sizes must be able to fit within memory
|
||||||
regardless of the storage backend as a single snapshot of the master
|
regardless of the storage backend as a single snapshot of the master
|
||||||
store is sent in a single chunk to initialize the clone.
|
store is sent in a single chunk to initialize the clone.
|
||||||
|
|
||||||
|
@ -194,9 +192,9 @@ last modification time.
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/broker/stores-connector.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/broker/stores-connector.bro
|
||||||
|
|
||||||
In the above example, if a local copy of the store contents isn't
|
In the above example, if a local copy of the store contents isn't
|
||||||
needed, just replace the :bro:see:`BrokerStore::create_clone` call with
|
needed, just replace the :bro:see:`Broker::create_clone` call with
|
||||||
:bro:see:`BrokerStore::create_frontend`. Queries will then be made against
|
:bro:see:`Broker::create_frontend`. Queries will then be made against
|
||||||
the remote master store instead of the local clone.
|
the remote master store instead of the local clone.
|
||||||
|
|
||||||
Note that all queries are made within Bro's asynchrounous ``when``
|
Note that all data store queries must be made within Bro's asynchronous
|
||||||
statements and must specify a timeout block.
|
``when`` statements and must specify a timeout block.
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "connector";
|
redef Broker::endpoint_name = "connector";
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
|
Broker::connect("127.0.0.1", broker_port, 1sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_established(peer_address: string,
|
event Broker::outgoing_connection_established(peer_address: string,
|
||||||
peer_port: port,
|
peer_port: port,
|
||||||
peer_name: string)
|
peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::outgoing_connection_established",
|
print "Broker::outgoing_connection_established",
|
||||||
peer_address, peer_port, peer_name;
|
peer_address, peer_port, peer_name;
|
||||||
terminate();
|
terminate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "listener";
|
redef Broker::endpoint_name = "listener";
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::listen(broker_port, "127.0.0.1");
|
Broker::listen(broker_port, "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::incoming_connection_established(peer_name: string)
|
event Broker::incoming_connection_established(peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::incoming_connection_established", peer_name;
|
print "Broker::incoming_connection_established", peer_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::incoming_connection_broken(peer_name: string)
|
event Broker::incoming_connection_broken(peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::incoming_connection_broken", peer_name;
|
print "Broker::incoming_connection_broken", peer_name;
|
||||||
terminate();
|
terminate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "connector";
|
redef Broker::endpoint_name = "connector";
|
||||||
global my_event: event(msg: string, c: count);
|
global my_event: event(msg: string, c: count);
|
||||||
global my_auto_event: event(msg: string, c: count);
|
global my_auto_event: event(msg: string, c: count);
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
|
Broker::connect("127.0.0.1", broker_port, 1sec);
|
||||||
BrokerComm::auto_event("bro/event/my_auto_event", my_auto_event);
|
Broker::auto_event("bro/event/my_auto_event", my_auto_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_established(peer_address: string,
|
event Broker::outgoing_connection_established(peer_address: string,
|
||||||
peer_port: port,
|
peer_port: port,
|
||||||
peer_name: string)
|
peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::outgoing_connection_established",
|
print "Broker::outgoing_connection_established",
|
||||||
peer_address, peer_port, peer_name;
|
peer_address, peer_port, peer_name;
|
||||||
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "hi", 0));
|
Broker::event("bro/event/my_event", Broker::event_args(my_event, "hi", 0));
|
||||||
event my_auto_event("stuff", 88);
|
event my_auto_event("stuff", 88);
|
||||||
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "...", 1));
|
Broker::event("bro/event/my_event", Broker::event_args(my_event, "...", 1));
|
||||||
event my_auto_event("more stuff", 51);
|
event my_auto_event("more stuff", 51);
|
||||||
BrokerComm::event("bro/event/my_event", BrokerComm::event_args(my_event, "bye", 2));
|
Broker::event("bro/event/my_event", Broker::event_args(my_event, "bye", 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_broken(peer_address: string,
|
event Broker::outgoing_connection_broken(peer_address: string,
|
||||||
peer_port: port)
|
peer_port: port)
|
||||||
{
|
{
|
||||||
terminate();
|
terminate();
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "listener";
|
redef Broker::endpoint_name = "listener";
|
||||||
global msg_count = 0;
|
global msg_count = 0;
|
||||||
global my_event: event(msg: string, c: count);
|
global my_event: event(msg: string, c: count);
|
||||||
global my_auto_event: event(msg: string, c: count);
|
global my_auto_event: event(msg: string, c: count);
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::subscribe_to_events("bro/event/");
|
Broker::subscribe_to_events("bro/event/");
|
||||||
BrokerComm::listen(broker_port, "127.0.0.1");
|
Broker::listen(broker_port, "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::incoming_connection_established(peer_name: string)
|
event Broker::incoming_connection_established(peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::incoming_connection_established", peer_name;
|
print "Broker::incoming_connection_established", peer_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event my_event(msg: string, c: count)
|
event my_event(msg: string, c: count)
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "connector";
|
redef Broker::endpoint_name = "connector";
|
||||||
redef Log::enable_local_logging = F;
|
redef Log::enable_local_logging = F;
|
||||||
redef Log::enable_remote_logging = F;
|
redef Log::enable_remote_logging = F;
|
||||||
global n = 0;
|
global n = 0;
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::enable_remote_logs(Test::LOG);
|
Broker::enable_remote_logs(Test::LOG);
|
||||||
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
|
Broker::connect("127.0.0.1", broker_port, 1sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
event do_write()
|
event do_write()
|
||||||
|
@ -24,16 +24,16 @@ event do_write()
|
||||||
event do_write();
|
event do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_established(peer_address: string,
|
event Broker::outgoing_connection_established(peer_address: string,
|
||||||
peer_port: port,
|
peer_port: port,
|
||||||
peer_name: string)
|
peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::outgoing_connection_established",
|
print "Broker::outgoing_connection_established",
|
||||||
peer_address, peer_port, peer_name;
|
peer_address, peer_port, peer_name;
|
||||||
event do_write();
|
event do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_broken(peer_address: string,
|
event Broker::outgoing_connection_broken(peer_address: string,
|
||||||
peer_port: port)
|
peer_port: port)
|
||||||
{
|
{
|
||||||
terminate();
|
terminate();
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "listener";
|
redef Broker::endpoint_name = "listener";
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::subscribe_to_logs("bro/log/Test::LOG");
|
Broker::subscribe_to_logs("bro/log/Test::LOG");
|
||||||
BrokerComm::listen(broker_port, "127.0.0.1");
|
Broker::listen(broker_port, "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::incoming_connection_established(peer_name: string)
|
event Broker::incoming_connection_established(peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::incoming_connection_established", peer_name;
|
print "Broker::incoming_connection_established", peer_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event Test::log_test(rec: Test::Info)
|
event Test::log_test(rec: Test::Info)
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "connector";
|
redef Broker::endpoint_name = "connector";
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::connect("127.0.0.1", broker_port, 1sec);
|
Broker::connect("127.0.0.1", broker_port, 1sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_established(peer_address: string,
|
event Broker::outgoing_connection_established(peer_address: string,
|
||||||
peer_port: port,
|
peer_port: port,
|
||||||
peer_name: string)
|
peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::outgoing_connection_established",
|
print "Broker::outgoing_connection_established",
|
||||||
peer_address, peer_port, peer_name;
|
peer_address, peer_port, peer_name;
|
||||||
BrokerComm::print("bro/print/hi", "hello");
|
Broker::print("bro/print/hi", "hello");
|
||||||
BrokerComm::print("bro/print/stuff", "...");
|
Broker::print("bro/print/stuff", "...");
|
||||||
BrokerComm::print("bro/print/bye", "goodbye");
|
Broker::print("bro/print/bye", "goodbye");
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_broken(peer_address: string,
|
event Broker::outgoing_connection_broken(peer_address: string,
|
||||||
peer_port: port)
|
peer_port: port)
|
||||||
{
|
{
|
||||||
terminate();
|
terminate();
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
|
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
redef BrokerComm::endpoint_name = "listener";
|
redef Broker::endpoint_name = "listener";
|
||||||
global msg_count = 0;
|
global msg_count = 0;
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::subscribe_to_prints("bro/print/");
|
Broker::subscribe_to_prints("bro/print/");
|
||||||
BrokerComm::listen(broker_port, "127.0.0.1");
|
Broker::listen(broker_port, "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::incoming_connection_established(peer_name: string)
|
event Broker::incoming_connection_established(peer_name: string)
|
||||||
{
|
{
|
||||||
print "BrokerComm::incoming_connection_established", peer_name;
|
print "Broker::incoming_connection_established", peer_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::print_handler(msg: string)
|
event Broker::print_handler(msg: string)
|
||||||
{
|
{
|
||||||
++msg_count;
|
++msg_count;
|
||||||
print "got print message", msg;
|
print "got print message", msg;
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
global h: opaque of BrokerStore::Handle;
|
global h: opaque of Broker::Handle;
|
||||||
|
|
||||||
function dv(d: BrokerComm::Data): BrokerComm::DataVector
|
function dv(d: Broker::Data): Broker::DataVector
|
||||||
{
|
{
|
||||||
local rval: BrokerComm::DataVector;
|
local rval: Broker::DataVector;
|
||||||
rval[0] = d;
|
rval[0] = d;
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
global ready: event();
|
global ready: event();
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_broken(peer_address: string,
|
event Broker::outgoing_connection_broken(peer_address: string,
|
||||||
peer_port: port)
|
peer_port: port)
|
||||||
{
|
{
|
||||||
terminate();
|
terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
event BrokerComm::outgoing_connection_established(peer_address: string,
|
event Broker::outgoing_connection_established(peer_address: string,
|
||||||
peer_port: port,
|
peer_port: port,
|
||||||
peer_name: string)
|
peer_name: string)
|
||||||
{
|
{
|
||||||
local myset: set[string] = {"a", "b", "c"};
|
local myset: set[string] = {"a", "b", "c"};
|
||||||
local myvec: vector of string = {"alpha", "beta", "gamma"};
|
local myvec: vector of string = {"alpha", "beta", "gamma"};
|
||||||
h = BrokerStore::create_master("mystore");
|
h = Broker::create_master("mystore");
|
||||||
BrokerStore::insert(h, BrokerComm::data("one"), BrokerComm::data(110));
|
Broker::insert(h, Broker::data("one"), Broker::data(110));
|
||||||
BrokerStore::insert(h, BrokerComm::data("two"), BrokerComm::data(223));
|
Broker::insert(h, Broker::data("two"), Broker::data(223));
|
||||||
BrokerStore::insert(h, BrokerComm::data("myset"), BrokerComm::data(myset));
|
Broker::insert(h, Broker::data("myset"), Broker::data(myset));
|
||||||
BrokerStore::insert(h, BrokerComm::data("myvec"), BrokerComm::data(myvec));
|
Broker::insert(h, Broker::data("myvec"), Broker::data(myvec));
|
||||||
BrokerStore::increment(h, BrokerComm::data("one"));
|
Broker::increment(h, Broker::data("one"));
|
||||||
BrokerStore::decrement(h, BrokerComm::data("two"));
|
Broker::decrement(h, Broker::data("two"));
|
||||||
BrokerStore::add_to_set(h, BrokerComm::data("myset"), BrokerComm::data("d"));
|
Broker::add_to_set(h, Broker::data("myset"), Broker::data("d"));
|
||||||
BrokerStore::remove_from_set(h, BrokerComm::data("myset"), BrokerComm::data("b"));
|
Broker::remove_from_set(h, Broker::data("myset"), Broker::data("b"));
|
||||||
BrokerStore::push_left(h, BrokerComm::data("myvec"), dv(BrokerComm::data("delta")));
|
Broker::push_left(h, Broker::data("myvec"), dv(Broker::data("delta")));
|
||||||
BrokerStore::push_right(h, BrokerComm::data("myvec"), dv(BrokerComm::data("omega")));
|
Broker::push_right(h, Broker::data("myvec"), dv(Broker::data("omega")));
|
||||||
|
|
||||||
when ( local res = BrokerStore::size(h) )
|
when ( local res = Broker::size(h) )
|
||||||
{
|
{
|
||||||
print "master size", res;
|
print "master size", res;
|
||||||
event ready();
|
event ready();
|
||||||
|
@ -47,7 +47,7 @@ event BrokerComm::outgoing_connection_established(peer_address: string,
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::connect("127.0.0.1", broker_port, 1secs);
|
Broker::connect("127.0.0.1", broker_port, 1secs);
|
||||||
BrokerComm::auto_event("bro/event/ready", ready);
|
Broker::auto_event("bro/event/ready", ready);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const broker_port: port = 9999/tcp &redef;
|
const broker_port: port = 9999/tcp &redef;
|
||||||
redef exit_only_after_terminate = T;
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
global h: opaque of BrokerStore::Handle;
|
global h: opaque of Broker::Handle;
|
||||||
global expected_key_count = 4;
|
global expected_key_count = 4;
|
||||||
global key_count = 0;
|
global key_count = 0;
|
||||||
|
|
||||||
function do_lookup(key: string)
|
function do_lookup(key: string)
|
||||||
{
|
{
|
||||||
when ( local res = BrokerStore::lookup(h, BrokerComm::data(key)) )
|
when ( local res = Broker::lookup(h, Broker::data(key)) )
|
||||||
{
|
{
|
||||||
++key_count;
|
++key_count;
|
||||||
print "lookup", key, res;
|
print "lookup", key, res;
|
||||||
|
@ -21,15 +21,15 @@ function do_lookup(key: string)
|
||||||
|
|
||||||
event ready()
|
event ready()
|
||||||
{
|
{
|
||||||
h = BrokerStore::create_clone("mystore");
|
h = Broker::create_clone("mystore");
|
||||||
|
|
||||||
when ( local res = BrokerStore::keys(h) )
|
when ( local res = Broker::keys(h) )
|
||||||
{
|
{
|
||||||
print "clone keys", res;
|
print "clone keys", res;
|
||||||
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 0)));
|
do_lookup(Broker::refine_to_string(Broker::vector_lookup(res$result, 0)));
|
||||||
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 1)));
|
do_lookup(Broker::refine_to_string(Broker::vector_lookup(res$result, 1)));
|
||||||
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 2)));
|
do_lookup(Broker::refine_to_string(Broker::vector_lookup(res$result, 2)));
|
||||||
do_lookup(BrokerComm::refine_to_string(BrokerComm::vector_lookup(res$result, 3)));
|
do_lookup(Broker::refine_to_string(Broker::vector_lookup(res$result, 3)));
|
||||||
}
|
}
|
||||||
timeout 10sec
|
timeout 10sec
|
||||||
{ print "timeout"; }
|
{ print "timeout"; }
|
||||||
|
@ -37,7 +37,7 @@ event ready()
|
||||||
|
|
||||||
event bro_init()
|
event bro_init()
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
BrokerComm::subscribe_to_events("bro/event/ready");
|
Broker::subscribe_to_events("bro/event/ready");
|
||||||
BrokerComm::listen(broker_port, "127.0.0.1");
|
Broker::listen(broker_port, "127.0.0.1");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
module Test;
|
module Test;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -14,6 +13,6 @@ export {
|
||||||
|
|
||||||
event bro_init() &priority=5
|
event bro_init() &priority=5
|
||||||
{
|
{
|
||||||
BrokerComm::enable();
|
Broker::enable();
|
||||||
Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test, $path="test"]);
|
Log::create_stream(Test::LOG, [$columns=Test::Info, $ev=log_test, $path="test"]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ GeoLocation
|
||||||
Install libGeoIP
|
Install libGeoIP
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
Before building Bro, you need to install libGeoIP.
|
||||||
|
|
||||||
* FreeBSD:
|
* FreeBSD:
|
||||||
|
|
||||||
.. console::
|
.. console::
|
||||||
|
|
||||||
sudo pkg_add -r GeoIP
|
sudo pkg install GeoIP
|
||||||
|
|
||||||
* RPM/RedHat-based Linux:
|
* RPM/RedHat-based Linux:
|
||||||
|
|
||||||
|
@ -40,80 +42,99 @@ Install libGeoIP
|
||||||
|
|
||||||
* Mac OS X:
|
* Mac OS X:
|
||||||
|
|
||||||
Vanilla OS X installations don't ship with libGeoIP, but if
|
You need to install from your preferred package management system
|
||||||
installed from your preferred package management system (e.g.
|
(e.g. MacPorts, Fink, or Homebrew). The name of the package that you need
|
||||||
MacPorts, Fink, or Homebrew), they should be automatically detected
|
may be libgeoip, geoip, or geoip-dev, depending on which package management
|
||||||
and Bro will compile against them.
|
system you are using.
|
||||||
|
|
||||||
|
|
||||||
GeoIPLite Database Installation
|
GeoIPLite Database Installation
|
||||||
------------------------------------
|
-------------------------------
|
||||||
|
|
||||||
A country database for GeoIPLite is included when you do the C API
|
A country database for GeoIPLite is included when you do the C API
|
||||||
install, but for Bro, we are using the city database which includes
|
install, but for Bro, we are using the city database which includes
|
||||||
cities and regions in addition to countries.
|
cities and regions in addition to countries.
|
||||||
|
|
||||||
`Download <http://www.maxmind.com/app/geolitecity>`__ the GeoLite city
|
`Download <http://www.maxmind.com/app/geolitecity>`__ the GeoLite city
|
||||||
binary database.
|
binary database:
|
||||||
|
|
||||||
.. console::
|
.. console::
|
||||||
|
|
||||||
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
|
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
|
||||||
gunzip GeoLiteCity.dat.gz
|
gunzip GeoLiteCity.dat.gz
|
||||||
|
|
||||||
Next, the file needs to be put in the database directory. This directory
|
Next, the file needs to be renamed and put in the GeoIP database directory.
|
||||||
should already exist and will vary depending on which platform and package
|
This directory should already exist and will vary depending on which platform
|
||||||
you are using. For FreeBSD, use ``/usr/local/share/GeoIP``. For Linux,
|
and package you are using. For FreeBSD, use ``/usr/local/share/GeoIP``. For
|
||||||
use ``/usr/share/GeoIP`` or ``/var/lib/GeoIP`` (choose whichever one
|
Linux, use ``/usr/share/GeoIP`` or ``/var/lib/GeoIP`` (choose whichever one
|
||||||
already exists).
|
already exists).
|
||||||
|
|
||||||
.. console::
|
.. console::
|
||||||
|
|
||||||
mv GeoLiteCity.dat <path_to_database_dir>/GeoIPCity.dat
|
mv GeoLiteCity.dat <path_to_database_dir>/GeoIPCity.dat
|
||||||
|
|
||||||
|
Note that there is a separate database for IPv6 addresses, which can also
|
||||||
|
be installed if you want GeoIP functionality for IPv6.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
Before using the GeoIP functionality, it is a good idea to verify that
|
||||||
|
everything is setup correctly. After installing libGeoIP and the GeoIP city
|
||||||
|
database, and building Bro, you can quickly check if the GeoIP functionality
|
||||||
|
works by running a command like this:
|
||||||
|
|
||||||
|
.. console::
|
||||||
|
|
||||||
|
bro -e "print lookup_location(8.8.8.8);"
|
||||||
|
|
||||||
|
If you see an error message similar to "Failed to open GeoIP City database",
|
||||||
|
then you may need to either rename or move your GeoIP city database file (the
|
||||||
|
error message should give you the full pathname of the database file that
|
||||||
|
Bro is looking for).
|
||||||
|
|
||||||
|
If you see an error message similar to "Bro was not configured for GeoIP
|
||||||
|
support", then you need to rebuild Bro and make sure it is linked against
|
||||||
|
libGeoIP. Normally, if libGeoIP is installed correctly then it should
|
||||||
|
automatically be found when building Bro. If this doesn't happen, then
|
||||||
|
you may need to specify the path to the libGeoIP installation
|
||||||
|
(e.g. ``./configure --with-geoip=<path>``).
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
There is a single built in function that provides the GeoIP
|
There is a built-in function that provides the GeoIP functionality:
|
||||||
functionality:
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
function lookup_location(a:addr): geo_location
|
function lookup_location(a:addr): geo_location
|
||||||
|
|
||||||
There is also the :bro:see:`geo_location` data structure that is returned
|
The return value of the :bro:see:`lookup_location` function is a record
|
||||||
from the :bro:see:`lookup_location` function:
|
type called :bro:see:`geo_location`, and it consists of several fields
|
||||||
|
containing the country, region, city, latitude, and longitude of the specified
|
||||||
.. code:: bro
|
IP address. Since one or more fields in this record will be uninitialized
|
||||||
|
for some IP addresses (for example, the country and region of an IP address
|
||||||
type geo_location: record {
|
might be known, but the city could be unknown), a field should be checked
|
||||||
country_code: string;
|
if it has a value before trying to access the value.
|
||||||
region: string;
|
|
||||||
city: string;
|
|
||||||
latitude: double;
|
|
||||||
longitude: double;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
To write a line in a log file for every ftp connection from hosts in
|
To show every ftp connection from hosts in Ohio, this is now very easy:
|
||||||
Ohio, this is now very easy:
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
global ftp_location_log: file = open_log_file("ftp-location");
|
|
||||||
|
|
||||||
event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool)
|
event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool)
|
||||||
{
|
{
|
||||||
local client = c$id$orig_h;
|
local client = c$id$orig_h;
|
||||||
local loc = lookup_location(client);
|
local loc = lookup_location(client);
|
||||||
if (loc$region == "OH" && loc$country_code == "US")
|
|
||||||
|
if (loc?$region && loc$region == "OH" && loc$country_code == "US")
|
||||||
{
|
{
|
||||||
print ftp_location_log, fmt("FTP Connection from:%s (%s,%s,%s)", client, loc$city, loc$region, loc$country_code);
|
local city = loc?$city ? loc$city : "<unknown>";
|
||||||
|
|
||||||
|
print fmt("FTP Connection from:%s (%s,%s,%s)", client, city,
|
||||||
|
loc$region, loc$country_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ For this example we assume that we want to import data from a blacklist
|
||||||
that contains server IP addresses as well as the timestamp and the reason
|
that contains server IP addresses as well as the timestamp and the reason
|
||||||
for the block.
|
for the block.
|
||||||
|
|
||||||
An example input file could look like this:
|
An example input file could look like this (note that all fields must be
|
||||||
|
tab-separated):
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -63,19 +64,23 @@ The two records are defined as:
|
||||||
reason: string;
|
reason: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
Note that the names of the fields in the record definitions have to correspond
|
Note that the names of the fields in the record definitions must correspond
|
||||||
to the column names listed in the '#fields' line of the log file, in this
|
to the column names listed in the '#fields' line of the log file, in this
|
||||||
case 'ip', 'timestamp', and 'reason'.
|
case 'ip', 'timestamp', and 'reason'. Also note that the ordering of the
|
||||||
|
columns does not matter, because each column is identified by name.
|
||||||
|
|
||||||
The log file is read into the table with a simple call of the ``add_table``
|
The log file is read into the table with a simple call of the
|
||||||
function:
|
:bro:id:`Input::add_table` function:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
global blacklist: table[addr] of Val = table();
|
global blacklist: table[addr] of Val = table();
|
||||||
|
|
||||||
Input::add_table([$source="blacklist.file", $name="blacklist", $idx=Idx, $val=Val, $destination=blacklist]);
|
event bro_init() {
|
||||||
|
Input::add_table([$source="blacklist.file", $name="blacklist",
|
||||||
|
$idx=Idx, $val=Val, $destination=blacklist]);
|
||||||
Input::remove("blacklist");
|
Input::remove("blacklist");
|
||||||
|
}
|
||||||
|
|
||||||
With these three lines we first create an empty table that should contain the
|
With these three lines we first create an empty table that should contain the
|
||||||
blacklist data and then instruct the input framework to open an input stream
|
blacklist data and then instruct the input framework to open an input stream
|
||||||
|
@ -92,7 +97,7 @@ Because of this, the data is not immediately accessible. Depending on the
|
||||||
size of the data source it might take from a few milliseconds up to a few
|
size of the data source it might take from a few milliseconds up to a few
|
||||||
seconds until all data is present in the table. Please note that this means
|
seconds until all data is present in the table. Please note that this means
|
||||||
that when Bro is running without an input source or on very short captured
|
that when Bro is running without an input source or on very short captured
|
||||||
files, it might terminate before the data is present in the system (because
|
files, it might terminate before the data is present in the table (because
|
||||||
Bro already handled all packets before the import thread finished).
|
Bro already handled all packets before the import thread finished).
|
||||||
|
|
||||||
Subsequent calls to an input source are queued until the previous action has
|
Subsequent calls to an input source are queued until the previous action has
|
||||||
|
@ -101,8 +106,8 @@ been completed. Because of this, it is, for example, possible to call
|
||||||
will remain queued until the first read has been completed.
|
will remain queued until the first read has been completed.
|
||||||
|
|
||||||
Once the input framework finishes reading from a data source, it fires
|
Once the input framework finishes reading from a data source, it fires
|
||||||
the ``end_of_data`` event. Once this event has been received all data
|
the :bro:id:`Input::end_of_data` event. Once this event has been received all
|
||||||
from the input file is available in the table.
|
data from the input file is available in the table.
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
|
@ -111,9 +116,9 @@ from the input file is available in the table.
|
||||||
print blacklist;
|
print blacklist;
|
||||||
}
|
}
|
||||||
|
|
||||||
The table can also already be used while the data is still being read - it
|
The table can be used while the data is still being read - it
|
||||||
just might not contain all lines in the input file when the event has not
|
just might not contain all lines from the input file before the event has
|
||||||
yet fired. After it has been populated it can be used like any other Bro
|
fired. After the table has been populated it can be used like any other Bro
|
||||||
table and blacklist entries can easily be tested:
|
table and blacklist entries can easily be tested:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
@ -130,10 +135,11 @@ changing. For these cases, the Bro input framework supports several ways to
|
||||||
deal with changing data files.
|
deal with changing data files.
|
||||||
|
|
||||||
The first, very basic method is an explicit refresh of an input stream. When
|
The first, very basic method is an explicit refresh of an input stream. When
|
||||||
an input stream is open, the function ``force_update`` can be called. This
|
an input stream is open (this means it has not yet been removed by a call to
|
||||||
will trigger a complete refresh of the table; any changed elements from the
|
:bro:id:`Input::remove`), the function :bro:id:`Input::force_update` can be
|
||||||
file will be updated. After the update is finished the ``end_of_data``
|
called. This will trigger a complete refresh of the table; any changed
|
||||||
event will be raised.
|
elements from the file will be updated. After the update is finished the
|
||||||
|
:bro:id:`Input::end_of_data` event will be raised.
|
||||||
|
|
||||||
In our example the call would look like:
|
In our example the call would look like:
|
||||||
|
|
||||||
|
@ -141,30 +147,35 @@ In our example the call would look like:
|
||||||
|
|
||||||
Input::force_update("blacklist");
|
Input::force_update("blacklist");
|
||||||
|
|
||||||
The input framework also supports two automatic refresh modes. The first mode
|
Alternatively, the input framework can automatically refresh the table
|
||||||
continually checks if a file has been changed. If the file has been changed, it
|
contents when it detects a change to the input file. To use this feature,
|
||||||
|
you need to specify a non-default read mode by setting the ``mode`` option
|
||||||
|
of the :bro:id:`Input::add_table` call. Valid values are ``Input::MANUAL``
|
||||||
|
(the default), ``Input::REREAD`` and ``Input::STREAM``. For example,
|
||||||
|
setting the value of the ``mode`` option in the previous example
|
||||||
|
would look like this:
|
||||||
|
|
||||||
|
.. code:: bro
|
||||||
|
|
||||||
|
Input::add_table([$source="blacklist.file", $name="blacklist",
|
||||||
|
$idx=Idx, $val=Val, $destination=blacklist,
|
||||||
|
$mode=Input::REREAD]);
|
||||||
|
|
||||||
|
When using the reread mode (i.e., ``$mode=Input::REREAD``), Bro continually
|
||||||
|
checks if the input file has been changed. If the file has been changed, it
|
||||||
is re-read and the data in the Bro table is updated to reflect the current
|
is re-read and the data in the Bro table is updated to reflect the current
|
||||||
state. Each time a change has been detected and all the new data has been
|
state. Each time a change has been detected and all the new data has been
|
||||||
read into the table, the ``end_of_data`` event is raised.
|
read into the table, the ``end_of_data`` event is raised.
|
||||||
|
|
||||||
The second mode is a streaming mode. This mode assumes that the source data
|
When using the streaming mode (i.e., ``$mode=Input::STREAM``), Bro assumes
|
||||||
file is an append-only file to which new data is continually appended. Bro
|
that the source data file is an append-only file to which new data is
|
||||||
continually checks for new data at the end of the file and will add the new
|
continually appended. Bro continually checks for new data at the end of
|
||||||
data to the table. If newer lines in the file have the same index as previous
|
the file and will add the new data to the table. If newer lines in the
|
||||||
lines, they will overwrite the values in the output table. Because of the
|
file have the same index as previous lines, they will overwrite the
|
||||||
nature of streaming reads (data is continually added to the table),
|
values in the output table. Because of the nature of streaming reads
|
||||||
the ``end_of_data`` event is never raised when using streaming reads.
|
(data is continually added to the table), the ``end_of_data`` event
|
||||||
|
is never raised when using streaming reads.
|
||||||
|
|
||||||
The reading mode can be selected by setting the ``mode`` option of the
|
|
||||||
add_table call. Valid values are ``MANUAL`` (the default), ``REREAD``
|
|
||||||
and ``STREAM``.
|
|
||||||
|
|
||||||
Hence, when adding ``$mode=Input::REREAD`` to the previous example, the
|
|
||||||
blacklist table will always reflect the state of the blacklist input file.
|
|
||||||
|
|
||||||
.. code:: bro
|
|
||||||
|
|
||||||
Input::add_table([$source="blacklist.file", $name="blacklist", $idx=Idx, $val=Val, $destination=blacklist, $mode=Input::REREAD]);
|
|
||||||
|
|
||||||
Receiving change events
|
Receiving change events
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -173,34 +184,40 @@ When re-reading files, it might be interesting to know exactly which lines in
|
||||||
the source files have changed.
|
the source files have changed.
|
||||||
|
|
||||||
For this reason, the input framework can raise an event each time when a data
|
For this reason, the input framework can raise an event each time when a data
|
||||||
item is added to, removed from or changed in a table.
|
item is added to, removed from, or changed in a table.
|
||||||
|
|
||||||
The event definition looks like this:
|
The event definition looks like this (note that you can change the name of
|
||||||
|
this event in your own Bro script):
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
event entry(description: Input::TableDescription, tpe: Input::Event, left: Idx, right: Val) {
|
event entry(description: Input::TableDescription, tpe: Input::Event,
|
||||||
# act on values
|
left: Idx, right: Val) {
|
||||||
|
# do something here...
|
||||||
|
print fmt("%s = %s", left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
The event has to be specified in ``$ev`` in the ``add_table`` call:
|
The event must be specified in ``$ev`` in the ``add_table`` call:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
Input::add_table([$source="blacklist.file", $name="blacklist", $idx=Idx, $val=Val, $destination=blacklist, $mode=Input::REREAD, $ev=entry]);
|
Input::add_table([$source="blacklist.file", $name="blacklist",
|
||||||
|
$idx=Idx, $val=Val, $destination=blacklist,
|
||||||
|
$mode=Input::REREAD, $ev=entry]);
|
||||||
|
|
||||||
The ``description`` field of the event contains the arguments that were
|
The ``description`` argument of the event contains the arguments that were
|
||||||
originally supplied to the add_table call. Hence, the name of the stream can,
|
originally supplied to the add_table call. Hence, the name of the stream can,
|
||||||
for example, be accessed with ``description$name``. ``tpe`` is an enum
|
for example, be accessed with ``description$name``. The ``tpe`` argument of the
|
||||||
containing the type of the change that occurred.
|
event is an enum containing the type of the change that occurred.
|
||||||
|
|
||||||
If a line that was not previously present in the table has been added,
|
If a line that was not previously present in the table has been added,
|
||||||
then ``tpe`` will contain ``Input::EVENT_NEW``. In this case ``left`` contains
|
then the value of ``tpe`` will be ``Input::EVENT_NEW``. In this case ``left``
|
||||||
the index of the added table entry and ``right`` contains the values of the
|
contains the index of the added table entry and ``right`` contains the
|
||||||
added entry.
|
values of the added entry.
|
||||||
|
|
||||||
If a table entry that already was present is altered during the re-reading or
|
If a table entry that already was present is altered during the re-reading or
|
||||||
streaming read of a file, ``tpe`` will contain ``Input::EVENT_CHANGED``. In
|
streaming read of a file, then the value of ``tpe`` will be
|
||||||
|
``Input::EVENT_CHANGED``. In
|
||||||
this case ``left`` contains the index of the changed table entry and ``right``
|
this case ``left`` contains the index of the changed table entry and ``right``
|
||||||
contains the values of the entry before the change. The reason for this is
|
contains the values of the entry before the change. The reason for this is
|
||||||
that the table already has been updated when the event is raised. The current
|
that the table already has been updated when the event is raised. The current
|
||||||
|
@ -208,8 +225,9 @@ value in the table can be ascertained by looking up the current table value.
|
||||||
Hence it is possible to compare the new and the old values of the table.
|
Hence it is possible to compare the new and the old values of the table.
|
||||||
|
|
||||||
If a table element is removed because it was no longer present during a
|
If a table element is removed because it was no longer present during a
|
||||||
re-read, then ``tpe`` will contain ``Input::REMOVED``. In this case ``left``
|
re-read, then the value of ``tpe`` will be ``Input::EVENT_REMOVED``. In this
|
||||||
contains the index and ``right`` the values of the removed element.
|
case ``left`` contains the index and ``right`` the values of the removed
|
||||||
|
element.
|
||||||
|
|
||||||
|
|
||||||
Filtering data during import
|
Filtering data during import
|
||||||
|
@ -222,24 +240,26 @@ can either accept or veto the change by returning true for an accepted
|
||||||
change and false for a rejected change. Furthermore, it can alter the data
|
change and false for a rejected change. Furthermore, it can alter the data
|
||||||
before it is written to the table.
|
before it is written to the table.
|
||||||
|
|
||||||
The following example filter will reject to add entries to the table when
|
The following example filter will reject adding entries to the table when
|
||||||
they were generated over a month ago. It will accept all changes and all
|
they were generated over a month ago. It will accept all changes and all
|
||||||
removals of values that are already present in the table.
|
removals of values that are already present in the table.
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
Input::add_table([$source="blacklist.file", $name="blacklist", $idx=Idx, $val=Val, $destination=blacklist, $mode=Input::REREAD,
|
Input::add_table([$source="blacklist.file", $name="blacklist",
|
||||||
|
$idx=Idx, $val=Val, $destination=blacklist,
|
||||||
|
$mode=Input::REREAD,
|
||||||
$pred(typ: Input::Event, left: Idx, right: Val) = {
|
$pred(typ: Input::Event, left: Idx, right: Val) = {
|
||||||
if ( typ != Input::EVENT_NEW ) {
|
if ( typ != Input::EVENT_NEW ) {
|
||||||
return T;
|
return T;
|
||||||
}
|
}
|
||||||
return ( ( current_time() - right$timestamp ) < (30 day) );
|
return (current_time() - right$timestamp) < 30day;
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
To change elements while they are being imported, the predicate function can
|
To change elements while they are being imported, the predicate function can
|
||||||
manipulate ``left`` and ``right``. Note that predicate functions are called
|
manipulate ``left`` and ``right``. Note that predicate functions are called
|
||||||
before the change is committed to the table. Hence, when a table element is
|
before the change is committed to the table. Hence, when a table element is
|
||||||
changed (``tpe`` is ``INPUT::EVENT_CHANGED``), ``left`` and ``right``
|
changed (``typ`` is ``Input::EVENT_CHANGED``), ``left`` and ``right``
|
||||||
contain the new values, but the destination (``blacklist`` in our example)
|
contain the new values, but the destination (``blacklist`` in our example)
|
||||||
still contains the old values. This allows predicate functions to examine
|
still contains the old values. This allows predicate functions to examine
|
||||||
the changes between the old and the new version before deciding if they
|
the changes between the old and the new version before deciding if they
|
||||||
|
@ -250,14 +270,19 @@ Different readers
|
||||||
|
|
||||||
The input framework supports different kinds of readers for different kinds
|
The input framework supports different kinds of readers for different kinds
|
||||||
of source data files. At the moment, the default reader reads ASCII files
|
of source data files. At the moment, the default reader reads ASCII files
|
||||||
formatted in the Bro log file format (tab-separated values). At the moment,
|
formatted in the Bro log file format (tab-separated values with a "#fields"
|
||||||
Bro comes with two other readers. The ``RAW`` reader reads a file that is
|
header line). Several other readers are included in Bro.
|
||||||
split by a specified record separator (usually newline). The contents are
|
|
||||||
|
The raw reader reads a file that is
|
||||||
|
split by a specified record separator (newline by default). The contents are
|
||||||
returned line-by-line as strings; it can, for example, be used to read
|
returned line-by-line as strings; it can, for example, be used to read
|
||||||
configuration files and the like and is probably
|
configuration files and the like and is probably
|
||||||
only useful in the event mode and not for reading data to tables.
|
only useful in the event mode and not for reading data to tables.
|
||||||
|
|
||||||
Another included reader is the ``BENCHMARK`` reader, which is being used
|
The binary reader is intended to be used with file analysis input streams (and
|
||||||
|
is the default type of reader for those streams).
|
||||||
|
|
||||||
|
The benchmark reader is being used
|
||||||
to optimize the speed of the input framework. It can generate arbitrary
|
to optimize the speed of the input framework. It can generate arbitrary
|
||||||
amounts of semi-random data in all Bro data types supported by the input
|
amounts of semi-random data in all Bro data types supported by the input
|
||||||
framework.
|
framework.
|
||||||
|
@ -270,75 +295,17 @@ aforementioned ones:
|
||||||
|
|
||||||
logging-input-sqlite
|
logging-input-sqlite
|
||||||
|
|
||||||
Add_table options
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
This section lists all possible options that can be used for the add_table
|
|
||||||
function and gives a short explanation of their use. Most of the options
|
|
||||||
already have been discussed in the previous sections.
|
|
||||||
|
|
||||||
The possible fields that can be set for a table stream are:
|
|
||||||
|
|
||||||
``source``
|
|
||||||
A mandatory string identifying the source of the data.
|
|
||||||
For the ASCII reader this is the filename.
|
|
||||||
|
|
||||||
``name``
|
|
||||||
A mandatory name for the filter that can later be used
|
|
||||||
to manipulate it further.
|
|
||||||
|
|
||||||
``idx``
|
|
||||||
Record type that defines the index of the table.
|
|
||||||
|
|
||||||
``val``
|
|
||||||
Record type that defines the values of the table.
|
|
||||||
|
|
||||||
``reader``
|
|
||||||
The reader used for this stream. Default is ``READER_ASCII``.
|
|
||||||
|
|
||||||
``mode``
|
|
||||||
The mode in which the stream is opened. Possible values are
|
|
||||||
``MANUAL``, ``REREAD`` and ``STREAM``. Default is ``MANUAL``.
|
|
||||||
``MANUAL`` means that the file is not updated after it has
|
|
||||||
been read. Changes to the file will not be reflected in the
|
|
||||||
data Bro knows. ``REREAD`` means that the whole file is read
|
|
||||||
again each time a change is found. This should be used for
|
|
||||||
files that are mapped to a table where individual lines can
|
|
||||||
change. ``STREAM`` means that the data from the file is
|
|
||||||
streamed. Events / table entries will be generated as new
|
|
||||||
data is appended to the file.
|
|
||||||
|
|
||||||
``destination``
|
|
||||||
The destination table.
|
|
||||||
|
|
||||||
``ev``
|
|
||||||
Optional event that is raised, when values are added to,
|
|
||||||
changed in, or deleted from the table. Events are passed an
|
|
||||||
Input::Event description as the first argument, the index
|
|
||||||
record as the second argument and the values as the third
|
|
||||||
argument.
|
|
||||||
|
|
||||||
``pred``
|
|
||||||
Optional predicate, that can prevent entries from being added
|
|
||||||
to the table and events from being sent.
|
|
||||||
|
|
||||||
``want_record``
|
|
||||||
Boolean value, that defines if the event wants to receive the
|
|
||||||
fields inside of a single record value, or individually
|
|
||||||
(default). This can be used if ``val`` is a record
|
|
||||||
containing only one type. In this case, if ``want_record`` is
|
|
||||||
set to false, the table will contain elements of the type
|
|
||||||
contained in ``val``.
|
|
||||||
|
|
||||||
Reading Data to Events
|
Reading Data to Events
|
||||||
======================
|
======================
|
||||||
|
|
||||||
The second supported mode of the input framework is reading data to Bro
|
The second supported mode of the input framework is reading data to Bro
|
||||||
events instead of reading them to a table using event streams.
|
events instead of reading them to a table.
|
||||||
|
|
||||||
Event streams work very similarly to table streams that were already
|
Event streams work very similarly to table streams that were already
|
||||||
discussed in much detail. To read the blacklist of the previous example
|
discussed in much detail. To read the blacklist of the previous example
|
||||||
into an event stream, the following Bro code could be used:
|
into an event stream, the :bro:id:`Input::add_event` function is used.
|
||||||
|
For example:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
|
@ -348,12 +315,15 @@ into an event stream, the following Bro code could be used:
|
||||||
reason: string;
|
reason: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
event blacklistentry(description: Input::EventDescription, tpe: Input::Event, ip: addr, timestamp: time, reason: string) {
|
event blacklistentry(description: Input::EventDescription,
|
||||||
# work with event data
|
t: Input::Event, data: Val) {
|
||||||
|
# do something here...
|
||||||
|
print "data:", data;
|
||||||
}
|
}
|
||||||
|
|
||||||
event bro_init() {
|
event bro_init() {
|
||||||
Input::add_event([$source="blacklist.file", $name="blacklist", $fields=Val, $ev=blacklistentry]);
|
Input::add_event([$source="blacklist.file", $name="blacklist",
|
||||||
|
$fields=Val, $ev=blacklistentry]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -364,52 +334,3 @@ data types are provided in a single record definition.
|
||||||
Apart from this, event streams work exactly the same as table streams and
|
Apart from this, event streams work exactly the same as table streams and
|
||||||
support most of the options that are also supported for table streams.
|
support most of the options that are also supported for table streams.
|
||||||
|
|
||||||
The options that can be set when creating an event stream with
|
|
||||||
``add_event`` are:
|
|
||||||
|
|
||||||
``source``
|
|
||||||
A mandatory string identifying the source of the data.
|
|
||||||
For the ASCII reader this is the filename.
|
|
||||||
|
|
||||||
``name``
|
|
||||||
A mandatory name for the stream that can later be used
|
|
||||||
to remove it.
|
|
||||||
|
|
||||||
``fields``
|
|
||||||
Name of a record type containing the fields, which should be
|
|
||||||
retrieved from the input stream.
|
|
||||||
|
|
||||||
``ev``
|
|
||||||
The event which is fired, after a line has been read from the
|
|
||||||
input source. The first argument that is passed to the event
|
|
||||||
is an Input::Event structure, followed by the data, either
|
|
||||||
inside of a record (if ``want_record is set``) or as
|
|
||||||
individual fields. The Input::Event structure can contain
|
|
||||||
information, if the received line is ``NEW``, has been
|
|
||||||
``CHANGED`` or ``DELETED``. Since the ASCII reader cannot
|
|
||||||
track this information for event filters, the value is
|
|
||||||
always ``NEW`` at the moment.
|
|
||||||
|
|
||||||
``mode``
|
|
||||||
The mode in which the stream is opened. Possible values are
|
|
||||||
``MANUAL``, ``REREAD`` and ``STREAM``. Default is ``MANUAL``.
|
|
||||||
``MANUAL`` means that the file is not updated after it has
|
|
||||||
been read. Changes to the file will not be reflected in the
|
|
||||||
data Bro knows. ``REREAD`` means that the whole file is read
|
|
||||||
again each time a change is found. This should be used for
|
|
||||||
files that are mapped to a table where individual lines can
|
|
||||||
change. ``STREAM`` means that the data from the file is
|
|
||||||
streamed. Events / table entries will be generated as new
|
|
||||||
data is appended to the file.
|
|
||||||
|
|
||||||
``reader``
|
|
||||||
The reader used for this stream. Default is ``READER_ASCII``.
|
|
||||||
|
|
||||||
``want_record``
|
|
||||||
Boolean value, that defines if the event wants to receive the
|
|
||||||
fields inside of a single record value, or individually
|
|
||||||
(default). If this is set to true, the event will receive a
|
|
||||||
single record of the type provided in ``fields``.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,17 +23,18 @@ In contrast to the ASCII reader and writer, the SQLite plugins have not yet
|
||||||
seen extensive use in production environments. While we are not aware
|
seen extensive use in production environments. While we are not aware
|
||||||
of any issues with them, we urge to caution when using them
|
of any issues with them, we urge to caution when using them
|
||||||
in production environments. There could be lingering issues which only occur
|
in production environments. There could be lingering issues which only occur
|
||||||
when the plugins are used with high amounts of data or in high-load environments.
|
when the plugins are used with high amounts of data or in high-load
|
||||||
|
environments.
|
||||||
|
|
||||||
Logging Data into SQLite Databases
|
Logging Data into SQLite Databases
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Logging support for SQLite is available in all Bro installations starting with
|
Logging support for SQLite is available in all Bro installations starting with
|
||||||
version 2.2. There is no need to load any additional scripts or for any compile-time
|
version 2.2. There is no need to load any additional scripts or for any
|
||||||
configurations.
|
compile-time configurations.
|
||||||
|
|
||||||
Sending data from existing logging streams to SQLite is rather straightforward. You
|
Sending data from existing logging streams to SQLite is rather straightforward.
|
||||||
have to define a filter which specifies SQLite as the writer.
|
You have to define a filter which specifies SQLite as the writer.
|
||||||
|
|
||||||
The following example code adds SQLite as a filter for the connection log:
|
The following example code adds SQLite as a filter for the connection log:
|
||||||
|
|
||||||
|
@ -44,15 +45,15 @@ The following example code adds SQLite as a filter for the connection log:
|
||||||
# Make sure this parses correctly at least.
|
# Make sure this parses correctly at least.
|
||||||
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-conn-filter.bro
|
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-conn-filter.bro
|
||||||
|
|
||||||
Bro will create the database file ``/var/db/conn.sqlite``, if it does not already exist.
|
Bro will create the database file ``/var/db/conn.sqlite``, if it does not
|
||||||
It will also create a table with the name ``conn`` (if it does not exist) and start
|
already exist. It will also create a table with the name ``conn`` (if it
|
||||||
appending connection information to the table.
|
does not exist) and start appending connection information to the table.
|
||||||
|
|
||||||
At the moment, SQLite databases are not rotated the same way ASCII log-files are. You
|
At the moment, SQLite databases are not rotated the same way ASCII log-files
|
||||||
have to take care to create them in an adequate location.
|
are. You have to take care to create them in an adequate location.
|
||||||
|
|
||||||
If you examine the resulting SQLite database, the schema will contain the same fields
|
If you examine the resulting SQLite database, the schema will contain the
|
||||||
that are present in the ASCII log files::
|
same fields that are present in the ASCII log files::
|
||||||
|
|
||||||
# sqlite3 /var/db/conn.sqlite
|
# sqlite3 /var/db/conn.sqlite
|
||||||
|
|
||||||
|
@ -75,27 +76,31 @@ from being created, you can remove the default filter:
|
||||||
Log::remove_filter(Conn::LOG, "default");
|
Log::remove_filter(Conn::LOG, "default");
|
||||||
|
|
||||||
|
|
||||||
To create a custom SQLite log file, you have to create a new log stream that contains
|
To create a custom SQLite log file, you have to create a new log stream
|
||||||
just the information you want to commit to the database. Please refer to the
|
that contains just the information you want to commit to the database.
|
||||||
:ref:`framework-logging` documentation on how to create custom log streams.
|
Please refer to the :ref:`framework-logging` documentation on how to
|
||||||
|
create custom log streams.
|
||||||
|
|
||||||
Reading Data from SQLite Databases
|
Reading Data from SQLite Databases
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Like logging support, support for reading data from SQLite databases is built into Bro starting
|
Like logging support, support for reading data from SQLite databases is
|
||||||
with version 2.2.
|
built into Bro starting with version 2.2.
|
||||||
|
|
||||||
Just as with the text-based input readers (please refer to the :ref:`framework-input`
|
Just as with the text-based input readers (please refer to the
|
||||||
documentation for them and for basic information on how to use the input-framework), the SQLite reader
|
:ref:`framework-input` documentation for them and for basic information
|
||||||
can be used to read data - in this case the result of SQL queries - into tables or into events.
|
on how to use the input framework), the SQLite reader can be used to
|
||||||
|
read data - in this case the result of SQL queries - into tables or into
|
||||||
|
events.
|
||||||
|
|
||||||
Reading Data into Tables
|
Reading Data into Tables
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
To read data from a SQLite database, we first have to provide Bro with the information, how
|
To read data from a SQLite database, we first have to provide Bro with
|
||||||
the resulting data will be structured. For this example, we expect that we have a SQLite database,
|
the information, how the resulting data will be structured. For this
|
||||||
which contains host IP addresses and the user accounts that are allowed to log into a specific
|
example, we expect that we have a SQLite database, which contains
|
||||||
machine.
|
host IP addresses and the user accounts that are allowed to log into
|
||||||
|
a specific machine.
|
||||||
|
|
||||||
The SQLite commands to create the schema are as follows::
|
The SQLite commands to create the schema are as follows::
|
||||||
|
|
||||||
|
@ -107,8 +112,8 @@ The SQLite commands to create the schema are as follows::
|
||||||
insert into machines_to_users values ('192.168.17.2', 'bernhard');
|
insert into machines_to_users values ('192.168.17.2', 'bernhard');
|
||||||
insert into machines_to_users values ('192.168.17.3', 'seth,matthias');
|
insert into machines_to_users values ('192.168.17.3', 'seth,matthias');
|
||||||
|
|
||||||
After creating a file called ``hosts.sqlite`` with this content, we can read the resulting table
|
After creating a file called ``hosts.sqlite`` with this content, we can
|
||||||
into Bro:
|
read the resulting table into Bro:
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/sqlite-read-table.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/sqlite-read-table.bro
|
||||||
|
|
||||||
|
@ -117,22 +122,25 @@ into Bro:
|
||||||
# Make sure this parses correctly at least.
|
# Make sure this parses correctly at least.
|
||||||
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-read-table.bro
|
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-read-table.bro
|
||||||
|
|
||||||
Afterwards, that table can be used to check logins into hosts against the available
|
Afterwards, that table can be used to check logins into hosts against
|
||||||
userlist.
|
the available userlist.
|
||||||
|
|
||||||
Turning Data into Events
|
Turning Data into Events
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
The second mode is to use the SQLite reader to output the input data as events. Typically there
|
The second mode is to use the SQLite reader to output the input data as events.
|
||||||
are two reasons to do this. First, when the structure of the input data is too complicated
|
Typically there are two reasons to do this. First, when the structure of
|
||||||
for a direct table import. In this case, the data can be read into an event which can then
|
the input data is too complicated for a direct table import. In this case,
|
||||||
create the necessary data structures in Bro in scriptland.
|
the data can be read into an event which can then create the necessary
|
||||||
|
data structures in Bro in scriptland.
|
||||||
|
|
||||||
The second reason is, that the dataset is too big to hold it in memory. In this case, the checks
|
The second reason is, that the dataset is too big to hold it in memory. In
|
||||||
can be performed on-demand, when Bro encounters a situation where it needs additional information.
|
this case, the checks can be performed on-demand, when Bro encounters a
|
||||||
|
situation where it needs additional information.
|
||||||
|
|
||||||
An example for this would be an internal huge database with malware hashes. Live database queries
|
An example for this would be an internal huge database with malware
|
||||||
could be used to check the sporadically happening downloads against the database.
|
hashes. Live database queries could be used to check the sporadically
|
||||||
|
happening downloads against the database.
|
||||||
|
|
||||||
The SQLite commands to create the schema are as follows::
|
The SQLite commands to create the schema are as follows::
|
||||||
|
|
||||||
|
@ -151,9 +159,10 @@ The SQLite commands to create the schema are as follows::
|
||||||
insert into malware_hashes values ('73f45106968ff8dc51fba105fa91306af1ff6666', 'ftp-trace');
|
insert into malware_hashes values ('73f45106968ff8dc51fba105fa91306af1ff6666', 'ftp-trace');
|
||||||
|
|
||||||
|
|
||||||
The following code uses the file-analysis framework to get the sha1 hashes of files that are
|
The following code uses the file-analysis framework to get the sha1 hashes
|
||||||
transmitted over the network. For each hash, a SQL-query is run against SQLite. If the query
|
of files that are transmitted over the network. For each hash, a SQL-query
|
||||||
returns with a result, we had a hit against our malware-database and output the matching hash.
|
is run against SQLite. If the query returns with a result, we had a hit
|
||||||
|
against our malware-database and output the matching hash.
|
||||||
|
|
||||||
.. btest-include:: ${DOC_ROOT}/frameworks/sqlite-read-events.bro
|
.. btest-include:: ${DOC_ROOT}/frameworks/sqlite-read-events.bro
|
||||||
|
|
||||||
|
@ -162,5 +171,5 @@ returns with a result, we had a hit against our malware-database and output the
|
||||||
# Make sure this parses correctly at least.
|
# Make sure this parses correctly at least.
|
||||||
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-read-events.bro
|
@TEST-EXEC: bro ${DOC_ROOT}/frameworks/sqlite-read-events.bro
|
||||||
|
|
||||||
If you run this script against the trace in ``testing/btest/Traces/ftp/ipv4.trace``, you
|
If you run this script against the trace in
|
||||||
will get one hit.
|
``testing/btest/Traces/ftp/ipv4.trace``, you will get one hit.
|
||||||
|
|
|
@ -537,6 +537,5 @@ Additional writers are available as external plugins:
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
../components/bro-plugins/dataseries/README
|
../components/bro-plugins/README
|
||||||
../components/bro-plugins/elasticsearch/README
|
|
||||||
|
|
||||||
|
|
|
@ -46,4 +46,4 @@ where Bro was originally installed). Review the files for differences
|
||||||
before copying and make adjustments as necessary (use the new version for
|
before copying and make adjustments as necessary (use the new version for
|
||||||
differences that aren't a result of a local change). Of particular note,
|
differences that aren't a result of a local change). Of particular note,
|
||||||
the copied version of ``$prefix/etc/broctl.cfg`` is likely to need changes
|
the copied version of ``$prefix/etc/broctl.cfg`` is likely to need changes
|
||||||
to the ``SpoolDir`` and ``LogDir`` settings.
|
to any settings that specify a pathname.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
.. _MacPorts: http://www.macports.org
|
.. _MacPorts: http://www.macports.org
|
||||||
.. _Fink: http://www.finkproject.org
|
.. _Fink: http://www.finkproject.org
|
||||||
.. _Homebrew: http://brew.sh
|
.. _Homebrew: http://brew.sh
|
||||||
.. _bro downloads page: http://bro.org/download/index.html
|
.. _bro downloads page: https://www.bro.org/download/index.html
|
||||||
|
|
||||||
.. _installing-bro:
|
.. _installing-bro:
|
||||||
|
|
||||||
|
@ -32,24 +32,22 @@ before you begin:
|
||||||
* Libz
|
* Libz
|
||||||
* Bash (for BroControl)
|
* Bash (for BroControl)
|
||||||
* Python (for BroControl)
|
* Python (for BroControl)
|
||||||
* C++ Actor Framework (CAF) (http://actor-framework.org)
|
* C++ Actor Framework (CAF) version 0.14 (http://actor-framework.org)
|
||||||
|
|
||||||
To build Bro from source, the following additional dependencies are required:
|
To build Bro from source, the following additional dependencies are required:
|
||||||
|
|
||||||
* CMake 2.8 or greater (http://www.cmake.org)
|
* CMake 2.8 or greater (http://www.cmake.org)
|
||||||
* Make
|
* Make
|
||||||
* C/C++ compiler with C++11 support
|
* C/C++ compiler with C++11 support (GCC 4.8+ or Clang 3.3+)
|
||||||
* SWIG (http://www.swig.org)
|
* SWIG (http://www.swig.org)
|
||||||
* Bison (GNU Parser Generator)
|
* Bison (GNU Parser Generator)
|
||||||
* Flex (Fast Lexical Analyzer)
|
* Flex (Fast Lexical Analyzer)
|
||||||
* Libpcap headers (http://www.tcpdump.org)
|
* Libpcap headers (http://www.tcpdump.org)
|
||||||
* OpenSSL headers (http://www.openssl.org)
|
* OpenSSL headers (http://www.openssl.org)
|
||||||
* zlib headers
|
* zlib headers
|
||||||
* Perl
|
* Python
|
||||||
|
|
||||||
.. todo::
|
To install CAF, first download the source code of the required version from: https://github.com/actor-framework/actor-framework/releases
|
||||||
|
|
||||||
Update with instructions for installing CAF.
|
|
||||||
|
|
||||||
To install the required dependencies, you can use:
|
To install the required dependencies, you can use:
|
||||||
|
|
||||||
|
@ -72,11 +70,26 @@ To install the required dependencies, you can use:
|
||||||
|
|
||||||
.. console::
|
.. console::
|
||||||
|
|
||||||
sudo pkg install bash cmake swig bison python perl5 py27-sqlite3
|
sudo pkg install bash cmake swig bison python py27-sqlite3
|
||||||
|
|
||||||
Note that in older versions of FreeBSD, you might have to use the
|
Note that in older versions of FreeBSD, you might have to use the
|
||||||
"pkg_add -r" command instead of "pkg install".
|
"pkg_add -r" command instead of "pkg install".
|
||||||
|
|
||||||
|
For older versions of FreeBSD (especially FreeBSD 9.x), the system compiler
|
||||||
|
is not new enough to compile Bro. For these systems, you will have to install
|
||||||
|
a newer compiler using pkg; the ``clang34`` package should work.
|
||||||
|
|
||||||
|
You will also have to define several environment variables on these older
|
||||||
|
systems to use the new compiler and headers similar to this before calling
|
||||||
|
configure:
|
||||||
|
|
||||||
|
.. console::
|
||||||
|
|
||||||
|
export CC=clang34
|
||||||
|
export CXX=clang++34
|
||||||
|
export CXXFLAGS="-stdlib=libc++ -I${LOCALBASE}/include/c++/v1 -L${LOCALBASE}/lib"
|
||||||
|
export LDFLAGS="-pthread"
|
||||||
|
|
||||||
* Mac OS X:
|
* Mac OS X:
|
||||||
|
|
||||||
Compiling source code on Macs requires first installing Xcode_ (in older
|
Compiling source code on Macs requires first installing Xcode_ (in older
|
||||||
|
@ -84,11 +97,14 @@ To install the required dependencies, you can use:
|
||||||
"Preferences..." -> "Downloads" menus to install the "Command Line Tools"
|
"Preferences..." -> "Downloads" menus to install the "Command Line Tools"
|
||||||
component).
|
component).
|
||||||
|
|
||||||
OS X comes with all required dependencies except for CMake_ and SWIG_.
|
OS X comes with all required dependencies except for CMake_, SWIG_,
|
||||||
Distributions of these dependencies can likely be obtained from your
|
OpenSSL, and CAF. (OpenSSL used to be part of OS X versions 10.10
|
||||||
preferred Mac OS X package management system (e.g. MacPorts_, Fink_,
|
and older, for which it does not need to be installed manually. It
|
||||||
or Homebrew_). Specifically for MacPorts, the ``cmake``, ``swig``,
|
was removed in OS X 10.11). Distributions of these dependencies can
|
||||||
and ``swig-python`` packages provide the required dependencies.
|
likely be obtained from your preferred Mac OS X package management
|
||||||
|
system (e.g. Homebrew_, MacPorts_, or Fink_). Specifically for
|
||||||
|
Homebrew, the ``cmake``, ``swig``, ``openssl`` and ``caf`` packages
|
||||||
|
provide the required dependencies.
|
||||||
|
|
||||||
|
|
||||||
Optional Dependencies
|
Optional Dependencies
|
||||||
|
@ -101,6 +117,8 @@ build time:
|
||||||
* sendmail (enables Bro and BroControl to send mail)
|
* sendmail (enables Bro and BroControl to send mail)
|
||||||
* curl (used by a Bro script that implements active HTTP)
|
* curl (used by a Bro script that implements active HTTP)
|
||||||
* gperftools (tcmalloc is used to improve memory and CPU usage)
|
* gperftools (tcmalloc is used to improve memory and CPU usage)
|
||||||
|
* jemalloc (http://www.canonware.com/jemalloc/)
|
||||||
|
* PF_RING (Linux only, see :doc:`Cluster Configuration <../configuration/index>`)
|
||||||
* ipsumdump (for trace-summary; http://www.cs.ucla.edu/~kohler/ipsumdump)
|
* ipsumdump (for trace-summary; http://www.cs.ucla.edu/~kohler/ipsumdump)
|
||||||
|
|
||||||
LibGeoIP is probably the most interesting and can be installed
|
LibGeoIP is probably the most interesting and can be installed
|
||||||
|
@ -117,7 +135,7 @@ code forms.
|
||||||
|
|
||||||
|
|
||||||
Using Pre-Built Binary Release Packages
|
Using Pre-Built Binary Release Packages
|
||||||
=======================================
|
---------------------------------------
|
||||||
|
|
||||||
See the `bro downloads page`_ for currently supported/targeted
|
See the `bro downloads page`_ for currently supported/targeted
|
||||||
platforms for binary releases and for installation instructions.
|
platforms for binary releases and for installation instructions.
|
||||||
|
@ -126,25 +144,21 @@ platforms for binary releases and for installation instructions.
|
||||||
|
|
||||||
Linux based binary installations are usually performed by adding
|
Linux based binary installations are usually performed by adding
|
||||||
information about the Bro packages to the respective system packaging
|
information about the Bro packages to the respective system packaging
|
||||||
tool. Then the usual system utilities such as ``apt``, ``yum``
|
tool. Then the usual system utilities such as ``apt``, ``dnf``, ``yum``,
|
||||||
or ``zypper`` are used to perform the installation. By default,
|
or ``zypper`` are used to perform the installation.
|
||||||
installations of binary packages will go into ``/opt/bro``.
|
|
||||||
|
|
||||||
* MacOS Disk Image with Installer
|
|
||||||
|
|
||||||
Just open the ``Bro-*.dmg`` and then run the ``.pkg`` installer.
|
|
||||||
Everything installed by the package will go into ``/opt/bro``.
|
|
||||||
|
|
||||||
The primary install prefix for binary packages is ``/opt/bro``.
|
The primary install prefix for binary packages is ``/opt/bro``.
|
||||||
|
|
||||||
Installing from Source
|
Installing from Source
|
||||||
======================
|
----------------------
|
||||||
|
|
||||||
Bro releases are bundled into source packages for convenience and are
|
Bro releases are bundled into source packages for convenience and are
|
||||||
available on the `bro downloads page`_. Alternatively, the latest
|
available on the `bro downloads page`_.
|
||||||
Bro development version can be obtained through git repositories
|
|
||||||
|
Alternatively, the latest Bro development version
|
||||||
|
can be obtained through git repositories
|
||||||
hosted at ``git.bro.org``. See our `git development documentation
|
hosted at ``git.bro.org``. See our `git development documentation
|
||||||
<http://bro.org/development/howtos/process.html>`_ for comprehensive
|
<https://www.bro.org/development/howtos/process.html>`_ for comprehensive
|
||||||
information on Bro's use of git revision control, but the short story
|
information on Bro's use of git revision control, but the short story
|
||||||
for downloading the full source code experience for Bro via git is:
|
for downloading the full source code experience for Bro via git is:
|
||||||
|
|
||||||
|
@ -165,13 +179,23 @@ run ``./configure --help``):
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
If the ``configure`` script fails, then it is most likely because it either
|
||||||
|
couldn't find a required dependency or it couldn't find a sufficiently new
|
||||||
|
version of a dependency. Assuming that you already installed all required
|
||||||
|
dependencies, then you may need to use one of the ``--with-*`` options
|
||||||
|
that can be given to the ``configure`` script to help it locate a dependency.
|
||||||
|
|
||||||
The default installation path is ``/usr/local/bro``, which would typically
|
The default installation path is ``/usr/local/bro``, which would typically
|
||||||
require root privileges when doing the ``make install``. A different
|
require root privileges when doing the ``make install``. A different
|
||||||
installation path can be chosen by specifying the ``--prefix`` option.
|
installation path can be chosen by specifying the ``configure`` script
|
||||||
Note that ``/usr`` and ``/opt/bro`` are the
|
``--prefix`` option. Note that ``/usr`` and ``/opt/bro`` are the
|
||||||
standard prefixes for binary Bro packages to be installed, so those are
|
standard prefixes for binary Bro packages to be installed, so those are
|
||||||
typically not good choices unless you are creating such a package.
|
typically not good choices unless you are creating such a package.
|
||||||
|
|
||||||
|
OpenBSD users, please see our `FAQ
|
||||||
|
<https://www.bro.org/documentation/faq.html>`_ if you are having
|
||||||
|
problems installing Bro.
|
||||||
|
|
||||||
Depending on the Bro package you downloaded, there may be auxiliary
|
Depending on the Bro package you downloaded, there may be auxiliary
|
||||||
tools and libraries available in the ``aux/`` directory. Some of them
|
tools and libraries available in the ``aux/`` directory. Some of them
|
||||||
will be automatically built and installed along with Bro. There are
|
will be automatically built and installed along with Bro. There are
|
||||||
|
@ -180,10 +204,6 @@ turn off unwanted auxiliary projects that would otherwise be installed
|
||||||
automatically. Finally, use ``make install-aux`` to install some of
|
automatically. Finally, use ``make install-aux`` to install some of
|
||||||
the other programs that are in the ``aux/bro-aux`` directory.
|
the other programs that are in the ``aux/bro-aux`` directory.
|
||||||
|
|
||||||
OpenBSD users, please see our `FAQ
|
|
||||||
<//www.bro.org/documentation/faq.html>`_ if you are having
|
|
||||||
problems installing Bro.
|
|
||||||
|
|
||||||
Finally, if you want to build the Bro documentation (not required, because
|
Finally, if you want to build the Bro documentation (not required, because
|
||||||
all of the documentation for the latest Bro release is available on the
|
all of the documentation for the latest Bro release is available on the
|
||||||
Bro web site), there are instructions in ``doc/README`` in the source
|
Bro web site), there are instructions in ``doc/README`` in the source
|
||||||
|
@ -192,7 +212,7 @@ distribution.
|
||||||
Configure the Run-Time Environment
|
Configure the Run-Time Environment
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Just remember that you may need to adjust your ``PATH`` environment variable
|
You may want to adjust your ``PATH`` environment variable
|
||||||
according to the platform/shell/package you're using. For example:
|
according to the platform/shell/package you're using. For example:
|
||||||
|
|
||||||
Bourne-Shell Syntax:
|
Bourne-Shell Syntax:
|
||||||
|
|
|
@ -54,13 +54,16 @@ Here is a more detailed explanation of each attribute:
|
||||||
|
|
||||||
.. bro:attr:: &redef
|
.. bro:attr:: &redef
|
||||||
|
|
||||||
Allows for redefinition of initial values of global objects declared as
|
Allows use of a :bro:keyword:`redef` to redefine initial values of
|
||||||
constant.
|
global variables (i.e., variables declared either :bro:keyword:`global`
|
||||||
|
or :bro:keyword:`const`). Example::
|
||||||
In this example, the constant (assuming it is global) can be redefined
|
|
||||||
with a :bro:keyword:`redef` at some later point::
|
|
||||||
|
|
||||||
const clever = T &redef;
|
const clever = T &redef;
|
||||||
|
global cache_size = 256 &redef;
|
||||||
|
|
||||||
|
Note that a variable declared "global" can also have its value changed
|
||||||
|
with assignment statements (doesn't matter if it has the "&redef"
|
||||||
|
attribute or not).
|
||||||
|
|
||||||
.. bro:attr:: &priority
|
.. bro:attr:: &priority
|
||||||
|
|
||||||
|
|
|
@ -71,9 +71,11 @@ Statements
|
||||||
Declarations
|
Declarations
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The following global declarations cannot occur within a function, hook, or
|
Declarations cannot occur within a function, hook, or event handler.
|
||||||
event handler. Also, these declarations cannot appear after any statements
|
|
||||||
that are outside of a function, hook, or event handler.
|
Declarations must appear before any statements (except those statements
|
||||||
|
that are in a function, hook, or event handler) in the concatenation of
|
||||||
|
all loaded Bro scripts.
|
||||||
|
|
||||||
.. bro:keyword:: module
|
.. bro:keyword:: module
|
||||||
|
|
||||||
|
@ -126,9 +128,12 @@ that are outside of a function, hook, or event handler.
|
||||||
.. bro:keyword:: global
|
.. bro:keyword:: global
|
||||||
|
|
||||||
Variables declared with the "global" keyword will be global.
|
Variables declared with the "global" keyword will be global.
|
||||||
|
|
||||||
If a type is not specified, then an initializer is required so that
|
If a type is not specified, then an initializer is required so that
|
||||||
the type can be inferred. Likewise, if an initializer is not supplied,
|
the type can be inferred. Likewise, if an initializer is not supplied,
|
||||||
then the type must be specified. Example::
|
then the type must be specified. In some cases, when the type cannot
|
||||||
|
be correctly inferred, the type must be specified even when an
|
||||||
|
initializer is present. Example::
|
||||||
|
|
||||||
global pi = 3.14;
|
global pi = 3.14;
|
||||||
global hosts: set[addr];
|
global hosts: set[addr];
|
||||||
|
@ -136,10 +141,11 @@ that are outside of a function, hook, or event handler.
|
||||||
|
|
||||||
Variable declarations outside of any function, hook, or event handler are
|
Variable declarations outside of any function, hook, or event handler are
|
||||||
required to use this keyword (unless they are declared with the
|
required to use this keyword (unless they are declared with the
|
||||||
:bro:keyword:`const` keyword). Definitions of functions, hooks, and
|
:bro:keyword:`const` keyword instead).
|
||||||
event handlers are not allowed to use the "global"
|
|
||||||
keyword (they already have global scope), except function declarations
|
Definitions of functions, hooks, and event handlers are not allowed
|
||||||
where no function body is supplied use the "global" keyword.
|
to use the "global" keyword. However, function declarations (i.e., no
|
||||||
|
function body is provided) can use the "global" keyword.
|
||||||
|
|
||||||
The scope of a global variable begins where the declaration is located,
|
The scope of a global variable begins where the declaration is located,
|
||||||
and extends through all remaining Bro scripts that are loaded (however,
|
and extends through all remaining Bro scripts that are loaded (however,
|
||||||
|
@ -150,18 +156,22 @@ that are outside of a function, hook, or event handler.
|
||||||
.. bro:keyword:: const
|
.. bro:keyword:: const
|
||||||
|
|
||||||
A variable declared with the "const" keyword will be constant.
|
A variable declared with the "const" keyword will be constant.
|
||||||
|
|
||||||
Variables declared as constant are required to be initialized at the
|
Variables declared as constant are required to be initialized at the
|
||||||
time of declaration. Example::
|
time of declaration. Normally, the type is inferred from the initializer,
|
||||||
|
but the type can be explicitly specified. Example::
|
||||||
|
|
||||||
const pi = 3.14;
|
const pi = 3.14;
|
||||||
const ssh_port: port = 22/tcp;
|
const ssh_port: port = 22/tcp;
|
||||||
|
|
||||||
The value of a constant cannot be changed later (the only
|
The value of a constant cannot be changed. The only exception is if the
|
||||||
exception is if the variable is global and has the :bro:attr:`&redef`
|
variable is a global constant and has the :bro:attr:`&redef`
|
||||||
attribute, then its value can be changed only with a :bro:keyword:`redef`).
|
attribute, but even then its value can be changed only with a
|
||||||
|
:bro:keyword:`redef`.
|
||||||
|
|
||||||
The scope of a constant is local if the declaration is in a
|
The scope of a constant is local if the declaration is in a
|
||||||
function, hook, or event handler, and global otherwise.
|
function, hook, or event handler, and global otherwise.
|
||||||
|
|
||||||
Note that the "const" keyword cannot be used with either the "local"
|
Note that the "const" keyword cannot be used with either the "local"
|
||||||
or "global" keywords (i.e., "const" replaces "local" and "global").
|
or "global" keywords (i.e., "const" replaces "local" and "global").
|
||||||
|
|
||||||
|
@ -184,7 +194,8 @@ that are outside of a function, hook, or event handler.
|
||||||
.. bro:keyword:: redef
|
.. bro:keyword:: redef
|
||||||
|
|
||||||
There are three ways that "redef" can be used: to change the value of
|
There are three ways that "redef" can be used: to change the value of
|
||||||
a global variable, to extend a record type or enum type, or to specify
|
a global variable (but only if it has the :bro:attr:`&redef` attribute),
|
||||||
|
to extend a record type or enum type, or to specify
|
||||||
a new event handler body that replaces all those that were previously
|
a new event handler body that replaces all those that were previously
|
||||||
defined.
|
defined.
|
||||||
|
|
||||||
|
@ -237,13 +248,14 @@ that are outside of a function, hook, or event handler.
|
||||||
Statements
|
Statements
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
Statements (except those contained within a function, hook, or event
|
||||||
|
handler) can appear only after all global declarations in the concatenation
|
||||||
|
of all loaded Bro scripts.
|
||||||
|
|
||||||
Each statement in a Bro script must be terminated with a semicolon (with a
|
Each statement in a Bro script must be terminated with a semicolon (with a
|
||||||
few exceptions noted below). An individual statement can span multiple
|
few exceptions noted below). An individual statement can span multiple
|
||||||
lines.
|
lines.
|
||||||
|
|
||||||
All statements (except those contained within a function, hook, or event
|
|
||||||
handler) must appear after all global declarations.
|
|
||||||
|
|
||||||
Here are the statements that the Bro scripting language supports.
|
Here are the statements that the Bro scripting language supports.
|
||||||
|
|
||||||
.. bro:keyword:: add
|
.. bro:keyword:: add
|
||||||
|
|
|
@ -340,15 +340,18 @@ Here is a more detailed description of each type:
|
||||||
|
|
||||||
table [ type^+ ] of type
|
table [ type^+ ] of type
|
||||||
|
|
||||||
where *type^+* is one or more types, separated by commas.
|
where *type^+* is one or more types, separated by commas. The
|
||||||
For example:
|
index type cannot be any of the following types: pattern, table, set,
|
||||||
|
vector, file, opaque, any.
|
||||||
|
|
||||||
|
Here is an example of declaring a table indexed by "count" values
|
||||||
|
and yielding "string" values:
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
global a: table[count] of string;
|
global a: table[count] of string;
|
||||||
|
|
||||||
declares a table indexed by "count" values and yielding
|
The yield type can also be more complex:
|
||||||
"string" values. The yield type can also be more complex:
|
|
||||||
|
|
||||||
.. code:: bro
|
.. code:: bro
|
||||||
|
|
||||||
|
@ -441,7 +444,9 @@ Here is a more detailed description of each type:
|
||||||
|
|
||||||
set [ type^+ ]
|
set [ type^+ ]
|
||||||
|
|
||||||
where *type^+* is one or more types separated by commas.
|
where *type^+* is one or more types separated by commas. The
|
||||||
|
index type cannot be any of the following types: pattern, table, set,
|
||||||
|
vector, file, opaque, any.
|
||||||
|
|
||||||
Sets can be initialized by listing elements enclosed by curly braces:
|
Sets can be initialized by listing elements enclosed by curly braces:
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ type Service: record {
|
||||||
rfc: count;
|
rfc: count;
|
||||||
};
|
};
|
||||||
|
|
||||||
function print_service(serv: Service): string
|
function print_service(serv: Service)
|
||||||
{
|
{
|
||||||
print fmt("Service: %s(RFC%d)",serv$name, serv$rfc);
|
print fmt("Service: %s(RFC%d)",serv$name, serv$rfc);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ type System: record {
|
||||||
services: set[Service];
|
services: set[Service];
|
||||||
};
|
};
|
||||||
|
|
||||||
function print_service(serv: Service): string
|
function print_service(serv: Service)
|
||||||
{
|
{
|
||||||
print fmt(" Service: %s(RFC%d)",serv$name, serv$rfc);
|
print fmt(" Service: %s(RFC%d)",serv$name, serv$rfc);
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ function print_service(serv: Service): string
|
||||||
print fmt(" port: %s", p);
|
print fmt(" port: %s", p);
|
||||||
}
|
}
|
||||||
|
|
||||||
function print_system(sys: System): string
|
function print_system(sys: System)
|
||||||
{
|
{
|
||||||
print fmt("System: %s", sys$name);
|
print fmt("System: %s", sys$name);
|
||||||
|
|
||||||
|
|
|
@ -776,7 +776,7 @@ string against which it will be tested to be on the right.
|
||||||
In the sample above, two local variables are declared to hold our
|
In the sample above, two local variables are declared to hold our
|
||||||
sample sentence and regular expression. Our regular expression in
|
sample sentence and regular expression. Our regular expression in
|
||||||
this case will return true if the string contains either the word
|
this case will return true if the string contains either the word
|
||||||
``quick`` or the word ``fox``. The ``if`` statement in the script uses
|
``quick`` or the word ``lazy``. The ``if`` statement in the script uses
|
||||||
embedded matching and the ``in`` operator to check for the existence
|
embedded matching and the ``in`` operator to check for the existence
|
||||||
of the pattern within the string. If the statement resolves to true,
|
of the pattern within the string. If the statement resolves to true,
|
||||||
:bro:id:`split` is called to break the string into separate pieces.
|
:bro:id:`split` is called to break the string into separate pieces.
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
@load ./main
|
@load ./main
|
||||||
|
@load ./store
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
##! Various data structure definitions for use with Bro's communication system.
|
##! Various data structure definitions for use with Bro's communication system.
|
||||||
|
|
||||||
module BrokerComm;
|
module Broker;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
||||||
## A name used to identify this endpoint to peers.
|
## A name used to identify this endpoint to peers.
|
||||||
## .. bro:see:: BrokerComm::connect BrokerComm::listen
|
## .. bro:see:: Broker::connect Broker::listen
|
||||||
const endpoint_name = "" &redef;
|
const endpoint_name = "" &redef;
|
||||||
|
|
||||||
## Change communication behavior.
|
## Change communication behavior.
|
||||||
|
@ -32,11 +32,11 @@ export {
|
||||||
|
|
||||||
## Opaque communication data.
|
## Opaque communication data.
|
||||||
type Data: record {
|
type Data: record {
|
||||||
d: opaque of BrokerComm::Data &optional;
|
d: opaque of Broker::Data &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
## Opaque communication data.
|
## Opaque communication data.
|
||||||
type DataVector: vector of BrokerComm::Data;
|
type DataVector: vector of Broker::Data;
|
||||||
|
|
||||||
## Opaque event communication data.
|
## Opaque event communication data.
|
||||||
type EventArgs: record {
|
type EventArgs: record {
|
||||||
|
@ -49,55 +49,7 @@ export {
|
||||||
## Opaque communication data used as a convenient way to wrap key-value
|
## Opaque communication data used as a convenient way to wrap key-value
|
||||||
## pairs that comprise table entries.
|
## pairs that comprise table entries.
|
||||||
type TableItem : record {
|
type TableItem : record {
|
||||||
key: BrokerComm::Data;
|
key: Broker::Data;
|
||||||
val: BrokerComm::Data;
|
val: Broker::Data;
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module BrokerStore;
|
|
||||||
|
|
||||||
export {
|
|
||||||
|
|
||||||
## Whether a data store query could be completed or not.
|
|
||||||
type QueryStatus: enum {
|
|
||||||
SUCCESS,
|
|
||||||
FAILURE,
|
|
||||||
};
|
|
||||||
|
|
||||||
## An expiry time for a key-value pair inserted in to a data store.
|
|
||||||
type ExpiryTime: record {
|
|
||||||
## Absolute point in time at which to expire the entry.
|
|
||||||
absolute: time &optional;
|
|
||||||
## A point in time relative to the last modification time at which
|
|
||||||
## to expire the entry. New modifications will delay the expiration.
|
|
||||||
since_last_modification: interval &optional;
|
|
||||||
};
|
|
||||||
|
|
||||||
## The result of a data store query.
|
|
||||||
type QueryResult: record {
|
|
||||||
## Whether the query completed or not.
|
|
||||||
status: BrokerStore::QueryStatus;
|
|
||||||
## The result of the query. Certain queries may use a particular
|
|
||||||
## data type (e.g. querying store size always returns a count, but
|
|
||||||
## a lookup may return various data types).
|
|
||||||
result: BrokerComm::Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
## Options to tune the SQLite storage backend.
|
|
||||||
type SQLiteOptions: record {
|
|
||||||
## File system path of the database.
|
|
||||||
path: string &default = "store.sqlite";
|
|
||||||
};
|
|
||||||
|
|
||||||
## Options to tune the RocksDB storage backend.
|
|
||||||
type RocksDBOptions: record {
|
|
||||||
## File system path of the database.
|
|
||||||
path: string &default = "store.rocksdb";
|
|
||||||
};
|
|
||||||
|
|
||||||
## Options to tune the particular storage backends.
|
|
||||||
type BackendOptions: record {
|
|
||||||
sqlite: SQLiteOptions &default = SQLiteOptions();
|
|
||||||
rocksdb: RocksDBOptions &default = RocksDBOptions();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
51
scripts/base/frameworks/broker/store.bro
Normal file
51
scripts/base/frameworks/broker/store.bro
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
##! Various data structure definitions for use with Bro's communication system.
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
|
||||||
|
module Broker;
|
||||||
|
|
||||||
|
export {
|
||||||
|
|
||||||
|
## Whether a data store query could be completed or not.
|
||||||
|
type QueryStatus: enum {
|
||||||
|
SUCCESS,
|
||||||
|
FAILURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
## An expiry time for a key-value pair inserted in to a data store.
|
||||||
|
type ExpiryTime: record {
|
||||||
|
## Absolute point in time at which to expire the entry.
|
||||||
|
absolute: time &optional;
|
||||||
|
## A point in time relative to the last modification time at which
|
||||||
|
## to expire the entry. New modifications will delay the expiration.
|
||||||
|
since_last_modification: interval &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## The result of a data store query.
|
||||||
|
type QueryResult: record {
|
||||||
|
## Whether the query completed or not.
|
||||||
|
status: Broker::QueryStatus;
|
||||||
|
## The result of the query. Certain queries may use a particular
|
||||||
|
## data type (e.g. querying store size always returns a count, but
|
||||||
|
## a lookup may return various data types).
|
||||||
|
result: Broker::Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Options to tune the SQLite storage backend.
|
||||||
|
type SQLiteOptions: record {
|
||||||
|
## File system path of the database.
|
||||||
|
path: string &default = "store.sqlite";
|
||||||
|
};
|
||||||
|
|
||||||
|
## Options to tune the RocksDB storage backend.
|
||||||
|
type RocksDBOptions: record {
|
||||||
|
## File system path of the database.
|
||||||
|
path: string &default = "store.rocksdb";
|
||||||
|
};
|
||||||
|
|
||||||
|
## Options to tune the particular storage backends.
|
||||||
|
type BackendOptions: record {
|
||||||
|
sqlite: SQLiteOptions &default = SQLiteOptions();
|
||||||
|
rocksdb: RocksDBOptions &default = RocksDBOptions();
|
||||||
|
};
|
||||||
|
}
|
|
@ -126,6 +126,9 @@ export {
|
||||||
## This is usually supplied on the command line for each instance
|
## This is usually supplied on the command line for each instance
|
||||||
## of the cluster that is started up.
|
## of the cluster that is started up.
|
||||||
const node = getenv("CLUSTER_NODE") &redef;
|
const node = getenv("CLUSTER_NODE") &redef;
|
||||||
|
|
||||||
|
## Interval for retrying failed connections between cluster nodes.
|
||||||
|
const retry_interval = 1min &redef;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_enabled(): bool
|
function is_enabled(): bool
|
||||||
|
|
|
@ -39,7 +39,7 @@ event bro_init() &priority=9
|
||||||
Communication::nodes["time-machine"] = [$host=nodes[i]$ip,
|
Communication::nodes["time-machine"] = [$host=nodes[i]$ip,
|
||||||
$zone_id=nodes[i]$zone_id,
|
$zone_id=nodes[i]$zone_id,
|
||||||
$p=nodes[i]$p,
|
$p=nodes[i]$p,
|
||||||
$connect=T, $retry=1min,
|
$connect=T, $retry=retry_interval,
|
||||||
$events=tm2manager_events];
|
$events=tm2manager_events];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ event bro_init() &priority=9
|
||||||
if ( n?$proxy )
|
if ( n?$proxy )
|
||||||
Communication::nodes[i]
|
Communication::nodes[i]
|
||||||
= [$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
= [$host=n$ip, $zone_id=n$zone_id, $p=n$p,
|
||||||
$connect=T, $auth=F, $sync=T, $retry=1mins];
|
$connect=T, $auth=F, $sync=T, $retry=retry_interval];
|
||||||
else if ( me?$proxy && me$proxy == i )
|
else if ( me?$proxy && me$proxy == i )
|
||||||
Communication::nodes[me$proxy]
|
Communication::nodes[me$proxy]
|
||||||
= [$host=nodes[i]$ip, $zone_id=nodes[i]$zone_id,
|
= [$host=nodes[i]$ip, $zone_id=nodes[i]$zone_id,
|
||||||
|
@ -70,7 +70,7 @@ event bro_init() &priority=9
|
||||||
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
||||||
$zone_id=nodes[i]$zone_id,
|
$zone_id=nodes[i]$zone_id,
|
||||||
$p=nodes[i]$p,
|
$p=nodes[i]$p,
|
||||||
$connect=T, $retry=1mins,
|
$connect=T, $retry=retry_interval,
|
||||||
$class=node,
|
$class=node,
|
||||||
$events=manager2proxy_events];
|
$events=manager2proxy_events];
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ event bro_init() &priority=9
|
||||||
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
Communication::nodes["manager"] = [$host=nodes[i]$ip,
|
||||||
$zone_id=nodes[i]$zone_id,
|
$zone_id=nodes[i]$zone_id,
|
||||||
$p=nodes[i]$p,
|
$p=nodes[i]$p,
|
||||||
$connect=T, $retry=1mins,
|
$connect=T, $retry=retry_interval,
|
||||||
$class=node,
|
$class=node,
|
||||||
$events=manager2worker_events];
|
$events=manager2worker_events];
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ event bro_init() &priority=9
|
||||||
Communication::nodes["proxy"] = [$host=nodes[i]$ip,
|
Communication::nodes["proxy"] = [$host=nodes[i]$ip,
|
||||||
$zone_id=nodes[i]$zone_id,
|
$zone_id=nodes[i]$zone_id,
|
||||||
$p=nodes[i]$p,
|
$p=nodes[i]$p,
|
||||||
$connect=T, $retry=1mins,
|
$connect=T, $retry=retry_interval,
|
||||||
$sync=T, $class=node,
|
$sync=T, $class=node,
|
||||||
$events=proxy2worker_events];
|
$events=proxy2worker_events];
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ event bro_init() &priority=9
|
||||||
$zone_id=nodes[i]$zone_id,
|
$zone_id=nodes[i]$zone_id,
|
||||||
$p=nodes[i]$p,
|
$p=nodes[i]$p,
|
||||||
$connect=T,
|
$connect=T,
|
||||||
$retry=1min,
|
$retry=retry_interval,
|
||||||
$events=tm2worker_events];
|
$events=tm2worker_events];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# MPEG v3 audio
|
# MPEG v3 audio
|
||||||
signature file-mpeg-audio {
|
signature file-mpeg-audio {
|
||||||
file-mime "audio/mpeg", 20
|
file-mime "audio/mpeg", 20
|
||||||
file-magic /^\xff[\xe2\xe3\xf2\xf3\xf6\xf7\xfa\xfb\xfc\xfd]/
|
file-magic /^(ID3|\xff[\xe2\xe3\xf2\xf3\xf6\xf7\xfa\xfb\xfc\xfd])/
|
||||||
}
|
}
|
||||||
|
|
||||||
# MPEG v4 audio
|
# MPEG v4 audio
|
||||||
|
|
|
@ -9,53 +9,53 @@ signature file-plaintext {
|
||||||
|
|
||||||
signature file-json {
|
signature file-json {
|
||||||
file-mime "text/json", 1
|
file-mime "text/json", 1
|
||||||
file-magic /^(\xef\xbb\xbf)?[\x0d\x0a[:blank:]]*\{[\x0d\x0a[:blank:]]*(["][^"]{1,}["]|[a-zA-Z][a-zA-Z0-9\\_]*)[\x0d\x0a[:blank:]]*:[\x0d\x0a[:blank:]]*(["]|\[|\{|[0-9]|true|false)/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?[\x0d\x0a[:blank:]]*\{[\x0d\x0a[:blank:]]*(["][^"]{1,}["]|[a-zA-Z][a-zA-Z0-9\\_]*)[\x0d\x0a[:blank:]]*:[\x0d\x0a[:blank:]]*(["]|\[|\{|[0-9]|true|false)/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-json2 {
|
signature file-json2 {
|
||||||
file-mime "text/json", 1
|
file-mime "text/json", 1
|
||||||
file-magic /^(\xef\xbb\xbf)?[\x0d\x0a[:blank:]]*\[[\x0d\x0a[:blank:]]*(((["][^"]{1,}["]|[0-9]{1,}(\.[0-9]{1,})?|true|false)[\x0d\x0a[:blank:]]*,)|\{|\[)[\x0d\x0a[:blank:]]*/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?[\x0d\x0a[:blank:]]*\[[\x0d\x0a[:blank:]]*(((["][^"]{1,}["]|[0-9]{1,}(\.[0-9]{1,})?|true|false)[\x0d\x0a[:blank:]]*,)|\{|\[)[\x0d\x0a[:blank:]]*/
|
||||||
}
|
}
|
||||||
|
|
||||||
# Match empty JSON documents.
|
# Match empty JSON documents.
|
||||||
signature file-json3 {
|
signature file-json3 {
|
||||||
file-mime "text/json", 0
|
file-mime "text/json", 0
|
||||||
file-magic /^(\xef\xbb\xbf)?[\x0d\x0a[:blank:]]*(\[\]|\{\})[\x0d\x0a[:blank:]]*$/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?[\x0d\x0a[:blank:]]*(\[\]|\{\})[\x0d\x0a[:blank:]]*$/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-xml {
|
signature file-xml {
|
||||||
file-mime "application/xml", 10
|
file-mime "application/xml", 10
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<\?xml /
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*\x00?<\x00?\?\x00?x\x00?m\x00?l\x00? \x00?/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-xhtml {
|
signature file-xhtml {
|
||||||
file-mime "text/html", 100
|
file-mime "text/html", 100
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<(![dD][oO][cC][tT][yY][pP][eE] {1,}[hH][tT][mM][lL]|[hH][tT][mM][lL]|[mM][eE][tT][aA] {1,}[hH][tT][tT][pP]-[eE][qQ][uU][iI][vV])/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<(![dD][oO][cC][tT][yY][pP][eE] {1,}[hH][tT][mM][lL]|[hH][tT][mM][lL]|[mM][eE][tT][aA] {1,}[hH][tT][tT][pP]-[eE][qQ][uU][iI][vV])/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-html {
|
signature file-html {
|
||||||
file-mime "text/html", 49
|
file-mime "text/html", 49
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<![dD][oO][cC][tT][yY][pP][eE] {1,}[hH][tT][mM][lL]/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<![dD][oO][cC][tT][yY][pP][eE] {1,}[hH][tT][mM][lL]/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-html2 {
|
signature file-html2 {
|
||||||
file-mime "text/html", 20
|
file-mime "text/html", 20
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<([hH][eE][aA][dD]|[hH][tT][mM][lL]|[tT][iI][tT][lL][eE]|[bB][oO][dD][yY])/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<([hH][eE][aA][dD]|[hH][tT][mM][lL]|[tT][iI][tT][lL][eE]|[bB][oO][dD][yY])/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-rss {
|
signature file-rss {
|
||||||
file-mime "text/rss", 90
|
file-mime "text/rss", 90
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[rR][sS][sS]/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[rR][sS][sS]/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-atom {
|
signature file-atom {
|
||||||
file-mime "text/atom", 100
|
file-mime "text/atom", 100
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<([rR][sS][sS][^>]*xmlns:atom|[fF][eE][eE][dD][^>]*xmlns=["']?http:\/\/www.w3.org\/2005\/Atom["']?)/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<([rR][sS][sS][^>]*xmlns:atom|[fF][eE][eE][dD][^>]*xmlns=["']?http:\/\/www.w3.org\/2005\/Atom["']?)/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-soap {
|
signature file-soap {
|
||||||
file-mime "application/soap+xml", 49
|
file-mime "application/soap+xml", 49
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[sS][oO][aA][pP](-[eE][nN][vV])?:[eE][nN][vV][eE][lL][oO][pP][eE]/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[sS][oO][aA][pP](-[eE][nN][vV])?:[eE][nN][vV][eE][lL][oO][pP][eE]/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-cross-domain-policy {
|
signature file-cross-domain-policy {
|
||||||
|
@ -70,7 +70,7 @@ signature file-cross-domain-policy2 {
|
||||||
|
|
||||||
signature file-xmlrpc {
|
signature file-xmlrpc {
|
||||||
file-mime "application/xml-rpc", 49
|
file-mime "application/xml-rpc", 49
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[mM][eE][tT][hH][oO][dD][rR][eE][sS][pP][oO][nN][sS][eE]>/
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[mM][eE][tT][hH][oO][dD][rR][eE][sS][pP][oO][nN][sS][eE]>/
|
||||||
}
|
}
|
||||||
|
|
||||||
signature file-coldfusion {
|
signature file-coldfusion {
|
||||||
|
@ -81,7 +81,13 @@ signature file-coldfusion {
|
||||||
# Adobe Flash Media Manifest
|
# Adobe Flash Media Manifest
|
||||||
signature file-f4m {
|
signature file-f4m {
|
||||||
file-mime "application/f4m", 49
|
file-mime "application/f4m", 49
|
||||||
file-magic /^(\xef\xbb\xbf)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[mM][aA][nN][iI][fF][eE][sS][tT][\x0d\x0a[:blank:]]{1,}xmlns=\"http:\/\/ns\.adobe\.com\/f4m\//
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*(<\?xml .*\?>)?([\x0d\x0a[:blank:]]*(<!--.*-->)?[\x0d\x0a[:blank:]]*)*<[mM][aA][nN][iI][fF][eE][sS][tT][\x0d\x0a[:blank:]]{1,}xmlns=\"http:\/\/ns\.adobe\.com\/f4m\//
|
||||||
|
}
|
||||||
|
|
||||||
|
# .ini style files
|
||||||
|
signature file-ini {
|
||||||
|
file-mime "text/ini", 20
|
||||||
|
file-magic /^(\xef\xbb\xbf|\xff\xfe|\xfe\xff)?[\x00\x0d\x0a[:blank:]]*\[[^\x0d\x0a]+\][[:blank:]\x00]*[\x0d\x0a]/
|
||||||
}
|
}
|
||||||
|
|
||||||
# Microsoft LNK files
|
# Microsoft LNK files
|
||||||
|
@ -90,6 +96,41 @@ signature file-lnk {
|
||||||
file-magic /^\x4C\x00\x00\x00\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x10\x00\x00\x00\x46/
|
file-magic /^\x4C\x00\x00\x00\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x10\x00\x00\x00\x46/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Microsoft Registry policies
|
||||||
|
signature file-pol {
|
||||||
|
file-mime "application/vnd.ms-pol", 49
|
||||||
|
file-magic /^PReg/
|
||||||
|
}
|
||||||
|
|
||||||
|
# Old style Windows registry file
|
||||||
|
signature file-reg {
|
||||||
|
file-mime "application/vnd.ms-reg", 49
|
||||||
|
file-magic /^REGEDIT4/
|
||||||
|
}
|
||||||
|
|
||||||
|
# Newer Windows registry file
|
||||||
|
signature file-reg-utf16 {
|
||||||
|
file-mime "application/vnd.ms-reg", 49
|
||||||
|
file-magic /^\xFF\xFEW\x00i\x00n\x00d\x00o\x00w\x00s\x00 \x00R\x00e\x00g\x00i\x00s\x00t\x00r\x00y\x00 \x00E\x00d\x00i\x00t\x00o\x00r\x00 \x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x005\x00\.\x000\x000/
|
||||||
|
}
|
||||||
|
|
||||||
|
# Microsoft Registry format (typically DESKTOP.DAT)
|
||||||
|
signature file-regf {
|
||||||
|
file-mime "application vnd.ms-regf", 49
|
||||||
|
file-magic /^\x72\x65\x67\x66/
|
||||||
|
}
|
||||||
|
|
||||||
|
# Microsoft Outlook PST files
|
||||||
|
signature file-pst {
|
||||||
|
file-mime "application/vnd.ms-outlook", 49
|
||||||
|
file-magic /!BDN......[\x0e\x0f\x15\x17][\x00-\x02]/
|
||||||
|
}
|
||||||
|
|
||||||
|
signature file-afpinfo {
|
||||||
|
file-mime "application/vnd.apple-afpinfo"
|
||||||
|
file-magic /^AFP/
|
||||||
|
}
|
||||||
|
|
||||||
signature file-jar {
|
signature file-jar {
|
||||||
file-mime "application/java-archive", 100
|
file-mime "application/java-archive", 100
|
||||||
file-magic /^PK\x03\x04.{1,200}\x14\x00..META-INF\/MANIFEST\.MF/
|
file-magic /^PK\x03\x04.{1,200}\x14\x00..META-INF\/MANIFEST\.MF/
|
||||||
|
|
|
@ -71,6 +71,14 @@ signature file-mp2p {
|
||||||
file-magic /\x00\x00\x01\xba([\x40-\x7f\xc0-\xff])/
|
file-magic /\x00\x00\x01\xba([\x40-\x7f\xc0-\xff])/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# MPEG transport stream data. These files typically have the extension "ts".
|
||||||
|
# Note: The 0x47 repeats every 188 bytes. Using four as the number of
|
||||||
|
# occurrences for the test here is arbitrary.
|
||||||
|
signature file-mp2t {
|
||||||
|
file-mime "video/mp2t", 40
|
||||||
|
file-magic /^(\x47.{187}){4}/
|
||||||
|
}
|
||||||
|
|
||||||
# Silicon Graphics video
|
# Silicon Graphics video
|
||||||
signature file-sgi-movie {
|
signature file-sgi-movie {
|
||||||
file-mime "video/x-sgi-movie", 70
|
file-mime "video/x-sgi-movie", 70
|
||||||
|
@ -94,3 +102,4 @@ signature file-3gpp {
|
||||||
file-mime "video/3gpp", 60
|
file-mime "video/3gpp", 60
|
||||||
file-magic /^....ftyp(3g[egps2]|avc1|mmp4)/
|
file-magic /^....ftyp(3g[egps2]|avc1|mmp4)/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
##! The input framework provides a way to read previously stored data either
|
##! The input framework provides a way to read previously stored data either
|
||||||
##! as an event stream or into a bro table.
|
##! as an event stream or into a Bro table.
|
||||||
|
|
||||||
module Input;
|
module Input;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type Event: enum {
|
type Event: enum {
|
||||||
|
## New data has been imported.
|
||||||
EVENT_NEW = 0,
|
EVENT_NEW = 0,
|
||||||
|
## Existing data has been changed.
|
||||||
EVENT_CHANGED = 1,
|
EVENT_CHANGED = 1,
|
||||||
|
## Previously existing data has been removed.
|
||||||
EVENT_REMOVED = 2,
|
EVENT_REMOVED = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## Type that defines the input stream read mode.
|
||||||
type Mode: enum {
|
type Mode: enum {
|
||||||
|
## Do not automatically reread the file after it has been read.
|
||||||
MANUAL = 0,
|
MANUAL = 0,
|
||||||
|
## Reread the entire file each time a change is found.
|
||||||
REREAD = 1,
|
REREAD = 1,
|
||||||
|
## Read data from end of file each time new data is appended.
|
||||||
STREAM = 2
|
STREAM = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,20 +31,20 @@ export {
|
||||||
|
|
||||||
## Separator between fields.
|
## Separator between fields.
|
||||||
## Please note that the separator has to be exactly one character long.
|
## Please note that the separator has to be exactly one character long.
|
||||||
## Can be overwritten by individual writers.
|
## Individual readers can use a different value.
|
||||||
const separator = "\t" &redef;
|
const separator = "\t" &redef;
|
||||||
|
|
||||||
## Separator between set elements.
|
## Separator between set elements.
|
||||||
## Please note that the separator has to be exactly one character long.
|
## Please note that the separator has to be exactly one character long.
|
||||||
## Can be overwritten by individual writers.
|
## Individual readers can use a different value.
|
||||||
const set_separator = "," &redef;
|
const set_separator = "," &redef;
|
||||||
|
|
||||||
## String to use for empty fields.
|
## String to use for empty fields.
|
||||||
## Can be overwritten by individual writers.
|
## Individual readers can use a different value.
|
||||||
const empty_field = "(empty)" &redef;
|
const empty_field = "(empty)" &redef;
|
||||||
|
|
||||||
## String to use for an unset &optional field.
|
## String to use for an unset &optional field.
|
||||||
## Can be overwritten by individual writers.
|
## Individual readers can use a different value.
|
||||||
const unset_field = "-" &redef;
|
const unset_field = "-" &redef;
|
||||||
|
|
||||||
## Flag that controls if the input framework accepts records
|
## Flag that controls if the input framework accepts records
|
||||||
|
@ -47,11 +54,11 @@ export {
|
||||||
## abort. Defaults to false (abort).
|
## abort. Defaults to false (abort).
|
||||||
const accept_unsupported_types = F &redef;
|
const accept_unsupported_types = F &redef;
|
||||||
|
|
||||||
## TableFilter description type used for the `table` method.
|
## A table input stream type used to send data to a Bro table.
|
||||||
type TableDescription: record {
|
type TableDescription: record {
|
||||||
# Common definitions for tables and events
|
# Common definitions for tables and events
|
||||||
|
|
||||||
## String that allows the reader to find the source.
|
## String that allows the reader to find the source of the data.
|
||||||
## For `READER_ASCII`, this is the filename.
|
## For `READER_ASCII`, this is the filename.
|
||||||
source: string;
|
source: string;
|
||||||
|
|
||||||
|
@ -61,7 +68,8 @@ export {
|
||||||
## Read mode to use for this stream.
|
## Read mode to use for this stream.
|
||||||
mode: Mode &default=default_mode;
|
mode: Mode &default=default_mode;
|
||||||
|
|
||||||
## Descriptive name. Used to remove a stream at a later time.
|
## Name of the input stream. This is used by some functions to
|
||||||
|
## manipulate the stream.
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
# Special definitions for tables
|
# Special definitions for tables
|
||||||
|
@ -73,31 +81,35 @@ export {
|
||||||
idx: any;
|
idx: any;
|
||||||
|
|
||||||
## Record that defines the values used as the elements of the table.
|
## Record that defines the values used as the elements of the table.
|
||||||
## If this is undefined, then *destination* has to be a set.
|
## If this is undefined, then *destination* must be a set.
|
||||||
val: any &optional;
|
val: any &optional;
|
||||||
|
|
||||||
## Defines if the value of the table is a record (default), or a single value.
|
## Defines if the value of the table is a record (default), or a single
|
||||||
## When this is set to false, then *val* can only contain one element.
|
## value. When this is set to false, then *val* can only contain one
|
||||||
|
## element.
|
||||||
want_record: bool &default=T;
|
want_record: bool &default=T;
|
||||||
|
|
||||||
## The event that is raised each time a value is added to, changed in or removed
|
## The event that is raised each time a value is added to, changed in,
|
||||||
## from the table. The event will receive an Input::Event enum as the first
|
## or removed from the table. The event will receive an
|
||||||
## argument, the *idx* record as the second argument and the value (record) as the
|
## Input::TableDescription as the first argument, an Input::Event
|
||||||
## third argument.
|
## enum as the second argument, the *idx* record as the third argument
|
||||||
ev: any &optional; # event containing idx, val as values.
|
## and the value (record) as the fourth argument.
|
||||||
|
ev: any &optional;
|
||||||
|
|
||||||
## Predicate function that can decide if an insertion, update or removal should
|
## Predicate function that can decide if an insertion, update or removal
|
||||||
## really be executed. Parameters are the same as for the event. If true is
|
## should really be executed. Parameters have same meaning as for the
|
||||||
## returned, the update is performed. If false is returned, it is skipped.
|
## event.
|
||||||
|
## If true is returned, the update is performed. If false is returned,
|
||||||
|
## it is skipped.
|
||||||
pred: function(typ: Input::Event, left: any, right: any): bool &optional;
|
pred: function(typ: Input::Event, left: any, right: any): bool &optional;
|
||||||
|
|
||||||
## A key/value table that will be passed on the reader.
|
## A key/value table that will be passed to the reader.
|
||||||
## Interpretation of the values is left to the writer, but
|
## Interpretation of the values is left to the reader, but
|
||||||
## usually they will be used for configuration purposes.
|
## usually they will be used for configuration purposes.
|
||||||
config: table[string] of string &default=table();
|
config: table[string] of string &default=table();
|
||||||
};
|
};
|
||||||
|
|
||||||
## EventFilter description type used for the `event` method.
|
## An event input stream type used to send input data to a Bro event.
|
||||||
type EventDescription: record {
|
type EventDescription: record {
|
||||||
# Common definitions for tables and events
|
# Common definitions for tables and events
|
||||||
|
|
||||||
|
@ -116,19 +128,26 @@ export {
|
||||||
|
|
||||||
# Special definitions for events
|
# Special definitions for events
|
||||||
|
|
||||||
## Record describing the fields to be retrieved from the source input.
|
## Record type describing the fields to be retrieved from the input
|
||||||
|
## source.
|
||||||
fields: any;
|
fields: any;
|
||||||
|
|
||||||
## If this is false, the event receives each value in fields as a separate argument.
|
## If this is false, the event receives each value in *fields* as a
|
||||||
## If this is set to true (default), the event receives all fields in a single record value.
|
## separate argument.
|
||||||
|
## If this is set to true (default), the event receives all fields in
|
||||||
|
## a single record value.
|
||||||
want_record: bool &default=T;
|
want_record: bool &default=T;
|
||||||
|
|
||||||
## The event that is raised each time a new line is received from the reader.
|
## The event that is raised each time a new line is received from the
|
||||||
## The event will receive an Input::Event enum as the first element, and the fields as the following arguments.
|
## reader. The event will receive an Input::EventDescription record
|
||||||
|
## as the first argument, an Input::Event enum as the second
|
||||||
|
## argument, and the fields (as specified in *fields*) as the following
|
||||||
|
## arguments (this will either be a single record value containing
|
||||||
|
## all fields, or each field value as a separate argument).
|
||||||
ev: any;
|
ev: any;
|
||||||
|
|
||||||
## A key/value table that will be passed on the reader.
|
## A key/value table that will be passed to the reader.
|
||||||
## Interpretation of the values is left to the writer, but
|
## Interpretation of the values is left to the reader, but
|
||||||
## usually they will be used for configuration purposes.
|
## usually they will be used for configuration purposes.
|
||||||
config: table[string] of string &default=table();
|
config: table[string] of string &default=table();
|
||||||
};
|
};
|
||||||
|
@ -155,28 +174,29 @@ export {
|
||||||
## field will be the same value as the *source* field.
|
## field will be the same value as the *source* field.
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
## A key/value table that will be passed on the reader.
|
## A key/value table that will be passed to the reader.
|
||||||
## Interpretation of the values is left to the writer, but
|
## Interpretation of the values is left to the reader, but
|
||||||
## usually they will be used for configuration purposes.
|
## usually they will be used for configuration purposes.
|
||||||
config: table[string] of string &default=table();
|
config: table[string] of string &default=table();
|
||||||
};
|
};
|
||||||
|
|
||||||
## Create a new table input from a given source.
|
## Create a new table input stream from a given source.
|
||||||
##
|
##
|
||||||
## description: `TableDescription` record describing the source.
|
## description: `TableDescription` record describing the source.
|
||||||
##
|
##
|
||||||
## Returns: true on success.
|
## Returns: true on success.
|
||||||
global add_table: function(description: Input::TableDescription) : bool;
|
global add_table: function(description: Input::TableDescription) : bool;
|
||||||
|
|
||||||
## Create a new event input from a given source.
|
## Create a new event input stream from a given source.
|
||||||
##
|
##
|
||||||
## description: `EventDescription` record describing the source.
|
## description: `EventDescription` record describing the source.
|
||||||
##
|
##
|
||||||
## Returns: true on success.
|
## Returns: true on success.
|
||||||
global add_event: function(description: Input::EventDescription) : bool;
|
global add_event: function(description: Input::EventDescription) : bool;
|
||||||
|
|
||||||
## Create a new file analysis input from a given source. Data read from
|
## Create a new file analysis input stream from a given source. Data read
|
||||||
## the source is automatically forwarded to the file analysis framework.
|
## from the source is automatically forwarded to the file analysis
|
||||||
|
## framework.
|
||||||
##
|
##
|
||||||
## description: A record describing the source.
|
## description: A record describing the source.
|
||||||
##
|
##
|
||||||
|
@ -199,7 +219,11 @@ export {
|
||||||
|
|
||||||
## Event that is called when the end of a data source has been reached,
|
## Event that is called when the end of a data source has been reached,
|
||||||
## including after an update.
|
## including after an update.
|
||||||
global end_of_data: event(name: string, source:string);
|
##
|
||||||
|
## name: Name of the input stream.
|
||||||
|
##
|
||||||
|
## source: String that identifies the data source (such as the filename).
|
||||||
|
global end_of_data: event(name: string, source: string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@load base/bif/input.bif
|
@load base/bif/input.bif
|
||||||
|
|
|
@ -11,7 +11,9 @@ export {
|
||||||
##
|
##
|
||||||
## name: name of the input stream.
|
## name: name of the input stream.
|
||||||
## source: source of the input stream.
|
## source: source of the input stream.
|
||||||
## exit_code: exit code of the program, or number of the signal that forced the program to exit.
|
## exit_code: exit code of the program, or number of the signal that forced
|
||||||
## signal_exit: false when program exited normally, true when program was forced to exit by a signal.
|
## the program to exit.
|
||||||
|
## signal_exit: false when program exited normally, true when program was
|
||||||
|
## forced to exit by a signal.
|
||||||
global process_finished: event(name: string, source:string, exit_code:count, signal_exit:bool);
|
global process_finished: event(name: string, source:string, exit_code:count, signal_exit:bool);
|
||||||
}
|
}
|
||||||
|
|
15
scripts/base/frameworks/netcontrol/__load__.bro
Normal file
15
scripts/base/frameworks/netcontrol/__load__.bro
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@load ./types
|
||||||
|
@load ./main
|
||||||
|
@load ./plugins
|
||||||
|
@load ./drop
|
||||||
|
@load ./shunt
|
||||||
|
@load ./catch-and-release
|
||||||
|
|
||||||
|
# The cluster framework must be loaded first.
|
||||||
|
@load base/frameworks/cluster
|
||||||
|
|
||||||
|
@if ( Cluster::is_enabled() )
|
||||||
|
@load ./cluster
|
||||||
|
@else
|
||||||
|
@load ./non-cluster
|
||||||
|
@endif
|
104
scripts/base/frameworks/netcontrol/catch-and-release.bro
Normal file
104
scripts/base/frameworks/netcontrol/catch-and-release.bro
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
##! Implementation of catch-and-release functionality for NetControl.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
@load ./drop
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Stops all packets involving an IP address from being forwarded. This function
|
||||||
|
## uses catch-and-release functionality, where the IP address is only dropped for
|
||||||
|
## a short amount of time that is incremented steadily when the IP is encountered
|
||||||
|
## again.
|
||||||
|
##
|
||||||
|
## a: The address to be dropped.
|
||||||
|
##
|
||||||
|
## t: How long to drop it, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing where the drop was triggered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global drop_address_catch_release: function(a: addr, location: string &default="") : string;
|
||||||
|
|
||||||
|
## Time intervals for which a subsequent drops of the same IP take
|
||||||
|
## effect.
|
||||||
|
const catch_release_intervals: vector of interval = vector(10min, 1hr, 24hrs, 7days) &redef;
|
||||||
|
}
|
||||||
|
|
||||||
|
function per_block_interval(t: table[addr] of count, idx: addr): interval
|
||||||
|
{
|
||||||
|
local ct = t[idx];
|
||||||
|
|
||||||
|
# watch for the time of the next block...
|
||||||
|
local blocktime = catch_release_intervals[ct];
|
||||||
|
if ( (ct+1) in catch_release_intervals )
|
||||||
|
blocktime = catch_release_intervals[ct+1];
|
||||||
|
|
||||||
|
return blocktime;
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is the internally maintained table containing all the currently going on catch-and-release
|
||||||
|
# blocks.
|
||||||
|
global blocks: table[addr] of count = {}
|
||||||
|
&create_expire=0secs
|
||||||
|
&expire_func=per_block_interval;
|
||||||
|
|
||||||
|
function current_block_interval(s: set[addr], idx: addr): interval
|
||||||
|
{
|
||||||
|
if ( idx !in blocks )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("Address %s not in blocks while inserting into current_blocks!", idx));
|
||||||
|
return 0sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return catch_release_intervals[blocks[idx]];
|
||||||
|
}
|
||||||
|
|
||||||
|
global current_blocks: set[addr] = set()
|
||||||
|
&create_expire=0secs
|
||||||
|
&expire_func=current_block_interval;
|
||||||
|
|
||||||
|
function drop_address_catch_release(a: addr, location: string &default=""): string
|
||||||
|
{
|
||||||
|
if ( a in blocks )
|
||||||
|
{
|
||||||
|
Reporter::warning(fmt("Address %s already blocked using catch-and-release - ignoring duplicate", a));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
local block_interval = catch_release_intervals[0];
|
||||||
|
local ret = drop_address(a, block_interval, location);
|
||||||
|
if ( ret != "" )
|
||||||
|
{
|
||||||
|
blocks[a] = 0;
|
||||||
|
add current_blocks[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_conn(a: addr)
|
||||||
|
{
|
||||||
|
if ( a in blocks )
|
||||||
|
{
|
||||||
|
if ( a in current_blocks )
|
||||||
|
# block has not been applied yet?
|
||||||
|
return;
|
||||||
|
|
||||||
|
# ok, this one returned again while still in the backoff period.
|
||||||
|
local try = blocks[a];
|
||||||
|
if ( (try+1) in catch_release_intervals )
|
||||||
|
++try;
|
||||||
|
|
||||||
|
blocks[a] = try;
|
||||||
|
add current_blocks[a];
|
||||||
|
local block_interval = catch_release_intervals[try];
|
||||||
|
drop_address(a, block_interval, "Re-drop by catch-and-release");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event new_connection(c: connection)
|
||||||
|
{
|
||||||
|
# let's only check originating connections...
|
||||||
|
check_conn(c$id$orig_h);
|
||||||
|
}
|
99
scripts/base/frameworks/netcontrol/cluster.bro
Normal file
99
scripts/base/frameworks/netcontrol/cluster.bro
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
##! Cluster support for the NetControl framework.
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
@load base/frameworks/cluster
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
export {
|
||||||
|
## This is the event used to transport add_rule calls to the manager.
|
||||||
|
global cluster_netcontrol_add_rule: event(r: Rule);
|
||||||
|
|
||||||
|
## This is the event used to transport remove_rule calls to the manager.
|
||||||
|
global cluster_netcontrol_remove_rule: event(id: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Workers need ability to forward commands to manager.
|
||||||
|
redef Cluster::worker2manager_events += /NetControl::cluster_netcontrol_(add|remove)_rule/;
|
||||||
|
## Workers need to see the result events from the manager.
|
||||||
|
redef Cluster::manager2worker_events += /NetControl::rule_(added|removed|timeout|error)/;
|
||||||
|
|
||||||
|
|
||||||
|
function activate(p: PluginState, priority: int)
|
||||||
|
{
|
||||||
|
# we only run the activate function on the manager.
|
||||||
|
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||||
|
return;
|
||||||
|
|
||||||
|
activate_impl(p, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
global local_rule_count: count = 1;
|
||||||
|
|
||||||
|
function add_rule(r: Rule) : string
|
||||||
|
{
|
||||||
|
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
return add_rule_impl(r);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( r$id == "" )
|
||||||
|
r$id = cat(Cluster::node, ":", ++local_rule_count);
|
||||||
|
|
||||||
|
event NetControl::cluster_netcontrol_add_rule(r);
|
||||||
|
return r$id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_rule(id: string) : bool
|
||||||
|
{
|
||||||
|
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
return remove_rule_impl(id);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event NetControl::cluster_netcontrol_remove_rule(id);
|
||||||
|
return T; # well, we can't know here. So - just hope...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
event NetControl::cluster_netcontrol_add_rule(r: Rule)
|
||||||
|
{
|
||||||
|
add_rule_impl(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::cluster_netcontrol_remove_rule(id: string)
|
||||||
|
{
|
||||||
|
remove_rule_impl(id);
|
||||||
|
}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
event rule_expire(r: Rule, p: PluginState) &priority=-5
|
||||||
|
{
|
||||||
|
rule_expire_impl(r, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_added(r: Rule, p: PluginState, msg: string &default="") &priority=5
|
||||||
|
{
|
||||||
|
rule_added_impl(r, p, msg);
|
||||||
|
|
||||||
|
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
|
||||||
|
schedule r$expire { rule_expire(r, p) };
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_removed(r: Rule, p: PluginState, msg: string &default="") &priority=-5
|
||||||
|
{
|
||||||
|
rule_removed_impl(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_timeout(r: Rule, i: FlowInfo, p: PluginState) &priority=-5
|
||||||
|
{
|
||||||
|
rule_timeout_impl(r, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_error(r: Rule, p: PluginState, msg: string &default="") &priority=-5
|
||||||
|
{
|
||||||
|
rule_error_impl(r, p, msg);
|
||||||
|
}
|
||||||
|
@endif
|
||||||
|
|
98
scripts/base/frameworks/netcontrol/drop.bro
Normal file
98
scripts/base/frameworks/netcontrol/drop.bro
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
##! Implementation of the drop functionality for NetControl.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Log::ID += { DROP };
|
||||||
|
|
||||||
|
## Stops all packets involving an IP address from being forwarded.
|
||||||
|
##
|
||||||
|
## a: The address to be dropped.
|
||||||
|
##
|
||||||
|
## t: How long to drop it, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing where the drop was triggered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global drop_address: function(a: addr, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
## Stops all packets involving an connection address from being forwarded.
|
||||||
|
##
|
||||||
|
## c: The connection to be dropped.
|
||||||
|
##
|
||||||
|
## t: How long to drop it, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing where the drop was triggered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global drop_connection: function(c: conn_id, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
type DropInfo: record {
|
||||||
|
## Time at which the recorded activity occurred.
|
||||||
|
ts: time &log;
|
||||||
|
## ID of the rule; unique during each Bro run
|
||||||
|
rule_id: string &log;
|
||||||
|
orig_h: addr &log; ##< The originator's IP address.
|
||||||
|
orig_p: port &log &optional; ##< The originator's port number.
|
||||||
|
resp_h: addr &log &optional; ##< The responder's IP address.
|
||||||
|
resp_p: port &log &optional; ##< The responder's port number.
|
||||||
|
## Expiry time of the shunt
|
||||||
|
expire: interval &log;
|
||||||
|
## Location where the underlying action was triggered.
|
||||||
|
location: string &log &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Event that can be handled to access the :bro:type:`NetControl::ShuntInfo`
|
||||||
|
## record as it is sent on to the logging framework.
|
||||||
|
global log_netcontrol_drop: event(rec: DropInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(NetControl::DROP, [$columns=DropInfo, $ev=log_netcontrol_drop, $path="netcontrol_drop"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop_connection(c: conn_id, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local e: Entity = [$ty=CONNECTION, $conn=c];
|
||||||
|
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||||
|
|
||||||
|
local id = add_rule(r);
|
||||||
|
|
||||||
|
# Error should already be logged
|
||||||
|
if ( id == "" )
|
||||||
|
return id;
|
||||||
|
|
||||||
|
local log = DropInfo($ts=network_time(), $rule_id=id, $orig_h=c$orig_h, $orig_p=c$orig_p, $resp_h=c$resp_h, $resp_p=c$resp_p, $expire=t);
|
||||||
|
|
||||||
|
if ( location != "" )
|
||||||
|
log$location=location;
|
||||||
|
|
||||||
|
Log::write(DROP, log);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop_address(a: addr, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local e: Entity = [$ty=ADDRESS, $ip=addr_to_subnet(a)];
|
||||||
|
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||||
|
|
||||||
|
local id = add_rule(r);
|
||||||
|
|
||||||
|
# Error should already be logged
|
||||||
|
if ( id == "" )
|
||||||
|
return id;
|
||||||
|
|
||||||
|
local log = DropInfo($ts=network_time(), $rule_id=id, $orig_h=a, $expire=t);
|
||||||
|
|
||||||
|
if ( location != "" )
|
||||||
|
log$location=location;
|
||||||
|
|
||||||
|
Log::write(DROP, log);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
935
scripts/base/frameworks/netcontrol/main.bro
Normal file
935
scripts/base/frameworks/netcontrol/main.bro
Normal file
|
@ -0,0 +1,935 @@
|
||||||
|
##! Bro's packet aquisition and control framework.
|
||||||
|
##!
|
||||||
|
##! This plugin-based framework allows to control the traffic that Bro monitors
|
||||||
|
##! as well as, if having access to the forwarding path, the traffic the network
|
||||||
|
##! forwards. By default, the framework lets everything through, to both Bro
|
||||||
|
##! itself as well as on the network. Scripts can then add rules to impose
|
||||||
|
##! restrictions on entities, such as specific connections or IP addresses.
|
||||||
|
##!
|
||||||
|
##! This framework has two APIs: a high-level and low-level. The high-level API
|
||||||
|
##! provides convinience functions for a set of common operations. The
|
||||||
|
##! low-level API provides full flexibility.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./plugin
|
||||||
|
@load ./types
|
||||||
|
|
||||||
|
export {
|
||||||
|
## The framework's logging stream identifier.
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# ### Generic functions and events.
|
||||||
|
# ###
|
||||||
|
|
||||||
|
# Activates a plugin.
|
||||||
|
#
|
||||||
|
# p: The plugin to acticate.
|
||||||
|
#
|
||||||
|
# priority: The higher the priority, the earlier this plugin will be checked
|
||||||
|
# whether it supports an operation, relative to other plugins.
|
||||||
|
global activate: function(p: PluginState, priority: int);
|
||||||
|
|
||||||
|
# Event that is used to initialize plugins. Place all plugin initialization
|
||||||
|
# related functionality in this event.
|
||||||
|
global NetControl::init: event();
|
||||||
|
|
||||||
|
# Event that is raised once all plugins activated in ``NetControl::init`` have finished
|
||||||
|
# their initialization.
|
||||||
|
global NetControl::init_done: event();
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# ### High-level API.
|
||||||
|
# ###
|
||||||
|
|
||||||
|
# ### Note - other high level primitives are in catch-and-release.bro, shunt.bro and
|
||||||
|
# ### drop.bro
|
||||||
|
|
||||||
|
## Allows all traffic involving a specific IP address to be forwarded.
|
||||||
|
##
|
||||||
|
## a: The address to be whitelistet.
|
||||||
|
##
|
||||||
|
## t: How long to whitelist it, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing whitelist was triddered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global whitelist_address: function(a: addr, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
## Allows all traffic involving a specific IP subnet to be forwarded.
|
||||||
|
##
|
||||||
|
## s: The subnet to be whitelistet.
|
||||||
|
##
|
||||||
|
## t: How long to whitelist it, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing whitelist was triddered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global whitelist_subnet: function(s: subnet, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
## Redirects an uni-directional flow to another port.
|
||||||
|
##
|
||||||
|
## f: The flow to redirect.
|
||||||
|
##
|
||||||
|
## out_port: Port to redirect the flow to
|
||||||
|
##
|
||||||
|
## t: How long to leave the redirect in place, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing where the redirect was triggered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global redirect_flow: function(f: flow_id, out_port: count, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
## Quarantines a host by redirecting rewriting DNS queries to the network dns server dns
|
||||||
|
## to the host. Host has to answer to all queries with its own address. Only http communication
|
||||||
|
## from infected to quarantinehost is allowed.
|
||||||
|
##
|
||||||
|
## infected: the host to quarantine
|
||||||
|
##
|
||||||
|
## dns: the network dns server
|
||||||
|
##
|
||||||
|
## quarantine: the quarantine server running a dns and a web server
|
||||||
|
##
|
||||||
|
## t: how long to leave the quarantine in place
|
||||||
|
##
|
||||||
|
## Returns: Vector of inserted rules on success, empty list on failure.
|
||||||
|
global quarantine_host: function(infected: addr, dns: addr, quarantine: addr, t: interval, location: string &default="") : vector of string;
|
||||||
|
|
||||||
|
## Flushes all state.
|
||||||
|
global clear: function();
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# ### Low-level API.
|
||||||
|
# ###
|
||||||
|
|
||||||
|
###### Manipulation of rules.
|
||||||
|
|
||||||
|
## Installs a rule.
|
||||||
|
##
|
||||||
|
## r: The rule to install.
|
||||||
|
##
|
||||||
|
## Returns: If succesful, returns an ID string unique to the rule that can later
|
||||||
|
## be used to refer to it. If unsuccessful, returns an empty string. The ID is also
|
||||||
|
## assigned to ``r$id``. Note that "successful" means "a plugin knew how to handle
|
||||||
|
## the rule", it doesn't necessarily mean that it was indeed successfully put in
|
||||||
|
## place, because that might happen asynchronously and thus fail only later.
|
||||||
|
global add_rule: function(r: Rule) : string;
|
||||||
|
|
||||||
|
## Removes a rule.
|
||||||
|
##
|
||||||
|
## id: The rule to remove, specified as the ID returned by :bro:id:`add_rule` .
|
||||||
|
##
|
||||||
|
## Returns: True if succesful, the relevant plugin indicated that it knew how
|
||||||
|
## to handle the removal. Note that again "success" means the plugin accepted the
|
||||||
|
## removal. They might still fail to put it into effect, as that might happen
|
||||||
|
## asynchronously and thus go wrong at that point.
|
||||||
|
global remove_rule: function(id: string) : bool;
|
||||||
|
|
||||||
|
## Searches all rules affecting a certain IP address.
|
||||||
|
##
|
||||||
|
## ip: The ip address to search for
|
||||||
|
##
|
||||||
|
## Returns: vector of all rules affecting the IP address
|
||||||
|
global find_rules_addr: function(ip: addr) : vector of Rule;
|
||||||
|
|
||||||
|
## Searches all rules affecting a certain subnet.
|
||||||
|
##
|
||||||
|
## sn: The subnet to search for
|
||||||
|
##
|
||||||
|
## Returns: vector of all rules affecting the subnet
|
||||||
|
global find_rules_subnet: function(sn: subnet) : vector of Rule;
|
||||||
|
|
||||||
|
###### Asynchronous feedback on rules.
|
||||||
|
|
||||||
|
## Confirms that a rule was put in place.
|
||||||
|
##
|
||||||
|
## r: The rule now in place.
|
||||||
|
##
|
||||||
|
## p: The state for the plugin that put it into place.
|
||||||
|
##
|
||||||
|
## msg: An optional informational message by the plugin.
|
||||||
|
global rule_added: event(r: Rule, p: PluginState, msg: string &default="");
|
||||||
|
|
||||||
|
## Reports that a rule was removed due to a remove: function() call.
|
||||||
|
##
|
||||||
|
## r: The rule now removed.
|
||||||
|
##
|
||||||
|
## p: The state for the plugin that had the rule in place and now
|
||||||
|
## removed it.
|
||||||
|
##
|
||||||
|
## msg: An optional informational message by the plugin.
|
||||||
|
global rule_removed: event(r: Rule, p: PluginState, msg: string &default="");
|
||||||
|
|
||||||
|
## Reports that a rule was removed internally due to a timeout.
|
||||||
|
##
|
||||||
|
## r: The rule now removed.
|
||||||
|
##
|
||||||
|
## i: Additional flow information, if supported by the protocol.
|
||||||
|
##
|
||||||
|
## p: The state for the plugin that had the rule in place and now
|
||||||
|
## removed it.
|
||||||
|
##
|
||||||
|
## msg: An optional informational message by the plugin.
|
||||||
|
global rule_timeout: event(r: Rule, i: FlowInfo, p: PluginState);
|
||||||
|
|
||||||
|
## Reports an error when operating on a rule.
|
||||||
|
##
|
||||||
|
## r: The rule that encountered an error.
|
||||||
|
##
|
||||||
|
## p: The state for the plugin that reported the error.
|
||||||
|
##
|
||||||
|
## msg: An optional informational message by the plugin.
|
||||||
|
global rule_error: event(r: Rule, p: PluginState, msg: string &default="");
|
||||||
|
|
||||||
|
## Hook that allows the modification of rules passed to add_rule before they
|
||||||
|
## are passed on to the plugins. If one of the hooks uses break, the rule is
|
||||||
|
## ignored and not passed on to any plugin.
|
||||||
|
##
|
||||||
|
## r: The rule to be added
|
||||||
|
global NetControl::rule_policy: hook(r: Rule);
|
||||||
|
|
||||||
|
##### Plugin functions
|
||||||
|
|
||||||
|
## Function called by plugins once they finished their activation. After all
|
||||||
|
## plugins defined in bro_init finished to activate, rules will start to be sent
|
||||||
|
## to the plugins. Rules that scripts try to set before the backends are ready
|
||||||
|
## will be discarded.
|
||||||
|
global plugin_activated: function(p: PluginState);
|
||||||
|
|
||||||
|
## Type of an entry in the NetControl log.
|
||||||
|
type InfoCategory: enum {
|
||||||
|
## A log entry reflecting a framework message.
|
||||||
|
MESSAGE,
|
||||||
|
## A log entry reflecting a framework message.
|
||||||
|
ERROR,
|
||||||
|
## A log entry about about a rule.
|
||||||
|
RULE
|
||||||
|
};
|
||||||
|
|
||||||
|
## State of an entry in the NetControl log.
|
||||||
|
type InfoState: enum {
|
||||||
|
REQUESTED,
|
||||||
|
SUCCEEDED,
|
||||||
|
FAILED,
|
||||||
|
REMOVED,
|
||||||
|
TIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
## The record type defining the column fields of the NetControl log.
|
||||||
|
type Info: record {
|
||||||
|
## Time at which the recorded activity occurred.
|
||||||
|
ts: time &log;
|
||||||
|
## ID of the rule; unique during each Bro run
|
||||||
|
rule_id: string &log &optional;
|
||||||
|
## Type of the log entry.
|
||||||
|
category: InfoCategory &log &optional;
|
||||||
|
## The command the log entry is about.
|
||||||
|
cmd: string &log &optional;
|
||||||
|
## State the log entry reflects.
|
||||||
|
state: InfoState &log &optional;
|
||||||
|
## String describing an action the entry is about.
|
||||||
|
action: string &log &optional;
|
||||||
|
## The target type of the action.
|
||||||
|
target: TargetType &log &optional;
|
||||||
|
## Type of the entity the log entry is about.
|
||||||
|
entity_type: string &log &optional;
|
||||||
|
## String describing the entity the log entry is about.
|
||||||
|
entity: string &log &optional;
|
||||||
|
## String describing the optional modification of the entry (e.h. redirect)
|
||||||
|
mod: string &log &optional;
|
||||||
|
## String with an additional message.
|
||||||
|
msg: string &log &optional;
|
||||||
|
## Number describing the priority of the log entry
|
||||||
|
priority: int &log &optional;
|
||||||
|
## Expiry time of the log entry
|
||||||
|
expire: interval &log &optional;
|
||||||
|
## Location where the underlying action was triggered.
|
||||||
|
location: string &log &optional;
|
||||||
|
## Plugin triggering the log entry.
|
||||||
|
plugin: string &log &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Event that can be handled to access the :bro:type:`NetControl::Info`
|
||||||
|
## record as it is sent on to the logging framework.
|
||||||
|
global log_netcontrol: event(rec: Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
redef record Rule += {
|
||||||
|
##< Internally set to the plugins handling the rule.
|
||||||
|
_plugin_ids: set[count] &default=count_set();
|
||||||
|
##< Internally set to the plugins on which the rule is currently active.
|
||||||
|
_active_plugin_ids: set[count] &default=count_set();
|
||||||
|
##< Track if the rule was added succesfully by all responsible plugins.
|
||||||
|
_added: bool &default=F;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Variable tracking the state of plugin activation. Once all plugins that
|
||||||
|
# have been added in bro_init are activated, this will switch to T and
|
||||||
|
# the event NetControl::init_done will be raised.
|
||||||
|
global plugins_active: bool = F;
|
||||||
|
|
||||||
|
# Set to true at the end of bro_init (with very low priority).
|
||||||
|
# Used to track when plugin activation could potentially be finished
|
||||||
|
global bro_init_done: bool = F;
|
||||||
|
|
||||||
|
# The counters that are used to generate the rule and plugin IDs
|
||||||
|
global rule_counter: count = 1;
|
||||||
|
global plugin_counter: count = 1;
|
||||||
|
|
||||||
|
# List of the currently active plugins
|
||||||
|
global plugins: vector of PluginState;
|
||||||
|
global plugin_ids: table[count] of PluginState;
|
||||||
|
|
||||||
|
# These tables hold information about rules.
|
||||||
|
global rules: table[string] of Rule; # Rules indexed by id and cid
|
||||||
|
|
||||||
|
# All rules that apply to a certain subnet/IP address.
|
||||||
|
global rules_by_subnets: table[subnet] of set[string];
|
||||||
|
|
||||||
|
# Rules pertaining to a specific entity.
|
||||||
|
# There always only can be one rule of each type for one entity.
|
||||||
|
global rule_entities: table[Entity, RuleType] of Rule;
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(NetControl::LOG, [$columns=Info, $ev=log_netcontrol, $path="netcontrol"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function entity_to_info(info: Info, e: Entity)
|
||||||
|
{
|
||||||
|
info$entity_type = fmt("%s", e$ty);
|
||||||
|
|
||||||
|
switch ( e$ty ) {
|
||||||
|
case ADDRESS:
|
||||||
|
info$entity = fmt("%s", e$ip);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECTION:
|
||||||
|
info$entity = fmt("%s/%d<->%s/%d",
|
||||||
|
e$conn$orig_h, e$conn$orig_p,
|
||||||
|
e$conn$resp_h, e$conn$resp_p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLOW:
|
||||||
|
local ffrom_ip = "*";
|
||||||
|
local ffrom_port = "*";
|
||||||
|
local fto_ip = "*";
|
||||||
|
local fto_port = "*";
|
||||||
|
local ffrom_mac = "*";
|
||||||
|
local fto_mac = "*";
|
||||||
|
if ( e$flow?$src_h )
|
||||||
|
ffrom_ip = cat(e$flow$src_h);
|
||||||
|
if ( e$flow?$src_p )
|
||||||
|
ffrom_port = fmt("%d", e$flow$src_p);
|
||||||
|
if ( e$flow?$dst_h )
|
||||||
|
fto_ip = cat(e$flow$dst_h);
|
||||||
|
if ( e$flow?$dst_p )
|
||||||
|
fto_port = fmt("%d", e$flow$dst_p);
|
||||||
|
info$entity = fmt("%s/%s->%s/%s",
|
||||||
|
ffrom_ip, ffrom_port,
|
||||||
|
fto_ip, fto_port);
|
||||||
|
if ( e$flow?$src_m || e$flow?$dst_m )
|
||||||
|
{
|
||||||
|
if ( e$flow?$src_m )
|
||||||
|
ffrom_mac = e$flow$src_m;
|
||||||
|
if ( e$flow?$dst_m )
|
||||||
|
fto_mac = e$flow$dst_m;
|
||||||
|
|
||||||
|
info$entity = fmt("%s (%s->%s)", info$entity, ffrom_mac, fto_mac);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MAC:
|
||||||
|
info$entity = e$mac;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
info$entity = "<unknown entity type>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_to_info(info: Info, r: Rule)
|
||||||
|
{
|
||||||
|
info$action = fmt("%s", r$ty);
|
||||||
|
info$target = r$target;
|
||||||
|
info$rule_id = r$id;
|
||||||
|
info$expire = r$expire;
|
||||||
|
info$priority = r$priority;
|
||||||
|
|
||||||
|
if ( r?$location && r$location != "" )
|
||||||
|
info$location = r$location;
|
||||||
|
|
||||||
|
if ( r$ty == REDIRECT )
|
||||||
|
info$mod = fmt("-> %d", r$out_port);
|
||||||
|
|
||||||
|
if ( r$ty == MODIFY )
|
||||||
|
{
|
||||||
|
local mfrom_ip = "_";
|
||||||
|
local mfrom_port = "_";
|
||||||
|
local mto_ip = "_";
|
||||||
|
local mto_port = "_";
|
||||||
|
local mfrom_mac = "_";
|
||||||
|
local mto_mac = "_";
|
||||||
|
if ( r$mod?$src_h )
|
||||||
|
mfrom_ip = cat(r$mod$src_h);
|
||||||
|
if ( r$mod?$src_p )
|
||||||
|
mfrom_port = fmt("%d", r$mod$src_p);
|
||||||
|
if ( r$mod?$dst_h )
|
||||||
|
mto_ip = cat(r$mod$dst_h);
|
||||||
|
if ( r$mod?$dst_p )
|
||||||
|
mto_port = fmt("%d", r$mod$dst_p);
|
||||||
|
|
||||||
|
if ( r$mod?$src_m )
|
||||||
|
mfrom_mac = r$mod$src_m;
|
||||||
|
if ( r$mod?$dst_m )
|
||||||
|
mto_mac = r$mod$dst_m;
|
||||||
|
|
||||||
|
info$mod = fmt("Src: %s/%s (%s) Dst: %s/%s (%s)",
|
||||||
|
mfrom_ip, mfrom_port, mfrom_mac, mto_ip, mto_port, mto_mac);
|
||||||
|
|
||||||
|
if ( r$mod?$redirect_port )
|
||||||
|
info$mod = fmt("%s -> %d", info$mod, r$mod$redirect_port);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
entity_to_info(info, r$entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_msg(msg: string, p: PluginState)
|
||||||
|
{
|
||||||
|
Log::write(LOG, [$ts=network_time(), $category=MESSAGE, $msg=msg, $plugin=p$plugin$name(p)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_error(msg: string, p: PluginState)
|
||||||
|
{
|
||||||
|
Log::write(LOG, [$ts=network_time(), $category=ERROR, $msg=msg, $plugin=p$plugin$name(p)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_msg_no_plugin(msg: string)
|
||||||
|
{
|
||||||
|
Log::write(LOG, [$ts=network_time(), $category=MESSAGE, $msg=msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_rule(r: Rule, cmd: string, state: InfoState, p: PluginState, msg: string &default="")
|
||||||
|
{
|
||||||
|
local info: Info = [$ts=network_time()];
|
||||||
|
info$category = RULE;
|
||||||
|
info$cmd = cmd;
|
||||||
|
info$state = state;
|
||||||
|
info$plugin = p$plugin$name(p);
|
||||||
|
if ( msg != "" )
|
||||||
|
info$msg = msg;
|
||||||
|
|
||||||
|
rule_to_info(info, r);
|
||||||
|
|
||||||
|
Log::write(LOG, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_rule_error(r: Rule, msg: string, p: PluginState)
|
||||||
|
{
|
||||||
|
local info: Info = [$ts=network_time(), $category=ERROR, $msg=msg, $plugin=p$plugin$name(p)];
|
||||||
|
rule_to_info(info, r);
|
||||||
|
Log::write(LOG, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_rule_no_plugin(r: Rule, state: InfoState, msg: string)
|
||||||
|
{
|
||||||
|
local info: Info = [$ts=network_time()];
|
||||||
|
info$category = RULE;
|
||||||
|
info$state = state;
|
||||||
|
info$msg = msg;
|
||||||
|
|
||||||
|
rule_to_info(info, r);
|
||||||
|
|
||||||
|
Log::write(LOG, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
function whitelist_address(a: addr, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local e: Entity = [$ty=ADDRESS, $ip=addr_to_subnet(a)];
|
||||||
|
local r: Rule = [$ty=WHITELIST, $priority=whitelist_priority, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||||
|
|
||||||
|
return add_rule(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
function whitelist_subnet(s: subnet, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local e: Entity = [$ty=ADDRESS, $ip=s];
|
||||||
|
local r: Rule = [$ty=WHITELIST, $priority=whitelist_priority, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||||
|
|
||||||
|
return add_rule(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function redirect_flow(f: flow_id, out_port: count, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local flow = NetControl::Flow(
|
||||||
|
$src_h=addr_to_subnet(f$src_h),
|
||||||
|
$src_p=f$src_p,
|
||||||
|
$dst_h=addr_to_subnet(f$dst_h),
|
||||||
|
$dst_p=f$dst_p
|
||||||
|
);
|
||||||
|
local e: Entity = [$ty=FLOW, $flow=flow];
|
||||||
|
local r: Rule = [$ty=REDIRECT, $target=FORWARD, $entity=e, $expire=t, $location=location, $out_port=out_port];
|
||||||
|
|
||||||
|
return add_rule(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
function quarantine_host(infected: addr, dns: addr, quarantine: addr, t: interval, location: string &default="") : vector of string
|
||||||
|
{
|
||||||
|
local orules: vector of string = vector();
|
||||||
|
local edrop: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected))];
|
||||||
|
local rdrop: Rule = [$ty=DROP, $target=FORWARD, $entity=edrop, $expire=t, $location=location];
|
||||||
|
orules[|orules|] = add_rule(rdrop);
|
||||||
|
|
||||||
|
local todnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(dns), $dst_p=53/udp)];
|
||||||
|
local todnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=todnse, $expire=t, $location=location, $mod=FlowMod($dst_h=quarantine), $priority=+5);
|
||||||
|
orules[|orules|] = add_rule(todnsr);
|
||||||
|
|
||||||
|
local fromdnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(dns), $src_p=53/udp, $dst_h=addr_to_subnet(infected))];
|
||||||
|
local fromdnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=fromdnse, $expire=t, $location=location, $mod=FlowMod($src_h=dns), $priority=+5);
|
||||||
|
orules[|orules|] = add_rule(fromdnsr);
|
||||||
|
|
||||||
|
local wle: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(quarantine), $dst_p=80/tcp)];
|
||||||
|
local wlr = Rule($ty=WHITELIST, $target=FORWARD, $entity=wle, $expire=t, $location=location, $priority=+5);
|
||||||
|
orules[|orules|] = add_rule(wlr);
|
||||||
|
|
||||||
|
return orules;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_plugins()
|
||||||
|
{
|
||||||
|
if ( plugins_active )
|
||||||
|
return;
|
||||||
|
|
||||||
|
local all_active = T;
|
||||||
|
for ( i in plugins )
|
||||||
|
{
|
||||||
|
local p = plugins[i];
|
||||||
|
if ( p$_activated == F )
|
||||||
|
all_active = F;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( all_active )
|
||||||
|
{
|
||||||
|
plugins_active = T;
|
||||||
|
|
||||||
|
# Skip log message if there are no plugins
|
||||||
|
if ( |plugins| > 0 )
|
||||||
|
log_msg_no_plugin("plugin initialization done");
|
||||||
|
|
||||||
|
event NetControl::init_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function plugin_activated(p: PluginState)
|
||||||
|
{
|
||||||
|
local id = p$_id;
|
||||||
|
if ( id !in plugin_ids )
|
||||||
|
{
|
||||||
|
log_error("unknown plugin activated", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plugin_ids[id]$_activated = T;
|
||||||
|
log_msg("activation finished", p);
|
||||||
|
|
||||||
|
if ( bro_init_done )
|
||||||
|
check_plugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init() &priority=-5
|
||||||
|
{
|
||||||
|
event NetControl::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::init() &priority=-20
|
||||||
|
{
|
||||||
|
bro_init_done = T;
|
||||||
|
|
||||||
|
check_plugins();
|
||||||
|
|
||||||
|
if ( plugins_active == F )
|
||||||
|
log_msg_no_plugin("waiting for plugins to initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Low-level functions that only runs on the manager (or standalone) Bro node.
|
||||||
|
|
||||||
|
function activate_impl(p: PluginState, priority: int)
|
||||||
|
{
|
||||||
|
p$_priority = priority;
|
||||||
|
plugins[|plugins|] = p;
|
||||||
|
sort(plugins, function(p1: PluginState, p2: PluginState) : int { return p2$_priority - p1$_priority; });
|
||||||
|
|
||||||
|
plugin_ids[plugin_counter] = p;
|
||||||
|
p$_id = plugin_counter;
|
||||||
|
++plugin_counter;
|
||||||
|
|
||||||
|
# perform one-time initialization
|
||||||
|
if ( p$plugin?$init )
|
||||||
|
{
|
||||||
|
log_msg(fmt("activating plugin with priority %d", priority), p);
|
||||||
|
p$plugin$init(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# no initialization necessary, mark plugin as active right away
|
||||||
|
plugin_activated(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_one_subnet_entry(s: subnet, r: Rule)
|
||||||
|
{
|
||||||
|
if ( ! check_subnet(s, rules_by_subnets) )
|
||||||
|
rules_by_subnets[s] = set(r$id);
|
||||||
|
else
|
||||||
|
add rules_by_subnets[s][r$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_subnet_entry(rule: Rule)
|
||||||
|
{
|
||||||
|
local e = rule$entity;
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
add_one_subnet_entry(e$ip, rule);
|
||||||
|
}
|
||||||
|
else if ( e$ty == CONNECTION )
|
||||||
|
{
|
||||||
|
add_one_subnet_entry(addr_to_subnet(e$conn$orig_h), rule);
|
||||||
|
add_one_subnet_entry(addr_to_subnet(e$conn$resp_h), rule);
|
||||||
|
}
|
||||||
|
else if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
if ( e$flow?$src_h )
|
||||||
|
add_one_subnet_entry(e$flow$src_h, rule);
|
||||||
|
if ( e$flow?$dst_h )
|
||||||
|
add_one_subnet_entry(e$flow$dst_h, rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_one_subnet_entry(s: subnet, r: Rule)
|
||||||
|
{
|
||||||
|
if ( ! check_subnet(s, rules_by_subnets) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( r$id !in rules_by_subnets[s] )
|
||||||
|
return;
|
||||||
|
|
||||||
|
delete rules_by_subnets[s][r$id];
|
||||||
|
if ( |rules_by_subnets[s]| == 0 )
|
||||||
|
delete rules_by_subnets[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_subnet_entry(rule: Rule)
|
||||||
|
{
|
||||||
|
local e = rule$entity;
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
remove_one_subnet_entry(e$ip, rule);
|
||||||
|
}
|
||||||
|
else if ( e$ty == CONNECTION )
|
||||||
|
{
|
||||||
|
remove_one_subnet_entry(addr_to_subnet(e$conn$orig_h), rule);
|
||||||
|
remove_one_subnet_entry(addr_to_subnet(e$conn$resp_h), rule);
|
||||||
|
}
|
||||||
|
else if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
if ( e$flow?$src_h )
|
||||||
|
remove_one_subnet_entry(e$flow$src_h, rule);
|
||||||
|
if ( e$flow?$dst_h )
|
||||||
|
remove_one_subnet_entry(e$flow$dst_h, rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function find_rules_subnet(sn: subnet) : vector of Rule
|
||||||
|
{
|
||||||
|
local ret: vector of Rule = vector();
|
||||||
|
|
||||||
|
local matches = matching_subnets(sn, rules_by_subnets);
|
||||||
|
|
||||||
|
for ( m in matches )
|
||||||
|
{
|
||||||
|
local sn_entry = matches[m];
|
||||||
|
local rule_ids = rules_by_subnets[sn_entry];
|
||||||
|
for ( rule_id in rules_by_subnets[sn_entry] )
|
||||||
|
{
|
||||||
|
if ( rule_id in rules )
|
||||||
|
ret[|ret|] = rules[rule_id];
|
||||||
|
else
|
||||||
|
Reporter::error("find_rules_subnet - internal data structure error, missing rule");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function find_rules_addr(ip: addr) : vector of Rule
|
||||||
|
{
|
||||||
|
return find_rules_subnet(addr_to_subnet(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_rule_impl(rule: Rule) : string
|
||||||
|
{
|
||||||
|
if ( ! plugins_active )
|
||||||
|
{
|
||||||
|
log_rule_no_plugin(rule, FAILED, "plugins not initialized yet");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
rule$cid = ++rule_counter; # numeric id that can be used by plugins for their rules.
|
||||||
|
|
||||||
|
if ( ! rule?$id || rule$id == "" )
|
||||||
|
rule$id = cat(rule$cid);
|
||||||
|
|
||||||
|
if ( ! hook NetControl::rule_policy(rule) )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if ( [rule$entity, rule$ty] in rule_entities )
|
||||||
|
{
|
||||||
|
log_rule_no_plugin(rule, FAILED, "discarded duplicate insertion");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
local accepted = F;
|
||||||
|
local priority: int = +0;
|
||||||
|
|
||||||
|
for ( i in plugins )
|
||||||
|
{
|
||||||
|
local p = plugins[i];
|
||||||
|
|
||||||
|
if ( p$_activated == F )
|
||||||
|
next;
|
||||||
|
|
||||||
|
# in this case, rule was accepted by earlier plugin and this plugin has a lower
|
||||||
|
# priority. Abort and do not send there...
|
||||||
|
if ( accepted == T && p$_priority != priority )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ( p$plugin$add_rule(p, rule) )
|
||||||
|
{
|
||||||
|
accepted = T;
|
||||||
|
priority = p$_priority;
|
||||||
|
log_rule(rule, "ADD", REQUESTED, p);
|
||||||
|
|
||||||
|
add rule$_plugin_ids[p$_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( accepted )
|
||||||
|
{
|
||||||
|
rules[rule$id] = rule;
|
||||||
|
rule_entities[rule$entity, rule$ty] = rule;
|
||||||
|
|
||||||
|
add_subnet_entry(rule);
|
||||||
|
|
||||||
|
return rule$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_rule_no_plugin(rule, FAILED, "not supported");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_rule_plugin(r: Rule, p: PluginState): bool
|
||||||
|
{
|
||||||
|
local success = T;
|
||||||
|
|
||||||
|
if ( ! p$plugin$remove_rule(p, r) )
|
||||||
|
{
|
||||||
|
# still continue and send to other plugins
|
||||||
|
log_rule_error(r, "remove failed", p);
|
||||||
|
success = F;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_rule(r, "REMOVE", REQUESTED, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_rule_impl(id: string) : bool
|
||||||
|
{
|
||||||
|
if ( id !in rules )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("Rule %s does not exist in NetControl::remove_rule", id));
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
local r = rules[id];
|
||||||
|
|
||||||
|
local success = T;
|
||||||
|
for ( plugin_id in r$_active_plugin_ids )
|
||||||
|
{
|
||||||
|
local p = plugin_ids[plugin_id];
|
||||||
|
success = remove_rule_plugin(r, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_expire_impl(r: Rule, p: PluginState) &priority=-5
|
||||||
|
{
|
||||||
|
# do not emit timeout events on shutdown
|
||||||
|
if ( bro_is_terminating() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( r$id !in rules )
|
||||||
|
# Removed already.
|
||||||
|
return;
|
||||||
|
|
||||||
|
event NetControl::rule_timeout(r, FlowInfo(), p); # timeout implementation will handle the removal
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_added_impl(r: Rule, p: PluginState, msg: string &default="")
|
||||||
|
{
|
||||||
|
if ( r$id !in rules )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Addition of unknown rule", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# use our version to prevent operating on copies.
|
||||||
|
local rule = rules[r$id];
|
||||||
|
if ( p$_id !in rule$_plugin_ids )
|
||||||
|
{
|
||||||
|
log_rule_error(rule, "Rule added to non-responsible plugin", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_rule(r, "ADD", SUCCEEDED, p, msg);
|
||||||
|
|
||||||
|
add rule$_active_plugin_ids[p$_id];
|
||||||
|
if ( |rule$_plugin_ids| == |rule$_active_plugin_ids| )
|
||||||
|
{
|
||||||
|
# rule was completely added.
|
||||||
|
rule$_added = T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_cleanup(r: Rule)
|
||||||
|
{
|
||||||
|
if ( |r$_active_plugin_ids| > 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
remove_subnet_entry(r);
|
||||||
|
|
||||||
|
delete rule_entities[r$entity, r$ty];
|
||||||
|
delete rules[r$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_removed_impl(r: Rule, p: PluginState, msg: string &default="")
|
||||||
|
{
|
||||||
|
if ( r$id !in rules )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Removal of non-existing rule", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# use our version to prevent operating on copies.
|
||||||
|
local rule = rules[r$id];
|
||||||
|
|
||||||
|
if ( p$_id !in rule$_plugin_ids )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Removed from non-assigned plugin", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p$_id in rule$_active_plugin_ids )
|
||||||
|
{
|
||||||
|
delete rule$_active_plugin_ids[p$_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
log_rule(rule, "REMOVE", SUCCEEDED, p, msg);
|
||||||
|
rule_cleanup(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_timeout_impl(r: Rule, i: FlowInfo, p: PluginState)
|
||||||
|
{
|
||||||
|
if ( r$id !in rules )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Timeout of non-existing rule", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local rule = rules[r$id];
|
||||||
|
|
||||||
|
local msg = "";
|
||||||
|
if ( i?$packet_count )
|
||||||
|
msg = fmt("Packets: %d", i$packet_count);
|
||||||
|
if ( i?$byte_count )
|
||||||
|
{
|
||||||
|
if ( msg != "" )
|
||||||
|
msg = msg + " ";
|
||||||
|
msg = fmt("%sBytes: %s", msg, i$byte_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_rule(rule, "EXPIRE", TIMEOUT, p, msg);
|
||||||
|
|
||||||
|
if ( ! p$plugin$can_expire )
|
||||||
|
{
|
||||||
|
# in this case, we actually have to delete the rule and the timeout
|
||||||
|
# call just originated locally
|
||||||
|
remove_rule_plugin(rule, p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p$_id !in rule$_plugin_ids )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Timeout from non-assigned plugin", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p$_id in rule$_active_plugin_ids )
|
||||||
|
{
|
||||||
|
delete rule$_active_plugin_ids[p$_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_cleanup(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_error_impl(r: Rule, p: PluginState, msg: string &default="")
|
||||||
|
{
|
||||||
|
if ( r$id !in rules )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Error of non-existing rule", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local rule = rules[r$id];
|
||||||
|
|
||||||
|
log_rule_error(rule, msg, p);
|
||||||
|
|
||||||
|
# Remove the plugin both from active and all plugins of the rule. If there
|
||||||
|
# are no plugins left afterwards - delete it
|
||||||
|
if ( p$_id !in rule$_plugin_ids )
|
||||||
|
{
|
||||||
|
log_rule_error(r, "Error from non-assigned plugin", p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p$_id in rule$_active_plugin_ids )
|
||||||
|
{
|
||||||
|
# error during removal. Let's pretend it worked.
|
||||||
|
delete rule$_plugin_ids[p$_id];
|
||||||
|
delete rule$_active_plugin_ids[p$_id];
|
||||||
|
rule_cleanup(rule);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# error during insertion. Meh. If we are the only plugin, remove the rule again.
|
||||||
|
# Otherwhise - keep it, minus us.
|
||||||
|
delete rule$_plugin_ids[p$_id];
|
||||||
|
if ( |rule$_plugin_ids| == 0 )
|
||||||
|
{
|
||||||
|
rule_cleanup(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear()
|
||||||
|
{
|
||||||
|
for ( id in rules )
|
||||||
|
remove_rule(id);
|
||||||
|
}
|
47
scripts/base/frameworks/netcontrol/non-cluster.bro
Normal file
47
scripts/base/frameworks/netcontrol/non-cluster.bro
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
|
||||||
|
function activate(p: PluginState, priority: int)
|
||||||
|
{
|
||||||
|
activate_impl(p, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_rule(r: Rule) : string
|
||||||
|
{
|
||||||
|
return add_rule_impl(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_rule(id: string) : bool
|
||||||
|
{
|
||||||
|
return remove_rule_impl(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_expire(r: Rule, p: PluginState) &priority=-5
|
||||||
|
{
|
||||||
|
rule_expire_impl(r, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_added(r: Rule, p: PluginState, msg: string &default="") &priority=5
|
||||||
|
{
|
||||||
|
rule_added_impl(r, p, msg);
|
||||||
|
|
||||||
|
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
|
||||||
|
schedule r$expire { rule_expire(r, p) };
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_removed(r: Rule, p: PluginState, msg: string &default="") &priority=-5
|
||||||
|
{
|
||||||
|
rule_removed_impl(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_timeout(r: Rule, i: FlowInfo, p: PluginState) &priority=-5
|
||||||
|
{
|
||||||
|
rule_timeout_impl(r, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rule_error(r: Rule, p: PluginState, msg: string &default="") &priority=-5
|
||||||
|
{
|
||||||
|
rule_error_impl(r, p, msg);
|
||||||
|
}
|
||||||
|
|
89
scripts/base/frameworks/netcontrol/plugin.bro
Normal file
89
scripts/base/frameworks/netcontrol/plugin.bro
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
##! Plugin interface for NetControl backends.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./types
|
||||||
|
|
||||||
|
export {
|
||||||
|
## State for a plugin instance.
|
||||||
|
type PluginState: record {
|
||||||
|
## Table for a plugin to store custom, instance-specfific state.
|
||||||
|
config: table[string] of string &default=table();
|
||||||
|
|
||||||
|
## Unique plugin identifier -- used for backlookup of plugins from Rules. Set internally.
|
||||||
|
_id: count &optional;
|
||||||
|
|
||||||
|
## Set internally.
|
||||||
|
_priority: int &default=+0;
|
||||||
|
|
||||||
|
## Set internally. Signifies if the plugin has returned that it has activated succesfully
|
||||||
|
_activated: bool &default=F;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Definition of a plugin.
|
||||||
|
#
|
||||||
|
# Generally a plugin needs to implement only what it can support. By
|
||||||
|
# returning failure, it indicates that it can't support something and the
|
||||||
|
# the framework will then try another plugin, if available; or inform the
|
||||||
|
# that the operation failed. If a function isn't implemented by a plugin,
|
||||||
|
# that's considered an implicit failure to support the operation.
|
||||||
|
#
|
||||||
|
# If plugin accepts a rule operation, it *must* generate one of the reporting
|
||||||
|
# events ``rule_{added,remove,error}`` to signal if it indeed worked out;
|
||||||
|
# this is separate from accepting the operation because often a plugin
|
||||||
|
# will only know later (i.e., asynchrously) if that was an error for
|
||||||
|
# something it thought it could handle.
|
||||||
|
type Plugin: record {
|
||||||
|
# Returns a descriptive name of the plugin instance, suitable for use in logging
|
||||||
|
# messages. Note that this function is not optional.
|
||||||
|
name: function(state: PluginState) : string;
|
||||||
|
|
||||||
|
## If true, plugin can expire rules itself. If false,
|
||||||
|
## framework will manage rule expiration.
|
||||||
|
can_expire: bool;
|
||||||
|
|
||||||
|
# One-time initialization function called when plugin gets registered, and
|
||||||
|
# before any other methods are called.
|
||||||
|
#
|
||||||
|
# If this function is provided, NetControl assumes that the plugin has to
|
||||||
|
# perform, potentially lengthy, initialization before the plugin will become
|
||||||
|
# active. In this case, the plugin has to call ``NetControl::plugin_activated``,
|
||||||
|
# once initialization finishes.
|
||||||
|
init: function(state: PluginState) &optional;
|
||||||
|
|
||||||
|
# One-time finalization function called when a plugin is shutdown; no further
|
||||||
|
# functions will be called afterwords.
|
||||||
|
done: function(state: PluginState) &optional;
|
||||||
|
|
||||||
|
# Implements the add_rule() operation. If the plugin accepts the rule,
|
||||||
|
# it returns true, false otherwise. The rule will already have its
|
||||||
|
# ``id`` field set, which the plugin may use for identification
|
||||||
|
# purposes.
|
||||||
|
add_rule: function(state: PluginState, r: Rule) : bool &optional;
|
||||||
|
|
||||||
|
# Implements the remove_rule() operation. This will only be called for
|
||||||
|
# rules that the plugins has previously accepted with add_rule(). The
|
||||||
|
# ``id`` field will match that of the add_rule() call. Generally,
|
||||||
|
# a plugin that accepts an add_rule() should also accept the
|
||||||
|
# remove_rule().
|
||||||
|
remove_rule: function(state: PluginState, r: Rule) : bool &optional;
|
||||||
|
|
||||||
|
# A transaction groups a number of operations. The plugin can add them internally
|
||||||
|
# and postpone putting them into effect until committed. This allows to build a
|
||||||
|
# configuration of multiple rules at once, including replaying a previous state.
|
||||||
|
transaction_begin: function(state: PluginState) &optional;
|
||||||
|
transaction_end: function(state: PluginState) &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Table for a plugin to store instance-specific configuration information.
|
||||||
|
#
|
||||||
|
# Note, it would be nicer to pass the Plugin instance to all the below, instead
|
||||||
|
# of this state table. However Bro's type resolver has trouble with refering to a
|
||||||
|
# record type from inside itself.
|
||||||
|
redef record PluginState += {
|
||||||
|
## The plugin that the state belongs to. (Defined separately
|
||||||
|
## because of cyclic type dependency.)
|
||||||
|
plugin: Plugin &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
5
scripts/base/frameworks/netcontrol/plugins/__load__.bro
Normal file
5
scripts/base/frameworks/netcontrol/plugins/__load__.bro
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@load ./debug
|
||||||
|
@load ./openflow
|
||||||
|
@load ./packetfilter
|
||||||
|
@load ./broker
|
||||||
|
@load ./acld
|
294
scripts/base/frameworks/netcontrol/plugins/acld.bro
Normal file
294
scripts/base/frameworks/netcontrol/plugins/acld.bro
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
##! Acld plugin for the netcontrol framework.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ../main
|
||||||
|
@load ../plugin
|
||||||
|
@load base/frameworks/broker
|
||||||
|
|
||||||
|
export {
|
||||||
|
type AclRule : record {
|
||||||
|
command: string;
|
||||||
|
cookie: count;
|
||||||
|
arg: string;
|
||||||
|
comment: string &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AcldConfig: record {
|
||||||
|
## The acld topic used to send events to
|
||||||
|
acld_topic: string;
|
||||||
|
## Broker host to connect to
|
||||||
|
acld_host: addr;
|
||||||
|
## Broker port to connect to
|
||||||
|
acld_port: port;
|
||||||
|
## Do we accept rules for the monitor path? Default false
|
||||||
|
monitor: bool &default=F;
|
||||||
|
## Do we accept rules for the forward path? Default true
|
||||||
|
forward: bool &default=T;
|
||||||
|
|
||||||
|
## Predicate that is called on rule insertion or removal.
|
||||||
|
##
|
||||||
|
## p: Current plugin state
|
||||||
|
##
|
||||||
|
## r: The rule to be inserted or removed
|
||||||
|
##
|
||||||
|
## Returns: T if the rule can be handled by the current backend, F otherwhise
|
||||||
|
check_pred: function(p: PluginState, r: Rule): bool &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Instantiates the acld plugin.
|
||||||
|
global create_acld: function(config: AcldConfig) : PluginState;
|
||||||
|
|
||||||
|
redef record PluginState += {
|
||||||
|
acld_config: AcldConfig &optional;
|
||||||
|
## The ID of this acld instance - for the mapping to PluginStates
|
||||||
|
acld_id: count &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Hook that is called after a rule is converted to an acld rule.
|
||||||
|
## The hook may modify the rule before it is sent to acld.
|
||||||
|
## Setting the acld command to F will cause the rule to be rejected
|
||||||
|
## by the plugin
|
||||||
|
##
|
||||||
|
## p: Current plugin state
|
||||||
|
##
|
||||||
|
## r: The rule to be inserted or removed
|
||||||
|
##
|
||||||
|
## ar: The acld rule to be inserted or removed
|
||||||
|
global NetControl::acld_rule_policy: hook(p: PluginState, r: Rule, ar: AclRule);
|
||||||
|
|
||||||
|
## Events that are sent from us to Broker
|
||||||
|
global acld_add_rule: event(id: count, r: Rule, ar: AclRule);
|
||||||
|
global acld_remove_rule: event(id: count, r: Rule, ar: AclRule);
|
||||||
|
|
||||||
|
## Events that are sent from Broker to us
|
||||||
|
global acld_rule_added: event(id: count, r: Rule, msg: string);
|
||||||
|
global acld_rule_removed: event(id: count, r: Rule, msg: string);
|
||||||
|
global acld_rule_error: event(id: count, r: Rule, msg: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
global netcontrol_acld_peers: table[port, string] of PluginState;
|
||||||
|
global netcontrol_acld_topics: set[string] = set();
|
||||||
|
global netcontrol_acld_id: table[count] of PluginState = table();
|
||||||
|
global netcontrol_acld_current_id: count = 0;
|
||||||
|
|
||||||
|
const acld_add_to_remove: table[string] of string = {
|
||||||
|
["drop"] = "restore",
|
||||||
|
["whitelist"] = "remwhitelist",
|
||||||
|
["blockhosthost"] = "restorehosthost",
|
||||||
|
["droptcpport"] = "restoretcpport",
|
||||||
|
["dropudpport"] = "restoreudpport",
|
||||||
|
["droptcpdsthostport"] ="restoretcpdsthostport",
|
||||||
|
["dropudpdsthostport"] ="restoreudpdsthostport",
|
||||||
|
["permittcpdsthostport"] ="unpermittcpdsthostport",
|
||||||
|
["permitudpdsthostport"] ="unpermitudpdsthostport",
|
||||||
|
["nullzero"] ="nonullzero"
|
||||||
|
};
|
||||||
|
|
||||||
|
event NetControl::acld_rule_added(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_acld_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_acld_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_added(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::acld_rule_removed(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_acld_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_acld_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_removed(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::acld_rule_error(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_acld_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_acld_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_error(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function acld_name(p: PluginState) : string
|
||||||
|
{
|
||||||
|
return fmt("Acld-%s", p$acld_config$acld_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
# check that subnet specifies an addr
|
||||||
|
function check_sn(sn: subnet) : bool
|
||||||
|
{
|
||||||
|
if ( is_v4_subnet(sn) && subnet_width(sn) == 32 )
|
||||||
|
return T;
|
||||||
|
if ( is_v6_subnet(sn) && subnet_width(sn) == 128 )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
Reporter::error(fmt("Acld: rule_to_acl_rule was given a subnet that does not specify a distinct address where needed - %s", sn));
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rule_to_acl_rule(p: PluginState, r: Rule) : AclRule
|
||||||
|
{
|
||||||
|
local e = r$entity;
|
||||||
|
|
||||||
|
local command: string = "";
|
||||||
|
local arg: string = "";
|
||||||
|
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
if ( r$ty == DROP )
|
||||||
|
command = "drop";
|
||||||
|
else if ( r$ty == WHITELIST )
|
||||||
|
command = "whitelist";
|
||||||
|
arg = cat(e$ip);
|
||||||
|
}
|
||||||
|
else if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
local f = e$flow;
|
||||||
|
if ( ( ! f?$src_h ) && ( ! f?$src_p ) && f?$dst_h && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||||
|
{
|
||||||
|
if ( !check_sn(f$dst_h) )
|
||||||
|
command = ""; # invalid addr, do nothing
|
||||||
|
else if ( is_tcp_port(f$dst_p) && r$ty == DROP )
|
||||||
|
command = "droptcpdsthostport";
|
||||||
|
else if ( is_tcp_port(f$dst_p) && r$ty == WHITELIST )
|
||||||
|
command = "permittcpdsthostport";
|
||||||
|
else if ( is_udp_port(f$dst_p) && r$ty == DROP)
|
||||||
|
command = "dropucpdsthostport";
|
||||||
|
else if ( is_udp_port(f$dst_p) && r$ty == WHITELIST)
|
||||||
|
command = "permitucpdsthostport";
|
||||||
|
|
||||||
|
arg = fmt("%s %d", subnet_to_addr(f$dst_h), f$dst_p);
|
||||||
|
}
|
||||||
|
else if ( f?$src_h && ( ! f?$src_p ) && f?$dst_h && ( ! f?$dst_p ) && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||||
|
{
|
||||||
|
if ( !check_sn(f$src_h) || !check_sn(f$dst_h) )
|
||||||
|
command = "";
|
||||||
|
else if ( r$ty == DROP )
|
||||||
|
command = "blockhosthost";
|
||||||
|
arg = fmt("%s %s", subnet_to_addr(f$src_h), subnet_to_addr(f$dst_h));
|
||||||
|
}
|
||||||
|
else if ( ( ! f?$src_h ) && ( ! f?$src_p ) && ( ! f?$dst_h ) && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||||
|
{
|
||||||
|
if ( is_tcp_port(f$dst_p) && r$ty == DROP )
|
||||||
|
command = "droptcpport";
|
||||||
|
else if ( is_udp_port(f$dst_p) && r$ty == DROP )
|
||||||
|
command = "dropudpport";
|
||||||
|
arg = fmt("%d", f$dst_p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local ar = AclRule($command=command, $cookie=r$cid, $arg=arg);
|
||||||
|
if ( r?$location )
|
||||||
|
ar$comment = r$location;
|
||||||
|
|
||||||
|
hook NetControl::acld_rule_policy(p, r, ar);
|
||||||
|
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acld_check_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
local c = p$acld_config;
|
||||||
|
|
||||||
|
if ( p$acld_config?$check_pred )
|
||||||
|
return p$acld_config$check_pred(p, r);
|
||||||
|
|
||||||
|
if ( r$target == MONITOR && c$monitor )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
if ( r$target == FORWARD && c$forward )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acld_add_rule_fun(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! acld_check_rule(p, r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local ar = rule_to_acl_rule(p, r);
|
||||||
|
|
||||||
|
if ( ar$command == "" )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
Broker::event(p$acld_config$acld_topic, Broker::event_args(acld_add_rule, p$acld_id, r, ar));
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acld_remove_rule_fun(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! acld_check_rule(p, r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local ar = rule_to_acl_rule(p, r);
|
||||||
|
if ( ar$command in acld_add_to_remove )
|
||||||
|
ar$command = acld_add_to_remove[ar$command];
|
||||||
|
else
|
||||||
|
return F;
|
||||||
|
|
||||||
|
Broker::event(p$acld_config$acld_topic, Broker::event_args(acld_remove_rule, p$acld_id, r, ar));
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acld_init(p: PluginState)
|
||||||
|
{
|
||||||
|
Broker::enable();
|
||||||
|
Broker::connect(cat(p$acld_config$acld_host), p$acld_config$acld_port, 1sec);
|
||||||
|
Broker::subscribe_to_events(p$acld_config$acld_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||||
|
{
|
||||||
|
if ( [peer_port, peer_address] !in netcontrol_acld_peers )
|
||||||
|
# ok, this one was none of ours...
|
||||||
|
return;
|
||||||
|
|
||||||
|
local p = netcontrol_acld_peers[peer_port, peer_address];
|
||||||
|
plugin_activated(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
global acld_plugin = Plugin(
|
||||||
|
$name=acld_name,
|
||||||
|
$can_expire = F,
|
||||||
|
$add_rule = acld_add_rule_fun,
|
||||||
|
$remove_rule = acld_remove_rule_fun,
|
||||||
|
$init = acld_init
|
||||||
|
);
|
||||||
|
|
||||||
|
function create_acld(config: AcldConfig) : PluginState
|
||||||
|
{
|
||||||
|
if ( config$acld_topic in netcontrol_acld_topics )
|
||||||
|
Reporter::warning(fmt("Topic %s was added to NetControl acld plugin twice. Possible duplication of commands", config$acld_topic));
|
||||||
|
else
|
||||||
|
add netcontrol_acld_topics[config$acld_topic];
|
||||||
|
|
||||||
|
local host = cat(config$acld_host);
|
||||||
|
local p: PluginState = [$acld_config=config, $plugin=acld_plugin, $acld_id=netcontrol_acld_current_id];
|
||||||
|
|
||||||
|
if ( [config$acld_port, host] in netcontrol_acld_peers )
|
||||||
|
Reporter::warning(fmt("Peer %s:%s was added to NetControl acld plugin twice.", host, config$acld_port));
|
||||||
|
else
|
||||||
|
netcontrol_acld_peers[config$acld_port, host] = p;
|
||||||
|
|
||||||
|
netcontrol_acld_id[netcontrol_acld_current_id] = p;
|
||||||
|
++netcontrol_acld_current_id;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
163
scripts/base/frameworks/netcontrol/plugins/broker.bro
Normal file
163
scripts/base/frameworks/netcontrol/plugins/broker.bro
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
##! Broker plugin for the netcontrol framework. Sends the raw data structures
|
||||||
|
##! used in NetControl on to Broker to allow for easy handling, e.g., of
|
||||||
|
##! command-line scripts.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ../main
|
||||||
|
@load ../plugin
|
||||||
|
@load base/frameworks/broker
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Instantiates the broker plugin.
|
||||||
|
global create_broker: function(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState;
|
||||||
|
|
||||||
|
redef record PluginState += {
|
||||||
|
## The broker topic used to send events to
|
||||||
|
broker_topic: string &optional;
|
||||||
|
## The ID of this broker instance - for the mapping to PluginStates
|
||||||
|
broker_id: count &optional;
|
||||||
|
## Broker host to connect to
|
||||||
|
broker_host: addr &optional;
|
||||||
|
## Broker port to connect to
|
||||||
|
broker_port: port &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
global broker_add_rule: event(id: count, r: Rule);
|
||||||
|
global broker_remove_rule: event(id: count, r: Rule);
|
||||||
|
|
||||||
|
global broker_rule_added: event(id: count, r: Rule, msg: string);
|
||||||
|
global broker_rule_removed: event(id: count, r: Rule, msg: string);
|
||||||
|
global broker_rule_error: event(id: count, r: Rule, msg: string);
|
||||||
|
global broker_rule_timeout: event(id: count, r: Rule, i: FlowInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
global netcontrol_broker_peers: table[port, string] of PluginState;
|
||||||
|
global netcontrol_broker_topics: set[string] = set();
|
||||||
|
global netcontrol_broker_id: table[count] of PluginState = table();
|
||||||
|
global netcontrol_broker_current_id: count = 0;
|
||||||
|
|
||||||
|
event NetControl::broker_rule_added(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_broker_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_broker_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_added(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::broker_rule_removed(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_broker_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_broker_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_removed(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::broker_rule_error(id: count, r: Rule, msg: string)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_broker_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_broker_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_error(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::broker_rule_timeout(id: count, r: Rule, i: FlowInfo)
|
||||||
|
{
|
||||||
|
if ( id !in netcontrol_broker_id )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local p = netcontrol_broker_id[id];
|
||||||
|
|
||||||
|
event NetControl::rule_timeout(r, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_name(p: PluginState) : string
|
||||||
|
{
|
||||||
|
return fmt("Broker-%s", p$broker_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_add_rule_fun(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
Broker::event(p$broker_topic, Broker::event_args(broker_add_rule, p$broker_id, r));
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_remove_rule_fun(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
Broker::event(p$broker_topic, Broker::event_args(broker_remove_rule, p$broker_id, r));
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_init(p: PluginState)
|
||||||
|
{
|
||||||
|
Broker::enable();
|
||||||
|
Broker::connect(cat(p$broker_host), p$broker_port, 1sec);
|
||||||
|
Broker::subscribe_to_events(p$broker_topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||||
|
{
|
||||||
|
if ( [peer_port, peer_address] !in netcontrol_broker_peers )
|
||||||
|
return;
|
||||||
|
|
||||||
|
local p = netcontrol_broker_peers[peer_port, peer_address];
|
||||||
|
plugin_activated(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
global broker_plugin = Plugin(
|
||||||
|
$name=broker_name,
|
||||||
|
$can_expire = F,
|
||||||
|
$add_rule = broker_add_rule_fun,
|
||||||
|
$remove_rule = broker_remove_rule_fun,
|
||||||
|
$init = broker_init
|
||||||
|
);
|
||||||
|
|
||||||
|
global broker_plugin_can_expire = Plugin(
|
||||||
|
$name=broker_name,
|
||||||
|
$can_expire = T,
|
||||||
|
$add_rule = broker_add_rule_fun,
|
||||||
|
$remove_rule = broker_remove_rule_fun,
|
||||||
|
$init = broker_init
|
||||||
|
);
|
||||||
|
|
||||||
|
function create_broker(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState
|
||||||
|
{
|
||||||
|
if ( topic in netcontrol_broker_topics )
|
||||||
|
Reporter::warning(fmt("Topic %s was added to NetControl broker plugin twice. Possible duplication of commands", topic));
|
||||||
|
else
|
||||||
|
add netcontrol_broker_topics[topic];
|
||||||
|
|
||||||
|
local plugin = broker_plugin;
|
||||||
|
if ( can_expire )
|
||||||
|
plugin = broker_plugin_can_expire;
|
||||||
|
|
||||||
|
local p: PluginState = [$broker_host=host, $broker_port=host_port, $plugin=plugin, $broker_topic=topic, $broker_id=netcontrol_broker_current_id];
|
||||||
|
|
||||||
|
if ( [host_port, cat(host)] in netcontrol_broker_peers )
|
||||||
|
Reporter::warning(fmt("Peer %s:%s was added to NetControl broker plugin twice.", host, host_port));
|
||||||
|
else
|
||||||
|
netcontrol_broker_peers[host_port, cat(host)] = p;
|
||||||
|
|
||||||
|
netcontrol_broker_id[netcontrol_broker_current_id] = p;
|
||||||
|
++netcontrol_broker_current_id;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
99
scripts/base/frameworks/netcontrol/plugins/debug.bro
Normal file
99
scripts/base/frameworks/netcontrol/plugins/debug.bro
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
##! Debugging plugin for the NetControl framework, providing insight into
|
||||||
|
##! executed operations.
|
||||||
|
|
||||||
|
@load ../plugin
|
||||||
|
@load ../main
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Instantiates a debug plugin for the NetControl framework. The debug
|
||||||
|
## plugin simply logs the operations it receives.
|
||||||
|
##
|
||||||
|
## do_something: If true, the plugin will claim it supports all operations; if
|
||||||
|
## false, it will indicate it doesn't support any.
|
||||||
|
global create_debug: function(do_something: bool) : PluginState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_something(p: PluginState) : bool
|
||||||
|
{
|
||||||
|
return p$config["all"] == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_name(p: PluginState) : string
|
||||||
|
{
|
||||||
|
return fmt("Debug-%s", (do_something(p) ? "All" : "None"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_log(p: PluginState, msg: string)
|
||||||
|
{
|
||||||
|
print fmt("netcontrol debug (%s): %s", debug_name(p), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_init(p: PluginState)
|
||||||
|
{
|
||||||
|
debug_log(p, "init");
|
||||||
|
plugin_activated(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_done(p: PluginState)
|
||||||
|
{
|
||||||
|
debug_log(p, "init");
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_add_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
local s = fmt("add_rule: %s", r);
|
||||||
|
debug_log(p, s);
|
||||||
|
|
||||||
|
if ( do_something(p) )
|
||||||
|
{
|
||||||
|
event NetControl::rule_added(r, p);
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_remove_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
local s = fmt("remove_rule: %s", r);
|
||||||
|
debug_log(p, s);
|
||||||
|
|
||||||
|
event NetControl::rule_removed(r, p);
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_transaction_begin(p: PluginState)
|
||||||
|
{
|
||||||
|
debug_log(p, "transaction_begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_transaction_end(p: PluginState)
|
||||||
|
{
|
||||||
|
debug_log(p, "transaction_end");
|
||||||
|
}
|
||||||
|
|
||||||
|
global debug_plugin = Plugin(
|
||||||
|
$name=debug_name,
|
||||||
|
$can_expire = F,
|
||||||
|
$init = debug_init,
|
||||||
|
$done = debug_done,
|
||||||
|
$add_rule = debug_add_rule,
|
||||||
|
$remove_rule = debug_remove_rule,
|
||||||
|
$transaction_begin = debug_transaction_begin,
|
||||||
|
$transaction_end = debug_transaction_end
|
||||||
|
);
|
||||||
|
|
||||||
|
function create_debug(do_something: bool) : PluginState
|
||||||
|
{
|
||||||
|
local p: PluginState = [$plugin=debug_plugin];
|
||||||
|
|
||||||
|
# FIXME: Why's the default not working?
|
||||||
|
p$config = table();
|
||||||
|
p$config["all"] = (do_something ? "1" : "0");
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
432
scripts/base/frameworks/netcontrol/plugins/openflow.bro
Normal file
432
scripts/base/frameworks/netcontrol/plugins/openflow.bro
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
##! OpenFlow plugin for the NetControl framework.
|
||||||
|
|
||||||
|
@load ../main
|
||||||
|
@load ../plugin
|
||||||
|
@load base/frameworks/openflow
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
export {
|
||||||
|
type OfConfig: record {
|
||||||
|
monitor: bool &default=T;
|
||||||
|
forward: bool &default=T;
|
||||||
|
idle_timeout: count &default=0;
|
||||||
|
table_id: count &optional;
|
||||||
|
priority_offset: int &default=+0; ##< add this to all rule priorities. Can be useful if you want the openflow priorities be offset from the netcontrol priorities without having to write a filter function.
|
||||||
|
|
||||||
|
## Predicate that is called on rule insertion or removal.
|
||||||
|
##
|
||||||
|
## p: Current plugin state
|
||||||
|
##
|
||||||
|
## r: The rule to be inserted or removed
|
||||||
|
##
|
||||||
|
## Returns: T if the rule can be handled by the current backend, F otherwhise
|
||||||
|
check_pred: function(p: PluginState, r: Rule): bool &optional;
|
||||||
|
match_pred: function(p: PluginState, e: Entity, m: vector of OpenFlow::ofp_match): vector of OpenFlow::ofp_match &optional;
|
||||||
|
flow_mod_pred: function(p: PluginState, r: Rule, m: OpenFlow::ofp_flow_mod): OpenFlow::ofp_flow_mod &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
redef record PluginState += {
|
||||||
|
## OpenFlow controller for NetControl OpenFlow plugin
|
||||||
|
of_controller: OpenFlow::Controller &optional;
|
||||||
|
## OpenFlow configuration record that is passed on initialization
|
||||||
|
of_config: OfConfig &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OfTable: record {
|
||||||
|
p: PluginState;
|
||||||
|
r: Rule;
|
||||||
|
c: count &default=0; # how many replies did we see so far? needed for ids where we have multiple rules...
|
||||||
|
packet_count: count &default=0;
|
||||||
|
byte_count: count &default=0;
|
||||||
|
duration_sec: double &default=0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
## the time interval after which an openflow message is considered to be timed out
|
||||||
|
## and we delete it from our internal tracking.
|
||||||
|
const openflow_message_timeout = 20secs &redef;
|
||||||
|
|
||||||
|
## the time interval after we consider a flow timed out. This should be fairly high (or
|
||||||
|
## even disabled) if you expect a lot of long flows. However, one also will have state
|
||||||
|
## buildup for quite a while if keeping this around...
|
||||||
|
const openflow_flow_timeout = 24hrs &redef;
|
||||||
|
|
||||||
|
## Instantiates an openflow plugin for the NetControl framework.
|
||||||
|
global create_openflow: function(controller: OpenFlow::Controller, config: OfConfig &default=[]) : PluginState;
|
||||||
|
}
|
||||||
|
|
||||||
|
global of_messages: table[count, OpenFlow::ofp_flow_mod_command] of OfTable &create_expire=openflow_message_timeout
|
||||||
|
&expire_func=function(t: table[count, OpenFlow::ofp_flow_mod_command] of OfTable, idx: any): interval
|
||||||
|
{
|
||||||
|
local rid: count;
|
||||||
|
local command: OpenFlow::ofp_flow_mod_command;
|
||||||
|
[rid, command] = idx;
|
||||||
|
|
||||||
|
local p = t[rid, command]$p;
|
||||||
|
local r = t[rid, command]$r;
|
||||||
|
event NetControl::rule_error(r, p, "Timeout during rule insertion/removal");
|
||||||
|
return 0secs;
|
||||||
|
};
|
||||||
|
|
||||||
|
global of_flows: table[count] of OfTable &create_expire=openflow_flow_timeout;
|
||||||
|
global of_instances: table[string] of PluginState;
|
||||||
|
|
||||||
|
function openflow_name(p: PluginState) : string
|
||||||
|
{
|
||||||
|
return fmt("Openflow-%s", p$of_controller$describe(p$of_controller$state));
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_check_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
local c = p$of_config;
|
||||||
|
|
||||||
|
if ( p$of_config?$check_pred )
|
||||||
|
return p$of_config$check_pred(p, r);
|
||||||
|
|
||||||
|
if ( r$target == MONITOR && c$monitor )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
if ( r$target == FORWARD && c$forward )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_match_pred(p: PluginState, e: Entity, m: vector of OpenFlow::ofp_match) : vector of OpenFlow::ofp_match
|
||||||
|
{
|
||||||
|
if ( p$of_config?$match_pred )
|
||||||
|
return p$of_config$match_pred(p, e, m);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_flow_mod_pred(p: PluginState, r: Rule, m: OpenFlow::ofp_flow_mod): OpenFlow::ofp_flow_mod
|
||||||
|
{
|
||||||
|
if ( p$of_config?$flow_mod_pred )
|
||||||
|
return p$of_config$flow_mod_pred(p, r, m);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
function determine_dl_type(s: subnet): count
|
||||||
|
{
|
||||||
|
local pdl = OpenFlow::ETH_IPv4;
|
||||||
|
if ( is_v6_subnet(s) )
|
||||||
|
pdl = OpenFlow::ETH_IPv6;
|
||||||
|
|
||||||
|
return pdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function determine_proto(p: port): count
|
||||||
|
{
|
||||||
|
local proto = OpenFlow::IP_TCP;
|
||||||
|
if ( is_udp_port(p) )
|
||||||
|
proto = OpenFlow::IP_UDP;
|
||||||
|
else if ( is_icmp_port(p) )
|
||||||
|
proto = OpenFlow::IP_ICMP;
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entity_to_match(p: PluginState, e: Entity): vector of OpenFlow::ofp_match
|
||||||
|
{
|
||||||
|
local v : vector of OpenFlow::ofp_match = vector();
|
||||||
|
|
||||||
|
if ( e$ty == CONNECTION )
|
||||||
|
{
|
||||||
|
v[|v|] = OpenFlow::match_conn(e$conn); # forward and...
|
||||||
|
v[|v|] = OpenFlow::match_conn(e$conn, T); # reverse
|
||||||
|
return openflow_match_pred(p, e, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( e$ty == MAC )
|
||||||
|
{
|
||||||
|
v[|v|] = OpenFlow::ofp_match(
|
||||||
|
$dl_src=e$mac
|
||||||
|
);
|
||||||
|
v[|v|] = OpenFlow::ofp_match(
|
||||||
|
$dl_dst=e$mac
|
||||||
|
);
|
||||||
|
|
||||||
|
return openflow_match_pred(p, e, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
local dl_type = OpenFlow::ETH_IPv4;
|
||||||
|
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
if ( is_v6_subnet(e$ip) )
|
||||||
|
dl_type = OpenFlow::ETH_IPv6;
|
||||||
|
|
||||||
|
v[|v|] = OpenFlow::ofp_match(
|
||||||
|
$dl_type=dl_type,
|
||||||
|
$nw_src=e$ip
|
||||||
|
);
|
||||||
|
|
||||||
|
v[|v|] = OpenFlow::ofp_match(
|
||||||
|
$dl_type=dl_type,
|
||||||
|
$nw_dst=e$ip
|
||||||
|
);
|
||||||
|
|
||||||
|
return openflow_match_pred(p, e, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
local proto = OpenFlow::IP_TCP;
|
||||||
|
|
||||||
|
if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
local m = OpenFlow::ofp_match();
|
||||||
|
local f = e$flow;
|
||||||
|
|
||||||
|
if ( f?$src_m )
|
||||||
|
m$dl_src=f$src_m;
|
||||||
|
if ( f?$dst_m )
|
||||||
|
m$dl_dst=f$dst_m;
|
||||||
|
|
||||||
|
if ( f?$src_h )
|
||||||
|
{
|
||||||
|
m$dl_type = determine_dl_type(f$src_h);
|
||||||
|
m$nw_src = f$src_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( f?$dst_h )
|
||||||
|
{
|
||||||
|
m$dl_type = determine_dl_type(f$dst_h);
|
||||||
|
m$nw_dst = f$dst_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( f?$src_p )
|
||||||
|
{
|
||||||
|
m$nw_proto = determine_proto(f$src_p);
|
||||||
|
m$tp_src = port_to_count(f$src_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( f?$dst_p )
|
||||||
|
{
|
||||||
|
m$nw_proto = determine_proto(f$dst_p);
|
||||||
|
m$tp_dst = port_to_count(f$dst_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
v[|v|] = m;
|
||||||
|
|
||||||
|
return openflow_match_pred(p, e, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reporter::error(fmt("Entity type %s not supported for openflow yet", cat(e$ty)));
|
||||||
|
return openflow_match_pred(p, e, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_rule_to_flow_mod(p: PluginState, r: Rule) : OpenFlow::ofp_flow_mod
|
||||||
|
{
|
||||||
|
local c = p$of_config;
|
||||||
|
|
||||||
|
local flow_mod = OpenFlow::ofp_flow_mod(
|
||||||
|
$cookie=OpenFlow::generate_cookie(r$cid*2), # leave one space for the cases in which we need two rules.
|
||||||
|
$command=OpenFlow::OFPFC_ADD,
|
||||||
|
$idle_timeout=c$idle_timeout,
|
||||||
|
$priority=int_to_count(r$priority + c$priority_offset),
|
||||||
|
$flags=OpenFlow::OFPFF_SEND_FLOW_REM # please notify us when flows are removed
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( r?$expire )
|
||||||
|
flow_mod$hard_timeout = double_to_count(interval_to_double(r$expire));
|
||||||
|
if ( c?$table_id )
|
||||||
|
flow_mod$table_id = c$table_id;
|
||||||
|
|
||||||
|
if ( r$ty == DROP )
|
||||||
|
{
|
||||||
|
# default, nothing to do. We simply do not add an output port to the rule...
|
||||||
|
}
|
||||||
|
else if ( r$ty == WHITELIST )
|
||||||
|
{
|
||||||
|
# at the moment our interpretation of whitelist is to hand this off to the switches L2/L3 routing.
|
||||||
|
flow_mod$actions$out_ports = vector(OpenFlow::OFPP_NORMAL);
|
||||||
|
}
|
||||||
|
else if ( r$ty == MODIFY )
|
||||||
|
{
|
||||||
|
# if no ports are given, just assume normal pipeline...
|
||||||
|
flow_mod$actions$out_ports = vector(OpenFlow::OFPP_NORMAL);
|
||||||
|
|
||||||
|
local mod = r$mod;
|
||||||
|
if ( mod?$redirect_port )
|
||||||
|
flow_mod$actions$out_ports = vector(mod$redirect_port);
|
||||||
|
|
||||||
|
if ( mod?$src_h )
|
||||||
|
flow_mod$actions$nw_src = mod$src_h;
|
||||||
|
if ( mod?$dst_h )
|
||||||
|
flow_mod$actions$nw_dst = mod$dst_h;
|
||||||
|
if ( mod?$src_m )
|
||||||
|
flow_mod$actions$dl_src = mod$src_m;
|
||||||
|
if ( mod?$dst_m )
|
||||||
|
flow_mod$actions$dl_dst = mod$dst_m;
|
||||||
|
if ( mod?$src_p )
|
||||||
|
flow_mod$actions$tp_src = mod$src_p;
|
||||||
|
if ( mod?$dst_p )
|
||||||
|
flow_mod$actions$tp_dst = mod$dst_p;
|
||||||
|
}
|
||||||
|
else if ( r$ty == REDIRECT )
|
||||||
|
{
|
||||||
|
# redirect to port c
|
||||||
|
flow_mod$actions$out_ports = vector(r$out_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("Rule type %s not supported for openflow yet", cat(r$ty)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return openflow_flow_mod_pred(p, r, flow_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_add_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! openflow_check_rule(p, r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local flow_mod = openflow_rule_to_flow_mod(p, r);
|
||||||
|
local matches = entity_to_match(p, r$entity);
|
||||||
|
|
||||||
|
for ( i in matches )
|
||||||
|
{
|
||||||
|
if ( OpenFlow::flow_mod(p$of_controller, matches[i], flow_mod) )
|
||||||
|
{
|
||||||
|
of_messages[r$cid, flow_mod$command] = OfTable($p=p, $r=r);
|
||||||
|
flow_mod = copy(flow_mod);
|
||||||
|
++flow_mod$cookie;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_remove_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! openflow_check_rule(p, r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local flow_mod: OpenFlow::ofp_flow_mod = [
|
||||||
|
$cookie=OpenFlow::generate_cookie(r$cid*2),
|
||||||
|
$command=OpenFlow::OFPFC_DELETE
|
||||||
|
];
|
||||||
|
|
||||||
|
if ( OpenFlow::flow_mod(p$of_controller, [], flow_mod) )
|
||||||
|
of_messages[r$cid, flow_mod$command] = OfTable($p=p, $r=r);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
# if this was an address or mac match, we also need to remove the reverse
|
||||||
|
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||||
|
{
|
||||||
|
local flow_mod_2 = copy(flow_mod);
|
||||||
|
++flow_mod_2$cookie;
|
||||||
|
OpenFlow::flow_mod(p$of_controller, [], flow_mod_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
event OpenFlow::flow_mod_success(name: string, match: OpenFlow::ofp_match, flow_mod: OpenFlow::ofp_flow_mod, msg: string) &priority=3
|
||||||
|
{
|
||||||
|
local id = OpenFlow::get_cookie_uid(flow_mod$cookie)/2;
|
||||||
|
if ( [id, flow_mod$command] !in of_messages )
|
||||||
|
return;
|
||||||
|
|
||||||
|
local r = of_messages[id,flow_mod$command]$r;
|
||||||
|
local p = of_messages[id,flow_mod$command]$p;
|
||||||
|
local c = of_messages[id,flow_mod$command]$c;
|
||||||
|
|
||||||
|
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||||
|
{
|
||||||
|
++of_messages[id,flow_mod$command]$c;
|
||||||
|
if ( of_messages[id,flow_mod$command]$c < 2 )
|
||||||
|
return; # will do stuff once the second part arrives...
|
||||||
|
}
|
||||||
|
|
||||||
|
delete of_messages[id,flow_mod$command];
|
||||||
|
|
||||||
|
if ( p$of_controller$supports_flow_removed )
|
||||||
|
of_flows[id] = OfTable($p=p, $r=r);
|
||||||
|
|
||||||
|
if ( flow_mod$command == OpenFlow::OFPFC_ADD )
|
||||||
|
event NetControl::rule_added(r, p, msg);
|
||||||
|
else if ( flow_mod$command == OpenFlow::OFPFC_DELETE || flow_mod$command == OpenFlow::OFPFC_DELETE_STRICT )
|
||||||
|
event NetControl::rule_removed(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event OpenFlow::flow_mod_failure(name: string, match: OpenFlow::ofp_match, flow_mod: OpenFlow::ofp_flow_mod, msg: string) &priority=3
|
||||||
|
{
|
||||||
|
local id = OpenFlow::get_cookie_uid(flow_mod$cookie)/2;
|
||||||
|
if ( [id, flow_mod$command] !in of_messages )
|
||||||
|
return;
|
||||||
|
|
||||||
|
local r = of_messages[id,flow_mod$command]$r;
|
||||||
|
local p = of_messages[id,flow_mod$command]$p;
|
||||||
|
delete of_messages[id,flow_mod$command];
|
||||||
|
|
||||||
|
event NetControl::rule_error(r, p, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
event OpenFlow::flow_removed(name: string, match: OpenFlow::ofp_match, cookie: count, priority: count, reason: count, duration_sec: count, idle_timeout: count, packet_count: count, byte_count: count)
|
||||||
|
{
|
||||||
|
local id = OpenFlow::get_cookie_uid(cookie)/2;
|
||||||
|
if ( id !in of_flows )
|
||||||
|
return;
|
||||||
|
|
||||||
|
local rec = of_flows[id];
|
||||||
|
local r = rec$r;
|
||||||
|
local p = rec$p;
|
||||||
|
|
||||||
|
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||||
|
{
|
||||||
|
++of_flows[id]$c;
|
||||||
|
if ( of_flows[id]$c < 2 )
|
||||||
|
return; # will do stuff once the second part arrives...
|
||||||
|
else
|
||||||
|
event NetControl::rule_timeout(r, FlowInfo($duration=double_to_interval((rec$duration_sec+duration_sec)/2), $packet_count=packet_count+rec$packet_count, $byte_count=byte_count+rec$byte_count), p);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event NetControl::rule_timeout(r, FlowInfo($duration=double_to_interval(duration_sec+0.0), $packet_count=packet_count, $byte_count=byte_count), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openflow_init(p: PluginState)
|
||||||
|
{
|
||||||
|
local name = p$of_controller$state$_name;
|
||||||
|
if ( name in of_instances )
|
||||||
|
Reporter::error(fmt("OpenFlow instance %s added to NetControl twice.", name));
|
||||||
|
|
||||||
|
of_instances[name] = p;
|
||||||
|
|
||||||
|
# let's check, if our OpenFlow controller is already active. If not, we have to wait for it to become active.
|
||||||
|
if ( p$of_controller$state$_activated )
|
||||||
|
plugin_activated(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
event OpenFlow::controller_activated(name: string, controller: OpenFlow::Controller)
|
||||||
|
{
|
||||||
|
if ( name in of_instances )
|
||||||
|
plugin_activated(of_instances[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
global openflow_plugin = Plugin(
|
||||||
|
$name=openflow_name,
|
||||||
|
$can_expire = T,
|
||||||
|
$init = openflow_init,
|
||||||
|
# $done = openflow_done,
|
||||||
|
$add_rule = openflow_add_rule,
|
||||||
|
$remove_rule = openflow_remove_rule
|
||||||
|
# $transaction_begin = openflow_transaction_begin,
|
||||||
|
# $transaction_end = openflow_transaction_end
|
||||||
|
);
|
||||||
|
|
||||||
|
function create_openflow(controller: OpenFlow::Controller, config: OfConfig &default=[]) : PluginState
|
||||||
|
{
|
||||||
|
local p: PluginState = [$plugin=openflow_plugin, $of_controller=controller, $of_config=config];
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
113
scripts/base/frameworks/netcontrol/plugins/packetfilter.bro
Normal file
113
scripts/base/frameworks/netcontrol/plugins/packetfilter.bro
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
##! NetControl plugin for the process-level PacketFilter that comes with
|
||||||
|
##! Bro. Since the PacketFilter in Bro is quite limited in scope
|
||||||
|
##! and can only add/remove filters for addresses, this is quite
|
||||||
|
##! limited in scope at the moment.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ../plugin
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Instantiates the packetfilter plugin.
|
||||||
|
global create_packetfilter: function() : PluginState;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we can handle this rule. If it specifies ports or
|
||||||
|
# anything Bro cannot handle, simply ignore it for now.
|
||||||
|
function packetfilter_check_rule(r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( r$ty != DROP )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( r$target != MONITOR )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local e = r$entity;
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
if ( e$ty != FLOW ) # everything else requires ports or MAC stuff
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( e$flow?$src_p || e$flow?$dst_p || e$flow?$src_m || e$flow?$dst_m )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function packetfilter_add_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! packetfilter_check_rule(r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local e = r$entity;
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
install_src_net_filter(e$ip, 0, 1.0);
|
||||||
|
install_dst_net_filter(e$ip, 0, 1.0);
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
local f = e$flow;
|
||||||
|
if ( f?$src_h )
|
||||||
|
install_src_net_filter(f$src_h, 0, 1.0);
|
||||||
|
if ( f?$dst_h )
|
||||||
|
install_dst_net_filter(f$dst_h, 0, 1.0);
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function packetfilter_remove_rule(p: PluginState, r: Rule) : bool
|
||||||
|
{
|
||||||
|
if ( ! packetfilter_check_rule(r) )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
local e = r$entity;
|
||||||
|
if ( e$ty == ADDRESS )
|
||||||
|
{
|
||||||
|
uninstall_src_net_filter(e$ip);
|
||||||
|
uninstall_dst_net_filter(e$ip);
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( e$ty == FLOW )
|
||||||
|
{
|
||||||
|
local f = e$flow;
|
||||||
|
if ( f?$src_h )
|
||||||
|
uninstall_src_net_filter(f$src_h);
|
||||||
|
if ( f?$dst_h )
|
||||||
|
uninstall_dst_net_filter(f$dst_h);
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function packetfilter_name(p: PluginState) : string
|
||||||
|
{
|
||||||
|
return "Packetfilter";
|
||||||
|
}
|
||||||
|
|
||||||
|
global packetfilter_plugin = Plugin(
|
||||||
|
$name=packetfilter_name,
|
||||||
|
$can_expire = F,
|
||||||
|
# $init = packetfilter_init,
|
||||||
|
# $done = packetfilter_done,
|
||||||
|
$add_rule = packetfilter_add_rule,
|
||||||
|
$remove_rule = packetfilter_remove_rule
|
||||||
|
);
|
||||||
|
|
||||||
|
function create_packetfilter() : PluginState
|
||||||
|
{
|
||||||
|
local p: PluginState = [$plugin=packetfilter_plugin];
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
69
scripts/base/frameworks/netcontrol/shunt.bro
Normal file
69
scripts/base/frameworks/netcontrol/shunt.bro
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
##! Implementation of the shunt functionality for NetControl.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Log::ID += { SHUNT };
|
||||||
|
|
||||||
|
## Stops forwarding a uni-directional flow's packets to Bro.
|
||||||
|
##
|
||||||
|
## f: The flow to shunt.
|
||||||
|
##
|
||||||
|
## t: How long to leave the shunt in place, with 0 being indefinitly.
|
||||||
|
##
|
||||||
|
## location: An optional string describing where the shunt was triggered.
|
||||||
|
##
|
||||||
|
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||||
|
global shunt_flow: function(f: flow_id, t: interval, location: string &default="") : string;
|
||||||
|
|
||||||
|
type ShuntInfo: record {
|
||||||
|
## Time at which the recorded activity occurred.
|
||||||
|
ts: time &log;
|
||||||
|
## ID of the rule; unique during each Bro run
|
||||||
|
rule_id: string &log;
|
||||||
|
## Flow ID of the shunted flow
|
||||||
|
f: flow_id &log;
|
||||||
|
## Expiry time of the shunt
|
||||||
|
expire: interval &log;
|
||||||
|
## Location where the underlying action was triggered.
|
||||||
|
location: string &log &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Event that can be handled to access the :bro:type:`NetControl::ShuntInfo`
|
||||||
|
## record as it is sent on to the logging framework.
|
||||||
|
global log_netcontrol_shunt: event(rec: ShuntInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(NetControl::SHUNT, [$columns=ShuntInfo, $ev=log_netcontrol_shunt, $path="netcontrol_shunt"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shunt_flow(f: flow_id, t: interval, location: string &default="") : string
|
||||||
|
{
|
||||||
|
local flow = NetControl::Flow(
|
||||||
|
$src_h=addr_to_subnet(f$src_h),
|
||||||
|
$src_p=f$src_p,
|
||||||
|
$dst_h=addr_to_subnet(f$dst_h),
|
||||||
|
$dst_p=f$dst_p
|
||||||
|
);
|
||||||
|
local e: Entity = [$ty=FLOW, $flow=flow];
|
||||||
|
local r: Rule = [$ty=DROP, $target=MONITOR, $entity=e, $expire=t, $location=location];
|
||||||
|
|
||||||
|
local id = add_rule(r);
|
||||||
|
|
||||||
|
# Error should already be logged
|
||||||
|
if ( id == "" )
|
||||||
|
return id;
|
||||||
|
|
||||||
|
local log = ShuntInfo($ts=network_time(), $rule_id=id, $f=f, $expire=t);
|
||||||
|
if ( location != "" )
|
||||||
|
log$location=location;
|
||||||
|
|
||||||
|
Log::write(SHUNT, log);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
109
scripts/base/frameworks/netcontrol/types.bro
Normal file
109
scripts/base/frameworks/netcontrol/types.bro
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
##! Types used by the NetControl framework.
|
||||||
|
|
||||||
|
module NetControl;
|
||||||
|
|
||||||
|
export {
|
||||||
|
const default_priority: int = +0 &redef;
|
||||||
|
const whitelist_priority: int = +5 &redef;
|
||||||
|
|
||||||
|
## Type of a :bro:id:`Entity` for defining an action.
|
||||||
|
type EntityType: enum {
|
||||||
|
ADDRESS, ##< Activity involving a specific IP address.
|
||||||
|
CONNECTION, ##< All of a bi-directional connection's activity.
|
||||||
|
FLOW, ##< All of a uni-directional flow's activity. Can contain wildcards.
|
||||||
|
MAC, ##< Activity involving a MAC address.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Type of a :bro:id:`Flow` for defining a flow.
|
||||||
|
type Flow: record {
|
||||||
|
src_h: subnet &optional; ##< The source IP address/subnet.
|
||||||
|
src_p: port &optional; ##< The source port number.
|
||||||
|
dst_h: subnet &optional; ##< The destination IP address/subnet.
|
||||||
|
dst_p: port &optional; ##< The desintation port number.
|
||||||
|
src_m: string &optional; ##< The source MAC address.
|
||||||
|
dst_m: string &optional; ##< The destination MAC address.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Type defining the enity an :bro:id:`Rule` is operating on.
|
||||||
|
type Entity: record {
|
||||||
|
ty: EntityType; ##< Type of entity.
|
||||||
|
conn: conn_id &optional; ##< Used with :bro:id:`CONNECTION` .
|
||||||
|
flow: Flow &optional; ##< Used with :bro:id:`FLOW` .
|
||||||
|
ip: subnet &optional; ##< Used with bro:id:`ADDRESS`; can specifiy a CIDR subnet.
|
||||||
|
mac: string &optional; ##< Used with :bro:id:`MAC`.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Target of :bro:id:`Rule` action.
|
||||||
|
type TargetType: enum {
|
||||||
|
FORWARD, #< Apply rule actively to traffic on forwarding path.
|
||||||
|
MONITOR, #< Apply rule passively to traffic sent to Bro for monitoring.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Type of rules that the framework supports. Each type lists the
|
||||||
|
## :bro:id:`Rule` argument(s) it uses, if any.
|
||||||
|
##
|
||||||
|
## Plugins may extend this type to define their own.
|
||||||
|
type RuleType: enum {
|
||||||
|
## Stop forwarding all packets matching entity.
|
||||||
|
##
|
||||||
|
## No arguments.
|
||||||
|
DROP,
|
||||||
|
|
||||||
|
## Begin modifying all packets matching entity.
|
||||||
|
##
|
||||||
|
## .. todo::
|
||||||
|
## Define arguments.
|
||||||
|
MODIFY,
|
||||||
|
|
||||||
|
## Begin redirecting all packets matching entity.
|
||||||
|
##
|
||||||
|
## .. todo::
|
||||||
|
## c: output port to redirect traffic to.
|
||||||
|
REDIRECT,
|
||||||
|
|
||||||
|
## Whitelists all packets of an entity, meaning no restrictions will be applied.
|
||||||
|
## While whitelisting is the default if no rule matches an this can type can be
|
||||||
|
## used to override lower-priority rules that would otherwise take effect for the
|
||||||
|
## entity.
|
||||||
|
WHITELIST,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Type of a :bro:id:`FlowMod` for defining a flow modification action.
|
||||||
|
type FlowMod: record {
|
||||||
|
src_h: addr &optional; ##< The source IP address.
|
||||||
|
src_p: count &optional; ##< The source port number.
|
||||||
|
dst_h: addr &optional; ##< The destination IP address.
|
||||||
|
dst_p: count &optional; ##< The desintation port number.
|
||||||
|
src_m: string &optional; ##< The source MAC address.
|
||||||
|
dst_m: string &optional; ##< The destination MAC address.
|
||||||
|
redirect_port: count &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## A rule for the framework to put in place. Of all rules currently in
|
||||||
|
## place, the first match will be taken, sorted by priority. All
|
||||||
|
## further rules will be ignored.
|
||||||
|
type Rule: record {
|
||||||
|
ty: RuleType; ##< Type of rule.
|
||||||
|
target: TargetType; ##< Where to apply rule.
|
||||||
|
entity: Entity; ##< Entity to apply rule to.
|
||||||
|
expire: interval &optional; ##< Timeout after which to expire the rule.
|
||||||
|
priority: int &default=default_priority; ##< Priority if multiple rules match an entity (larger value is higher priority).
|
||||||
|
location: string &optional; ##< Optional string describing where/what installed the rule.
|
||||||
|
|
||||||
|
out_port: count &optional; ##< Argument for bro:id:`REDIRECT` rules.
|
||||||
|
mod: FlowMod &optional; ##< Argument for :bro:id:`MODIFY` rules.
|
||||||
|
|
||||||
|
id: string &default=""; ##< Internally determined unique ID for this rule. Will be set when added.
|
||||||
|
cid: count &default=0; ##< Internally determined unique numeric ID for this rule. Set when added.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Information of a flow that can be provided by switches when the flow times out.
|
||||||
|
## Currently this is heavily influenced by the data that OpenFlow returns by default.
|
||||||
|
## That being said - their design makes sense and this is probably the data one
|
||||||
|
## can expect to be available.
|
||||||
|
type FlowInfo: record {
|
||||||
|
duration: interval &optional; ##< total duration of the rule
|
||||||
|
packet_count: count &optional; ##< number of packets exchanged over connections matched by the rule
|
||||||
|
byte_count: count &optional; ##< total bytes exchanged over connections matched by the rule
|
||||||
|
};
|
||||||
|
}
|
13
scripts/base/frameworks/openflow/__load__.bro
Normal file
13
scripts/base/frameworks/openflow/__load__.bro
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@load ./consts
|
||||||
|
@load ./types
|
||||||
|
@load ./main
|
||||||
|
@load ./plugins
|
||||||
|
|
||||||
|
# The cluster framework must be loaded first.
|
||||||
|
@load base/frameworks/cluster
|
||||||
|
|
||||||
|
@if ( Cluster::is_enabled() )
|
||||||
|
@load ./cluster
|
||||||
|
@else
|
||||||
|
@load ./non-cluster
|
||||||
|
@endif
|
120
scripts/base/frameworks/openflow/cluster.bro
Normal file
120
scripts/base/frameworks/openflow/cluster.bro
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
##! Cluster support for the OpenFlow framework.
|
||||||
|
|
||||||
|
@load ./main
|
||||||
|
@load base/frameworks/cluster
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
export {
|
||||||
|
## This is the event used to transport flow_mod messages to the manager.
|
||||||
|
global cluster_flow_mod: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod);
|
||||||
|
|
||||||
|
## This is the event used to transport flow_clear messages to the manager.
|
||||||
|
global cluster_flow_clear: event(name: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Workers need ability to forward commands to manager.
|
||||||
|
redef Cluster::worker2manager_events += /OpenFlow::cluster_flow_(mod|clear)/;
|
||||||
|
|
||||||
|
# the flow_mod function wrapper
|
||||||
|
function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool
|
||||||
|
{
|
||||||
|
if ( ! controller?$flow_mod )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
return controller$flow_mod(controller$state, match, flow_mod);
|
||||||
|
else
|
||||||
|
event OpenFlow::cluster_flow_mod(controller$state$_name, match, flow_mod);
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flow_clear(controller: Controller): bool
|
||||||
|
{
|
||||||
|
if ( ! controller?$flow_clear )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
return controller$flow_clear(controller$state);
|
||||||
|
else
|
||||||
|
event OpenFlow::cluster_flow_clear(controller$state$_name);
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||||
|
event OpenFlow::cluster_flow_mod(name: string, match: ofp_match, flow_mod: ofp_flow_mod)
|
||||||
|
{
|
||||||
|
if ( name !in name_to_controller )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("OpenFlow controller %s not found in mapping on master", name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local c = name_to_controller[name];
|
||||||
|
|
||||||
|
if ( ! c$state$_activated )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( c?$flow_mod )
|
||||||
|
c$flow_mod(c$state, match, flow_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
event OpenFlow::cluster_flow_clear(name: string)
|
||||||
|
{
|
||||||
|
if ( name !in name_to_controller )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("OpenFlow controller %s not found in mapping on master", name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local c = name_to_controller[name];
|
||||||
|
|
||||||
|
if ( ! c$state$_activated )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( c?$flow_clear )
|
||||||
|
c$flow_clear(c$state);
|
||||||
|
}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
function register_controller(tpe: OpenFlow::Plugin, name: string, controller: Controller)
|
||||||
|
{
|
||||||
|
controller$state$_name = cat(tpe, name);
|
||||||
|
controller$state$_plugin = tpe;
|
||||||
|
|
||||||
|
# we only run the init functions on the manager.
|
||||||
|
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||||
|
return;
|
||||||
|
|
||||||
|
register_controller_impl(tpe, name, controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregister_controller(controller: Controller)
|
||||||
|
{
|
||||||
|
# we only run the on the manager.
|
||||||
|
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||||
|
return;
|
||||||
|
|
||||||
|
unregister_controller_impl(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup_controller(name: string): vector of Controller
|
||||||
|
{
|
||||||
|
# we only run the on the manager. Otherwhise we don't have a mapping or state -> return empty
|
||||||
|
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||||
|
return vector();
|
||||||
|
|
||||||
|
# I am not quite sure if we can actually get away with this - in the
|
||||||
|
# current state, this means that the individual nodes cannot lookup
|
||||||
|
# a controller by name.
|
||||||
|
#
|
||||||
|
# This means that there can be no reactions to things on the actual
|
||||||
|
# worker nodes - because they cannot look up a name. On the other hand -
|
||||||
|
# currently we also do not even send the events to the worker nodes (at least
|
||||||
|
# not if we are using broker). Because of that I am not really feeling that
|
||||||
|
# badly about it...
|
||||||
|
|
||||||
|
return lookup_controller_impl(name);
|
||||||
|
}
|
229
scripts/base/frameworks/openflow/consts.bro
Normal file
229
scripts/base/frameworks/openflow/consts.bro
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
##! Constants used by the OpenFlow framework.
|
||||||
|
|
||||||
|
# All types/constants not specific to OpenFlow will be defined here
|
||||||
|
# unitl they somehow get into Bro.
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
# Some cookie specific constants.
|
||||||
|
# first 24 bits
|
||||||
|
const COOKIE_BID_SIZE = 16777216;
|
||||||
|
# start at bit 40 (1 << 40)
|
||||||
|
const COOKIE_BID_START = 1099511627776;
|
||||||
|
# bro specific cookie ID shall have the 42 bit set (1 << 42)
|
||||||
|
const BRO_COOKIE_ID = 4;
|
||||||
|
# 8 bits group identifier
|
||||||
|
const COOKIE_GID_SIZE = 256;
|
||||||
|
# start at bit 32 (1 << 32)
|
||||||
|
const COOKIE_GID_START = 4294967296;
|
||||||
|
# 32 bits unique identifier
|
||||||
|
const COOKIE_UID_SIZE = 4294967296;
|
||||||
|
# start at bit 0 (1 << 0)
|
||||||
|
const COOKIE_UID_START = 0;
|
||||||
|
|
||||||
|
export {
|
||||||
|
# All ethertypes can be found at
|
||||||
|
# http://standards.ieee.org/develop/regauth/ethertype/eth.txt
|
||||||
|
# but are not interesting for us at this point
|
||||||
|
#type ethertype: enum {
|
||||||
|
# Internet protocol version 4
|
||||||
|
const ETH_IPv4 = 0x0800;
|
||||||
|
# Address resolution protocol
|
||||||
|
const ETH_ARP = 0x0806;
|
||||||
|
# Wake on LAN
|
||||||
|
const ETH_WOL = 0x0842;
|
||||||
|
# Reverse address resolution protocol
|
||||||
|
const ETH_RARP = 0x8035;
|
||||||
|
# Appletalk
|
||||||
|
const ETH_APPLETALK = 0x809B;
|
||||||
|
# Appletalk address resolution protocol
|
||||||
|
const ETH_APPLETALK_ARP = 0x80F3;
|
||||||
|
# IEEE 802.1q & IEEE 802.1aq
|
||||||
|
const ETH_VLAN = 0x8100;
|
||||||
|
# Novell IPX old
|
||||||
|
const ETH_IPX_OLD = 0x8137;
|
||||||
|
# Novell IPX
|
||||||
|
const ETH_IPX = 0x8138;
|
||||||
|
# Internet protocol version 6
|
||||||
|
const ETH_IPv6 = 0x86DD;
|
||||||
|
# IEEE 802.3x
|
||||||
|
const ETH_ETHER_FLOW_CONTROL = 0x8808;
|
||||||
|
# Multiprotocol Label Switching unicast
|
||||||
|
const ETH_MPLS_UNICAST = 0x8847;
|
||||||
|
# Multiprotocol Label Switching multicast
|
||||||
|
const ETH_MPLS_MULTICAST = 0x8848;
|
||||||
|
# Point-to-point protocol over Ethernet discovery phase (rfc2516)
|
||||||
|
const ETH_PPPOE_DISCOVERY = 0x8863;
|
||||||
|
# Point-to-point protocol over Ethernet session phase (rfc2516)
|
||||||
|
const ETH_PPPOE_SESSION = 0x8864;
|
||||||
|
# Jumbo frames
|
||||||
|
const ETH_JUMBO_FRAMES = 0x8870;
|
||||||
|
# IEEE 802.1X
|
||||||
|
const ETH_EAP_OVER_LAN = 0x888E;
|
||||||
|
# IEEE 802.1ad & IEEE 802.1aq
|
||||||
|
const ETH_PROVIDER_BRIDING = 0x88A8;
|
||||||
|
# IEEE 802.1ae
|
||||||
|
const ETH_MAC_SECURITY = 0x88E5;
|
||||||
|
# IEEE 802.1ad (QinQ)
|
||||||
|
const ETH_QINQ = 0x9100;
|
||||||
|
#};
|
||||||
|
|
||||||
|
# A list of ip protocol numbers can be found at
|
||||||
|
# http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
|
||||||
|
#type iptype: enum {
|
||||||
|
# IPv6 Hop-by-Hop Option (RFC2460)
|
||||||
|
const IP_HOPOPT = 0x00;
|
||||||
|
# Internet Control Message Protocol (RFC792)
|
||||||
|
const IP_ICMP = 0x01;
|
||||||
|
# Internet Group Management Protocol (RFC1112)
|
||||||
|
const IP_IGMP = 0x02;
|
||||||
|
# Gateway-to-Gateway Protocol (RFC823)
|
||||||
|
const IP_GGP = 0x03;
|
||||||
|
# IP-Within-IP (encapsulation) (RFC2003)
|
||||||
|
const IP_IPIP = 0x04;
|
||||||
|
# Internet Stream Protocol (RFC1190;RFC1819)
|
||||||
|
const IP_ST = 0x05;
|
||||||
|
# Tansmission Control Protocol (RFC793)
|
||||||
|
const IP_TCP = 0x06;
|
||||||
|
# Core-based trees (RFC2189)
|
||||||
|
const IP_CBT = 0x07;
|
||||||
|
# Exterior Gateway Protocol (RFC888)
|
||||||
|
const IP_EGP = 0x08;
|
||||||
|
# Interior Gateway Protocol (any private interior
|
||||||
|
# gateway (used by Cisco for their IGRP))
|
||||||
|
const IP_IGP = 0x09;
|
||||||
|
# User Datagram Protocol (RFC768)
|
||||||
|
const IP_UDP = 0x11;
|
||||||
|
# Reliable Datagram Protocol (RFC908)
|
||||||
|
const IP_RDP = 0x1B;
|
||||||
|
# IPv6 Encapsulation (RFC2473)
|
||||||
|
const IP_IPv6 = 0x29;
|
||||||
|
# Resource Reservation Protocol (RFC2205)
|
||||||
|
const IP_RSVP = 0x2E;
|
||||||
|
# Generic Routing Encapsulation (RFC2784;RFC2890)
|
||||||
|
const IP_GRE = 0x2F;
|
||||||
|
# Open Shortest Path First (RFC1583)
|
||||||
|
const IP_OSPF = 0x59;
|
||||||
|
# Multicast Transport Protocol
|
||||||
|
const IP_MTP = 0x5C;
|
||||||
|
# IP-within-IP Encapsulation Protocol (RFC2003)
|
||||||
|
### error 0x5E;
|
||||||
|
# Ethernet-within-IP Encapsulation Protocol (RFC3378)
|
||||||
|
const IP_ETHERIP = 0x61;
|
||||||
|
# Layer Two Tunneling Protocol Version 3 (RFC3931)
|
||||||
|
const IP_L2TP = 0x73;
|
||||||
|
# Intermediate System to Intermediate System (IS-IS) Protocol over IPv4 (RFC1142;RFC1195)
|
||||||
|
const IP_ISIS = 0x7C;
|
||||||
|
# Fibre Channel
|
||||||
|
const IP_FC = 0x85;
|
||||||
|
# Multiprotocol Label Switching Encapsulated in IP (RFC4023)
|
||||||
|
const IP_MPLS = 0x89;
|
||||||
|
#};
|
||||||
|
|
||||||
|
## Return value for a cookie from a flow
|
||||||
|
## which is not added, modified or deleted
|
||||||
|
## from the bro openflow framework
|
||||||
|
const INVALID_COOKIE = 0xffffffffffffffff;
|
||||||
|
# Openflow pysical port definitions
|
||||||
|
## Send the packet out the input port. This
|
||||||
|
## virual port must be explicitly used in
|
||||||
|
## order to send back out of the input port.
|
||||||
|
const OFPP_IN_PORT = 0xfffffff8;
|
||||||
|
## Perform actions in flow table.
|
||||||
|
## NB: This can only be the destination port
|
||||||
|
## for packet-out messages.
|
||||||
|
const OFPP_TABLE = 0xfffffff9;
|
||||||
|
## Process with normal L2/L3 switching.
|
||||||
|
const OFPP_NORMAL = 0xfffffffa;
|
||||||
|
## All pysical ports except input port and
|
||||||
|
## those disabled by STP.
|
||||||
|
const OFPP_FLOOD = 0xfffffffb;
|
||||||
|
## All pysical ports except input port.
|
||||||
|
const OFPP_ALL = 0xfffffffc;
|
||||||
|
## Send to controller.
|
||||||
|
const OFPP_CONTROLLER = 0xfffffffd;
|
||||||
|
## Local openflow "port".
|
||||||
|
const OFPP_LOCAL = 0xfffffffe;
|
||||||
|
## Wildcard port used only for flow mod (delete) and flow stats requests.
|
||||||
|
const OFPP_ANY = 0xffffffff;
|
||||||
|
# Openflow no buffer constant.
|
||||||
|
const OFP_NO_BUFFER = 0xffffffff;
|
||||||
|
## Send flow removed message when flow
|
||||||
|
## expires or is deleted.
|
||||||
|
const OFPFF_SEND_FLOW_REM = 0x1;
|
||||||
|
## Check for overlapping entries first.
|
||||||
|
const OFPFF_CHECK_OVERLAP = 0x2;
|
||||||
|
## Remark this is for emergency.
|
||||||
|
## Flows added with this are only used
|
||||||
|
## when the controller is disconnected.
|
||||||
|
const OFPFF_EMERG = 0x4;
|
||||||
|
|
||||||
|
# Wildcard table used for table config,
|
||||||
|
# flow stats and flow deletes.
|
||||||
|
const OFPTT_ALL = 0xff;
|
||||||
|
|
||||||
|
## Openflow action_type definitions
|
||||||
|
##
|
||||||
|
## The openflow action type defines
|
||||||
|
## what actions openflow can take
|
||||||
|
## to modify a packet
|
||||||
|
type ofp_action_type: enum {
|
||||||
|
## Output to switch port.
|
||||||
|
OFPAT_OUTPUT = 0x0000,
|
||||||
|
## Set the 802.1q VLAN id.
|
||||||
|
OFPAT_SET_VLAN_VID = 0x0001,
|
||||||
|
## Set the 802.1q priority.
|
||||||
|
OFPAT_SET_VLAN_PCP = 0x0002,
|
||||||
|
## Strip the 802.1q header.
|
||||||
|
OFPAT_STRIP_VLAN = 0x0003,
|
||||||
|
## Ethernet source address.
|
||||||
|
OFPAT_SET_DL_SRC = 0x0004,
|
||||||
|
## Ethernet destination address.
|
||||||
|
OFPAT_SET_DL_DST = 0x0005,
|
||||||
|
## IP source address
|
||||||
|
OFPAT_SET_NW_SRC = 0x0006,
|
||||||
|
## IP destination address.
|
||||||
|
OFPAT_SET_NW_DST = 0x0007,
|
||||||
|
## IP ToS (DSCP field, 6 bits).
|
||||||
|
OFPAT_SET_NW_TOS = 0x0008,
|
||||||
|
## TCP/UDP source port.
|
||||||
|
OFPAT_SET_TP_SRC = 0x0009,
|
||||||
|
## TCP/UDP destination port.
|
||||||
|
OFPAT_SET_TP_DST = 0x000a,
|
||||||
|
## Output to queue.
|
||||||
|
OFPAT_ENQUEUE = 0x000b,
|
||||||
|
## Vendor specific
|
||||||
|
OFPAT_VENDOR = 0xffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Openflow flow_mod_command definitions
|
||||||
|
##
|
||||||
|
## The openflow flow_mod_command describes
|
||||||
|
## of what kind an action is.
|
||||||
|
type ofp_flow_mod_command: enum {
|
||||||
|
## New flow.
|
||||||
|
OFPFC_ADD = 0x0,
|
||||||
|
## Modify all matching flows.
|
||||||
|
OFPFC_MODIFY = 0x1,
|
||||||
|
## Modify entry strictly matching wildcards.
|
||||||
|
OFPFC_MODIFY_STRICT = 0x2,
|
||||||
|
## Delete all matching flows.
|
||||||
|
OFPFC_DELETE = 0x3,
|
||||||
|
## Strictly matching wildcards and priority.
|
||||||
|
OFPFC_DELETE_STRICT = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Openflow config flag definitions
|
||||||
|
##
|
||||||
|
## TODO: describe
|
||||||
|
type ofp_config_flags: enum {
|
||||||
|
## No special handling for fragments.
|
||||||
|
OFPC_FRAG_NORMAL = 0,
|
||||||
|
## Drop fragments.
|
||||||
|
OFPC_FRAG_DROP = 1,
|
||||||
|
## Reassemble (only if OFPC_IP_REASM set).
|
||||||
|
OFPC_FRAG_REASM = 2,
|
||||||
|
OFPC_FRAG_MASK = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
289
scripts/base/frameworks/openflow/main.bro
Normal file
289
scripts/base/frameworks/openflow/main.bro
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
##! Bro's OpenFlow control framework
|
||||||
|
##!
|
||||||
|
##! This plugin-based framework allows to control OpenFlow capable
|
||||||
|
##! switches by implementing communication to an OpenFlow controller
|
||||||
|
##! via plugins. The framework has to be instantiated via the new function
|
||||||
|
##! in one of the plugins. This framework only offers very low-level
|
||||||
|
##! functionality; if you want to use OpenFlow capable switches, e.g.,
|
||||||
|
##! for shunting, please look at the PACF framework, which provides higher
|
||||||
|
##! level functions and can use the OpenFlow framework as a backend.
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
@load ./consts
|
||||||
|
@load ./types
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Global flow_mod function.
|
||||||
|
##
|
||||||
|
## controller: The controller which should execute the flow modification
|
||||||
|
##
|
||||||
|
## match: The ofp_match record which describes the flow to match.
|
||||||
|
##
|
||||||
|
## flow_mod: The openflow flow_mod record which describes the action to take.
|
||||||
|
##
|
||||||
|
## Returns: F on error or if the plugin does not support the operation, T when the operation was queued.
|
||||||
|
global flow_mod: function(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool;
|
||||||
|
|
||||||
|
## Clear the current flow table of the controller.
|
||||||
|
##
|
||||||
|
## controller: The controller which should execute the flow modification
|
||||||
|
##
|
||||||
|
## Returns: F on error or if the plugin does not support the operation, T when the operation was queued.
|
||||||
|
global flow_clear: function(controller: Controller): bool;
|
||||||
|
|
||||||
|
## Event confirming successful modification of a flow rule.
|
||||||
|
##
|
||||||
|
## name: The unique name of the OpenFlow controller from which this event originated.
|
||||||
|
##
|
||||||
|
## match: The ofp_match record which describes the flow to match.
|
||||||
|
##
|
||||||
|
## flow_mod: The openflow flow_mod record which describes the action to take.
|
||||||
|
##
|
||||||
|
## msg: An optional informational message by the plugin.
|
||||||
|
global flow_mod_success: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default="");
|
||||||
|
|
||||||
|
## Reports an error while installing a flow Rule.
|
||||||
|
##
|
||||||
|
## name: The unique name of the OpenFlow controller from which this event originated.
|
||||||
|
##
|
||||||
|
## match: The ofp_match record which describes the flow to match.
|
||||||
|
##
|
||||||
|
## flow_mod: The openflow flow_mod record which describes the action to take.
|
||||||
|
##
|
||||||
|
## msg: Message to describe the event.
|
||||||
|
global flow_mod_failure: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default="");
|
||||||
|
|
||||||
|
## Reports that a flow was removed by the switch because of either the hard or the idle timeout.
|
||||||
|
## This message is only generated by controllers that indicate that they support flow removal
|
||||||
|
## in supports_flow_removed.
|
||||||
|
##
|
||||||
|
## name: The unique name of the OpenFlow controller from which this event originated.
|
||||||
|
##
|
||||||
|
## match: The ofp_match record which was used to create the flow.
|
||||||
|
##
|
||||||
|
## cookie: The cookie that was specified when creating the flow.
|
||||||
|
##
|
||||||
|
## priority: The priority that was specified when creating the flow.
|
||||||
|
##
|
||||||
|
## reason: The reason for flow removal (OFPRR_*)
|
||||||
|
##
|
||||||
|
## duration_sec: duration of the flow in seconds
|
||||||
|
##
|
||||||
|
## packet_count: packet count of the flow
|
||||||
|
##
|
||||||
|
## byte_count: byte count of the flow
|
||||||
|
global flow_removed: event(name: string, match: ofp_match, cookie: count, priority: count, reason: count, duration_sec: count, idle_timeout: count, packet_count: count, byte_count: count);
|
||||||
|
|
||||||
|
## Convert a conn_id record into an ofp_match record that can be used to
|
||||||
|
## create match objects for OpenFlow.
|
||||||
|
##
|
||||||
|
## id: the conn_id record that describes the record.
|
||||||
|
##
|
||||||
|
## reverse: reverse the sources and destinations when creating the match record (default F)
|
||||||
|
##
|
||||||
|
## Returns: ofp_match object for the conn_id record.
|
||||||
|
global match_conn: function(id: conn_id, reverse: bool &default=F): ofp_match;
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# ### Low-level functions for cookie handling and plugin registration.
|
||||||
|
# ###
|
||||||
|
|
||||||
|
## Function to get the unique id out of a given cookie.
|
||||||
|
##
|
||||||
|
## cookie: The openflow match cookie.
|
||||||
|
##
|
||||||
|
## Returns: The cookie unique id.
|
||||||
|
global get_cookie_uid: function(cookie: count): count;
|
||||||
|
|
||||||
|
## Function to get the group id out of a given cookie.
|
||||||
|
##
|
||||||
|
## cookie: The openflow match cookie.
|
||||||
|
##
|
||||||
|
## Returns: The cookie group id.
|
||||||
|
global get_cookie_gid: function(cookie: count): count;
|
||||||
|
|
||||||
|
## Function to generate a new cookie using our group id.
|
||||||
|
##
|
||||||
|
## cookie: The openflow match cookie.
|
||||||
|
##
|
||||||
|
## Returns: The cookie group id.
|
||||||
|
global generate_cookie: function(cookie: count &default=0): count;
|
||||||
|
|
||||||
|
## Function to register a controller instance. This function
|
||||||
|
## is called automatically by the plugin _new functions.
|
||||||
|
##
|
||||||
|
## tpe: type of this plugin
|
||||||
|
##
|
||||||
|
## name: unique name of this controller instance.
|
||||||
|
##
|
||||||
|
## controller: The controller to register
|
||||||
|
global register_controller: function(tpe: OpenFlow::Plugin, name: string, controller: Controller);
|
||||||
|
|
||||||
|
## Function to unregister a controller instance. This function
|
||||||
|
## should be called when a specific controller should no longer
|
||||||
|
## be used.
|
||||||
|
##
|
||||||
|
## controller: The controller to unregister
|
||||||
|
global unregister_controller: function(controller: Controller);
|
||||||
|
|
||||||
|
## Function to signal that a controller finished activation and is
|
||||||
|
## ready to use. Will throw the ``OpenFlow::controller_activated``
|
||||||
|
## event.
|
||||||
|
global controller_init_done: function(controller: Controller);
|
||||||
|
|
||||||
|
## Event that is raised once a controller finishes initialization
|
||||||
|
## and is completely activated.
|
||||||
|
## name: unique name of this controller instance.
|
||||||
|
##
|
||||||
|
## controller: The controller that finished activation.
|
||||||
|
global OpenFlow::controller_activated: event(name: string, controller: Controller);
|
||||||
|
|
||||||
|
## Function to lookup a controller instance by name
|
||||||
|
##
|
||||||
|
## name: unique name of the controller to look up
|
||||||
|
##
|
||||||
|
## Returns: one element vector with controller, if found. Empty vector otherwhise.
|
||||||
|
global lookup_controller: function(name: string): vector of Controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
global name_to_controller: table[string] of Controller;
|
||||||
|
|
||||||
|
|
||||||
|
function match_conn(id: conn_id, reverse: bool &default=F): ofp_match
|
||||||
|
{
|
||||||
|
local dl_type = ETH_IPv4;
|
||||||
|
local proto = IP_TCP;
|
||||||
|
|
||||||
|
local orig_h: addr;
|
||||||
|
local orig_p: port;
|
||||||
|
local resp_h: addr;
|
||||||
|
local resp_p: port;
|
||||||
|
|
||||||
|
if ( reverse == F )
|
||||||
|
{
|
||||||
|
orig_h = id$orig_h;
|
||||||
|
orig_p = id$orig_p;
|
||||||
|
resp_h = id$resp_h;
|
||||||
|
resp_p = id$resp_p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
orig_h = id$resp_h;
|
||||||
|
orig_p = id$resp_p;
|
||||||
|
resp_h = id$orig_h;
|
||||||
|
resp_p = id$orig_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_v6_addr(orig_h) )
|
||||||
|
dl_type = ETH_IPv6;
|
||||||
|
|
||||||
|
if ( is_udp_port(orig_p) )
|
||||||
|
proto = IP_UDP;
|
||||||
|
else if ( is_icmp_port(orig_p) )
|
||||||
|
proto = IP_ICMP;
|
||||||
|
|
||||||
|
return ofp_match(
|
||||||
|
$dl_type=dl_type,
|
||||||
|
$nw_proto=proto,
|
||||||
|
$nw_src=addr_to_subnet(orig_h),
|
||||||
|
$tp_src=port_to_count(orig_p),
|
||||||
|
$nw_dst=addr_to_subnet(resp_h),
|
||||||
|
$tp_dst=port_to_count(resp_p)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
# local function to forge a flow_mod cookie for this framework.
|
||||||
|
# all flow entries from the openflow framework should have the
|
||||||
|
# 42 bit of the cookie set.
|
||||||
|
function generate_cookie(cookie: count &default=0): count
|
||||||
|
{
|
||||||
|
local c = BRO_COOKIE_ID * COOKIE_BID_START;
|
||||||
|
|
||||||
|
if ( cookie >= COOKIE_UID_SIZE )
|
||||||
|
Reporter::warning(fmt("The given cookie uid '%d' is > 32bit and will be discarded", cookie));
|
||||||
|
else
|
||||||
|
c += cookie;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
# local function to check if a given flow_mod cookie is forged from this framework.
|
||||||
|
function is_valid_cookie(cookie: count): bool
|
||||||
|
{
|
||||||
|
if ( cookie / COOKIE_BID_START == BRO_COOKIE_ID )
|
||||||
|
return T;
|
||||||
|
|
||||||
|
Reporter::warning(fmt("The given Openflow cookie '%d' is not valid", cookie));
|
||||||
|
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_cookie_uid(cookie: count): count
|
||||||
|
{
|
||||||
|
if( is_valid_cookie(cookie) )
|
||||||
|
return (cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START));
|
||||||
|
|
||||||
|
return INVALID_COOKIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_cookie_gid(cookie: count): count
|
||||||
|
{
|
||||||
|
if( is_valid_cookie(cookie) )
|
||||||
|
return (
|
||||||
|
(cookie - (COOKIE_BID_START * BRO_COOKIE_ID) -
|
||||||
|
(cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START))) /
|
||||||
|
COOKIE_GID_START
|
||||||
|
);
|
||||||
|
|
||||||
|
return INVALID_COOKIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function controller_init_done(controller: Controller)
|
||||||
|
{
|
||||||
|
if ( controller$state$_name !in name_to_controller )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("Openflow initialized unknown plugin %s successfully?", controller$state$_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller$state$_activated = T;
|
||||||
|
event OpenFlow::controller_activated(controller$state$_name, controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Functions that are called from cluster.bro and non-cluster.bro
|
||||||
|
|
||||||
|
function register_controller_impl(tpe: OpenFlow::Plugin, name: string, controller: Controller)
|
||||||
|
{
|
||||||
|
if ( controller$state$_name in name_to_controller )
|
||||||
|
{
|
||||||
|
Reporter::error(fmt("OpenFlow Controller %s was already registered. Ignored duplicate registration", controller$state$_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name_to_controller[controller$state$_name] = controller;
|
||||||
|
|
||||||
|
if ( controller?$init )
|
||||||
|
controller$init(controller$state);
|
||||||
|
else
|
||||||
|
controller_init_done(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregister_controller_impl(controller: Controller)
|
||||||
|
{
|
||||||
|
if ( controller$state$_name in name_to_controller )
|
||||||
|
delete name_to_controller[controller$state$_name];
|
||||||
|
else
|
||||||
|
Reporter::error("OpenFlow Controller %s was not registered in unregister.");
|
||||||
|
|
||||||
|
if ( controller?$destroy )
|
||||||
|
controller$destroy(controller$state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup_controller_impl(name: string): vector of Controller
|
||||||
|
{
|
||||||
|
if ( name in name_to_controller )
|
||||||
|
return vector(name_to_controller[name]);
|
||||||
|
else
|
||||||
|
return vector();
|
||||||
|
}
|
44
scripts/base/frameworks/openflow/non-cluster.bro
Normal file
44
scripts/base/frameworks/openflow/non-cluster.bro
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
@load ./main
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
# the flow_mod function wrapper
|
||||||
|
function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool
|
||||||
|
{
|
||||||
|
if ( ! controller$state$_activated )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( controller?$flow_mod )
|
||||||
|
return controller$flow_mod(controller$state, match, flow_mod);
|
||||||
|
else
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flow_clear(controller: Controller): bool
|
||||||
|
{
|
||||||
|
if ( ! controller$state$_activated )
|
||||||
|
return F;
|
||||||
|
|
||||||
|
if ( controller?$flow_clear )
|
||||||
|
return controller$flow_clear(controller$state);
|
||||||
|
else
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
function register_controller(tpe: OpenFlow::Plugin, name: string, controller: Controller)
|
||||||
|
{
|
||||||
|
controller$state$_name = cat(tpe, name);
|
||||||
|
controller$state$_plugin = tpe;
|
||||||
|
|
||||||
|
register_controller_impl(tpe, name, controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregister_controller(controller: Controller)
|
||||||
|
{
|
||||||
|
unregister_controller_impl(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup_controller(name: string): vector of Controller
|
||||||
|
{
|
||||||
|
return lookup_controller_impl(name);
|
||||||
|
}
|
3
scripts/base/frameworks/openflow/plugins/__load__.bro
Normal file
3
scripts/base/frameworks/openflow/plugins/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@load ./ryu
|
||||||
|
@load ./log
|
||||||
|
@load ./broker
|
95
scripts/base/frameworks/openflow/plugins/broker.bro
Normal file
95
scripts/base/frameworks/openflow/plugins/broker.bro
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
##! OpenFlow plugin for interfacing to controllers via Broker.
|
||||||
|
|
||||||
|
@load base/frameworks/openflow
|
||||||
|
@load base/frameworks/broker
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Plugin += {
|
||||||
|
BROKER,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Broker controller constructor.
|
||||||
|
##
|
||||||
|
## host: Controller ip.
|
||||||
|
##
|
||||||
|
## host_port: Controller listen port.
|
||||||
|
##
|
||||||
|
## topic: broker topic to send messages to.
|
||||||
|
##
|
||||||
|
## dpid: OpenFlow switch datapath id.
|
||||||
|
##
|
||||||
|
## Returns: OpenFlow::Controller record
|
||||||
|
global broker_new: function(name: string, host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller;
|
||||||
|
|
||||||
|
redef record ControllerState += {
|
||||||
|
## Controller ip.
|
||||||
|
broker_host: addr &optional;
|
||||||
|
## Controller listen port.
|
||||||
|
broker_port: port &optional;
|
||||||
|
## OpenFlow switch datapath id.
|
||||||
|
broker_dpid: count &optional;
|
||||||
|
## Topic to sent events for this controller to
|
||||||
|
broker_topic: string &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
global broker_flow_mod: event(name: string, dpid: count, match: ofp_match, flow_mod: ofp_flow_mod);
|
||||||
|
global broker_flow_clear: event(name: string, dpid: count);
|
||||||
|
}
|
||||||
|
|
||||||
|
global broker_peers: table[port, string] of Controller;
|
||||||
|
|
||||||
|
function broker_describe(state: ControllerState): string
|
||||||
|
{
|
||||||
|
return fmt("Broker-%s:%d-%d", state$broker_host, state$broker_port, state$broker_dpid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_flow_mod_fun(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
|
||||||
|
{
|
||||||
|
Broker::event(state$broker_topic, Broker::event_args(broker_flow_mod, state$_name, state$broker_dpid, match, flow_mod));
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_flow_clear_fun(state: OpenFlow::ControllerState): bool
|
||||||
|
{
|
||||||
|
Broker::event(state$broker_topic, Broker::event_args(broker_flow_clear, state$_name, state$broker_dpid));
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function broker_init(state: OpenFlow::ControllerState)
|
||||||
|
{
|
||||||
|
Broker::enable();
|
||||||
|
Broker::connect(cat(state$broker_host), state$broker_port, 1sec);
|
||||||
|
Broker::subscribe_to_events(state$broker_topic); # openflow success and failure events are directly sent back via the other plugin via broker.
|
||||||
|
}
|
||||||
|
|
||||||
|
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
|
||||||
|
{
|
||||||
|
if ( [peer_port, peer_address] !in broker_peers )
|
||||||
|
# ok, this one was none of ours...
|
||||||
|
return;
|
||||||
|
|
||||||
|
local p = broker_peers[peer_port, peer_address];
|
||||||
|
controller_init_done(p);
|
||||||
|
delete broker_peers[peer_port, peer_address];
|
||||||
|
}
|
||||||
|
|
||||||
|
# broker controller constructor
|
||||||
|
function broker_new(name: string, host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller
|
||||||
|
{
|
||||||
|
local c = OpenFlow::Controller($state=OpenFlow::ControllerState($broker_host=host, $broker_port=host_port, $broker_dpid=dpid, $broker_topic=topic),
|
||||||
|
$flow_mod=broker_flow_mod_fun, $flow_clear=broker_flow_clear_fun, $describe=broker_describe, $supports_flow_removed=T, $init=broker_init);
|
||||||
|
|
||||||
|
register_controller(OpenFlow::BROKER, name, c);
|
||||||
|
|
||||||
|
if ( [host_port, cat(host)] in broker_peers )
|
||||||
|
Reporter::warning(fmt("Peer %s:%s was added to NetControl acld plugin twice.", host, host_port));
|
||||||
|
else
|
||||||
|
broker_peers[host_port, cat(host)] = c;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
76
scripts/base/frameworks/openflow/plugins/log.bro
Normal file
76
scripts/base/frameworks/openflow/plugins/log.bro
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
##! OpenFlow plugin that outputs flow-modification commands
|
||||||
|
##! to a Bro log file.
|
||||||
|
|
||||||
|
@load base/frameworks/openflow
|
||||||
|
@load base/frameworks/logging
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Plugin += {
|
||||||
|
OFLOG,
|
||||||
|
};
|
||||||
|
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
## Log controller constructor.
|
||||||
|
##
|
||||||
|
## dpid: OpenFlow switch datapath id.
|
||||||
|
##
|
||||||
|
## success_event: If true, flow_mod_success is raised for each logged line.
|
||||||
|
##
|
||||||
|
## Returns: OpenFlow::Controller record
|
||||||
|
global log_new: function(dpid: count, success_event: bool &default=T): OpenFlow::Controller;
|
||||||
|
|
||||||
|
redef record ControllerState += {
|
||||||
|
## OpenFlow switch datapath id.
|
||||||
|
log_dpid: count &optional;
|
||||||
|
## Raise or do not raise success event
|
||||||
|
log_success_event: bool &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## The record type which contains column fields of the OpenFlow log.
|
||||||
|
type Info: record {
|
||||||
|
## Network time
|
||||||
|
ts: time &log;
|
||||||
|
## OpenFlow switch datapath id
|
||||||
|
dpid: count &log;
|
||||||
|
## OpenFlow match fields
|
||||||
|
match: ofp_match &log;
|
||||||
|
## OpenFlow modify flow entry message
|
||||||
|
flow_mod: ofp_flow_mod &log;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Event that can be handled to access the :bro:type:`OpenFlow::Info`
|
||||||
|
## record as it is sent on to the logging framework.
|
||||||
|
global log_openflow: event(rec: Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(OpenFlow::LOG, [$columns=Info, $ev=log_openflow, $path="openflow"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_flow_mod(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
|
||||||
|
{
|
||||||
|
Log::write(OpenFlow::LOG, [$ts=network_time(), $dpid=state$log_dpid, $match=match, $flow_mod=flow_mod]);
|
||||||
|
if ( state$log_success_event )
|
||||||
|
event OpenFlow::flow_mod_success(state$_name, match, flow_mod);
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_describe(state: ControllerState): string
|
||||||
|
{
|
||||||
|
return fmt("Log-%d", state$log_dpid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_new(dpid: count, success_event: bool &default=T): OpenFlow::Controller
|
||||||
|
{
|
||||||
|
local c = OpenFlow::Controller($state=OpenFlow::ControllerState($log_dpid=dpid, $log_success_event=success_event),
|
||||||
|
$flow_mod=log_flow_mod, $describe=log_describe, $supports_flow_removed=F);
|
||||||
|
|
||||||
|
register_controller(OpenFlow::OFLOG, cat(dpid), c);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
190
scripts/base/frameworks/openflow/plugins/ryu.bro
Normal file
190
scripts/base/frameworks/openflow/plugins/ryu.bro
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
##! OpenFlow plugin for the Ryu controller.
|
||||||
|
|
||||||
|
@load base/frameworks/openflow
|
||||||
|
@load base/utils/active-http
|
||||||
|
@load base/utils/exec
|
||||||
|
@load base/utils/json
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Plugin += {
|
||||||
|
RYU,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Ryu controller constructor.
|
||||||
|
##
|
||||||
|
## host: Controller ip.
|
||||||
|
##
|
||||||
|
## host_port: Controller listen port.
|
||||||
|
##
|
||||||
|
## dpid: OpenFlow switch datapath id.
|
||||||
|
##
|
||||||
|
## Returns: OpenFlow::Controller record
|
||||||
|
global ryu_new: function(host: addr, host_port: count, dpid: count): OpenFlow::Controller;
|
||||||
|
|
||||||
|
redef record ControllerState += {
|
||||||
|
## Controller ip.
|
||||||
|
ryu_host: addr &optional;
|
||||||
|
## Controller listen port.
|
||||||
|
ryu_port: count &optional;
|
||||||
|
## OpenFlow switch datapath id.
|
||||||
|
ryu_dpid: count &optional;
|
||||||
|
## Enable debug mode - output JSON to stdout; do not perform actions
|
||||||
|
ryu_debug: bool &default=F;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ryu ReST API flow_mod URL-path
|
||||||
|
const RYU_FLOWENTRY_PATH = "/stats/flowentry/";
|
||||||
|
# Ryu ReST API flow_stats URL-path
|
||||||
|
#const RYU_FLOWSTATS_PATH = "/stats/flow/";
|
||||||
|
|
||||||
|
# Ryu ReST API action_output type.
|
||||||
|
type ryu_flow_action: record {
|
||||||
|
# Ryu uses strings as its ReST API output action.
|
||||||
|
_type: string;
|
||||||
|
# The output port for type OUTPUT
|
||||||
|
_port: count &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
# The ReST API documentation can be found at
|
||||||
|
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
|
||||||
|
# Ryu ReST API flow_mod type.
|
||||||
|
type ryu_ofp_flow_mod: record {
|
||||||
|
dpid: count;
|
||||||
|
cookie: count &optional;
|
||||||
|
cookie_mask: count &optional;
|
||||||
|
table_id: count &optional;
|
||||||
|
idle_timeout: count &optional;
|
||||||
|
hard_timeout: count &optional;
|
||||||
|
priority: count &optional;
|
||||||
|
flags: count &optional;
|
||||||
|
match: OpenFlow::ofp_match;
|
||||||
|
actions: vector of ryu_flow_action;
|
||||||
|
out_port: count &optional;
|
||||||
|
out_group: count &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Mapping between ofp flow mod commands and ryu urls
|
||||||
|
const ryu_url: table[ofp_flow_mod_command] of string = {
|
||||||
|
[OFPFC_ADD] = "add",
|
||||||
|
[OFPFC_MODIFY] = "modify",
|
||||||
|
[OFPFC_MODIFY_STRICT] = "modify_strict",
|
||||||
|
[OFPFC_DELETE] = "delete",
|
||||||
|
[OFPFC_DELETE_STRICT] = "delete_strict",
|
||||||
|
};
|
||||||
|
|
||||||
|
# Ryu flow_mod function
|
||||||
|
function ryu_flow_mod(state: OpenFlow::ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
|
||||||
|
{
|
||||||
|
if ( state$_plugin != RYU )
|
||||||
|
{
|
||||||
|
Reporter::error("Ryu openflow plugin was called with state of non-ryu plugin");
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate ryu_flow_actions because their type differs (using strings as type).
|
||||||
|
local flow_actions: vector of ryu_flow_action = vector();
|
||||||
|
|
||||||
|
for ( i in flow_mod$actions$out_ports )
|
||||||
|
flow_actions[|flow_actions|] = ryu_flow_action($_type="OUTPUT", $_port=flow_mod$actions$out_ports[i]);
|
||||||
|
|
||||||
|
# Generate our ryu_flow_mod record for the ReST API call.
|
||||||
|
local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod(
|
||||||
|
$dpid=state$ryu_dpid,
|
||||||
|
$cookie=flow_mod$cookie,
|
||||||
|
$idle_timeout=flow_mod$idle_timeout,
|
||||||
|
$hard_timeout=flow_mod$hard_timeout,
|
||||||
|
$priority=flow_mod$priority,
|
||||||
|
$flags=flow_mod$flags,
|
||||||
|
$match=match,
|
||||||
|
$actions=flow_actions
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( flow_mod?$out_port )
|
||||||
|
mod$out_port = flow_mod$out_port;
|
||||||
|
if ( flow_mod?$out_group )
|
||||||
|
mod$out_group = flow_mod$out_group;
|
||||||
|
|
||||||
|
# Type of the command
|
||||||
|
local command_type: string;
|
||||||
|
|
||||||
|
if ( flow_mod$command in ryu_url )
|
||||||
|
command_type = ryu_url[flow_mod$command];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Reporter::warning(fmt("The given OpenFlow command type '%s' is not available", cat(flow_mod$command)));
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, command_type);
|
||||||
|
|
||||||
|
if ( state$ryu_debug )
|
||||||
|
{
|
||||||
|
print url;
|
||||||
|
print to_json(mod);
|
||||||
|
event OpenFlow::flow_mod_success(state$_name, match, flow_mod);
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create the ActiveHTTP request and convert the record to a Ryu ReST API JSON string
|
||||||
|
local request: ActiveHTTP::Request = ActiveHTTP::Request(
|
||||||
|
$url=url,
|
||||||
|
$method="POST",
|
||||||
|
$client_data=to_json(mod)
|
||||||
|
);
|
||||||
|
|
||||||
|
# Execute call to Ryu's ReST API
|
||||||
|
when ( local result = ActiveHTTP::request(request) )
|
||||||
|
{
|
||||||
|
if(result$code == 200)
|
||||||
|
event OpenFlow::flow_mod_success(state$_name, match, flow_mod, result$body);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Reporter::warning(fmt("Flow modification failed with error: %s", result$body));
|
||||||
|
event OpenFlow::flow_mod_failure(state$_name, match, flow_mod, result$body);
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ryu_flow_clear(state: OpenFlow::ControllerState): bool
|
||||||
|
{
|
||||||
|
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, "clear", "/", state$ryu_dpid);
|
||||||
|
|
||||||
|
if ( state$ryu_debug )
|
||||||
|
{
|
||||||
|
print url;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
local request: ActiveHTTP::Request = ActiveHTTP::Request(
|
||||||
|
$url=url,
|
||||||
|
$method="DELETE"
|
||||||
|
);
|
||||||
|
|
||||||
|
when ( local result = ActiveHTTP::request(request) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ryu_describe(state: ControllerState): string
|
||||||
|
{
|
||||||
|
return fmt("Ryu-%d-http://%s:%d", state$ryu_dpid, state$ryu_host, state$ryu_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ryu controller constructor
|
||||||
|
function ryu_new(host: addr, host_port: count, dpid: count): OpenFlow::Controller
|
||||||
|
{
|
||||||
|
local c = OpenFlow::Controller($state=OpenFlow::ControllerState($ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid),
|
||||||
|
$flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear, $describe=ryu_describe, $supports_flow_removed=F);
|
||||||
|
|
||||||
|
register_controller(OpenFlow::RYU, cat(host,host_port,dpid), c);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
132
scripts/base/frameworks/openflow/types.bro
Normal file
132
scripts/base/frameworks/openflow/types.bro
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
##! Types used by the OpenFlow framework.
|
||||||
|
|
||||||
|
module OpenFlow;
|
||||||
|
|
||||||
|
@load ./consts
|
||||||
|
|
||||||
|
export {
|
||||||
|
## Available openflow plugins
|
||||||
|
type Plugin: enum {
|
||||||
|
## Internal placeholder plugin
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
## Controller related state.
|
||||||
|
## Can be redefined by plugins to
|
||||||
|
## add state.
|
||||||
|
type ControllerState: record {
|
||||||
|
## Internally set to the type of plugin used.
|
||||||
|
_plugin: Plugin &optional;
|
||||||
|
## Internally set to the unique name of the controller.
|
||||||
|
_name: string &optional;
|
||||||
|
## Internally set to true once the controller is activated
|
||||||
|
_activated: bool &default=F;
|
||||||
|
} &redef;
|
||||||
|
|
||||||
|
## Openflow match definition.
|
||||||
|
##
|
||||||
|
## The openflow match record describes
|
||||||
|
## which packets match to a specific
|
||||||
|
## rule in a flow table.
|
||||||
|
type ofp_match: record {
|
||||||
|
# Input switch port.
|
||||||
|
in_port: count &optional;
|
||||||
|
# Ethernet source address.
|
||||||
|
dl_src: string &optional;
|
||||||
|
# Ethernet destination address.
|
||||||
|
dl_dst: string &optional;
|
||||||
|
# Input VLAN id.
|
||||||
|
dl_vlan: count &optional;
|
||||||
|
# Input VLAN priority.
|
||||||
|
dl_vlan_pcp: count &optional;
|
||||||
|
# Ethernet frame type.
|
||||||
|
dl_type: count &optional;
|
||||||
|
# IP ToS (actually DSCP field, 6bits).
|
||||||
|
nw_tos: count &optional;
|
||||||
|
# IP protocol or lower 8 bits of ARP opcode.
|
||||||
|
nw_proto: count &optional;
|
||||||
|
# At the moment, we store both v4 and v6 in the same fields.
|
||||||
|
# This is not how OpenFlow does it, we might want to change that...
|
||||||
|
# IP source address.
|
||||||
|
nw_src: subnet &optional;
|
||||||
|
# IP destination address.
|
||||||
|
nw_dst: subnet &optional;
|
||||||
|
# TCP/UDP source port.
|
||||||
|
tp_src: count &optional;
|
||||||
|
# TCP/UDP destination port.
|
||||||
|
tp_dst: count &optional;
|
||||||
|
} &log;
|
||||||
|
|
||||||
|
## The actions that can be taken in a flow.
|
||||||
|
## (Sepearate record to make ofp_flow_mod less crowded)
|
||||||
|
type ofp_flow_action: record {
|
||||||
|
## Output ports to send data to.
|
||||||
|
out_ports: vector of count &default=vector();
|
||||||
|
## set vlan vid to this value
|
||||||
|
vlan_vid: count &optional;
|
||||||
|
## set vlan priority to this value
|
||||||
|
vlan_pcp: count &optional;
|
||||||
|
## strip vlan tag
|
||||||
|
vlan_strip: bool &default=F;
|
||||||
|
## set ethernet source address
|
||||||
|
dl_src: string &optional;
|
||||||
|
## set ethernet destination address
|
||||||
|
dl_dst: string &optional;
|
||||||
|
## set ip tos to this value
|
||||||
|
nw_tos: count &optional;
|
||||||
|
## set source to this ip
|
||||||
|
nw_src: addr &optional;
|
||||||
|
## set destination to this ip
|
||||||
|
nw_dst: addr &optional;
|
||||||
|
## set tcp/udp source port
|
||||||
|
tp_src: count &optional;
|
||||||
|
## set tcp/udp destination port
|
||||||
|
tp_dst: count &optional;
|
||||||
|
} &log;
|
||||||
|
|
||||||
|
## Openflow flow_mod definition, describing the action to perform.
|
||||||
|
type ofp_flow_mod: record {
|
||||||
|
## Opaque controller-issued identifier.
|
||||||
|
# This is optional in the specification - but let's force
|
||||||
|
# it so we always can identify our flows...
|
||||||
|
cookie: count; # &default=BRO_COOKIE_ID * COOKIE_BID_START;
|
||||||
|
# Flow actions
|
||||||
|
## Table to put the flow in. OFPTT_ALL can be used for delete,
|
||||||
|
## to delete flows from all matching tables.
|
||||||
|
table_id: count &optional;
|
||||||
|
## One of OFPFC_*.
|
||||||
|
command: ofp_flow_mod_command; # &default=OFPFC_ADD;
|
||||||
|
## Idle time before discarding (seconds).
|
||||||
|
idle_timeout: count &default=0;
|
||||||
|
## Max time before discarding (seconds).
|
||||||
|
hard_timeout: count &default=0;
|
||||||
|
## Priority level of flow entry.
|
||||||
|
priority: count &default=0;
|
||||||
|
## For OFPFC_DELETE* commands, require matching entried to include
|
||||||
|
## this as an output port/group. OFPP_ANY/OFPG_ANY means no restrictions.
|
||||||
|
out_port: count &optional;
|
||||||
|
out_group: count &optional;
|
||||||
|
## Bitmap of the OFPFF_* flags
|
||||||
|
flags: count &default=0;
|
||||||
|
## Actions to take on match
|
||||||
|
actions: ofp_flow_action &default=ofp_flow_action();
|
||||||
|
} &log;
|
||||||
|
|
||||||
|
## Controller record representing an openflow controller
|
||||||
|
type Controller: record {
|
||||||
|
## Controller related state.
|
||||||
|
state: ControllerState;
|
||||||
|
## Does the controller support the flow_removed event?
|
||||||
|
supports_flow_removed: bool;
|
||||||
|
## function that describes the controller. Has to be implemented.
|
||||||
|
describe: function(state: ControllerState): string;
|
||||||
|
## one-time initialization function. If defined, controller_init_done has to be called once initialization finishes.
|
||||||
|
init: function (state: ControllerState) &optional;
|
||||||
|
## one-time destruction function
|
||||||
|
destroy: function (state: ControllerState) &optional;
|
||||||
|
## flow_mod function
|
||||||
|
flow_mod: function(state: ControllerState, match: ofp_match, flow_mod: ofp_flow_mod): bool &optional;
|
||||||
|
## flow_clear function
|
||||||
|
flow_clear: function(state: ControllerState): bool &optional;
|
||||||
|
};
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ redef enum PcapFilterID += {
|
||||||
|
|
||||||
function test_filter(filter: string): bool
|
function test_filter(filter: string): bool
|
||||||
{
|
{
|
||||||
if ( ! precompile_pcap_filter(FilterTester, filter) )
|
if ( ! Pcap::precompile_pcap_filter(FilterTester, filter) )
|
||||||
{
|
{
|
||||||
# The given filter was invalid
|
# The given filter was invalid
|
||||||
# TODO: generate a notice.
|
# TODO: generate a notice.
|
||||||
|
@ -273,7 +273,7 @@ function install(): bool
|
||||||
return F;
|
return F;
|
||||||
|
|
||||||
local ts = current_time();
|
local ts = current_time();
|
||||||
if ( ! precompile_pcap_filter(DefaultPcapFilter, tmp_filter) )
|
if ( ! Pcap::precompile_pcap_filter(DefaultPcapFilter, tmp_filter) )
|
||||||
{
|
{
|
||||||
NOTICE([$note=Compile_Failure,
|
NOTICE([$note=Compile_Failure,
|
||||||
$msg=fmt("Compiling packet filter failed"),
|
$msg=fmt("Compiling packet filter failed"),
|
||||||
|
@ -303,7 +303,7 @@ function install(): bool
|
||||||
}
|
}
|
||||||
info$filter = current_filter;
|
info$filter = current_filter;
|
||||||
|
|
||||||
if ( ! install_pcap_filter(DefaultPcapFilter) )
|
if ( ! Pcap::install_pcap_filter(DefaultPcapFilter) )
|
||||||
{
|
{
|
||||||
# Installing the filter failed for some reason.
|
# Installing the filter failed for some reason.
|
||||||
info$success = F;
|
info$success = F;
|
||||||
|
|
|
@ -280,6 +280,13 @@ function parse_mozilla(unparsed_version: string): Description
|
||||||
v = parse(parts[1])$version;
|
v = parse(parts[1])$version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( /AdobeAIR\/[0-9\.]*/ in unparsed_version )
|
||||||
|
{
|
||||||
|
software_name = "AdobeAIR";
|
||||||
|
parts = split_string_all(unparsed_version, /AdobeAIR\/[0-9\.]*/);
|
||||||
|
if ( 1 in parts )
|
||||||
|
v = parse(parts[1])$version;
|
||||||
|
}
|
||||||
else if ( /AppleWebKit\/[0-9\.]*/ in unparsed_version )
|
else if ( /AppleWebKit\/[0-9\.]*/ in unparsed_version )
|
||||||
{
|
{
|
||||||
software_name = "Unspecified WebKit";
|
software_name = "Unspecified WebKit";
|
||||||
|
|
|
@ -39,6 +39,13 @@ type count_set: set[count];
|
||||||
## directly and then remove this alias.
|
## directly and then remove this alias.
|
||||||
type index_vec: vector of count;
|
type index_vec: vector of count;
|
||||||
|
|
||||||
|
## A vector of subnets.
|
||||||
|
##
|
||||||
|
## .. todo:: We need this type definition only for declaring builtin functions
|
||||||
|
## via ``bifcl``. We should extend ``bifcl`` to understand composite types
|
||||||
|
## directly and then remove this alias.
|
||||||
|
type subnet_vec: vector of subnet;
|
||||||
|
|
||||||
## A vector of any, used by some builtin functions to store a list of varying
|
## A vector of any, used by some builtin functions to store a list of varying
|
||||||
## types.
|
## types.
|
||||||
##
|
##
|
||||||
|
@ -120,6 +127,18 @@ type conn_id: record {
|
||||||
resp_p: port; ##< The responder's port number.
|
resp_p: port; ##< The responder's port number.
|
||||||
} &log;
|
} &log;
|
||||||
|
|
||||||
|
## The identifying 4-tuple of a uni-directional flow.
|
||||||
|
##
|
||||||
|
## .. note:: It's actually a 5-tuple: the transport-layer protocol is stored as
|
||||||
|
## part of the port values, `src_p` and `dst_p`, and can be extracted from
|
||||||
|
## them with :bro:id:`get_port_transport_proto`.
|
||||||
|
type flow_id : record {
|
||||||
|
src_h: addr; ##< The source IP address.
|
||||||
|
src_p: port; ##< The source port number.
|
||||||
|
dst_h: addr; ##< The destination IP address.
|
||||||
|
dst_p: port; ##< The desintation port number.
|
||||||
|
} &log;
|
||||||
|
|
||||||
## Specifics about an ICMP conversation. ICMP events typically pass this in
|
## Specifics about an ICMP conversation. ICMP events typically pass this in
|
||||||
## addition to :bro:type:`conn_id`.
|
## addition to :bro:type:`conn_id`.
|
||||||
##
|
##
|
||||||
|
@ -345,6 +364,12 @@ type connection: record {
|
||||||
## for the connection unless the :bro:id:`tunnel_changed` event is
|
## for the connection unless the :bro:id:`tunnel_changed` event is
|
||||||
## handled and reassigns this field to the new encapsulation.
|
## handled and reassigns this field to the new encapsulation.
|
||||||
tunnel: EncapsulatingConnVector &optional;
|
tunnel: EncapsulatingConnVector &optional;
|
||||||
|
|
||||||
|
## The outer VLAN, if applicable, for this connection.
|
||||||
|
vlan: int &optional;
|
||||||
|
|
||||||
|
## The inner VLAN, if applicable, for this connection.
|
||||||
|
inner_vlan: int &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
## Default amount of time a file can be inactive before the file analysis
|
## Default amount of time a file can be inactive before the file analysis
|
||||||
|
@ -768,71 +793,6 @@ type entropy_test_result: record {
|
||||||
serial_correlation: double; ##< Serial correlation coefficient.
|
serial_correlation: double; ##< Serial correlation coefficient.
|
||||||
};
|
};
|
||||||
|
|
||||||
# Prototypes of Bro built-in functions.
|
|
||||||
@load base/bif/strings.bif
|
|
||||||
@load base/bif/bro.bif
|
|
||||||
@load base/bif/reporter.bif
|
|
||||||
|
|
||||||
## Deprecated. This is superseded by the new logging framework.
|
|
||||||
global log_file_name: function(tag: string): string &redef;
|
|
||||||
|
|
||||||
## Deprecated. This is superseded by the new logging framework.
|
|
||||||
global open_log_file: function(tag: string): file &redef;
|
|
||||||
|
|
||||||
## Specifies a directory for Bro to store its persistent state. All globals can
|
|
||||||
## be declared persistent via the :bro:attr:`&persistent` attribute.
|
|
||||||
const state_dir = ".state" &redef;
|
|
||||||
|
|
||||||
## Length of the delays inserted when storing state incrementally. To avoid
|
|
||||||
## dropping packets when serializing larger volumes of persistent state to
|
|
||||||
## disk, Bro interleaves the operation with continued packet processing.
|
|
||||||
const state_write_delay = 0.01 secs &redef;
|
|
||||||
|
|
||||||
global done_with_network = F;
|
|
||||||
event net_done(t: time) { done_with_network = T; }
|
|
||||||
|
|
||||||
function log_file_name(tag: string): string
|
|
||||||
{
|
|
||||||
local suffix = getenv("BRO_LOG_SUFFIX") == "" ? "log" : getenv("BRO_LOG_SUFFIX");
|
|
||||||
return fmt("%s.%s", tag, suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
function open_log_file(tag: string): file
|
|
||||||
{
|
|
||||||
return open(log_file_name(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
## Internal function.
|
|
||||||
function add_interface(iold: string, inew: string): string
|
|
||||||
{
|
|
||||||
if ( iold == "" )
|
|
||||||
return inew;
|
|
||||||
else
|
|
||||||
return fmt("%s %s", iold, inew);
|
|
||||||
}
|
|
||||||
|
|
||||||
## Network interfaces to listen on. Use ``redef interfaces += "eth0"`` to
|
|
||||||
## extend.
|
|
||||||
global interfaces = "" &add_func = add_interface;
|
|
||||||
|
|
||||||
## Internal function.
|
|
||||||
function add_signature_file(sold: string, snew: string): string
|
|
||||||
{
|
|
||||||
if ( sold == "" )
|
|
||||||
return snew;
|
|
||||||
else
|
|
||||||
return cat(sold, " ", snew);
|
|
||||||
}
|
|
||||||
|
|
||||||
## Signature files to read. Use ``redef signature_files += "foo.sig"`` to
|
|
||||||
## extend. Signature files added this way will be searched relative to
|
|
||||||
## ``BROPATH``. Using the ``@load-sigs`` directive instead is preferred
|
|
||||||
## since that can search paths relative to the current script.
|
|
||||||
global signature_files = "" &add_func = add_signature_file;
|
|
||||||
|
|
||||||
## ``p0f`` fingerprint file to use. Will be searched relative to ``BROPATH``.
|
|
||||||
const passive_fingerprint_file = "base/misc/p0f.fp" &redef;
|
|
||||||
|
|
||||||
# TCP values for :bro:see:`endpoint` *state* field.
|
# TCP values for :bro:see:`endpoint` *state* field.
|
||||||
# todo:: these should go into an enum to make them autodoc'able.
|
# todo:: these should go into an enum to make them autodoc'able.
|
||||||
const TCP_INACTIVE = 0; ##< Endpoint is still inactive.
|
const TCP_INACTIVE = 0; ##< Endpoint is still inactive.
|
||||||
|
@ -1511,6 +1471,7 @@ type l2_hdr: record {
|
||||||
src: string &optional; ##< L2 source (if Ethernet).
|
src: string &optional; ##< L2 source (if Ethernet).
|
||||||
dst: string &optional; ##< L2 destination (if Ethernet).
|
dst: string &optional; ##< L2 destination (if Ethernet).
|
||||||
vlan: count &optional; ##< Outermost VLAN tag if any (and Ethernet).
|
vlan: count &optional; ##< Outermost VLAN tag if any (and Ethernet).
|
||||||
|
inner_vlan: count &optional; ##< Innermost VLAN tag if any (and Ethernet).
|
||||||
eth_type: count &optional; ##< Innermost Ethertype (if Ethernet).
|
eth_type: count &optional; ##< Innermost Ethertype (if Ethernet).
|
||||||
proto: layer3_proto; ##< L3 protocol.
|
proto: layer3_proto; ##< L3 protocol.
|
||||||
};
|
};
|
||||||
|
@ -1742,6 +1703,71 @@ type gtp_delete_pdp_ctx_response_elements: record {
|
||||||
ext: gtp_private_extension &optional;
|
ext: gtp_private_extension &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Prototypes of Bro built-in functions.
|
||||||
|
@load base/bif/strings.bif
|
||||||
|
@load base/bif/bro.bif
|
||||||
|
@load base/bif/reporter.bif
|
||||||
|
|
||||||
|
## Deprecated. This is superseded by the new logging framework.
|
||||||
|
global log_file_name: function(tag: string): string &redef;
|
||||||
|
|
||||||
|
## Deprecated. This is superseded by the new logging framework.
|
||||||
|
global open_log_file: function(tag: string): file &redef;
|
||||||
|
|
||||||
|
## Specifies a directory for Bro to store its persistent state. All globals can
|
||||||
|
## be declared persistent via the :bro:attr:`&persistent` attribute.
|
||||||
|
const state_dir = ".state" &redef;
|
||||||
|
|
||||||
|
## Length of the delays inserted when storing state incrementally. To avoid
|
||||||
|
## dropping packets when serializing larger volumes of persistent state to
|
||||||
|
## disk, Bro interleaves the operation with continued packet processing.
|
||||||
|
const state_write_delay = 0.01 secs &redef;
|
||||||
|
|
||||||
|
global done_with_network = F;
|
||||||
|
event net_done(t: time) { done_with_network = T; }
|
||||||
|
|
||||||
|
function log_file_name(tag: string): string
|
||||||
|
{
|
||||||
|
local suffix = getenv("BRO_LOG_SUFFIX") == "" ? "log" : getenv("BRO_LOG_SUFFIX");
|
||||||
|
return fmt("%s.%s", tag, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
function open_log_file(tag: string): file
|
||||||
|
{
|
||||||
|
return open(log_file_name(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
## Internal function.
|
||||||
|
function add_interface(iold: string, inew: string): string
|
||||||
|
{
|
||||||
|
if ( iold == "" )
|
||||||
|
return inew;
|
||||||
|
else
|
||||||
|
return fmt("%s %s", iold, inew);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Network interfaces to listen on. Use ``redef interfaces += "eth0"`` to
|
||||||
|
## extend.
|
||||||
|
global interfaces = "" &add_func = add_interface;
|
||||||
|
|
||||||
|
## Internal function.
|
||||||
|
function add_signature_file(sold: string, snew: string): string
|
||||||
|
{
|
||||||
|
if ( sold == "" )
|
||||||
|
return snew;
|
||||||
|
else
|
||||||
|
return cat(sold, " ", snew);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Signature files to read. Use ``redef signature_files += "foo.sig"`` to
|
||||||
|
## extend. Signature files added this way will be searched relative to
|
||||||
|
## ``BROPATH``. Using the ``@load-sigs`` directive instead is preferred
|
||||||
|
## since that can search paths relative to the current script.
|
||||||
|
global signature_files = "" &add_func = add_signature_file;
|
||||||
|
|
||||||
|
## ``p0f`` fingerprint file to use. Will be searched relative to ``BROPATH``.
|
||||||
|
const passive_fingerprint_file = "base/misc/p0f.fp" &redef;
|
||||||
|
|
||||||
## Definition of "secondary filters". A secondary filter is a BPF filter given
|
## Definition of "secondary filters". A secondary filter is a BPF filter given
|
||||||
## as index in this table. For each such filter, the corresponding event is
|
## as index in this table. For each such filter, the corresponding event is
|
||||||
## raised for all matching packets.
|
## raised for all matching packets.
|
||||||
|
@ -2502,7 +2528,7 @@ global dns_skip_all_addl = T &redef;
|
||||||
|
|
||||||
## If a DNS request includes more than this many queries, assume it's non-DNS
|
## If a DNS request includes more than this many queries, assume it's non-DNS
|
||||||
## traffic and do not process it. Set to 0 to turn off this functionality.
|
## traffic and do not process it. Set to 0 to turn off this functionality.
|
||||||
global dns_max_queries = 5;
|
global dns_max_queries = 25 &redef;
|
||||||
|
|
||||||
## HTTP session statistics.
|
## HTTP session statistics.
|
||||||
##
|
##
|
||||||
|
@ -3655,20 +3681,11 @@ export {
|
||||||
## Toggle whether to do GRE decapsulation.
|
## Toggle whether to do GRE decapsulation.
|
||||||
const enable_gre = T &redef;
|
const enable_gre = T &redef;
|
||||||
|
|
||||||
## With this option set, the Teredo analysis will first check to see if
|
|
||||||
## other protocol analyzers have confirmed that they think they're
|
|
||||||
## parsing the right protocol and only continue with Teredo tunnel
|
|
||||||
## decapsulation if nothing else has yet confirmed. This can help
|
|
||||||
## reduce false positives of UDP traffic (e.g. DNS) that also happens
|
|
||||||
## to have a valid Teredo encapsulation.
|
|
||||||
const yielding_teredo_decapsulation = T &redef;
|
|
||||||
|
|
||||||
## With this set, the Teredo analyzer waits until it sees both sides
|
## With this set, the Teredo analyzer waits until it sees both sides
|
||||||
## of a connection using a valid Teredo encapsulation before issuing
|
## of a connection using a valid Teredo encapsulation before issuing
|
||||||
## a :bro:see:`protocol_confirmation`. If it's false, the first
|
## a :bro:see:`protocol_confirmation`. If it's false, the first
|
||||||
## occurrence of a packet with valid Teredo encapsulation causes a
|
## occurrence of a packet with valid Teredo encapsulation causes a
|
||||||
## confirmation. Both cases are still subject to effects of
|
## confirmation.
|
||||||
## :bro:see:`Tunnel::yielding_teredo_decapsulation`.
|
|
||||||
const delay_teredo_confirmation = T &redef;
|
const delay_teredo_confirmation = T &redef;
|
||||||
|
|
||||||
## With this set, the GTP analyzer waits until the most-recent upflow
|
## With this set, the GTP analyzer waits until the most-recent upflow
|
||||||
|
@ -3684,7 +3701,6 @@ export {
|
||||||
## (includes GRE tunnels).
|
## (includes GRE tunnels).
|
||||||
const ip_tunnel_timeout = 24hrs &redef;
|
const ip_tunnel_timeout = 24hrs &redef;
|
||||||
} # end export
|
} # end export
|
||||||
module GLOBAL;
|
|
||||||
|
|
||||||
module Reporter;
|
module Reporter;
|
||||||
export {
|
export {
|
||||||
|
@ -3703,10 +3719,18 @@ export {
|
||||||
## external harness and shouldn't output anything to the console.
|
## external harness and shouldn't output anything to the console.
|
||||||
const errors_to_stderr = T &redef;
|
const errors_to_stderr = T &redef;
|
||||||
}
|
}
|
||||||
module GLOBAL;
|
|
||||||
|
|
||||||
## Number of bytes per packet to capture from live interfaces.
|
module Pcap;
|
||||||
const snaplen = 8192 &redef;
|
export {
|
||||||
|
## Number of bytes per packet to capture from live interfaces.
|
||||||
|
const snaplen = 8192 &redef;
|
||||||
|
|
||||||
|
## Number of Mbytes to provide as buffer space when capturing from live
|
||||||
|
## interfaces.
|
||||||
|
const bufsize = 128 &redef;
|
||||||
|
} # end export
|
||||||
|
|
||||||
|
module GLOBAL;
|
||||||
|
|
||||||
## Seed for hashes computed internally for probabilistic data structures. Using
|
## Seed for hashes computed internally for probabilistic data structures. Using
|
||||||
## the same value here will make the hashes compatible between independent Bro
|
## the same value here will make the hashes compatible between independent Bro
|
||||||
|
|
|
@ -37,6 +37,10 @@
|
||||||
@load base/frameworks/reporter
|
@load base/frameworks/reporter
|
||||||
@load base/frameworks/sumstats
|
@load base/frameworks/sumstats
|
||||||
@load base/frameworks/tunnels
|
@load base/frameworks/tunnels
|
||||||
|
@ifdef ( Broker::enable )
|
||||||
|
@load base/frameworks/openflow
|
||||||
|
@load base/frameworks/netcontrol
|
||||||
|
@endif
|
||||||
|
|
||||||
@load base/protocols/conn
|
@load base/protocols/conn
|
||||||
@load base/protocols/dhcp
|
@load base/protocols/dhcp
|
||||||
|
@ -52,6 +56,7 @@
|
||||||
@load base/protocols/pop3
|
@load base/protocols/pop3
|
||||||
@load base/protocols/radius
|
@load base/protocols/radius
|
||||||
@load base/protocols/rdp
|
@load base/protocols/rdp
|
||||||
|
@load base/protocols/rfb
|
||||||
@load base/protocols/sip
|
@load base/protocols/sip
|
||||||
@load base/protocols/snmp
|
@load base/protocols/snmp
|
||||||
@load base/protocols/smtp
|
@load base/protocols/smtp
|
||||||
|
|
|
@ -47,7 +47,7 @@ export {
|
||||||
## S2 Connection established and close attempt by originator seen (but no reply from responder).
|
## S2 Connection established and close attempt by originator seen (but no reply from responder).
|
||||||
## S3 Connection established and close attempt by responder seen (but no reply from originator).
|
## S3 Connection established and close attempt by responder seen (but no reply from originator).
|
||||||
## RSTO Connection established, originator aborted (sent a RST).
|
## RSTO Connection established, originator aborted (sent a RST).
|
||||||
## RSTR Established, responder aborted.
|
## RSTR Responder sent a RST.
|
||||||
## RSTOS0 Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder.
|
## RSTOS0 Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder.
|
||||||
## RSTRH Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator.
|
## RSTRH Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator.
|
||||||
## SH Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was "half" open).
|
## SH Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was "half" open).
|
||||||
|
@ -87,7 +87,8 @@ export {
|
||||||
## f packet with FIN bit set
|
## f packet with FIN bit set
|
||||||
## r packet with RST bit set
|
## r packet with RST bit set
|
||||||
## c packet with a bad checksum
|
## c packet with a bad checksum
|
||||||
## i inconsistent packet (e.g. SYN+RST bits both set)
|
## i inconsistent packet (e.g. FIN+RST bits set)
|
||||||
|
## q multi-flag packet (SYN+FIN or SYN+RST bits set)
|
||||||
## ====== ====================================================
|
## ====== ====================================================
|
||||||
##
|
##
|
||||||
## If the event comes from the originator, the letter is in
|
## If the event comes from the originator, the letter is in
|
||||||
|
|
|
@ -26,6 +26,7 @@ export {
|
||||||
[49] = "DHCID", [99] = "SPF", [100] = "DINFO", [101] = "UID",
|
[49] = "DHCID", [99] = "SPF", [100] = "DINFO", [101] = "UID",
|
||||||
[102] = "GID", [103] = "UNSPEC", [249] = "TKEY", [250] = "TSIG",
|
[102] = "GID", [103] = "UNSPEC", [249] = "TKEY", [250] = "TSIG",
|
||||||
[251] = "IXFR", [252] = "AXFR", [253] = "MAILB", [254] = "MAILA",
|
[251] = "IXFR", [252] = "AXFR", [253] = "MAILB", [254] = "MAILA",
|
||||||
|
[257] = "CAA",
|
||||||
[32768] = "TA", [32769] = "DLV",
|
[32768] = "TA", [32769] = "DLV",
|
||||||
[ANY] = "*",
|
[ANY] = "*",
|
||||||
} &default = function(n: count): string { return fmt("query-%d", n); };
|
} &default = function(n: count): string { return fmt("query-%d", n); };
|
||||||
|
|
|
@ -213,7 +213,7 @@ event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool) &prior
|
||||||
# on a different file could be checked, but the file size will
|
# on a different file could be checked, but the file size will
|
||||||
# be overwritten by the server response to the RETR command
|
# be overwritten by the server response to the RETR command
|
||||||
# if that's given as well which would be more correct.
|
# if that's given as well which would be more correct.
|
||||||
c$ftp$file_size = extract_count(msg);
|
c$ftp$file_size = extract_count(msg, F);
|
||||||
}
|
}
|
||||||
|
|
||||||
# PASV and EPSV processing
|
# PASV and EPSV processing
|
||||||
|
|
|
@ -41,6 +41,8 @@ export {
|
||||||
## misspelled like the standard declares, but the name used here
|
## misspelled like the standard declares, but the name used here
|
||||||
## is "referrer" spelled correctly.
|
## is "referrer" spelled correctly.
|
||||||
referrer: string &log &optional;
|
referrer: string &log &optional;
|
||||||
|
## Value of the version portion of the request.
|
||||||
|
version: string &log &optional;
|
||||||
## Value of the User-Agent header from the client.
|
## Value of the User-Agent header from the client.
|
||||||
user_agent: string &log &optional;
|
user_agent: string &log &optional;
|
||||||
## Actual uncompressed content size of the data transferred from
|
## Actual uncompressed content size of the data transferred from
|
||||||
|
@ -222,6 +224,8 @@ event http_reply(c: connection, version: string, code: count, reason: string) &p
|
||||||
|
|
||||||
c$http$status_code = code;
|
c$http$status_code = code;
|
||||||
c$http$status_msg = reason;
|
c$http$status_msg = reason;
|
||||||
|
c$http$version = version;
|
||||||
|
|
||||||
if ( code_in_range(code, 100, 199) )
|
if ( code_in_range(code, 100, 199) )
|
||||||
{
|
{
|
||||||
c$http$info_code = code;
|
c$http$info_code = code;
|
||||||
|
@ -270,7 +274,7 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr
|
||||||
{
|
{
|
||||||
if ( /^[bB][aA][sS][iI][cC] / in value )
|
if ( /^[bB][aA][sS][iI][cC] / in value )
|
||||||
{
|
{
|
||||||
local userpass = decode_base64(sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, ""));
|
local userpass = decode_base64_conn(c$id, sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, ""));
|
||||||
local up = split_string(userpass, /:/);
|
local up = split_string(userpass, /:/);
|
||||||
if ( |up| >= 2 )
|
if ( |up| >= 2 )
|
||||||
{
|
{
|
||||||
|
|
1
scripts/base/protocols/rfb/README
Normal file
1
scripts/base/protocols/rfb/README
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Support for Remote FrameBuffer analysis. This includes all VNC servers.
|
3
scripts/base/protocols/rfb/__load__.bro
Normal file
3
scripts/base/protocols/rfb/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Generated by binpac_quickstart
|
||||||
|
@load ./main
|
||||||
|
@load-sigs ./dpd.sig
|
12
scripts/base/protocols/rfb/dpd.sig
Normal file
12
scripts/base/protocols/rfb/dpd.sig
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
signature dpd_rfb_server {
|
||||||
|
ip-proto == tcp
|
||||||
|
payload /^RFB/
|
||||||
|
requires-reverse-signature dpd_rfb_client
|
||||||
|
enable "rfb"
|
||||||
|
}
|
||||||
|
|
||||||
|
signature dpd_rfb_client {
|
||||||
|
ip-proto == tcp
|
||||||
|
payload /^RFB/
|
||||||
|
tcp-state originator
|
||||||
|
}
|
164
scripts/base/protocols/rfb/main.bro
Normal file
164
scripts/base/protocols/rfb/main.bro
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
module RFB;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
type Info: record {
|
||||||
|
## Timestamp for when the event happened.
|
||||||
|
ts: time &log;
|
||||||
|
## Unique ID for the connection.
|
||||||
|
uid: string &log;
|
||||||
|
## The connection's 4-tuple of endpoint addresses/ports.
|
||||||
|
id: conn_id &log;
|
||||||
|
|
||||||
|
## Major version of the client.
|
||||||
|
client_major_version: string &log &optional;
|
||||||
|
## Minor version of the client.
|
||||||
|
client_minor_version: string &log &optional;
|
||||||
|
## Major version of the server.
|
||||||
|
server_major_version: string &log &optional;
|
||||||
|
## Major version of the client.
|
||||||
|
server_minor_version: string &log &optional;
|
||||||
|
|
||||||
|
## Identifier of authentication method used.
|
||||||
|
authentication_method: string &log &optional;
|
||||||
|
## Whether or not authentication was succesful.
|
||||||
|
auth: bool &log &optional;
|
||||||
|
|
||||||
|
## Whether the client has an exclusive or a shared session.
|
||||||
|
share_flag: bool &log &optional;
|
||||||
|
## Name of the screen that is being shared.
|
||||||
|
desktop_name: string &log &optional;
|
||||||
|
## Width of the screen that is being shared.
|
||||||
|
width: count &log &optional;
|
||||||
|
## Height of the screen that is being shared.
|
||||||
|
height: count &log &optional;
|
||||||
|
|
||||||
|
## Internally used value to determine if this connection
|
||||||
|
## has already been logged.
|
||||||
|
done: bool &default=F;
|
||||||
|
};
|
||||||
|
|
||||||
|
global log_rfb: event(rec: Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
function friendly_auth_name(auth: count): string
|
||||||
|
{
|
||||||
|
switch (auth) {
|
||||||
|
case 0:
|
||||||
|
return "Invalid";
|
||||||
|
case 1:
|
||||||
|
return "None";
|
||||||
|
case 2:
|
||||||
|
return "VNC";
|
||||||
|
case 16:
|
||||||
|
return "Tight";
|
||||||
|
case 17:
|
||||||
|
return "Ultra";
|
||||||
|
case 18:
|
||||||
|
return "TLS";
|
||||||
|
case 19:
|
||||||
|
return "VeNCrypt";
|
||||||
|
case 20:
|
||||||
|
return "GTK-VNC SASL";
|
||||||
|
case 21:
|
||||||
|
return "MD5 hash authentication";
|
||||||
|
case 22:
|
||||||
|
return "Colin Dean xvp";
|
||||||
|
case 30:
|
||||||
|
return "Apple Remote Desktop";
|
||||||
|
}
|
||||||
|
return "RealVNC";
|
||||||
|
}
|
||||||
|
|
||||||
|
redef record connection += {
|
||||||
|
rfb: Info &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(RFB::LOG, [$columns=Info, $ev=log_rfb, $path="rfb"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_log(c:connection)
|
||||||
|
{
|
||||||
|
local state = c$rfb;
|
||||||
|
if ( state$done )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::write(RFB::LOG, c$rfb);
|
||||||
|
c$rfb$done = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_session(c: connection)
|
||||||
|
{
|
||||||
|
if ( ! c?$rfb )
|
||||||
|
{
|
||||||
|
local info: Info;
|
||||||
|
info$ts = network_time();
|
||||||
|
info$uid = c$uid;
|
||||||
|
info$id = c$id;
|
||||||
|
|
||||||
|
c$rfb = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_event(c: connection) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_client_version(c: connection, major_version: string, minor_version: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
c$rfb$client_major_version = major_version;
|
||||||
|
c$rfb$client_minor_version = minor_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_server_version(c: connection, major_version: string, minor_version: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
c$rfb$server_major_version = major_version;
|
||||||
|
c$rfb$server_minor_version = minor_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_authentication_type(c: connection, authtype: count) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
c$rfb$authentication_method = friendly_auth_name(authtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_server_parameters(c: connection, name: string, width: count, height: count) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
c$rfb$desktop_name = name;
|
||||||
|
c$rfb$width = width;
|
||||||
|
c$rfb$height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_server_parameters(c: connection, name: string, width: count, height: count) &priority=-5
|
||||||
|
{
|
||||||
|
write_log(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_auth_result(c: connection, result: bool) &priority=5
|
||||||
|
{
|
||||||
|
c$rfb$auth = !result;
|
||||||
|
}
|
||||||
|
|
||||||
|
event rfb_share_flag(c: connection, flag: bool) &priority=5
|
||||||
|
{
|
||||||
|
c$rfb$share_flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
event connection_state_remove(c: connection) &priority=-5
|
||||||
|
{
|
||||||
|
if ( c?$rfb )
|
||||||
|
{
|
||||||
|
write_log(c);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,9 +60,9 @@ export {
|
||||||
## Contents of the Warning: header
|
## Contents of the Warning: header
|
||||||
warning: string &log &optional;
|
warning: string &log &optional;
|
||||||
## Contents of the Content-Length: header from the client
|
## Contents of the Content-Length: header from the client
|
||||||
request_body_len: string &log &optional;
|
request_body_len: count &log &optional;
|
||||||
## Contents of the Content-Length: header from the server
|
## Contents of the Content-Length: header from the server
|
||||||
response_body_len: string &log &optional;
|
response_body_len: count &log &optional;
|
||||||
## Contents of the Content-Type: header from the server
|
## Contents of the Content-Type: header from the server
|
||||||
content_type: string &log &optional;
|
content_type: string &log &optional;
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,7 @@ export {
|
||||||
## that the SIP analyzer will only accept methods consisting solely
|
## that the SIP analyzer will only accept methods consisting solely
|
||||||
## of letters ``[A-Za-z]``.
|
## of letters ``[A-Za-z]``.
|
||||||
const sip_methods: set[string] = {
|
const sip_methods: set[string] = {
|
||||||
"REGISTER", "INVITE", "ACK", "CANCEL", "BYE", "OPTIONS"
|
"REGISTER", "INVITE", "ACK", "CANCEL", "BYE", "OPTIONS", "NOTIFY", "SUBSCRIBE"
|
||||||
} &redef;
|
} &redef;
|
||||||
|
|
||||||
## Event that can be handled to access the SIP record as it is sent on
|
## Event that can be handled to access the SIP record as it is sent on
|
||||||
|
@ -127,17 +127,6 @@ function set_state(c: connection, is_request: bool)
|
||||||
c$sip_state = s;
|
c$sip_state = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
# These deal with new requests and responses.
|
|
||||||
if ( is_request && c$sip_state$current_request !in c$sip_state$pending )
|
|
||||||
c$sip_state$pending[c$sip_state$current_request] = new_sip_session(c);
|
|
||||||
if ( ! is_request && c$sip_state$current_response !in c$sip_state$pending )
|
|
||||||
c$sip_state$pending[c$sip_state$current_response] = new_sip_session(c);
|
|
||||||
|
|
||||||
if ( is_request )
|
|
||||||
c$sip = c$sip_state$pending[c$sip_state$current_request];
|
|
||||||
else
|
|
||||||
c$sip = c$sip_state$pending[c$sip_state$current_response];
|
|
||||||
|
|
||||||
if ( is_request )
|
if ( is_request )
|
||||||
{
|
{
|
||||||
if ( c$sip_state$current_request !in c$sip_state$pending )
|
if ( c$sip_state$current_request !in c$sip_state$pending )
|
||||||
|
@ -152,7 +141,6 @@ function set_state(c: connection, is_request: bool)
|
||||||
|
|
||||||
c$sip = c$sip_state$pending[c$sip_state$current_response];
|
c$sip = c$sip_state$pending[c$sip_state$current_response];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function flush_pending(c: connection)
|
function flush_pending(c: connection)
|
||||||
|
@ -163,7 +151,9 @@ function flush_pending(c: connection)
|
||||||
for ( r in c$sip_state$pending )
|
for ( r in c$sip_state$pending )
|
||||||
{
|
{
|
||||||
# We don't use pending elements at index 0.
|
# We don't use pending elements at index 0.
|
||||||
if ( r == 0 ) next;
|
if ( r == 0 )
|
||||||
|
next;
|
||||||
|
|
||||||
Log::write(SIP::LOG, c$sip_state$pending[r]);
|
Log::write(SIP::LOG, c$sip_state$pending[r]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,16 +195,39 @@ event sip_header(c: connection, is_request: bool, name: string, value: string) &
|
||||||
if ( c$sip_state$current_request !in c$sip_state$pending )
|
if ( c$sip_state$current_request !in c$sip_state$pending )
|
||||||
++c$sip_state$current_request;
|
++c$sip_state$current_request;
|
||||||
set_state(c, is_request);
|
set_state(c, is_request);
|
||||||
if ( name == "CALL-ID" ) c$sip$call_id = value;
|
switch ( name )
|
||||||
else if ( name == "CONTENT-LENGTH" || name == "L" ) c$sip$request_body_len = value;
|
{
|
||||||
else if ( name == "CSEQ" ) c$sip$seq = value;
|
case "CALL-ID":
|
||||||
else if ( name == "DATE" ) c$sip$date = value;
|
c$sip$call_id = value;
|
||||||
else if ( name == "FROM" || name == "F" ) c$sip$request_from = split_string1(value, /;[ ]?tag=/)[0];
|
break;
|
||||||
else if ( name == "REPLY-TO" ) c$sip$reply_to = value;
|
case "CONTENT-LENGTH", "L":
|
||||||
else if ( name == "SUBJECT" || name == "S" ) c$sip$subject = value;
|
c$sip$request_body_len = to_count(value);
|
||||||
else if ( name == "TO" || name == "T" ) c$sip$request_to = value;
|
break;
|
||||||
else if ( name == "USER-AGENT" ) c$sip$user_agent = value;
|
case "CSEQ":
|
||||||
else if ( name == "VIA" || name == "V" ) c$sip$request_path[|c$sip$request_path|] = split_string1(value, /;[ ]?branch/)[0];
|
c$sip$seq = value;
|
||||||
|
break;
|
||||||
|
case "DATE":
|
||||||
|
c$sip$date = value;
|
||||||
|
break;
|
||||||
|
case "FROM", "F":
|
||||||
|
c$sip$request_from = split_string1(value, /;[ ]?tag=/)[0];
|
||||||
|
break;
|
||||||
|
case "REPLY-TO":
|
||||||
|
c$sip$reply_to = value;
|
||||||
|
break;
|
||||||
|
case "SUBJECT", "S":
|
||||||
|
c$sip$subject = value;
|
||||||
|
break;
|
||||||
|
case "TO", "T":
|
||||||
|
c$sip$request_to = value;
|
||||||
|
break;
|
||||||
|
case "USER-AGENT":
|
||||||
|
c$sip$user_agent = value;
|
||||||
|
break;
|
||||||
|
case "VIA", "V":
|
||||||
|
c$sip$request_path[|c$sip$request_path|] = split_string1(value, /;[ ]?branch/)[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
c$sip_state$pending[c$sip_state$current_request] = c$sip;
|
c$sip_state$pending[c$sip_state$current_request] = c$sip;
|
||||||
}
|
}
|
||||||
|
@ -222,13 +235,29 @@ event sip_header(c: connection, is_request: bool, name: string, value: string) &
|
||||||
{
|
{
|
||||||
if ( c$sip_state$current_response !in c$sip_state$pending )
|
if ( c$sip_state$current_response !in c$sip_state$pending )
|
||||||
++c$sip_state$current_response;
|
++c$sip_state$current_response;
|
||||||
|
|
||||||
set_state(c, is_request);
|
set_state(c, is_request);
|
||||||
if ( name == "CONTENT-LENGTH" || name == "L" ) c$sip$response_body_len = value;
|
switch ( name )
|
||||||
else if ( name == "CONTENT-TYPE" || name == "C" ) c$sip$content_type = value;
|
{
|
||||||
else if ( name == "WARNING" ) c$sip$warning = value;
|
case "CONTENT-LENGTH", "L":
|
||||||
else if ( name == "FROM" || name == "F" ) c$sip$response_from = split_string1(value, /;[ ]?tag=/)[0];
|
c$sip$response_body_len = to_count(value);
|
||||||
else if ( name == "TO" || name == "T" ) c$sip$response_to = value;
|
break;
|
||||||
else if ( name == "VIA" || name == "V" ) c$sip$response_path[|c$sip$response_path|] = split_string1(value, /;[ ]?branch/)[0];
|
case "CONTENT-TYPE", "C":
|
||||||
|
c$sip$content_type = value;
|
||||||
|
break;
|
||||||
|
case "WARNING":
|
||||||
|
c$sip$warning = value;
|
||||||
|
break;
|
||||||
|
case "FROM", "F":
|
||||||
|
c$sip$response_from = split_string1(value, /;[ ]?tag=/)[0];
|
||||||
|
break;
|
||||||
|
case "TO", "T":
|
||||||
|
c$sip$response_to = value;
|
||||||
|
break;
|
||||||
|
case "VIA", "V":
|
||||||
|
c$sip$response_path[|c$sip$response_path|] = split_string1(value, /;[ ]?branch/)[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
c$sip_state$pending[c$sip_state$current_response] = c$sip;
|
c$sip_state$pending[c$sip_state$current_response] = c$sip;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ export {
|
||||||
from: string &log &optional;
|
from: string &log &optional;
|
||||||
## Contents of the To header.
|
## Contents of the To header.
|
||||||
to: set[string] &log &optional;
|
to: set[string] &log &optional;
|
||||||
|
## Contents of the CC header.
|
||||||
|
cc: set[string] &log &optional;
|
||||||
## Contents of the ReplyTo header.
|
## Contents of the ReplyTo header.
|
||||||
reply_to: string &log &optional;
|
reply_to: string &log &optional;
|
||||||
## Contents of the MsgID header.
|
## Contents of the MsgID header.
|
||||||
|
@ -239,6 +241,16 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=5
|
||||||
add c$smtp$to[to_parts[i]];
|
add c$smtp$to[to_parts[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if ( h$name == "CC" )
|
||||||
|
{
|
||||||
|
if ( ! c$smtp?$cc )
|
||||||
|
c$smtp$cc = set();
|
||||||
|
|
||||||
|
local cc_parts = split_string(h$value, /[[:blank:]]*,[[:blank:]]*/);
|
||||||
|
for ( i in cc_parts )
|
||||||
|
add c$smtp$cc[cc_parts[i]];
|
||||||
|
}
|
||||||
|
|
||||||
else if ( h$name == "X-ORIGINATING-IP" )
|
else if ( h$name == "X-ORIGINATING-IP" )
|
||||||
{
|
{
|
||||||
local addresses = extract_ip_addresses(h$value);
|
local addresses = extract_ip_addresses(h$value);
|
||||||
|
|
|
@ -46,11 +46,10 @@ export {
|
||||||
## authentication success or failure when compression is enabled.
|
## authentication success or failure when compression is enabled.
|
||||||
const compression_algorithms = set("zlib", "zlib@openssh.com") &redef;
|
const compression_algorithms = set("zlib", "zlib@openssh.com") &redef;
|
||||||
|
|
||||||
## If true, we tell the event engine to not look at further data
|
## If true, after detection detach the SSH analyzer from the connection
|
||||||
## packets after the initial SSH handshake. Helps with performance
|
## to prevent continuing to process encrypted traffic. Helps with performance
|
||||||
## (especially with large file transfers) but precludes some
|
## (especially with large file transfers).
|
||||||
## kinds of analyses. Defaults to T.
|
const disable_analyzer_after_detection = T &redef;
|
||||||
const skip_processing_after_detection = T &redef;
|
|
||||||
|
|
||||||
## Event that can be handled to access the SSH record as it is sent on
|
## Event that can be handled to access the SSH record as it is sent on
|
||||||
## to the logging framework.
|
## to the logging framework.
|
||||||
|
@ -70,6 +69,8 @@ redef record Info += {
|
||||||
# Store capabilities from the first host for
|
# Store capabilities from the first host for
|
||||||
# comparison with the second (internal use)
|
# comparison with the second (internal use)
|
||||||
capabilities: Capabilities &optional;
|
capabilities: Capabilities &optional;
|
||||||
|
## Analzyer ID
|
||||||
|
analyzer_id: count &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
redef record connection += {
|
redef record connection += {
|
||||||
|
@ -130,11 +131,8 @@ event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=5
|
||||||
|
|
||||||
c$ssh$auth_success = T;
|
c$ssh$auth_success = T;
|
||||||
|
|
||||||
if ( skip_processing_after_detection)
|
if ( disable_analyzer_after_detection )
|
||||||
{
|
disable_analyzer(c$id, c$ssh$analyzer_id);
|
||||||
skip_further_processing(c$id);
|
|
||||||
set_record_packets(c$id, F);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=-5
|
event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=-5
|
||||||
|
@ -233,3 +231,12 @@ event ssh2_server_host_key(c: connection, key: string) &priority=5
|
||||||
{
|
{
|
||||||
generate_fingerprint(c, key);
|
generate_fingerprint(c, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=20
|
||||||
|
{
|
||||||
|
if ( atype == Analyzer::ANALYZER_SSH )
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
c$ssh$analyzer_id = aid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ export {
|
||||||
[7] = "client_authz",
|
[7] = "client_authz",
|
||||||
[8] = "server_authz",
|
[8] = "server_authz",
|
||||||
[9] = "cert_type",
|
[9] = "cert_type",
|
||||||
[10] = "elliptic_curves",
|
[10] = "elliptic_curves", # new name: supported_groups - draft-ietf-tls-negotiated-ff-dhe
|
||||||
[11] = "ec_point_formats",
|
[11] = "ec_point_formats",
|
||||||
[12] = "srp",
|
[12] = "srp",
|
||||||
[13] = "signature_algorithms",
|
[13] = "signature_algorithms",
|
||||||
|
@ -120,9 +120,10 @@ export {
|
||||||
[18] = "signed_certificate_timestamp",
|
[18] = "signed_certificate_timestamp",
|
||||||
[19] = "client_certificate_type",
|
[19] = "client_certificate_type",
|
||||||
[20] = "server_certificate_type",
|
[20] = "server_certificate_type",
|
||||||
[21] = "padding", # temporary till 2016-03-12
|
[21] = "padding",
|
||||||
[22] = "encrypt_then_mac",
|
[22] = "encrypt_then_mac",
|
||||||
[23] = "extended_master_secret",
|
[23] = "extended_master_secret",
|
||||||
|
[24] = "token_binding", # temporary till 2017-02-04 - draft-ietf-tokbind-negotiation
|
||||||
[35] = "SessionTicket TLS",
|
[35] = "SessionTicket TLS",
|
||||||
[40] = "extended_random",
|
[40] = "extended_random",
|
||||||
[13172] = "next_protocol_negotiation",
|
[13172] = "next_protocol_negotiation",
|
||||||
|
@ -165,7 +166,10 @@ export {
|
||||||
[26] = "brainpoolP256r1",
|
[26] = "brainpoolP256r1",
|
||||||
[27] = "brainpoolP384r1",
|
[27] = "brainpoolP384r1",
|
||||||
[28] = "brainpoolP512r1",
|
[28] = "brainpoolP512r1",
|
||||||
# draft-ietf-tls-negotiated-ff-dhe-05
|
# Temporary till 2017-03-01 - draft-ietf-tls-rfc4492bis
|
||||||
|
[29] = "ecdh_x25519",
|
||||||
|
[30] = "ecdh_x448",
|
||||||
|
# draft-ietf-tls-negotiated-ff-dhe-10
|
||||||
[256] = "ffdhe2048",
|
[256] = "ffdhe2048",
|
||||||
[257] = "ffdhe3072",
|
[257] = "ffdhe3072",
|
||||||
[258] = "ffdhe4096",
|
[258] = "ffdhe4096",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
signature dpd_ssl_server {
|
signature dpd_ssl_server {
|
||||||
ip-proto == tcp
|
ip-proto == tcp
|
||||||
# Server hello.
|
# Server hello.
|
||||||
payload /^(\x16\x03[\x00\x01\x02\x03]..\x02...\x03[\x00\x01\x02\x03]|...?\x04..\x00\x02).*/
|
payload /^((\x15\x03[\x00\x01\x02\x03]....)?\x16\x03[\x00\x01\x02\x03]..\x02...\x03[\x00\x01\x02\x03]|...?\x04..\x00\x02).*/
|
||||||
requires-reverse-signature dpd_ssl_client
|
requires-reverse-signature dpd_ssl_client
|
||||||
enable "ssl"
|
enable "ssl"
|
||||||
tcp-state responder
|
tcp-state responder
|
||||||
|
|
|
@ -9,6 +9,6 @@ signature dpd_ayiya {
|
||||||
|
|
||||||
signature dpd_teredo {
|
signature dpd_teredo {
|
||||||
ip-proto = udp
|
ip-proto = udp
|
||||||
payload /^(\x00\x00)|(\x00\x01)|([\x60-\x6f])/
|
payload /^(\x00\x00)|(\x00\x01)|([\x60-\x6f].{7}((\x20\x01\x00\x00)).{28})|([\x60-\x6f].{23}((\x20\x01\x00\x00))).{12}/
|
||||||
enable "teredo"
|
enable "teredo"
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue