mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Refactor IP-in-IP tunnel support.
UDP tunnel support removed for now, to be re-added in specific analyzers later, but IP-in-IP is now decapsulated recursively so nested tunnels can be seen and the inner packets get sent through the IP fragment reassembler if necessary.
This commit is contained in:
parent
4062fc1776
commit
b51dd191d7
21 changed files with 300 additions and 323 deletions
|
@ -179,19 +179,27 @@ type AnalyzerID: count;
|
|||
|
||||
module Tunnel;
|
||||
export {
|
||||
## Records the identity of a the parent of a tunneled connection.
|
||||
type Parent: record {
|
||||
## The 4-tuple of the tunnel "connection". In case of an IP-in-IP
|
||||
## Records the identity of an encapsulating parent of a tunneled connection.
|
||||
type EncapsulatingConn: record {
|
||||
## The 4-tuple of the encapsulating "connection". In case of an IP-in-IP
|
||||
## tunnel the ports will be set to 0. The direction (i.e., orig and
|
||||
## resp) of the parent are set according to the tunneled connection
|
||||
## resp) are set according to the first tunneled packet seen
|
||||
## and not according to the side that established the tunnel.
|
||||
cid: conn_id;
|
||||
## The type of tunnel.
|
||||
tunnel_type: Tunneltype;
|
||||
tunnel_type: Tunnel::Type;
|
||||
} &log;
|
||||
} # end export
|
||||
module GLOBAL;
|
||||
|
||||
## A type alias for a vector of encapsulating "connections", i.e for when
|
||||
## there are tunnels within tunnels.
|
||||
##
|
||||
## .. todo:: We need this type definition only for declaring builtin functions
|
||||
## via ``bifcl``. We should extend ``bifcl`` to understand composite types
|
||||
## directly and then remove this alias.
|
||||
type encapsulating_conns: vector of Tunnel::EncapsulatingConn;
|
||||
|
||||
## Statistics about an endpoint.
|
||||
##
|
||||
## todo::Where is this used?
|
||||
|
@ -239,8 +247,11 @@ type connection: record {
|
|||
## used to tag and locate information associated with that connection.
|
||||
uid: string;
|
||||
## If the connection is tunneled, this field contains information about
|
||||
## the encapsulating "connection".
|
||||
tunnel_parent: Tunnel::Parent &optional;
|
||||
## the encapsulating "connection(s)" with the outermost one starting
|
||||
## at index zero. It's also always the first such enapsulation seen
|
||||
## for the connection unless the :bro:id:`tunnel_changed` event is handled
|
||||
## and re-assigns this field to the new encapsulation.
|
||||
tunnel: encapsulating_conns &optional;
|
||||
};
|
||||
|
||||
## Fields of a SYN packet.
|
||||
|
@ -2616,16 +2627,10 @@ const record_all_packets = F &redef;
|
|||
## .. bro:see:: conn_stats
|
||||
const ignore_keep_alive_rexmit = F &redef;
|
||||
|
||||
## Whether the analysis engine parses IP packets encapsulated in
|
||||
## UDP tunnels.
|
||||
##
|
||||
## .. bro:see:: tunnel_port
|
||||
const parse_udp_tunnels = F &redef;
|
||||
|
||||
module Tunnel;
|
||||
export {
|
||||
## Whether to decapsulate IP tunnels (IPinIP, 6in4, 6to4)
|
||||
const decapsulate_ip = F &redef;
|
||||
const decapsulate_ip = T &redef;
|
||||
|
||||
## Whether to decapsulate UDP tunnels (e.g., Teredo, IPv4 in UDP)
|
||||
const decapsulate_udp = F &redef;
|
||||
|
@ -2640,6 +2645,9 @@ export {
|
|||
## If udp_tunnel_allports is T :bro:id:`udp_tunnel_ports` is ignored and we
|
||||
## check every UDP packet for tunnels.
|
||||
const udp_tunnel_allports = F &redef;
|
||||
|
||||
## The maximum depth of a tunnel to decapsulate until giving up.
|
||||
const max_depth: count = 2 &redef;
|
||||
} # end export
|
||||
module GLOBAL;
|
||||
|
||||
|
|
|
@ -24,9 +24,8 @@
|
|||
##! is stored as the ``tunnel_parent`` member of :bro:type:`connection`,
|
||||
##! which is of type :bro:type:`Tunnel::Parent`.
|
||||
##!
|
||||
##! *Limitation:* The decapsulated packets are not fed through the
|
||||
##! defragmenter again and decapsulation happens only on the primary
|
||||
##! path, i.e., it's not available for the secondary path.
|
||||
##! *Limitation:* decapsulation happens only on the primary path, i.e.
|
||||
##! it's not available for the secondary path.
|
||||
|
||||
@load base/protocols/conn
|
||||
|
||||
|
|
19
src/Conn.cc
19
src/Conn.cc
|
@ -112,7 +112,8 @@ unsigned int Connection::external_connections = 0;
|
|||
|
||||
IMPLEMENT_SERIAL(Connection, SER_CONNECTION);
|
||||
|
||||
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, TunnelParent* arg_tunnel_parent)
|
||||
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
||||
const Encapsulation& arg_encap)
|
||||
{
|
||||
sessions = s;
|
||||
key = k;
|
||||
|
@ -156,7 +157,7 @@ Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, T
|
|||
|
||||
uid = 0; // Will set later.
|
||||
|
||||
tunnel_parent = arg_tunnel_parent;
|
||||
encapsulation = arg_encap;
|
||||
|
||||
if ( conn_timer_mgr )
|
||||
{
|
||||
|
@ -182,7 +183,6 @@ Connection::~Connection()
|
|||
Unref(conn_val);
|
||||
}
|
||||
|
||||
delete tunnel_parent;
|
||||
delete key;
|
||||
delete root_analyzer;
|
||||
delete conn_timer_mgr;
|
||||
|
@ -192,6 +192,15 @@ Connection::~Connection()
|
|||
--external_connections;
|
||||
}
|
||||
|
||||
void Connection::CheckEncapsulation(const Encapsulation& arg_encap)
|
||||
{
|
||||
if ( encapsulation != arg_encap )
|
||||
{
|
||||
Event(tunnel_changed, 0, arg_encap.GetVectorVal());
|
||||
encapsulation = arg_encap;
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Done()
|
||||
{
|
||||
finished = 1;
|
||||
|
@ -346,8 +355,8 @@ RecordVal* Connection::BuildConnVal()
|
|||
|
||||
char tmp[20];
|
||||
conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
|
||||
if ( tunnel_parent )
|
||||
conn_val->Assign(10, tunnel_parent->GetRecordVal());
|
||||
if ( encapsulation.Depth() > 0 )
|
||||
conn_val->Assign(10, encapsulation.GetVectorVal());
|
||||
}
|
||||
|
||||
if ( root_analyzer )
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "RuleMatcher.h"
|
||||
#include "AnalyzerTags.h"
|
||||
#include "IPAddr.h"
|
||||
#include "TunnelHandler.h"
|
||||
|
||||
class Connection;
|
||||
class ConnectionTimer;
|
||||
|
@ -51,9 +52,12 @@ class Analyzer;
|
|||
|
||||
class Connection : public BroObj {
|
||||
public:
|
||||
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, TunnelParent *arg_tunnel_parent);
|
||||
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
||||
const Encapsulation& arg_encap);
|
||||
virtual ~Connection();
|
||||
|
||||
void CheckEncapsulation(const Encapsulation& arg_encap);
|
||||
|
||||
// Invoked when connection is about to be removed. Use Ref(this)
|
||||
// inside Done to keep the connection object around (though it'll
|
||||
// no longer be accessible from the dictionary of active
|
||||
|
@ -276,7 +280,7 @@ protected:
|
|||
double inactivity_timeout;
|
||||
RecordVal* conn_val;
|
||||
LoginConn* login_conn; // either nil, or this
|
||||
TunnelParent* tunnel_parent; // nil if not tunneled
|
||||
Encapsulation encapsulation; // tunnels
|
||||
int suppress_event; // suppress certain events to once per conn.
|
||||
|
||||
unsigned int installed_status_timer:1;
|
||||
|
|
|
@ -30,9 +30,6 @@ int partial_connection_ok;
|
|||
int tcp_SYN_ack_ok;
|
||||
int tcp_match_undelivered;
|
||||
|
||||
int encap_hdr_size;
|
||||
int udp_tunnel_port;
|
||||
|
||||
double frag_timeout;
|
||||
|
||||
double tcp_SYN_timeout;
|
||||
|
@ -322,10 +319,6 @@ void init_net_var()
|
|||
tcp_SYN_ack_ok = opt_internal_int("tcp_SYN_ack_ok");
|
||||
tcp_match_undelivered = opt_internal_int("tcp_match_undelivered");
|
||||
|
||||
encap_hdr_size = opt_internal_int("encap_hdr_size");
|
||||
|
||||
udp_tunnel_port = opt_internal_int("udp_tunnel_port") & ~UDP_PORT_MASK;
|
||||
|
||||
frag_timeout = opt_internal_double("frag_timeout");
|
||||
|
||||
tcp_SYN_timeout = opt_internal_double("tcp_SYN_timeout");
|
||||
|
|
|
@ -33,9 +33,6 @@ extern int partial_connection_ok;
|
|||
extern int tcp_SYN_ack_ok;
|
||||
extern int tcp_match_undelivered;
|
||||
|
||||
extern int encap_hdr_size;
|
||||
extern int udp_tunnel_port;
|
||||
|
||||
extern double frag_timeout;
|
||||
|
||||
extern double tcp_SYN_timeout;
|
||||
|
|
102
src/Sessions.cc
102
src/Sessions.cc
|
@ -126,12 +126,6 @@ NetSessions::NetSessions()
|
|||
arp_analyzer = new ARP_Analyzer();
|
||||
else
|
||||
arp_analyzer = 0;
|
||||
|
||||
|
||||
if ( BifConst::Tunnel::decapsulate_ip || BifConst::Tunnel::decapsulate_udp )
|
||||
tunnel_handler = new TunnelHandler(this);
|
||||
else
|
||||
tunnel_handler = 0;
|
||||
}
|
||||
|
||||
NetSessions::~NetSessions()
|
||||
|
@ -185,6 +179,8 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
if ( record_all_packets )
|
||||
DumpPacket(hdr, pkt);
|
||||
|
||||
Encapsulation encapsulation;
|
||||
|
||||
if ( pkt_elem && pkt_elem->IPHdr() )
|
||||
// Fast path for "normal" IP packets if an IP_Hdr is
|
||||
// already extracted when doing PacketSort. Otherwise
|
||||
|
@ -192,7 +188,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
// difference here is that header extraction in
|
||||
// PacketSort does not generate Weird events.
|
||||
|
||||
DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size, encapsulation);
|
||||
|
||||
else
|
||||
{
|
||||
|
@ -217,7 +213,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
if ( ip->ip_v == 4 )
|
||||
{
|
||||
IP_Hdr ip_hdr(ip, false);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, encapsulation);
|
||||
}
|
||||
|
||||
else if ( ip->ip_v == 6 )
|
||||
|
@ -229,7 +225,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
|
||||
IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size), false, caplen);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, encapsulation);
|
||||
}
|
||||
|
||||
else if ( ARP_Analyzer::IsARP(pkt, hdr_size) )
|
||||
|
@ -351,7 +347,7 @@ int NetSessions::CheckConnectionTag(Connection* conn)
|
|||
|
||||
void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||
int hdr_size)
|
||||
int hdr_size, Encapsulation& encapsulation)
|
||||
{
|
||||
uint32 caplen = hdr->caplen - hdr_size;
|
||||
const struct ip* ip4 = ip_hdr->IP4_Hdr();
|
||||
|
@ -458,24 +454,10 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
#endif
|
||||
|
||||
TunnelInfo *tunnel_info = 0;
|
||||
if ( tunnel_handler )
|
||||
{
|
||||
tunnel_info = tunnel_handler->DecapsulateTunnel(ip_hdr, len, caplen, hdr, pkt);
|
||||
if (tunnel_info)
|
||||
{
|
||||
ip4 = tunnel_info->child->IP4_Hdr();
|
||||
ip_hdr = tunnel_info->child;
|
||||
len -= tunnel_info->hdr_len;
|
||||
caplen -= tunnel_info->hdr_len;
|
||||
}
|
||||
}
|
||||
|
||||
int proto = ip_hdr->NextProto();
|
||||
|
||||
if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt) )
|
||||
{
|
||||
delete tunnel_info;
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -540,9 +522,51 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
break;
|
||||
}
|
||||
|
||||
case IPPROTO_IP:
|
||||
case IPPROTO_IPV4:
|
||||
case IPPROTO_IPV6:
|
||||
{
|
||||
if ( ! BifConst::Tunnel::decapsulate_ip )
|
||||
{
|
||||
reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), "ip_tunnel");
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( encapsulation.Depth() >= BifConst::Tunnel::max_depth )
|
||||
{
|
||||
reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), "tunnel_depth");
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
IP_Hdr* inner_ip;
|
||||
if ( proto == IPPROTO_IPV6 )
|
||||
inner_ip = new IP_Hdr((const struct ip6_hdr*) data, false, caplen);
|
||||
else
|
||||
inner_ip = new IP_Hdr((const struct ip*) data, false);
|
||||
|
||||
struct pcap_pkthdr fake_hdr;
|
||||
fake_hdr.caplen = fake_hdr.len = caplen;
|
||||
fake_hdr.ts = hdr->ts;
|
||||
|
||||
EncapsulatingConn ec(ip_hdr->SrcAddr(), ip_hdr->DstAddr(),
|
||||
ip_hdr->IP4_Hdr() ?
|
||||
( proto == IPPROTO_IPV6 ?
|
||||
BifEnum::Tunnel::IP6_IN_IP4 : BifEnum::Tunnel::IP4_IN_IP4 ) :
|
||||
( proto == IPPROTO_IPV6 ?
|
||||
BifEnum::Tunnel::IP6_IN_IP6 : BifEnum::Tunnel::IP4_IN_IP6 ));
|
||||
encapsulation.Add(ec);
|
||||
|
||||
DoNextPacket(t, &fake_hdr, inner_ip, data, 0, encapsulation);
|
||||
|
||||
delete inner_ip;
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt);
|
||||
delete tunnel_info;
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -558,7 +582,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
conn = (Connection*) d->Lookup(h);
|
||||
if ( ! conn )
|
||||
{
|
||||
conn = NewConn(h, t, &id, data, proto, tunnel_info);
|
||||
conn = NewConn(h, t, &id, data, proto, encapsulation);
|
||||
if ( conn )
|
||||
d->Insert(h, conn);
|
||||
}
|
||||
|
@ -569,7 +593,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
if ( consistent < 0 )
|
||||
{
|
||||
delete h;
|
||||
delete tunnel_info;
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -580,18 +603,20 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
conn->Event(connection_reused, 0);
|
||||
|
||||
Remove(conn);
|
||||
conn = NewConn(h, t, &id, data, proto, tunnel_info);
|
||||
conn = NewConn(h, t, &id, data, proto, encapsulation);
|
||||
if ( conn )
|
||||
d->Insert(h, conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete h;
|
||||
conn->CheckEncapsulation(encapsulation);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! conn )
|
||||
{
|
||||
delete h;
|
||||
delete tunnel_info;
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -618,8 +643,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
record_packet, record_content,
|
||||
hdr, pkt, hdr_size);
|
||||
|
||||
delete tunnel_info;
|
||||
|
||||
if ( f )
|
||||
{
|
||||
// Above we already recorded the fragment in its entirety.
|
||||
|
@ -651,11 +674,19 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
|||
case IPPROTO_UDP:
|
||||
min_hdr_len = sizeof(struct udphdr);
|
||||
break;
|
||||
case IPPROTO_IP:
|
||||
case IPPROTO_IPV4:
|
||||
min_hdr_len = sizeof(struct ip);
|
||||
break;
|
||||
case IPPROTO_IPV6:
|
||||
min_hdr_len = sizeof(struct ip6_hdr);
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
case IPPROTO_ICMPV6:
|
||||
default:
|
||||
// Use for all other packets.
|
||||
min_hdr_len = ICMP_MINLEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( len < min_hdr_len )
|
||||
|
@ -962,14 +993,14 @@ void NetSessions::GetStats(SessionStats& s) const
|
|||
}
|
||||
|
||||
Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
|
||||
const u_char* data, int proto, TunnelInfo* tunnel_info)
|
||||
const u_char* data, int proto,
|
||||
const Encapsulation& encapsulation)
|
||||
{
|
||||
// FIXME: This should be cleaned up a bit, it's too protocol-specific.
|
||||
// But I'm not yet sure what the right abstraction for these things is.
|
||||
int src_h = ntohs(id->src_port);
|
||||
int dst_h = ntohs(id->dst_port);
|
||||
int flags = 0;
|
||||
TunnelParent *tunnel_parent = 0;
|
||||
|
||||
// Hmm... This is not great.
|
||||
TransportProto tproto = TRANSPORT_UNKNOWN;
|
||||
|
@ -1019,10 +1050,7 @@ Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
|
|||
id = &flip_id;
|
||||
}
|
||||
|
||||
if ( tunnel_info )
|
||||
tunnel_parent = new TunnelParent(&(tunnel_info->parent));
|
||||
|
||||
Connection* conn = new Connection(this, k, t, id, tunnel_parent);
|
||||
Connection* conn = new Connection(this, k, t, id, encapsulation);
|
||||
conn->SetTransport(tproto);
|
||||
dpm->BuildInitialAnalyzerTree(tproto, conn, data);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
struct pcap_pkthdr;
|
||||
|
||||
class Encapsulation;
|
||||
class Connection;
|
||||
class ConnID;
|
||||
class OSFingerprint;
|
||||
|
@ -26,9 +27,6 @@ class Discarder;
|
|||
class SteppingStoneManager;
|
||||
class PacketFilter;
|
||||
|
||||
class TunnelHandler;
|
||||
class TunnelInfo;
|
||||
|
||||
class PacketSortElement;
|
||||
|
||||
struct SessionStats {
|
||||
|
@ -145,7 +143,7 @@ protected:
|
|||
friend class TimerMgrExpireTimer;
|
||||
|
||||
Connection* NewConn(HashKey* k, double t, const ConnID* id,
|
||||
const u_char* data, int proto, TunnelInfo *tunnel_info);
|
||||
const u_char* data, int proto, const Encapsulation& encapsulation);
|
||||
|
||||
// Check whether the tag of the current packet is consistent with
|
||||
// the given connection. Returns:
|
||||
|
@ -178,7 +176,7 @@ protected:
|
|||
|
||||
void DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||
int hdr_size);
|
||||
int hdr_size, Encapsulation& encapsulation);
|
||||
|
||||
void NextPacketSecondary(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size,
|
||||
|
@ -216,8 +214,6 @@ protected:
|
|||
int num_packets_processed;
|
||||
PacketProfiler* pkt_profiler;
|
||||
|
||||
TunnelHandler *tunnel_handler;
|
||||
|
||||
// We may use independent timer managers for different sets of related
|
||||
// activity. The managers are identified by an unique tag.
|
||||
typedef std::map<TimerMgr::Tag, TimerMgr*> TimerMgrMap;
|
||||
|
|
|
@ -1,178 +1,52 @@
|
|||
// $Id: Sessions.cc 7075 2010-09-13 02:39:38Z vern $
|
||||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "TunnelHandler.h"
|
||||
#include "Conn.h"
|
||||
#include "Sessions.h"
|
||||
|
||||
|
||||
TunnelHandler::TunnelHandler(NetSessions *arg_s)
|
||||
RecordVal* EncapsulatingConn::GetRecordVal() const
|
||||
{
|
||||
s = arg_s;
|
||||
PortVal *pv = 0;
|
||||
TableVal *udp_tunnel_ports = BifConst::Tunnel::udp_tunnel_ports->AsTableVal();
|
||||
// Find UDP ports we want to analyze. Store them in an array for faster
|
||||
// lookup.
|
||||
for ( int i = 0; i< 65536; i++ )
|
||||
{
|
||||
if ( pv )
|
||||
Unref(pv);
|
||||
pv = new PortVal(i, TRANSPORT_UDP);
|
||||
if ( udp_tunnel_ports->Lookup(pv, false) )
|
||||
udp_ports[i] = 1;
|
||||
else
|
||||
udp_ports[i] = 0;
|
||||
}
|
||||
Unref(pv);
|
||||
}
|
||||
|
||||
TunnelInfo* TunnelHandler::DecapsulateTunnel(const IP_Hdr *ip_hdr, int len, int caplen,
|
||||
const struct pcap_pkthdr* hdr, const u_char* const pkt)
|
||||
{
|
||||
TunnelInfo *tunnel_info = 0;
|
||||
|
||||
switch ( ip_hdr->NextProto() ) {
|
||||
case IPPROTO_IPV6: /* 6in4 and 6to4 */
|
||||
if ( BifConst::Tunnel::decapsulate_ip )
|
||||
{
|
||||
if ( len < (int)sizeof(struct ip6_hdr) ||
|
||||
caplen < (int)sizeof(struct ip6_hdr) )
|
||||
{
|
||||
s->Weird("truncated_header", hdr, pkt);
|
||||
return 0;
|
||||
}
|
||||
// TODO: check if IP6 header makes sense
|
||||
tunnel_info = new TunnelInfo();
|
||||
tunnel_info->child = new IP_Hdr((const struct ip6_hdr*)ip_hdr->Payload(), false, caplen);
|
||||
tunnel_info->parent.tunneltype = BifEnum::Tunnel::IP6_IN_IP;
|
||||
tunnel_info->hdr_len = tunnel_info->child->HdrLen();
|
||||
tunnel_info->SetParentIPs(ip_hdr);
|
||||
return tunnel_info;
|
||||
}
|
||||
break;
|
||||
// TODO: IP in IP. Find test traces first. IP proto 0 and/or 4
|
||||
case IPPROTO_UDP:
|
||||
if ( BifConst::Tunnel::decapsulate_udp )
|
||||
{
|
||||
if ( len < (int)sizeof(struct udphdr) ||
|
||||
caplen < (int)sizeof(struct udphdr) )
|
||||
// No weird here. Main packet processing will raise it.
|
||||
return 0;
|
||||
return HandleUDP(ip_hdr, len, caplen);
|
||||
}
|
||||
|
||||
RecordVal *rv =
|
||||
new RecordVal(BifType::Record::Tunnel::EncapsulatingConn);
|
||||
TransportProto tproto;
|
||||
switch ( type ) {
|
||||
case BifEnum::Tunnel::IP6_IN_IP4:
|
||||
case BifEnum::Tunnel::IP4_IN_IP4:
|
||||
case BifEnum::Tunnel::IP6_IN_IP6:
|
||||
case BifEnum::Tunnel::IP4_IN_IP6:
|
||||
tproto = TRANSPORT_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
tproto = TRANSPORT_UDP;
|
||||
break;
|
||||
} /* end switch */
|
||||
return 0;
|
||||
} // end switch
|
||||
|
||||
RecordVal* id_val = new RecordVal(conn_id);
|
||||
id_val->Assign(0, new AddrVal(src_addr));
|
||||
id_val->Assign(1, new PortVal(ntohs(src_port), tproto));
|
||||
id_val->Assign(2, new AddrVal(dst_addr));
|
||||
id_val->Assign(3, new PortVal(ntohs(dst_port), tproto));
|
||||
rv->Assign(0, id_val);
|
||||
rv->Assign(1, new EnumVal(type, BifType::Enum::Tunnel::Type));
|
||||
return rv;
|
||||
}
|
||||
|
||||
TunnelInfo* TunnelHandler::HandleUDP(const IP_Hdr *ip_hdr, int len, int caplen)
|
||||
bool operator==(const Encapsulation& e1, const Encapsulation& e2)
|
||||
{
|
||||
// We already know we that we have a valid UDP header
|
||||
const u_char *data = ip_hdr->Payload();
|
||||
const struct udphdr* uh = (const struct udphdr*)data;
|
||||
IP_Hdr *cand_ip_hdr = 0;
|
||||
BifEnum::Tunnel::Tunneltype tunneltype = BifEnum::Tunnel::NONE;
|
||||
|
||||
int hdr_len = sizeof(struct udphdr);
|
||||
data += hdr_len;
|
||||
|
||||
int datalen = (int)ntohs(uh->uh_ulen);
|
||||
datalen = min(datalen, min(len, caplen));
|
||||
datalen -= hdr_len;
|
||||
|
||||
if ( BifConst::Tunnel::udp_tunnel_allports ||
|
||||
udp_ports[ntohs(uh->uh_sport)] ||
|
||||
udp_ports[ntohs(uh->uh_dport)] )
|
||||
{
|
||||
cand_ip_hdr = LookForIPHdr(data, datalen);
|
||||
if ( cand_ip_hdr )
|
||||
if ( e1.conns )
|
||||
if ( e2.conns )
|
||||
{
|
||||
// Found and IP hdr directly in the UDP payload
|
||||
tunneltype = (cand_ip_hdr->IP4_Hdr()) ?
|
||||
BifEnum::Tunnel::IP4_IN_UDP : BifEnum::Tunnel::IP6_IN_UDP;
|
||||
if ( e1.conns->size() != e2.conns->size() )
|
||||
return false;
|
||||
else
|
||||
for ( size_t i = 0; i < e1.conns->size(); ++i )
|
||||
if ( (*e1.conns)[i] != (*e2.conns)[i] )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if ( datalen >= 8 )
|
||||
{
|
||||
// Look for AYIAY tunnels
|
||||
u_char id_byte = data[0];
|
||||
u_char sig_byte = data[1];
|
||||
u_char next_hdr = data[3];
|
||||
|
||||
// identity length field is high bits of id_byte.
|
||||
// length in octets is 2 to the power of length field
|
||||
int id_len = (1 << (id_byte>>4));
|
||||
|
||||
// signature length field is high bits of sig_byte
|
||||
// length in octets 4 * length field
|
||||
int sig_len = 4*(sig_byte>>4);
|
||||
|
||||
datalen -= 8 + id_len + sig_len;
|
||||
data += 8 + id_len + sig_len;
|
||||
if ( datalen <= 0 )
|
||||
return 0;
|
||||
cand_ip_hdr = LookForIPHdr(data, datalen);
|
||||
if ( cand_ip_hdr )
|
||||
{
|
||||
hdr_len += 8 + id_len + sig_len;
|
||||
tunneltype = (cand_ip_hdr->IP4_Hdr()) ?
|
||||
BifEnum::Tunnel::IP4_IN_AYIAY : BifEnum::Tunnel::IP6_IN_AYIAY;
|
||||
}
|
||||
}
|
||||
if ( cand_ip_hdr )
|
||||
{
|
||||
TunnelInfo *tunnel_info = new TunnelInfo();
|
||||
tunnel_info->child = cand_ip_hdr;
|
||||
tunnel_info->parent.tunneltype = tunneltype;
|
||||
tunnel_info->SetParentIPs(ip_hdr);
|
||||
tunnel_info->SetParentPorts(uh);
|
||||
tunnel_info->hdr_len = hdr_len + cand_ip_hdr->HdrLen();
|
||||
return tunnel_info;
|
||||
};
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
IP_Hdr* TunnelHandler::LookForIPHdr(const u_char *data, int datalen)
|
||||
{
|
||||
IP_Hdr *cand_ip_hdr = 0;
|
||||
if (datalen < (int)sizeof(struct ip))
|
||||
return 0;
|
||||
|
||||
const struct ip *ip4 = (const struct ip*)(data);
|
||||
if ( ip4->ip_v == 4 )
|
||||
cand_ip_hdr = new IP_Hdr((const struct ip*)ip4, false);
|
||||
else if ( ip4->ip_v == 6 && (datalen > (int)sizeof(struct ip6_hdr)) )
|
||||
cand_ip_hdr = new IP_Hdr((const struct ip6_hdr*)data, false, datalen);
|
||||
|
||||
if ( cand_ip_hdr )
|
||||
{
|
||||
switch ( cand_ip_hdr->NextProto() ) {
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_ICMP:
|
||||
if ( (int)cand_ip_hdr->TotalLen() != datalen )
|
||||
{
|
||||
delete cand_ip_hdr;
|
||||
cand_ip_hdr = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
delete cand_ip_hdr;
|
||||
cand_ip_hdr = 0;
|
||||
break;
|
||||
} // end switch
|
||||
}
|
||||
return cand_ip_hdr;
|
||||
else
|
||||
return false;
|
||||
else
|
||||
if ( e2.conns )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,105 +3,109 @@
|
|||
#ifndef tunnelhandler_h
|
||||
#define tunnelhandler_h
|
||||
|
||||
#include <netinet/udp.h>
|
||||
#include "net_util.h"
|
||||
#include "IP.h"
|
||||
#include "config.h"
|
||||
#include "NetVar.h"
|
||||
#include "IPAddr.h"
|
||||
#include "Conn.h"
|
||||
#include "Sessions.h"
|
||||
#include "Val.h"
|
||||
#include <vector>
|
||||
|
||||
class TunnelParent {
|
||||
class EncapsulatingConn {
|
||||
public:
|
||||
TunnelParent()
|
||||
EncapsulatingConn()
|
||||
: src_port(0), dst_port(0), type(BifEnum::Tunnel::NONE) {}
|
||||
|
||||
EncapsulatingConn(const IPAddr& s, const IPAddr& d,
|
||||
BifEnum::Tunnel::Type t)
|
||||
: src_addr(s), dst_addr(d), src_port(0), dst_port(0), type(t) {}
|
||||
|
||||
EncapsulatingConn(const IPAddr& s, const IPAddr& d, uint16 sp, uint16 dp,
|
||||
BifEnum::Tunnel::Type t)
|
||||
: src_addr(s), dst_addr(d), src_port(sp), dst_port(dp), type(t) {}
|
||||
|
||||
EncapsulatingConn(const EncapsulatingConn& other)
|
||||
: src_addr(other.src_addr), dst_addr(other.dst_addr),
|
||||
src_port(other.src_port), dst_port(other.dst_port),
|
||||
type(other.type) {}
|
||||
|
||||
~EncapsulatingConn() {}
|
||||
|
||||
RecordVal* GetRecordVal() const;
|
||||
|
||||
friend bool operator==(const EncapsulatingConn& ec1,
|
||||
const EncapsulatingConn& ec2)
|
||||
{
|
||||
tunneltype = BifEnum::Tunnel::NONE;
|
||||
src_port = dst_port = 0;
|
||||
return ec1.type == ec2.type && ec1.src_addr == ec2.src_addr &&
|
||||
ec1.src_port == ec2.src_port && ec1.dst_port == ec2.dst_port;
|
||||
}
|
||||
|
||||
TunnelParent(TunnelParent *other)
|
||||
friend bool operator!=(const EncapsulatingConn& ec1,
|
||||
const EncapsulatingConn& ec2)
|
||||
{
|
||||
tunneltype = other->tunneltype;
|
||||
src_addr = other->src_addr;
|
||||
dst_addr = other->dst_addr;
|
||||
src_port = other->src_port;
|
||||
dst_port = other->dst_port;
|
||||
}
|
||||
|
||||
RecordVal* GetRecordVal() const
|
||||
{
|
||||
RecordVal *rv = new RecordVal(BifType::Record::Tunnel::Parent);
|
||||
TransportProto tproto;
|
||||
switch ( tunneltype ) {
|
||||
case BifEnum::Tunnel::IP6_IN_IP:
|
||||
case BifEnum::Tunnel::IP4_IN_IP:
|
||||
tproto = TRANSPORT_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
tproto = TRANSPORT_UDP;
|
||||
} // end switch
|
||||
|
||||
RecordVal* id_val = new RecordVal(conn_id);
|
||||
id_val->Assign(0, new AddrVal(src_addr));
|
||||
id_val->Assign(1, new PortVal(ntohs(src_port), tproto));
|
||||
id_val->Assign(2, new AddrVal(dst_addr));
|
||||
id_val->Assign(3, new PortVal(ntohs(dst_port), tproto));
|
||||
rv->Assign(0, id_val);
|
||||
rv->Assign(1, new EnumVal(tunneltype, BifType::Enum::Tunnel::Tunneltype));
|
||||
return rv;
|
||||
return ! ( ec1 == ec2 );
|
||||
}
|
||||
|
||||
IPAddr src_addr;
|
||||
IPAddr dst_addr;
|
||||
uint16 src_port;
|
||||
uint16 dst_port;
|
||||
BifEnum::Tunnel::Tunneltype tunneltype;
|
||||
BifEnum::Tunnel::Type type;
|
||||
};
|
||||
|
||||
class TunnelInfo {
|
||||
class Encapsulation {
|
||||
public:
|
||||
TunnelInfo()
|
||||
Encapsulation() : conns(0) {}
|
||||
|
||||
Encapsulation(const Encapsulation& other)
|
||||
{
|
||||
child = 0;
|
||||
hdr_len = 0;
|
||||
}
|
||||
~TunnelInfo()
|
||||
{
|
||||
if (child) delete child;
|
||||
if ( other.conns )
|
||||
conns = new vector<EncapsulatingConn>(*(other.conns));
|
||||
else
|
||||
conns = 0;
|
||||
}
|
||||
|
||||
void SetParentIPs(const IP_Hdr *ip_hdr)
|
||||
Encapsulation& operator=(const Encapsulation& other)
|
||||
{
|
||||
parent.src_addr = ip_hdr->SrcAddr();
|
||||
parent.dst_addr = ip_hdr->DstAddr();
|
||||
}
|
||||
void SetParentPorts(const struct udphdr *uh)
|
||||
{
|
||||
parent.src_port = uh->uh_sport;
|
||||
parent.dst_port = uh->uh_dport;
|
||||
if ( this == &other ) return *this;
|
||||
delete conns;
|
||||
if ( other.conns )
|
||||
conns = new vector<EncapsulatingConn>(*(other.conns));
|
||||
else
|
||||
conns = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
IP_Hdr *child;
|
||||
TunnelParent parent;
|
||||
int hdr_len;
|
||||
~Encapsulation() { delete conns; }
|
||||
|
||||
void Add(const EncapsulatingConn& c)
|
||||
{
|
||||
if ( ! conns )
|
||||
conns = new vector<EncapsulatingConn>();
|
||||
conns->push_back(c);
|
||||
}
|
||||
|
||||
size_t Depth() const
|
||||
{
|
||||
return conns ? conns->size() : 0;
|
||||
}
|
||||
|
||||
VectorVal* GetVectorVal() const
|
||||
{
|
||||
VectorVal* vv = new VectorVal(new VectorType(
|
||||
BifType::Record::Tunnel::EncapsulatingConn->Ref()));
|
||||
if ( conns )
|
||||
for ( size_t i = 0; i < conns->size(); ++i )
|
||||
vv->Assign(i, (*conns)[i].GetRecordVal(), 0);
|
||||
return vv;
|
||||
}
|
||||
|
||||
friend bool operator==(const Encapsulation& e1, const Encapsulation& e2);
|
||||
|
||||
friend bool operator!=(const Encapsulation& e1, const Encapsulation& e2)
|
||||
{
|
||||
return ! ( e1 == e2 );
|
||||
}
|
||||
|
||||
vector<EncapsulatingConn>* conns;
|
||||
};
|
||||
|
||||
class TunnelHandler {
|
||||
public:
|
||||
TunnelHandler(NetSessions *arg_s);
|
||||
~TunnelHandler();
|
||||
|
||||
// Main entry point. Returns a nil if not tunneled.
|
||||
TunnelInfo* DecapsulateTunnel(const IP_Hdr* ip_hdr, int len, int caplen,
|
||||
// need those for passing them back to NetSessions::Weird()
|
||||
const struct pcap_pkthdr* hdr, const u_char* const pkt);
|
||||
|
||||
protected:
|
||||
NetSessions *s;
|
||||
short udp_ports[65536]; // which UDP ports to decapsulate
|
||||
IP_Hdr* LookForIPHdr(const u_char *data, int datalen);
|
||||
TunnelInfo* HandleUDP(const IP_Hdr *ip_hdr, int len, int caplen);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
const ignore_keep_alive_rexmit: bool;
|
||||
const skip_http_data: bool;
|
||||
const parse_udp_tunnels: bool;
|
||||
const use_conn_size_analyzer: bool;
|
||||
const report_gaps_for_partial: bool;
|
||||
|
||||
|
@ -16,3 +15,4 @@ const Tunnel::decapsulate_ip: bool;
|
|||
const Tunnel::decapsulate_udp: bool;
|
||||
const Tunnel::udp_tunnel_ports: any;
|
||||
const Tunnel::udp_tunnel_allports: bool;
|
||||
const Tunnel::max_depth: count;
|
||||
|
|
|
@ -141,6 +141,17 @@ event dns_mapping_altered%(dm: dns_mapping, old_addrs: addr_set, new_addrs: addr
|
|||
## event.
|
||||
event new_connection%(c: connection%);
|
||||
|
||||
## Generated for a connection whose tunneling has changed. This could
|
||||
## be from a previously seen connection now being encapsulated in a tunnel,
|
||||
## or from the outer encapsulation changing. Note that the connection's
|
||||
## *tunnel* field is NOT automatically assigned to the new encapsulation value
|
||||
## internally after this event is raised.
|
||||
##
|
||||
## c: The connection whose tunnel/encapsulation changed.
|
||||
##
|
||||
## e: The new encapsulation.
|
||||
event tunnel_changed%(c: connection, e: encapsulating_conns%);
|
||||
|
||||
## Generated when reassembly starts for a TCP connection. The event is raised
|
||||
## at the moment when Bro's TCP analyzer enables stream reassembly for a
|
||||
## connection.
|
||||
|
|
|
@ -170,16 +170,18 @@ enum ID %{
|
|||
|
||||
|
||||
module Tunnel;
|
||||
enum Tunneltype %{
|
||||
enum Type %{
|
||||
NONE,
|
||||
IP6_IN_IP,
|
||||
IP4_IN_IP,
|
||||
IP6_IN_IP4,
|
||||
IP4_IN_IP4,
|
||||
IP6_IN_IP6,
|
||||
IP4_IN_IP6,
|
||||
IP6_IN_UDP,
|
||||
IP4_IN_UDP,
|
||||
IP6_IN_AYIAY,
|
||||
IP4_IN_AYIAY,
|
||||
%}
|
||||
|
||||
type Parent: record;
|
||||
type EncapsulatingConn: record;
|
||||
|
||||
module GLOBAL;
|
||||
|
|
22
testing/btest/Baseline/core.tunnels.ip-in-ip/output
Normal file
22
testing/btest/Baseline/core.tunnels.ip-in-ip/output
Normal file
|
@ -0,0 +1,22 @@
|
|||
new_connection: tunnel
|
||||
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
|
||||
encap: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
|
||||
new_connection: tunnel
|
||||
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
|
||||
encap: [[cid=[orig_h=feed::beef, orig_p=0/unknown, resp_h=feed::cafe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6], [cid=[orig_h=babe::beef, orig_p=0/unknown, resp_h=dead::babe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
|
||||
new_connection: tunnel
|
||||
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
|
||||
encap: [[cid=[orig_h=1.2.3.4, orig_p=0/unknown, resp_h=5.6.7.8, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP4]]
|
||||
new_connection: tunnel
|
||||
conn_id: [orig_h=70.55.213.211, orig_p=31337/tcp, resp_h=192.88.99.1, resp_p=80/tcp]
|
||||
encap: [[cid=[orig_h=2002:4637:d5d3::4637:d5d3, orig_p=0/unknown, resp_h=2001:4860:0:2001::68, resp_p=0/unknown], tunnel_type=Tunnel::IP4_IN_IP6]]
|
||||
new_connection: tunnel
|
||||
conn_id: [orig_h=10.0.0.1, orig_p=30000/udp, resp_h=10.0.0.2, resp_p=13000/udp]
|
||||
encap: [[cid=[orig_h=1.2.3.4, orig_p=0/unknown, resp_h=5.6.7.8, resp_p=0/unknown], tunnel_type=Tunnel::IP4_IN_IP4]]
|
||||
new_connection: tunnel
|
||||
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
|
||||
encap: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
|
||||
tunnel_changed:
|
||||
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
|
||||
old: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
|
||||
new: [[cid=[orig_h=feed::beef, orig_p=0/unknown, resp_h=feed::cafe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
|
BIN
testing/btest/Traces/tunnels/4in4.pcap
Normal file
BIN
testing/btest/Traces/tunnels/4in4.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tunnels/4in6.pcap
Normal file
BIN
testing/btest/Traces/tunnels/4in6.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tunnels/6in4.pcap
Normal file
BIN
testing/btest/Traces/tunnels/6in4.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tunnels/6in6-tunnel-change.pcap
Normal file
BIN
testing/btest/Traces/tunnels/6in6-tunnel-change.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tunnels/6in6.pcap
Normal file
BIN
testing/btest/Traces/tunnels/6in6.pcap
Normal file
Binary file not shown.
BIN
testing/btest/Traces/tunnels/6in6in6.pcap
Normal file
BIN
testing/btest/Traces/tunnels/6in6in6.pcap
Normal file
Binary file not shown.
30
testing/btest/core/tunnels/ip-in-ip.test
Normal file
30
testing/btest/core/tunnels/ip-in-ip.test
Normal file
|
@ -0,0 +1,30 @@
|
|||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6in6.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in4.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/4in6.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/4in4.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6-tunnel-change.pcap %INPUT >>output 2>&1
|
||||
# @TEST-EXEC: btest-diff output
|
||||
|
||||
event new_connection(c: connection)
|
||||
{
|
||||
if ( c?$tunnel )
|
||||
{
|
||||
print "new_connection: tunnel";
|
||||
print fmt(" conn_id: %s", c$id);
|
||||
print fmt(" encap: %s", c$tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
print "new_connection: no tunnel";
|
||||
}
|
||||
}
|
||||
|
||||
event tunnel_changed(c: connection, e: encapsulating_conns)
|
||||
{
|
||||
print "tunnel_changed:";
|
||||
print fmt(" conn_id: %s", c$id);
|
||||
if ( c?$tunnel )
|
||||
print fmt(" old: %s", c$tunnel);
|
||||
print fmt(" new: %s", e);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue