Skip to content

Commit fe79254

Browse files
committed
Merge branch 'udp-sink' into develop
2 parents e2fa53e + 7dbe748 commit fe79254

34 files changed

+2709
-139
lines changed

doc/CollectorDoc.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,11 @@ intercept targets across them.
117117

118118

119119
### Nokia Mirror Configuration
120+
NOTE: Nokia mirrored traffic can also be handled using a UDP Sink (see below
121+
for more information).
122+
120123
If you are using OpenLI to translate the intercept records produced by
121-
Alcatel-Lucent devices into ETSI-compliant output, any collectors that
124+
Nokia / Alcatel-Lucent devices into ETSI-compliant output, any collectors that
122125
are expected to receive mirrored copies of the Nokia intercept records need
123126
to be able to identify which packets are encapsulated records to be
124127
translated.
@@ -133,6 +136,9 @@ Note that in this context, the sink refers to the destination IP address
133136
and port of the mirrored Nokia traffic.
134137

135138
### Juniper Mirror Configuration
139+
NOTE: JMirror traffic can also be handled using a UDP Sink (see below
140+
for more information).
141+
136142
If you are using Juniper Packet Mirroring (a.k.a. JMirror) to mirror intercepted
137143
traffic into an OpenLI collector, you will need to configure OpenLI with the
138144
IP address and port that the mirrored traffic is being sent to so that the
@@ -186,6 +192,40 @@ are not spoofed (maybe because the SIP is being generated from inside your
186192
network) and you are unable to include any of the more reliable fields in
187193
your SIP traffic.
188194

195+
### UDP Sinks
196+
Many networking equipment vendors offer a limited lawful interception
197+
capability on their devices that can siphon copies of traffic for a
198+
particular network user (i.e. an intercept target) to a pre-defined destination.
199+
These streams typically encapsulate the captured traffic in IP/UDP, and may
200+
also include a small shim header at the beginning of the datagram payload.
201+
202+
OpenLI collectors can act as the destination for these UDP streams, whereby
203+
the collector can be configured to listen on certain UDP ports for datagrams.
204+
We call these IP/port pairs "UDP sinks". Then, in the intercept configuration,
205+
tell OpenLI which UDP sinks will be the recipients of traffic for the target
206+
user. Finally, when you configure the mirroring on your router/device, set the
207+
destination to be the IP address and UDP port that you designated as the
208+
corresponding sink on your collector.
209+
210+
The OpenLI collector will then assume any traffic delivered to it on the
211+
IP address and UDP port of the specified sink(s) must belong to the target
212+
of the associated intercept, and automatically intercept and encode each
213+
packet accordingly.
214+
215+
Important notes:
216+
* each UDP sink on a collector can only be associated with at most one
217+
intercept at a time, but you may have multiple sinks attached to a single
218+
intercept (e.g. to handle cases where inbound and outbound traffic were
219+
mirrored separately).
220+
* UDP sinks currently only support IP intercepts, not VoIP or email
221+
intercepts.
222+
* vendmirrorid configuration is not required for IP intercepts that use UDP
223+
sinks, and in fact no checking of the intercept ID in the post-UDP shim
224+
is performed at all.
225+
* UDP sinks without an attached intercept will remain idle until an
226+
intercept is assigned to it via the provisioner.
227+
228+
189229
### Configuration Syntax
190230
All config options aside from the input configuration are standard YAML
191231
key-value pairs, where the key is the option name and the value is your chosen
@@ -303,6 +343,17 @@ elements:
303343
* ip -- the IP address of the device that received the mirrored traffic
304344
* port -- the port that the sink was listening on for mirrored traffic
305345

346+
---
347+
348+
UDP sinks are defined a YAML sequence with a key of `udpsinks:`. Each sequence
349+
item describes a single listening UDP sink instance and must contain the
350+
following three key-value elements:
351+
* listenaddr -- the IP address of the interface to consume UDP mirror traffic on
352+
* listenport -- the port number to receive UDP mirror traffic on
353+
* identifier -- an identifier string that is unique to this particular
354+
collector (in case another collector is also listening on the
355+
exact same IP address and port).
356+
306357

307358
### Email interception options
308359

doc/ProvisionerDoc.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,13 @@ used to identify the intercept target.
242242
* Static IPs -- if the target has a static IP (range), you can use this
243243
parameter to tell OpenLI which IPs belong to the target.
244244

245+
Alternatively, you can configure your OpenLI collectors to act as the
246+
recipient for one of the vendor mirrored streams over UDP. If you wish to use
247+
this approach, you can tell OpenLI which of the collector UDP sinks you intend
248+
to use as a destination for the mirrored stream(s) and any packets that
249+
arrive at those UDP sinks will be de-encapsulated and intercepted as part of
250+
this IP intercept.
251+
245252
If you are relying solely on the User as the target identification method, you
246253
will need to ensure that the OpenLI collectors receive a copy of all RADIUS
247254
traffic relating to the subscribers whose IP traffic will be passing that
@@ -639,21 +646,60 @@ Optional key-value elements for an IP intercept are:
639646
mirroring platform for the target user.
640647
(only for re-encoding ALU or JMirror intercepts as
641648
ETSI)
649+
* `udpsinks` -- the set of collector UDP sinks that should be
650+
used to intercept mirrored traffic for this
651+
intercept.
652+
653+
`udpsinks:` are expressed as a YAML list: one list item per UDP sink that
654+
you want to attach to this intercept. Each list item is a YAML map containing
655+
the following key-value elements:
656+
657+
* `collectorid` -- the collector instance that the UDP sink is
658+
running on. This should match the `identifier`
659+
field in the corresponding UDP sink configuration
660+
on that collector.
661+
* `listenaddr` -- the IP address that the UDP sink is listening on.
662+
* `listenport` -- the UDP port that the UDP sink is listening on.
663+
* `sessionid` -- the session ID (also known as CIN) to use when
664+
encoding traffic intercepted via this UDP sink.
665+
Only applies if the encapsulation format does
666+
not include a session ID already.
667+
* `direction` -- if the encapsulation format does not include
668+
a direction tag on each mirrored packet, packets
669+
received by this UDP sink will be tagged as
670+
travelling in this direction relative to the
671+
intercept target. Possible values are `from`,
672+
`to`, and `both` -- default is `both`.
673+
* `encapsulation` -- the format of the post-UDP shim header that
674+
precedes the mirrored packet content (if any).
675+
Supported values are `raw` (i.e. no shim),
676+
`jmirror`, `nokia` and `cisco`. Default is `raw`.
677+
* `sourcehost` -- if set, the UDP sink will only intercept packets
678+
that originate from this source IP address. May
679+
be set to `any` to allow any source IP (be
680+
careful!).
681+
* `sourceport` -- if set, the UDP sink will only intercept packet
682+
that have a source port matching this number.
683+
May be set to 0 to allow any source port
684+
number (be careful!).
685+
686+
642687
* `staticips` -- a list of IP ranges that are known to have been
643688
assigned to the target.
644689

645690
`staticips:` are expressed as a YAML list: one list item per IP range that
646691
is associated with the target. Each list item is a YAML map containing the
647692
following key-value elements:
648693

649-
* `iprange` -- the IP range, expressed in CIDR notation. If a
694+
* `iprange` -- the IP range, expressed in CIDR notation. If a
650695
single address (i.e. no subnet) is given, a /32
651696
or /128 mask will be added automatically.
652-
* `sessionid` -- the session ID (also known as CIN) to associate
697+
* `sessionid` -- the session ID (also known as CIN) to associate
653698
with intercepts for this address range. See
654699
example config for more information about the
655700
meaning of this field.
656701

702+
657703
---
658704
A VOIP intercept must contain the following key-value elements:
659705

doc/exampleconfigs/collector-example.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,24 @@ x2x3inputs:
192192
- listenaddr: 10.230.1.1
193193
listenport: 25000
194194
certfile: /etc/openli/openli-collector-crt.pem
195+
196+
197+
# List of IP/port pairs to act as sinks for intercepted traffic sent by
198+
# vendor equipment using the vendor's custom UDP-encapsulated format.
199+
#
200+
# Note: identifier should be unique to this collector, as it is used to
201+
# disambiguate between UDP sinks on other collectors that might just also
202+
# happen to be using the same RFC1918 addresses for UDP sinks.
203+
udpsinks:
204+
- identifier: collector01
205+
listenaddr: 10.230.1.200
206+
listenport: 17870
207+
208+
- identifier: collector01
209+
listenaddr: 10.230.1.200
210+
listenport: 17871
211+
212+
- identifier: collector01
213+
listenaddr: 10.230.1.200
214+
listenport: 17872
215+

doc/exampleconfigs/running-intercept-example.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,50 @@ ipintercepts:
148148
agencyid: "Police" # ID of agency to send intercept to
149149
accesstype: "LAN" # Access tech used by the target to access IP
150150

151+
# This intercept is also consuming a FlowTapLite packet stream into an
152+
# ETSI-compliant intercept. In this case, we are using a UDP sink instance
153+
# on the collector to act as the destination for the mirrored stream.
154+
# Because the mirrored stream is being sent to the collector directly, we
155+
# do not need to provide the intercept ID using `vendmirrorid`, as we
156+
# can safely assume that any UDP packets arriving on the UDP sink from the
157+
# designated source belong to the intercept we are configuring here.
158+
# FlowTapLite does not include a post-UDP shim in its encapsulation, so
159+
# we configure it as `raw` in this case. Nokia, Juniper Mirroring, and Cisco
160+
# are all also supported encapsulation formats.
161+
162+
- liid: OAPP9321HN # LIID, should be provided by requesting agency
163+
authcountrycode: NZ # Authorisation country code
164+
deliverycountrycode: NZ # Delivery country code
165+
user: "lexluthor" # Username identifying the target in your AAA
166+
mediator: 6001 # ID of the mediator to send intercept via
167+
agencyid: "Spooks" # ID of agency to send intercept to
168+
accesstype: "ADSL" # Access tech used by the target to access IP
169+
udpsinks:
170+
- collectorid: "collector01" # The collector where this sink is running
171+
listenaddr: "10.230.1.200" # The IP address where the sink is listening
172+
listenport: 17870 # The UDP port where the sink is listening
173+
encapsulation: "raw" # No shim header to strip
174+
direction: "to" # Tag all traffic on this sink as "to the
175+
# intercept target"
176+
sessionid: 1200 # Use 1200 as the CIN for this intercept
177+
sourcehost: "10.230.1.1" # Only intercept packets coming from this IP
178+
sourceport: 9999 # Only intercept packets where the source
179+
# port is this number
180+
- collectorid: "collector02" # The collector where this sink is running
181+
listenaddr: "10.230.1.200" # The IP address where the sink is listening
182+
listenport: 17872 # The UDP port where the sink is listening
183+
encapsulation: "raw" # No shim header to strip
184+
direction: "from" # Tag all traffic on this sink as "from the
185+
# intercept target"
186+
sessionid: 1200 # Use 1200 as the CIN for this intercept
187+
sourcehost: "10.230.1.1" # Only intercept packets coming from this IP
188+
sourceport: 8888 # Only intercept packets where the source
189+
# port is this number
190+
191+
# ^ Note that some encapsulation formats include a session ID and/or direction
192+
# in their shim headers -- in those cases, OpenLI will use the values
193+
# derived from the headers rather than those provided in the configuration here.
194+
151195
# This intercept will attempt to intercept all IP traffic for a mobile
152196
# phone user, using GTPv2 packets to detect the start and end of the target's
153197
# sessions. Note that the accesstype must be set to "mobile". The user field

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ openlicollector_SOURCES=collector/collector.c \
9090
collector/sip_worker_redirection.c \
9191
collector/sip_worker_redirection.h \
9292
collector/x2x3_ingest.c collector/x2x3_ingest.h \
93-
collector/x2x3_cond_attrs.c \
93+
collector/x2x3_cond_attrs.c collector/udp_sink_worker.c \
9494
$(PLUGIN_SRCS)
9595

9696
openlicollector_LDADD = @ADD_LIBS@ -L$(abs_top_srcdir)/extlib/libpatricia/.libs

src/collector/alushim_parser.c

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ static inline uint32_t alushim_get_interceptid(alushimhdr_t *aluhdr) {
6060
static inline int alushim_get_direction(alushimhdr_t *aluhdr) {
6161

6262
uint32_t intid = ntohl(aluhdr->interceptid);
63-
6463
/* In ETSI, 0 = from target, 1 = to target, 2 = unknown */
6564
/* In ALU, 0 = ingress (from subscriber), 1 = egress (to subscriber) */
6665

@@ -83,38 +82,24 @@ static inline int alushim_get_direction(alushimhdr_t *aluhdr) {
8382
return 2;
8483
}
8584

86-
int check_alu_intercept(colthread_local_t *loc,
87-
libtrace_packet_t *packet, packet_info_t *pinfo,
88-
coreserver_t *alusources, vendmirror_intercept_list_t *aluints) {
85+
uint8_t *decode_alushim_from_udp_payload(uint8_t *payload, uint32_t plen,
86+
uint32_t *cin, uint8_t *dir, uint32_t *shimintid, uint32_t *bodylen) {
87+
8988

90-
coreserver_t *cs;
91-
vendmirror_intercept_t *alu, *tmp;
92-
vendmirror_intercept_list_t *vmilist;
9389
uint16_t ethertype;
9490
alushimhdr_t *aluhdr = NULL;
95-
uint32_t rem = 0, shimintid, cin;
9691
void *l3, *l2;
92+
uint32_t rem = plen;
9793

98-
if ((cs = match_packet_to_coreserver(alusources, pinfo, 1)) == NULL) {
99-
return 0;
100-
}
101-
102-
/* Extract the intercept ID, direction and session ID */
103-
aluhdr = (alushimhdr_t *)get_udp_payload(packet, &rem, NULL, NULL);
94+
aluhdr = (alushimhdr_t *)payload;
10495
if (!aluhdr || rem < sizeof(alushimhdr_t)) {
105-
return 0;
96+
return NULL;
10697
}
10798

108-
shimintid = alushim_get_interceptid(aluhdr);
109-
110-
/* See if the intercept ID is in our set of intercepts */
111-
HASH_FIND(hh, aluints, &shimintid, sizeof(shimintid), vmilist);
112-
if (vmilist == NULL) {
113-
return 0;
114-
}
99+
*shimintid = alushim_get_interceptid(aluhdr);
115100

116101
/* Strip the extra headers + shim */
117-
l2 = ((char *)aluhdr) + sizeof(alushimhdr_t);
102+
l2 = ((uint8_t *)aluhdr) + sizeof(alushimhdr_t);
118103
rem -= sizeof(alushimhdr_t);
119104

120105
/* TODO add support for layer 3 only intercepts? */
@@ -139,28 +124,67 @@ int check_alu_intercept(colthread_local_t *loc,
139124
continue;
140125
case TRACE_ETHERTYPE_ARP:
141126
/* Probably shouldn't be intercepting ARP */
142-
return 0;
127+
return NULL;
143128
case TRACE_ETHERTYPE_IP:
144129
case TRACE_ETHERTYPE_IPV6:
145130
break;
146131
default:
147-
return 0;
132+
return NULL;
148133
}
149134
break;
150135
}
151136

152137
if (!l3 || rem == 0) {
153138
logger(LOG_INFO,
154-
"Warning: unable to find IP header of ALU-intercepted packet from mirror (ID: %u)",
155-
cs->serverkey, shimintid);
156-
return -1;
139+
"Warning: unable to find IP header of ALU-intercepted packet from mirror (ID: %u)", *shimintid);
140+
return NULL;
141+
}
142+
143+
/* Use the session ID from the shim as the CIN */
144+
*cin = ntohl(aluhdr->sessionid);
145+
*dir = alushim_get_direction(aluhdr);
146+
*bodylen = rem;
147+
return l3;
148+
}
149+
150+
int check_alu_intercept(colthread_local_t *loc,
151+
libtrace_packet_t *packet, packet_info_t *pinfo,
152+
coreserver_t *alusources, vendmirror_intercept_list_t *aluints) {
153+
154+
coreserver_t *cs;
155+
vendmirror_intercept_t *alu, *tmp;
156+
vendmirror_intercept_list_t *vmilist;
157+
uint32_t rem = 0, shimintid, cin, bodylen;
158+
void *l3;
159+
uint8_t *payload = NULL;
160+
uint8_t direction;
161+
162+
if ((cs = match_packet_to_coreserver(alusources, pinfo, 1)) == NULL) {
163+
return 0;
164+
}
165+
166+
/* Extract the intercept ID, direction and session ID */
167+
payload = get_udp_payload(packet, &rem, NULL, NULL);
168+
if (!payload || rem < sizeof(alushimhdr_t)) {
169+
return 0;
170+
}
171+
172+
l3 = decode_alushim_from_udp_payload(payload, rem, &cin, &direction,
173+
&shimintid, &bodylen);
174+
if (!l3) {
175+
return 0;
176+
}
177+
178+
/* See if the intercept ID is in our set of intercepts */
179+
HASH_FIND(hh, aluints, &shimintid, sizeof(shimintid), vmilist);
180+
if (vmilist == NULL) {
181+
return 0;
157182
}
158183

159184
/* Direction 0 = ingress (i.e. coming from the subscriber) */
160185

161186
/* Use the session ID from the shim as the CIN */
162187
/* TODO double check that this will be available in the RADIUS stream */
163-
cin = ntohl(aluhdr->sessionid);
164188

165189
HASH_ITER(hh, vmilist->intercepts, alu, tmp) {
166190
if (pinfo->tv.tv_sec < alu->common.tostart_time) {
@@ -173,8 +197,7 @@ int check_alu_intercept(colthread_local_t *loc,
173197

174198
/* Create an appropriate IPCC and export it */
175199
if (push_vendor_mirrored_ipcc_job(loc->zmq_pubsocks[0], &(alu->common),
176-
trace_get_timeval(packet), cin, alushim_get_direction(aluhdr),
177-
l3, rem) == 0) {
200+
trace_get_timeval(packet), cin, direction, l3, bodylen) == 0) {
178201
/* for some reason, we failed to create or send the IPCC to
179202
* the sequencing thread? */
180203

src/collector/alushim_parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "coreserver.h"
3232
#include "intercept.h"
3333

34+
uint8_t *decode_alushim_from_udp_payload(uint8_t *payload, uint32_t plen,
35+
uint32_t *cin, uint8_t *dir, uint32_t *shimintid, uint32_t *bodylen);
3436
int check_alu_intercept(colthread_local_t *loc,
3537
libtrace_packet_t *packet, packet_info_t *pinfo,
3638
coreserver_t *alusources, vendmirror_intercept_list_t *aluints);

0 commit comments

Comments
 (0)