mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Merge branch 'master' into topic/jsiwek/bit-1235
This commit is contained in:
commit
02c86b2304
186 changed files with 6577 additions and 3101 deletions
210
CHANGES
210
CHANGES
|
@ -1,4 +1,214 @@
|
|||
|
||||
2.3-234 | 2014-10-14 14:42:09 -0500
|
||||
|
||||
* Documentation fixes. (Steve Smoot)
|
||||
|
||||
2.3-233 | 2014-10-09 16:00:27 -0500
|
||||
|
||||
* Change find-bro-logs unit test to follow symlinks. (Jon Siwek)
|
||||
|
||||
* Add error checks and messages to a test script (Daniel Thayer)
|
||||
|
||||
2.3-230 | 2014-10-08 08:15:17 -0700
|
||||
|
||||
* Further baseline normalization for plugin test portability. (Robin
|
||||
Sommer)
|
||||
|
||||
2.3-229 | 2014-10-07 20:18:11 -0700
|
||||
|
||||
* Fix for test portability. (Robin Sommer)
|
||||
|
||||
2.3-228 | 2014-10-07 15:32:37 -0700
|
||||
|
||||
* Include plugin unit tests into the top-level btest configuration. (Robin Sommer)
|
||||
|
||||
* Switching the prefix separator for packet source/dumper plugins
|
||||
once more, now to "::". Addresses BIT-1267. (Robin Sommer)
|
||||
|
||||
* Fix for allowing a packet source/dumper plugin to support multiple
|
||||
prefixes with a colon. (Robin Sommer)
|
||||
|
||||
2.3-225 | 2014-10-07 15:13:35 -0700
|
||||
|
||||
* Updating plugin documentation. (Robin Sommer)
|
||||
|
||||
2.3-224 | 2014-10-07 14:32:17 -0700
|
||||
|
||||
* Improved the log file reference documentation. (Jeannette Dopheide
|
||||
and Daniel Thayer)
|
||||
|
||||
* Improves shockwave flash file signatures. (Seth Hall)
|
||||
|
||||
- This moves the signatures out of the libmagic imported signatures
|
||||
and into our own general.sig.
|
||||
|
||||
- Expand the detection to LZMA compressed flash files.
|
||||
|
||||
* Add new script language reference documentation on operators,
|
||||
statements, and directives. Also improved the documentation on
|
||||
types and attributes by splitting them into two docs, and
|
||||
providing more examples and adding a chart on the top of each page
|
||||
with links to each type and attribute for easier access to the
|
||||
information. (Daniel Thayer)
|
||||
|
||||
* Split the types and attributes reference doc into two docs.
|
||||
(Daniel Thayer)
|
||||
|
||||
2.3-208 | 2014-10-03 09:38:52 -0500
|
||||
|
||||
* BIT-1268: Fix uninitialized router_list argument in
|
||||
dhcp_offer/dhcp_ack. (Jon Siwek)
|
||||
|
||||
2.3-207 | 2014-10-02 16:39:17 -0700
|
||||
|
||||
* Updating plugin docs. (Robin Sommer)
|
||||
|
||||
* Fix packet sources being treated as idle when a packet is
|
||||
available. Addresses BIT-1266. (Jon Siwek)
|
||||
|
||||
* Fix regression causing the main loop to spin more frequently.
|
||||
Addresses BIT-1266. (Jon Siwek)
|
||||
|
||||
2.3-203 | 2014-09-29 20:06:54 -0700
|
||||
|
||||
* Fix to use length parameter in DNP3 time conversion correctly now.
|
||||
(Robin Sommer)
|
||||
|
||||
2.3-202 | 2014-09-29 17:05:18 -0700
|
||||
|
||||
* New SSL extension type from IANA and a few other SSL const
|
||||
changes. (Johanna Amann)
|
||||
|
||||
* Make unexpected pipe errors fatal as precaution. Addresses
|
||||
BIT-1260. (Jon Siwek)
|
||||
|
||||
* Adding a function for DNP3 to translate the timestamp format. (Hui
|
||||
Lin)
|
||||
|
||||
2.3-197 | 2014-09-29 10:42:01 -0500
|
||||
|
||||
* Fix possible seg fault in TCP reassembler. (Jon Siwek)
|
||||
|
||||
2.3-196 | 2014-09-25 17:53:27 -0700
|
||||
|
||||
* Changing prefix for packet sources/dumper from ':' to '%'.
|
||||
Addresses BIT-1249. (Robin Sommer)
|
||||
|
||||
* Remove timeouts from remote communication loop. The select() now
|
||||
blocks until there's work to do instead of relying on a small
|
||||
timeout value which can cause unproductive use of cpu cycles. (Jon
|
||||
Siwek)
|
||||
|
||||
* Improve error message when failing to activate a plugin. Also fix
|
||||
a unit test helper script that checks plugin availability. (Jon
|
||||
Siwek)
|
||||
|
||||
2.3-183 | 2014-09-24 10:08:04 -0500
|
||||
|
||||
* Add a "node" field to Intel::Seen struture and intel.log to
|
||||
indicate which node discovered a hit on an intel item. (Seth Hall)
|
||||
|
||||
* BIT-1261: Fixes to plugin quick start doc. (Jon Siwek)
|
||||
|
||||
2.3-180 | 2014-09-22 12:52:41 -0500
|
||||
|
||||
* BIT-1259: Fix issue w/ duplicate TCP reassembly deliveries.
|
||||
(Jon Siwek)
|
||||
|
||||
2.3-178 | 2014-09-18 14:29:46 -0500
|
||||
|
||||
* BIT-1256: Fix file analysis events from coming after bro_done().
|
||||
(Jon Siwek)
|
||||
|
||||
2.3-177 | 2014-09-17 09:41:27 -0500
|
||||
|
||||
* Documentation fixes. (Chris Mavrakis)
|
||||
|
||||
2.3-174 | 2014-09-17 09:37:09 -0500
|
||||
|
||||
* Fixed some "make doc" warnings caused by reST formatting
|
||||
(Daniel Thayer).
|
||||
|
||||
2.3-172 | 2014-09-15 13:38:52 -0500
|
||||
|
||||
* Remove unneeded allocations for HTTP messages. (Jon Siwek)
|
||||
|
||||
2.3-171 | 2014-09-15 11:14:57 -0500
|
||||
|
||||
* Fix a compile error on systems without pcap-int.h. (Jon Siwek)
|
||||
|
||||
2.3-170 | 2014-09-12 19:28:01 -0700
|
||||
|
||||
* Fix incorrect data delivery skips after gap in HTTP Content-Range.
|
||||
Addresses BIT-1247. (Jon Siwek)
|
||||
|
||||
* Fix file analysis placement of data after gap in HTTP
|
||||
Content-Range. Addresses BIT-1248. (Jon Siwek)
|
||||
|
||||
* Fix issue w/ TCP reassembler not delivering some segments.
|
||||
Addresses BIT-1246. (Jon Siwek)
|
||||
|
||||
* Fix MIME entity file data/gap ordering and raise http_entity_data
|
||||
in line with data arrival. Addresses BIT-1240. (Jon Siwek)
|
||||
|
||||
* Implement file ID caching for MIME_Mail. (Jon Siwek)
|
||||
|
||||
* Fix a compile error. (Jon Siwek)
|
||||
|
||||
2.3-161 | 2014-09-09 12:35:38 -0500
|
||||
|
||||
* Bugfixes and test updates/additions. (Robin Sommer)
|
||||
|
||||
* Interface tweaks and docs for PktSrc/PktDumper. (Robin Sommer)
|
||||
|
||||
* Moving PCAP-related bifs to iosource/pcap.bif. (Robin Sommer)
|
||||
|
||||
* Moving some of the BPF filtering code into base class.
|
||||
This will allow packet sources that don't support BPF natively to
|
||||
emulate the filtering via libpcap. (Robin Sommer)
|
||||
|
||||
* Removing FlowSrc. (Robin Sommer)
|
||||
|
||||
* Removing remaining pieces of the 2ndary path, and left-over
|
||||
files of packet sorter. (Robin Sommer)
|
||||
|
||||
* A bunch of infrastructure work to move IOSource, IOSourceRegistry
|
||||
(now iosource::Manager) and PktSrc/PktDumper code into iosource/,
|
||||
and over to a plugin structure. (Robin Sommer)
|
||||
|
||||
2.3-137 | 2014-09-08 19:01:13 -0500
|
||||
|
||||
* Fix Broxygen's rendering of opaque types. (Jon Siwek)
|
||||
|
||||
2.3-136 | 2014-09-07 20:50:46 -0700
|
||||
|
||||
* Change more http links to https. (Johanna Amann)
|
||||
|
||||
2.3-134 | 2014-09-04 16:16:36 -0700
|
||||
|
||||
* Fixed a number of issues with OCSP reply validation. Addresses
|
||||
BIT-1212. (Johanna Amann)
|
||||
|
||||
* Fix null pointer dereference in OCSP verification code in case no
|
||||
certificate is sent as part as the ocsp reply. Addresses BIT-1212.
|
||||
(Johanna Amann)
|
||||
|
||||
2.3-131 | 2014-09-04 16:10:32 -0700
|
||||
|
||||
* Make links in documentation templates protocol relative. (Johanna
|
||||
Amann)
|
||||
|
||||
2.3-129 | 2014-09-02 17:21:21 -0700
|
||||
|
||||
* Simplify a conditional with equivalent branches. (Jon Siwek)
|
||||
|
||||
* Change EDNS parsing code to use rdlength more cautiously. (Jon
|
||||
Siwek)
|
||||
|
||||
* Fix a memory leak when bind() fails due to EADDRINUSE. (Jon Siwek)
|
||||
|
||||
* Fix possible buffer over-read in DNS TSIG parsing. (Jon Siwek)
|
||||
|
||||
2.3-124 | 2014-08-26 09:24:19 -0500
|
||||
|
||||
* Better documentation for sub_bytes (Jimmy Jones)
|
||||
|
|
2
NEWS
2
NEWS
|
@ -22,7 +22,7 @@ New Functionality
|
|||
plugin can furthermore hook into Bro's processing a number of places
|
||||
to add custom logic.
|
||||
|
||||
See http://www.bro.org/sphinx-git/devel/plugins.html for more
|
||||
See https://www.bro.org/sphinx-git/devel/plugins.html for more
|
||||
information on writing plugins.
|
||||
|
||||
Changed Functionality
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2.3-124
|
||||
2.3-234
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e5969f5a40f5cc192a751375cb61131d32c0fc1
|
||||
Subproject commit 3a4684801aafa0558383199e9abd711650b53af9
|
|
@ -1 +1 @@
|
|||
Subproject commit 181f084432e277f899140647d9b788059b3cccb1
|
||||
Subproject commit 95afe42e7474113a16cb2cb09ebdf8b552c59744
|
|
@ -1 +1 @@
|
|||
Subproject commit 6be54279bb7ecb5e03d8bcdc7660d323dc4de1bc
|
||||
Subproject commit 33d0ed4a54a6ecf08a0b5fe18831aa413b437066
|
|
@ -1 +1 @@
|
|||
Subproject commit f0e0efda05e4b20924efc1b826ad5d85c8b65f83
|
||||
Subproject commit 2f808bc8541378b1a4953cca02c58c43945d154f
|
|
@ -1 +1 @@
|
|||
Subproject commit 6de518922e5f89d52d831ea6fb6adb7fff94437e
|
||||
Subproject commit ad600b5bdcd56a2723e323c0f2c8e1708956ca4f
|
2
cmake
2
cmake
|
@ -1 +1 @@
|
|||
Subproject commit aa15263ae39667e5e9bd73690b05aa4af9147ca3
|
||||
Subproject commit 03de0cc467d2334dcb851eddd843d59fef217909
|
4
doc/_templates/layout.html
vendored
4
doc/_templates/layout.html
vendored
|
@ -10,7 +10,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<iframe src="http://www.bro.org/frames/header-no-logo.html" width="100%" height="100px" frameborder="0" marginheight="0" scrolling="no" marginwidth="0">
|
||||
<iframe src="//www.bro.org/frames/header-no-logo.html" width="100%" height="100px" frameborder="0" marginheight="0" scrolling="no" marginwidth="0">
|
||||
</iframe>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -108,6 +108,6 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<iframe src="http://www.bro.org/frames/footer.html" width="100%" height="420px" frameborder="0" marginheight="0" scrolling="no" marginwidth="0">
|
||||
<iframe src="//www.bro.org/frames/footer.html" width="100%" height="420px" frameborder="0" marginheight="0" scrolling="no" marginwidth="0">
|
||||
</iframe>
|
||||
{% endblock %}
|
||||
|
|
|
@ -17,11 +17,11 @@ functionality to Bro:
|
|||
|
||||
- File analyzers.
|
||||
|
||||
- Packet sources and packet dumpers. TODO: Not yet.
|
||||
- Packet sources and packet dumpers.
|
||||
|
||||
- Logging framework backends. TODO: Not yet.
|
||||
- Logging framework backends.
|
||||
|
||||
- Input framework readers. TODO: Not yet.
|
||||
- Input framework readers.
|
||||
|
||||
A plugin's functionality is available to the user just as if Bro had
|
||||
the corresponding code built-in. Indeed, internally many of Bro's
|
||||
|
@ -57,10 +57,10 @@ called ``Demo::Rot13``.
|
|||
|
||||
The ``init-plugin`` script puts a number of files in place. The full
|
||||
layout is described later. For now, all we need is
|
||||
``src/functions.bif``. It's initially empty, but we'll add our new bif
|
||||
``src/rot13.bif``. It's initially empty, but we'll add our new bif
|
||||
there as follows::
|
||||
|
||||
# cat scripts/functions.bif
|
||||
# cat src/rot13.bif
|
||||
module CaesarCipher;
|
||||
|
||||
function rot13%(s: string%) : string
|
||||
|
@ -73,23 +73,25 @@ there as follows::
|
|||
*p = (*p - b + 13) % 26 + b;
|
||||
}
|
||||
|
||||
return new StringVal(new BroString(1, rot13, strlen(rot13)));
|
||||
BroString* bs = new BroString(1, reinterpret_cast<byte_vec>(rot13),
|
||||
strlen(rot13));
|
||||
return new StringVal(bs);
|
||||
%}
|
||||
|
||||
The syntax of this file is just like any other ``*.bif`` file; we
|
||||
won't go into it here.
|
||||
|
||||
Now we can already compile our plugin, we just need to tell the
|
||||
Makefile put in place by ``init-plugin`` where the Bro source tree is
|
||||
located (Bro needs to have been built there first)::
|
||||
configure script put in place by ``init-plugin`` where the Bro source
|
||||
tree is located (Bro needs to have been built there first)::
|
||||
|
||||
# make BRO=/path/to/bro/dist
|
||||
# ./configure --bro-dist=/path/to/bro/dist && make
|
||||
[... cmake output ...]
|
||||
|
||||
Now our ``rot13-plugin`` directory has everything that it needs
|
||||
for Bro to recognize it as a dynamic plugin. Once we point Bro to it,
|
||||
it will pull it in automatically, as we can check with the ``-N``
|
||||
option:
|
||||
option::
|
||||
|
||||
# export BRO_PLUGIN_PATH=/path/to/rot13-plugin
|
||||
# bro -N
|
||||
|
@ -100,7 +102,7 @@ option:
|
|||
That looks quite good, except for the dummy description that we should
|
||||
replace with something nicer so that users will know what our plugin
|
||||
is about. We do this by editing the ``config.description`` line in
|
||||
``src/Plugin.cc``, like this:
|
||||
``src/Plugin.cc``, like this::
|
||||
|
||||
[...]
|
||||
plugin::Configuration Configure()
|
||||
|
@ -193,7 +195,7 @@ directory.
|
|||
A directory with the plugin's custom Bro scripts. When the plugin
|
||||
gets activated, this directory will be automatically added to
|
||||
``BROPATH``, so that any scripts/modules inside can be
|
||||
``@load``ed.
|
||||
"@load"ed.
|
||||
|
||||
``scripts``/__load__.bro
|
||||
A Bro script that will be loaded immediately when the plugin gets
|
||||
|
@ -263,23 +265,25 @@ plugins to unconditionally activate, even in bare mode.
|
|||
activated plugins. Note that plugins compiled statically into Bro are
|
||||
always activated, and hence show up as such even in bare mode.
|
||||
|
||||
Plugin Component
|
||||
================
|
||||
Plugin Components
|
||||
=================
|
||||
|
||||
The following gives additional information about providing individual
|
||||
types of functionality via plugins. Note that a single plugin can
|
||||
provide more than one type. For example, a plugin could provide
|
||||
multiple protocol analyzers at once; or both a logging backend and
|
||||
input reader at the same time.
|
||||
The following subsections detail providing individual types of
|
||||
functionality via plugins. Note that a single plugin can provide more
|
||||
than one component type. For example, a plugin could provide multiple
|
||||
protocol analyzers at once; or both a logging backend and input reader
|
||||
at the same time.
|
||||
|
||||
We now walk briefly through the specifics of providing a specific type
|
||||
of functionality (a *component*) through a plugin. We'll focus on
|
||||
their interfaces to the plugin system, rather than specifics on
|
||||
writing the corresponding logic (usually the best way to get going on
|
||||
that is to start with an existing plugin providing a corresponding
|
||||
component and adapt that). We'll also point out how the CMake
|
||||
infrastructure put in place by the ``init-plugin`` helper script ties
|
||||
the various pieces together.
|
||||
.. todo::
|
||||
|
||||
These subsections are mostly missing right now, as much of their
|
||||
content isn't actually plugin-specific, but concerns generally
|
||||
writing such functionality for Bro. The best way to get started
|
||||
right now is to look at existing code implementing similar
|
||||
functionality, either as a plugin or inside Bro proper. Also, for
|
||||
each component type there's a unit test in
|
||||
``testing/btest/plugins`` creating a basic plugin skeleton with a
|
||||
corresponding component.
|
||||
|
||||
Bro Scripts
|
||||
-----------
|
||||
|
@ -313,22 +317,22 @@ TODO.
|
|||
Logging Writer
|
||||
--------------
|
||||
|
||||
Not yet available as plugins.
|
||||
TODO.
|
||||
|
||||
Input Reader
|
||||
------------
|
||||
|
||||
Not yet available as plugins.
|
||||
TODO.
|
||||
|
||||
Packet Sources
|
||||
--------------
|
||||
|
||||
Not yet available as plugins.
|
||||
TODO.
|
||||
|
||||
Packet Dumpers
|
||||
--------------
|
||||
|
||||
Not yet available as plugins.
|
||||
TODO.
|
||||
|
||||
Hooks
|
||||
=====
|
||||
|
@ -410,25 +414,32 @@ Run the test-suite::
|
|||
Debugging Plugins
|
||||
=================
|
||||
|
||||
Plugins can use Bro's standard debug logger by using the
|
||||
``PLUGIN_DBG_LOG(<plugin>, <args>)`` macro (defined in
|
||||
``DebugLogger.h``), where ``<plugin>`` is the ``Plugin`` instance and
|
||||
``<args>`` are printf-style arguments, just as with Bro's standard
|
||||
debuggging macros.
|
||||
If your plugin isn't loading as expected, Bro's debugging facilities
|
||||
can help to illuminate what's going on. To enable, recompile Bro
|
||||
with debugging support (``./configure --enable-debug``), and
|
||||
afterwards rebuild your plugin as well. If you then run Bro with ``-B
|
||||
plugins``, it will produce a file ``debug.log`` that records details
|
||||
about the process for searching, loading, and activating plugins.
|
||||
|
||||
At runtime, one then activates a plugin's debugging output with ``-B
|
||||
plugin-<name>``, where ``<name>`` is the name of the plugin as
|
||||
returned by its ``Configure()`` method, yet with the
|
||||
namespace-separator ``::`` replaced with a simple dash. Example: If
|
||||
the plugin is called ``Bro::Demo``, use ``-B plugin-Bro-Demo``. As
|
||||
usual, the debugging output will be recorded to ``debug.log`` if Bro's
|
||||
compiled in debug mode.
|
||||
To generate your own debugging output from inside your plugin, you can
|
||||
add a custom debug stream by using the ``PLUGIN_DBG_LOG(<plugin>,
|
||||
<args>)`` macro (defined in ``DebugLogger.h``), where ``<plugin>`` is
|
||||
the ``Plugin`` instance and ``<args>`` are printf-style arguments,
|
||||
just as with Bro's standard debugging macros (grep for ``DBG_LOG`` in
|
||||
Bro's ``src/`` to see examples). At runtime, you can then activate
|
||||
your plugin's debugging output with ``-B plugin-<name>``, where
|
||||
``<name>`` is the name of the plugin as returned by its
|
||||
``Configure()`` method, yet with the namespace-separator ``::``
|
||||
replaced with a simple dash. Example: If the plugin is called
|
||||
``Bro::Demo``, use ``-B plugin-Bro-Demo``. As usual, the debugging
|
||||
output will be recorded to ``debug.log`` if Bro's compiled in debug
|
||||
mode.
|
||||
|
||||
|
||||
Documenting Plugins
|
||||
===================
|
||||
|
||||
..todo::
|
||||
.. todo::
|
||||
|
||||
Integrate all this with Broxygen.
|
||||
|
||||
|
|
|
@ -176,6 +176,10 @@ class BroIdentifier(BroGeneric):
|
|||
def get_index_text(self, objectname, name):
|
||||
return name
|
||||
|
||||
class BroKeyword(BroGeneric):
|
||||
def get_index_text(self, objectname, name):
|
||||
return name
|
||||
|
||||
class BroAttribute(BroGeneric):
|
||||
def get_index_text(self, objectname, name):
|
||||
return _('%s (attribute)') % (name)
|
||||
|
@ -213,6 +217,7 @@ class BroDomain(Domain):
|
|||
'type': ObjType(l_('type'), 'type'),
|
||||
'namespace': ObjType(l_('namespace'), 'namespace'),
|
||||
'id': ObjType(l_('id'), 'id'),
|
||||
'keyword': ObjType(l_('keyword'), 'keyword'),
|
||||
'enum': ObjType(l_('enum'), 'enum'),
|
||||
'attr': ObjType(l_('attr'), 'attr'),
|
||||
}
|
||||
|
@ -221,6 +226,7 @@ class BroDomain(Domain):
|
|||
'type': BroGeneric,
|
||||
'namespace': BroNamespace,
|
||||
'id': BroIdentifier,
|
||||
'keyword': BroKeyword,
|
||||
'enum': BroEnum,
|
||||
'attr': BroAttribute,
|
||||
}
|
||||
|
@ -229,6 +235,7 @@ class BroDomain(Domain):
|
|||
'type': XRefRole(),
|
||||
'namespace': XRefRole(),
|
||||
'id': XRefRole(),
|
||||
'keyword': XRefRole(),
|
||||
'enum': XRefRole(),
|
||||
'attr': XRefRole(),
|
||||
'see': XRefRole(),
|
||||
|
|
|
@ -45,7 +45,13 @@ Reference Section
|
|||
script-reference/index.rst
|
||||
components/index.rst
|
||||
|
||||
..
|
||||
Development
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
devel/plugins.rst
|
||||
|
||||
* :ref:`General Index <genindex>`
|
||||
* :ref:`search`
|
||||
|
|
|
@ -180,7 +180,7 @@ automatically. Finally, use ``make install-aux`` to install some of
|
|||
the other programs that are in the ``aux/bro-aux`` directory.
|
||||
|
||||
OpenBSD users, please see our `FAQ
|
||||
<http://www.bro.org/documentation/faq.html>`_ if you are having
|
||||
<//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
|
||||
|
|
|
@ -111,7 +111,9 @@ default, including:
|
|||
such "crud" that is usually not worth following up on.
|
||||
|
||||
As you can see, some log files are specific to a particular protocol,
|
||||
while others aggregate information across different types of activity.
|
||||
while others aggregate information across different types of activity.
|
||||
For a complete list of log files and a description of its purpose,
|
||||
see :doc:`List of Log Files <../script-reference/list-of-log-files>`.
|
||||
|
||||
.. _bro-cut:
|
||||
|
||||
|
@ -250,44 +252,3 @@ protocol, it can have multiple ``GET``/``POST``/etc requests in a
|
|||
stream and Bro is able to extract and track that information for you,
|
||||
giving you an in-depth and structured view into HTTP traffic on your
|
||||
network.
|
||||
|
||||
-----------------------
|
||||
Common Log Files
|
||||
-----------------------
|
||||
As a monitoring tool, Bro records a detailed view of the traffic inspected
|
||||
and the events generated in a series of relevant log files. These files can
|
||||
later be reviewed for monitoring, auditing and troubleshooting purposes.
|
||||
|
||||
In this section we present a brief explanation of the most commonly used log
|
||||
files generated by Bro including links to descriptions of some of the fields
|
||||
for each log type.
|
||||
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+=================+=======================================+==============================+
|
||||
| http.log | Shows all HTTP requests and replies | :bro:type:`HTTP::Info` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| ftp.log | Records FTP activity | :bro:type:`FTP::Info` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| ssl.log | Records SSL sessions including | :bro:type:`SSL::Info` |
|
||||
| | certificates used | |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| known_certs.log | Includes SSL certificates used | :bro:type:`Known::CertsInfo` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| smtp.log | Summarizes SMTP traffic on a network | :bro:type:`SMTP::Info` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| dns.log | Shows all DNS activity on a network | :bro:type:`DNS::Info` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| conn.log | Records all connections seen by Bro | :bro:type:`Conn::Info` |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| dpd.log | Shows network activity on | :bro:type:`DPD::Info` |
|
||||
| | non-standard ports | |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| files.log | Records information about all files | :bro:type:`Files::Info` |
|
||||
| | transmitted over the network | |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
| weird.log | Records unexpected protocol-level | :bro:type:`Weird::Info` |
|
||||
| | activity | |
|
||||
+-----------------+---------------------------------------+------------------------------+
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
.. _FAQ: http://www.bro.org/documentation/faq.html
|
||||
.. _FAQ: //www.bro.org/documentation/faq.html
|
||||
|
||||
.. _quickstart:
|
||||
|
||||
|
|
232
doc/script-reference/attributes.rst
Normal file
232
doc/script-reference/attributes.rst
Normal file
|
@ -0,0 +1,232 @@
|
|||
Attributes
|
||||
==========
|
||||
|
||||
The Bro scripting language supports the following attributes.
|
||||
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| Name | Description |
|
||||
+=============================+===============================================+
|
||||
| :bro:attr:`&redef` |Redefine a global constant or extend a type. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&priority` |Specify priority for event handler or hook. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&log` |Mark a record field as to be written to a log. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&optional` |Allow a record field value to be missing. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&default` |Specify a default value. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&add_func` |Specify a function to call for each "redef +=".|
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&delete_func` |Same as "&add_func", except for "redef -=". |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&expire_func` |Specify a function to call when container |
|
||||
| |element expires. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&read_expire` |Specify a read timeout interval. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&write_expire` |Specify a write timeout interval. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&create_expire` |Specify a creation timeout interval. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&synchronized` |Synchronize a variable across nodes. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&persistent` |Make a variable persistent (written to disk). |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&rotate_interval`|Rotate a file after specified interval. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&rotate_size` |Rotate a file after specified file size. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&encrypt` |Encrypt a file when writing to disk. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&raw_output` |Open file in raw mode (chars. are not escaped).|
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&mergeable` |Prefer set union for synchronized state. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&group` |Group event handlers to activate/deactivate. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&error_handler` |Used internally for reporter framework events. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
| :bro:attr:`&type_column` |Used by input framework for "port" type. |
|
||||
+-----------------------------+-----------------------------------------------+
|
||||
|
||||
Here is a more detailed explanation of each attribute:
|
||||
|
||||
.. bro:attr:: &redef
|
||||
|
||||
Allows for redefinition of initial values of global objects declared as
|
||||
constant.
|
||||
|
||||
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;
|
||||
|
||||
.. bro:attr:: &priority
|
||||
|
||||
Specifies the execution priority (as a signed integer) of a hook or
|
||||
event handler. Higher values are executed before lower ones. The
|
||||
default value is 0. Example::
|
||||
|
||||
event bro_init() &priority=10
|
||||
{
|
||||
print "high priority";
|
||||
}
|
||||
|
||||
.. bro:attr:: &log
|
||||
|
||||
Writes a :bro:type:`record` field to the associated log stream.
|
||||
|
||||
.. bro:attr:: &optional
|
||||
|
||||
Allows a record field value to be missing (i.e., neither initialized nor
|
||||
ever assigned a value).
|
||||
|
||||
In this example, the record could be instantiated with either
|
||||
"myrec($a=127.0.0.1)" or "myrec($a=127.0.0.1, $b=80/tcp)"::
|
||||
|
||||
type myrec: record { a: addr; b: port &optional; };
|
||||
|
||||
The ``?$`` operator can be used to check if a record field has a value or
|
||||
not (it returns a ``bool`` value of ``T`` if the field has a value,
|
||||
and ``F`` if not).
|
||||
|
||||
.. bro:attr:: &default
|
||||
|
||||
Specifies a default value for a record field, container element, or a
|
||||
function/hook/event parameter.
|
||||
|
||||
In this example, the record could be instantiated with either
|
||||
"myrec($a=5, $c=3.14)" or "myrec($a=5, $b=53/udp, $c=3.14)"::
|
||||
|
||||
type myrec: record { a: count; b: port &default=80/tcp; c: double; };
|
||||
|
||||
In this example, the table will return the string ``"foo"`` for any
|
||||
attempted access to a non-existing index::
|
||||
|
||||
global mytable: table[count] of string &default="foo";
|
||||
|
||||
When used with function/hook/event parameters, all of the parameters
|
||||
with the "&default" attribute must come after all other parameters.
|
||||
For example, the following function could be called either as "myfunc(5)"
|
||||
or as "myfunc(5, 53/udp)"::
|
||||
|
||||
function myfunc(a: count, b: port &default=80/tcp)
|
||||
{
|
||||
print a, b;
|
||||
}
|
||||
|
||||
.. bro:attr:: &add_func
|
||||
|
||||
Can be applied to an identifier with &redef to specify a function to
|
||||
be called any time a "redef <id> += ..." declaration is parsed. The
|
||||
function takes two arguments of the same type as the identifier, the first
|
||||
being the old value of the variable and the second being the new
|
||||
value given after the "+=" operator in the "redef" declaration. The
|
||||
return value of the function will be the actual new value of the
|
||||
variable after the "redef" declaration is parsed.
|
||||
|
||||
.. bro:attr:: &delete_func
|
||||
|
||||
Same as :bro:attr:`&add_func`, except for :bro:keyword:`redef` declarations
|
||||
that use the "-=" operator.
|
||||
|
||||
.. bro:attr:: &expire_func
|
||||
|
||||
Called right before a container element expires. The function's
|
||||
first parameter is of the same type of the container and the second
|
||||
parameter the same type of the container's index. The return
|
||||
value is an :bro:type:`interval` indicating the amount of additional
|
||||
time to wait before expiring the container element at the given
|
||||
index (which will trigger another execution of this function).
|
||||
|
||||
.. bro:attr:: &read_expire
|
||||
|
||||
Specifies a read expiration timeout for container elements. That is,
|
||||
the element expires after the given amount of time since the last
|
||||
time it has been read. Note that a write also counts as a read.
|
||||
|
||||
.. bro:attr:: &write_expire
|
||||
|
||||
Specifies a write expiration timeout for container elements. That
|
||||
is, the element expires after the given amount of time since the
|
||||
last time it has been written.
|
||||
|
||||
.. bro:attr:: &create_expire
|
||||
|
||||
Specifies a creation expiration timeout for container elements. That
|
||||
is, the element expires after the given amount of time since it has
|
||||
been inserted into the container, regardless of any reads or writes.
|
||||
|
||||
.. bro:attr:: &synchronized
|
||||
|
||||
Synchronizes variable accesses across nodes. The value of a
|
||||
``&synchronized`` variable is automatically propagated to all peers
|
||||
when it changes.
|
||||
|
||||
.. bro:attr:: &persistent
|
||||
|
||||
Makes a variable persistent, i.e., its value is written to disk (per
|
||||
default at shutdown time).
|
||||
|
||||
.. bro:attr:: &rotate_interval
|
||||
|
||||
Rotates a file after a specified interval.
|
||||
|
||||
.. bro:attr:: &rotate_size
|
||||
|
||||
Rotates a file after it has reached a given size in bytes.
|
||||
|
||||
.. bro:attr:: &encrypt
|
||||
|
||||
Encrypts files right before writing them to disk.
|
||||
|
||||
.. bro:attr:: &raw_output
|
||||
|
||||
Opens a file in raw mode, i.e., non-ASCII characters are not
|
||||
escaped.
|
||||
|
||||
.. bro:attr:: &mergeable
|
||||
|
||||
Prefers merging sets on assignment for synchronized state. This
|
||||
attribute is used in conjunction with :bro:attr:`&synchronized`
|
||||
container types: when the same container is updated at two peers
|
||||
with different values, the propagation of the state causes a race
|
||||
condition, where the last update succeeds. This can cause
|
||||
inconsistencies and can be avoided by unifying the two sets, rather
|
||||
than merely overwriting the old value.
|
||||
|
||||
.. bro:attr:: &group
|
||||
|
||||
Groups event handlers such that those in the same group can be
|
||||
jointly activated or deactivated.
|
||||
|
||||
.. bro:attr:: &error_handler
|
||||
|
||||
Internally set on the events that are associated with the reporter
|
||||
framework: :bro:id:`reporter_info`, :bro:id:`reporter_warning`, and
|
||||
:bro:id:`reporter_error`. It prevents any handlers of those events
|
||||
from being able to generate reporter messages that go through any of
|
||||
those events (i.e., it prevents an infinite event recursion). Instead,
|
||||
such nested reporter messages are output to stderr.
|
||||
|
||||
.. bro:attr:: &type_column
|
||||
|
||||
Used by the input framework. It can be used on columns of type
|
||||
:bro:type:`port` (such a column only contains the port number) and
|
||||
specifies the name of an additional column in
|
||||
the input file which specifies the protocol of the port (tcp/udp/icmp).
|
||||
|
||||
In the following example, the input file would contain four columns
|
||||
named "ip", "srcp", "proto", and "msg"::
|
||||
|
||||
type Idx: record {
|
||||
ip: addr;
|
||||
};
|
||||
|
||||
|
||||
type Val: record {
|
||||
srcp: port &type_column = "proto";
|
||||
msg: string;
|
||||
};
|
||||
|
173
doc/script-reference/directives.rst
Normal file
173
doc/script-reference/directives.rst
Normal file
|
@ -0,0 +1,173 @@
|
|||
Directives
|
||||
==========
|
||||
|
||||
The Bro scripting language supports a number of directives that can
|
||||
affect which scripts will be loaded or which lines in a script will be
|
||||
executed. Directives are evaluated before script execution begins.
|
||||
|
||||
.. bro:keyword:: @DEBUG
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
.. bro:keyword:: @DIR
|
||||
|
||||
Expands to the directory pathname where the current script is located.
|
||||
|
||||
Example::
|
||||
|
||||
print "Directory:", @DIR;
|
||||
|
||||
|
||||
.. bro:keyword:: @FILENAME
|
||||
|
||||
Expands to the filename of the current script.
|
||||
|
||||
Example::
|
||||
|
||||
print "File:", @FILENAME;
|
||||
|
||||
.. bro:keyword:: @load
|
||||
|
||||
Loads the specified Bro script, specified as the relative pathname
|
||||
of the file (relative to one of the directories in Bro's file search path).
|
||||
If the Bro script filename ends with ".bro", then you don't need to
|
||||
specify the file extension. The filename cannot contain any whitespace.
|
||||
|
||||
In this example, Bro will try to load a script
|
||||
"policy/misc/capture-loss.bro" by looking in each directory in the file
|
||||
search path (the file search path can be changed by setting the BROPATH
|
||||
environment variable)::
|
||||
|
||||
@load policy/misc/capture-loss
|
||||
|
||||
If you specify the name of a directory instead of a filename, then
|
||||
Bro will try to load a file in that directory called "__load__.bro"
|
||||
(presumably that file will contain additional "@load" directives).
|
||||
|
||||
In this example, Bro will try to load a file "tuning/defaults/__load__.bro"
|
||||
by looking in each directory in the file search path::
|
||||
|
||||
@load tuning/defaults
|
||||
|
||||
The purpose of this directive is to ensure that all script dependencies
|
||||
are satisfied, and to avoid having to list every needed Bro script
|
||||
on the command-line. Bro keeps track of which scripts have been
|
||||
loaded, so it is not an error to load a script more than once (once
|
||||
a script has been loaded, any subsequent "@load" directives
|
||||
for that script are ignored).
|
||||
|
||||
|
||||
.. bro:keyword:: @load-sigs
|
||||
|
||||
This works similarly to "@load", except that in this case the filename
|
||||
represents a signature file (not a Bro script). If the signature filename
|
||||
ends with ".sig", then you don't need to specify the file extension
|
||||
in the "@load-sigs" directive. The filename cannot contain any
|
||||
whitespace.
|
||||
|
||||
In this example, Bro will try to load a signature file
|
||||
"base/protocols/ssl/dpd.sig"::
|
||||
|
||||
@load-sigs base/protocols/ssl/dpd
|
||||
|
||||
The format for a signature file is explained in the documentation for the
|
||||
`Signature Framework <../frameworks/signatures.html>`_.
|
||||
|
||||
|
||||
.. bro:keyword:: @unload
|
||||
|
||||
This specifies a Bro script that we don't want to load (so a subsequent
|
||||
attempt to load the specified script will be skipped). However,
|
||||
if the specified script has already been loaded, then this directive
|
||||
has no affect.
|
||||
|
||||
In the following example, if the "policy/misc/capture-loss.bro" script
|
||||
has not been loaded yet, then Bro will not load it::
|
||||
|
||||
@unload policy/misc/capture-loss
|
||||
|
||||
|
||||
.. bro:keyword:: @prefixes
|
||||
|
||||
Specifies a filename prefix to use when looking for script files
|
||||
to load automatically. The prefix cannot contain any whitespace.
|
||||
|
||||
In the following example, the prefix "cluster" is used and all prefixes
|
||||
that were previously specified are not used::
|
||||
|
||||
@prefixes = cluster
|
||||
|
||||
In the following example, the prefix "cluster-manager" is used in
|
||||
addition to any previously-specified prefixes::
|
||||
|
||||
@prefixes += cluster-manager
|
||||
|
||||
The way this works is that after Bro parses all script files, then for each
|
||||
loaded script Bro will take the absolute path of the script and then
|
||||
it removes the portion of the directory path that is in Bro's file
|
||||
search path. Then it replaces each "/" character with a period "."
|
||||
and then prepends the prefix (specified in the "@prefixes" directive)
|
||||
followed by a period. The resulting filename is searched for in each
|
||||
directory in Bro's file search path. If a matching file is found, then
|
||||
the file is automatically loaded.
|
||||
|
||||
For example, if a script called "local.bro" has been loaded, and a prefix
|
||||
of "test" was specified, then Bro will look for a file named
|
||||
"test.local.bro" in each directory of Bro's file search path.
|
||||
|
||||
An alternative way to specify prefixes is to use the "-p" Bro
|
||||
command-line option.
|
||||
|
||||
.. bro:keyword:: @if
|
||||
|
||||
The specified expression must evaluate to type :bro:type:`bool`. If the
|
||||
value is true, then the following script lines (up to the next "@else"
|
||||
or "@endif") are available to be executed.
|
||||
|
||||
Example::
|
||||
|
||||
@if ( ver == 2 )
|
||||
print "version 2 detected";
|
||||
@endif
|
||||
|
||||
.. bro:keyword:: @ifdef
|
||||
|
||||
This works like "@if", except that the result is true if the specified
|
||||
identifier is defined.
|
||||
|
||||
Example::
|
||||
|
||||
@ifdef ( pi )
|
||||
print "pi is defined";
|
||||
@endif
|
||||
|
||||
.. bro:keyword:: @ifndef
|
||||
|
||||
This works exactly like "@ifdef", except that the result is true if the
|
||||
specified identifier is not defined.
|
||||
|
||||
Example::
|
||||
|
||||
@ifndef ( pi )
|
||||
print "pi is not defined";
|
||||
@endif
|
||||
|
||||
.. bro:keyword:: @else
|
||||
|
||||
This directive is optional after an "@if", "@ifdef", or
|
||||
"@ifndef". If present, it provides an else clause.
|
||||
|
||||
Example::
|
||||
|
||||
@ifdef ( pi )
|
||||
print "pi is defined";
|
||||
@else
|
||||
print "pi is not defined";
|
||||
@endif
|
||||
|
||||
.. bro:keyword:: @endif
|
||||
|
||||
This directive is required to terminate each "@if", "@ifdef", or
|
||||
"@ifndef".
|
||||
|
|
@ -5,10 +5,17 @@ Script Reference
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
operators
|
||||
types
|
||||
attributes
|
||||
statements
|
||||
directives
|
||||
log-files
|
||||
notices
|
||||
proto-analyzers
|
||||
file-analyzers
|
||||
builtins
|
||||
packages
|
||||
scripts
|
||||
Broxygen Example Script </scripts/broxygen/example.bro>
|
||||
|
||||
|
||||
|
|
148
doc/script-reference/log-files.rst
Normal file
148
doc/script-reference/log-files.rst
Normal file
|
@ -0,0 +1,148 @@
|
|||
=========
|
||||
Log Files
|
||||
=========
|
||||
|
||||
Listed below are the log files generated by Bro, including a brief description
|
||||
of the log file and links to descriptions of the fields for each log
|
||||
type.
|
||||
|
||||
Network Protocols
|
||||
-----------------
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| conn.log | TCP/UDP/ICMP connections | :bro:type:`Conn::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| dhcp.log | DHCP leases | :bro:type:`DHCP::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| dnp3.log | DNP3 requests and replies | :bro:type:`DNP3::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| dns.log | DNS activity | :bro:type:`DNS::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| ftp.log | FTP activity | :bro:type:`FTP::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| http.log | HTTP requests and replies | :bro:type:`HTTP::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| irc.log | IRC commands and responses | :bro:type:`IRC::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| modbus.log | Modbus commands and responses | :bro:type:`Modbus::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| modbus_register_change.log | Tracks changes to Modbus holding | :bro:type:`Modbus::MemmapInfo` |
|
||||
| | registers | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| radius.log | RADIUS authentication attempts | :bro:type:`RADIUS::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| smtp.log | SMTP transactions | :bro:type:`SMTP::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| snmp.log | SNMP messages | :bro:type:`SNMP::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| socks.log | SOCKS proxy requests | :bro:type:`SOCKS::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| ssh.log | SSH connections | :bro:type:`SSH::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| ssl.log | SSL/TLS handshake info | :bro:type:`SSL::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| syslog.log | Syslog messages | :bro:type:`Syslog::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| tunnel.log | Tunneling protocol events | :bro:type:`Tunnel::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
||||
Files
|
||||
-----
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| files.log | File analysis results | :bro:type:`Files::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| x509.log | X.509 certificate info | :bro:type:`X509::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
||||
Detection
|
||||
---------
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| intel.log | Intelligence data matches | :bro:type:`Intel::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| notice.log | Bro notices | :bro:type:`Notice::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| notice_alarm.log | The alarm stream | :bro:enum:`Notice::ACTION_ALARM`|
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| signatures.log | Signature matches | :bro:type:`Signatures::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| traceroute.log | Traceroute detection | :bro:type:`Traceroute::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
||||
|
||||
Network Observations
|
||||
--------------------
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| app_stats.log | Web app usage statistics | :bro:type:`AppStats::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| known_certs.log | SSL certificates | :bro:type:`Known::CertsInfo` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| known_devices.log | MAC addresses of devices on the | :bro:type:`Known::DevicesInfo` |
|
||||
| | network | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| known_hosts.log | Hosts that have completed TCP | :bro:type:`Known::HostsInfo` |
|
||||
| | handshakes | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| known_modbus.log | Modbus masters and slaves | :bro:type:`Known::ModbusInfo` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| known_services.log | Services running on hosts | :bro:type:`Known::ServicesInfo` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| software.log | Software being used on the network | :bro:type:`Software::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| barnyard2.log | Alerts received from Barnyard2 | :bro:type:`Barnyard2::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| dpd.log | Dynamic protocol detection failures | :bro:type:`DPD::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| unified2.log | Interprets Snort's unified output | :bro:type:`Unified2::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| weird.log | Unexpected network-level activity | :bro:type:`Weird::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
||||
Bro Diagnostics
|
||||
---------------
|
||||
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| Log File | Description | Field Descriptions |
|
||||
+============================+=======================================+=================================+
|
||||
| capture_loss.log | Packet loss rate | :bro:type:`CaptureLoss::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| cluster.log | Bro cluster messages | :bro:type:`Cluster::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| communication.log | Communication events between Bro or | :bro:type:`Communication::Info` |
|
||||
| | Broccoli instances | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| loaded_scripts.log | Shows all scripts loaded by Bro | :bro:type:`LoadedScripts::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| packet_filter.log | List packet filters that were applied | :bro:type:`PacketFilter::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| prof.log | Profiling statistics (to create this | N/A |
|
||||
| | log, load policy/misc/profiling.bro) | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| reporter.log | Internal error/warning/info messages | :bro:type:`Reporter::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| stats.log | Memory/event/packet/lag statistics | :bro:type:`Stats::Info` |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| stderr.log | Captures standard error when Bro is | N/A |
|
||||
| | started from BroControl | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
| stdout.log | Captures standard output when Bro is | N/A |
|
||||
| | started from BroControl | |
|
||||
+----------------------------+---------------------------------------+---------------------------------+
|
||||
|
191
doc/script-reference/operators.rst
Normal file
191
doc/script-reference/operators.rst
Normal file
|
@ -0,0 +1,191 @@
|
|||
Operators
|
||||
=========
|
||||
|
||||
The Bro scripting language supports the following operators. Note that
|
||||
each data type only supports a subset of these operators. For more
|
||||
details, see the documentation about the `data types <types.html>`_.
|
||||
|
||||
Relational operators
|
||||
--------------------
|
||||
|
||||
The relational operators evaluate to type :bro:type:`bool`.
|
||||
|
||||
+------------------------------+--------------+
|
||||
| Name | Syntax |
|
||||
+==============================+==============+
|
||||
| Equality | *a* == *b* |
|
||||
+------------------------------+--------------+
|
||||
| Inequality | *a* != *b* |
|
||||
+------------------------------+--------------+
|
||||
| Less than | *a* < *b* |
|
||||
+------------------------------+--------------+
|
||||
| Less than or equal | *a* <= *b* |
|
||||
+------------------------------+--------------+
|
||||
| Greater than | *a* > *b* |
|
||||
+------------------------------+--------------+
|
||||
| Greater than or equal | *a* >= *b* |
|
||||
+------------------------------+--------------+
|
||||
|
||||
|
||||
Logical operators
|
||||
-----------------
|
||||
|
||||
The logical operators require operands of type :bro:type:`bool`, and
|
||||
evaluate to type :bro:type:`bool`.
|
||||
|
||||
+------------------------------+--------------+
|
||||
| Name | Syntax |
|
||||
+==============================+==============+
|
||||
| Logical AND | *a* && *b* |
|
||||
+------------------------------+--------------+
|
||||
| Logical OR | *a* \|\| *b* |
|
||||
+------------------------------+--------------+
|
||||
| Logical NOT | ! *a* |
|
||||
+------------------------------+--------------+
|
||||
|
||||
|
||||
Arithmetic operators
|
||||
--------------------
|
||||
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Name | Syntax | Notes |
|
||||
+==============================+=============+===============================+
|
||||
| Addition | *a* + *b* | For :bro:type:`string` |
|
||||
| | | operands, this performs |
|
||||
| | | string concatenation. |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Subtraction | *a* - *b* | |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Multiplication | *a* \* *b* | |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Division | *a* / *b* | For :bro:type:`int` or |
|
||||
| | | :bro:type:`count` operands, |
|
||||
| | | the fractional part of the |
|
||||
| | | result is dropped. |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Modulo | *a* % *b* | Operand types cannot be |
|
||||
| | | "double". |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Unary plus | \+ *a* | |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Unary minus | \- *a* | |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Pre-increment | ++ *a* | Operand type cannot be |
|
||||
| | | "double". |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Pre-decrement | ``--`` *a* | Operand type cannot be |
|
||||
| | | "double". |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Absolute value | \| *a* \| | If operand is |
|
||||
| | | :bro:type:`string`, |
|
||||
| | | :bro:type:`set`, |
|
||||
| | | :bro:type:`table`, or |
|
||||
| | | :bro:type:`vector`, this |
|
||||
| | | evaluates to number |
|
||||
| | | of elements. |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
|
||||
|
||||
Assignment operators
|
||||
--------------------
|
||||
|
||||
The assignment operators evaluate to the result of the assignment.
|
||||
|
||||
+------------------------------+-------------+
|
||||
| Name | Syntax |
|
||||
+==============================+=============+
|
||||
| Assignment | *a* = *b* |
|
||||
+------------------------------+-------------+
|
||||
| Addition assignment | *a* += *b* |
|
||||
+------------------------------+-------------+
|
||||
| Subtraction assignment | *a* -= *b* |
|
||||
+------------------------------+-------------+
|
||||
|
||||
|
||||
Record field operators
|
||||
----------------------
|
||||
|
||||
The record field operators take a :bro:type:`record` as the first operand,
|
||||
and a field name as the second operand. For both operators, the specified
|
||||
field name must be in the declaration of the record type.
|
||||
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Name | Syntax | Notes |
|
||||
+==============================+=============+===============================+
|
||||
| Field access | *a* $ *b* | |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
| Field value existence test | *a* ?$ *b* | Evaluates to type |
|
||||
| | | :bro:type:`bool`. |
|
||||
| | | True if the specified field |
|
||||
| | | has been assigned a value, or |
|
||||
| | | false if not. |
|
||||
+------------------------------+-------------+-------------------------------+
|
||||
|
||||
|
||||
Other operators
|
||||
---------------
|
||||
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Name | Syntax | Notes |
|
||||
+================================+===================+========================+
|
||||
| Membership test | *a* in *b* |Evaluates to type |
|
||||
| | |:bro:type:`bool`. Do not|
|
||||
| | |confuse this use of "in"|
|
||||
| | |with that used in a |
|
||||
| | |:bro:keyword:`for` |
|
||||
| | |statement. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Non-membership test | *a* !in *b* |This is the logical NOT |
|
||||
| | |of the "in" operator. |
|
||||
| | |For example: "a !in b" |
|
||||
| | |is equivalent to |
|
||||
| | |"!(a in b)". |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Table or vector element access | *a* [ *b* ] |This operator can also |
|
||||
| | |be used with a |
|
||||
| | |:bro:type:`set`, but |
|
||||
| | |only with the |
|
||||
| | |:bro:keyword:`add` or |
|
||||
| | |:bro:keyword:`delete` |
|
||||
| | |statement. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Substring extraction | *a* [ *b* : *c* ] |See the |
|
||||
| | |:bro:type:`string` type |
|
||||
| | |for more details. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Create a deep copy | copy ( *a* ) |This is relevant only |
|
||||
| | |for data types that are |
|
||||
| | |assigned by reference, |
|
||||
| | |such as |
|
||||
| | |:bro:type:`vector`, |
|
||||
| | |:bro:type:`set`, |
|
||||
| | |:bro:type:`table`, |
|
||||
| | |and :bro:type:`record`. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Module namespace access | *a* \:\: *b* |The first operand is the|
|
||||
| | |module name, and the |
|
||||
| | |second operand is an |
|
||||
| | |identifier that refers |
|
||||
| | |to a global variable, |
|
||||
| | |enumeration constant, or|
|
||||
| | |user-defined type that |
|
||||
| | |was exported from the |
|
||||
| | |module. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
| Conditional | *a* ? *b* : *c* |The first operand must |
|
||||
| | |evaluate to type |
|
||||
| | |:bro:type:`bool`. |
|
||||
| | |If true, then the |
|
||||
| | |second expression is |
|
||||
| | |evaluated and is the |
|
||||
| | |result of the entire |
|
||||
| | |expression. Otherwise, |
|
||||
| | |the third expression is |
|
||||
| | |evaluated and is the |
|
||||
| | |result of the entire |
|
||||
| | |expression. The types of|
|
||||
| | |the second and third |
|
||||
| | |operands must be |
|
||||
| | |compatible. |
|
||||
+--------------------------------+-------------------+------------------------+
|
||||
|
602
doc/script-reference/statements.rst
Normal file
602
doc/script-reference/statements.rst
Normal file
|
@ -0,0 +1,602 @@
|
|||
Declarations and Statements
|
||||
===========================
|
||||
|
||||
The Bro scripting language supports the following declarations and
|
||||
statements.
|
||||
|
||||
|
||||
Declarations
|
||||
~~~~~~~~~~~~
|
||||
|
||||
+----------------------------+-----------------------------+
|
||||
| Name | Description |
|
||||
+============================+=============================+
|
||||
| :bro:keyword:`module` | Change the current module |
|
||||
+----------------------------+-----------------------------+
|
||||
| :bro:keyword:`export` | Export identifiers from the |
|
||||
| | current module |
|
||||
+----------------------------+-----------------------------+
|
||||
| :bro:keyword:`global` | Declare a global variable |
|
||||
+----------------------------+-----------------------------+
|
||||
| :bro:keyword:`const` | Declare a constant |
|
||||
+----------------------------+-----------------------------+
|
||||
| :bro:keyword:`type` | Declare a user-defined type |
|
||||
+----------------------------+-----------------------------+
|
||||
| :bro:keyword:`redef` | Redefine a global value or |
|
||||
| | extend a user-defined type |
|
||||
+----------------------------+-----------------------------+
|
||||
| `function/event/hook`_ | Declare a function, event |
|
||||
| | handler, or hook |
|
||||
+----------------------------+-----------------------------+
|
||||
|
||||
Statements
|
||||
~~~~~~~~~~
|
||||
|
||||
+----------------------------+------------------------+
|
||||
| Name | Description |
|
||||
+============================+========================+
|
||||
| :bro:keyword:`local` | Declare a local |
|
||||
| | variable |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`add`, | Add or delete |
|
||||
| :bro:keyword:`delete` | elements |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`print` | Print to stdout or a |
|
||||
| | file |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`for`, | Loop over each |
|
||||
| :bro:keyword:`next`, | element in a container |
|
||||
| :bro:keyword:`break` | object |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`if` | Evaluate boolean |
|
||||
| | expression and if true,|
|
||||
| | execute a statement |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`switch`, | Evaluate expression |
|
||||
| :bro:keyword:`break`, | and execute statement |
|
||||
| :bro:keyword:`fallthrough` | with a matching value |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`when` | Asynchronous execution |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`event`, | Invoke or schedule |
|
||||
| :bro:keyword:`schedule` | an event handler |
|
||||
+----------------------------+------------------------+
|
||||
| :bro:keyword:`return` | Return from function, |
|
||||
| | hook, or event handler |
|
||||
+----------------------------+------------------------+
|
||||
|
||||
Declarations
|
||||
------------
|
||||
|
||||
The following global declarations cannot occur within a function, hook, or
|
||||
event handler. Also, these declarations cannot appear after any statements
|
||||
that are outside of a function, hook, or event handler.
|
||||
|
||||
.. bro:keyword:: module
|
||||
|
||||
The "module" keyword is used to change the current module. This
|
||||
affects the scope of any subsequently declared global identifiers.
|
||||
|
||||
Example::
|
||||
|
||||
module mymodule;
|
||||
|
||||
If a global identifier is declared after a "module" declaration,
|
||||
then its scope ends at the end of the current Bro script or at the
|
||||
next "module" declaration, whichever comes first. However, if a
|
||||
global identifier is declared after a "module" declaration, but inside
|
||||
an :bro:keyword:`export` block, then its scope ends at the end of the
|
||||
last loaded Bro script, but it must be referenced using the namespace
|
||||
operator (``::``) in other modules.
|
||||
|
||||
There can be any number of "module" declarations in a Bro script.
|
||||
The same "module" declaration can appear in any number of different
|
||||
Bro scripts.
|
||||
|
||||
|
||||
.. bro:keyword:: export
|
||||
|
||||
An "export" block contains one or more declarations
|
||||
(no statements are allowed in an "export" block) that the current
|
||||
module is exporting. This enables these global identifiers to be visible
|
||||
in other modules (but not prior to their declaration) via the namespace
|
||||
operator (``::``). See the :bro:keyword:`module` keyword for a more
|
||||
detailed explanation.
|
||||
|
||||
Example::
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
};
|
||||
|
||||
const conntime = 30sec &redef;
|
||||
}
|
||||
|
||||
Note that the braces in an "export" block are always required
|
||||
(they do not indicate a compound statement). Also, no semicolon is
|
||||
needed to terminate an "export" block.
|
||||
|
||||
.. bro:keyword:: global
|
||||
|
||||
Variables declared with the "global" keyword will be global.
|
||||
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,
|
||||
then the type must be specified. Example::
|
||||
|
||||
global pi = 3.14;
|
||||
global hosts: set[addr];
|
||||
global ciphers: table[string] of string = table();
|
||||
|
||||
Variable declarations outside of any function, hook, or event handler are
|
||||
required to use this keyword (unless they are declared with the
|
||||
:bro:keyword:`const` keyword). Definitions of functions, hooks, and
|
||||
event handlers are not allowed to use the "global"
|
||||
keyword (they already have global scope), except function declarations
|
||||
where no function body is supplied use the "global" keyword.
|
||||
|
||||
The scope of a global variable begins where the declaration is located,
|
||||
and extends through all remaining Bro scripts that are loaded (however,
|
||||
see the :bro:keyword:`module` keyword for an explanation of how modules
|
||||
change the visibility of global identifiers).
|
||||
|
||||
|
||||
.. bro:keyword:: const
|
||||
|
||||
A variable declared with the "const" keyword will be constant.
|
||||
Variables declared as constant are required to be initialized at the
|
||||
time of declaration. Example::
|
||||
|
||||
const pi = 3.14;
|
||||
const ssh_port: port = 22/tcp;
|
||||
|
||||
The value of a constant cannot be changed later (the only
|
||||
exception is if the variable is global and has the :bro:attr:`&redef`
|
||||
attribute, 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
|
||||
function, hook, or event handler, and global otherwise.
|
||||
Note that the "const" keyword cannot be used with either the "local"
|
||||
or "global" keywords (i.e., "const" replaces "local" and "global").
|
||||
|
||||
|
||||
.. bro:keyword:: type
|
||||
|
||||
The "type" keyword is used to declare a user-defined type. The name
|
||||
of this new type has global scope and can be used anywhere a built-in
|
||||
type name can occur.
|
||||
|
||||
The "type" keyword is most commonly used when defining a
|
||||
:bro:type:`record` or an :bro:type:`enum`, but is also useful when
|
||||
dealing with more complex types.
|
||||
|
||||
Example::
|
||||
|
||||
type mytype: table[count] of table[addr, port] of string;
|
||||
global myvar: mytype;
|
||||
|
||||
.. bro:keyword:: redef
|
||||
|
||||
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 new event handler body that replaces all those that were previously
|
||||
defined.
|
||||
|
||||
If you're using "redef" to change a global variable (defined using either
|
||||
:bro:keyword:`const` or :bro:keyword:`global`), then the variable that you
|
||||
want to change must have the :bro:attr:`&redef` attribute. If the variable
|
||||
you're changing is a table, set, or pattern, you can use ``+=`` to add
|
||||
new elements, or you can use ``=`` to specify a new value (all previous
|
||||
contents of the object are removed). If the variable you're changing is a
|
||||
set or table, then you can use the ``-=`` operator to remove the
|
||||
specified elements (nothing happens for specified elements that don't
|
||||
exist). If the variable you are changing is not a table, set, or pattern,
|
||||
then you must use the ``=`` operator.
|
||||
|
||||
Examples::
|
||||
|
||||
redef pi = 3.14;
|
||||
|
||||
If you're using "redef" to extend a record or enum, then you must
|
||||
use the ``+=`` assignment operator.
|
||||
For an enum, you can add more enumeration constants, and for a record
|
||||
you can add more record fields (however, each record field in the "redef"
|
||||
must have either the :bro:attr:`&optional` or :bro:attr:`&default`
|
||||
attribute).
|
||||
|
||||
Examples::
|
||||
|
||||
redef enum color += { Blue, Red };
|
||||
redef record MyRecord += { n2:int &optional; s2:string &optional; };
|
||||
|
||||
If you're using "redef" to specify a new event handler body that
|
||||
replaces all those that were previously defined (i.e., any subsequently
|
||||
defined event handler body will not be affected by this "redef"), then
|
||||
the syntax is the same as a regular event handler definition except for
|
||||
the presence of the "redef" keyword.
|
||||
|
||||
Example::
|
||||
|
||||
redef event myevent(s:string) { print "Redefined", s; }
|
||||
|
||||
|
||||
.. _function/event/hook:
|
||||
|
||||
**function/event/hook**
|
||||
For details on how to declare a :bro:type:`function`,
|
||||
:bro:type:`event` handler, or :bro:type:`hook`,
|
||||
see the documentation for those types.
|
||||
|
||||
|
||||
Statements
|
||||
----------
|
||||
|
||||
Each statement in a Bro script must be terminated with a semicolon (with a
|
||||
few exceptions noted below). An individual statement can span multiple
|
||||
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.
|
||||
|
||||
.. bro:keyword:: add
|
||||
|
||||
The "add" statement is used to add an element to a :bro:type:`set`.
|
||||
Nothing happens if the specified element already exists in the set.
|
||||
|
||||
Example::
|
||||
|
||||
local myset: set[string];
|
||||
add myset["test"];
|
||||
|
||||
.. bro:keyword:: break
|
||||
|
||||
The "break" statement is used to break out of a :bro:keyword:`switch` or
|
||||
:bro:keyword:`for` statement.
|
||||
|
||||
|
||||
.. bro:keyword:: delete
|
||||
|
||||
The "delete" statement is used to remove an element from a
|
||||
:bro:type:`set` or :bro:type:`table`. Nothing happens if the
|
||||
specified element does not exist in the set or table.
|
||||
|
||||
Example::
|
||||
|
||||
local myset = set("this", "test");
|
||||
local mytable = table(["key1"] = 80/tcp, ["key2"] = 53/udp);
|
||||
delete myset["test"];
|
||||
delete mytable["key1"];
|
||||
|
||||
.. bro:keyword:: event
|
||||
|
||||
The "event" statement immediately queues invocation of an event handler.
|
||||
|
||||
Example::
|
||||
|
||||
event myevent("test", 5);
|
||||
|
||||
.. bro:keyword:: fallthrough
|
||||
|
||||
The "fallthrough" statement can be used as the last statement in a
|
||||
"case" block to indicate that execution should continue into the
|
||||
next "case" or "default" label.
|
||||
|
||||
For an example, see the :bro:keyword:`switch` statement.
|
||||
|
||||
.. bro:keyword:: for
|
||||
|
||||
A "for" loop iterates over each element in a string, set, vector, or
|
||||
table and executes a statement for each iteration.
|
||||
|
||||
For each iteration of the loop, a loop variable will be assigned to an
|
||||
element if the expression evaluates to a string or set, or an index if
|
||||
the expression evaluates to a vector or table. Then the statement
|
||||
is executed. However, the statement will not be executed if the expression
|
||||
evaluates to an object with no elements.
|
||||
|
||||
If the expression is a table or a set with more than one index, then the
|
||||
loop variable must be specified as a comma-separated list of different
|
||||
loop variables (one for each index), enclosed in brackets.
|
||||
|
||||
A :bro:keyword:`break` statement can be used at any time to immediately
|
||||
terminate the "for" loop, and a :bro:keyword:`next` statement can be
|
||||
used to skip to the next loop iteration.
|
||||
|
||||
Note that the loop variable in a "for" statement is not allowed to be
|
||||
a global variable, and it does not need to be declared prior to the "for"
|
||||
statement. The type will be inferred from the elements of the
|
||||
expression.
|
||||
|
||||
Example::
|
||||
|
||||
local myset = set(80/tcp, 81/tcp);
|
||||
local mytable = table([10.0.0.1, 80/tcp]="s1", [10.0.0.2, 81/tcp]="s2");
|
||||
|
||||
for (p in myset)
|
||||
print p;
|
||||
|
||||
for ([i,j] in mytable) {
|
||||
if (mytable[i,j] == "done")
|
||||
break;
|
||||
if (mytable[i,j] == "skip")
|
||||
next;
|
||||
print i,j;
|
||||
}
|
||||
|
||||
|
||||
.. bro:keyword:: if
|
||||
|
||||
Evaluates a given expression, which must yield a :bro:type:`bool` value.
|
||||
If true, then a specified statement is executed. If false, then
|
||||
the statement is not executed. Example::
|
||||
|
||||
if ( x == 2 ) print "x is 2";
|
||||
|
||||
|
||||
However, if the expression evaluates to false and if an "else" is
|
||||
provided, then the statement following the "else" is executed. Example::
|
||||
|
||||
if ( x == 2 )
|
||||
print "x is 2";
|
||||
else
|
||||
print "x is not 2";
|
||||
|
||||
.. bro:keyword:: local
|
||||
|
||||
A variable declared with the "local" keyword will be local. 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, then the
|
||||
type must be specified.
|
||||
|
||||
Examples::
|
||||
|
||||
local x1 = 5.7;
|
||||
local x2: double;
|
||||
local x3: double = 5.7;
|
||||
|
||||
Variable declarations inside a function, hook, or event handler are
|
||||
required to use this keyword (the only two exceptions are variables
|
||||
declared with :bro:keyword:`const`, and variables implicitly declared in a
|
||||
:bro:keyword:`for` statement).
|
||||
|
||||
The scope of a local variable starts at the location where it is declared
|
||||
and persists to the end of the function, hook,
|
||||
or event handler in which it is declared (this is true even if the
|
||||
local variable was declared within a `compound statement`_ or is the loop
|
||||
variable in a "for" statement).
|
||||
|
||||
|
||||
.. bro:keyword:: next
|
||||
|
||||
The "next" statement can only appear within a :bro:keyword:`for` loop.
|
||||
It causes execution to skip to the next iteration.
|
||||
|
||||
For an example, see the :bro:keyword:`for` statement.
|
||||
|
||||
.. bro:keyword:: print
|
||||
|
||||
The "print" statement takes a comma-separated list of one or more
|
||||
expressions. Each expression in the list is evaluated and then converted
|
||||
to a string. Then each string is printed, with each string separated by
|
||||
a comma in the output.
|
||||
|
||||
Examples::
|
||||
|
||||
print 3.14;
|
||||
print "Results", x, y;
|
||||
|
||||
By default, the "print" statement writes to the standard
|
||||
output (stdout). However, if the first expression is of type
|
||||
:bro:type:`file`, then "print" writes to that file.
|
||||
|
||||
If a string contains non-printable characters (i.e., byte values that are
|
||||
not in the range 32 - 126), then the "print" statement converts each
|
||||
non-printable character to an escape sequence before it is printed.
|
||||
|
||||
For more control over how the strings are formatted, see the :bro:id:`fmt`
|
||||
function.
|
||||
|
||||
.. bro:keyword:: return
|
||||
|
||||
The "return" statement immediately exits the current function, hook, or
|
||||
event handler. For a function, the specified expression (if any) is
|
||||
evaluated and returned. A "return" statement in a hook or event handler
|
||||
cannot return a value because event handlers and hooks do not have
|
||||
return types.
|
||||
|
||||
Examples::
|
||||
|
||||
function my_func(): string
|
||||
{
|
||||
return "done";
|
||||
}
|
||||
|
||||
event my_event(n: count)
|
||||
{
|
||||
if ( n == 0 ) return;
|
||||
|
||||
print n;
|
||||
}
|
||||
|
||||
There is a special form of the "return" statement that is only allowed
|
||||
in functions. Syntactically, it looks like a :bro:keyword:`when` statement
|
||||
immediately preceded by the "return" keyword. This form of the "return"
|
||||
statement is used to specify a function that delays its result (such a
|
||||
function can only be called in the expression of a :bro:keyword:`when`
|
||||
statement). The function returns at the time the "when"
|
||||
statement's condition becomes true, and the function returns the value
|
||||
that the "when" statement's body returns (or if the condition does
|
||||
not become true within the specified timeout interval, then the function
|
||||
returns the value that the "timeout" block returns).
|
||||
|
||||
Example::
|
||||
|
||||
global X: table[string] of count;
|
||||
|
||||
function a() : count
|
||||
{
|
||||
# This delays until condition becomes true.
|
||||
return when ( "a" in X )
|
||||
{
|
||||
return X["a"];
|
||||
}
|
||||
timeout 30 sec
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
# Installs a trigger which fires if a() returns 42.
|
||||
when ( a() == 42 )
|
||||
print "expected result";
|
||||
|
||||
print "Waiting for a() to return...";
|
||||
X["a"] = 42;
|
||||
}
|
||||
|
||||
|
||||
.. bro:keyword:: schedule
|
||||
|
||||
The "schedule" statement is used to raise a specified event with
|
||||
specified parameters at a later time specified as an :bro:type:`interval`.
|
||||
|
||||
Example::
|
||||
|
||||
schedule 30sec { myevent(x, y, z) };
|
||||
|
||||
Note that the braces are always required (they do not indicate a
|
||||
`compound statement`_).
|
||||
|
||||
Note that "schedule" is actually an expression that returns a value
|
||||
of type "timer", but in practice the return value is not used.
|
||||
|
||||
.. bro:keyword:: switch
|
||||
|
||||
A "switch" statement evaluates a given expression and jumps to
|
||||
the first "case" label which contains a matching value (the result of the
|
||||
expression must be type-compatible with all of the values in all of the
|
||||
"case" labels). If there is no matching value, then execution jumps to
|
||||
the "default" label instead, and if there is no "default" label then
|
||||
execution jumps out of the "switch" block.
|
||||
|
||||
Here is an example (assuming that "get_day_of_week" is a
|
||||
function that returns a string)::
|
||||
|
||||
switch get_day_of_week()
|
||||
{
|
||||
case "Sa", "Su":
|
||||
print "weekend";
|
||||
fallthrough;
|
||||
case "Mo", "Tu", "We", "Th", "Fr":
|
||||
print "valid result";
|
||||
break;
|
||||
default:
|
||||
print "invalid result";
|
||||
break;
|
||||
}
|
||||
|
||||
A "switch" block can have any number of "case" labels, and one
|
||||
optional "default" label.
|
||||
|
||||
A "case" label can have a comma-separated list of
|
||||
more than one value. A value in a "case" label can be an expression,
|
||||
but it must be a constant expression (i.e., the expression can consist
|
||||
only of constants).
|
||||
|
||||
Each "case" and the "default" block must
|
||||
end with either a :bro:keyword:`break`, :bro:keyword:`fallthrough`, or
|
||||
:bro:keyword:`return` statement (although "return" is allowed only
|
||||
if the "switch" statement is inside a function, hook, or event handler).
|
||||
If a "case" (or "default") block contain more than one statement, then
|
||||
there is no need to wrap them in braces.
|
||||
|
||||
Note that the braces in a "switch" statement are always required (these
|
||||
do not indicate the presence of a `compound statement`_), and that no
|
||||
semicolon is needed at the end of a "switch" statement.
|
||||
|
||||
|
||||
.. bro:keyword:: when
|
||||
|
||||
Evaluates a given expression, which must result in a value of type
|
||||
:bro:type:`bool`. When the value of the expression becomes available
|
||||
and if the result is true, then a specified statement is executed.
|
||||
|
||||
In the following example, if the expression evaluates to true, then
|
||||
the "print" statement is executed::
|
||||
|
||||
when ( (local x = foo()) && x == 42 )
|
||||
print x;
|
||||
|
||||
However, if a timeout is specified, and if the expression does not
|
||||
evaluate to true within the specified timeout interval, then the
|
||||
statement following the "timeout" keyword is executed::
|
||||
|
||||
when ( (local x = foo()) && x == 42 )
|
||||
print x;
|
||||
timeout 5sec {
|
||||
print "timeout";
|
||||
}
|
||||
|
||||
Note that when a timeout is specified the braces are
|
||||
always required (these do not indicate a `compound statement`_).
|
||||
|
||||
The expression in a "when" statement can contain a declaration of a local
|
||||
variable but only if the declaration is written in the form
|
||||
"local *var* = *init*" (example: "local x = myfunction()"). This form
|
||||
of a local declaration is actually an expression, the result of which
|
||||
is always a boolean true value.
|
||||
|
||||
The expression in a "when" statement can contain an asynchronous function
|
||||
call such as :bro:id:`lookup_hostname` (in fact, this is the only place
|
||||
such a function can be called), but it can also contain an ordinary
|
||||
function call. When an asynchronous function call is in the expression,
|
||||
then Bro will continue processing statements in the script following
|
||||
the "when" statement, and when the result of the function call is available
|
||||
Bro will finish evaluating the expression in the "when" statement.
|
||||
See the :bro:keyword:`return` statement for an explanation of how to
|
||||
create an asynchronous function in a Bro script.
|
||||
|
||||
|
||||
.. _compound statement:
|
||||
|
||||
**compound statement**
|
||||
A compound statement is created by wrapping zero or more statements in
|
||||
braces ``{ }``. Individual statements inside the braces need to be
|
||||
terminated by a semicolon, but a semicolon is not needed at the end
|
||||
(outside of the braces) of a compound statement.
|
||||
|
||||
A compound statement is required in order to execute more than one
|
||||
statement in the body of a :bro:keyword:`for`, :bro:keyword:`if`, or
|
||||
:bro:keyword:`when` statement.
|
||||
|
||||
Example::
|
||||
|
||||
if ( x == 2 ) {
|
||||
print "x is 2";
|
||||
++x;
|
||||
}
|
||||
|
||||
Note that there are other places in the Bro scripting language that use
|
||||
braces, but that do not indicate the presence of a compound
|
||||
statement (these are noted in the documentation).
|
||||
|
||||
.. _null:
|
||||
|
||||
**null statement**
|
||||
The null statement (executing it has no effect) consists of just a
|
||||
semicolon. This might be useful during testing or debugging a Bro script
|
||||
in places where a statement is required, but it is probably not useful
|
||||
otherwise.
|
||||
|
||||
Example::
|
||||
|
||||
if ( x == 2 )
|
||||
;
|
||||
|
|
@ -1,106 +1,128 @@
|
|||
Types and Attributes
|
||||
====================
|
||||
|
||||
Types
|
||||
-----
|
||||
=====
|
||||
|
||||
Every value in a Bro script has a type (see below for a list of all built-in
|
||||
types). Although Bro variables have static types (meaning that their type
|
||||
is fixed), their type is inferred from the value to which they are
|
||||
initially assigned when the variable is declared without an explicit type
|
||||
name.
|
||||
The Bro scripting language supports the following built-in types:
|
||||
|
||||
Automatic conversions happen when a binary operator has operands of
|
||||
different types. Automatic conversions are limited to converting between
|
||||
numeric types. The numeric types are ``int``, ``count``, and ``double``
|
||||
(``bool`` is not a numeric type).
|
||||
When an automatic conversion occurs, values are promoted to the "highest"
|
||||
type in the expression. In general, this promotion follows a simple
|
||||
hierarchy: ``double`` is highest, ``int`` comes next, and ``count`` is
|
||||
lowest.
|
||||
+-----------------------+--------------------+
|
||||
| Name | Description |
|
||||
+=======================+====================+
|
||||
| :bro:type:`bool` | Boolean |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`count`, | Numeric types |
|
||||
| :bro:type:`int`, | |
|
||||
| :bro:type:`double` | |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`time`, | Time types |
|
||||
| :bro:type:`interval` | |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`string` | String |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`pattern` | Regular expression |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`port`, | Network types |
|
||||
| :bro:type:`addr`, | |
|
||||
| :bro:type:`subnet` | |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`enum` | Enumeration |
|
||||
| | (user-defined type)|
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`table`, | Container types |
|
||||
| :bro:type:`set`, | |
|
||||
| :bro:type:`vector`, | |
|
||||
| :bro:type:`record` | |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`function`, | Executable types |
|
||||
| :bro:type:`event`, | |
|
||||
| :bro:type:`hook` | |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`file` | File type (only |
|
||||
| | for writing) |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`opaque` | Opaque type (for |
|
||||
| | some built-in |
|
||||
| | functions) |
|
||||
+-----------------------+--------------------+
|
||||
| :bro:type:`any` | Any type (for |
|
||||
| | functions or |
|
||||
| | containers) |
|
||||
+-----------------------+--------------------+
|
||||
|
||||
The Bro scripting language supports the following built-in types.
|
||||
|
||||
.. bro:type:: void
|
||||
|
||||
An internal Bro type (i.e., "void" is not a reserved keyword in the Bro
|
||||
scripting language) representing the absence of a return type for a
|
||||
function.
|
||||
Here is a more detailed description of each type:
|
||||
|
||||
.. bro:type:: bool
|
||||
|
||||
Reflects a value with one of two meanings: true or false. The two
|
||||
``bool`` constants are ``T`` and ``F``.
|
||||
"bool" constants are ``T`` and ``F``.
|
||||
|
||||
The ``bool`` type supports the following operators: equality/inequality
|
||||
The "bool" type supports the following operators: equality/inequality
|
||||
(``==``, ``!=``), logical and/or (``&&``, ``||``), logical
|
||||
negation (``!``), and absolute value (where ``|T|`` is 1, and ``|F|`` is 0).
|
||||
negation (``!``), and absolute value (where ``|T|`` is 1, and ``|F|`` is 0,
|
||||
and in both cases the result type is :bro:type:`count`).
|
||||
|
||||
.. bro:type:: int
|
||||
|
||||
A numeric type representing a 64-bit signed integer. An ``int`` constant
|
||||
is a string of digits preceded by a ``+`` or ``-`` sign, e.g.
|
||||
A numeric type representing a 64-bit signed integer. An "int" constant
|
||||
is a string of digits preceded by a "+" or "-" sign, e.g.
|
||||
``-42`` or ``+5`` (the "+" sign is optional but see note about type
|
||||
inferencing below). An ``int`` constant can also be written in
|
||||
inferencing below). An "int" constant can also be written in
|
||||
hexadecimal notation (in which case "0x" must be between the sign and
|
||||
the hex digits), e.g. ``-0xFF`` or ``+0xabc123``.
|
||||
|
||||
The ``int`` type supports the following operators: arithmetic
|
||||
The "int" type supports the following operators: arithmetic
|
||||
operators (``+``, ``-``, ``*``, ``/``, ``%``), comparison operators
|
||||
(``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``), assignment operators
|
||||
(``=``, ``+=``, ``-=``), pre-increment (``++``), pre-decrement
|
||||
(``--``), and absolute value (e.g., ``|-3|`` is 3).
|
||||
(``--``), unary plus and minus (``+``, ``-``), and absolute value
|
||||
(e.g., ``|-3|`` is 3, but the result type is :bro:type:`count`).
|
||||
|
||||
When using type inferencing use care so that the
|
||||
intended type is inferred, e.g. ``local size_difference = 0`` will
|
||||
infer :bro:type:`count`, while ``local size_difference = +0``
|
||||
will infer :bro:type:`int`.
|
||||
intended type is inferred, e.g. "local size_difference = 0" will
|
||||
infer ":bro:type:`count`", while "local size_difference = +0"
|
||||
will infer "int".
|
||||
|
||||
.. bro:type:: count
|
||||
|
||||
A numeric type representing a 64-bit unsigned integer. A ``count``
|
||||
constant is a string of digits, e.g. ``1234`` or ``0``. A ``count``
|
||||
A numeric type representing a 64-bit unsigned integer. A "count"
|
||||
constant is a string of digits, e.g. ``1234`` or ``0``. A "count"
|
||||
can also be written in hexadecimal notation (in which case "0x" must
|
||||
precede the hex digits), e.g. ``0xff`` or ``0xABC123``.
|
||||
|
||||
The ``count`` type supports the same operators as the :bro:type:`int`
|
||||
type. A unary plus or minus applied to a ``count`` results in an ``int``.
|
||||
|
||||
.. bro:type:: counter
|
||||
|
||||
An alias to :bro:type:`count`.
|
||||
The "count" type supports the same operators as the ":bro:type:`int`"
|
||||
type, but a unary plus or minus applied to a "count" results in an
|
||||
"int".
|
||||
|
||||
.. bro:type:: double
|
||||
|
||||
A numeric type representing a double-precision floating-point
|
||||
number. Floating-point constants are written as a string of digits
|
||||
with an optional decimal point, optional scale-factor in scientific
|
||||
notation, and optional ``+`` or ``-`` sign. Examples are ``-1234``,
|
||||
notation, and optional "+" or "-" sign. Examples are ``-1234``,
|
||||
``-1234e0``, ``3.14159``, and ``.003E-23``.
|
||||
|
||||
The ``double`` type supports the following operators: arithmetic
|
||||
The "double" type supports the following operators: arithmetic
|
||||
operators (``+``, ``-``, ``*``, ``/``), comparison operators
|
||||
(``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``), assignment operators
|
||||
(``=``, ``+=``, ``-=``), and absolute value (e.g., ``|-3.14|`` is 3.14).
|
||||
(``=``, ``+=``, ``-=``), unary plus and minus (``+``, ``-``), and
|
||||
absolute value (e.g., ``|-3.14|`` is 3.14).
|
||||
|
||||
When using type inferencing use care so that the
|
||||
intended type is inferred, e.g. ``local size_difference = 5`` will
|
||||
infer :bro:type:`count`, while ``local size_difference = 5.0``
|
||||
will infer :bro:type:`double`.
|
||||
intended type is inferred, e.g. "local size_difference = 5" will
|
||||
infer ":bro:type:`count`", while "local size_difference = 5.0"
|
||||
will infer "double".
|
||||
|
||||
.. bro:type:: time
|
||||
|
||||
A temporal type representing an absolute time. There is currently
|
||||
no way to specify a ``time`` constant, but one can use the
|
||||
:bro:id:`double_to_time`, :bro:id:`current_time`, or :bro:id:`network_time`
|
||||
built-in functions to assign a value to a ``time``-typed variable.
|
||||
built-in functions to assign a value to a ``time``-typed variable.
|
||||
|
||||
Time values support the comparison operators (``==``, ``!=``, ``<``,
|
||||
``<=``, ``>``, ``>=``). A ``time`` value can be subtracted from
|
||||
another ``time`` value to produce an ``interval`` value. An ``interval``
|
||||
value can be added to, or subtracted from, a ``time`` value to produce a
|
||||
``time`` value. The absolute value of a ``time`` value is a ``double``
|
||||
with the same numeric value.
|
||||
another ``time`` value to produce an :bro:type:`interval` value. An
|
||||
``interval`` value can be added to, or subtracted from, a ``time`` value
|
||||
to produce a ``time`` value. The absolute value of a ``time`` value is
|
||||
a :bro:type:`double` with the same numeric value.
|
||||
|
||||
.. bro:type:: interval
|
||||
|
||||
|
@ -115,52 +137,58 @@ The Bro scripting language supports the following built-in types.
|
|||
``3.5mins``. An ``interval`` can also be negated, for example
|
||||
``-12 hr`` represents "twelve hours in the past".
|
||||
|
||||
Intervals support addition and subtraction. Intervals also support
|
||||
division (in which case the result is a ``double`` value), the
|
||||
comparison operators (``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``),
|
||||
and the assignment operators (``=``, ``+=``, ``-=``). Also, an
|
||||
``interval`` can be multiplied or divided by an arithmetic type
|
||||
(``count``, ``int``, or ``double``) to produce an ``interval`` value.
|
||||
The absolute value of an ``interval`` is a ``double`` value equal to the
|
||||
number of seconds in the ``interval`` (e.g., ``|-1 min|`` is 60).
|
||||
Intervals support addition and subtraction, the comparison operators
|
||||
(``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``), the assignment
|
||||
operators (``=``, ``+=``, ``-=``), and unary plus and minus (``+``, ``-``).
|
||||
|
||||
Intervals also support division (in which case the result is a
|
||||
:bro:type:`double` value). An ``interval`` can be multiplied or divided
|
||||
by an arithmetic type (``count``, ``int``, or ``double``) to produce
|
||||
an ``interval`` value. The absolute value of an ``interval`` is a
|
||||
``double`` value equal to the number of seconds in the ``interval``
|
||||
(e.g., ``|-1 min|`` is 60.0).
|
||||
|
||||
.. bro:type:: string
|
||||
|
||||
A type used to hold character-string values which represent text.
|
||||
String constants are created by enclosing text in double quotes (")
|
||||
and the backslash character (\\) introduces escape sequences (all of
|
||||
the C-style escape sequences are supported).
|
||||
A type used to hold character-string values which represent text, although
|
||||
strings in a Bro script can actually contain any arbitrary binary data.
|
||||
|
||||
String constants are created by enclosing text within a pair of double
|
||||
quotes ("). A string constant cannot span multiple lines in a Bro script.
|
||||
The backslash character (\\) introduces escape sequences. The
|
||||
following escape sequences are recognized: ``\n``, ``\t``, ``\v``, ``\b``,
|
||||
``\r``, ``\f``, ``\a``, ``\ooo`` (where each 'o' is an octal digit),
|
||||
``\xhh`` (where each 'h' is a hexadecimal digit). For escape sequences
|
||||
that don't match any of these, Bro will just remove the backslash (so
|
||||
to represent a literal backslash in a string constant, you just use
|
||||
two consecutive backslashes).
|
||||
|
||||
Strings support concatenation (``+``), and assignment (``=``, ``+=``).
|
||||
Strings also support the comparison operators (``==``, ``!=``, ``<``,
|
||||
``<=``, ``>``, ``>=``). The number of characters in a string can be
|
||||
found by enclosing the string within pipe characters (e.g., ``|"abc"|``
|
||||
is 3).
|
||||
|
||||
The subscript operator can extract an individual character or a substring
|
||||
of a string (string indexing is zero-based, but an index of
|
||||
-1 refers to the last character in the string, and -2 refers to the
|
||||
second-to-last character, etc.). When extracting a substring, the
|
||||
starting and ending index values are separated by a colon. For example::
|
||||
|
||||
local orig = "0123456789";
|
||||
local third_char = orig[2];
|
||||
local last_char = orig[-1];
|
||||
local first_three_chars = orig[0:2];
|
||||
|
||||
Substring searching can be performed using the "in" or "!in"
|
||||
is 3). Substring searching can be performed using the "in" or "!in"
|
||||
operators (e.g., "bar" in "foobar" yields true).
|
||||
|
||||
Note that Bro represents strings internally as a count and vector of
|
||||
bytes rather than a NUL-terminated byte string (although string
|
||||
constants are also automatically NUL-terminated). This is because
|
||||
network traffic can easily introduce NULs into strings either by
|
||||
nature of an application, inadvertently, or maliciously. And while
|
||||
NULs are allowed in Bro strings, when present in strings passed as
|
||||
arguments to many functions, a run-time error can occur as their
|
||||
presence likely indicates a sort of problem. In that case, the
|
||||
string will also only be represented to the user as the literal
|
||||
"<string-with-NUL>" string.
|
||||
The subscript operator can extract a substring of a string. To do this,
|
||||
specify the starting index to extract (if the starting index is omitted,
|
||||
then zero is assumed), followed by a colon and index
|
||||
one past the last character to extract (if the last index is omitted,
|
||||
then the extracted substring will go to the end of the original string).
|
||||
However, if both the colon and last index are omitted, then a string of
|
||||
length one is extracted. String indexing is zero-based, but an index
|
||||
of -1 refers to the last character in the string, and -2 refers to the
|
||||
second-to-last character, etc. Here are a few examples::
|
||||
|
||||
local orig = "0123456789";
|
||||
local second_char = orig[1];
|
||||
local last_char = orig[-1];
|
||||
local first_two_chars = orig[:2];
|
||||
local last_two_chars = orig[8:];
|
||||
local no_first_and_last = orig[1:9];
|
||||
|
||||
Note that the subscript operator cannot be used to modify a string (i.e.,
|
||||
it cannot be on the left side of an assignment operator).
|
||||
|
||||
.. bro:type:: pattern
|
||||
|
||||
|
@ -174,7 +202,7 @@ The Bro scripting language supports the following built-in types.
|
|||
and embedded.
|
||||
|
||||
In exact matching the ``==`` equality relational operator is used
|
||||
with one :bro:type:`pattern` operand and one :bro:type:`string`
|
||||
with one "pattern" operand and one ":bro:type:`string`"
|
||||
operand (order of operands does not matter) to check whether the full
|
||||
string exactly matches the pattern. In exact matching, the ``^``
|
||||
beginning-of-line and ``$`` end-of-line anchors are redundant since
|
||||
|
@ -190,8 +218,8 @@ The Bro scripting language supports the following built-in types.
|
|||
yields false. The ``!=`` operator would yield the negation of ``==``.
|
||||
|
||||
In embedded matching the ``in`` operator is used with one
|
||||
:bro:type:`pattern` operand (which must be on the left-hand side) and
|
||||
one :bro:type:`string` operand, but tests whether the pattern
|
||||
"pattern" operand (which must be on the left-hand side) and
|
||||
one ":bro:type:`string`" operand, but tests whether the pattern
|
||||
appears anywhere within the given string. For example::
|
||||
|
||||
/foo|bar/ in "foobar"
|
||||
|
@ -203,27 +231,12 @@ The Bro scripting language supports the following built-in types.
|
|||
is false since "oob" does not appear at the start of "foobar". The
|
||||
``!in`` operator would yield the negation of ``in``.
|
||||
|
||||
.. bro:type:: enum
|
||||
|
||||
A type allowing the specification of a set of related values that
|
||||
have no further structure. An example declaration:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
type color: enum { Red, White, Blue, };
|
||||
|
||||
The last comma after ``Blue`` is optional.
|
||||
|
||||
The only operations allowed on enumerations are equality comparisons
|
||||
(``==``, ``!=``) and assignment (``=``).
|
||||
Enumerations do not have associated values or ordering.
|
||||
|
||||
.. bro:type:: port
|
||||
|
||||
A type representing transport-level port numbers. Besides TCP and
|
||||
A type representing transport-level port numbers (besides TCP and
|
||||
UDP ports, there is a concept of an ICMP "port" where the source
|
||||
port is the ICMP message type and the destination port the ICMP
|
||||
message code. A ``port`` constant is written as an unsigned integer
|
||||
message code). A ``port`` constant is written as an unsigned integer
|
||||
followed by one of ``/tcp``, ``/udp``, ``/icmp``, or ``/unknown``.
|
||||
|
||||
Ports support the comparison operators (``==``, ``!=``, ``<``, ``<=``,
|
||||
|
@ -255,14 +268,6 @@ The Bro scripting language supports the following built-in types.
|
|||
address) are treated internally as IPv4 addresses (for example,
|
||||
``[::ffff:192.168.1.100]`` is equal to ``192.168.1.100``).
|
||||
|
||||
Hostname constants can also be used, but since a hostname can
|
||||
correspond to multiple IP addresses, the type of such a variable is a
|
||||
:bro:type:`set` of :bro:type:`addr` elements. For example:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local a = www.google.com;
|
||||
|
||||
Addresses can be compared for equality (``==``, ``!=``),
|
||||
and also for ordering (``<``, ``<=``, ``>``, ``>=``). The absolute value
|
||||
of an address gives the size in bits (32 for IPv4, and 128 for IPv6).
|
||||
|
@ -285,9 +290,17 @@ The Bro scripting language supports the following built-in types.
|
|||
if ( a in s )
|
||||
print "true";
|
||||
|
||||
Note that you can check if a given ``addr`` is IPv4 or IPv6 using
|
||||
You can check if a given ``addr`` is IPv4 or IPv6 using
|
||||
the :bro:id:`is_v4_addr` and :bro:id:`is_v6_addr` built-in functions.
|
||||
|
||||
Note that hostname constants can also be used, but since a hostname can
|
||||
correspond to multiple IP addresses, the type of such a variable is
|
||||
"set[addr]". For example:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local a = www.google.com;
|
||||
|
||||
.. bro:type:: subnet
|
||||
|
||||
A type representing a block of IP addresses in CIDR notation. A
|
||||
|
@ -296,13 +309,24 @@ The Bro scripting language supports the following built-in types.
|
|||
number. For example, ``192.168.0.0/16`` or ``[fe80::]/64``.
|
||||
|
||||
Subnets can be compared for equality (``==``, ``!=``). An
|
||||
:bro:type:`addr` can be checked for inclusion in a subnet using
|
||||
the "in" or "!in" operators.
|
||||
"addr" can be checked for inclusion in a subnet using
|
||||
the ``in`` or ``!in`` operators.
|
||||
|
||||
.. bro:type:: any
|
||||
.. bro:type:: enum
|
||||
|
||||
Used to bypass strong typing. For example, a function can take an
|
||||
argument of type ``any`` when it may be of different types.
|
||||
A type allowing the specification of a set of related values that
|
||||
have no further structure. An example declaration:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
type color: enum { Red, White, Blue, };
|
||||
|
||||
The last comma after ``Blue`` is optional. Both the type name ``color``
|
||||
and the individual values (``Red``, etc.) have global scope.
|
||||
|
||||
Enumerations do not have associated values or ordering.
|
||||
The only operations allowed on enumerations are equality comparisons
|
||||
(``==``, ``!=``) and assignment (``=``).
|
||||
|
||||
.. bro:type:: table
|
||||
|
||||
|
@ -316,24 +340,25 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
table [ type^+ ] of type
|
||||
|
||||
where *type^+* is one or more types, separated by commas. For example:
|
||||
where *type^+* is one or more types, separated by commas.
|
||||
For example:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global a: table[count] of string;
|
||||
|
||||
declares a table indexed by :bro:type:`count` values and yielding
|
||||
:bro:type:`string` values. The yield type can also be more complex:
|
||||
declares a table indexed by "count" values and yielding
|
||||
"string" values. The yield type can also be more complex:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global a: table[count] of table[addr, port] of string;
|
||||
|
||||
which declares a table indexed by :bro:type:`count` and yielding
|
||||
another :bro:type:`table` which is indexed by an :bro:type:`addr`
|
||||
and :bro:type:`port` to yield a :bro:type:`string`.
|
||||
which declares a table indexed by "count" and yielding
|
||||
another "table" which is indexed by an "addr"
|
||||
and "port" to yield a "string".
|
||||
|
||||
Initialization of tables occurs by enclosing a set of initializers within
|
||||
One way to initialize a table is by enclosing a set of initializers within
|
||||
braces, for example:
|
||||
|
||||
.. code:: bro
|
||||
|
@ -343,18 +368,17 @@ The Bro scripting language supports the following built-in types.
|
|||
[5] = "five",
|
||||
};
|
||||
|
||||
A table constructor (equivalent to above example) can also be used
|
||||
to create a table:
|
||||
A table constructor can also be used to create a table:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global t2: table[count] of string = table(
|
||||
[11] = "eleven",
|
||||
[5] = "five"
|
||||
global t2 = table(
|
||||
[192.168.0.2, 22/tcp] = "ssh",
|
||||
[192.168.0.3, 80/tcp] = "http"
|
||||
);
|
||||
|
||||
Table constructors can also be explicitly named by a type, which is
|
||||
useful for when a more complex index type could otherwise be
|
||||
useful when a more complex index type could otherwise be
|
||||
ambiguous:
|
||||
|
||||
.. code:: bro
|
||||
|
@ -381,17 +405,7 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
if ( 13 in t )
|
||||
...
|
||||
|
||||
Iterate over tables with a ``for`` loop:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local t: table[count] of string;
|
||||
for ( n in t )
|
||||
...
|
||||
|
||||
local services: table[addr, port] of string;
|
||||
for ( [a, p] in services )
|
||||
if ( [192.168.0.2, 22/tcp] in t2 )
|
||||
...
|
||||
|
||||
Add or overwrite individual table elements by assignment:
|
||||
|
@ -400,7 +414,7 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
t[13] = "thirteen";
|
||||
|
||||
Remove individual table elements with ``delete``:
|
||||
Remove individual table elements with :bro:keyword:`delete`:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
|
@ -416,6 +430,9 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
|t|
|
||||
|
||||
See the :bro:keyword:`for` statement for info on how to iterate over
|
||||
the elements in a table.
|
||||
|
||||
.. bro:type:: set
|
||||
|
||||
A set is like a :bro:type:`table`, but it is a collection of indices
|
||||
|
@ -426,25 +443,22 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
where *type^+* is one or more types separated by commas.
|
||||
|
||||
Sets are initialized by listing elements enclosed by curly braces:
|
||||
Sets can be initialized by listing elements enclosed by curly braces:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global s: set[port] = { 21/tcp, 23/tcp, 80/tcp, 443/tcp };
|
||||
global s2: set[port, string] = { [21/tcp, "ftp"], [23/tcp, "telnet"] };
|
||||
|
||||
The types are explicitly shown in the example above, but they could
|
||||
have been left to type inference.
|
||||
|
||||
A set constructor (equivalent to above example) can also be used to
|
||||
create a set:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global s3: set[port] = set(21/tcp, 23/tcp, 80/tcp, 443/tcp);
|
||||
global s3 = set(21/tcp, 23/tcp, 80/tcp, 443/tcp);
|
||||
|
||||
Set constructors can also be explicitly named by a type, which is
|
||||
useful for when a more complex index type could otherwise be
|
||||
useful when a more complex index type could otherwise be
|
||||
ambiguous:
|
||||
|
||||
.. code:: bro
|
||||
|
@ -465,18 +479,10 @@ The Bro scripting language supports the following built-in types.
|
|||
if ( 21/tcp in s )
|
||||
...
|
||||
|
||||
if ( 21/tcp !in s )
|
||||
if ( [21/tcp, "ftp"] !in s2 )
|
||||
...
|
||||
|
||||
Iterate over a set with a ``for`` loop:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local s: set[port];
|
||||
for ( p in s )
|
||||
...
|
||||
|
||||
Elements are added with ``add``:
|
||||
Elements are added with :bro:keyword:`add`:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
|
@ -485,7 +491,7 @@ The Bro scripting language supports the following built-in types.
|
|||
Nothing happens if the element with value ``22/tcp`` was already present in
|
||||
the set.
|
||||
|
||||
And removed with ``delete``:
|
||||
And removed with :bro:keyword:`delete`:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
|
@ -501,6 +507,9 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
|s|
|
||||
|
||||
See the :bro:keyword:`for` statement for info on how to iterate over
|
||||
the elements in a set.
|
||||
|
||||
.. bro:type:: vector
|
||||
|
||||
A vector is like a :bro:type:`table`, except it's always indexed by a
|
||||
|
@ -515,7 +524,7 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
.. code:: bro
|
||||
|
||||
global v: vector of string = vector("one", "two", "three");
|
||||
local v = vector("one", "two", "three");
|
||||
|
||||
Vector constructors can also be explicitly named by a type, which
|
||||
is useful for when a more complex yield type could otherwise be
|
||||
|
@ -539,14 +548,6 @@ The Bro scripting language supports the following built-in types.
|
|||
|
||||
print v[2];
|
||||
|
||||
Iterate over a vector with a ``for`` loop:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local v: vector of string;
|
||||
for ( n in v )
|
||||
...
|
||||
|
||||
An element can be added to a vector by assigning the value (a value
|
||||
that already exists at that index will be overwritten):
|
||||
|
||||
|
@ -577,11 +578,17 @@ The Bro scripting language supports the following built-in types.
|
|||
The resulting vector of bool is the logical "and" (or logical "or") of
|
||||
each element of the operand vectors.
|
||||
|
||||
See the :bro:keyword:`for` statement for info on how to iterate over
|
||||
the elements in a vector.
|
||||
|
||||
.. bro:type:: record
|
||||
|
||||
A ``record`` is a collection of values. Each value has a field name
|
||||
A "record" is a collection of values. Each value has a field name
|
||||
and a type. Values do not need to have the same type and the types
|
||||
have no restrictions. An example record type definition:
|
||||
have no restrictions. Field names must follow the same syntax as
|
||||
regular variable names (except that field names are allowed to be the
|
||||
same as local or global variables). An example record type
|
||||
definition:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
|
@ -590,85 +597,44 @@ The Bro scripting language supports the following built-in types.
|
|||
s: string &optional;
|
||||
};
|
||||
|
||||
Access to a record field uses the dollar sign (``$``) operator:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global r: MyRecordType;
|
||||
r$c = 13;
|
||||
|
||||
Record assignment can be done field by field or as a whole like:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
r = [$c = 13, $s = "thirteen"];
|
||||
|
||||
Records can be initialized or assigned as a whole in three different ways.
|
||||
When assigning a whole record value, all fields that are not
|
||||
:bro:attr:`&optional` or have a :bro:attr:`&default` attribute must
|
||||
be specified.
|
||||
|
||||
To test for existence of a field that is :bro:attr:`&optional`, use the
|
||||
``?$`` operator:
|
||||
be specified. First, there's a constructor syntax:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
if ( r?$s )
|
||||
...
|
||||
|
||||
Records can also be created using a constructor syntax:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global r2: MyRecordType = record($c = 7);
|
||||
local r: MyRecordType = record($c = 7);
|
||||
|
||||
And the constructor can be explicitly named by type, too, which
|
||||
is arguably more readable code:
|
||||
is arguably more readable:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global r3 = MyRecordType($c = 42);
|
||||
local r = MyRecordType($c = 42);
|
||||
|
||||
.. bro:type:: opaque
|
||||
|
||||
A data type whose actual representation/implementation is
|
||||
intentionally hidden, but whose values may be passed to certain
|
||||
functions that can actually access the internal/hidden resources.
|
||||
Opaque types are differentiated from each other by qualifying them
|
||||
like ``opaque of md5`` or ``opaque of sha1``. Any valid identifier
|
||||
can be used as the type qualifier.
|
||||
|
||||
An example use of this type is the set of built-in functions which
|
||||
perform hashing:
|
||||
And the third way is like this:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
local handle: opaque of md5 = md5_hash_init();
|
||||
md5_hash_update(handle, "test");
|
||||
md5_hash_update(handle, "testing");
|
||||
print md5_hash_finish(handle);
|
||||
local r: MyRecordType = [$c = 13, $s = "thirteen"];
|
||||
|
||||
Here the opaque type is used to provide a handle to a particular
|
||||
resource which is calculating an MD5 checksum incrementally over
|
||||
time, but the details of that resource aren't relevant, it's only
|
||||
necessary to have a handle as a way of identifying it and
|
||||
distinguishing it from other such resources.
|
||||
|
||||
.. bro:type:: file
|
||||
|
||||
Bro supports writing to files, but not reading from them. Files
|
||||
can be opened using either the :bro:id:`open` or :bro:id:`open_for_append`
|
||||
built-in functions, and closed using the :bro:id:`close` built-in
|
||||
function. For example, declare, open, and write to a file
|
||||
and finally close it like:
|
||||
Access to a record field uses the dollar sign (``$``) operator, and
|
||||
record fields can be assigned with this:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
global f: file = open("myfile");
|
||||
print f, "hello, world";
|
||||
close(f);
|
||||
local r: MyRecordType;
|
||||
r$c = 13;
|
||||
|
||||
Writing to files like this for logging usually isn't recommended, for better
|
||||
logging support see :doc:`/frameworks/logging`.
|
||||
To test if a field that is :bro:attr:`&optional` has been assigned a
|
||||
value, use the ``?$`` operator (it returns a :bro:type:`bool` value of
|
||||
``T`` if the field has been assigned a value, or ``F`` if not):
|
||||
|
||||
.. code:: bro
|
||||
|
||||
if ( r ?$ s )
|
||||
...
|
||||
|
||||
.. bro:type:: function
|
||||
|
||||
|
@ -700,6 +666,16 @@ The Bro scripting language supports the following built-in types.
|
|||
type, but when it is, the return type and argument list (including the
|
||||
name of each argument) must match exactly.
|
||||
|
||||
Here is an example function that takes no parameters and does not
|
||||
return a value:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
function my_func()
|
||||
{
|
||||
print "my_func";
|
||||
}
|
||||
|
||||
Function types don't need to have a name and can be assigned anonymously:
|
||||
|
||||
.. code:: bro
|
||||
|
@ -742,9 +718,20 @@ The Bro scripting language supports the following built-in types.
|
|||
Event handlers are nearly identical in both syntax and semantics to
|
||||
a :bro:type:`function`, with the two differences being that event
|
||||
handlers have no return type since they never return a value, and
|
||||
you cannot call an event handler. Instead of directly calling an
|
||||
event handler from a script, event handler bodies are executed when
|
||||
they are invoked by one of three different methods:
|
||||
you cannot call an event handler.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: bro
|
||||
|
||||
event my_event(r: bool, s: string)
|
||||
{
|
||||
print "my_event", r, s;
|
||||
}
|
||||
|
||||
Instead of directly calling an event handler from a script, event
|
||||
handler bodies are executed when they are invoked by one of three
|
||||
different methods:
|
||||
|
||||
- From the event engine
|
||||
|
||||
|
@ -765,7 +752,7 @@ The Bro scripting language supports the following built-in types.
|
|||
This assumes that ``password_exposed`` was previously declared
|
||||
as an event handler type with compatible arguments.
|
||||
|
||||
- Via the ``schedule`` expression in a script
|
||||
- Via the :bro:keyword:`schedule` expression in a script
|
||||
|
||||
This delays the invocation of event handlers until some time in
|
||||
the future. For example:
|
||||
|
@ -789,8 +776,8 @@ The Bro scripting language supports the following built-in types.
|
|||
immediate and they do not get scheduled through an event queue.
|
||||
Also, a unique feature of a hook is that a given hook handler body
|
||||
can short-circuit the execution of remaining hook handlers simply by
|
||||
exiting from the body as a result of a ``break`` statement (as
|
||||
opposed to a ``return`` or just reaching the end of the body).
|
||||
exiting from the body as a result of a :bro:keyword:`break` statement (as
|
||||
opposed to a :bro:keyword:`return` or just reaching the end of the body).
|
||||
|
||||
A hook type is declared like::
|
||||
|
||||
|
@ -859,142 +846,60 @@ The Bro scripting language supports the following built-in types.
|
|||
executed due to one handler body exiting as a result of a ``break``
|
||||
statement.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
.. bro:type:: file
|
||||
|
||||
Attributes occur at the end of type/event declarations and change their
|
||||
behavior. The syntax is ``&key`` or ``&key=val``, e.g., ``type T:
|
||||
set[count] &read_expire=5min`` or ``event foo() &priority=-3``. The Bro
|
||||
scripting language supports the following built-in attributes.
|
||||
Bro supports writing to files, but not reading from them (to read from
|
||||
files see the :doc:`/frameworks/input`). Files
|
||||
can be opened using either the :bro:id:`open` or :bro:id:`open_for_append`
|
||||
built-in functions, and closed using the :bro:id:`close` built-in
|
||||
function. For example, declare, open, and write to a file and finally
|
||||
close it like:
|
||||
|
||||
.. bro:attr:: &optional
|
||||
.. code:: bro
|
||||
|
||||
Allows a record field to be missing. For example the type ``record {
|
||||
a: addr; b: port &optional; }`` could be instantiated both as
|
||||
singleton ``[$a=127.0.0.1]`` or pair ``[$a=127.0.0.1, $b=80/tcp]``.
|
||||
local f = open("myfile");
|
||||
print f, "hello, world";
|
||||
close(f);
|
||||
|
||||
.. bro:attr:: &default
|
||||
Writing to files like this for logging usually isn't recommended, for better
|
||||
logging support see :doc:`/frameworks/logging`.
|
||||
|
||||
Uses a default value for a record field, a function/hook/event
|
||||
parameter, or container elements. For example, ``table[int] of
|
||||
string &default="foo"`` would create a table that returns the
|
||||
:bro:type:`string` ``"foo"`` for any non-existing index.
|
||||
.. bro:type:: opaque
|
||||
|
||||
.. bro:attr:: &redef
|
||||
A data type whose actual representation/implementation is
|
||||
intentionally hidden, but whose values may be passed to certain
|
||||
built-in functions that can actually access the internal/hidden resources.
|
||||
Opaque types are differentiated from each other by qualifying them
|
||||
like "opaque of md5" or "opaque of sha1".
|
||||
|
||||
Allows for redefinition of initial object values. This is typically
|
||||
used with constants, for example, ``const clever = T &redef;`` would
|
||||
allow the constant to be redefined at some later point during script
|
||||
execution.
|
||||
An example use of this type is the set of built-in functions which
|
||||
perform hashing:
|
||||
|
||||
.. bro:attr:: &rotate_interval
|
||||
.. code:: bro
|
||||
|
||||
Rotates a file after a specified interval.
|
||||
local handle = md5_hash_init();
|
||||
md5_hash_update(handle, "test");
|
||||
md5_hash_update(handle, "testing");
|
||||
print md5_hash_finish(handle);
|
||||
|
||||
.. bro:attr:: &rotate_size
|
||||
Here the opaque type is used to provide a handle to a particular
|
||||
resource which is calculating an MD5 hash incrementally over
|
||||
time, but the details of that resource aren't relevant, it's only
|
||||
necessary to have a handle as a way of identifying it and
|
||||
distinguishing it from other such resources.
|
||||
|
||||
Rotates a file after it has reached a given size in bytes.
|
||||
.. bro:type:: any
|
||||
|
||||
.. bro:attr:: &add_func
|
||||
Used to bypass strong typing. For example, a function can take an
|
||||
argument of type ``any`` when it may be of different types.
|
||||
The only operation allowed on a variable of type ``any`` is assignment.
|
||||
|
||||
Can be applied to an identifier with &redef to specify a function to
|
||||
be called any time a "redef <id> += ..." declaration is parsed. The
|
||||
function takes two arguments of the same type as the identifier, the first
|
||||
being the old value of the variable and the second being the new
|
||||
value given after the "+=" operator in the "redef" declaration. The
|
||||
return value of the function will be the actual new value of the
|
||||
variable after the "redef" declaration is parsed.
|
||||
Note that users aren't expected to use this type. It's provided mainly
|
||||
for use by some built-in functions and scripts included with Bro.
|
||||
|
||||
.. bro:attr:: &delete_func
|
||||
.. bro:type:: void
|
||||
|
||||
Same as &add_func, except for "redef" declarations that use the "-="
|
||||
operator.
|
||||
An internal Bro type (i.e., "void" is not a reserved keyword in the Bro
|
||||
scripting language) representing the absence of a return type for a
|
||||
function.
|
||||
|
||||
.. bro:attr:: &expire_func
|
||||
|
||||
Called right before a container element expires. The function's
|
||||
first parameter is of the same type of the container and the second
|
||||
parameter the same type of the container's index. The return
|
||||
value is an :bro:type:`interval` indicating the amount of additional
|
||||
time to wait before expiring the container element at the given
|
||||
index (which will trigger another execution of this function).
|
||||
|
||||
.. bro:attr:: &read_expire
|
||||
|
||||
Specifies a read expiration timeout for container elements. That is,
|
||||
the element expires after the given amount of time since the last
|
||||
time it has been read. Note that a write also counts as a read.
|
||||
|
||||
.. bro:attr:: &write_expire
|
||||
|
||||
Specifies a write expiration timeout for container elements. That
|
||||
is, the element expires after the given amount of time since the
|
||||
last time it has been written.
|
||||
|
||||
.. bro:attr:: &create_expire
|
||||
|
||||
Specifies a creation expiration timeout for container elements. That
|
||||
is, the element expires after the given amount of time since it has
|
||||
been inserted into the container, regardless of any reads or writes.
|
||||
|
||||
.. bro:attr:: &persistent
|
||||
|
||||
Makes a variable persistent, i.e., its value is written to disk (per
|
||||
default at shutdown time).
|
||||
|
||||
.. bro:attr:: &synchronized
|
||||
|
||||
Synchronizes variable accesses across nodes. The value of a
|
||||
``&synchronized`` variable is automatically propagated to all peers
|
||||
when it changes.
|
||||
|
||||
.. bro:attr:: &encrypt
|
||||
|
||||
Encrypts files right before writing them to disk.
|
||||
|
||||
.. TODO: needs to be documented in more detail.
|
||||
|
||||
.. bro:attr:: &raw_output
|
||||
|
||||
Opens a file in raw mode, i.e., non-ASCII characters are not
|
||||
escaped.
|
||||
|
||||
.. bro:attr:: &mergeable
|
||||
|
||||
Prefers set union to assignment for synchronized state. This
|
||||
attribute is used in conjunction with :bro:attr:`&synchronized`
|
||||
container types: when the same container is updated at two peers
|
||||
with different value, the propagation of the state causes a race
|
||||
condition, where the last update succeeds. This can cause
|
||||
inconsistencies and can be avoided by unifying the two sets, rather
|
||||
than merely overwriting the old value.
|
||||
|
||||
.. bro:attr:: &priority
|
||||
|
||||
Specifies the execution priority (as a signed integer) of a hook or
|
||||
event handler. Higher values are executed before lower ones. The
|
||||
default value is 0.
|
||||
|
||||
.. bro:attr:: &group
|
||||
|
||||
Groups event handlers such that those in the same group can be
|
||||
jointly activated or deactivated.
|
||||
|
||||
.. bro:attr:: &log
|
||||
|
||||
Writes a record field to the associated log stream.
|
||||
|
||||
.. bro:attr:: &error_handler
|
||||
|
||||
Internally set on the events that are associated with the reporter
|
||||
framework: :bro:id:`reporter_info`, :bro:id:`reporter_warning`, and
|
||||
:bro:id:`reporter_error`. It prevents any handlers of those events
|
||||
from being able to generate reporter messages that go through any of
|
||||
those events (i.e., it prevents an infinite event recursion). Instead,
|
||||
such nested reporter messages are output to stderr.
|
||||
|
||||
.. bro:attr:: &type_column
|
||||
|
||||
Used by the input framework. It can be used on columns of type
|
||||
:bro:type:`port` and specifies the name of an additional column in
|
||||
the input file which specifies the protocol of the port (tcp/udp/icmp).
|
|
@ -10,6 +10,6 @@ event bro_init()
|
|||
|
||||
print fmt("contents of v1: %s", v1);
|
||||
print fmt("length of v1: %d", |v1|);
|
||||
print fmt("contents of v1: %s", v2);
|
||||
print fmt("contents of v2: %s", v2);
|
||||
print fmt("length of v2: %d", |v2|);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
event bro_init()
|
||||
{
|
||||
local test_string = "The quick brown fox jumped over the lazy dog.";
|
||||
local test_string = "The quick brown fox jumps over the lazy dog.";
|
||||
local test_pattern = /quick|lazy/;
|
||||
|
||||
if ( test_pattern in test_string )
|
||||
|
|
|
@ -730,7 +730,7 @@ Bro supports ``usec``, ``msec``, ``sec``, ``min``, ``hr``, or ``day`` which repr
|
|||
microseconds, milliseconds, seconds, minutes, hours, and days
|
||||
respectively. In fact, the interval data type allows for a surprising
|
||||
amount of variation in its definitions. There can be a space between
|
||||
the numeric constant or they can crammed together like a temporal
|
||||
the numeric constant or they can be crammed together like a temporal
|
||||
portmanteau. The time unit can be either singular or plural. All of
|
||||
this adds up to to the fact that both ``42hrs`` and ``42 hr`` are
|
||||
perfectly valid and logically equivalent in Bro. The point, however,
|
||||
|
@ -819,7 +819,7 @@ with the ``typedef`` and ``struct`` keywords, Bro allows you to cobble
|
|||
together new data types to suit the needs of your situation.
|
||||
|
||||
When combined with the ``type`` keyword, ``record`` can generate a
|
||||
composite type. We have, in fact, already encountered a a complex
|
||||
composite type. We have, in fact, already encountered a complex
|
||||
example of the ``record`` data type in the earlier sections, the
|
||||
:bro:type:`connection` record passed to many events. Another one,
|
||||
:bro:type:`Conn::Info`, which corresponds to the fields logged into
|
||||
|
@ -1014,8 +1014,8 @@ remaining logs to factor.log.
|
|||
:lines: 38-62
|
||||
:linenos:
|
||||
|
||||
To dynamically alter the file in which a stream writes its logs a
|
||||
filter can specify function returns a string to be used as the
|
||||
To dynamically alter the file in which a stream writes its logs, a
|
||||
filter can specify a function that returns a string to be used as the
|
||||
filename for the current call to ``Log::write``. The definition for
|
||||
this function has to take as its parameters a ``Log::ID`` called id, a
|
||||
string called ``path`` and the appropriate record type for the logs called
|
||||
|
|
|
@ -9,3 +9,8 @@ signature file-tar {
|
|||
file-magic /([[:print:]\x00]){100}(([[:digit:]\x00\x20]){8}){3}/
|
||||
file-mime "application/x-tar", 150
|
||||
}
|
||||
|
||||
signature file-swf {
|
||||
file-magic /(F|C|Z)WS/
|
||||
file-mime "application/x-shockwave-flash", 60
|
||||
}
|
|
@ -2769,19 +2769,6 @@ signature file-magic-auto408 {
|
|||
file-magic /(.{512})(\xec\xa5\xc1)/
|
||||
}
|
||||
|
||||
# >0 string,=FWS (len=3), ["Macromedia Flash data,"], swap_endian=0
|
||||
# >>3 byte&,x, ["version %d"], swap_endian=0
|
||||
signature file-magic-auto409 {
|
||||
file-mime "application/x-shockwave-flash", 1
|
||||
file-magic /(FWS)(.{1})/
|
||||
}
|
||||
|
||||
# >0 string,=CWS (len=3), ["Macromedia Flash data (compressed),"], swap_endian=0
|
||||
signature file-magic-auto410 {
|
||||
file-mime "application/x-shockwave-flash", 60
|
||||
file-magic /(CWS)/
|
||||
}
|
||||
|
||||
# >0 regex/20,=^\.[A-Za-z0-9][A-Za-z0-9][ \t] (len=29), ["troff or preprocessor input text"], swap_endian=0
|
||||
signature file-magic-auto411 {
|
||||
file-mime "text/troff", 59
|
||||
|
|
|
@ -264,10 +264,10 @@ export {
|
|||
## Returns: The set of MIME types.
|
||||
global registered_mime_types: function(tag: Analyzer::Tag) : set[string];
|
||||
|
||||
## Returns a table of all MIME-type-to-analyzer mappings currently registered.
|
||||
## Returns a table of all MIME-type-to-analyzer mappings currently registered.
|
||||
##
|
||||
## Returns: A table mapping each analyzer to the set of MIME types registered for
|
||||
## it.
|
||||
## Returns: A table mapping each analyzer to the set of MIME types
|
||||
## registered for it.
|
||||
global all_registered_mime_types: function() : table[Analyzer::Tag] of set[string];
|
||||
|
||||
## Event that can be handled to access the Info record as it is sent on
|
||||
|
|
|
@ -81,6 +81,9 @@ export {
|
|||
## Where the data was discovered.
|
||||
where: Where &log;
|
||||
|
||||
## The name of the node where the match was discovered.
|
||||
node: string &optional &log;
|
||||
|
||||
## If the data was discovered within a connection, the
|
||||
## connection record should go here to give context to the data.
|
||||
conn: connection &optional;
|
||||
|
@ -240,6 +243,11 @@ function Intel::seen(s: Seen)
|
|||
s$indicator_type = Intel::ADDR;
|
||||
}
|
||||
|
||||
if ( ! s?$node )
|
||||
{
|
||||
s$node = peer_description;
|
||||
}
|
||||
|
||||
if ( have_full_data )
|
||||
{
|
||||
local items = get_items(s);
|
||||
|
|
|
@ -2485,8 +2485,7 @@ type http_message_stat: record {
|
|||
header_length: count;
|
||||
};
|
||||
|
||||
## Maximum number of HTTP entity data delivered to events. The amount of data
|
||||
## can be limited for better performance, zero disables truncation.
|
||||
## Maximum number of HTTP entity data delivered to events.
|
||||
##
|
||||
## .. bro:see:: http_entity_data skip_http_entity_data skip_http_data
|
||||
global http_entity_data_delivery_size = 1500 &redef;
|
||||
|
|
|
@ -30,6 +30,7 @@ export {
|
|||
const HELLO_REQUEST = 0;
|
||||
const CLIENT_HELLO = 1;
|
||||
const SERVER_HELLO = 2;
|
||||
const HELLO_VERIFY_REQUEST = 3; # RFC 6347
|
||||
const SESSION_TICKET = 4; # RFC 5077
|
||||
const CERTIFICATE = 11;
|
||||
const SERVER_KEY_EXCHANGE = 12;
|
||||
|
@ -40,6 +41,7 @@ export {
|
|||
const FINISHED = 20;
|
||||
const CERTIFICATE_URL = 21; # RFC 3546
|
||||
const CERTIFICATE_STATUS = 22; # RFC 3546
|
||||
const SUPPLEMENTAL_DATA = 23; # RFC 4680
|
||||
|
||||
## Mapping between numeric codes and human readable strings for alert
|
||||
## levels.
|
||||
|
@ -112,7 +114,8 @@ export {
|
|||
[19] = "client_certificate_type",
|
||||
[20] = "server_certificate_type",
|
||||
[21] = "padding", # temporary till 2015-03-12
|
||||
[22] = "encrypt_then_mac", # temporary till 2015-06-05
|
||||
[22] = "encrypt_then_mac",
|
||||
[23] = "extended_master_secret", # temporary till 2015-09-26
|
||||
[35] = "SessionTicket TLS",
|
||||
[40] = "extended_random",
|
||||
[13172] = "next_protocol_negotiation",
|
||||
|
|
|
@ -157,6 +157,7 @@ add_subdirectory(analyzer)
|
|||
add_subdirectory(broxygen)
|
||||
add_subdirectory(file_analysis)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(iosource)
|
||||
add_subdirectory(logging)
|
||||
add_subdirectory(probabilistic)
|
||||
|
||||
|
@ -254,7 +255,6 @@ set(bro_SRCS
|
|||
Anon.cc
|
||||
Attr.cc
|
||||
Base64.cc
|
||||
BPF_Program.cc
|
||||
Brofiler.cc
|
||||
BroString.cc
|
||||
CCL.cc
|
||||
|
@ -279,14 +279,13 @@ set(bro_SRCS
|
|||
EventRegistry.cc
|
||||
Expr.cc
|
||||
File.cc
|
||||
FlowSrc.cc
|
||||
Flare.cc
|
||||
Frag.cc
|
||||
Frame.cc
|
||||
Func.cc
|
||||
Hash.cc
|
||||
ID.cc
|
||||
IntSet.cc
|
||||
IOSource.cc
|
||||
IP.cc
|
||||
IPAddr.cc
|
||||
List.cc
|
||||
|
@ -299,7 +298,7 @@ set(bro_SRCS
|
|||
OSFinger.cc
|
||||
PacketFilter.cc
|
||||
PersistenceSerializer.cc
|
||||
PktSrc.cc
|
||||
Pipe.cc
|
||||
PolicyFile.cc
|
||||
PrefixTable.cc
|
||||
PriorityQueue.cc
|
||||
|
|
|
@ -210,6 +210,7 @@ bool ChunkedIOFd::WriteChunk(Chunk* chunk, bool partial)
|
|||
else
|
||||
pending_head = pending_tail = q;
|
||||
|
||||
write_flare.Fire();
|
||||
return Flush();
|
||||
}
|
||||
|
||||
|
@ -232,6 +233,7 @@ bool ChunkedIOFd::PutIntoWriteBuffer(Chunk* chunk)
|
|||
write_len += len;
|
||||
|
||||
delete chunk;
|
||||
write_flare.Fire();
|
||||
|
||||
if ( network_time - last_flush > 0.005 )
|
||||
FlushWriteBuffer();
|
||||
|
@ -269,6 +271,10 @@ bool ChunkedIOFd::FlushWriteBuffer()
|
|||
if ( unsigned(written) == len )
|
||||
{
|
||||
write_pos = write_len = 0;
|
||||
|
||||
if ( ! pending_head )
|
||||
write_flare.Extinguish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -318,7 +324,12 @@ bool ChunkedIOFd::Flush()
|
|||
}
|
||||
}
|
||||
|
||||
return FlushWriteBuffer();
|
||||
bool rval = FlushWriteBuffer();
|
||||
|
||||
if ( ! pending_head && write_len == 0 )
|
||||
write_flare.Extinguish();
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
uint32 ChunkedIOFd::ChunkAvailable()
|
||||
|
@ -394,6 +405,9 @@ bool ChunkedIOFd::Read(Chunk** chunk, bool may_block)
|
|||
#ifdef DEBUG_COMMUNICATION
|
||||
AddToBuffer("<false:read-chunk>", true);
|
||||
#endif
|
||||
if ( ! ChunkAvailable() )
|
||||
read_flare.Extinguish();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -402,9 +416,15 @@ bool ChunkedIOFd::Read(Chunk** chunk, bool may_block)
|
|||
#ifdef DEBUG_COMMUNICATION
|
||||
AddToBuffer("<null:no-data>", true);
|
||||
#endif
|
||||
read_flare.Extinguish();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ChunkAvailable() )
|
||||
read_flare.Fire();
|
||||
else
|
||||
read_flare.Extinguish();
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( *chunk )
|
||||
DBG_LOG(DBG_CHUNKEDIO, "read of size %d %s[%s]",
|
||||
|
@ -481,6 +501,9 @@ bool ChunkedIOFd::ReadChunk(Chunk** chunk, bool may_block)
|
|||
read_pos = 0;
|
||||
read_len = bytes_left;
|
||||
|
||||
if ( ! ChunkAvailable() )
|
||||
read_flare.Extinguish();
|
||||
|
||||
// If allowed, wait a bit for something to read.
|
||||
if ( may_block )
|
||||
{
|
||||
|
@ -607,6 +630,14 @@ bool ChunkedIOFd::IsFillingUp()
|
|||
return stats.pending > MAX_BUFFERED_CHUNKS_SOFT;
|
||||
}
|
||||
|
||||
iosource::FD_Set ChunkedIOFd::ExtraReadFDs() const
|
||||
{
|
||||
iosource::FD_Set rval;
|
||||
rval.Insert(write_flare.FD());
|
||||
rval.Insert(read_flare.FD());
|
||||
return rval;
|
||||
}
|
||||
|
||||
void ChunkedIOFd::Clear()
|
||||
{
|
||||
while ( pending_head )
|
||||
|
@ -618,6 +649,9 @@ void ChunkedIOFd::Clear()
|
|||
}
|
||||
|
||||
pending_head = pending_tail = 0;
|
||||
|
||||
if ( write_len == 0 )
|
||||
write_flare.Extinguish();
|
||||
}
|
||||
|
||||
const char* ChunkedIOFd::Error()
|
||||
|
@ -830,6 +864,7 @@ bool ChunkedIOSSL::Write(Chunk* chunk)
|
|||
else
|
||||
write_head = write_tail = q;
|
||||
|
||||
write_flare.Fire();
|
||||
Flush();
|
||||
return true;
|
||||
}
|
||||
|
@ -935,6 +970,7 @@ bool ChunkedIOSSL::Flush()
|
|||
write_state = LEN;
|
||||
}
|
||||
|
||||
write_flare.Extinguish();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1104,6 +1140,13 @@ bool ChunkedIOSSL::IsFillingUp()
|
|||
return false;
|
||||
}
|
||||
|
||||
iosource::FD_Set ChunkedIOSSL::ExtraReadFDs() const
|
||||
{
|
||||
iosource::FD_Set rval;
|
||||
rval.Insert(write_flare.FD());
|
||||
return rval;
|
||||
}
|
||||
|
||||
void ChunkedIOSSL::Clear()
|
||||
{
|
||||
while ( write_head )
|
||||
|
@ -1114,6 +1157,7 @@ void ChunkedIOSSL::Clear()
|
|||
write_head = next;
|
||||
}
|
||||
write_head = write_tail = 0;
|
||||
write_flare.Extinguish();
|
||||
}
|
||||
|
||||
const char* ChunkedIOSSL::Error()
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
#include "config.h"
|
||||
#include "List.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "Flare.h"
|
||||
#include "iosource/FD_Set.h"
|
||||
#include <list>
|
||||
|
||||
#ifdef NEED_KRB5_H
|
||||
|
@ -95,6 +96,11 @@ public:
|
|||
// Returns underlying fd if available, -1 otherwise.
|
||||
virtual int Fd() { return -1; }
|
||||
|
||||
// Returns supplementary file descriptors that become read-ready in order
|
||||
// to signal that there is some work that can be performed.
|
||||
virtual iosource::FD_Set ExtraReadFDs() const
|
||||
{ return iosource::FD_Set(); }
|
||||
|
||||
// Makes sure that no additional protocol data is written into
|
||||
// the output stream. If this is activated, the output cannot
|
||||
// be read again by any of these classes!
|
||||
|
@ -177,6 +183,7 @@ public:
|
|||
virtual void Clear();
|
||||
virtual bool Eof() { return eof; }
|
||||
virtual int Fd() { return fd; }
|
||||
virtual iosource::FD_Set ExtraReadFDs() const;
|
||||
virtual void Stats(char* buffer, int length);
|
||||
|
||||
private:
|
||||
|
@ -240,6 +247,8 @@ private:
|
|||
ChunkQueue* pending_tail;
|
||||
|
||||
pid_t pid;
|
||||
bro::Flare write_flare;
|
||||
bro::Flare read_flare;
|
||||
};
|
||||
|
||||
// Chunked I/O using an SSL connection.
|
||||
|
@ -262,6 +271,7 @@ public:
|
|||
virtual void Clear();
|
||||
virtual bool Eof() { return eof; }
|
||||
virtual int Fd() { return socket; }
|
||||
virtual iosource::FD_Set ExtraReadFDs() const;
|
||||
virtual void Stats(char* buffer, int length);
|
||||
|
||||
private:
|
||||
|
@ -303,6 +313,8 @@ private:
|
|||
|
||||
// One SSL for all connections.
|
||||
static SSL_CTX* ctx;
|
||||
|
||||
bro::Flare write_flare;
|
||||
};
|
||||
|
||||
#include <zlib.h>
|
||||
|
@ -328,6 +340,8 @@ public:
|
|||
|
||||
virtual bool Eof() { return io->Eof(); }
|
||||
virtual int Fd() { return io->Fd(); }
|
||||
virtual iosource::FD_Set ExtraReadFDs() const
|
||||
{ return io->ExtraReadFDs(); }
|
||||
virtual void Stats(char* buffer, int length);
|
||||
|
||||
void EnableCompression(int level)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "Net.h"
|
||||
#include "Var.h"
|
||||
#include "Reporter.h"
|
||||
#include "iosource/Manager.h"
|
||||
|
||||
extern "C" {
|
||||
extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
||||
|
@ -404,17 +405,17 @@ DNS_Mgr::~DNS_Mgr()
|
|||
delete [] dir;
|
||||
}
|
||||
|
||||
bool DNS_Mgr::Init()
|
||||
void DNS_Mgr::InitPostScript()
|
||||
{
|
||||
if ( did_init )
|
||||
return true;
|
||||
return;
|
||||
|
||||
const char* cache_dir = dir ? dir : ".";
|
||||
|
||||
if ( mode == DNS_PRIME && ! ensure_dir(cache_dir) )
|
||||
{
|
||||
did_init = 0;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
cache_name = new char[strlen(cache_dir) + 64];
|
||||
|
@ -433,14 +434,12 @@ bool DNS_Mgr::Init()
|
|||
|
||||
did_init = 1;
|
||||
|
||||
io_sources.Register(this, true);
|
||||
iosource_mgr->Register(this, true);
|
||||
|
||||
// We never set idle to false, having the main loop only calling us from
|
||||
// time to time. If we're issuing more DNS requests than we can handle
|
||||
// in this way, we are having problems anyway ...
|
||||
idle = true;
|
||||
|
||||
return true;
|
||||
SetIdle(true);
|
||||
}
|
||||
|
||||
static TableVal* fake_name_lookup_result(const char* name)
|
||||
|
@ -1217,9 +1216,10 @@ void DNS_Mgr::IssueAsyncRequests()
|
|||
}
|
||||
}
|
||||
|
||||
void DNS_Mgr::GetFds(int* read, int* write, int* except)
|
||||
void DNS_Mgr::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except)
|
||||
{
|
||||
*read = nb_dns_fd(nb_dns);
|
||||
read->Insert(nb_dns_fd(nb_dns));
|
||||
}
|
||||
|
||||
double DNS_Mgr::NextTimestamp(double* network_time)
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "BroList.h"
|
||||
#include "Dict.h"
|
||||
#include "EventHandler.h"
|
||||
#include "IOSource.h"
|
||||
#include "iosource/IOSource.h"
|
||||
#include "IPAddr.h"
|
||||
|
||||
class Val;
|
||||
|
@ -40,12 +40,12 @@ enum DNS_MgrMode {
|
|||
// Number of seconds we'll wait for a reply.
|
||||
#define DNS_TIMEOUT 5
|
||||
|
||||
class DNS_Mgr : public IOSource {
|
||||
class DNS_Mgr : public iosource::IOSource {
|
||||
public:
|
||||
DNS_Mgr(DNS_MgrMode mode);
|
||||
virtual ~DNS_Mgr();
|
||||
|
||||
bool Init();
|
||||
void InitPostScript();
|
||||
void Flush();
|
||||
|
||||
// Looks up the address or addresses of the given host, and returns
|
||||
|
@ -132,7 +132,8 @@ protected:
|
|||
void DoProcess(bool flush);
|
||||
|
||||
// IOSource interface.
|
||||
virtual void GetFds(int* read, int* write, int* except);
|
||||
virtual void GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except);
|
||||
virtual double NextTimestamp(double* network_time);
|
||||
virtual void Process();
|
||||
virtual const char* Tag() { return "DNS_Mgr"; }
|
||||
|
|
|
@ -16,9 +16,10 @@ DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
|
|||
{ "compressor", 0, false }, {"string", 0, false },
|
||||
{ "notifiers", 0, false }, { "main-loop", 0, false },
|
||||
{ "dpd", 0, false }, { "tm", 0, false },
|
||||
{ "logging", 0, false }, {"input", 0, false },
|
||||
{ "logging", 0, false }, {"input", 0, false },
|
||||
{ "threading", 0, false }, { "file_analysis", 0, false },
|
||||
{ "plugins", 0, false }, { "broxygen", 0, false }
|
||||
{ "plugins", 0, false }, { "broxygen", 0, false },
|
||||
{ "pktio", 0, false}
|
||||
};
|
||||
|
||||
DebugLogger::DebugLogger(const char* filename)
|
||||
|
|
|
@ -31,6 +31,7 @@ enum DebugStream {
|
|||
DBG_FILE_ANALYSIS, // File analysis
|
||||
DBG_PLUGINS, // Plugin system
|
||||
DBG_BROXYGEN, // Broxygen
|
||||
DBG_PKTIO, // Packet sources and dumpers.
|
||||
|
||||
NUM_DBGS // Has to be last
|
||||
};
|
||||
|
|
74
src/Flare.cc
Normal file
74
src/Flare.cc
Normal file
|
@ -0,0 +1,74 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "Flare.h"
|
||||
#include "Reporter.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace bro;
|
||||
|
||||
Flare::Flare()
|
||||
: pipe(FD_CLOEXEC, FD_CLOEXEC, O_NONBLOCK, O_NONBLOCK)
|
||||
{
|
||||
}
|
||||
|
||||
static void bad_pipe_op(const char* which)
|
||||
{
|
||||
char buf[256];
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
reporter->FatalErrorWithCore("unexpected pipe %s failure: %s", which, buf);
|
||||
}
|
||||
|
||||
void Flare::Fire()
|
||||
{
|
||||
char tmp;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int n = write(pipe.WriteFD(), &tmp, 1);
|
||||
|
||||
if ( n > 0 )
|
||||
// Success -- wrote a byte to pipe.
|
||||
break;
|
||||
|
||||
if ( n < 0 )
|
||||
{
|
||||
if ( errno == EAGAIN )
|
||||
// Success: pipe is full and just need at least one byte in it.
|
||||
break;
|
||||
|
||||
if ( errno == EINTR )
|
||||
// Interrupted: try again.
|
||||
continue;
|
||||
|
||||
bad_pipe_op("write");
|
||||
}
|
||||
|
||||
// No error, but didn't write a byte: try again.
|
||||
}
|
||||
}
|
||||
|
||||
void Flare::Extinguish()
|
||||
{
|
||||
char tmp[256];
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int n = read(pipe.ReadFD(), &tmp, sizeof(tmp));
|
||||
|
||||
if ( n >= 0 )
|
||||
// Pipe may not be empty yet: try again.
|
||||
continue;
|
||||
|
||||
if ( errno == EAGAIN )
|
||||
// Success: pipe is now empty.
|
||||
break;
|
||||
|
||||
if ( errno == EINTR )
|
||||
// Interrupted: try again.
|
||||
continue;
|
||||
|
||||
bad_pipe_op("read");
|
||||
}
|
||||
}
|
44
src/Flare.h
Normal file
44
src/Flare.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef BRO_FLARE_H
|
||||
#define BRO_FLARE_H
|
||||
|
||||
#include "Pipe.h"
|
||||
|
||||
namespace bro {
|
||||
|
||||
class Flare {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a flare object that can be used to signal a "ready" status via
|
||||
* a file descriptor that may be integrated with select(), poll(), etc.
|
||||
* Not thread-safe, but that should only require Fire()/Extinguish() calls
|
||||
* to be made mutually exclusive (across all copies of a Flare).
|
||||
*/
|
||||
Flare();
|
||||
|
||||
/**
|
||||
* @return a file descriptor that will become ready if the flare has been
|
||||
* Fire()'d and not yet Extinguished()'d.
|
||||
*/
|
||||
int FD() const
|
||||
{ return pipe.ReadFD(); }
|
||||
|
||||
/**
|
||||
* Put the object in the "ready" state.
|
||||
*/
|
||||
void Fire();
|
||||
|
||||
/**
|
||||
* Take the object out of the "ready" state.
|
||||
*/
|
||||
void Extinguish();
|
||||
|
||||
private:
|
||||
Pipe pipe;
|
||||
};
|
||||
|
||||
} // namespace bro
|
||||
|
||||
#endif // BRO_FLARE_H
|
228
src/FlowSrc.cc
228
src/FlowSrc.cc
|
@ -1,228 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
//
|
||||
// Written by Bernhard Ager, TU Berlin (2006/2007).
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "FlowSrc.h"
|
||||
#include "Net.h"
|
||||
#include "analyzer/protocol/netflow/netflow_pac.h"
|
||||
#include <errno.h>
|
||||
|
||||
FlowSrc::FlowSrc()
|
||||
{ // TODO: v9.
|
||||
selectable_fd = -1;
|
||||
idle = false;
|
||||
data = 0;
|
||||
pdu_len = -1;
|
||||
exporter_ip = 0;
|
||||
current_timestamp = next_timestamp = 0.0;
|
||||
netflow_analyzer = new binpac::NetFlow::NetFlow_Analyzer();
|
||||
}
|
||||
|
||||
FlowSrc::~FlowSrc()
|
||||
{
|
||||
delete netflow_analyzer;
|
||||
}
|
||||
|
||||
void FlowSrc::GetFds(int* read, int* write, int* except)
|
||||
{
|
||||
if ( selectable_fd >= 0 )
|
||||
*read = selectable_fd;
|
||||
}
|
||||
|
||||
double FlowSrc::NextTimestamp(double* network_time)
|
||||
{
|
||||
if ( ! data && ! ExtractNextPDU() )
|
||||
return -1.0;
|
||||
else
|
||||
return next_timestamp;
|
||||
}
|
||||
|
||||
void FlowSrc::Process()
|
||||
{
|
||||
if ( ! data && ! ExtractNextPDU() )
|
||||
return;
|
||||
|
||||
// This is normally done by calling net_packet_dispatch(),
|
||||
// but as we don't have a packet to dispatch ...
|
||||
net_update_time(next_timestamp);
|
||||
expire_timers();
|
||||
|
||||
netflow_analyzer->downflow()->set_exporter_ip(exporter_ip);
|
||||
|
||||
// We handle exceptions in NewData (might have changed w/ new binpac).
|
||||
netflow_analyzer->NewData(0, data, data + pdu_len);
|
||||
data = 0;
|
||||
}
|
||||
|
||||
void FlowSrc::Close()
|
||||
{
|
||||
safe_close(selectable_fd);
|
||||
}
|
||||
|
||||
|
||||
FlowSocketSrc::~FlowSocketSrc()
|
||||
{
|
||||
}
|
||||
|
||||
int FlowSocketSrc::ExtractNextPDU()
|
||||
{
|
||||
sockaddr_in from;
|
||||
socklen_t fromlen = sizeof(from);
|
||||
pdu_len = recvfrom(selectable_fd, buffer, NF_MAX_PKT_SIZE, 0,
|
||||
(struct sockaddr*) &from, &fromlen);
|
||||
if ( pdu_len < 0 )
|
||||
{
|
||||
reporter->Error("problem reading NetFlow data from socket");
|
||||
data = 0;
|
||||
next_timestamp = -1.0;
|
||||
closed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( fromlen != sizeof(from) )
|
||||
{
|
||||
reporter->Error("malformed NetFlow PDU");
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = buffer;
|
||||
exporter_ip = from.sin_addr.s_addr;
|
||||
next_timestamp = current_time();
|
||||
|
||||
if ( next_timestamp < current_timestamp )
|
||||
next_timestamp = current_timestamp;
|
||||
else
|
||||
current_timestamp = next_timestamp;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
FlowSocketSrc::FlowSocketSrc(const char* listen_parms)
|
||||
{
|
||||
int n = strlen(listen_parms) + 1;
|
||||
|
||||
char laddr[n], port[n], ident[n];
|
||||
laddr[0] = port[0] = ident[0] = '\0';
|
||||
|
||||
int ret = sscanf(listen_parms, "%[^:]:%[^=]=%s", laddr, port, ident);
|
||||
if ( ret < 2 )
|
||||
{
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"parsing your listen-spec went nuts: laddr='%s', port='%s'\n",
|
||||
laddr[0] ? laddr : "", port[0] ? port : "");
|
||||
closed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const char* id = (ret == 3) ? ident : listen_parms;
|
||||
netflow_analyzer->downflow()->set_identifier(id);
|
||||
|
||||
struct addrinfo aiprefs = {
|
||||
0, PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, NULL, NULL, NULL
|
||||
};
|
||||
struct addrinfo* ainfo = 0;
|
||||
if ( (ret = getaddrinfo(laddr, port, &aiprefs, &ainfo)) != 0 )
|
||||
{
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"getaddrinfo(%s, %s, ...): %s",
|
||||
laddr, port, gai_strerror(ret));
|
||||
closed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (selectable_fd = socket (PF_INET, SOCK_DGRAM, 0)) < 0 )
|
||||
{
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"socket: %s", strerror(errno));
|
||||
closed = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ( bind (selectable_fd, ainfo->ai_addr, ainfo->ai_addrlen) < 0 )
|
||||
{
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"bind: %s", strerror(errno));
|
||||
closed = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
freeaddrinfo(ainfo);
|
||||
}
|
||||
|
||||
|
||||
FlowFileSrc::~FlowFileSrc()
|
||||
{
|
||||
delete [] readfile;
|
||||
}
|
||||
|
||||
int FlowFileSrc::ExtractNextPDU()
|
||||
{
|
||||
FlowFileSrcPDUHeader pdu_header;
|
||||
|
||||
if ( read(selectable_fd, &pdu_header, sizeof(pdu_header)) <
|
||||
int(sizeof(pdu_header)) )
|
||||
return Error(errno, "read header");
|
||||
|
||||
if ( pdu_header.pdu_length > NF_MAX_PKT_SIZE )
|
||||
{
|
||||
reporter->Error("NetFlow packet too long");
|
||||
|
||||
// Safely skip over the too-long PDU.
|
||||
if ( lseek(selectable_fd, pdu_header.pdu_length, SEEK_CUR) < 0 )
|
||||
return Error(errno, "lseek");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( read(selectable_fd, buffer, pdu_header.pdu_length) <
|
||||
pdu_header.pdu_length )
|
||||
return Error(errno, "read data");
|
||||
|
||||
if ( next_timestamp < pdu_header.network_time )
|
||||
{
|
||||
next_timestamp = pdu_header.network_time;
|
||||
current_timestamp = pdu_header.network_time;
|
||||
}
|
||||
else
|
||||
current_timestamp = next_timestamp;
|
||||
|
||||
data = buffer;
|
||||
pdu_len = pdu_header.pdu_length;
|
||||
exporter_ip = pdu_header.ipaddr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
FlowFileSrc::FlowFileSrc(const char* readfile)
|
||||
{
|
||||
int n = strlen(readfile) + 1;
|
||||
char ident[n];
|
||||
this->readfile = new char[n];
|
||||
|
||||
int ret = sscanf(readfile, "%[^=]=%s", this->readfile, ident);
|
||||
const char* id = (ret == 2) ? ident : this->readfile;
|
||||
netflow_analyzer->downflow()->set_identifier(id);
|
||||
|
||||
selectable_fd = open(this->readfile, O_RDONLY);
|
||||
if ( selectable_fd < 0 )
|
||||
{
|
||||
closed = 1;
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"open: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
int FlowFileSrc::Error(int errlvl, const char* errmsg)
|
||||
{
|
||||
snprintf(errbuf, BRO_FLOW_ERRBUF_SIZE,
|
||||
"%s: %s", errmsg, strerror(errlvl));
|
||||
data = 0;
|
||||
next_timestamp = -1.0;
|
||||
closed = 1;
|
||||
return 0;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
//
|
||||
// Written by Bernhard Ager, TU Berlin (2006/2007).
|
||||
|
||||
#ifndef flowsrc_h
|
||||
#define flowsrc_h
|
||||
|
||||
#include "IOSource.h"
|
||||
#include "NetVar.h"
|
||||
#include "binpac.h"
|
||||
|
||||
#define BRO_FLOW_ERRBUF_SIZE 512
|
||||
|
||||
// TODO: 1500 is enough for v5 - how about the others?
|
||||
// 65536 would be enough for any UDP packet.
|
||||
#define NF_MAX_PKT_SIZE 8192
|
||||
|
||||
struct FlowFileSrcPDUHeader {
|
||||
double network_time;
|
||||
int pdu_length;
|
||||
uint32 ipaddr;
|
||||
};
|
||||
|
||||
// Avoid including netflow_pac.h by explicitly declaring the NetFlow_Analyzer.
|
||||
namespace binpac {
|
||||
namespace NetFlow {
|
||||
class NetFlow_Analyzer;
|
||||
}
|
||||
}
|
||||
|
||||
class FlowSrc : public IOSource {
|
||||
public:
|
||||
virtual ~FlowSrc();
|
||||
|
||||
// IOSource interface:
|
||||
bool IsReady();
|
||||
void GetFds(int* read, int* write, int* except);
|
||||
double NextTimestamp(double* network_time);
|
||||
void Process();
|
||||
|
||||
const char* Tag() { return "FlowSrc"; }
|
||||
const char* ErrorMsg() const { return errbuf; }
|
||||
|
||||
protected:
|
||||
FlowSrc();
|
||||
|
||||
virtual int ExtractNextPDU() = 0;
|
||||
virtual void Close();
|
||||
|
||||
int selectable_fd;
|
||||
|
||||
double current_timestamp;
|
||||
double next_timestamp;
|
||||
binpac::NetFlow::NetFlow_Analyzer* netflow_analyzer;
|
||||
|
||||
u_char buffer[NF_MAX_PKT_SIZE];
|
||||
u_char* data;
|
||||
int pdu_len;
|
||||
uint32 exporter_ip; // in network byte order
|
||||
|
||||
char errbuf[BRO_FLOW_ERRBUF_SIZE];
|
||||
};
|
||||
|
||||
class FlowSocketSrc : public FlowSrc {
|
||||
public:
|
||||
FlowSocketSrc(const char* listen_parms);
|
||||
virtual ~FlowSocketSrc();
|
||||
|
||||
int ExtractNextPDU();
|
||||
};
|
||||
|
||||
class FlowFileSrc : public FlowSrc {
|
||||
public:
|
||||
FlowFileSrc(const char* readfile);
|
||||
~FlowFileSrc();
|
||||
|
||||
int ExtractNextPDU();
|
||||
|
||||
protected:
|
||||
int Error(int errlvl, const char* errmsg);
|
||||
char* readfile;
|
||||
};
|
||||
|
||||
#endif
|
176
src/IOSource.cc
176
src/IOSource.cc
|
@ -1,176 +0,0 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "util.h"
|
||||
#include "IOSource.h"
|
||||
|
||||
IOSourceRegistry io_sources;
|
||||
|
||||
IOSourceRegistry::~IOSourceRegistry()
|
||||
{
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
delete *i;
|
||||
|
||||
sources.clear();
|
||||
}
|
||||
|
||||
void IOSourceRegistry::RemoveAll()
|
||||
{
|
||||
// We're cheating a bit here ...
|
||||
dont_counts = sources.size();
|
||||
}
|
||||
|
||||
IOSource* IOSourceRegistry::FindSoonest(double* ts)
|
||||
{
|
||||
// Remove sources which have gone dry. For simplicity, we only
|
||||
// remove at most one each time.
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
if ( ! (*i)->src->IsOpen() )
|
||||
{
|
||||
delete *i;
|
||||
sources.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ideally, we would always call select on the fds to see which
|
||||
// are ready, and return the soonest. Unfortunately, that'd mean
|
||||
// one select-call per packet, which we can't afford in high-volume
|
||||
// environments. Thus, we call select only every SELECT_FREQUENCY
|
||||
// call (or if all sources report that they are dry).
|
||||
|
||||
++call_count;
|
||||
|
||||
IOSource* soonest_src = 0;
|
||||
double soonest_ts = 1e20;
|
||||
double soonest_local_network_time = 1e20;
|
||||
bool all_idle = true;
|
||||
|
||||
// Find soonest source of those which tell us they have something to
|
||||
// process.
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
{
|
||||
if ( ! (*i)->src->IsIdle() )
|
||||
{
|
||||
all_idle = false;
|
||||
double local_network_time = 0;
|
||||
double ts = (*i)->src->NextTimestamp(&local_network_time);
|
||||
if ( ts > 0 && ts < soonest_ts )
|
||||
{
|
||||
soonest_ts = ts;
|
||||
soonest_src = (*i)->src;
|
||||
soonest_local_network_time =
|
||||
local_network_time ?
|
||||
local_network_time : ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found one and aren't going to select this time,
|
||||
// return it.
|
||||
int maxx = 0;
|
||||
|
||||
if ( soonest_src && (call_count % SELECT_FREQUENCY) != 0 )
|
||||
goto finished;
|
||||
|
||||
// Select on the join of all file descriptors.
|
||||
fd_set fd_read, fd_write, fd_except;
|
||||
|
||||
FD_ZERO(&fd_read);
|
||||
FD_ZERO(&fd_write);
|
||||
FD_ZERO(&fd_except);
|
||||
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
{
|
||||
Source* src = (*i);
|
||||
|
||||
if ( ! src->src->IsIdle() )
|
||||
// No need to select on sources which we know to
|
||||
// be ready.
|
||||
continue;
|
||||
|
||||
src->fd_read = src->fd_write = src->fd_except = 0;
|
||||
src->src->GetFds(&src->fd_read, &src->fd_write, &src->fd_except);
|
||||
|
||||
FD_SET(src->fd_read, &fd_read);
|
||||
FD_SET(src->fd_write, &fd_write);
|
||||
FD_SET(src->fd_except, &fd_except);
|
||||
|
||||
maxx = max(src->fd_read, maxx);
|
||||
maxx = max(src->fd_write, maxx);
|
||||
maxx = max(src->fd_except, maxx);
|
||||
}
|
||||
|
||||
// We can't block indefinitely even when all sources are dry:
|
||||
// we're doing some IOSource-independent stuff in the main loop,
|
||||
// so we need to return from time to time. (Instead of no time-out
|
||||
// at all, we use a very small one. This lets FreeBSD trigger a
|
||||
// BPF buffer switch on the next read when the hold buffer is empty
|
||||
// while the store buffer isn't filled yet.
|
||||
|
||||
struct timeval timeout;
|
||||
|
||||
if ( all_idle )
|
||||
{
|
||||
// Interesting: when all sources are dry, simply sleeping a
|
||||
// bit *without* watching for any fd becoming ready may
|
||||
// decrease CPU load. I guess that's because it allows
|
||||
// the kernel's packet buffers to fill. - Robin
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 20; // SELECT_TIMEOUT;
|
||||
select(0, 0, 0, 0, &timeout);
|
||||
}
|
||||
|
||||
if ( ! maxx )
|
||||
// No selectable fd at all.
|
||||
goto finished;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if ( select(maxx + 1, &fd_read, &fd_write, &fd_except, &timeout) > 0 )
|
||||
{ // Find soonest.
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
{
|
||||
Source* src = (*i);
|
||||
|
||||
if ( ! src->src->IsIdle() )
|
||||
continue;
|
||||
|
||||
if ( FD_ISSET(src->fd_read, &fd_read) ||
|
||||
FD_ISSET(src->fd_write, &fd_write) ||
|
||||
FD_ISSET(src->fd_except, &fd_except) )
|
||||
{
|
||||
double local_network_time = 0;
|
||||
double ts = src->src->NextTimestamp(&local_network_time);
|
||||
if ( ts > 0.0 && ts < soonest_ts )
|
||||
{
|
||||
soonest_ts = ts;
|
||||
soonest_src = src->src;
|
||||
soonest_local_network_time =
|
||||
local_network_time ?
|
||||
local_network_time : ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
*ts = soonest_local_network_time;
|
||||
return soonest_src;
|
||||
}
|
||||
|
||||
void IOSourceRegistry::Register(IOSource* src, bool dont_count)
|
||||
{
|
||||
Source* s = new Source;
|
||||
s->src = src;
|
||||
if ( dont_count )
|
||||
++dont_counts;
|
||||
return sources.push_back(s);
|
||||
}
|
103
src/IOSource.h
103
src/IOSource.h
|
@ -1,103 +0,0 @@
|
|||
// Interface for classes providing/consuming data during Bro's main loop.
|
||||
|
||||
#ifndef iosource_h
|
||||
#define iosource_h
|
||||
|
||||
#include <list>
|
||||
#include "Timer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class IOSource {
|
||||
public:
|
||||
IOSource() { idle = closed = false; }
|
||||
virtual ~IOSource() {}
|
||||
|
||||
// Returns true if source has nothing ready to process.
|
||||
bool IsIdle() const { return idle; }
|
||||
|
||||
// Returns true if more data is to be expected in the future.
|
||||
// Otherwise, source may be removed.
|
||||
bool IsOpen() const { return ! closed; }
|
||||
|
||||
// Returns select'able fds (leaves args untouched if we don't have
|
||||
// selectable fds).
|
||||
virtual void GetFds(int* read, int* write, int* except) = 0;
|
||||
|
||||
// The following two methods are only called when either IsIdle()
|
||||
// returns false or select() on one of the fds indicates that there's
|
||||
// data to process.
|
||||
|
||||
// Returns timestamp (in global network time) associated with next
|
||||
// data item. If the source wants the data item to be processed
|
||||
// with a local network time, it sets the argument accordingly.
|
||||
virtual double NextTimestamp(double* network_time) = 0;
|
||||
|
||||
// Processes and consumes next data item.
|
||||
virtual void Process() = 0;
|
||||
|
||||
// Returns tag of timer manager associated with last processed
|
||||
// data item, nil for global timer manager.
|
||||
virtual TimerMgr::Tag* GetCurrentTag() { return 0; }
|
||||
|
||||
// Returns a descriptual tag for debugging.
|
||||
virtual const char* Tag() = 0;
|
||||
|
||||
protected:
|
||||
// Derived classed are to set this to true if they have gone dry
|
||||
// temporarily.
|
||||
bool idle;
|
||||
|
||||
// Derived classed are to set this to true if they have gone dry
|
||||
// permanently.
|
||||
bool closed;
|
||||
};
|
||||
|
||||
class IOSourceRegistry {
|
||||
public:
|
||||
IOSourceRegistry() { call_count = 0; dont_counts = 0; }
|
||||
~IOSourceRegistry();
|
||||
|
||||
// If dont_count is true, this source does not contribute to the
|
||||
// number of IOSources returned by Size(). The effect is that
|
||||
// if all sources but the non-counting ones have gone dry,
|
||||
// processing will shut down.
|
||||
void Register(IOSource* src, bool dont_count = false);
|
||||
|
||||
// This may block for some time.
|
||||
IOSource* FindSoonest(double* ts);
|
||||
|
||||
int Size() const { return sources.size() - dont_counts; }
|
||||
|
||||
// Terminate IOSource processing immediately by removing all
|
||||
// sources (and therefore returning a Size() of zero).
|
||||
void Terminate() { RemoveAll(); }
|
||||
|
||||
protected:
|
||||
// When looking for a source with something to process,
|
||||
// every SELECT_FREQUENCY calls we will go ahead and
|
||||
// block on a select().
|
||||
static const int SELECT_FREQUENCY = 25;
|
||||
|
||||
// Microseconds to wait in an empty select if no source is ready.
|
||||
static const int SELECT_TIMEOUT = 50;
|
||||
|
||||
void RemoveAll();
|
||||
|
||||
unsigned int call_count;
|
||||
int dont_counts;
|
||||
|
||||
struct Source {
|
||||
IOSource* src;
|
||||
int fd_read;
|
||||
int fd_write;
|
||||
int fd_except;
|
||||
};
|
||||
|
||||
typedef list<Source*> SourceList;
|
||||
SourceList sources;
|
||||
};
|
||||
|
||||
extern IOSourceRegistry io_sources;
|
||||
|
||||
#endif
|
198
src/Net.cc
198
src/Net.cc
|
@ -29,6 +29,9 @@
|
|||
#include "Anon.h"
|
||||
#include "Serializer.h"
|
||||
#include "PacketDumper.h"
|
||||
#include "iosource/Manager.h"
|
||||
#include "iosource/PktSrc.h"
|
||||
#include "iosource/PktDumper.h"
|
||||
#include "plugin/Manager.h"
|
||||
|
||||
extern "C" {
|
||||
|
@ -39,10 +42,7 @@ extern "C" {
|
|||
extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
||||
}
|
||||
|
||||
PList(PktSrc) pkt_srcs;
|
||||
|
||||
// FIXME: We should really merge PktDumper and PacketDumper.
|
||||
PktDumper* pkt_dumper = 0;
|
||||
iosource::PktDumper* pkt_dumper = 0;
|
||||
|
||||
int reading_live = 0;
|
||||
int reading_traces = 0;
|
||||
|
@ -63,8 +63,8 @@ const u_char* current_pkt = 0;
|
|||
int current_dispatched = 0;
|
||||
int current_hdr_size = 0;
|
||||
double current_timestamp = 0.0;
|
||||
PktSrc* current_pktsrc = 0;
|
||||
IOSource* current_iosrc;
|
||||
iosource::PktSrc* current_pktsrc = 0;
|
||||
iosource::IOSource* current_iosrc = 0;
|
||||
|
||||
std::list<ScannedFile> files_scanned;
|
||||
std::vector<string> sig_files;
|
||||
|
@ -113,17 +113,21 @@ RETSIGTYPE watchdog(int /* signo */)
|
|||
// saving the packet which caused the
|
||||
// watchdog to trigger may be helpful,
|
||||
// so we'll save that one nevertheless.
|
||||
pkt_dumper = new PktDumper("watchdog-pkt.pcap");
|
||||
if ( pkt_dumper->IsError() )
|
||||
pkt_dumper = iosource_mgr->OpenPktDumper("watchdog-pkt.pcap", false);
|
||||
if ( ! pkt_dumper || pkt_dumper->IsError() )
|
||||
{
|
||||
reporter->Error("watchdog: can't open watchdog-pkt.pcap for writing\n");
|
||||
delete pkt_dumper;
|
||||
reporter->Error("watchdog: can't open watchdog-pkt.pcap for writing");
|
||||
pkt_dumper = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pkt_dumper )
|
||||
pkt_dumper->Dump(current_hdr, current_pkt);
|
||||
{
|
||||
iosource::PktDumper::Packet p;
|
||||
p.hdr = current_hdr;
|
||||
p.data = current_pkt;
|
||||
pkt_dumper->Dump(&p);
|
||||
}
|
||||
}
|
||||
|
||||
net_get_final_stats();
|
||||
|
@ -149,118 +153,40 @@ void net_update_time(double new_network_time)
|
|||
}
|
||||
|
||||
void net_init(name_list& interfaces, name_list& readfiles,
|
||||
name_list& netflows, name_list& flowfiles,
|
||||
const char* writefile, const char* filter,
|
||||
const char* secondary_filter, int do_watchdog)
|
||||
const char* writefile, int do_watchdog)
|
||||
{
|
||||
if ( readfiles.length() > 0 || flowfiles.length() > 0 )
|
||||
if ( readfiles.length() > 0 )
|
||||
{
|
||||
reading_live = pseudo_realtime > 0.0;
|
||||
reading_traces = 1;
|
||||
|
||||
for ( int i = 0; i < readfiles.length(); ++i )
|
||||
{
|
||||
PktFileSrc* ps = new PktFileSrc(readfiles[i], filter);
|
||||
iosource::PktSrc* ps = iosource_mgr->OpenPktSrc(readfiles[i], false);
|
||||
assert(ps);
|
||||
|
||||
if ( ! ps->IsOpen() )
|
||||
reporter->FatalError("%s: problem with trace file %s - %s\n",
|
||||
prog, readfiles[i], ps->ErrorMsg());
|
||||
else
|
||||
{
|
||||
pkt_srcs.append(ps);
|
||||
io_sources.Register(ps);
|
||||
}
|
||||
|
||||
if ( secondary_filter )
|
||||
{
|
||||
// We use a second PktFileSrc for the
|
||||
// secondary path.
|
||||
PktFileSrc* ps = new PktFileSrc(readfiles[i],
|
||||
secondary_filter,
|
||||
TYPE_FILTER_SECONDARY);
|
||||
|
||||
if ( ! ps->IsOpen() )
|
||||
reporter->FatalError("%s: problem with trace file %s - %s\n",
|
||||
prog, readfiles[i],
|
||||
ps->ErrorMsg());
|
||||
else
|
||||
{
|
||||
pkt_srcs.append(ps);
|
||||
io_sources.Register(ps);
|
||||
}
|
||||
|
||||
ps->AddSecondaryTablePrograms();
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 0; i < flowfiles.length(); ++i )
|
||||
{
|
||||
FlowFileSrc* fs = new FlowFileSrc(flowfiles[i]);
|
||||
|
||||
if ( ! fs->IsOpen() )
|
||||
reporter->FatalError("%s: problem with netflow file %s - %s\n",
|
||||
prog, flowfiles[i], fs->ErrorMsg());
|
||||
else
|
||||
{
|
||||
io_sources.Register(fs);
|
||||
}
|
||||
reporter->FatalError("problem with trace file %s (%s)",
|
||||
readfiles[i],
|
||||
ps->ErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
else if ((interfaces.length() > 0 || netflows.length() > 0))
|
||||
else if ( interfaces.length() > 0 )
|
||||
{
|
||||
reading_live = 1;
|
||||
reading_traces = 0;
|
||||
|
||||
for ( int i = 0; i < interfaces.length(); ++i )
|
||||
{
|
||||
PktSrc* ps;
|
||||
ps = new PktInterfaceSrc(interfaces[i], filter);
|
||||
iosource::PktSrc* ps = iosource_mgr->OpenPktSrc(interfaces[i], true);
|
||||
assert(ps);
|
||||
|
||||
if ( ! ps->IsOpen() )
|
||||
reporter->FatalError("%s: problem with interface %s - %s\n",
|
||||
prog, interfaces[i], ps->ErrorMsg());
|
||||
else
|
||||
{
|
||||
pkt_srcs.append(ps);
|
||||
io_sources.Register(ps);
|
||||
}
|
||||
|
||||
if ( secondary_filter )
|
||||
{
|
||||
PktSrc* ps;
|
||||
ps = new PktInterfaceSrc(interfaces[i],
|
||||
filter, TYPE_FILTER_SECONDARY);
|
||||
|
||||
if ( ! ps->IsOpen() )
|
||||
reporter->Error("%s: problem with interface %s - %s\n",
|
||||
prog, interfaces[i],
|
||||
ps->ErrorMsg());
|
||||
else
|
||||
{
|
||||
pkt_srcs.append(ps);
|
||||
io_sources.Register(ps);
|
||||
}
|
||||
|
||||
ps->AddSecondaryTablePrograms();
|
||||
}
|
||||
reporter->FatalError("problem with interface %s (%s)",
|
||||
interfaces[i],
|
||||
ps->ErrorMsg());
|
||||
}
|
||||
|
||||
for ( int i = 0; i < netflows.length(); ++i )
|
||||
{
|
||||
FlowSocketSrc* fs = new FlowSocketSrc(netflows[i]);
|
||||
|
||||
if ( ! fs->IsOpen() )
|
||||
{
|
||||
reporter->Error("%s: problem with netflow socket %s - %s\n",
|
||||
prog, netflows[i], fs->ErrorMsg());
|
||||
delete fs;
|
||||
}
|
||||
|
||||
else
|
||||
io_sources.Register(fs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
@ -272,12 +198,12 @@ void net_init(name_list& interfaces, name_list& readfiles,
|
|||
|
||||
if ( writefile )
|
||||
{
|
||||
// ### This will fail horribly if there are multiple
|
||||
// interfaces with different-lengthed media.
|
||||
pkt_dumper = new PktDumper(writefile);
|
||||
if ( pkt_dumper->IsError() )
|
||||
reporter->FatalError("%s: can't open write file \"%s\" - %s\n",
|
||||
prog, writefile, pkt_dumper->ErrorMsg());
|
||||
pkt_dumper = iosource_mgr->OpenPktDumper(writefile, false);
|
||||
assert(pkt_dumper);
|
||||
|
||||
if ( ! pkt_dumper->IsOpen() )
|
||||
reporter->FatalError("problem opening dump file %s (%s)",
|
||||
writefile, pkt_dumper->ErrorMsg());
|
||||
|
||||
ID* id = global_scope()->Lookup("trace_output_file");
|
||||
if ( ! id )
|
||||
|
@ -298,7 +224,7 @@ void net_init(name_list& interfaces, name_list& readfiles,
|
|||
}
|
||||
}
|
||||
|
||||
void expire_timers(PktSrc* src_ps)
|
||||
void expire_timers(iosource::PktSrc* src_ps)
|
||||
{
|
||||
SegmentProfiler(segment_logger, "expiring-timers");
|
||||
TimerMgr* tmgr =
|
||||
|
@ -311,8 +237,8 @@ void expire_timers(PktSrc* src_ps)
|
|||
}
|
||||
|
||||
void net_packet_dispatch(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* pkt, int hdr_size,
|
||||
PktSrc* src_ps)
|
||||
const u_char* pkt, int hdr_size,
|
||||
iosource::PktSrc* src_ps)
|
||||
{
|
||||
if ( ! bro_start_network_time )
|
||||
bro_start_network_time = t;
|
||||
|
@ -368,11 +294,11 @@ void net_run()
|
|||
{
|
||||
set_processing_status("RUNNING", "net_run");
|
||||
|
||||
while ( io_sources.Size() ||
|
||||
while ( iosource_mgr->Size() ||
|
||||
(BifConst::exit_only_after_terminate && ! terminating) )
|
||||
{
|
||||
double ts;
|
||||
IOSource* src = io_sources.FindSoonest(&ts);
|
||||
iosource::IOSource* src = iosource_mgr->FindSoonest(&ts);
|
||||
|
||||
#ifdef DEBUG
|
||||
static int loop_counter = 0;
|
||||
|
@ -470,16 +396,19 @@ void net_run()
|
|||
|
||||
void net_get_final_stats()
|
||||
{
|
||||
loop_over_list(pkt_srcs, i)
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
{
|
||||
PktSrc* ps = pkt_srcs[i];
|
||||
iosource::PktSrc* ps = *i;
|
||||
|
||||
if ( ps->IsLive() )
|
||||
{
|
||||
struct PktSrc::Stats s;
|
||||
iosource::PktSrc::Stats s;
|
||||
ps->Statistics(&s);
|
||||
reporter->Info("%d packets received on interface %s, %d dropped\n",
|
||||
s.received, ps->Interface(), s.dropped);
|
||||
reporter->Info("%d packets received on interface %s, %d dropped",
|
||||
s.received, ps->Path().c_str(), s.dropped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -499,8 +428,6 @@ void net_finish(int drain_events)
|
|||
sessions->Done();
|
||||
}
|
||||
|
||||
delete pkt_dumper;
|
||||
|
||||
#ifdef DEBUG
|
||||
extern int reassem_seen_bytes, reassem_copied_bytes;
|
||||
// DEBUG_MSG("Reassembly (TCP and IP/Frag): %d bytes seen, %d bytes copied\n",
|
||||
|
@ -521,29 +448,6 @@ void net_delete()
|
|||
delete ip_anonymizer[i];
|
||||
}
|
||||
|
||||
// net_packet_match
|
||||
//
|
||||
// Description:
|
||||
// - Checks if a packet matches a filter. It just wraps up a call to
|
||||
// [pcap.h's] bpf_filter().
|
||||
//
|
||||
// Inputs:
|
||||
// - fp: a BPF-compiled filter
|
||||
// - pkt: a pointer to the packet
|
||||
// - len: the original packet length
|
||||
// - caplen: the captured packet length. This is pkt length
|
||||
//
|
||||
// Output:
|
||||
// - return: 1 if the packet matches the filter, 0 otherwise
|
||||
|
||||
int net_packet_match(BPF_Program* fp, const u_char* pkt,
|
||||
u_int len, u_int caplen)
|
||||
{
|
||||
// NOTE: I don't like too much un-const'ing the pkt variable.
|
||||
return bpf_filter(fp->GetProgram()->bf_insns, (u_char*) pkt, len, caplen);
|
||||
}
|
||||
|
||||
|
||||
int _processing_suspended = 0;
|
||||
|
||||
static double suspend_start = 0;
|
||||
|
@ -561,8 +465,12 @@ void net_continue_processing()
|
|||
if ( _processing_suspended == 1 )
|
||||
{
|
||||
reporter->Info("processing continued");
|
||||
loop_over_list(pkt_srcs, i)
|
||||
pkt_srcs[i]->ContinueAfterSuspend();
|
||||
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
(*i)->ContinueAfterSuspend();
|
||||
}
|
||||
|
||||
--_processing_suspended;
|
||||
|
|
25
src/Net.h
25
src/Net.h
|
@ -5,17 +5,15 @@
|
|||
|
||||
#include "net_util.h"
|
||||
#include "util.h"
|
||||
#include "BPF_Program.h"
|
||||
#include "List.h"
|
||||
#include "PktSrc.h"
|
||||
#include "FlowSrc.h"
|
||||
#include "Func.h"
|
||||
#include "RemoteSerializer.h"
|
||||
#include "iosource/IOSource.h"
|
||||
#include "iosource/PktSrc.h"
|
||||
#include "iosource/PktDumper.h"
|
||||
|
||||
extern void net_init(name_list& interfaces, name_list& readfiles,
|
||||
name_list& netflows, name_list& flowfiles,
|
||||
const char* writefile, const char* filter,
|
||||
const char* secondary_filter, int do_watchdog);
|
||||
const char* writefile, int do_watchdog);
|
||||
extern void net_run();
|
||||
extern void net_get_final_stats();
|
||||
extern void net_finish(int drain_events);
|
||||
|
@ -23,10 +21,8 @@ extern void net_delete(); // Reclaim all memory, etc.
|
|||
extern void net_update_time(double new_network_time);
|
||||
extern void net_packet_dispatch(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* pkt, int hdr_size,
|
||||
PktSrc* src_ps);
|
||||
extern int net_packet_match(BPF_Program* fp, const u_char* pkt,
|
||||
u_int len, u_int caplen);
|
||||
extern void expire_timers(PktSrc* src_ps = 0);
|
||||
iosource::PktSrc* src_ps);
|
||||
extern void expire_timers(iosource::PktSrc* src_ps = 0);
|
||||
extern void termination_signal();
|
||||
|
||||
// Functions to temporarily suspend processing of live input (network packets
|
||||
|
@ -83,13 +79,10 @@ extern const u_char* current_pkt;
|
|||
extern int current_dispatched;
|
||||
extern int current_hdr_size;
|
||||
extern double current_timestamp;
|
||||
extern PktSrc* current_pktsrc;
|
||||
extern IOSource* current_iosrc;
|
||||
extern iosource::PktSrc* current_pktsrc;
|
||||
extern iosource::IOSource* current_iosrc;
|
||||
|
||||
declare(PList,PktSrc);
|
||||
extern PList(PktSrc) pkt_srcs;
|
||||
|
||||
extern PktDumper* pkt_dumper; // where to save packets
|
||||
extern iosource::PktDumper* pkt_dumper; // where to save packets
|
||||
|
||||
extern char* writefile;
|
||||
|
||||
|
|
83
src/Pipe.cc
Normal file
83
src/Pipe.cc
Normal file
|
@ -0,0 +1,83 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "Pipe.h"
|
||||
#include "Reporter.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace bro;
|
||||
|
||||
static void pipe_fail(int eno)
|
||||
{
|
||||
char tmp[256];
|
||||
strerror_r(eno, tmp, sizeof(tmp));
|
||||
reporter->FatalError("Pipe failure: %s", tmp);
|
||||
}
|
||||
|
||||
static void set_flags(int fd, int flags)
|
||||
{
|
||||
if ( flags )
|
||||
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | flags);
|
||||
}
|
||||
|
||||
static void set_status_flags(int fd, int flags)
|
||||
{
|
||||
if ( flags )
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags);
|
||||
}
|
||||
|
||||
static int dup_or_fail(int fd, int flags)
|
||||
{
|
||||
int rval = dup(fd);
|
||||
|
||||
if ( rval < 0 )
|
||||
pipe_fail(errno);
|
||||
|
||||
set_flags(fd, flags);
|
||||
return rval;
|
||||
}
|
||||
|
||||
Pipe::Pipe(int flags0, int flags1, int status_flags0, int status_flags1)
|
||||
{
|
||||
// pipe2 can set flags atomically, but not yet available everywhere.
|
||||
if ( ::pipe(fds) )
|
||||
pipe_fail(errno);
|
||||
|
||||
flags[0] = flags0;
|
||||
flags[1] = flags1;
|
||||
|
||||
set_flags(fds[0], flags[0]);
|
||||
set_flags(fds[1], flags[1]);
|
||||
set_status_flags(fds[0], status_flags0);
|
||||
set_status_flags(fds[1], status_flags1);
|
||||
}
|
||||
|
||||
Pipe::~Pipe()
|
||||
{
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
Pipe::Pipe(const Pipe& other)
|
||||
{
|
||||
fds[0] = dup_or_fail(other.fds[0], other.flags[0]);
|
||||
fds[1] = dup_or_fail(other.fds[1], other.flags[1]);
|
||||
flags[0] = other.flags[0];
|
||||
flags[1] = other.flags[1];
|
||||
}
|
||||
|
||||
Pipe& Pipe::operator=(const Pipe& other)
|
||||
{
|
||||
if ( this == &other )
|
||||
return *this;
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
fds[0] = dup_or_fail(other.fds[0], other.flags[0]);
|
||||
fds[1] = dup_or_fail(other.fds[1], other.flags[1]);
|
||||
flags[0] = other.flags[0];
|
||||
flags[1] = other.flags[1];
|
||||
return *this;
|
||||
}
|
56
src/Pipe.h
Normal file
56
src/Pipe.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef BRO_PIPE_H
|
||||
#define BRO_PIPE_H
|
||||
|
||||
namespace bro {
|
||||
|
||||
class Pipe {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a pair of file descriptors via pipe(), or aborts if it cannot.
|
||||
* @param flags0 file descriptor flags to set on read end of pipe.
|
||||
* @param flags1 file descriptor flags to set on write end of pipe.
|
||||
* @param status_flags0 descriptor status flags to set on read end of pipe.
|
||||
* @param status_flags1 descriptor status flags to set on write end of pipe.
|
||||
*/
|
||||
Pipe(int flags0 = 0, int flags1 = 0, int status_flags0 = 0,
|
||||
int status_flags1 = 0);
|
||||
|
||||
/**
|
||||
* Close the pair of file descriptors owned by the object.
|
||||
*/
|
||||
~Pipe();
|
||||
|
||||
/**
|
||||
* Make a copy of another Pipe object (file descriptors are dup'd).
|
||||
*/
|
||||
Pipe(const Pipe& other);
|
||||
|
||||
/**
|
||||
* Assign a Pipe object by closing file descriptors and duping those of
|
||||
* the other.
|
||||
*/
|
||||
Pipe& operator=(const Pipe& other);
|
||||
|
||||
/**
|
||||
* @return the file descriptor associated with the read-end of the pipe.
|
||||
*/
|
||||
int ReadFD() const
|
||||
{ return fds[0]; }
|
||||
|
||||
/**
|
||||
* @return the file descriptor associated with the write-end of the pipe.
|
||||
*/
|
||||
int WriteFD() const
|
||||
{ return fds[1]; }
|
||||
|
||||
private:
|
||||
int fds[2];
|
||||
int flags[2];
|
||||
};
|
||||
|
||||
} // namespace bro
|
||||
|
||||
#endif // BRO_PIPE_H
|
804
src/PktSrc.cc
804
src/PktSrc.cc
|
@ -1,804 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "PktSrc.h"
|
||||
#include "Hash.h"
|
||||
#include "Net.h"
|
||||
#include "Sessions.h"
|
||||
|
||||
|
||||
// ### This needs auto-confing.
|
||||
#ifdef HAVE_PCAP_INT_H
|
||||
#include <pcap-int.h>
|
||||
#endif
|
||||
|
||||
PktSrc::PktSrc()
|
||||
{
|
||||
interface = readfile = 0;
|
||||
data = last_data = 0;
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr_size = 0;
|
||||
datalink = 0;
|
||||
netmask = 0xffffff00;
|
||||
pd = 0;
|
||||
idle = false;
|
||||
|
||||
next_sync_point = 0;
|
||||
first_timestamp = current_timestamp = next_timestamp = 0.0;
|
||||
first_wallclock = current_wallclock = 0;
|
||||
|
||||
stats.received = stats.dropped = stats.link = 0;
|
||||
}
|
||||
|
||||
PktSrc::~PktSrc()
|
||||
{
|
||||
Close();
|
||||
|
||||
loop_over_list(program_list, i)
|
||||
delete program_list[i];
|
||||
|
||||
BPF_Program* code;
|
||||
IterCookie* cookie = filters.InitForIteration();
|
||||
while ( (code = filters.NextEntry(cookie)) )
|
||||
delete code;
|
||||
|
||||
delete [] interface;
|
||||
delete [] readfile;
|
||||
}
|
||||
|
||||
void PktSrc::GetFds(int* read, int* write, int* except)
|
||||
{
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
// Select would give erroneous results. But we simulate it
|
||||
// by setting idle accordingly.
|
||||
idle = CheckPseudoTime() == 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( selectable_fd >= 0 )
|
||||
*read = selectable_fd;
|
||||
}
|
||||
|
||||
int PktSrc::ExtractNextPacket()
|
||||
{
|
||||
// Don't return any packets if processing is suspended (except for the
|
||||
// very first packet which we need to set up times).
|
||||
if ( net_is_processing_suspended() && first_timestamp )
|
||||
{
|
||||
idle = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = last_data = pcap_next(pd, &hdr);
|
||||
|
||||
if ( data && (hdr.len == 0 || hdr.caplen == 0) )
|
||||
{
|
||||
sessions->Weird("empty_pcap_header", &hdr, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
next_timestamp = hdr.ts.tv_sec + double(hdr.ts.tv_usec) / 1e6;
|
||||
|
||||
if ( pseudo_realtime )
|
||||
current_wallclock = current_time(true);
|
||||
|
||||
if ( ! first_timestamp )
|
||||
first_timestamp = next_timestamp;
|
||||
|
||||
idle = (data == 0);
|
||||
|
||||
if ( data )
|
||||
++stats.received;
|
||||
|
||||
// Source has gone dry. If it's a network interface, this just means
|
||||
// it's timed out. If it's a file, though, then the file has been
|
||||
// exhausted.
|
||||
if ( ! data && ! IsLive() )
|
||||
{
|
||||
closed = true;
|
||||
|
||||
if ( pseudo_realtime && using_communication )
|
||||
{
|
||||
if ( remote_trace_sync_interval )
|
||||
remote_serializer->SendFinalSyncPoint();
|
||||
else
|
||||
remote_serializer->Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
return data != 0;
|
||||
}
|
||||
|
||||
double PktSrc::NextTimestamp(double* local_network_time)
|
||||
{
|
||||
if ( ! data && ! ExtractNextPacket() )
|
||||
return -1.0;
|
||||
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
// Delay packet if necessary.
|
||||
double packet_time = CheckPseudoTime();
|
||||
if ( packet_time )
|
||||
return packet_time;
|
||||
|
||||
idle = true;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
return next_timestamp;
|
||||
}
|
||||
|
||||
void PktSrc::ContinueAfterSuspend()
|
||||
{
|
||||
current_wallclock = current_time(true);
|
||||
}
|
||||
|
||||
double PktSrc::CurrentPacketWallClock()
|
||||
{
|
||||
// We stop time when we are suspended.
|
||||
if ( net_is_processing_suspended() )
|
||||
current_wallclock = current_time(true);
|
||||
|
||||
return current_wallclock;
|
||||
}
|
||||
|
||||
double PktSrc::CheckPseudoTime()
|
||||
{
|
||||
if ( ! data && ! ExtractNextPacket() )
|
||||
return 0;
|
||||
|
||||
if ( ! current_timestamp )
|
||||
return bro_start_time;
|
||||
|
||||
if ( remote_trace_sync_interval )
|
||||
{
|
||||
if ( next_sync_point == 0 || next_timestamp >= next_sync_point )
|
||||
{
|
||||
int n = remote_serializer->SendSyncPoint();
|
||||
next_sync_point = first_timestamp +
|
||||
n * remote_trace_sync_interval;
|
||||
remote_serializer->Log(RemoteSerializer::LogInfo,
|
||||
fmt("stopping at packet %.6f, next sync-point at %.6f",
|
||||
current_timestamp, next_sync_point));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double pseudo_time = next_timestamp - first_timestamp;
|
||||
double ct = (current_time(true) - first_wallclock) * pseudo_realtime;
|
||||
|
||||
return pseudo_time <= ct ? bro_start_time + pseudo_time : 0;
|
||||
}
|
||||
|
||||
void PktSrc::Process()
|
||||
{
|
||||
if ( ! data && ! ExtractNextPacket() )
|
||||
return;
|
||||
|
||||
current_timestamp = next_timestamp;
|
||||
|
||||
int pkt_hdr_size = hdr_size;
|
||||
|
||||
// Unfortunately some packets on the link might have MPLS labels
|
||||
// while others don't. That means we need to ask the link-layer if
|
||||
// labels are in place.
|
||||
bool have_mpls = false;
|
||||
|
||||
int protocol = 0;
|
||||
|
||||
switch ( datalink ) {
|
||||
case DLT_NULL:
|
||||
{
|
||||
protocol = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
|
||||
|
||||
// From the Wireshark Wiki: "AF_INET6, unfortunately, has
|
||||
// different values in {NetBSD,OpenBSD,BSD/OS},
|
||||
// {FreeBSD,DragonFlyBSD}, and {Darwin/Mac OS X}, so an IPv6
|
||||
// packet might have a link-layer header with 24, 28, or 30
|
||||
// as the AF_ value." As we may be reading traces captured on
|
||||
// platforms other than what we're running on, we accept them
|
||||
// all here.
|
||||
if ( protocol != AF_INET
|
||||
&& protocol != AF_INET6
|
||||
&& protocol != 24
|
||||
&& protocol != 28
|
||||
&& protocol != 30 )
|
||||
{
|
||||
sessions->Weird("non_ip_packet_in_null_transport", &hdr, data);
|
||||
data = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DLT_EN10MB:
|
||||
{
|
||||
// Get protocol being carried from the ethernet frame.
|
||||
protocol = (data[12] << 8) + data[13];
|
||||
|
||||
switch ( protocol )
|
||||
{
|
||||
// MPLS carried over the ethernet frame.
|
||||
case 0x8847:
|
||||
// Remove the data link layer and denote a
|
||||
// header size of zero before the IP header.
|
||||
have_mpls = true;
|
||||
data += get_link_header_size(datalink);
|
||||
pkt_hdr_size = 0;
|
||||
break;
|
||||
|
||||
// VLAN carried over the ethernet frame.
|
||||
case 0x8100:
|
||||
data += get_link_header_size(datalink);
|
||||
|
||||
// Check for MPLS in VLAN.
|
||||
if ( ((data[2] << 8) + data[3]) == 0x8847 )
|
||||
have_mpls = true;
|
||||
|
||||
data += 4; // Skip the vlan header
|
||||
pkt_hdr_size = 0;
|
||||
|
||||
// Check for 802.1ah (Q-in-Q) containing IP.
|
||||
// Only do a second layer of vlan tag
|
||||
// stripping because there is no
|
||||
// specification that allows for deeper
|
||||
// nesting.
|
||||
if ( ((data[2] << 8) + data[3]) == 0x0800 )
|
||||
data += 4;
|
||||
|
||||
break;
|
||||
|
||||
// PPPoE carried over the ethernet frame.
|
||||
case 0x8864:
|
||||
data += get_link_header_size(datalink);
|
||||
protocol = (data[6] << 8) + data[7];
|
||||
data += 8; // Skip the PPPoE session and PPP header
|
||||
pkt_hdr_size = 0;
|
||||
|
||||
if ( protocol != 0x0021 && protocol != 0x0057 )
|
||||
{
|
||||
// Neither IPv4 nor IPv6.
|
||||
sessions->Weird("non_ip_packet_in_pppoe_encapsulation", &hdr, data);
|
||||
data = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DLT_PPP_SERIAL:
|
||||
{
|
||||
// Get PPP protocol.
|
||||
protocol = (data[2] << 8) + data[3];
|
||||
|
||||
if ( protocol == 0x0281 )
|
||||
{
|
||||
// MPLS Unicast. Remove the data link layer and
|
||||
// denote a header size of zero before the IP header.
|
||||
have_mpls = true;
|
||||
data += get_link_header_size(datalink);
|
||||
pkt_hdr_size = 0;
|
||||
}
|
||||
|
||||
else if ( protocol != 0x0021 && protocol != 0x0057 )
|
||||
{
|
||||
// Neither IPv4 nor IPv6.
|
||||
sessions->Weird("non_ip_packet_in_ppp_encapsulation", &hdr, data);
|
||||
data = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( have_mpls )
|
||||
{
|
||||
// Skip the MPLS label stack.
|
||||
bool end_of_stack = false;
|
||||
|
||||
while ( ! end_of_stack )
|
||||
{
|
||||
end_of_stack = *(data + 2) & 0x01;
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
current_pseudo = CheckPseudoTime();
|
||||
net_packet_dispatch(current_pseudo, &hdr, data, pkt_hdr_size, this);
|
||||
if ( ! first_wallclock )
|
||||
first_wallclock = current_time(true);
|
||||
}
|
||||
|
||||
else
|
||||
net_packet_dispatch(current_timestamp, &hdr, data, pkt_hdr_size, this);
|
||||
|
||||
data = 0;
|
||||
}
|
||||
|
||||
bool PktSrc::GetCurrentPacket(const struct pcap_pkthdr** arg_hdr,
|
||||
const u_char** arg_pkt)
|
||||
{
|
||||
if ( ! last_data )
|
||||
return false;
|
||||
|
||||
*arg_hdr = &hdr;
|
||||
*arg_pkt = last_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
int PktSrc::PrecompileFilter(int index, const char* filter)
|
||||
{
|
||||
// Compile filter.
|
||||
BPF_Program* code = new BPF_Program();
|
||||
|
||||
if ( ! code->Compile(pd, filter, netmask, errbuf, sizeof(errbuf)) )
|
||||
{
|
||||
delete code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store it in hash.
|
||||
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
|
||||
BPF_Program* oldcode = filters.Lookup(hash);
|
||||
if ( oldcode )
|
||||
delete oldcode;
|
||||
|
||||
filters.Insert(hash, code);
|
||||
delete hash;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PktSrc::SetFilter(int index)
|
||||
{
|
||||
// We don't want load-level filters for the secondary path.
|
||||
if ( filter_type == TYPE_FILTER_SECONDARY && index > 0 )
|
||||
return 1;
|
||||
|
||||
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
|
||||
BPF_Program* code = filters.Lookup(hash);
|
||||
delete hash;
|
||||
|
||||
if ( ! code )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"No precompiled pcap filter for index %d",
|
||||
index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( pcap_setfilter(pd, code->GetProgram()) < 0 )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"pcap_setfilter(%d): %s",
|
||||
index, pcap_geterr(pd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef HAVE_LINUX
|
||||
// Linux doesn't clear counters when resetting filter.
|
||||
stats.received = stats.dropped = stats.link = 0;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PktSrc::SetHdrSize()
|
||||
{
|
||||
int dl = pcap_datalink(pd);
|
||||
hdr_size = get_link_header_size(dl);
|
||||
|
||||
if ( hdr_size < 0 )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"unknown data link type 0x%x", dl);
|
||||
Close();
|
||||
}
|
||||
|
||||
datalink = dl;
|
||||
}
|
||||
|
||||
void PktSrc::Close()
|
||||
{
|
||||
if ( pd )
|
||||
{
|
||||
pcap_close(pd);
|
||||
pd = 0;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PktSrc::AddSecondaryTablePrograms()
|
||||
{
|
||||
BPF_Program* program;
|
||||
|
||||
loop_over_list(secondary_path->EventTable(), i)
|
||||
{
|
||||
SecondaryEvent* se = secondary_path->EventTable()[i];
|
||||
program = new BPF_Program();
|
||||
|
||||
if ( ! program->Compile(snaplen, datalink, se->Filter(),
|
||||
netmask, errbuf, sizeof(errbuf)) )
|
||||
{
|
||||
delete program;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
SecondaryProgram* sp = new SecondaryProgram(program, se);
|
||||
program_list.append(sp);
|
||||
}
|
||||
}
|
||||
|
||||
void PktSrc::Statistics(Stats* s)
|
||||
{
|
||||
if ( reading_traces )
|
||||
s->received = s->dropped = s->link = 0;
|
||||
|
||||
else
|
||||
{
|
||||
struct pcap_stat pstat;
|
||||
if ( pcap_stats(pd, &pstat) < 0 )
|
||||
{
|
||||
reporter->Error("problem getting packet filter statistics: %s",
|
||||
ErrorMsg());
|
||||
s->received = s->dropped = s->link = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
s->dropped = pstat.ps_drop;
|
||||
s->link = pstat.ps_recv;
|
||||
}
|
||||
}
|
||||
|
||||
s->received = stats.received;
|
||||
|
||||
if ( pseudo_realtime )
|
||||
s->dropped = 0;
|
||||
|
||||
stats.dropped = s->dropped;
|
||||
}
|
||||
|
||||
PktInterfaceSrc::PktInterfaceSrc(const char* arg_interface, const char* filter,
|
||||
PktSrc_Filter_Type ft)
|
||||
: PktSrc()
|
||||
{
|
||||
char tmp_errbuf[PCAP_ERRBUF_SIZE];
|
||||
filter_type = ft;
|
||||
|
||||
// Determine interface if not specified.
|
||||
if ( ! arg_interface && ! (arg_interface = pcap_lookupdev(tmp_errbuf)) )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"pcap_lookupdev: %s", tmp_errbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
interface = copy_string(arg_interface);
|
||||
|
||||
// Determine network and netmask.
|
||||
uint32 net;
|
||||
if ( pcap_lookupnet(interface, &net, &netmask, tmp_errbuf) < 0 )
|
||||
{
|
||||
// ### The lookup can fail if no address is assigned to
|
||||
// the interface; and libpcap doesn't have any useful notion
|
||||
// of error codes, just error strings - how bogus - so we
|
||||
// just kludge around the error :-(.
|
||||
// sprintf(errbuf, "pcap_lookupnet %s", tmp_errbuf);
|
||||
// return;
|
||||
net = 0;
|
||||
netmask = 0xffffff00;
|
||||
}
|
||||
|
||||
// We use the smallest time-out possible to return almost immediately if
|
||||
// no packets are available. (We can't use set_nonblocking() as it's
|
||||
// broken on FreeBSD: even when select() indicates that we can read
|
||||
// something, we may get nothing if the store buffer hasn't filled up
|
||||
// yet.)
|
||||
pd = pcap_open_live(interface, snaplen, 1, 1, tmp_errbuf);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"pcap_open_live: %s", tmp_errbuf);
|
||||
closed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// ### This needs autoconf'ing.
|
||||
#ifdef HAVE_PCAP_INT_H
|
||||
reporter->Info("pcap bufsize = %d\n", ((struct pcap *) pd)->bufsize);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX
|
||||
if ( pcap_setnonblock(pd, 1, tmp_errbuf) < 0 )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"pcap_setnonblock: %s", tmp_errbuf);
|
||||
pcap_close(pd);
|
||||
closed = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
selectable_fd = pcap_fileno(pd);
|
||||
|
||||
if ( PrecompileFilter(0, filter) && SetFilter(0) )
|
||||
{
|
||||
SetHdrSize();
|
||||
|
||||
if ( closed )
|
||||
// Couldn't get header size.
|
||||
return;
|
||||
|
||||
reporter->Info("listening on %s, capture length %d bytes\n", interface, snaplen);
|
||||
}
|
||||
else
|
||||
closed = true;
|
||||
}
|
||||
|
||||
|
||||
PktFileSrc::PktFileSrc(const char* arg_readfile, const char* filter,
|
||||
PktSrc_Filter_Type ft)
|
||||
: PktSrc()
|
||||
{
|
||||
readfile = copy_string(arg_readfile);
|
||||
|
||||
filter_type = ft;
|
||||
|
||||
pd = pcap_open_offline((char*) readfile, errbuf);
|
||||
|
||||
if ( pd && PrecompileFilter(0, filter) && SetFilter(0) )
|
||||
{
|
||||
SetHdrSize();
|
||||
|
||||
if ( closed )
|
||||
// Unknown link layer type.
|
||||
return;
|
||||
|
||||
// We don't put file sources into non-blocking mode as
|
||||
// otherwise we would not be able to identify the EOF.
|
||||
|
||||
selectable_fd = fileno(pcap_file(pd));
|
||||
|
||||
if ( selectable_fd < 0 )
|
||||
reporter->InternalError("OS does not support selectable pcap fd");
|
||||
}
|
||||
else
|
||||
closed = true;
|
||||
}
|
||||
|
||||
|
||||
SecondaryPath::SecondaryPath()
|
||||
{
|
||||
filter = 0;
|
||||
|
||||
// Glue together the secondary filter, if exists.
|
||||
Val* secondary_fv = internal_val("secondary_filters");
|
||||
if ( secondary_fv->AsTableVal()->Size() == 0 )
|
||||
return;
|
||||
|
||||
int did_first = 0;
|
||||
const TableEntryValPDict* v = secondary_fv->AsTable();
|
||||
IterCookie* c = v->InitForIteration();
|
||||
TableEntryVal* tv;
|
||||
HashKey* h;
|
||||
|
||||
while ( (tv = v->NextEntry(h, c)) )
|
||||
{
|
||||
// Get the index values.
|
||||
ListVal* index =
|
||||
secondary_fv->AsTableVal()->RecoverIndex(h);
|
||||
|
||||
const char* str =
|
||||
index->Index(0)->Ref()->AsString()->CheckString();
|
||||
|
||||
if ( ++did_first == 1 )
|
||||
{
|
||||
filter = copy_string(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( strlen(filter) > 0 )
|
||||
{
|
||||
char* tmp_f = new char[strlen(str) + strlen(filter) + 32];
|
||||
if ( strlen(str) == 0 )
|
||||
sprintf(tmp_f, "%s", filter);
|
||||
else
|
||||
sprintf(tmp_f, "(%s) or (%s)", filter, str);
|
||||
delete [] filter;
|
||||
filter = tmp_f;
|
||||
}
|
||||
}
|
||||
|
||||
// Build secondary_path event table item and link it.
|
||||
SecondaryEvent* se =
|
||||
new SecondaryEvent(index->Index(0)->Ref()->AsString()->CheckString(),
|
||||
tv->Value()->AsFunc() );
|
||||
|
||||
event_list.append(se);
|
||||
|
||||
delete h;
|
||||
Unref(index);
|
||||
}
|
||||
}
|
||||
|
||||
SecondaryPath::~SecondaryPath()
|
||||
{
|
||||
loop_over_list(event_list, i)
|
||||
delete event_list[i];
|
||||
|
||||
delete [] filter;
|
||||
}
|
||||
|
||||
|
||||
SecondaryProgram::~SecondaryProgram()
|
||||
{
|
||||
delete program;
|
||||
}
|
||||
|
||||
PktDumper::PktDumper(const char* arg_filename, bool arg_append)
|
||||
{
|
||||
filename[0] = '\0';
|
||||
is_error = false;
|
||||
append = arg_append;
|
||||
dumper = 0;
|
||||
open_time = 0.0;
|
||||
|
||||
// We need a pcap_t with a reasonable link-layer type. We try to get it
|
||||
// from the packet sources. If not available, we fall back to Ethernet.
|
||||
// FIXME: Perhaps we should make this configurable?
|
||||
int linktype = -1;
|
||||
|
||||
if ( pkt_srcs.length() )
|
||||
linktype = pkt_srcs[0]->LinkType();
|
||||
|
||||
if ( linktype < 0 )
|
||||
linktype = DLT_EN10MB;
|
||||
|
||||
pd = pcap_open_dead(linktype, snaplen);
|
||||
if ( ! pd )
|
||||
{
|
||||
Error("error for pcap_open_dead");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( arg_filename )
|
||||
Open(arg_filename);
|
||||
}
|
||||
|
||||
bool PktDumper::Open(const char* arg_filename)
|
||||
{
|
||||
if ( ! arg_filename && ! *filename )
|
||||
{
|
||||
Error("no filename given");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( arg_filename )
|
||||
{
|
||||
if ( dumper && streq(arg_filename, filename) )
|
||||
// Already open.
|
||||
return true;
|
||||
|
||||
safe_strncpy(filename, arg_filename, FNBUF_LEN);
|
||||
}
|
||||
|
||||
if ( dumper )
|
||||
Close();
|
||||
|
||||
struct stat s;
|
||||
int exists = 0;
|
||||
|
||||
if ( append )
|
||||
{
|
||||
// See if output file already exists (and is non-empty).
|
||||
exists = stat(filename, &s); ;
|
||||
|
||||
if ( exists < 0 && errno != ENOENT )
|
||||
{
|
||||
Error(fmt("can't stat file %s: %s", filename, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! append || exists < 0 || s.st_size == 0 )
|
||||
{
|
||||
// Open new file.
|
||||
dumper = pcap_dump_open(pd, filename);
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(pcap_geterr(pd));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Old file and we need to append, which, unfortunately,
|
||||
// is not supported by libpcap. So, we have to hack a
|
||||
// little bit, knowing that pcap_dumpter_t is, in fact,
|
||||
// a FILE ... :-(
|
||||
dumper = (pcap_dumper_t*) fopen(filename, "a");
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(fmt("can't open dump %s: %s", filename, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
open_time = network_time;
|
||||
is_error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PktDumper::Close()
|
||||
{
|
||||
if ( dumper )
|
||||
{
|
||||
pcap_dump_close(dumper);
|
||||
dumper = 0;
|
||||
is_error = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PktDumper::Dump(const struct pcap_pkthdr* hdr, const u_char* pkt)
|
||||
{
|
||||
if ( ! dumper )
|
||||
return false;
|
||||
|
||||
if ( ! open_time )
|
||||
open_time = network_time;
|
||||
|
||||
pcap_dump((u_char*) dumper, hdr, pkt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PktDumper::Error(const char* errstr)
|
||||
{
|
||||
safe_strncpy(errbuf, errstr, sizeof(errbuf));
|
||||
is_error = true;
|
||||
}
|
||||
|
||||
int get_link_header_size(int dl)
|
||||
{
|
||||
switch ( dl ) {
|
||||
case DLT_NULL:
|
||||
return 4;
|
||||
|
||||
case DLT_EN10MB:
|
||||
return 14;
|
||||
|
||||
case DLT_FDDI:
|
||||
return 13 + 8; // fddi_header + LLC
|
||||
|
||||
#ifdef DLT_LINUX_SLL
|
||||
case DLT_LINUX_SLL:
|
||||
return 16;
|
||||
#endif
|
||||
|
||||
case DLT_PPP_SERIAL: // PPP_SERIAL
|
||||
return 4;
|
||||
|
||||
case DLT_RAW:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
258
src/PktSrc.h
258
src/PktSrc.h
|
@ -1,258 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef pktsrc_h
|
||||
#define pktsrc_h
|
||||
|
||||
#include "Dict.h"
|
||||
#include "Expr.h"
|
||||
#include "BPF_Program.h"
|
||||
#include "IOSource.h"
|
||||
#include "RemoteSerializer.h"
|
||||
|
||||
#define BRO_PCAP_ERRBUF_SIZE PCAP_ERRBUF_SIZE + 256
|
||||
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
|
||||
declare(PDict,BPF_Program);
|
||||
|
||||
// Whether a PktSrc object is used by the normal filter structure or the
|
||||
// secondary-path structure.
|
||||
typedef enum {
|
||||
TYPE_FILTER_NORMAL, // the normal filter
|
||||
TYPE_FILTER_SECONDARY, // the secondary-path filter
|
||||
} PktSrc_Filter_Type;
|
||||
|
||||
|
||||
// {filter,event} tuples conforming the secondary path.
|
||||
class SecondaryEvent {
|
||||
public:
|
||||
SecondaryEvent(const char* arg_filter, Func* arg_event)
|
||||
{
|
||||
filter = arg_filter;
|
||||
event = arg_event;
|
||||
}
|
||||
|
||||
const char* Filter() { return filter; }
|
||||
Func* Event() { return event; }
|
||||
|
||||
private:
|
||||
const char* filter;
|
||||
Func* event;
|
||||
};
|
||||
|
||||
declare(PList,SecondaryEvent);
|
||||
typedef PList(SecondaryEvent) secondary_event_list;
|
||||
|
||||
|
||||
|
||||
class SecondaryPath {
|
||||
public:
|
||||
SecondaryPath();
|
||||
~SecondaryPath();
|
||||
|
||||
secondary_event_list& EventTable() { return event_list; }
|
||||
const char* Filter() { return filter; }
|
||||
|
||||
private:
|
||||
secondary_event_list event_list;
|
||||
// OR'ed union of all SecondaryEvent filters
|
||||
char* filter;
|
||||
};
|
||||
|
||||
// Main secondary-path object.
|
||||
extern SecondaryPath* secondary_path;
|
||||
|
||||
|
||||
// {program, {filter,event}} tuple table.
|
||||
class SecondaryProgram {
|
||||
public:
|
||||
SecondaryProgram(BPF_Program* arg_program, SecondaryEvent* arg_event)
|
||||
{
|
||||
program = arg_program;
|
||||
event = arg_event;
|
||||
}
|
||||
|
||||
~SecondaryProgram();
|
||||
|
||||
BPF_Program* Program() { return program; }
|
||||
SecondaryEvent* Event() { return event; }
|
||||
|
||||
private:
|
||||
// Associated program.
|
||||
BPF_Program *program;
|
||||
|
||||
// Event that is run in case the program is matched.
|
||||
SecondaryEvent* event;
|
||||
};
|
||||
|
||||
declare(PList,SecondaryProgram);
|
||||
typedef PList(SecondaryProgram) secondary_program_list;
|
||||
|
||||
|
||||
|
||||
class PktSrc : public IOSource {
|
||||
public:
|
||||
~PktSrc();
|
||||
|
||||
// IOSource interface
|
||||
bool IsReady();
|
||||
void GetFds(int* read, int* write, int* except);
|
||||
double NextTimestamp(double* local_network_time);
|
||||
void Process();
|
||||
const char* Tag() { return "PktSrc"; }
|
||||
|
||||
const char* ErrorMsg() const { return errbuf; }
|
||||
void ClearErrorMsg() { *errbuf ='\0'; }
|
||||
|
||||
// Returns the packet last processed; false if there is no
|
||||
// current packet available.
|
||||
bool GetCurrentPacket(const pcap_pkthdr** hdr, const u_char** pkt);
|
||||
|
||||
int HdrSize() const { return hdr_size; }
|
||||
int DataLink() const { return datalink; }
|
||||
|
||||
void ConsumePacket() { data = 0; }
|
||||
|
||||
int IsLive() const { return interface != 0; }
|
||||
|
||||
pcap_t* PcapHandle() const { return pd; }
|
||||
int LinkType() const { return pcap_datalink(pd); }
|
||||
|
||||
const char* ReadFile() const { return readfile; }
|
||||
const char* Interface() const { return interface; }
|
||||
PktSrc_Filter_Type FilterType() const { return filter_type; }
|
||||
void AddSecondaryTablePrograms();
|
||||
const secondary_program_list& ProgramTable() const
|
||||
{ return program_list; }
|
||||
|
||||
// Signal packet source that processing was suspended and is now going
|
||||
// to be continued.
|
||||
void ContinueAfterSuspend();
|
||||
|
||||
// Only valid in pseudo-realtime mode.
|
||||
double CurrentPacketTimestamp() { return current_pseudo; }
|
||||
double CurrentPacketWallClock();
|
||||
|
||||
struct Stats {
|
||||
unsigned int received; // pkts received (w/o drops)
|
||||
unsigned int dropped; // pkts dropped
|
||||
unsigned int link; // total packets on link
|
||||
// (not always not available)
|
||||
};
|
||||
|
||||
virtual void Statistics(Stats* stats);
|
||||
|
||||
// Precompiles a filter and associates the given index with it.
|
||||
// Returns true on success, 0 if a problem occurred.
|
||||
virtual int PrecompileFilter(int index, const char* filter);
|
||||
|
||||
// Activates the filter with the given index.
|
||||
// Returns true on success, 0 if a problem occurred.
|
||||
virtual int SetFilter(int index);
|
||||
|
||||
protected:
|
||||
PktSrc();
|
||||
|
||||
static const int PCAP_TIMEOUT = 20;
|
||||
|
||||
void SetHdrSize();
|
||||
|
||||
virtual void Close();
|
||||
|
||||
// Returns 1 on success, 0 on time-out/gone dry.
|
||||
virtual int ExtractNextPacket();
|
||||
|
||||
// Checks if the current packet has a pseudo-time <= current_time.
|
||||
// If yes, returns pseudo-time, otherwise 0.
|
||||
double CheckPseudoTime();
|
||||
|
||||
double current_timestamp;
|
||||
double next_timestamp;
|
||||
|
||||
// Only set in pseudo-realtime mode.
|
||||
double first_timestamp;
|
||||
double first_wallclock;
|
||||
double current_wallclock;
|
||||
double current_pseudo;
|
||||
|
||||
struct pcap_pkthdr hdr;
|
||||
const u_char* data; // contents of current packet
|
||||
const u_char* last_data; // same, but unaffected by consuming
|
||||
int hdr_size;
|
||||
int datalink;
|
||||
double next_sync_point; // For trace synchronziation in pseudo-realtime
|
||||
|
||||
char* interface; // nil if not reading from an interface
|
||||
char* readfile; // nil if not reading from a file
|
||||
|
||||
pcap_t* pd;
|
||||
int selectable_fd;
|
||||
uint32 netmask;
|
||||
char errbuf[BRO_PCAP_ERRBUF_SIZE];
|
||||
|
||||
Stats stats;
|
||||
|
||||
PDict(BPF_Program) filters; // precompiled filters
|
||||
|
||||
PktSrc_Filter_Type filter_type; // normal path or secondary path
|
||||
secondary_program_list program_list;
|
||||
};
|
||||
|
||||
class PktInterfaceSrc : public PktSrc {
|
||||
public:
|
||||
PktInterfaceSrc(const char* interface, const char* filter,
|
||||
PktSrc_Filter_Type ft=TYPE_FILTER_NORMAL);
|
||||
};
|
||||
|
||||
class PktFileSrc : public PktSrc {
|
||||
public:
|
||||
PktFileSrc(const char* readfile, const char* filter,
|
||||
PktSrc_Filter_Type ft=TYPE_FILTER_NORMAL);
|
||||
};
|
||||
|
||||
|
||||
extern int get_link_header_size(int dl);
|
||||
|
||||
class PktDumper {
|
||||
public:
|
||||
PktDumper(const char* file = 0, bool append = false);
|
||||
~PktDumper() { Close(); }
|
||||
|
||||
bool Open(const char* file = 0);
|
||||
bool Close();
|
||||
bool Dump(const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||
|
||||
pcap_dumper_t* PcapDumper() { return dumper; }
|
||||
|
||||
const char* FileName() const { return filename; }
|
||||
bool IsError() const { return is_error; }
|
||||
const char* ErrorMsg() const { return errbuf; }
|
||||
|
||||
// This heuristic will horribly fail if we're using packets
|
||||
// with different link layers. (If we can't derive a reasonable value
|
||||
// from the packet sources, our fall-back is Ethernet.)
|
||||
int HdrSize() const
|
||||
{ return get_link_header_size(pcap_datalink(pd)); }
|
||||
|
||||
// Network time when dump file was opened.
|
||||
double OpenTime() const { return open_time; }
|
||||
|
||||
private:
|
||||
void InitPd();
|
||||
void Error(const char* str);
|
||||
|
||||
static const int FNBUF_LEN = 1024;
|
||||
char filename[FNBUF_LEN];
|
||||
|
||||
bool append;
|
||||
pcap_dumper_t* dumper;
|
||||
pcap_t* pd;
|
||||
double open_time;
|
||||
|
||||
bool is_error;
|
||||
char errbuf[BRO_PCAP_ERRBUF_SIZE];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -188,10 +188,10 @@
|
|||
#include "File.h"
|
||||
#include "Conn.h"
|
||||
#include "Reporter.h"
|
||||
#include "threading/SerialTypes.h"
|
||||
#include "logging/Manager.h"
|
||||
#include "IPAddr.h"
|
||||
#include "bro_inet_ntop.h"
|
||||
#include "iosource/Manager.h"
|
||||
#include "logging/Manager.h"
|
||||
#include "logging/logging.bif.h"
|
||||
|
||||
extern "C" {
|
||||
|
@ -285,10 +285,10 @@ struct ping_args {
|
|||
\
|
||||
if ( ! c ) \
|
||||
{ \
|
||||
idle = io->IsIdle();\
|
||||
SetIdle(io->IsIdle());\
|
||||
return true; \
|
||||
} \
|
||||
idle = false; \
|
||||
SetIdle(false); \
|
||||
}
|
||||
|
||||
static const char* msgToStr(int msg)
|
||||
|
@ -534,7 +534,6 @@ RemoteSerializer::RemoteSerializer()
|
|||
current_sync_point = 0;
|
||||
syncing_times = false;
|
||||
io = 0;
|
||||
closed = false;
|
||||
terminating = false;
|
||||
in_sync = 0;
|
||||
last_flush = 0;
|
||||
|
@ -559,7 +558,7 @@ RemoteSerializer::~RemoteSerializer()
|
|||
delete io;
|
||||
}
|
||||
|
||||
void RemoteSerializer::Init()
|
||||
void RemoteSerializer::Enable()
|
||||
{
|
||||
if ( initialized )
|
||||
return;
|
||||
|
@ -572,7 +571,7 @@ void RemoteSerializer::Init()
|
|||
|
||||
Fork();
|
||||
|
||||
io_sources.Register(this);
|
||||
iosource_mgr->Register(this);
|
||||
|
||||
Log(LogInfo, fmt("communication started, parent pid is %d, child pid is %d", getpid(), child_pid));
|
||||
initialized = 1;
|
||||
|
@ -1276,7 +1275,7 @@ bool RemoteSerializer::Listen(const IPAddr& ip, uint16 port, bool expect_ssl,
|
|||
return false;
|
||||
|
||||
listening = true;
|
||||
closed = false;
|
||||
SetClosed(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1345,7 +1344,7 @@ bool RemoteSerializer::StopListening()
|
|||
return false;
|
||||
|
||||
listening = false;
|
||||
closed = ! IsActive();
|
||||
SetClosed(! IsActive());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1368,12 +1367,14 @@ void RemoteSerializer::Unregister(ID* id)
|
|||
}
|
||||
}
|
||||
|
||||
void RemoteSerializer::GetFds(int* read, int* write, int* except)
|
||||
void RemoteSerializer::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except)
|
||||
{
|
||||
*read = io->Fd();
|
||||
read->Insert(io->Fd());
|
||||
read->Insert(io->ExtraReadFDs());
|
||||
|
||||
if ( io->CanWrite() )
|
||||
*write = io->Fd();
|
||||
write->Insert(io->Fd());
|
||||
}
|
||||
|
||||
double RemoteSerializer::NextTimestamp(double* local_network_time)
|
||||
|
@ -1383,7 +1384,7 @@ double RemoteSerializer::NextTimestamp(double* local_network_time)
|
|||
if ( received_logs > 0 )
|
||||
{
|
||||
// If we processed logs last time, assume there's more.
|
||||
idle = false;
|
||||
SetIdle(false);
|
||||
received_logs = 0;
|
||||
return timer_mgr->Time();
|
||||
}
|
||||
|
@ -1398,7 +1399,7 @@ double RemoteSerializer::NextTimestamp(double* local_network_time)
|
|||
pt = timer_mgr->Time();
|
||||
|
||||
if ( packets.length() )
|
||||
idle = false;
|
||||
SetIdle(false);
|
||||
|
||||
if ( et >= 0 && (et < pt || pt < 0) )
|
||||
return et;
|
||||
|
@ -1477,7 +1478,7 @@ void RemoteSerializer::Process()
|
|||
}
|
||||
|
||||
if ( packets.length() )
|
||||
idle = false;
|
||||
SetIdle(false);
|
||||
}
|
||||
|
||||
void RemoteSerializer::Finish()
|
||||
|
@ -1509,7 +1510,7 @@ bool RemoteSerializer::Poll(bool may_block)
|
|||
}
|
||||
|
||||
io->Flush();
|
||||
idle = false;
|
||||
SetIdle(false);
|
||||
|
||||
switch ( msgstate ) {
|
||||
case TYPE:
|
||||
|
@ -1691,7 +1692,7 @@ bool RemoteSerializer::DoMessage()
|
|||
|
||||
case MSG_TERMINATE:
|
||||
assert(terminating);
|
||||
io_sources.Terminate();
|
||||
iosource_mgr->Terminate();
|
||||
return true;
|
||||
|
||||
case MSG_REMOTE_PRINT:
|
||||
|
@ -1879,7 +1880,7 @@ void RemoteSerializer::RemovePeer(Peer* peer)
|
|||
delete peer->cache_out;
|
||||
delete peer;
|
||||
|
||||
closed = ! IsActive();
|
||||
SetClosed(! IsActive());
|
||||
|
||||
if ( in_sync == peer )
|
||||
in_sync = 0;
|
||||
|
@ -2841,7 +2842,7 @@ void RemoteSerializer::GotEvent(const char* name, double time,
|
|||
BufferedEvent* e = new BufferedEvent;
|
||||
|
||||
// Our time, not the time when the event was generated.
|
||||
e->time = pkt_srcs.length() ?
|
||||
e->time = iosource_mgr->GetPktSrcs().size() ?
|
||||
time_t(network_time) : time_t(timer_mgr->Time());
|
||||
|
||||
e->src = current_peer->id;
|
||||
|
@ -3086,7 +3087,7 @@ RecordVal* RemoteSerializer::GetPeerVal(PeerID id)
|
|||
void RemoteSerializer::ChildDied()
|
||||
{
|
||||
Log(LogError, "child died");
|
||||
closed = true;
|
||||
SetClosed(true);
|
||||
child_pid = 0;
|
||||
|
||||
// Shut down the main process as well.
|
||||
|
@ -3185,7 +3186,7 @@ void RemoteSerializer::FatalError(const char* msg)
|
|||
Log(LogError, msg);
|
||||
reporter->Error("%s", msg);
|
||||
|
||||
closed = true;
|
||||
SetClosed(true);
|
||||
|
||||
if ( kill(child_pid, SIGQUIT) < 0 )
|
||||
reporter->Warning("warning: cannot kill child pid %d, %s", child_pid, strerror(errno));
|
||||
|
@ -3356,6 +3357,15 @@ SocketComm::~SocketComm()
|
|||
|
||||
static unsigned int first_rtime = 0;
|
||||
|
||||
static void fd_vector_set(const std::vector<int>& fds, fd_set* set, int* max)
|
||||
{
|
||||
for ( size_t i = 0; i < fds.size(); ++i )
|
||||
{
|
||||
FD_SET(fds[i], set);
|
||||
*max = ::max(fds[i], *max);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketComm::Run()
|
||||
{
|
||||
first_rtime = (unsigned int) current_time(true);
|
||||
|
@ -3377,10 +3387,9 @@ void SocketComm::Run()
|
|||
FD_ZERO(&fd_write);
|
||||
FD_ZERO(&fd_except);
|
||||
|
||||
int max_fd = 0;
|
||||
|
||||
int max_fd = io->Fd();
|
||||
FD_SET(io->Fd(), &fd_read);
|
||||
max_fd = io->Fd();
|
||||
max_fd = std::max(max_fd, io->ExtraReadFDs().Set(&fd_read));
|
||||
|
||||
loop_over_list(peers, i)
|
||||
{
|
||||
|
@ -3389,6 +3398,8 @@ void SocketComm::Run()
|
|||
FD_SET(peers[i]->io->Fd(), &fd_read);
|
||||
if ( peers[i]->io->Fd() > max_fd )
|
||||
max_fd = peers[i]->io->Fd();
|
||||
max_fd = std::max(max_fd,
|
||||
peers[i]->io->ExtraReadFDs().Set(&fd_read));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3439,38 +3450,17 @@ void SocketComm::Run()
|
|||
if ( ! io->IsFillingUp() && shutting_conns_down )
|
||||
shutting_conns_down = false;
|
||||
|
||||
// We cannot rely solely on select() as the there may
|
||||
// be some data left in our input/output queues. So, we use
|
||||
// a small timeout for select and check for data
|
||||
// manually afterwards.
|
||||
|
||||
static long selects = 0;
|
||||
static long canwrites = 0;
|
||||
static long timeouts = 0;
|
||||
|
||||
++selects;
|
||||
if ( io->CanWrite() )
|
||||
++canwrites;
|
||||
|
||||
// FIXME: Fine-tune this (timeouts, flush, etc.)
|
||||
struct timeval small_timeout;
|
||||
small_timeout.tv_sec = 0;
|
||||
small_timeout.tv_usec =
|
||||
io->CanWrite() || io->CanRead() ? 1 : 10;
|
||||
|
||||
#if 0
|
||||
if ( ! io->CanWrite() )
|
||||
usleep(10);
|
||||
#endif
|
||||
|
||||
int a = select(max_fd + 1, &fd_read, &fd_write, &fd_except,
|
||||
&small_timeout);
|
||||
|
||||
if ( a == 0 )
|
||||
++timeouts;
|
||||
int a = select(max_fd + 1, &fd_read, &fd_write, &fd_except, 0);
|
||||
|
||||
if ( selects % 100000 == 0 )
|
||||
Log(fmt("selects=%ld canwrites=%ld timeouts=%ld", selects, canwrites, timeouts));
|
||||
Log(fmt("selects=%ld canwrites=%ld", selects, canwrites));
|
||||
|
||||
if ( a < 0 )
|
||||
// Ignore errors for now.
|
||||
|
@ -4212,6 +4202,7 @@ bool SocketComm::Listen()
|
|||
safe_close(fd);
|
||||
CloseListenFDs();
|
||||
listen_next_try = time(0) + bind_retry_interval;
|
||||
freeaddrinfo(res0);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "Dict.h"
|
||||
#include "List.h"
|
||||
#include "Serializer.h"
|
||||
#include "IOSource.h"
|
||||
#include "iosource/IOSource.h"
|
||||
#include "Stats.h"
|
||||
#include "File.h"
|
||||
#include "logging/WriterBackend.h"
|
||||
|
@ -22,13 +22,13 @@ namespace threading {
|
|||
}
|
||||
|
||||
// This class handles the communication done in Bro's main loop.
|
||||
class RemoteSerializer : public Serializer, public IOSource {
|
||||
class RemoteSerializer : public Serializer, public iosource::IOSource {
|
||||
public:
|
||||
RemoteSerializer();
|
||||
virtual ~RemoteSerializer();
|
||||
|
||||
// Initialize the remote serializer (calling this will fork).
|
||||
void Init();
|
||||
void Enable();
|
||||
|
||||
// FIXME: Use SourceID directly (or rename everything to Peer*).
|
||||
typedef SourceID PeerID;
|
||||
|
@ -140,7 +140,8 @@ public:
|
|||
void Finish();
|
||||
|
||||
// Overidden from IOSource:
|
||||
virtual void GetFds(int* read, int* write, int* except);
|
||||
virtual void GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except);
|
||||
virtual double NextTimestamp(double* local_network_time);
|
||||
virtual void Process();
|
||||
virtual TimerMgr::Tag* GetCurrentTag();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "Conn.h"
|
||||
#include "Timer.h"
|
||||
#include "RemoteSerializer.h"
|
||||
#include "iosource/Manager.h"
|
||||
|
||||
Serializer::Serializer(SerializationFormat* arg_format)
|
||||
{
|
||||
|
@ -1045,7 +1046,7 @@ EventPlayer::EventPlayer(const char* file)
|
|||
Error(fmt("event replayer: cannot open %s", file));
|
||||
|
||||
if ( ReadHeader() )
|
||||
io_sources.Register(this);
|
||||
iosource_mgr->Register(this);
|
||||
}
|
||||
|
||||
EventPlayer::~EventPlayer()
|
||||
|
@ -1067,9 +1068,10 @@ void EventPlayer::GotFunctionCall(const char* name, double time,
|
|||
// We don't replay function calls.
|
||||
}
|
||||
|
||||
void EventPlayer::GetFds(int* read, int* write, int* except)
|
||||
void EventPlayer::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except)
|
||||
{
|
||||
*read = fd;
|
||||
read->Insert(fd);
|
||||
}
|
||||
|
||||
double EventPlayer::NextTimestamp(double* local_network_time)
|
||||
|
@ -1085,7 +1087,7 @@ double EventPlayer::NextTimestamp(double* local_network_time)
|
|||
{
|
||||
UnserialInfo info(this);
|
||||
Unserialize(&info);
|
||||
closed = io->Eof();
|
||||
SetClosed(io->Eof());
|
||||
}
|
||||
|
||||
if ( ! ne_time )
|
||||
|
@ -1142,7 +1144,7 @@ bool Packet::Serialize(SerialInfo* info) const
|
|||
static BroFile* profiling_output = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
static PktDumper* dump = 0;
|
||||
static iosource::PktDumper* dump = 0;
|
||||
#endif
|
||||
|
||||
Packet* Packet::Unserialize(UnserialInfo* info)
|
||||
|
@ -1188,7 +1190,7 @@ Packet* Packet::Unserialize(UnserialInfo* info)
|
|||
p->hdr = hdr;
|
||||
p->pkt = (u_char*) pkt;
|
||||
p->tag = tag;
|
||||
p->hdr_size = get_link_header_size(p->link_type);
|
||||
p->hdr_size = iosource::PktSrc::GetLinkHeaderSize(p->link_type);
|
||||
|
||||
delete [] tag;
|
||||
|
||||
|
@ -1213,9 +1215,15 @@ Packet* Packet::Unserialize(UnserialInfo* info)
|
|||
if ( debug_logger.IsEnabled(DBG_TM) )
|
||||
{
|
||||
if ( ! dump )
|
||||
dump = new PktDumper("tm.pcap");
|
||||
dump = iosource_mgr->OpenPktDumper("tm.pcap", true);
|
||||
|
||||
dump->Dump(p->hdr, p->pkt);
|
||||
if ( dump )
|
||||
{
|
||||
iosource::PktDumper::Packet dp;
|
||||
dp.hdr = p->hdr;
|
||||
dp.data = p->pkt;
|
||||
dump->Dump(&dp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "SerialInfo.h"
|
||||
#include "IP.h"
|
||||
#include "Timer.h"
|
||||
#include "IOSource.h"
|
||||
#include "iosource/IOSource.h"
|
||||
#include "Reporter.h"
|
||||
|
||||
class SerializationCache;
|
||||
|
@ -350,12 +350,13 @@ public:
|
|||
};
|
||||
|
||||
// Plays a file of events back.
|
||||
class EventPlayer : public FileSerializer, public IOSource {
|
||||
class EventPlayer : public FileSerializer, public iosource::IOSource {
|
||||
public:
|
||||
EventPlayer(const char* file);
|
||||
virtual ~EventPlayer();
|
||||
|
||||
virtual void GetFds(int* read, int* write, int* except);
|
||||
virtual void GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except);
|
||||
virtual double NextTimestamp(double* local_network_time);
|
||||
virtual void Process();
|
||||
virtual const char* Tag() { return "EventPlayer"; }
|
||||
|
|
|
@ -167,7 +167,7 @@ void NetSessions::Done()
|
|||
|
||||
void NetSessions::DispatchPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* pkt, int hdr_size,
|
||||
PktSrc* src_ps)
|
||||
iosource::PktSrc* src_ps)
|
||||
{
|
||||
const struct ip* ip_hdr = 0;
|
||||
const u_char* ip_data = 0;
|
||||
|
@ -184,10 +184,7 @@ void NetSessions::DispatchPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
// Blanket encapsulation
|
||||
hdr_size += encap_hdr_size;
|
||||
|
||||
if ( src_ps->FilterType() == TYPE_FILTER_NORMAL )
|
||||
NextPacket(t, hdr, pkt, hdr_size);
|
||||
else
|
||||
NextPacketSecondary(t, hdr, pkt, hdr_size, src_ps);
|
||||
NextPacket(t, hdr, pkt, hdr_size);
|
||||
}
|
||||
|
||||
void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
|
@ -262,53 +259,6 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
DumpPacket(hdr, pkt);
|
||||
}
|
||||
|
||||
void NetSessions::NextPacketSecondary(double /* t */, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size,
|
||||
const PktSrc* src_ps)
|
||||
{
|
||||
SegmentProfiler(segment_logger, "processing-secondary-packet");
|
||||
|
||||
++num_packets_processed;
|
||||
|
||||
uint32 caplen = hdr->caplen - hdr_size;
|
||||
if ( caplen < sizeof(struct ip) )
|
||||
{
|
||||
Weird("truncated_IP", hdr, pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
const struct ip* ip = (const struct ip*) (pkt + hdr_size);
|
||||
if ( ip->ip_v == 4 )
|
||||
{
|
||||
const secondary_program_list& spt = src_ps->ProgramTable();
|
||||
|
||||
loop_over_list(spt, i)
|
||||
{
|
||||
SecondaryProgram* sp = spt[i];
|
||||
if ( ! net_packet_match(sp->Program(), pkt,
|
||||
hdr->len, hdr->caplen) )
|
||||
continue;
|
||||
|
||||
val_list* args = new val_list;
|
||||
StringVal* cmd_val =
|
||||
new StringVal(sp->Event()->Filter());
|
||||
args->append(cmd_val);
|
||||
IP_Hdr ip_hdr(ip, false);
|
||||
args->append(ip_hdr.BuildPktHdrVal());
|
||||
// ### Need to queue event here.
|
||||
try
|
||||
{
|
||||
sp->Event()->Event()->Call(args);
|
||||
}
|
||||
|
||||
catch ( InterpreterException& e )
|
||||
{ /* Already reported. */ }
|
||||
|
||||
delete args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int NetSessions::CheckConnectionTag(Connection* conn)
|
||||
{
|
||||
if ( current_iosrc->GetCurrentTag() )
|
||||
|
@ -1440,14 +1390,24 @@ void NetSessions::DumpPacket(const struct pcap_pkthdr* hdr,
|
|||
return;
|
||||
|
||||
if ( len == 0 )
|
||||
pkt_dumper->Dump(hdr, pkt);
|
||||
{
|
||||
iosource::PktDumper::Packet p;
|
||||
p.hdr = hdr;
|
||||
p.data = pkt;
|
||||
pkt_dumper->Dump(&p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
struct pcap_pkthdr h = *hdr;
|
||||
h.caplen = len;
|
||||
if ( h.caplen > hdr->caplen )
|
||||
reporter->InternalError("bad modified caplen");
|
||||
pkt_dumper->Dump(&h, pkt);
|
||||
|
||||
iosource::PktDumper::Packet p;
|
||||
p.hdr = &h;
|
||||
p.data = pkt;
|
||||
pkt_dumper->Dump(&p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,11 +69,11 @@ public:
|
|||
~NetSessions();
|
||||
|
||||
// Main entry point for packet processing. Dispatches the packet
|
||||
// either through NextPacket() or NextPacketSecondary(), optionally
|
||||
// employing the packet sorter first.
|
||||
// either through NextPacket(), optionally employing the packet
|
||||
// sorter first.
|
||||
void DispatchPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size,
|
||||
PktSrc* src_ps);
|
||||
iosource::PktSrc* src_ps);
|
||||
|
||||
void Done(); // call to drain events before destructing
|
||||
|
||||
|
@ -221,10 +221,6 @@ protected:
|
|||
void NextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size);
|
||||
|
||||
void NextPacketSecondary(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size,
|
||||
const PktSrc* src_ps);
|
||||
|
||||
// Record the given packet (if a dumper is active). If len=0
|
||||
// then the whole packet is recorded, otherwise just the first
|
||||
// len bytes.
|
||||
|
|
|
@ -1381,6 +1381,11 @@ void OpaqueType::Describe(ODesc* d) const
|
|||
d->Add(name.c_str());
|
||||
}
|
||||
|
||||
void OpaqueType::DescribeReST(ODesc* d, bool roles_only) const
|
||||
{
|
||||
d->Add(fmt(":bro:type:`%s` of %s", type_name(Tag()), name.c_str()));
|
||||
}
|
||||
|
||||
IMPLEMENT_SERIAL(OpaqueType, SER_OPAQUE_TYPE);
|
||||
|
||||
bool OpaqueType::DoSerialize(SerialInfo* info) const
|
||||
|
|
|
@ -534,6 +534,7 @@ public:
|
|||
const string& Name() const { return name; }
|
||||
|
||||
void Describe(ODesc* d) const;
|
||||
void DescribeReST(ODesc* d, bool roles_only = false) const;
|
||||
|
||||
protected:
|
||||
OpaqueType() { }
|
||||
|
|
|
@ -465,10 +465,7 @@ void Val::Describe(ODesc* d) const
|
|||
d->SP();
|
||||
}
|
||||
|
||||
if ( d->IsReadable() )
|
||||
ValDescribe(d);
|
||||
else
|
||||
Val::ValDescribe(d);
|
||||
ValDescribe(d);
|
||||
}
|
||||
|
||||
void Val::DescribeReST(ODesc* d) const
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Analyzer.h"
|
||||
#include "Manager.h"
|
||||
#include "binpac.h"
|
||||
|
||||
#include "analyzer/protocol/pia/PIA.h"
|
||||
#include "../Event.h"
|
||||
|
|
|
@ -188,6 +188,9 @@ flow DHCP_Flow(is_orig: bool) {
|
|||
switch ( type )
|
||||
{
|
||||
case DHCPOFFER:
|
||||
if ( ! router_list )
|
||||
router_list = new TableVal(dhcp_router_list);
|
||||
|
||||
BifEvent::generate_dhcp_offer(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
dhcp_msg_val_->Ref(), new AddrVal(subnet_mask),
|
||||
|
@ -195,6 +198,9 @@ flow DHCP_Flow(is_orig: bool) {
|
|||
break;
|
||||
|
||||
case DHCPACK:
|
||||
if ( ! router_list )
|
||||
router_list = new TableVal(dhcp_router_list);
|
||||
|
||||
BifEvent::generate_dhcp_ack(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
dhcp_msg_val_->Ref(), new AddrVal(subnet_mask),
|
||||
|
@ -202,12 +208,14 @@ flow DHCP_Flow(is_orig: bool) {
|
|||
break;
|
||||
|
||||
case DHCPNAK:
|
||||
Unref(router_list);
|
||||
BifEvent::generate_dhcp_nak(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
dhcp_msg_val_->Ref(), host_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
Unref(router_list);
|
||||
Unref(host_name);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
##
|
||||
event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string%);
|
||||
|
||||
## Generated for DHCP messages of type *DHCPOFFER* (server to client in response to
|
||||
## DHCPDISCOVER with offer of configuration parameters).
|
||||
## Generated for DHCP messages of type *DHCPOFFER* (server to client in response
|
||||
## to DHCPDISCOVER with offer of configuration parameters).
|
||||
##
|
||||
## c: The connection record describing the underlying UDP flow.
|
||||
##
|
||||
|
@ -34,7 +34,7 @@ event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr, host_name: st
|
|||
## serv_addr: The server address specified by the message.
|
||||
##
|
||||
## host_name: Optional host name value. May differ from the host name requested
|
||||
## from the client.
|
||||
## from the client.
|
||||
##
|
||||
## .. bro:see:: dhcp_discover dhcp_request dhcp_decline dhcp_ack dhcp_nak
|
||||
## dhcp_release dhcp_inform
|
||||
|
@ -103,7 +103,7 @@ event dhcp_decline%(c: connection, msg: dhcp_msg, host_name: string%);
|
|||
## serv_addr: The server address specified by the message.
|
||||
##
|
||||
## host_name: Optional host name value. May differ from the host name requested
|
||||
## from the client.
|
||||
## from the client.
|
||||
##
|
||||
## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_nak
|
||||
## dhcp_release dhcp_inform
|
||||
|
|
|
@ -4,6 +4,24 @@ connection DNP3_Conn(bro_analyzer: BroAnalyzer) {
|
|||
downflow = DNP3_Flow(false);
|
||||
};
|
||||
|
||||
%header{
|
||||
uint64 bytestring_to_time(const_bytestring time48, size_t length);
|
||||
%}
|
||||
|
||||
%code{
|
||||
uint64 bytestring_to_time(const_bytestring time48, size_t length)
|
||||
{
|
||||
/* In DNP3, a timestamp is represented by 6 bytes since epoch
|
||||
in milliseconds. The 6 bytes are stored in big endian format. */
|
||||
uint64 epochTime = 0;
|
||||
|
||||
for ( unsigned int i = 0; i < length; i++ )
|
||||
epochTime = time48[length - i - 1] + epochTime * 256;
|
||||
|
||||
return epochTime;
|
||||
}
|
||||
%}
|
||||
|
||||
flow DNP3_Flow(is_orig: bool) {
|
||||
flowunit = DNP3_PDU(is_orig) withcontext (connection, this);
|
||||
|
||||
|
@ -222,7 +240,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_counter_32wFlagTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, count_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, count_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -236,7 +254,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_counter_16wFlagTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, count_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, count_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -390,7 +408,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_32wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -404,7 +422,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_16wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -502,7 +520,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_analog_input_event_32wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, value, bytestring_to_val(time48));
|
||||
is_orig(), flag, value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -516,7 +534,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_analog_input_event_16wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, value, bytestring_to_val(time48));
|
||||
is_orig(), flag, value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -558,7 +576,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_analog_input_event_SPwTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, value, bytestring_to_val(time48));
|
||||
is_orig(), flag, value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -572,7 +590,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_analog_input_event_DPwTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, value_low, value_high, bytestring_to_val(time48));
|
||||
is_orig(), flag, value_low, value_high, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -614,7 +632,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_event_32wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -628,7 +646,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_event_16wTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -670,7 +688,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_event_SPwTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -684,7 +702,7 @@ flow DNP3_Flow(is_orig: bool) {
|
|||
BifEvent::generate_dnp3_frozen_analog_input_event_DPwTime(
|
||||
connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
is_orig(), flag, frozen_value_low, frozen_value_high, bytestring_to_val(time48));
|
||||
is_orig(), flag, frozen_value_low, frozen_value_high, bytestring_to_time(time48, sizeof(time48)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -130,11 +130,11 @@ event dnp3_frozen_counter_16wFlag%(c: connection, is_orig: bool, flag:count, cou
|
|||
|
||||
## Generated for DNP3 objects with the group number 21 and variation number 5
|
||||
## frozen counter 32 bit with flag and time
|
||||
event dnp3_frozen_counter_32wFlagTime%(c: connection, is_orig: bool, flag:count, count_value: count, time48: string%);
|
||||
event dnp3_frozen_counter_32wFlagTime%(c: connection, is_orig: bool, flag:count, count_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 21 and variation number 6
|
||||
## frozen counter 16 bit with flag and time
|
||||
event dnp3_frozen_counter_16wFlagTime%(c: connection, is_orig: bool, flag:count, count_value: count, time48: string%);
|
||||
event dnp3_frozen_counter_16wFlagTime%(c: connection, is_orig: bool, flag:count, count_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 21 and variation number 9
|
||||
## frozen counter 32 bit without flag
|
||||
|
@ -178,11 +178,11 @@ event dnp3_frozen_analog_input_16wFlag%(c: connection, is_orig: bool, flag: coun
|
|||
|
||||
## Generated for DNP3 objects with the group number 31 and variation number 3
|
||||
## frozen analog input 32 bit with time-of-freeze
|
||||
event dnp3_frozen_analog_input_32wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_32wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 31 and variation number 4
|
||||
## frozen analog input 16 bit with time-of-freeze
|
||||
event dnp3_frozen_analog_input_16wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_16wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 31 and variation number 5
|
||||
## frozen analog input 32 bit without flag
|
||||
|
@ -210,11 +210,11 @@ event dnp3_analog_input_event_16woTime%(c: connection, is_orig: bool, flag: coun
|
|||
|
||||
## Generated for DNP3 objects with the group number 32 and variation number 3
|
||||
## analog input event 32 bit with time
|
||||
event dnp3_analog_input_event_32wTime%(c: connection, is_orig: bool, flag: count, value: count, time48: string%);
|
||||
event dnp3_analog_input_event_32wTime%(c: connection, is_orig: bool, flag: count, value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 32 and variation number 4
|
||||
## analog input event 16 bit with time
|
||||
event dnp3_analog_input_event_16wTime%(c: connection, is_orig: bool, flag: count, value: count, time48: string%);
|
||||
event dnp3_analog_input_event_16wTime%(c: connection, is_orig: bool, flag: count, value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 32 and variation number 5
|
||||
## analog input event single-precision float point without time
|
||||
|
@ -226,11 +226,11 @@ event dnp3_analog_input_event_DPwoTime%(c: connection, is_orig: bool, flag: coun
|
|||
|
||||
## Generated for DNP3 objects with the group number 32 and variation number 7
|
||||
## analog input event single-precision float point with time
|
||||
event dnp3_analog_input_event_SPwTime%(c: connection, is_orig: bool, flag: count, value: count, time48: string%);
|
||||
event dnp3_analog_input_event_SPwTime%(c: connection, is_orig: bool, flag: count, value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 32 and variation number 8
|
||||
## analog input event double-precisiion float point with time
|
||||
event dnp3_analog_input_event_DPwTime%(c: connection, is_orig: bool, flag: count, value_low: count, value_high: count, time48: string%);
|
||||
event dnp3_analog_input_event_DPwTime%(c: connection, is_orig: bool, flag: count, value_low: count, value_high: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 33 and variation number 1
|
||||
## frozen analog input event 32 bit without time
|
||||
|
@ -242,11 +242,11 @@ event dnp3_frozen_analog_input_event_16woTime%(c: connection, is_orig: bool, fla
|
|||
|
||||
## Generated for DNP3 objects with the group number 33 and variation number 3
|
||||
## frozen analog input event 32 bit with time
|
||||
event dnp3_frozen_analog_input_event_32wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_event_32wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 33 and variation number 4
|
||||
## frozen analog input event 16 bit with time
|
||||
event dnp3_frozen_analog_input_event_16wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_event_16wTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 33 and variation number 5
|
||||
## frozen analog input event single-precision float point without time
|
||||
|
@ -258,11 +258,11 @@ event dnp3_frozen_analog_input_event_DPwoTime%(c: connection, is_orig: bool, fla
|
|||
|
||||
## Generated for DNP3 objects with the group number 33 and variation number 7
|
||||
## frozen analog input event single-precision float point with time
|
||||
event dnp3_frozen_analog_input_event_SPwTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_event_SPwTime%(c: connection, is_orig: bool, flag: count, frozen_value: count, time48: count%);
|
||||
|
||||
## Generated for DNP3 objects with the group number 34 and variation number 8
|
||||
## frozen analog input event double-precision float point with time
|
||||
event dnp3_frozen_analog_input_event_DPwTime%(c: connection, is_orig: bool, flag: count, frozen_value_low: count, frozen_value_high: count, time48: string%);
|
||||
event dnp3_frozen_analog_input_event_DPwTime%(c: connection, is_orig: bool, flag: count, frozen_value_low: count, frozen_value_high: count, time48: count%);
|
||||
|
||||
## g70
|
||||
event dnp3_file_transport%(c: connection, is_orig: bool, file_handle: count, block_num: count, file_data: string%);
|
||||
|
|
|
@ -692,15 +692,23 @@ int DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg,
|
|||
data += rdlength;
|
||||
len -= rdlength;
|
||||
}
|
||||
else
|
||||
{ // no data, move on
|
||||
data += rdlength;
|
||||
len -= rdlength;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DNS_Interpreter::ExtractOctets(const u_char*& data, int& len,
|
||||
BroString** p)
|
||||
{
|
||||
uint16 dlen = ExtractShort(data, len);
|
||||
dlen = min(len, static_cast<int>(dlen));
|
||||
|
||||
if ( p )
|
||||
*p = new BroString(data, dlen, 0);
|
||||
|
||||
data += dlen;
|
||||
len -= dlen;
|
||||
}
|
||||
|
||||
int DNS_Interpreter::ParseRR_TSIG(DNS_MsgInfo* msg,
|
||||
const u_char*& data, int& len, int rdlength,
|
||||
const u_char* msg_start)
|
||||
|
@ -718,24 +726,17 @@ int DNS_Interpreter::ParseRR_TSIG(DNS_MsgInfo* msg,
|
|||
uint32 sign_time_sec = ExtractLong(data, len);
|
||||
unsigned int sign_time_msec = ExtractShort(data, len);
|
||||
unsigned int fudge = ExtractShort(data, len);
|
||||
|
||||
u_char request_MAC[16];
|
||||
memcpy(request_MAC, data, sizeof(request_MAC));
|
||||
|
||||
// Here we adjust the size of the requested MAC + u_int16_t
|
||||
// for length. See RFC 2845, sec 2.3.
|
||||
int n = sizeof(request_MAC) + sizeof(u_int16_t);
|
||||
data += n;
|
||||
len -= n;
|
||||
|
||||
BroString* request_MAC;
|
||||
ExtractOctets(data, len, &request_MAC);
|
||||
unsigned int orig_id = ExtractShort(data, len);
|
||||
unsigned int rr_error = ExtractShort(data, len);
|
||||
ExtractOctets(data, len, 0); // Other Data
|
||||
|
||||
msg->tsig = new TSIG_DATA;
|
||||
|
||||
msg->tsig->alg_name =
|
||||
new BroString(alg_name, alg_name_end - alg_name, 1);
|
||||
msg->tsig->sig = new BroString(request_MAC, sizeof(request_MAC), 1);
|
||||
msg->tsig->sig = request_MAC;
|
||||
msg->tsig->time_s = sign_time_sec;
|
||||
msg->tsig->time_ms = sign_time_msec;
|
||||
msg->tsig->fudge = fudge;
|
||||
|
|
|
@ -180,6 +180,7 @@ protected:
|
|||
|
||||
uint16 ExtractShort(const u_char*& data, int& len);
|
||||
uint32 ExtractLong(const u_char*& data, int& len);
|
||||
void ExtractOctets(const u_char*& data, int& len, BroString** p);
|
||||
|
||||
int ParseRR_Name(DNS_MsgInfo* msg,
|
||||
const u_char*& data, int& len, int rdlength,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// See the file in the main distribution directory for copyright.
|
||||
|
||||
|
||||
#include "plugin/Plugin.h"
|
||||
|
||||
#include "Finger.h"
|
||||
|
|
|
@ -243,10 +243,13 @@ int HTTP_Entity::Undelivered(int64_t len)
|
|||
return 0;
|
||||
|
||||
if ( is_partial_content )
|
||||
{
|
||||
precomputed_file_id = file_mgr->Gap(body_length, len,
|
||||
http_message->MyHTTP_Analyzer()->GetAnalyzerTag(),
|
||||
http_message->MyHTTP_Analyzer()->Conn(),
|
||||
http_message->IsOrig(), precomputed_file_id);
|
||||
offset += len;
|
||||
}
|
||||
else
|
||||
precomputed_file_id = file_mgr->Gap(body_length, len,
|
||||
http_message->MyHTTP_Analyzer()->GetAnalyzerTag(),
|
||||
|
@ -556,12 +559,9 @@ HTTP_Message::HTTP_Message(HTTP_Analyzer* arg_analyzer,
|
|||
|
||||
current_entity = 0;
|
||||
top_level = new HTTP_Entity(this, 0, expect_body);
|
||||
entity_data_buffer = 0;
|
||||
BeginEntity(top_level);
|
||||
|
||||
buffer_offset = buffer_size = 0;
|
||||
data_buffer = 0;
|
||||
total_buffer_size = 0;
|
||||
|
||||
start_time = network_time;
|
||||
body_length = 0;
|
||||
content_gap_length = 0;
|
||||
|
@ -571,6 +571,7 @@ HTTP_Message::HTTP_Message(HTTP_Analyzer* arg_analyzer,
|
|||
HTTP_Message::~HTTP_Message()
|
||||
{
|
||||
delete top_level;
|
||||
delete [] entity_data_buffer;
|
||||
}
|
||||
|
||||
Val* HTTP_Message::BuildMessageStat(const int interrupted, const char* msg)
|
||||
|
@ -618,22 +619,14 @@ void HTTP_Message::Done(const int interrupted, const char* detail)
|
|||
}
|
||||
|
||||
MyHTTP_Analyzer()->HTTP_MessageDone(is_orig, this);
|
||||
|
||||
delete_strings(buffers);
|
||||
|
||||
if ( data_buffer )
|
||||
{
|
||||
delete data_buffer;
|
||||
data_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int HTTP_Message::Undelivered(int64_t len)
|
||||
{
|
||||
if ( ! top_level )
|
||||
return 0;
|
||||
HTTP_Entity* e = current_entity ? current_entity
|
||||
: static_cast<HTTP_Entity*>(top_level);
|
||||
|
||||
if ( ((HTTP_Entity*) top_level)->Undelivered(len) )
|
||||
if ( e && e->Undelivered(len) )
|
||||
{
|
||||
content_gap_length += len;
|
||||
return 1;
|
||||
|
@ -666,8 +659,6 @@ void HTTP_Message::EndEntity(mime::MIME_Entity* entity)
|
|||
body_length += ((HTTP_Entity*) entity)->BodyLength();
|
||||
header_length += ((HTTP_Entity*) entity)->HeaderLength();
|
||||
|
||||
DeliverEntityData();
|
||||
|
||||
if ( http_end_entity )
|
||||
{
|
||||
val_list* vl = new val_list();
|
||||
|
@ -741,31 +732,18 @@ void HTTP_Message::SubmitTrailingHeaders(mime::MIME_HeaderList& /* hlist */)
|
|||
|
||||
void HTTP_Message::SubmitData(int len, const char* buf)
|
||||
{
|
||||
if ( buf != (const char*) data_buffer->Bytes() + buffer_offset ||
|
||||
buffer_offset + len > buffer_size )
|
||||
{
|
||||
reporter->AnalyzerError(MyHTTP_Analyzer(),
|
||||
"HTTP message buffer misalignment");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_offset += len;
|
||||
if ( buffer_offset >= buffer_size )
|
||||
{
|
||||
buffers.push_back(data_buffer);
|
||||
data_buffer = 0;
|
||||
}
|
||||
if ( http_entity_data )
|
||||
MyHTTP_Analyzer()->HTTP_EntityData(is_orig,
|
||||
new BroString(reinterpret_cast<const u_char*>(buf), len, 0));
|
||||
}
|
||||
|
||||
int HTTP_Message::RequestBuffer(int* plen, char** pbuf)
|
||||
{
|
||||
if ( ! data_buffer )
|
||||
if ( ! InitBuffer(mime_segment_length) )
|
||||
return 0;
|
||||
|
||||
*plen = data_buffer->Len() - buffer_offset;
|
||||
*pbuf = (char*) data_buffer->Bytes() + buffer_offset;
|
||||
if ( ! entity_data_buffer )
|
||||
entity_data_buffer = new char[http_entity_data_delivery_size];
|
||||
|
||||
*plen = http_entity_data_delivery_size;
|
||||
*pbuf = entity_data_buffer;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -806,9 +784,6 @@ void HTTP_Message::SetPlainDelivery(int64_t length)
|
|||
|
||||
if ( length > 0 && BifConst::skip_http_data )
|
||||
content_line->SkipBytesAfterThisLine(length);
|
||||
|
||||
if ( ! data_buffer )
|
||||
InitBuffer(length);
|
||||
}
|
||||
|
||||
void HTTP_Message::SkipEntityData()
|
||||
|
@ -817,87 +792,6 @@ void HTTP_Message::SkipEntityData()
|
|||
current_entity->SkipBody();
|
||||
}
|
||||
|
||||
void HTTP_Message::DeliverEntityData()
|
||||
{
|
||||
if ( http_entity_data )
|
||||
{
|
||||
const BroString* entity_data = 0;
|
||||
|
||||
if ( data_buffer && buffer_offset > 0 )
|
||||
{
|
||||
if ( buffer_offset < buffer_size )
|
||||
{
|
||||
entity_data = new BroString(data_buffer->Bytes(), buffer_offset, 0);
|
||||
delete data_buffer;
|
||||
}
|
||||
else
|
||||
entity_data = data_buffer;
|
||||
|
||||
data_buffer = 0;
|
||||
|
||||
if ( buffers.empty() )
|
||||
MyHTTP_Analyzer()->HTTP_EntityData(is_orig,
|
||||
entity_data);
|
||||
else
|
||||
buffers.push_back(entity_data);
|
||||
|
||||
entity_data = 0;
|
||||
}
|
||||
|
||||
if ( ! buffers.empty() )
|
||||
{
|
||||
if ( buffers.size() == 1 )
|
||||
{
|
||||
entity_data = buffers[0];
|
||||
buffers.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
entity_data = concatenate(buffers);
|
||||
delete_strings(buffers);
|
||||
}
|
||||
|
||||
MyHTTP_Analyzer()->HTTP_EntityData(is_orig, entity_data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_strings(buffers);
|
||||
|
||||
if ( data_buffer )
|
||||
delete data_buffer;
|
||||
|
||||
data_buffer = 0;
|
||||
}
|
||||
|
||||
total_buffer_size = 0;
|
||||
}
|
||||
|
||||
int HTTP_Message::InitBuffer(int64_t length)
|
||||
{
|
||||
if ( length <= 0 )
|
||||
return 0;
|
||||
|
||||
if ( total_buffer_size >= http_entity_data_delivery_size )
|
||||
DeliverEntityData();
|
||||
|
||||
if ( total_buffer_size + length > http_entity_data_delivery_size )
|
||||
{
|
||||
length = http_entity_data_delivery_size - total_buffer_size;
|
||||
if ( length <= 0 )
|
||||
return 0;
|
||||
}
|
||||
|
||||
u_char* b = new u_char[length];
|
||||
data_buffer = new BroString(0, b, length);
|
||||
|
||||
buffer_size = length;
|
||||
total_buffer_size += length;
|
||||
buffer_offset = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void HTTP_Message::Weird(const char* msg)
|
||||
{
|
||||
analyzer->Weird(msg);
|
||||
|
@ -1844,7 +1738,7 @@ void HTTP_Analyzer::ParseVersion(data_chunk_t ver, const IPAddr& host,
|
|||
}
|
||||
}
|
||||
|
||||
void HTTP_Analyzer::HTTP_EntityData(int is_orig, const BroString* entity_data)
|
||||
void HTTP_Analyzer::HTTP_EntityData(int is_orig, BroString* entity_data)
|
||||
{
|
||||
if ( http_entity_data )
|
||||
{
|
||||
|
@ -1852,8 +1746,7 @@ void HTTP_Analyzer::HTTP_EntityData(int is_orig, const BroString* entity_data)
|
|||
vl->append(BuildConnVal());
|
||||
vl->append(new Val(is_orig, TYPE_BOOL));
|
||||
vl->append(new Val(entity_data->Len(), TYPE_COUNT));
|
||||
// FIXME: Make sure that removing the const here is indeed ok...
|
||||
vl->append(new StringVal(const_cast<BroString*>(entity_data)));
|
||||
vl->append(new StringVal(entity_data));
|
||||
ConnectionEvent(http_entity_data, vl);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -134,13 +134,7 @@ protected:
|
|||
tcp::ContentLine_Analyzer* content_line;
|
||||
bool is_orig;
|
||||
|
||||
vector<const BroString*> buffers;
|
||||
|
||||
// Controls the total buffer size within http_entity_data_delivery_size.
|
||||
int total_buffer_size;
|
||||
|
||||
int buffer_offset, buffer_size;
|
||||
BroString* data_buffer;
|
||||
char* entity_data_buffer;
|
||||
|
||||
double start_time;
|
||||
|
||||
|
@ -153,9 +147,6 @@ protected:
|
|||
|
||||
HTTP_Entity* current_entity;
|
||||
|
||||
int InitBuffer(int64_t length);
|
||||
void DeliverEntityData();
|
||||
|
||||
Val* BuildMessageStat(const int interrupted, const char* msg);
|
||||
};
|
||||
|
||||
|
@ -167,7 +158,7 @@ public:
|
|||
void Undelivered(tcp::TCP_Endpoint* sender, uint64 seq, int len);
|
||||
|
||||
void HTTP_Header(int is_orig, mime::MIME_Header* h);
|
||||
void HTTP_EntityData(int is_orig, const BroString* entity_data);
|
||||
void HTTP_EntityData(int is_orig, BroString* entity_data);
|
||||
void HTTP_MessageDone(int is_orig, HTTP_Message* message);
|
||||
void HTTP_Event(const char* category, const char* detail);
|
||||
void HTTP_Event(const char* category, StringVal *detail);
|
||||
|
|
|
@ -142,8 +142,9 @@ int fputs(data_chunk_t b, FILE* fp)
|
|||
void MIME_Mail::Undelivered(int len)
|
||||
{
|
||||
// is_orig param not available, doesn't matter as long as it's consistent
|
||||
file_mgr->Gap(cur_entity_len, len, analyzer->GetAnalyzerTag(), analyzer->Conn(),
|
||||
false);
|
||||
cur_entity_id = file_mgr->Gap(cur_entity_len, len,
|
||||
analyzer->GetAnalyzerTag(), analyzer->Conn(),
|
||||
false, cur_entity_id);
|
||||
}
|
||||
|
||||
int strcasecmp_n(data_chunk_t s, const char* t)
|
||||
|
@ -644,11 +645,7 @@ void MIME_Entity::EndOfData()
|
|||
if ( content_encoding == CONTENT_ENCODING_BASE64 )
|
||||
FinishDecodeBase64();
|
||||
|
||||
if ( data_buf_offset > 0 )
|
||||
{
|
||||
SubmitData(data_buf_offset, data_buf_data);
|
||||
data_buf_offset = -1;
|
||||
}
|
||||
FlushData();
|
||||
}
|
||||
|
||||
message->EndEntity (this);
|
||||
|
@ -1002,6 +999,7 @@ void MIME_Entity::DecodeDataLine(int len, const char* data, int trailing_CRLF)
|
|||
DecodeBinary(len, data, trailing_CRLF);
|
||||
break;
|
||||
}
|
||||
FlushData();
|
||||
}
|
||||
|
||||
void MIME_Entity::DecodeBinary(int len, const char* data, int trailing_CRLF)
|
||||
|
@ -1201,6 +1199,15 @@ void MIME_Entity::DataOctets(int len, const char* data)
|
|||
}
|
||||
}
|
||||
|
||||
void MIME_Entity::FlushData()
|
||||
{
|
||||
if ( data_buf_offset > 0 )
|
||||
{
|
||||
SubmitData(data_buf_offset, data_buf_data);
|
||||
data_buf_offset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void MIME_Entity::SubmitHeader(MIME_Header* h)
|
||||
{
|
||||
message->SubmitHeader(h);
|
||||
|
@ -1347,6 +1354,7 @@ MIME_Mail::~MIME_Mail()
|
|||
void MIME_Mail::BeginEntity(MIME_Entity* /* entity */)
|
||||
{
|
||||
cur_entity_len = 0;
|
||||
cur_entity_id.clear();
|
||||
|
||||
if ( mime_begin_entity )
|
||||
{
|
||||
|
@ -1386,6 +1394,7 @@ void MIME_Mail::EndEntity(MIME_Entity* /* entity */)
|
|||
}
|
||||
|
||||
file_mgr->EndOfFile(analyzer->GetAnalyzerTag(), analyzer->Conn());
|
||||
cur_entity_id.clear();
|
||||
}
|
||||
|
||||
void MIME_Mail::SubmitHeader(MIME_Header* h)
|
||||
|
@ -1448,8 +1457,9 @@ void MIME_Mail::SubmitData(int len, const char* buf)
|
|||
}
|
||||
|
||||
// is_orig param not available, doesn't matter as long as it's consistent
|
||||
file_mgr->DataIn(reinterpret_cast<const u_char*>(buf), len,
|
||||
analyzer->GetAnalyzerTag(), analyzer->Conn(), false);
|
||||
cur_entity_id = file_mgr->DataIn(reinterpret_cast<const u_char*>(buf), len,
|
||||
analyzer->GetAnalyzerTag(), analyzer->Conn(), false,
|
||||
cur_entity_id);
|
||||
|
||||
cur_entity_len += len;
|
||||
buffer_start = (buf + len) - (char*)data_buffer->Bytes();
|
||||
|
|
|
@ -133,6 +133,7 @@ protected:
|
|||
int GetDataBuffer();
|
||||
void DataOctet(char ch);
|
||||
void DataOctets(int len, const char* data);
|
||||
void FlushData();
|
||||
virtual void SubmitData(int len, const char* buf);
|
||||
|
||||
virtual void SubmitHeader(MIME_Header* h);
|
||||
|
@ -258,6 +259,7 @@ protected:
|
|||
BroString* data_buffer;
|
||||
|
||||
uint64 cur_entity_len;
|
||||
string cur_entity_id;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -117,6 +117,45 @@ void TCP_Reassembler::SetContentsFile(BroFile* f)
|
|||
record_contents_file = f;
|
||||
}
|
||||
|
||||
static inline bool established(const TCP_Endpoint* a, const TCP_Endpoint* b)
|
||||
{
|
||||
return a->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
b->state == TCP_ENDPOINT_ESTABLISHED;
|
||||
}
|
||||
|
||||
static inline bool report_gap(const TCP_Endpoint* a, const TCP_Endpoint* b)
|
||||
{
|
||||
return content_gap &&
|
||||
( BifConst::report_gaps_for_partial || established(a, b) );
|
||||
}
|
||||
|
||||
void TCP_Reassembler::Gap(uint64 seq, uint64 len)
|
||||
{
|
||||
// Only report on content gaps for connections that
|
||||
// are in a cleanly established state. In other
|
||||
// states, these can arise falsely due to things
|
||||
// like sequence number mismatches in RSTs, or
|
||||
// unseen previous packets in partial connections.
|
||||
// The one opportunity we lose here is on clean FIN
|
||||
// handshakes, but Oh Well.
|
||||
|
||||
if ( report_gap(endp, endp->peer) )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(dst_analyzer->BuildConnVal());
|
||||
vl->append(new Val(IsOrig(), TYPE_BOOL));
|
||||
vl->append(new Val(seq, TYPE_COUNT));
|
||||
vl->append(new Val(len, TYPE_COUNT));
|
||||
dst_analyzer->ConnectionEvent(content_gap, vl);
|
||||
}
|
||||
|
||||
if ( type == Direct )
|
||||
dst_analyzer->NextUndelivered(seq, len, IsOrig());
|
||||
else
|
||||
dst_analyzer->ForwardUndelivered(seq, len, IsOrig());
|
||||
|
||||
had_gap = true;
|
||||
}
|
||||
|
||||
void TCP_Reassembler::Undelivered(uint64 up_to_seq)
|
||||
{
|
||||
|
@ -189,48 +228,35 @@ void TCP_Reassembler::Undelivered(uint64 up_to_seq)
|
|||
|
||||
if ( ! skip_deliveries )
|
||||
{
|
||||
// This can happen because we're processing a trace
|
||||
// that's been filtered. For example, if it's just
|
||||
// SYN/FIN data, then there can be data in the FIN
|
||||
// packet, but it's undelievered because it's out of
|
||||
// sequence.
|
||||
|
||||
uint64 seq = last_reassem_seq;
|
||||
uint64 len = up_to_seq - last_reassem_seq;
|
||||
|
||||
// Only report on content gaps for connections that
|
||||
// are in a cleanly established state. In other
|
||||
// states, these can arise falsely due to things
|
||||
// like sequence number mismatches in RSTs, or
|
||||
// unseen previous packets in partial connections.
|
||||
// The one opportunity we lose here is on clean FIN
|
||||
// handshakes, but Oh Well.
|
||||
|
||||
if ( content_gap &&
|
||||
(BifConst::report_gaps_for_partial ||
|
||||
(endpoint->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
peer->state == TCP_ENDPOINT_ESTABLISHED ) ) )
|
||||
// If we have blocks that begin below up_to_seq, deliver them.
|
||||
DataBlock* b = blocks;
|
||||
while ( b )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(dst_analyzer->BuildConnVal());
|
||||
vl->append(new Val(IsOrig(), TYPE_BOOL));
|
||||
vl->append(new Val(seq, TYPE_COUNT));
|
||||
vl->append(new Val(len, TYPE_COUNT));
|
||||
if ( b->seq < last_reassem_seq )
|
||||
{
|
||||
// Already delivered this block.
|
||||
b = b->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_analyzer->ConnectionEvent(content_gap, vl);
|
||||
if ( b->seq >= up_to_seq )
|
||||
// Block is beyond what we need to process at this point.
|
||||
break;
|
||||
|
||||
uint64 gap_at_seq = last_reassem_seq;
|
||||
uint64 gap_len = b->seq - last_reassem_seq;
|
||||
|
||||
Gap(gap_at_seq, gap_len);
|
||||
last_reassem_seq += gap_len;
|
||||
BlockInserted(b);
|
||||
// Inserting a block may cause trimming of what's buffered,
|
||||
// so have to assume 'b' is invalid, hence re-assign to start.
|
||||
b = blocks;
|
||||
}
|
||||
|
||||
if ( type == Direct )
|
||||
dst_analyzer->NextUndelivered(last_reassem_seq,
|
||||
len, IsOrig());
|
||||
else
|
||||
{
|
||||
dst_analyzer->ForwardUndelivered(last_reassem_seq,
|
||||
len, IsOrig());
|
||||
}
|
||||
if ( up_to_seq > last_reassem_seq )
|
||||
Gap(last_reassem_seq, up_to_seq - last_reassem_seq);
|
||||
}
|
||||
|
||||
had_gap = true;
|
||||
}
|
||||
|
||||
// We should record and match undelivered even if we are skipping
|
||||
|
@ -243,7 +269,8 @@ void TCP_Reassembler::Undelivered(uint64 up_to_seq)
|
|||
MatchUndelivered(up_to_seq, false);
|
||||
|
||||
// But we need to re-adjust last_reassem_seq in either case.
|
||||
last_reassem_seq = up_to_seq; // we've done our best ...
|
||||
if ( up_to_seq > last_reassem_seq )
|
||||
last_reassem_seq = up_to_seq; // we've done our best ...
|
||||
}
|
||||
|
||||
void TCP_Reassembler::MatchUndelivered(uint64 up_to_seq, bool use_last_upper)
|
||||
|
|
|
@ -94,6 +94,7 @@ private:
|
|||
DECLARE_SERIAL(TCP_Reassembler);
|
||||
|
||||
void Undelivered(uint64 up_to_seq);
|
||||
void Gap(uint64 seq, uint64 len);
|
||||
|
||||
void RecordToSeq(uint64 start_seq, uint64 stop_seq, BroFile* f);
|
||||
void RecordBlock(DataBlock* b, BroFile* f);
|
||||
|
|
139
src/bro.bif
139
src/bro.bif
|
@ -21,6 +21,7 @@
|
|||
#include "IPAddr.h"
|
||||
#include "util.h"
|
||||
#include "file_analysis/Manager.h"
|
||||
#include "iosource/Manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -33,7 +34,7 @@ TableType* var_sizes;
|
|||
// and hence it's declared in NetVar.{h,cc}.
|
||||
extern RecordType* gap_info;
|
||||
|
||||
static PktDumper* addl_pkt_dumper = 0;
|
||||
static iosource::PktDumper* addl_pkt_dumper = 0;
|
||||
|
||||
bro_int_t parse_int(const char*& fmt)
|
||||
{
|
||||
|
@ -1675,11 +1676,14 @@ function net_stats%(%): NetStats
|
|||
unsigned int drop = 0;
|
||||
unsigned int link = 0;
|
||||
|
||||
loop_over_list(pkt_srcs, i)
|
||||
{
|
||||
PktSrc* ps = pkt_srcs[i];
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
struct PktSrc::Stats stat;
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
{
|
||||
iosource::PktSrc* ps = *i;
|
||||
|
||||
struct iosource::PktSrc::Stats stat;
|
||||
ps->Statistics(&stat);
|
||||
recv += stat.received;
|
||||
drop += stat.dropped;
|
||||
|
@ -3224,10 +3228,15 @@ function dump_current_packet%(file_name: string%) : bool
|
|||
return new Val(0, TYPE_BOOL);
|
||||
|
||||
if ( ! addl_pkt_dumper )
|
||||
addl_pkt_dumper = new PktDumper(0, true);
|
||||
addl_pkt_dumper = iosource_mgr->OpenPktDumper(file_name->CheckString(), true);
|
||||
|
||||
addl_pkt_dumper->Open(file_name->CheckString());
|
||||
addl_pkt_dumper->Dump(hdr, pkt);
|
||||
if ( addl_pkt_dumper )
|
||||
{
|
||||
iosource::PktDumper::Packet p;
|
||||
p.hdr = hdr;
|
||||
p.data = pkt;
|
||||
addl_pkt_dumper->Dump(&p);
|
||||
}
|
||||
|
||||
return new Val(! addl_pkt_dumper->IsError(), TYPE_BOOL);
|
||||
%}
|
||||
|
@ -3284,10 +3293,15 @@ function dump_packet%(pkt: pcap_packet, file_name: string%) : bool
|
|||
hdr.len = (*pkt_vl)[3]->AsCount();
|
||||
|
||||
if ( ! addl_pkt_dumper )
|
||||
addl_pkt_dumper = new PktDumper(0, true);
|
||||
addl_pkt_dumper = iosource_mgr->OpenPktDumper(file_name->CheckString(), true);
|
||||
|
||||
addl_pkt_dumper->Open(file_name->CheckString());
|
||||
addl_pkt_dumper->Dump(&hdr, (*pkt_vl)[4]->AsString()->Bytes());
|
||||
if ( addl_pkt_dumper )
|
||||
{
|
||||
iosource::PktDumper::Packet p;
|
||||
p.hdr = &hdr;
|
||||
p.data = (*pkt_vl)[4]->AsString()->Bytes();
|
||||
addl_pkt_dumper->Dump(&p);
|
||||
}
|
||||
|
||||
return new Val(addl_pkt_dumper->IsError(), TYPE_BOOL);
|
||||
%}
|
||||
|
@ -4110,14 +4124,14 @@ function rotate_file_by_name%(f: string%): rotate_info
|
|||
bool is_addl_pkt_dumper = false;
|
||||
|
||||
// Special case: one of current dump files.
|
||||
if ( pkt_dumper && streq(pkt_dumper->FileName(), f->CheckString()) )
|
||||
if ( pkt_dumper && streq(pkt_dumper->Path().c_str(), f->CheckString()) )
|
||||
{
|
||||
is_pkt_dumper = true;
|
||||
pkt_dumper->Close();
|
||||
}
|
||||
|
||||
if ( addl_pkt_dumper &&
|
||||
streq(addl_pkt_dumper->FileName(), f->CheckString()) )
|
||||
streq(addl_pkt_dumper->Path().c_str(), f->CheckString()) )
|
||||
{
|
||||
is_addl_pkt_dumper = true;
|
||||
addl_pkt_dumper->Close();
|
||||
|
@ -4214,103 +4228,6 @@ function enable_raw_output%(f: file%): any
|
|||
#
|
||||
# ===========================================================================
|
||||
|
||||
## Precompiles a PCAP filter and binds it to a given identifier.
|
||||
##
|
||||
## id: The PCAP identifier to reference the filter *s* later on.
|
||||
##
|
||||
## s: The PCAP filter. See ``man tcpdump`` for valid expressions.
|
||||
##
|
||||
## Returns: True if *s* is valid and precompiles successfully.
|
||||
##
|
||||
## .. bro:see:: install_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
## pcap_error
|
||||
function precompile_pcap_filter%(id: PcapFilterID, s: string%): bool
|
||||
%{
|
||||
bool success = true;
|
||||
|
||||
loop_over_list(pkt_srcs, i)
|
||||
{
|
||||
pkt_srcs[i]->ClearErrorMsg();
|
||||
|
||||
if ( ! pkt_srcs[i]->PrecompileFilter(id->ForceAsInt(),
|
||||
s->CheckString()) )
|
||||
{
|
||||
reporter->Error("precompile_pcap_filter: %s",
|
||||
pkt_srcs[i]->ErrorMsg());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return new Val(success, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Installs a PCAP filter that has been precompiled with
|
||||
## :bro:id:`precompile_pcap_filter`.
|
||||
##
|
||||
## id: The PCAP filter id of a precompiled filter.
|
||||
##
|
||||
## Returns: True if the filter associated with *id* has been installed
|
||||
## successfully.
|
||||
##
|
||||
## .. bro:see:: precompile_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
## pcap_error
|
||||
function install_pcap_filter%(id: PcapFilterID%): bool
|
||||
%{
|
||||
bool success = true;
|
||||
|
||||
loop_over_list(pkt_srcs, i)
|
||||
{
|
||||
pkt_srcs[i]->ClearErrorMsg();
|
||||
|
||||
if ( ! pkt_srcs[i]->SetFilter(id->ForceAsInt()) )
|
||||
success = false;
|
||||
}
|
||||
|
||||
return new Val(success, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Returns a string representation of the last PCAP error.
|
||||
##
|
||||
## Returns: A descriptive error message of the PCAP function that failed.
|
||||
##
|
||||
## .. bro:see:: precompile_pcap_filter
|
||||
## install_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
function pcap_error%(%): string
|
||||
%{
|
||||
loop_over_list(pkt_srcs, i)
|
||||
{
|
||||
const char* err = pkt_srcs[i]->ErrorMsg();
|
||||
if ( *err )
|
||||
return new StringVal(err);
|
||||
}
|
||||
|
||||
return new StringVal("no error");
|
||||
%}
|
||||
|
||||
## Installs a filter to drop packets from a given IP source address with
|
||||
## a certain probability if none of a given set of TCP flags are set.
|
||||
## Note that for IPv6 packets with a Destination options header that has
|
||||
|
@ -4542,7 +4459,7 @@ function enable_communication%(%): any
|
|||
return 0;
|
||||
|
||||
using_communication = 1;
|
||||
remote_serializer->Init();
|
||||
remote_serializer->Enable();
|
||||
return 0;
|
||||
%}
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ void File::EndOfFile()
|
|||
void File::Gap(uint64 offset, uint64 len)
|
||||
{
|
||||
DBG_LOG(DBG_FILE_ANALYSIS, "[%s] Gap of size %" PRIu64 " at offset %" PRIu64,
|
||||
id.c_str(), offset, len);
|
||||
id.c_str(), len, offset);
|
||||
|
||||
analyzers.DrainModifications();
|
||||
|
||||
|
|
|
@ -104,6 +104,39 @@ STACK_OF(X509)* x509_get_untrusted_stack(VectorVal* certs_vec)
|
|||
return untrusted_certs;
|
||||
}
|
||||
|
||||
// We need this function to be able to identify the signer certificate of an
|
||||
// OCSP request out of a list of possible certificates.
|
||||
X509* x509_get_ocsp_signer(STACK_OF(X509) *certs, OCSP_RESPID *rid)
|
||||
{
|
||||
// We support two lookup types - either by response id or by key.
|
||||
if ( rid->type == V_OCSP_RESPID_NAME )
|
||||
return X509_find_by_subject(certs, rid->value.byName);
|
||||
|
||||
// There only should be name and type - but let's be sure...
|
||||
if ( rid->type != V_OCSP_RESPID_KEY )
|
||||
return 0;
|
||||
|
||||
// Just like OpenSSL, we just support SHA-1 lookups and bail out otherwhise.
|
||||
if ( rid->value.byKey->length != SHA_DIGEST_LENGTH )
|
||||
return 0;
|
||||
|
||||
unsigned char* key_hash = rid->value.byKey->data;
|
||||
for ( int i = 0; i < sk_X509_num(certs); ++i )
|
||||
{
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
X509* cert = sk_X509_value(certs, i);
|
||||
if ( ! X509_pubkey_digest(cert, EVP_sha1(), digest, NULL) )
|
||||
// digest failed for this certificate, try with next
|
||||
continue;
|
||||
|
||||
if ( memcmp(digest, key_hash, SHA_DIGEST_LENGTH) == 0 )
|
||||
// keys match, return certificate
|
||||
return cert;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
%%}
|
||||
|
||||
## Parses a certificate into an X509::Certificate structure.
|
||||
|
@ -221,6 +254,7 @@ function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_c
|
|||
int out = -1;
|
||||
int result = -1;
|
||||
X509* issuer_certificate = 0;
|
||||
X509* signer = 0;
|
||||
OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(NULL, &start, ocsp_reply->Len());
|
||||
if ( ! resp )
|
||||
{
|
||||
|
@ -250,19 +284,47 @@ function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_c
|
|||
// inject the certificates in the certificate list of the OCSP reply, they actually are used during
|
||||
// the lookup.
|
||||
// Yay.
|
||||
|
||||
if ( ! basic->certs )
|
||||
{
|
||||
basic->certs = sk_X509_new_null();
|
||||
if ( ! basic->certs )
|
||||
{
|
||||
rval = x509_result_record(-1, "Could not allocate basic x509 stack");
|
||||
goto x509_ocsp_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
issuer_certificate = 0;
|
||||
for ( int i = 0; i < sk_X509_num(untrusted_certs); i++)
|
||||
{
|
||||
sk_X509_push(basic->certs, X509_dup(sk_X509_value(untrusted_certs, i)));
|
||||
|
||||
if ( X509_NAME_cmp(X509_get_issuer_name(cert), X509_get_subject_name(sk_X509_value(untrusted_certs, i))) )
|
||||
if ( X509_NAME_cmp(X509_get_issuer_name(cert), X509_get_subject_name(sk_X509_value(untrusted_certs, i))) == 0 )
|
||||
issuer_certificate = sk_X509_value(untrusted_certs, i);
|
||||
}
|
||||
|
||||
// Because we actually want to be able to give nice error messages that show why we were
|
||||
// not able to verify the OCSP response - do our own verification logic first.
|
||||
signer = x509_get_ocsp_signer(basic->certs, basic->tbsResponseData->responderId);
|
||||
|
||||
/*
|
||||
Do this perhaps - OpenSSL also cannot do it, so I do not really feel bad about it.
|
||||
Needs a different lookup because the root store is no stack of X509 certs
|
||||
|
||||
if ( !s igner )
|
||||
// if we did not find it in the certificates that were sent, search in the root store
|
||||
signer = x509_get_ocsp_signer(basic->certs, basic->tbsResponseData->responderId);
|
||||
*/
|
||||
|
||||
if ( ! signer )
|
||||
{
|
||||
rval = x509_result_record(-1, "Could not find OCSP responder certificate");
|
||||
goto x509_ocsp_cleanup;
|
||||
}
|
||||
|
||||
csc = X509_STORE_CTX_new();
|
||||
X509_STORE_CTX_init(csc, ctx, sk_X509_value(basic->certs, 0), basic->certs);
|
||||
X509_STORE_CTX_init(csc, ctx, signer, basic->certs);
|
||||
X509_STORE_CTX_set_time(csc, 0, (time_t) verify_time);
|
||||
X509_STORE_CTX_set_purpose(csc, X509_PURPOSE_OCSP_HELPER);
|
||||
|
||||
|
@ -281,7 +343,6 @@ function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_c
|
|||
goto x509_ocsp_cleanup;
|
||||
}
|
||||
|
||||
|
||||
// ok, now we verified the OCSP response. This means that we have a valid chain tying it
|
||||
// to a root that we trust and that the signature also hopefully is valid. This does not yet
|
||||
// mean that the ocsp response actually matches the certificate the server send us or that
|
||||
|
@ -322,7 +383,7 @@ function x509_ocsp_verify%(certs: x509_opaque_vector, ocsp_reply: string, root_c
|
|||
goto x509_ocsp_cleanup;
|
||||
}
|
||||
|
||||
if ( ! OCSP_id_cmp(certid, single->certId) )
|
||||
if ( OCSP_id_cmp(certid, single->certId) != 0 )
|
||||
return x509_result_record(-1, "OCSP reply is not for host certificate");
|
||||
|
||||
// next - check freshness of proof...
|
||||
|
|
|
@ -58,7 +58,14 @@ int pcap_compile_nopcap(int snaplen_arg, int linktype_arg,
|
|||
}
|
||||
#endif
|
||||
|
||||
BPF_Program::BPF_Program() : m_compiled(), m_program()
|
||||
// Simple heuristic to identify filters that always match, so that we can
|
||||
// skip the filtering in that case. "ip or not ip" is Bro's default filter.
|
||||
static bool filter_matches_anything(const char *filter)
|
||||
{
|
||||
return (! filter) || strlen(filter) == 0 || strcmp(filter, "ip or not ip") == 0;
|
||||
}
|
||||
|
||||
BPF_Program::BPF_Program() : m_compiled(), m_matches_anything(false), m_program()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -86,12 +93,14 @@ bool BPF_Program::Compile(pcap_t* pcap, const char* filter, uint32 netmask,
|
|||
}
|
||||
|
||||
m_compiled = true;
|
||||
m_matches_anything = filter_matches_anything(filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPF_Program::Compile(int snaplen, int linktype, const char* filter,
|
||||
uint32 netmask, char* errbuf, bool optimize)
|
||||
uint32 netmask, char* errbuf, unsigned int errbuf_len,
|
||||
bool optimize)
|
||||
{
|
||||
FreeCode();
|
||||
|
||||
|
@ -99,15 +108,23 @@ bool BPF_Program::Compile(int snaplen, int linktype, const char* filter,
|
|||
char my_error[PCAP_ERRBUF_SIZE];
|
||||
|
||||
int err = pcap_compile_nopcap(snaplen, linktype, &m_program,
|
||||
(char *) filter, optimize, netmask, error);
|
||||
(char *) filter, optimize, netmask, my_error);
|
||||
if ( err < 0 && errbuf )
|
||||
safe_strncpy(errbuf, my_errbuf, PCAP_ERRBUF_SIZE);
|
||||
safe_strncpy(errbuf, my_error, errbuf_len);
|
||||
*errbuf = '\0';
|
||||
#else
|
||||
int err = pcap_compile_nopcap(snaplen, linktype, &m_program,
|
||||
(char*) filter, optimize, netmask);
|
||||
|
||||
if ( err < 0 && errbuf && errbuf_len )
|
||||
*errbuf = '\0';
|
||||
#endif
|
||||
|
||||
if ( err == 0 )
|
||||
{
|
||||
m_compiled = true;
|
||||
m_matches_anything = filter_matches_anything(filter);
|
||||
}
|
||||
|
||||
return err == 0;
|
||||
}
|
|
@ -30,12 +30,17 @@ public:
|
|||
// similarly to pcap_compile_nopcap(). Parameters are
|
||||
// similar. Returns true on success.
|
||||
bool Compile(int snaplen, int linktype, const char* filter,
|
||||
uint32 netmask, char* errbuf = 0, bool optimize = true);
|
||||
uint32 netmask, char* errbuf = 0, unsigned int errbuf_len = 0,
|
||||
bool optimize = true);
|
||||
|
||||
// Returns true if this program currently contains compiled
|
||||
// code, false otherwise.
|
||||
bool IsCompiled() { return m_compiled; }
|
||||
|
||||
// Returns true if this program matches any packets. This is not
|
||||
// comprehensive, but can identify a few cases where it does.
|
||||
bool MatchesAnything() { return m_matches_anything; }
|
||||
|
||||
// Accessor to the compiled program. Returns nil when
|
||||
// no program is currently compiled.
|
||||
bpf_program* GetProgram();
|
||||
|
@ -46,6 +51,7 @@ protected:
|
|||
// (I like to prefix member variables with m_, makes it clear
|
||||
// in the implementation whether it's a global or not. --ck)
|
||||
bool m_compiled;
|
||||
bool m_matches_anything;
|
||||
struct bpf_program m_program;
|
||||
};
|
||||
|
23
src/iosource/CMakeLists.txt
Normal file
23
src/iosource/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
include(BroSubdir)
|
||||
|
||||
include_directories(BEFORE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_subdirectory(pcap)
|
||||
|
||||
set(iosource_SRCS
|
||||
BPF_Program.cc
|
||||
Component.cc
|
||||
Manager.cc
|
||||
PktDumper.cc
|
||||
PktSrc.cc
|
||||
)
|
||||
|
||||
bif_target(pcap.bif)
|
||||
|
||||
bro_add_subdir_library(iosource ${iosource_SRCS})
|
||||
add_dependencies(bro_iosource generate_outputs)
|
||||
|
165
src/iosource/Component.cc
Normal file
165
src/iosource/Component.cc
Normal file
|
@ -0,0 +1,165 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "Component.h"
|
||||
|
||||
#include "Desc.h"
|
||||
#include "Reporter.h"
|
||||
|
||||
using namespace iosource;
|
||||
|
||||
Component::Component(const std::string& name)
|
||||
: plugin::Component(plugin::component::IOSOURCE, name)
|
||||
{
|
||||
}
|
||||
|
||||
Component::Component(plugin::component::Type type, const std::string& name)
|
||||
: plugin::Component(type, name)
|
||||
{
|
||||
}
|
||||
|
||||
Component::~Component()
|
||||
{
|
||||
}
|
||||
|
||||
PktSrcComponent::PktSrcComponent(const std::string& arg_name, const std::string& arg_prefix, InputType arg_type, factory_callback arg_factory)
|
||||
: iosource::Component(plugin::component::PKTSRC, arg_name)
|
||||
{
|
||||
tokenize_string(arg_prefix, ":", &prefixes);
|
||||
type = arg_type;
|
||||
factory = arg_factory;
|
||||
}
|
||||
|
||||
PktSrcComponent::~PktSrcComponent()
|
||||
{
|
||||
}
|
||||
|
||||
const std::vector<std::string>& PktSrcComponent::Prefixes() const
|
||||
{
|
||||
return prefixes;
|
||||
}
|
||||
|
||||
bool PktSrcComponent::HandlesPrefix(const string& prefix) const
|
||||
{
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin();
|
||||
i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PktSrcComponent::DoesLive() const
|
||||
{
|
||||
return type == LIVE || type == BOTH;
|
||||
}
|
||||
|
||||
bool PktSrcComponent::DoesTrace() const
|
||||
{
|
||||
return type == TRACE || type == BOTH;
|
||||
}
|
||||
|
||||
PktSrcComponent::factory_callback PktSrcComponent::Factory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
void PktSrcComponent::DoDescribe(ODesc* d) const
|
||||
{
|
||||
iosource::Component::DoDescribe(d);
|
||||
|
||||
string prefs;
|
||||
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin();
|
||||
i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
|
||||
d->Add("interface prefix");
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
|
||||
d->Add(" ");
|
||||
d->Add(prefs);
|
||||
d->Add("; supports ");
|
||||
|
||||
switch ( type ) {
|
||||
case LIVE:
|
||||
d->Add("live input");
|
||||
break;
|
||||
|
||||
case TRACE:
|
||||
d->Add("trace input");
|
||||
break;
|
||||
|
||||
case BOTH:
|
||||
d->Add("live and trace input");
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("unknown PkrSrc type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PktDumperComponent::PktDumperComponent(const std::string& name, const std::string& arg_prefix, factory_callback arg_factory)
|
||||
: plugin::Component(plugin::component::PKTDUMPER, name)
|
||||
{
|
||||
tokenize_string(arg_prefix, ":", &prefixes);
|
||||
factory = arg_factory;
|
||||
}
|
||||
|
||||
PktDumperComponent::~PktDumperComponent()
|
||||
{
|
||||
}
|
||||
|
||||
PktDumperComponent::factory_callback PktDumperComponent::Factory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& PktDumperComponent::Prefixes() const
|
||||
{
|
||||
return prefixes;
|
||||
}
|
||||
|
||||
bool PktDumperComponent::HandlesPrefix(const string& prefix) const
|
||||
{
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin();
|
||||
i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PktDumperComponent::DoDescribe(ODesc* d) const
|
||||
{
|
||||
plugin::Component::DoDescribe(d);
|
||||
|
||||
string prefs;
|
||||
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin();
|
||||
i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
|
||||
d->Add("dumper prefix");
|
||||
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
|
||||
d->Add(": ");
|
||||
d->Add(prefs);
|
||||
}
|
177
src/iosource/Component.h
Normal file
177
src/iosource/Component.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_PLUGIN_COMPONENT_H
|
||||
#define IOSOURCE_PLUGIN_COMPONENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "plugin/Component.h"
|
||||
|
||||
namespace iosource {
|
||||
|
||||
class IOSource;
|
||||
class PktSrc;
|
||||
class PktDumper;
|
||||
|
||||
/**
|
||||
* Component description for plugins providing IOSources.
|
||||
*/
|
||||
class Component : public plugin::Component {
|
||||
public:
|
||||
typedef IOSource* (*factory_callback)();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
Component(const std::string& name);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
Component(const Component& other);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Component();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor to use by derived classes.
|
||||
*
|
||||
* @param type The type of the componnent.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
Component(plugin::component::Type type, const std::string& name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Component description for plugins providing a PktSrc for packet input.
|
||||
*/
|
||||
class PktSrcComponent : public iosource::Component {
|
||||
public:
|
||||
/**
|
||||
* Type of input a packet source supports.
|
||||
*/
|
||||
enum InputType {
|
||||
LIVE, ///< Live input.
|
||||
TRACE, ///< Offline input from trace file.
|
||||
BOTH ///< Live input as well as offline.
|
||||
};
|
||||
|
||||
typedef PktSrc* (*factory_callback)(const std::string& path, bool is_live);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*
|
||||
* @param prefixes The list of interface/file prefixes associated
|
||||
* with this component.
|
||||
*
|
||||
* @param type Type of input the component supports.
|
||||
*
|
||||
* @param factor Factory function to instantiate component.
|
||||
*/
|
||||
PktSrcComponent(const std::string& name, const std::string& prefixes, InputType type, factory_callback factory);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~PktSrcComponent();
|
||||
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* live traffic.
|
||||
*/
|
||||
bool DoesLive() const;
|
||||
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* offline traces.
|
||||
*/
|
||||
bool DoesTrace() const;
|
||||
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "bro -NN".
|
||||
*/
|
||||
virtual void DoDescribe(ODesc* d) const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> prefixes;
|
||||
InputType type;
|
||||
factory_callback factory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component description for plugins providing a PktDumper for packet output.
|
||||
*
|
||||
* PktDumpers aren't IOSurces but we locate them here to keep them along with
|
||||
* the PktSrc.
|
||||
*/
|
||||
class PktDumperComponent : public plugin::Component {
|
||||
public:
|
||||
typedef PktDumper* (*factory_callback)(const std::string& path, bool append);
|
||||
|
||||
/**
|
||||
* XXX
|
||||
*/
|
||||
PktDumperComponent(const std::string& name, const std::string& prefixes, factory_callback factory);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktDumperComponent();
|
||||
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "bro -NN".
|
||||
*/
|
||||
virtual void DoDescribe(ODesc* d) const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> prefixes;
|
||||
factory_callback factory;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
104
src/iosource/FD_Set.h
Normal file
104
src/iosource/FD_Set.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef BRO_FD_SET_H
|
||||
#define BRO_FD_SET_H
|
||||
|
||||
#include <set>
|
||||
#include <sys/select.h>
|
||||
|
||||
namespace iosource {
|
||||
|
||||
/**
|
||||
* A container holding a set of file descriptors.
|
||||
*/
|
||||
class FD_Set {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor. The set is initially empty.
|
||||
*/
|
||||
FD_Set() : max(-1), fds()
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Insert a file descriptor in to the set.
|
||||
* @param fd the fd to insert in the set.
|
||||
* @return false if fd was already in the set, else true.
|
||||
*/
|
||||
bool Insert(int fd)
|
||||
{
|
||||
if ( max < fd )
|
||||
max = fd;
|
||||
|
||||
return fds.insert(fd).second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts all the file descriptors from another set in to this one.
|
||||
* @param other a file descriptor set to merge in to this one.
|
||||
*/
|
||||
void Insert(const FD_Set& other)
|
||||
{
|
||||
for ( std::set<int>::const_iterator it = other.fds.begin();
|
||||
it != other.fds.end(); ++it )
|
||||
Insert(*it);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the set.
|
||||
*/
|
||||
void Clear()
|
||||
{ max = -1; fds.clear(); }
|
||||
|
||||
/**
|
||||
* Insert file descriptors in to a fd_set for use with select().
|
||||
* @return the greatest file descriptor inserted.
|
||||
*/
|
||||
int Set(fd_set* set) const
|
||||
{
|
||||
for ( std::set<int>::const_iterator it = fds.begin(); it != fds.end();
|
||||
++it )
|
||||
FD_SET(*it, set);
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether a file descriptor belonging to this set is within the
|
||||
* fd_set arugment.
|
||||
*/
|
||||
bool Ready(fd_set* set) const
|
||||
{
|
||||
for ( std::set<int>::const_iterator it = fds.begin(); it != fds.end();
|
||||
++it )
|
||||
{
|
||||
if ( FD_ISSET(*it, set) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether any file descriptors have been added to the set.
|
||||
*/
|
||||
bool Empty() const
|
||||
{
|
||||
return fds.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the greatest file descriptor of all that have been added to the
|
||||
* set, or -1 if the set is empty.
|
||||
*/
|
||||
int Max() const
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
private:
|
||||
int max;
|
||||
std::set<int> fds;
|
||||
};
|
||||
|
||||
} // namespace bro
|
||||
|
||||
#endif // BRO_FD_SET_H
|
140
src/iosource/IOSource.h
Normal file
140
src/iosource/IOSource.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_IOSOURCE_H
|
||||
#define IOSOURCE_IOSOURCE_H
|
||||
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include "FD_Set.h"
|
||||
#include "Timer.h"
|
||||
|
||||
namespace iosource {
|
||||
|
||||
/**
|
||||
* Interface class for components providing/consuming data inside Bro's main
|
||||
* loop.
|
||||
*/
|
||||
class IOSource {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
IOSource() { idle = false; closed = false; }
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~IOSource() {}
|
||||
|
||||
/**
|
||||
* Returns true if source has nothing ready to process.
|
||||
*/
|
||||
bool IsIdle() const { return idle; }
|
||||
|
||||
/**
|
||||
* Returns true if more data is to be expected in the future.
|
||||
* Otherwise, source may be removed.
|
||||
*/
|
||||
bool IsOpen() const { return ! closed; }
|
||||
|
||||
/**
|
||||
* Initializes the source. Can be overwritten by derived classes.
|
||||
*/
|
||||
virtual void Init() { }
|
||||
|
||||
/**
|
||||
* Finalizes the source when it's being closed. Can be overwritten by
|
||||
* derived classes.
|
||||
*/
|
||||
virtual void Done() { }
|
||||
|
||||
/**
|
||||
* Returns select'able file descriptors for this source. Leaves the
|
||||
* passed values untouched if not available.
|
||||
*
|
||||
* @param read Pointer to container where to insert a read descriptor.
|
||||
*
|
||||
* @param write Pointer to container where to insert a write descriptor.
|
||||
*
|
||||
* @param except Pointer to container where to insert a except descriptor.
|
||||
*/
|
||||
virtual void GetFds(FD_Set* read, FD_Set* write, FD_Set* except) = 0;
|
||||
|
||||
/**
|
||||
* Returns the timestamp (in \a global network time) associated with
|
||||
* next data item from this source. If the source wants the data
|
||||
* item to be processed with a local network time, it sets the
|
||||
* argument accordingly.
|
||||
*
|
||||
* This method will be called only when either IsIdle() returns
|
||||
* false, or select() on one of the fds returned by GetFDs()
|
||||
* indicates that there's data to process.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*
|
||||
* @param network_time A pointer to store the \a local network time
|
||||
* associated with the next item (as opposed to global network time).
|
||||
*
|
||||
* @return The global network time of the next entry, or a value
|
||||
* smaller than zero if none is available currently.
|
||||
*/
|
||||
virtual double NextTimestamp(double* network_time) = 0;
|
||||
|
||||
/**
|
||||
* Processes and consumes next data item.
|
||||
*
|
||||
* This method will be called only when either IsIdle() returns
|
||||
* false, or select() on one of the fds returned by GetFDs()
|
||||
* indicates that there's data to process.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
|
||||
/**
|
||||
* Returns the tag of the timer manafger associated with the last
|
||||
* procesees data item.
|
||||
*
|
||||
* Can be overridden by derived classes.
|
||||
*
|
||||
* @return The tag, or null for the global timer manager.
|
||||
*
|
||||
*/
|
||||
virtual TimerMgr::Tag* GetCurrentTag() { return 0; }
|
||||
|
||||
/**
|
||||
* Returns a descriptual tag representing the source for debugging.
|
||||
*
|
||||
* Can be overridden by derived classes.
|
||||
*
|
||||
* @return The debugging name.
|
||||
*/
|
||||
virtual const char* Tag() = 0;
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Callback for derived classes to call when they have gone dry
|
||||
* temporarily.
|
||||
*
|
||||
* @param is_idle True if the source is idle currently.
|
||||
*/
|
||||
void SetIdle(bool is_idle) { idle = is_idle; }
|
||||
|
||||
/*
|
||||
* Callback for derived class to call when they have shutdown.
|
||||
*
|
||||
* @param is_closed True if the source is now closed.
|
||||
*/
|
||||
void SetClosed(bool is_closed) { closed = is_closed; }
|
||||
|
||||
private:
|
||||
bool idle;
|
||||
bool closed;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
314
src/iosource/Manager.cc
Normal file
314
src/iosource/Manager.cc
Normal file
|
@ -0,0 +1,314 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Manager.h"
|
||||
#include "IOSource.h"
|
||||
#include "PktSrc.h"
|
||||
#include "PktDumper.h"
|
||||
#include "plugin/Manager.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define DEFAULT_PREFIX "pcap"
|
||||
|
||||
using namespace iosource;
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
{
|
||||
(*i)->src->Done();
|
||||
delete *i;
|
||||
}
|
||||
|
||||
sources.clear();
|
||||
|
||||
for ( PktDumperList::iterator i = pkt_dumpers.begin(); i != pkt_dumpers.end(); ++i )
|
||||
{
|
||||
(*i)->Done();
|
||||
delete *i;
|
||||
}
|
||||
|
||||
pkt_dumpers.clear();
|
||||
}
|
||||
|
||||
void Manager::RemoveAll()
|
||||
{
|
||||
// We're cheating a bit here ...
|
||||
dont_counts = sources.size();
|
||||
}
|
||||
|
||||
IOSource* Manager::FindSoonest(double* ts)
|
||||
{
|
||||
// Remove sources which have gone dry. For simplicity, we only
|
||||
// remove at most one each time.
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
if ( ! (*i)->src->IsOpen() )
|
||||
{
|
||||
(*i)->src->Done();
|
||||
delete *i;
|
||||
sources.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ideally, we would always call select on the fds to see which
|
||||
// are ready, and return the soonest. Unfortunately, that'd mean
|
||||
// one select-call per packet, which we can't afford in high-volume
|
||||
// environments. Thus, we call select only every SELECT_FREQUENCY
|
||||
// call (or if all sources report that they are dry).
|
||||
|
||||
++call_count;
|
||||
|
||||
IOSource* soonest_src = 0;
|
||||
double soonest_ts = 1e20;
|
||||
double soonest_local_network_time = 1e20;
|
||||
bool all_idle = true;
|
||||
|
||||
// Find soonest source of those which tell us they have something to
|
||||
// process.
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
{
|
||||
if ( ! (*i)->src->IsIdle() )
|
||||
{
|
||||
all_idle = false;
|
||||
double local_network_time = 0;
|
||||
double ts = (*i)->src->NextTimestamp(&local_network_time);
|
||||
if ( ts > 0 && ts < soonest_ts )
|
||||
{
|
||||
soonest_ts = ts;
|
||||
soonest_src = (*i)->src;
|
||||
soonest_local_network_time =
|
||||
local_network_time ?
|
||||
local_network_time : ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found one and aren't going to select this time,
|
||||
// return it.
|
||||
int maxx = 0;
|
||||
|
||||
if ( soonest_src && (call_count % SELECT_FREQUENCY) != 0 )
|
||||
goto finished;
|
||||
|
||||
// Select on the join of all file descriptors.
|
||||
fd_set fd_read, fd_write, fd_except;
|
||||
|
||||
FD_ZERO(&fd_read);
|
||||
FD_ZERO(&fd_write);
|
||||
FD_ZERO(&fd_except);
|
||||
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
{
|
||||
Source* src = (*i);
|
||||
|
||||
if ( ! src->src->IsIdle() )
|
||||
// No need to select on sources which we know to
|
||||
// be ready.
|
||||
continue;
|
||||
|
||||
src->Clear();
|
||||
src->src->GetFds(&src->fd_read, &src->fd_write, &src->fd_except);
|
||||
if ( src->fd_read.Empty() ) src->fd_read.Insert(0);
|
||||
if ( src->fd_write.Empty() ) src->fd_write.Insert(0);
|
||||
if ( src->fd_except.Empty() ) src->fd_except.Insert(0);
|
||||
src->SetFds(&fd_read, &fd_write, &fd_except, &maxx);
|
||||
}
|
||||
|
||||
// We can't block indefinitely even when all sources are dry:
|
||||
// we're doing some IOSource-independent stuff in the main loop,
|
||||
// so we need to return from time to time. (Instead of no time-out
|
||||
// at all, we use a very small one. This lets FreeBSD trigger a
|
||||
// BPF buffer switch on the next read when the hold buffer is empty
|
||||
// while the store buffer isn't filled yet.
|
||||
|
||||
struct timeval timeout;
|
||||
|
||||
if ( all_idle )
|
||||
{
|
||||
// Interesting: when all sources are dry, simply sleeping a
|
||||
// bit *without* watching for any fd becoming ready may
|
||||
// decrease CPU load. I guess that's because it allows
|
||||
// the kernel's packet buffers to fill. - Robin
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 20; // SELECT_TIMEOUT;
|
||||
select(0, 0, 0, 0, &timeout);
|
||||
}
|
||||
|
||||
if ( ! maxx )
|
||||
// No selectable fd at all.
|
||||
goto finished;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if ( select(maxx + 1, &fd_read, &fd_write, &fd_except, &timeout) > 0 )
|
||||
{ // Find soonest.
|
||||
for ( SourceList::iterator i = sources.begin();
|
||||
i != sources.end(); ++i )
|
||||
{
|
||||
Source* src = (*i);
|
||||
|
||||
if ( ! src->src->IsIdle() )
|
||||
continue;
|
||||
|
||||
if ( src->Ready(&fd_read, &fd_write, &fd_except) )
|
||||
{
|
||||
double local_network_time = 0;
|
||||
double ts = src->src->NextTimestamp(&local_network_time);
|
||||
if ( ts > 0.0 && ts < soonest_ts )
|
||||
{
|
||||
soonest_ts = ts;
|
||||
soonest_src = src->src;
|
||||
soonest_local_network_time =
|
||||
local_network_time ?
|
||||
local_network_time : ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
*ts = soonest_local_network_time;
|
||||
return soonest_src;
|
||||
}
|
||||
|
||||
void Manager::Register(IOSource* src, bool dont_count)
|
||||
{
|
||||
src->Init();
|
||||
Source* s = new Source;
|
||||
s->src = src;
|
||||
if ( dont_count )
|
||||
++dont_counts;
|
||||
|
||||
sources.push_back(s);
|
||||
}
|
||||
|
||||
void Manager::Register(PktSrc* src)
|
||||
{
|
||||
pkt_srcs.push_back(src);
|
||||
Register(src, false);
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> split_prefix(std::string path)
|
||||
{
|
||||
// See if the path comes with a prefix telling us which type of
|
||||
// PktSrc to use. If not, choose default.
|
||||
std::string prefix;
|
||||
|
||||
std::string::size_type i = path.find("::");
|
||||
if ( i != std::string::npos )
|
||||
{
|
||||
prefix = path.substr(0, i);
|
||||
path = path.substr(i + 2, std::string::npos);
|
||||
}
|
||||
|
||||
else
|
||||
prefix= DEFAULT_PREFIX;
|
||||
|
||||
return std::make_pair(prefix, path);
|
||||
}
|
||||
|
||||
PktSrc* Manager::OpenPktSrc(const std::string& path, bool is_live)
|
||||
{
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
std::string prefix = t.first;
|
||||
std::string npath = t.second;
|
||||
|
||||
// Find the component providing packet sources of the requested prefix.
|
||||
|
||||
PktSrcComponent* component = 0;
|
||||
|
||||
std::list<PktSrcComponent*> all_components = plugin_mgr->Components<PktSrcComponent>();
|
||||
|
||||
for ( std::list<PktSrcComponent*>::const_iterator i = all_components.begin();
|
||||
i != all_components.end(); i++ )
|
||||
{
|
||||
PktSrcComponent* c = *i;
|
||||
|
||||
if ( c->HandlesPrefix(prefix) &&
|
||||
(( is_live && c->DoesLive() ) ||
|
||||
(! is_live && c->DoesTrace())) )
|
||||
{
|
||||
component = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet source '%s' not recognized, or mode not supported", prefix.c_str());
|
||||
|
||||
// Instantiate packet source.
|
||||
|
||||
PktSrc* ps = (*component->Factory())(npath, is_live);
|
||||
assert(ps);
|
||||
|
||||
if ( ! ps->IsOpen() && ps->IsError() )
|
||||
// Set an error message if it didn't open successfully.
|
||||
ps->Error("could not open");
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Created packet source of type %s for %s", component->Name().c_str(), npath.c_str());
|
||||
|
||||
Register(ps);
|
||||
return ps;
|
||||
}
|
||||
|
||||
|
||||
PktDumper* Manager::OpenPktDumper(const string& path, bool append)
|
||||
{
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
std::string prefix = t.first;
|
||||
std::string npath = t.second;
|
||||
|
||||
// Find the component providing packet dumpers of the requested prefix.
|
||||
|
||||
PktDumperComponent* component = 0;
|
||||
|
||||
std::list<PktDumperComponent*> all_components = plugin_mgr->Components<PktDumperComponent>();
|
||||
|
||||
for ( std::list<PktDumperComponent*>::const_iterator i = all_components.begin();
|
||||
i != all_components.end(); i++ )
|
||||
{
|
||||
if ( (*i)->HandlesPrefix(prefix) )
|
||||
{
|
||||
component = (*i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet dumper '%s' not recognized", prefix.c_str());
|
||||
|
||||
// Instantiate packet dumper.
|
||||
|
||||
PktDumper* pd = (*component->Factory())(npath, append);
|
||||
assert(pd);
|
||||
|
||||
if ( ! pd->IsOpen() && pd->IsError() )
|
||||
// Set an error message if it didn't open successfully.
|
||||
pd->Error("could not open");
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Created packer dumper of type %s for %s", component->Name().c_str(), npath.c_str());
|
||||
|
||||
pd->Init();
|
||||
pkt_dumpers.push_back(pd);
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
void Manager::Source::SetFds(fd_set* read, fd_set* write, fd_set* except,
|
||||
int* maxx) const
|
||||
{
|
||||
*maxx = std::max(*maxx, fd_read.Set(read));
|
||||
*maxx = std::max(*maxx, fd_write.Set(write));
|
||||
*maxx = std::max(*maxx, fd_except.Set(except));
|
||||
}
|
146
src/iosource/Manager.h
Normal file
146
src/iosource/Manager.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_MANAGER_H
|
||||
#define IOSOURCE_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "iosource/FD_Set.h"
|
||||
|
||||
namespace iosource {
|
||||
|
||||
class IOSource;
|
||||
class PktSrc;
|
||||
class PktDumper;
|
||||
|
||||
/**
|
||||
* Singleton class managing all IOSources.
|
||||
*/
|
||||
class Manager {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Manager() { call_count = 0; dont_counts = 0; }
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Manager();
|
||||
|
||||
/**
|
||||
* Registers an IOSource with the manager.
|
||||
*
|
||||
* @param src The source. The manager takes ownership.
|
||||
*
|
||||
* @param dont_count If true, this source does not contribute to the
|
||||
* number of IOSources returned by Size(). The effect is that if all
|
||||
* sources except for the non-counting ones have gone dry, processing
|
||||
* will shut down.
|
||||
*/
|
||||
void Register(IOSource* src, bool dont_count = false);
|
||||
|
||||
/**
|
||||
* Returns the packet source with the soonest available input. This
|
||||
* may block for a little while if all are dry.
|
||||
*
|
||||
* @param ts A pointer where to store the timestamp of the input that
|
||||
* the soonest source has available next.
|
||||
*
|
||||
* @return The source, or null if no source has input.
|
||||
*/
|
||||
IOSource* FindSoonest(double* ts);
|
||||
|
||||
/**
|
||||
* Returns the number of registered and still active sources,
|
||||
* excluding those that are registered as \a dont_cont.
|
||||
*/
|
||||
int Size() const { return sources.size() - dont_counts; }
|
||||
|
||||
typedef std::list<PktSrc *> PktSrcList;
|
||||
|
||||
/**
|
||||
* Returns a list of all registered PktSrc instances. This is a
|
||||
* subset of all registered IOSource instances.
|
||||
*/
|
||||
const PktSrcList& GetPktSrcs() const { return pkt_srcs; }
|
||||
|
||||
/**
|
||||
* Terminate all processing immediately by removing all sources (and
|
||||
* therefore now returning a Size() of zero).
|
||||
*/
|
||||
void Terminate() { RemoveAll(); }
|
||||
|
||||
/**
|
||||
* Opens a new packet source.
|
||||
*
|
||||
* @param path The interface or file name, as one would give to Bro \c -i.
|
||||
*
|
||||
* @param is_live True if \a path represents a live interface, false
|
||||
* for a file.
|
||||
*
|
||||
* @return The new packet source, or null if an error occured.
|
||||
*/
|
||||
PktSrc* OpenPktSrc(const std::string& path, bool is_live);
|
||||
|
||||
/**
|
||||
* Opens a new packet dumper.
|
||||
*
|
||||
* @param path The file name to dump into.
|
||||
*
|
||||
* @param append True to append if \a path already exists.
|
||||
*
|
||||
* @return The new packet dumper, or null if an error occured.
|
||||
*/
|
||||
PktDumper* OpenPktDumper(const std::string& path, bool append);
|
||||
|
||||
private:
|
||||
/**
|
||||
* When looking for a source with something to process, every
|
||||
* SELECT_FREQUENCY calls we will go ahead and block on a select().
|
||||
*/
|
||||
static const int SELECT_FREQUENCY = 25;
|
||||
|
||||
/**
|
||||
* Microseconds to wait in an empty select if no source is ready.
|
||||
*/
|
||||
static const int SELECT_TIMEOUT = 50;
|
||||
|
||||
void Register(PktSrc* src);
|
||||
void RemoveAll();
|
||||
|
||||
unsigned int call_count;
|
||||
int dont_counts;
|
||||
|
||||
struct Source {
|
||||
IOSource* src;
|
||||
FD_Set fd_read;
|
||||
FD_Set fd_write;
|
||||
FD_Set fd_except;
|
||||
|
||||
bool Ready(fd_set* read, fd_set* write, fd_set* except) const
|
||||
{ return fd_read.Ready(read) || fd_write.Ready(write) ||
|
||||
fd_except.Ready(except); }
|
||||
|
||||
void SetFds(fd_set* read, fd_set* write, fd_set* except,
|
||||
int* maxx) const;
|
||||
|
||||
void Clear()
|
||||
{ fd_read.Clear(); fd_write.Clear(); fd_except.Clear(); }
|
||||
};
|
||||
|
||||
typedef std::list<Source*> SourceList;
|
||||
SourceList sources;
|
||||
|
||||
typedef std::list<PktDumper *> PktDumperList;
|
||||
|
||||
PktSrcList pkt_srcs;
|
||||
PktDumperList pkt_dumpers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern iosource::Manager* iosource_mgr;
|
||||
|
||||
#endif
|
||||
|
84
src/iosource/PktDumper.cc
Normal file
84
src/iosource/PktDumper.cc
Normal file
|
@ -0,0 +1,84 @@
|
|||
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "PktDumper.h"
|
||||
|
||||
using namespace iosource;
|
||||
|
||||
PktDumper::PktDumper()
|
||||
{
|
||||
is_open = false;
|
||||
errmsg = "";
|
||||
}
|
||||
|
||||
PktDumper::~PktDumper()
|
||||
{
|
||||
}
|
||||
|
||||
void PktDumper::Init()
|
||||
{
|
||||
Open();
|
||||
}
|
||||
|
||||
void PktDumper::Done()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
const std::string& PktDumper::Path() const
|
||||
{
|
||||
return props.path;
|
||||
}
|
||||
|
||||
bool PktDumper::IsOpen() const
|
||||
{
|
||||
return is_open;
|
||||
}
|
||||
|
||||
double PktDumper::OpenTime() const
|
||||
{
|
||||
return is_open ? props.open_time : 0;
|
||||
}
|
||||
|
||||
bool PktDumper::IsError() const
|
||||
{
|
||||
return errmsg.size();
|
||||
}
|
||||
|
||||
const char* PktDumper::ErrorMsg() const
|
||||
{
|
||||
return errmsg.size() ? errmsg.c_str() : 0;
|
||||
}
|
||||
|
||||
int PktDumper::HdrSize() const
|
||||
{
|
||||
return is_open ? props.hdr_size : -1;
|
||||
}
|
||||
|
||||
void PktDumper::Opened(const Properties& arg_props)
|
||||
{
|
||||
is_open = true;
|
||||
props = arg_props;
|
||||
DBG_LOG(DBG_PKTIO, "Opened dumper %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktDumper::Closed()
|
||||
{
|
||||
is_open = false;
|
||||
DBG_LOG(DBG_PKTIO, "Closed dumper %s", props.path.c_str());
|
||||
props.path = "";
|
||||
}
|
||||
|
||||
void PktDumper::Error(const std::string& msg)
|
||||
{
|
||||
errmsg = msg;
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Error with dumper %s: %s",
|
||||
IsOpen() ? props.path.c_str() : "<not open>",
|
||||
msg.c_str());
|
||||
}
|
163
src/iosource/PktDumper.h
Normal file
163
src/iosource/PktDumper.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_PKTSRC_PKTDUMPER_H
|
||||
#define IOSOURCE_PKTSRC_PKTDUMPER_H
|
||||
|
||||
#include "IOSource.h"
|
||||
|
||||
namespace iosource {
|
||||
|
||||
/**
|
||||
* Base class for packet dumpers.
|
||||
*/
|
||||
class PktDumper {
|
||||
public:
|
||||
/**
|
||||
* Structure describing a packet.
|
||||
*/
|
||||
struct Packet {
|
||||
/**
|
||||
* The pcap header associated with the packet.
|
||||
*/
|
||||
const struct pcap_pkthdr* hdr;
|
||||
|
||||
/**
|
||||
* The full content of the packet.
|
||||
*/
|
||||
const unsigned char* data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktDumper();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~PktDumper();
|
||||
|
||||
/**
|
||||
* Returns the path associated with the dumper.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
|
||||
/**
|
||||
* Returns true if the dumper is open for writing.
|
||||
*/
|
||||
bool IsOpen() const;
|
||||
|
||||
/**
|
||||
* Returns the time when the dumper was opened for writing.
|
||||
*/
|
||||
double OpenTime() const;
|
||||
|
||||
/**
|
||||
* Returns returns true if the dumper has encountered an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
|
||||
/**
|
||||
* Returns if the dumper has encountered an error, returns a
|
||||
* corresponding error message. Returns an emoty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
|
||||
/**
|
||||
* Returns the size of the link-layer headers with this dumper.
|
||||
*/
|
||||
int HdrSize() const;
|
||||
|
||||
/**
|
||||
* Writes a packet to the dumper.
|
||||
*
|
||||
* @param pkt The packet to record.
|
||||
*/
|
||||
bool Record(const Packet* pkt);
|
||||
|
||||
// PktDumper interface for derived classes to implement.
|
||||
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
|
||||
/**
|
||||
* Called by the manager system to close the dumper.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Called to write a packet to the dumper.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param pkt The packet to record.
|
||||
*
|
||||
* @return True if succesful, false otherwise (in which case \a
|
||||
* Error() must have been called.)
|
||||
*/
|
||||
virtual bool Dump(const Packet* pkt) = 0;
|
||||
|
||||
protected:
|
||||
friend class Manager;
|
||||
|
||||
/**
|
||||
* Structure to pass back information about the packet dumper to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties {
|
||||
std::string path;
|
||||
int hdr_size;
|
||||
double open_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
|
||||
/**
|
||||
* Called from derived classes to signal an error.
|
||||
*
|
||||
* @param msg A corresponding error message.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Called by the manager to initialize the dumper.
|
||||
*/
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* Called by the manager to shutdown the dumper.
|
||||
*/
|
||||
void Done();
|
||||
|
||||
private:
|
||||
bool is_open;
|
||||
Properties props;
|
||||
|
||||
std::string errmsg;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
525
src/iosource/PktSrc.cc
Normal file
525
src/iosource/PktSrc.cc
Normal file
|
@ -0,0 +1,525 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "PktSrc.h"
|
||||
#include "Hash.h"
|
||||
#include "Net.h"
|
||||
#include "Sessions.h"
|
||||
|
||||
using namespace iosource;
|
||||
|
||||
PktSrc::PktSrc()
|
||||
{
|
||||
have_packet = false;
|
||||
errbuf = "";
|
||||
SetClosed(true);
|
||||
|
||||
next_sync_point = 0;
|
||||
first_timestamp = 0.0;
|
||||
first_wallclock = current_wallclock = 0;
|
||||
}
|
||||
|
||||
PktSrc::~PktSrc()
|
||||
{
|
||||
BPF_Program* code;
|
||||
IterCookie* cookie = filters.InitForIteration();
|
||||
while ( (code = filters.NextEntry(cookie)) )
|
||||
delete code;
|
||||
}
|
||||
|
||||
const std::string& PktSrc::Path() const
|
||||
{
|
||||
static std::string not_open("not open");
|
||||
return IsOpen() ? props.path : not_open;
|
||||
}
|
||||
|
||||
const char* PktSrc::ErrorMsg() const
|
||||
{
|
||||
return errbuf.size() ? errbuf.c_str() : 0;
|
||||
}
|
||||
|
||||
int PktSrc::LinkType() const
|
||||
{
|
||||
return IsOpen() ? props.link_type : -1;
|
||||
}
|
||||
|
||||
uint32 PktSrc::Netmask() const
|
||||
{
|
||||
return IsOpen() ? props.netmask : PCAP_NETMASK_UNKNOWN;
|
||||
}
|
||||
|
||||
bool PktSrc::IsError() const
|
||||
{
|
||||
return ErrorMsg();
|
||||
}
|
||||
|
||||
int PktSrc::HdrSize() const
|
||||
{
|
||||
return IsOpen() ? props.hdr_size : -1;
|
||||
}
|
||||
|
||||
int PktSrc::SnapLen() const
|
||||
{
|
||||
return snaplen; // That's a global. Change?
|
||||
}
|
||||
|
||||
bool PktSrc::IsLive() const
|
||||
{
|
||||
return props.is_live;
|
||||
}
|
||||
|
||||
double PktSrc::CurrentPacketTimestamp()
|
||||
{
|
||||
return current_pseudo;
|
||||
}
|
||||
|
||||
double PktSrc::CurrentPacketWallClock()
|
||||
{
|
||||
// We stop time when we are suspended.
|
||||
if ( net_is_processing_suspended() )
|
||||
current_wallclock = current_time(true);
|
||||
|
||||
return current_wallclock;
|
||||
}
|
||||
|
||||
void PktSrc::Opened(const Properties& arg_props)
|
||||
{
|
||||
if ( arg_props.hdr_size < 0 )
|
||||
{
|
||||
char buf[512];
|
||||
safe_snprintf(buf, sizeof(buf),
|
||||
"unknown data link type 0x%x", props.link_type);
|
||||
Error(buf);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
props = arg_props;
|
||||
SetClosed(false);
|
||||
|
||||
if ( ! PrecompileFilter(0, "") || ! SetFilter(0) )
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( props.is_live )
|
||||
Info(fmt("listening on %s, capture length %d bytes\n", props.path.c_str(), SnapLen()));
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Closed()
|
||||
{
|
||||
SetClosed(true);
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Closed source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Error(const std::string& msg)
|
||||
{
|
||||
// We don't report this immediately, Bro will ask us for the error
|
||||
// once it notices we aren't open.
|
||||
errbuf = msg;
|
||||
DBG_LOG(DBG_PKTIO, "Error with source %s: %s",
|
||||
IsOpen() ? props.path.c_str() : "<not open>",
|
||||
msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Info(const std::string& msg)
|
||||
{
|
||||
reporter->Info("%s", msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Weird(const std::string& msg, const Packet* p)
|
||||
{
|
||||
sessions->Weird(msg.c_str(), p->hdr, p->data, 0);
|
||||
}
|
||||
|
||||
void PktSrc::InternalError(const std::string& msg)
|
||||
{
|
||||
reporter->InternalError("%s", msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::ContinueAfterSuspend()
|
||||
{
|
||||
current_wallclock = current_time(true);
|
||||
}
|
||||
|
||||
int PktSrc::GetLinkHeaderSize(int link_type)
|
||||
{
|
||||
switch ( link_type ) {
|
||||
case DLT_NULL:
|
||||
return 4;
|
||||
|
||||
case DLT_EN10MB:
|
||||
return 14;
|
||||
|
||||
case DLT_FDDI:
|
||||
return 13 + 8; // fddi_header + LLC
|
||||
|
||||
#ifdef DLT_LINUX_SLL
|
||||
case DLT_LINUX_SLL:
|
||||
return 16;
|
||||
#endif
|
||||
|
||||
case DLT_PPP_SERIAL: // PPP_SERIAL
|
||||
return 4;
|
||||
|
||||
case DLT_RAW:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
double PktSrc::CheckPseudoTime()
|
||||
{
|
||||
if ( ! IsOpen() )
|
||||
return 0;
|
||||
|
||||
if ( ! ExtractNextPacketInternal() )
|
||||
return 0;
|
||||
|
||||
if ( remote_trace_sync_interval )
|
||||
{
|
||||
if ( next_sync_point == 0 || current_packet.ts >= next_sync_point )
|
||||
{
|
||||
int n = remote_serializer->SendSyncPoint();
|
||||
next_sync_point = first_timestamp +
|
||||
n * remote_trace_sync_interval;
|
||||
remote_serializer->Log(RemoteSerializer::LogInfo,
|
||||
fmt("stopping at packet %.6f, next sync-point at %.6f",
|
||||
current_packet.ts, next_sync_point));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double pseudo_time = current_packet.ts - first_timestamp;
|
||||
double ct = (current_time(true) - first_wallclock) * pseudo_realtime;
|
||||
|
||||
return pseudo_time <= ct ? bro_start_time + pseudo_time : 0;
|
||||
}
|
||||
|
||||
void PktSrc::Init()
|
||||
{
|
||||
Open();
|
||||
}
|
||||
|
||||
void PktSrc::Done()
|
||||
{
|
||||
if ( IsOpen() )
|
||||
Close();
|
||||
}
|
||||
|
||||
void PktSrc::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except)
|
||||
{
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
// Select would give erroneous results. But we simulate it
|
||||
// by setting idle accordingly.
|
||||
SetIdle(CheckPseudoTime() == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( IsOpen() && props.selectable_fd >= 0 )
|
||||
read->Insert(props.selectable_fd);
|
||||
}
|
||||
|
||||
double PktSrc::NextTimestamp(double* local_network_time)
|
||||
{
|
||||
if ( ! IsOpen() )
|
||||
return -1.0;
|
||||
|
||||
if ( ! ExtractNextPacketInternal() )
|
||||
return -1.0;
|
||||
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
// Delay packet if necessary.
|
||||
double packet_time = CheckPseudoTime();
|
||||
if ( packet_time )
|
||||
return packet_time;
|
||||
|
||||
SetIdle(true);
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
return current_packet.ts;
|
||||
}
|
||||
|
||||
void PktSrc::Process()
|
||||
{
|
||||
if ( ! IsOpen() )
|
||||
return;
|
||||
|
||||
if ( ! ExtractNextPacketInternal() )
|
||||
return;
|
||||
|
||||
int pkt_hdr_size = props.hdr_size;
|
||||
|
||||
// Unfortunately some packets on the link might have MPLS labels
|
||||
// while others don't. That means we need to ask the link-layer if
|
||||
// labels are in place.
|
||||
bool have_mpls = false;
|
||||
|
||||
int protocol = 0;
|
||||
const u_char* data = current_packet.data;
|
||||
|
||||
switch ( props.link_type ) {
|
||||
case DLT_NULL:
|
||||
{
|
||||
protocol = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
|
||||
|
||||
// From the Wireshark Wiki: "AF_INET6, unfortunately, has
|
||||
// different values in {NetBSD,OpenBSD,BSD/OS},
|
||||
// {FreeBSD,DragonFlyBSD}, and {Darwin/Mac OS X}, so an IPv6
|
||||
// packet might have a link-layer header with 24, 28, or 30
|
||||
// as the AF_ value." As we may be reading traces captured on
|
||||
// platforms other than what we're running on, we accept them
|
||||
// all here.
|
||||
if ( protocol != AF_INET
|
||||
&& protocol != AF_INET6
|
||||
&& protocol != 24
|
||||
&& protocol != 28
|
||||
&& protocol != 30 )
|
||||
{
|
||||
Weird("non_ip_packet_in_null_transport", ¤t_packet);
|
||||
goto done;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DLT_EN10MB:
|
||||
{
|
||||
// Get protocol being carried from the ethernet frame.
|
||||
protocol = (data[12] << 8) + data[13];
|
||||
|
||||
switch ( protocol )
|
||||
{
|
||||
// MPLS carried over the ethernet frame.
|
||||
case 0x8847:
|
||||
// Remove the data link layer and denote a
|
||||
// header size of zero before the IP header.
|
||||
have_mpls = true;
|
||||
data += GetLinkHeaderSize(props.link_type);
|
||||
pkt_hdr_size = 0;
|
||||
break;
|
||||
|
||||
// VLAN carried over the ethernet frame.
|
||||
case 0x8100:
|
||||
data += GetLinkHeaderSize(props.link_type);
|
||||
|
||||
// Check for MPLS in VLAN.
|
||||
if ( ((data[2] << 8) + data[3]) == 0x8847 )
|
||||
have_mpls = true;
|
||||
|
||||
data += 4; // Skip the vlan header
|
||||
pkt_hdr_size = 0;
|
||||
|
||||
// Check for 802.1ah (Q-in-Q) containing IP.
|
||||
// Only do a second layer of vlan tag
|
||||
// stripping because there is no
|
||||
// specification that allows for deeper
|
||||
// nesting.
|
||||
if ( ((data[2] << 8) + data[3]) == 0x0800 )
|
||||
data += 4;
|
||||
|
||||
break;
|
||||
|
||||
// PPPoE carried over the ethernet frame.
|
||||
case 0x8864:
|
||||
data += GetLinkHeaderSize(props.link_type);
|
||||
protocol = (data[6] << 8) + data[7];
|
||||
data += 8; // Skip the PPPoE session and PPP header
|
||||
pkt_hdr_size = 0;
|
||||
|
||||
if ( protocol != 0x0021 && protocol != 0x0057 )
|
||||
{
|
||||
// Neither IPv4 nor IPv6.
|
||||
Weird("non_ip_packet_in_pppoe_encapsulation", ¤t_packet);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DLT_PPP_SERIAL:
|
||||
{
|
||||
// Get PPP protocol.
|
||||
protocol = (data[2] << 8) + data[3];
|
||||
|
||||
if ( protocol == 0x0281 )
|
||||
{
|
||||
// MPLS Unicast. Remove the data link layer and
|
||||
// denote a header size of zero before the IP header.
|
||||
have_mpls = true;
|
||||
data += GetLinkHeaderSize(props.link_type);
|
||||
pkt_hdr_size = 0;
|
||||
}
|
||||
|
||||
else if ( protocol != 0x0021 && protocol != 0x0057 )
|
||||
{
|
||||
// Neither IPv4 nor IPv6.
|
||||
Weird("non_ip_packet_in_ppp_encapsulation", ¤t_packet);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( have_mpls )
|
||||
{
|
||||
// Skip the MPLS label stack.
|
||||
bool end_of_stack = false;
|
||||
|
||||
while ( ! end_of_stack )
|
||||
{
|
||||
end_of_stack = *(data + 2) & 0x01;
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pseudo_realtime )
|
||||
{
|
||||
current_pseudo = CheckPseudoTime();
|
||||
net_packet_dispatch(current_pseudo, current_packet.hdr, data, pkt_hdr_size, this);
|
||||
if ( ! first_wallclock )
|
||||
first_wallclock = current_time(true);
|
||||
}
|
||||
|
||||
else
|
||||
net_packet_dispatch(current_packet.ts, current_packet.hdr, data, pkt_hdr_size, this);
|
||||
|
||||
done:
|
||||
have_packet = 0;
|
||||
DoneWithPacket();
|
||||
}
|
||||
|
||||
const char* PktSrc::Tag()
|
||||
{
|
||||
return "PktSrc";
|
||||
}
|
||||
|
||||
bool PktSrc::ExtractNextPacketInternal()
|
||||
{
|
||||
if ( have_packet )
|
||||
return true;
|
||||
|
||||
have_packet = false;
|
||||
|
||||
// Don't return any packets if processing is suspended (except for the
|
||||
// very first packet which we need to set up times).
|
||||
if ( net_is_processing_suspended() && first_timestamp )
|
||||
{
|
||||
SetIdle(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( pseudo_realtime )
|
||||
current_wallclock = current_time(true);
|
||||
|
||||
if ( ExtractNextPacket(¤t_packet) )
|
||||
{
|
||||
if ( ! first_timestamp )
|
||||
first_timestamp = current_packet.ts;
|
||||
|
||||
SetIdle(false);
|
||||
have_packet = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( pseudo_realtime && using_communication && ! IsOpen() )
|
||||
{
|
||||
// Source has gone dry, we're done.
|
||||
if ( remote_trace_sync_interval )
|
||||
remote_serializer->SendFinalSyncPoint();
|
||||
else
|
||||
remote_serializer->Terminate();
|
||||
}
|
||||
|
||||
SetIdle(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PktSrc::PrecompileBPFFilter(int index, const std::string& filter)
|
||||
{
|
||||
if ( index < 0 )
|
||||
return false;
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
// Compile filter.
|
||||
BPF_Program* code = new BPF_Program();
|
||||
|
||||
if ( ! code->Compile(SnapLen(), LinkType(), filter.c_str(), Netmask(), errbuf, sizeof(errbuf)) )
|
||||
{
|
||||
string msg = fmt("cannot compile BPF filter \"%s\"", filter.c_str());
|
||||
|
||||
if ( *errbuf )
|
||||
msg += ": " + string(errbuf);
|
||||
|
||||
Error(msg);
|
||||
|
||||
delete code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store it in hash.
|
||||
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
|
||||
BPF_Program* oldcode = filters.Lookup(hash);
|
||||
if ( oldcode )
|
||||
delete oldcode;
|
||||
|
||||
filters.Insert(hash, code);
|
||||
delete hash;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
BPF_Program* PktSrc::GetBPFFilter(int index)
|
||||
{
|
||||
if ( index < 0 )
|
||||
return 0;
|
||||
|
||||
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
|
||||
BPF_Program* code = filters.Lookup(hash);
|
||||
delete hash;
|
||||
return code;
|
||||
}
|
||||
|
||||
bool PktSrc::ApplyBPFFilter(int index, const struct pcap_pkthdr *hdr, const u_char *pkt)
|
||||
{
|
||||
BPF_Program* code = GetBPFFilter(index);
|
||||
|
||||
if ( ! code )
|
||||
{
|
||||
Error(fmt("BPF filter %d not compiled", index));
|
||||
Close();
|
||||
}
|
||||
|
||||
if ( code->MatchesAnything() )
|
||||
return true;
|
||||
|
||||
return pcap_offline_filter(code->GetProgram(), hdr, pkt);
|
||||
}
|
||||
|
||||
bool PktSrc::GetCurrentPacket(const pcap_pkthdr** hdr, const u_char** pkt)
|
||||
{
|
||||
if ( ! have_packet )
|
||||
return false;
|
||||
|
||||
*hdr = current_packet.hdr;
|
||||
*pkt = current_packet.data;
|
||||
return true;
|
||||
}
|
417
src/iosource/PktSrc.h
Normal file
417
src/iosource/PktSrc.h
Normal file
|
@ -0,0 +1,417 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_PKTSRC_PKTSRC_H
|
||||
#define IOSOURCE_PKTSRC_PKTSRC_H
|
||||
|
||||
#include "IOSource.h"
|
||||
#include "BPF_Program.h"
|
||||
#include "Dict.h"
|
||||
|
||||
declare(PDict,BPF_Program);
|
||||
|
||||
namespace iosource {
|
||||
|
||||
/**
|
||||
* Base class for packet sources.
|
||||
*/
|
||||
class PktSrc : public IOSource {
|
||||
public:
|
||||
/**
|
||||
* Struct for returning statistics on a packet source.
|
||||
*/
|
||||
struct Stats {
|
||||
/**
|
||||
* Packets received by source after filtering (w/o drops).
|
||||
*/
|
||||
unsigned int received;
|
||||
|
||||
/**
|
||||
* Packets dropped by source.
|
||||
*/
|
||||
unsigned int dropped; // pkts dropped
|
||||
|
||||
/**
|
||||
* Total number of packets on link before filtering.
|
||||
* Optional, can be left unset if not available.
|
||||
*/
|
||||
unsigned int link;
|
||||
|
||||
Stats() { received = dropped = link = 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktSrc();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~PktSrc();
|
||||
|
||||
/**
|
||||
* Returns the path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
|
||||
/**
|
||||
* Returns true if this is a live source.
|
||||
*/
|
||||
bool IsLive() const;
|
||||
|
||||
/**
|
||||
* Returns the link type of the source.
|
||||
*/
|
||||
int LinkType() const;
|
||||
|
||||
/**
|
||||
* Returns the netmask associated with the source, or \c
|
||||
* PCAP_NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32 Netmask() const;
|
||||
|
||||
/**
|
||||
* Returns true if the source has flagged an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
|
||||
/**
|
||||
* If the source encountered an error, returns a corresponding error
|
||||
* message. Returns an empty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
|
||||
/**
|
||||
* Returns the size of the link-layer header for this source.
|
||||
*/
|
||||
int HdrSize() const;
|
||||
|
||||
/**
|
||||
* Returns the snap length for this source.
|
||||
*/
|
||||
int SnapLen() const;
|
||||
|
||||
/**
|
||||
* In pseudo-realtime mode, returns the logical timestamp of the
|
||||
* current packet. Undefined if not running pseudo-realtime mode.
|
||||
*/
|
||||
double CurrentPacketTimestamp();
|
||||
|
||||
/**
|
||||
* In pseudo-realtime mode, returns the wall clock time associated
|
||||
* with current packet. Undefined if not running pseudo-realtime
|
||||
* mode.
|
||||
*/
|
||||
double CurrentPacketWallClock();
|
||||
|
||||
/**
|
||||
* Signals packet source that processing is going to be continued
|
||||
* after previous suspension.
|
||||
*/
|
||||
void ContinueAfterSuspend();
|
||||
|
||||
/**
|
||||
* Precompiles a BPF filter and associates the given index with it.
|
||||
* The compiled filter will be then available via \a GetBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index to associate with the filter.
|
||||
*
|
||||
* @param BPF filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred.
|
||||
*/
|
||||
bool PrecompileBPFFilter(int index, const std::string& filter);
|
||||
|
||||
/**
|
||||
* Returns the precompiled BPF filter associated with a given index,
|
||||
* if any, as compiled by \a PrecompileBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @return The BPF filter associated, or null if none has been
|
||||
* (successfully) compiled.
|
||||
*/
|
||||
BPF_Program* GetBPFFilter(int index);
|
||||
|
||||
/**
|
||||
* Applies a precompiled BPF filter to a packet. This will close the
|
||||
* source with an error message if no filter with that index has been
|
||||
* compiled.
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index of the filter to apply.
|
||||
*
|
||||
* @param hdr The header of the packet to filter.
|
||||
*
|
||||
* @param pkt The content of the packet to filter.
|
||||
*
|
||||
* @return True if it maches. */
|
||||
bool ApplyBPFFilter(int index, const struct pcap_pkthdr *hdr, const u_char *pkt);
|
||||
|
||||
/**
|
||||
* Returns the packet currently being processed, if available.
|
||||
*
|
||||
* @param hdr A pointer to pass the header of the current packet back.
|
||||
*
|
||||
* @param pkt A pointer to pass the content of the current packet
|
||||
* back.
|
||||
*
|
||||
* @return True if the current packet is available, or false if not.
|
||||
*/
|
||||
bool GetCurrentPacket(const pcap_pkthdr** hdr, const u_char** pkt);
|
||||
|
||||
// PacketSource interace for derived classes to override.
|
||||
|
||||
/**
|
||||
* Precompiles a filter and associates a given index with it. The
|
||||
* filter syntax is defined by the packet source's implenentation.
|
||||
*
|
||||
* Derived classes must implement this to implement their filtering.
|
||||
* If they want to use BPF but don't support it natively, they can
|
||||
* call the corresponding helper method provided by \a PktSrc.
|
||||
*
|
||||
* @param index The index to associate with the filter
|
||||
*
|
||||
* @param filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or filtering
|
||||
* is not supported.
|
||||
*/
|
||||
virtual bool PrecompileFilter(int index, const std::string& filter) = 0;
|
||||
|
||||
/**
|
||||
* Activates a precompiled filter with the given index.
|
||||
*
|
||||
* Derived classes must implement this to implement their filtering.
|
||||
* If they want to use BPF but don't support it natively, they can
|
||||
* call the corresponding helper method provided by \a PktSrc.
|
||||
*
|
||||
* @param index The index of the filter to activate.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or the
|
||||
* filtering is not supported.
|
||||
*/
|
||||
virtual bool SetFilter(int index) = 0;
|
||||
|
||||
/**
|
||||
* Returns current statistics about the source.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param stats A statistics structure that the method fill out.
|
||||
*/
|
||||
virtual void Statistics(Stats* stats) = 0;
|
||||
|
||||
/**
|
||||
* Helper method to return the header size for a given link tyoe.
|
||||
*
|
||||
* @param link_type The link tyoe.
|
||||
*
|
||||
* @return The header size in bytes.
|
||||
*/
|
||||
static int GetLinkHeaderSize(int link_type);
|
||||
|
||||
protected:
|
||||
friend class Manager;
|
||||
|
||||
// Methods to use by derived classes.
|
||||
|
||||
/**
|
||||
* Structure to pass back information about the packet source to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties {
|
||||
/**
|
||||
* The path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
std::string path;
|
||||
|
||||
/**
|
||||
* A file descriptor suitable to use with \a select() for
|
||||
* determining if there's input available from this source.
|
||||
*/
|
||||
int selectable_fd;
|
||||
|
||||
/**
|
||||
* The link type for packets from this source.
|
||||
*/
|
||||
int link_type;
|
||||
|
||||
/**
|
||||
* The size of the link-layer header for packets from this
|
||||
* source. \a GetLinkHeaderSize() may be used to derive this
|
||||
* value.
|
||||
*/
|
||||
int hdr_size;
|
||||
|
||||
/**
|
||||
* The netmask associated with the source, or \c
|
||||
* PCAP_NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32 netmask;
|
||||
|
||||
/**
|
||||
* True if the source is reading live inout, false for
|
||||
* working offline.
|
||||
*/
|
||||
bool is_live;
|
||||
|
||||
Properties()
|
||||
{
|
||||
netmask = PCAP_NETMASK_UNKNOWN;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure describing a packet.
|
||||
*/
|
||||
struct Packet {
|
||||
/**
|
||||
* Time associated with the packet.
|
||||
*/
|
||||
double ts;
|
||||
|
||||
/**
|
||||
* The pcap header associated with the packet.
|
||||
*/
|
||||
const struct ::pcap_pkthdr* hdr;
|
||||
|
||||
/**
|
||||
* The full content of the packet.
|
||||
*/
|
||||
const u_char* data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to send an informational
|
||||
* message to the user.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void Info(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flag send an error.
|
||||
*
|
||||
* @param msg The message going with the error.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flah a "weird" situation.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*
|
||||
* @param pkt The packet associated with the weird, or null if none.
|
||||
*/
|
||||
void Weird(const std::string& msg, const Packet* pkt);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flag an internal error,
|
||||
* which will abort execution.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void InternalError(const std::string& msg);
|
||||
|
||||
// PktSrc interface for derived classes to implement.
|
||||
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
|
||||
/**
|
||||
* Called by the manager system to close the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Provides the next packet from the source.
|
||||
*
|
||||
* @param pkt The packet structure to fill in with the packet's
|
||||
* information. The callee keep ownership of the data but must
|
||||
* guaranetee that it stays available at least until \a
|
||||
* DoneWithPacket() is called. It is guaranteed that no two calls to
|
||||
* this method will hapen with \a DoneWithPacket() in between.
|
||||
*
|
||||
* @return True if a packet is available and *pkt* filled in. False
|
||||
* if not packet is available or an error occured (which must be
|
||||
* flageed via Error()).
|
||||
*/
|
||||
virtual bool ExtractNextPacket(Packet* pkt) = 0;
|
||||
|
||||
/**
|
||||
* Signals that the data of previously extracted packet will no
|
||||
* longer be needed.
|
||||
*/
|
||||
virtual void DoneWithPacket() = 0;
|
||||
|
||||
private:
|
||||
// Checks if the current packet has a pseudo-time <= current_time. If
|
||||
// yes, returns pseudo-time, otherwise 0.
|
||||
double CheckPseudoTime();
|
||||
|
||||
// Internal helper for ExtractNextPacket().
|
||||
bool ExtractNextPacketInternal();
|
||||
|
||||
// IOSource interface implementation.
|
||||
virtual void Init();
|
||||
virtual void Done();
|
||||
virtual void GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
|
||||
iosource::FD_Set* except);
|
||||
virtual double NextTimestamp(double* local_network_time);
|
||||
virtual void Process();
|
||||
virtual const char* Tag();
|
||||
|
||||
Properties props;
|
||||
|
||||
bool have_packet;
|
||||
Packet current_packet;
|
||||
|
||||
// For BPF filtering support.
|
||||
PDict(BPF_Program) filters;
|
||||
|
||||
// Only set in pseudo-realtime mode.
|
||||
double first_timestamp;
|
||||
double first_wallclock;
|
||||
double current_wallclock;
|
||||
double current_pseudo;
|
||||
double next_sync_point; // For trace synchronziation in pseudo-realtime
|
||||
|
||||
std::string errbuf;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
104
src/iosource/pcap.bif
Normal file
104
src/iosource/pcap.bif
Normal file
|
@ -0,0 +1,104 @@
|
|||
|
||||
## Precompiles a PCAP filter and binds it to a given identifier.
|
||||
##
|
||||
## id: The PCAP identifier to reference the filter *s* later on.
|
||||
##
|
||||
## s: The PCAP filter. See ``man tcpdump`` for valid expressions.
|
||||
##
|
||||
## Returns: True if *s* is valid and precompiles successfully.
|
||||
##
|
||||
## .. bro:see:: install_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
## pcap_error
|
||||
function precompile_pcap_filter%(id: PcapFilterID, s: string%): bool
|
||||
%{
|
||||
bool success = true;
|
||||
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
{
|
||||
iosource::PktSrc* ps = *i;
|
||||
|
||||
if ( ! ps->PrecompileFilter(id->ForceAsInt(),
|
||||
s->CheckString()) )
|
||||
success = false;
|
||||
}
|
||||
|
||||
return new Val(success, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Installs a PCAP filter that has been precompiled with
|
||||
## :bro:id:`precompile_pcap_filter`.
|
||||
##
|
||||
## id: The PCAP filter id of a precompiled filter.
|
||||
##
|
||||
## Returns: True if the filter associated with *id* has been installed
|
||||
## successfully.
|
||||
##
|
||||
## .. bro:see:: precompile_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
## pcap_error
|
||||
function install_pcap_filter%(id: PcapFilterID%): bool
|
||||
%{
|
||||
bool success = true;
|
||||
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
{
|
||||
iosource::PktSrc* ps = *i;
|
||||
|
||||
if ( ! ps->SetFilter(id->ForceAsInt()) )
|
||||
success = false;
|
||||
}
|
||||
|
||||
return new Val(success, TYPE_BOOL);
|
||||
%}
|
||||
|
||||
## Returns a string representation of the last PCAP error.
|
||||
##
|
||||
## Returns: A descriptive error message of the PCAP function that failed.
|
||||
##
|
||||
## .. bro:see:: precompile_pcap_filter
|
||||
## install_pcap_filter
|
||||
## install_src_addr_filter
|
||||
## install_src_net_filter
|
||||
## uninstall_src_addr_filter
|
||||
## uninstall_src_net_filter
|
||||
## install_dst_addr_filter
|
||||
## install_dst_net_filter
|
||||
## uninstall_dst_addr_filter
|
||||
## uninstall_dst_net_filter
|
||||
function pcap_error%(%): string
|
||||
%{
|
||||
const iosource::Manager::PktSrcList& pkt_srcs(iosource_mgr->GetPktSrcs());
|
||||
|
||||
for ( iosource::Manager::PktSrcList::const_iterator i = pkt_srcs.begin();
|
||||
i != pkt_srcs.end(); i++ )
|
||||
{
|
||||
iosource::PktSrc* ps = *i;
|
||||
|
||||
const char* err = ps->ErrorMsg();
|
||||
if ( *err )
|
||||
return new StringVal(err);
|
||||
}
|
||||
|
||||
return new StringVal("no error");
|
||||
%}
|
8
src/iosource/pcap/CMakeLists.txt
Normal file
8
src/iosource/pcap/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
include(BroPlugin)
|
||||
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
bro_plugin_begin(Bro Pcap)
|
||||
bro_plugin_cc(Source.cc Dumper.cc Plugin.cc)
|
||||
bro_plugin_end()
|
112
src/iosource/pcap/Dumper.cc
Normal file
112
src/iosource/pcap/Dumper.cc
Normal file
|
@ -0,0 +1,112 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "Dumper.h"
|
||||
#include "../PktSrc.h"
|
||||
#include "../../Net.h"
|
||||
|
||||
using namespace iosource::pcap;
|
||||
|
||||
PcapDumper::PcapDumper(const std::string& path, bool arg_append)
|
||||
{
|
||||
append = arg_append;
|
||||
props.path = path;
|
||||
dumper = 0;
|
||||
pd = 0;
|
||||
}
|
||||
|
||||
PcapDumper::~PcapDumper()
|
||||
{
|
||||
}
|
||||
|
||||
void PcapDumper::Open()
|
||||
{
|
||||
int linktype = -1;
|
||||
|
||||
pd = pcap_open_dead(DLT_EN10MB, snaplen);
|
||||
if ( ! pd )
|
||||
{
|
||||
Error("error for pcap_open_dead");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( props.path.empty() )
|
||||
{
|
||||
Error("no filename given");
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
int exists = 0;
|
||||
|
||||
if ( append )
|
||||
{
|
||||
// See if output file already exists (and is non-empty).
|
||||
exists = stat(props.path.c_str(), &s); ;
|
||||
|
||||
if ( exists < 0 && errno != ENOENT )
|
||||
{
|
||||
Error(fmt("can't stat file %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! append || exists < 0 || s.st_size == 0 )
|
||||
{
|
||||
// Open new file.
|
||||
dumper = pcap_dump_open(pd, props.path.c_str());
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(pcap_geterr(pd));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Old file and we need to append, which, unfortunately,
|
||||
// is not supported by libpcap. So, we have to hack a
|
||||
// little bit, knowing that pcap_dumpter_t is, in fact,
|
||||
// a FILE ... :-(
|
||||
dumper = (pcap_dumper_t*) fopen(props.path.c_str(), "a");
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(fmt("can't open dump %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
props.open_time = network_time;
|
||||
props.hdr_size = PktSrc::GetLinkHeaderSize(pcap_datalink(pd));
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
void PcapDumper::Close()
|
||||
{
|
||||
if ( ! dumper )
|
||||
return;
|
||||
|
||||
pcap_dump_close(dumper);
|
||||
pcap_close(pd);
|
||||
dumper = 0;
|
||||
pd = 0;
|
||||
|
||||
Closed();
|
||||
}
|
||||
|
||||
bool PcapDumper::Dump(const Packet* pkt)
|
||||
{
|
||||
if ( ! dumper )
|
||||
return false;
|
||||
|
||||
pcap_dump((u_char*) dumper, pkt->hdr, pkt->data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
iosource::PktDumper* PcapDumper::Instantiate(const std::string& path, bool append)
|
||||
{
|
||||
return new PcapDumper(path, append);
|
||||
}
|
41
src/iosource/pcap/Dumper.h
Normal file
41
src/iosource/pcap/Dumper.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// See the file in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_PKTSRC_PCAP_DUMPER_H
|
||||
#define IOSOURCE_PKTSRC_PCAP_DUMPER_H
|
||||
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
|
||||
#include "../PktDumper.h"
|
||||
|
||||
namespace iosource {
|
||||
namespace pcap {
|
||||
|
||||
class PcapDumper : public PktDumper {
|
||||
public:
|
||||
PcapDumper(const std::string& path, bool append);
|
||||
virtual ~PcapDumper();
|
||||
|
||||
static PktDumper* Instantiate(const std::string& path, bool appen);
|
||||
|
||||
protected:
|
||||
// PktDumper interface.
|
||||
virtual void Open();
|
||||
virtual void Close();
|
||||
virtual bool Dump(const Packet* pkt);
|
||||
|
||||
private:
|
||||
Properties props;
|
||||
|
||||
bool append;
|
||||
pcap_dumper_t* dumper;
|
||||
pcap_t* pd;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
27
src/iosource/pcap/Plugin.cc
Normal file
27
src/iosource/pcap/Plugin.cc
Normal file
|
@ -0,0 +1,27 @@
|
|||
// See the file in the main distribution directory for copyright.
|
||||
|
||||
#include "plugin/Plugin.h"
|
||||
|
||||
#include "Source.h"
|
||||
#include "Dumper.h"
|
||||
|
||||
namespace plugin {
|
||||
namespace Bro_Pcap {
|
||||
|
||||
class Plugin : public plugin::Plugin {
|
||||
public:
|
||||
plugin::Configuration Configure()
|
||||
{
|
||||
AddComponent(new ::iosource::PktSrcComponent("PcapReader", "pcap", ::iosource::PktSrcComponent::BOTH, ::iosource::pcap::PcapSource::Instantiate));
|
||||
AddComponent(new ::iosource::PktDumperComponent("PcapWriter", "pcap", ::iosource::pcap::PcapDumper::Instantiate));
|
||||
|
||||
plugin::Configuration config;
|
||||
config.name = "Bro::Pcap";
|
||||
config.description = "Packet aquisition via libpcap";
|
||||
return config;
|
||||
}
|
||||
} plugin;
|
||||
|
||||
}
|
||||
}
|
||||
|
272
src/iosource/pcap/Source.cc
Normal file
272
src/iosource/pcap/Source.cc
Normal file
|
@ -0,0 +1,272 @@
|
|||
// See the file in the main distribution directory for copyright.
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Source.h"
|
||||
|
||||
#ifdef HAVE_PCAP_INT_H
|
||||
#include <pcap-int.h>
|
||||
#endif
|
||||
|
||||
using namespace iosource::pcap;
|
||||
|
||||
PcapSource::~PcapSource()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
PcapSource::PcapSource(const std::string& path, bool is_live)
|
||||
{
|
||||
props.path = path;
|
||||
props.is_live = is_live;
|
||||
last_data = 0;
|
||||
}
|
||||
|
||||
void PcapSource::Open()
|
||||
{
|
||||
if ( props.is_live )
|
||||
OpenLive();
|
||||
else
|
||||
OpenOffline();
|
||||
}
|
||||
|
||||
void PcapSource::Close()
|
||||
{
|
||||
if ( ! pd )
|
||||
return;
|
||||
|
||||
pcap_close(pd);
|
||||
pd = 0;
|
||||
last_data = 0;
|
||||
|
||||
Closed();
|
||||
}
|
||||
|
||||
void PcapSource::OpenLive()
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
char tmp_errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
// Determine interface if not specified.
|
||||
if ( props.path.empty() )
|
||||
props.path = pcap_lookupdev(tmp_errbuf);
|
||||
|
||||
if ( props.path.empty() )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"pcap_lookupdev: %s", tmp_errbuf);
|
||||
Error(errbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine network and netmask.
|
||||
uint32 net;
|
||||
if ( pcap_lookupnet(props.path.c_str(), &net, &props.netmask, tmp_errbuf) < 0 )
|
||||
{
|
||||
// ### The lookup can fail if no address is assigned to
|
||||
// the interface; and libpcap doesn't have any useful notion
|
||||
// of error codes, just error std::strings - how bogus - so we
|
||||
// just kludge around the error :-(.
|
||||
// sprintf(errbuf, "pcap_lookupnet %s", tmp_errbuf);
|
||||
// return;
|
||||
props.netmask = 0xffffff00;
|
||||
}
|
||||
|
||||
// We use the smallest time-out possible to return almost immediately if
|
||||
// no packets are available. (We can't use set_nonblocking() as it's
|
||||
// broken on FreeBSD: even when select() indicates that we can read
|
||||
// something, we may get nothing if the store buffer hasn't filled up
|
||||
// yet.)
|
||||
pd = pcap_open_live(props.path.c_str(), SnapLen(), 1, 1, tmp_errbuf);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
Error(tmp_errbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
// ### This needs autoconf'ing.
|
||||
#ifdef HAVE_PCAP_INT_H
|
||||
Info(fmt("pcap bufsize = %d\n", ((struct pcap *) pd)->bufsize));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX
|
||||
if ( pcap_setnonblock(pd, 1, tmp_errbuf) < 0 )
|
||||
{
|
||||
PcapError();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
props.selectable_fd = pcap_fileno(pd);
|
||||
|
||||
SetHdrSize();
|
||||
|
||||
if ( ! pd )
|
||||
// Was closed, couldn't get header size.
|
||||
return;
|
||||
|
||||
props.is_live = true;
|
||||
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
void PcapSource::OpenOffline()
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
pd = pcap_open_offline(props.path.c_str(), errbuf);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
Error(errbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
SetHdrSize();
|
||||
|
||||
if ( ! pd )
|
||||
// Was closed, unknown link layer type.
|
||||
return;
|
||||
|
||||
props.selectable_fd = fileno(pcap_file(pd));
|
||||
|
||||
if ( props.selectable_fd < 0 )
|
||||
InternalError("OS does not support selectable pcap fd");
|
||||
|
||||
props.is_live = false;
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
bool PcapSource::ExtractNextPacket(Packet* pkt)
|
||||
{
|
||||
if ( ! pd )
|
||||
return false;
|
||||
|
||||
const u_char* data = pcap_next(pd, ¤t_hdr);
|
||||
|
||||
if ( ! data )
|
||||
{
|
||||
// Source has gone dry. If it's a network interface, this just means
|
||||
// it's timed out. If it's a file, though, then the file has been
|
||||
// exhausted.
|
||||
if ( ! props.is_live )
|
||||
Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pkt->ts = current_hdr.ts.tv_sec + double(current_hdr.ts.tv_usec) / 1e6;
|
||||
pkt->hdr = ¤t_hdr;
|
||||
pkt->data = last_data = data;
|
||||
|
||||
if ( current_hdr.len == 0 || current_hdr.caplen == 0 )
|
||||
{
|
||||
Weird("empty_pcap_header", pkt);
|
||||
return false;
|
||||
}
|
||||
|
||||
last_hdr = current_hdr;
|
||||
last_data = data;
|
||||
++stats.received;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PcapSource::DoneWithPacket()
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
bool PcapSource::PrecompileFilter(int index, const std::string& filter)
|
||||
{
|
||||
return PktSrc::PrecompileBPFFilter(index, filter);
|
||||
}
|
||||
|
||||
bool PcapSource::SetFilter(int index)
|
||||
{
|
||||
if ( ! pd )
|
||||
return true; // Prevent error message
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
BPF_Program* code = GetBPFFilter(index);
|
||||
|
||||
if ( ! code )
|
||||
{
|
||||
safe_snprintf(errbuf, sizeof(errbuf),
|
||||
"No precompiled pcap filter for index %d",
|
||||
index);
|
||||
Error(errbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pcap_setfilter(pd, code->GetProgram()) < 0 )
|
||||
{
|
||||
PcapError();
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef HAVE_LINUX
|
||||
// Linux doesn't clear counters when resetting filter.
|
||||
stats.received = stats.dropped = stats.link = 0;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PcapSource::Statistics(Stats* s)
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
if ( ! (props.is_live && pd) )
|
||||
s->received = s->dropped = s->link = 0;
|
||||
|
||||
else
|
||||
{
|
||||
struct pcap_stat pstat;
|
||||
if ( pcap_stats(pd, &pstat) < 0 )
|
||||
{
|
||||
PcapError();
|
||||
s->received = s->dropped = s->link = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
s->dropped = pstat.ps_drop;
|
||||
s->link = pstat.ps_recv;
|
||||
}
|
||||
}
|
||||
|
||||
s->received = stats.received;
|
||||
|
||||
if ( ! props.is_live )
|
||||
s->dropped = 0;
|
||||
}
|
||||
|
||||
void PcapSource::PcapError()
|
||||
{
|
||||
if ( pd )
|
||||
Error(fmt("pcap_error: %s", pcap_geterr(pd)));
|
||||
else
|
||||
Error("pcap_error: not open");
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
void PcapSource::SetHdrSize()
|
||||
{
|
||||
if ( ! pd )
|
||||
return;
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
props.link_type = pcap_datalink(pd);
|
||||
props.hdr_size = GetLinkHeaderSize(props.link_type);
|
||||
}
|
||||
|
||||
iosource::PktSrc* PcapSource::Instantiate(const std::string& path, bool is_live)
|
||||
{
|
||||
return new PcapSource(path, is_live);
|
||||
}
|
47
src/iosource/pcap/Source.h
Normal file
47
src/iosource/pcap/Source.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef IOSOURCE_PKTSRC_PCAP_SOURCE_H
|
||||
#define IOSOURCE_PKTSRC_PCAP_SOURCE_H
|
||||
|
||||
#include "../PktSrc.h"
|
||||
|
||||
namespace iosource {
|
||||
namespace pcap {
|
||||
|
||||
class PcapSource : public iosource::PktSrc {
|
||||
public:
|
||||
PcapSource(const std::string& path, bool is_live);
|
||||
virtual ~PcapSource();
|
||||
|
||||
static PktSrc* Instantiate(const std::string& path, bool is_live);
|
||||
|
||||
protected:
|
||||
// PktSrc interface.
|
||||
virtual void Open();
|
||||
virtual void Close();
|
||||
virtual bool ExtractNextPacket(Packet* pkt);
|
||||
virtual void DoneWithPacket();
|
||||
virtual bool PrecompileFilter(int index, const std::string& filter);
|
||||
virtual bool SetFilter(int index);
|
||||
virtual void Statistics(Stats* stats);
|
||||
|
||||
private:
|
||||
void OpenLive();
|
||||
void OpenOffline();
|
||||
void PcapError();
|
||||
void SetHdrSize();
|
||||
|
||||
Properties props;
|
||||
Stats stats;
|
||||
|
||||
pcap_t *pd;
|
||||
|
||||
struct pcap_pkthdr current_hdr;
|
||||
struct pcap_pkthdr last_hdr;
|
||||
const u_char* last_data;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
57
src/main.cc
57
src/main.cc
|
@ -57,6 +57,7 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void);
|
|||
#include "plugin/Manager.h"
|
||||
#include "file_analysis/Manager.h"
|
||||
#include "broxygen/Manager.h"
|
||||
#include "iosource/Manager.h"
|
||||
|
||||
#include "binpac_bro.h"
|
||||
|
||||
|
@ -92,6 +93,7 @@ plugin::Manager* plugin_mgr = 0;
|
|||
analyzer::Manager* analyzer_mgr = 0;
|
||||
file_analysis::Manager* file_mgr = 0;
|
||||
broxygen::Manager* broxygen_mgr = 0;
|
||||
iosource::Manager* iosource_mgr = 0;
|
||||
Stmt* stmts;
|
||||
EventHandlerPtr net_done = 0;
|
||||
RuleMatcher* rule_matcher = 0;
|
||||
|
@ -108,7 +110,6 @@ int signal_val = 0;
|
|||
int optimize = 0;
|
||||
int do_notice_analysis = 0;
|
||||
int rule_bench = 0;
|
||||
SecondaryPath* secondary_path = 0;
|
||||
extern char version[];
|
||||
char* command_line_policy = 0;
|
||||
vector<string> params;
|
||||
|
@ -340,6 +341,10 @@ void terminate_bro()
|
|||
|
||||
terminating = true;
|
||||
|
||||
// File analysis termination may produce events, so do it early on in
|
||||
// the termination process.
|
||||
file_mgr->Terminate();
|
||||
|
||||
brofiler.WriteStats();
|
||||
|
||||
EventHandlerPtr bro_done = internal_handler("bro_done");
|
||||
|
@ -364,7 +369,6 @@ void terminate_bro()
|
|||
|
||||
mgr.Drain();
|
||||
|
||||
file_mgr->Terminate();
|
||||
log_mgr->Terminate();
|
||||
input_mgr->Terminate();
|
||||
thread_mgr->Terminate();
|
||||
|
@ -375,20 +379,16 @@ void terminate_bro()
|
|||
|
||||
delete broxygen_mgr;
|
||||
delete timer_mgr;
|
||||
delete dns_mgr;
|
||||
delete persistence_serializer;
|
||||
delete event_player;
|
||||
delete event_serializer;
|
||||
delete state_serializer;
|
||||
delete event_registry;
|
||||
delete secondary_path;
|
||||
delete remote_serializer;
|
||||
delete analyzer_mgr;
|
||||
delete file_mgr;
|
||||
delete log_mgr;
|
||||
delete plugin_mgr;
|
||||
delete thread_mgr;
|
||||
delete reporter;
|
||||
delete iosource_mgr;
|
||||
|
||||
reporter = 0;
|
||||
}
|
||||
|
@ -448,8 +448,6 @@ int main(int argc, char** argv)
|
|||
|
||||
name_list interfaces;
|
||||
name_list read_files;
|
||||
name_list netflows;
|
||||
name_list flow_files;
|
||||
name_list rule_files;
|
||||
char* bst_file = 0;
|
||||
char* id_name = 0;
|
||||
|
@ -551,7 +549,7 @@ int main(int argc, char** argv)
|
|||
opterr = 0;
|
||||
|
||||
char opts[256];
|
||||
safe_strncpy(opts, "B:D:e:f:I:i:K:l:n:p:R:r:s:T:t:U:w:x:X:y:Y:z:CFGLNOPSWabdghvZQ",
|
||||
safe_strncpy(opts, "B:D:e:f:I:i:K:l:n:p:R:r:s:T:t:U:w:x:X:z:CFGLNOPSWabdghvZQ",
|
||||
sizeof(opts));
|
||||
|
||||
#ifdef USE_PERFTOOLS_DEBUG
|
||||
|
@ -611,10 +609,6 @@ int main(int argc, char** argv)
|
|||
writefile = optarg;
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
flow_files.append(optarg);
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
if ( streq(optarg, "notice") )
|
||||
do_notice_analysis = 1;
|
||||
|
@ -708,10 +702,6 @@ int main(int argc, char** argv)
|
|||
do_watchdog = 1;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
netflows.append(optarg);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage();
|
||||
break;
|
||||
|
@ -799,8 +789,7 @@ int main(int argc, char** argv)
|
|||
// seed the PRNG. We should do this here (but at least Linux, FreeBSD
|
||||
// and Solaris provide /dev/urandom).
|
||||
|
||||
if ( (interfaces.length() > 0 || netflows.length() > 0) &&
|
||||
(read_files.length() > 0 || flow_files.length() > 0 ))
|
||||
if ( interfaces.length() > 0 && read_files.length() > 0 )
|
||||
usage();
|
||||
|
||||
#ifdef USE_IDMEF
|
||||
|
@ -823,7 +812,7 @@ int main(int argc, char** argv)
|
|||
plugin_mgr->SearchDynamicPlugins(bro_plugin_path());
|
||||
|
||||
if ( optind == argc &&
|
||||
read_files.length() == 0 && flow_files.length() == 0 &&
|
||||
read_files.length() == 0 &&
|
||||
interfaces.length() == 0 &&
|
||||
! (id_name || bst_file) && ! command_line_policy && ! print_plugins )
|
||||
add_input_file("-");
|
||||
|
@ -850,6 +839,7 @@ int main(int argc, char** argv)
|
|||
// policy, but we can't parse policy without DNS resolution.
|
||||
dns_mgr->SetDir(".state");
|
||||
|
||||
iosource_mgr = new iosource::Manager();
|
||||
persistence_serializer = new PersistenceSerializer();
|
||||
remote_serializer = new RemoteSerializer();
|
||||
event_registry = new EventRegistry();
|
||||
|
@ -863,9 +853,17 @@ int main(int argc, char** argv)
|
|||
file_mgr->InitPreScript();
|
||||
broxygen_mgr->InitPreScript();
|
||||
|
||||
bool missing_plugin = false;
|
||||
|
||||
for ( set<string>::const_iterator i = requested_plugins.begin();
|
||||
i != requested_plugins.end(); i++ )
|
||||
plugin_mgr->ActivateDynamicPlugin(*i);
|
||||
{
|
||||
if ( ! plugin_mgr->ActivateDynamicPlugin(*i) )
|
||||
missing_plugin = true;
|
||||
}
|
||||
|
||||
if ( missing_plugin )
|
||||
reporter->FatalError("Failed to activate requested dynamic plugin(s).");
|
||||
|
||||
plugin_mgr->ActivateDynamicPlugins(! bare_mode);
|
||||
|
||||
|
@ -916,6 +914,7 @@ int main(int argc, char** argv)
|
|||
|
||||
analyzer_mgr->InitPostScript();
|
||||
file_mgr->InitPostScript();
|
||||
dns_mgr->InitPostScript();
|
||||
|
||||
if ( parse_only )
|
||||
{
|
||||
|
@ -981,8 +980,7 @@ int main(int argc, char** argv)
|
|||
// ### Add support for debug command file.
|
||||
dbg_init_debugger(0);
|
||||
|
||||
if ( (flow_files.length() == 0 || read_files.length() == 0) &&
|
||||
(netflows.length() == 0 || interfaces.length() == 0) )
|
||||
if ( read_files.length() == 0 && interfaces.length() == 0 )
|
||||
{
|
||||
Val* interfaces_val = internal_val("interfaces");
|
||||
if ( interfaces_val )
|
||||
|
@ -999,13 +997,8 @@ int main(int argc, char** argv)
|
|||
|
||||
snaplen = internal_val("snaplen")->AsCount();
|
||||
|
||||
// Initialize the secondary path, if it's needed.
|
||||
secondary_path = new SecondaryPath();
|
||||
|
||||
if ( dns_type != DNS_PRIME )
|
||||
net_init(interfaces, read_files, netflows, flow_files,
|
||||
writefile, "",
|
||||
secondary_path->Filter(), do_watchdog);
|
||||
net_init(interfaces, read_files, writefile, do_watchdog);
|
||||
|
||||
BroFile::SetDefaultRotation(log_rotate_interval, log_max_size);
|
||||
|
||||
|
@ -1164,9 +1157,9 @@ int main(int argc, char** argv)
|
|||
|
||||
have_pending_timers = ! reading_traces && timer_mgr->Size() > 0;
|
||||
|
||||
io_sources.Register(thread_mgr, true);
|
||||
iosource_mgr->Register(thread_mgr, true);
|
||||
|
||||
if ( io_sources.Size() > 0 ||
|
||||
if ( iosource_mgr->Size() > 0 ||
|
||||
have_pending_timers ||
|
||||
BifConst::exit_only_after_terminate )
|
||||
{
|
||||
|
|
|
@ -50,6 +50,18 @@ void Component::Describe(ODesc* d) const
|
|||
d->Add("File Analyzer");
|
||||
break;
|
||||
|
||||
case component::IOSOURCE:
|
||||
d->Add("I/O Source");
|
||||
break;
|
||||
|
||||
case component::PKTSRC:
|
||||
d->Add("Packet Source");
|
||||
break;
|
||||
|
||||
case component::PKTDUMPER:
|
||||
d->Add("Packet Dumper");
|
||||
break;
|
||||
|
||||
default:
|
||||
reporter->InternalWarning("unknown component type in plugin::Component::Describe");
|
||||
d->Add("<unknown component type>");
|
||||
|
|
|
@ -12,20 +12,23 @@ namespace plugin {
|
|||
namespace component {
|
||||
|
||||
/**
|
||||
* Component types.
|
||||
* Component types.
|
||||
*/
|
||||
enum Type {
|
||||
READER, /// An input reader (not currently used).
|
||||
WRITER, /// A logging writer (not currenly used).
|
||||
ANALYZER, /// A protocol analyzer.
|
||||
FILE_ANALYZER /// A file analyzer.
|
||||
FILE_ANALYZER, /// A file analyzer.
|
||||
IOSOURCE, /// An I/O source, excluding packet sources.
|
||||
PKTSRC, /// A packet source.
|
||||
PKTDUMPER /// A packet dumper.
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for plugin components. A component is a specific piece of
|
||||
* functionality that a plugin provides, such as a protocol analyzer or a log
|
||||
* writer.
|
||||
* writer.
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
|
|
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