-
Notifications
You must be signed in to change notification settings - Fork 6
Engage Bridging Service
Engage's groups are channels of communications between teams of users carrying out specific job functions. Most of the time these folks' communications stay within the group they're in. However, there are times when its necessary to combine groups so that those users can communicate with each other. For this, we use the Engage Bridging Service, or EBS for short.
This process of "bridging" is sometimes also called "patching" or "cross-banding". They all basically mean the same thing.
EBS is a "headless" application that runs on a variety of platforms and has a number of similarities to Rallypoints. So if you're already familiar with Rallypoints you're well on your way to understanding and using EBS.
The major difference between EBS and Rallypoints, though, is that while Rallypoints are fundamentally designed to route packets between users, EBS' function is to route traffic between Engage groups (channels). Now, because we're talking about interfacing with Engage groups, EBS actually uses the Engage Engine as the means to do this bridging.
Technically, EBS is really just a headless Engage-powered application that instructs it's underlying Engage Engine to perform the bridging operations needed.
- Bridging & Patching With Engage
- Prerequisites
- Installation
- Operation
- Configuration
- Licensing & the Device Identifier
- Bridging
- Monitoring
- Examples
EBS runs on Linux, MacOSX, and Microsoft Windows at this time - with Linux being the preferred platform. For Linux, you'll need either a branch of Red Hat or Debian. For Red Hat, we recommend CentOS 7 or higher while for Debian, we recommend Ubuntu 18 or higher or Debian 9 or higher.
While EBS is not a server in the sense that it has clients connecting to it over the network, it does listen for TCP connections for environments where systems such as load balancers or other network infrastructure systems check on the availability or health of the process by opening TCP connections. If you are operating in such an environment, make sure that the port you configure for this purpose is opened. This "health check port" does not typically have traffic going back and forth - most health checkers simply open the connection and either close it right away or keep it open for a period of time. Make sure that if you enable this, DoS detection logic in firewalls and/or your operating system may need to be tuned to handle fast connect/disconnect operations from the health checker.
Also, because EBS uses an Engage Engine underneath, and that Engine is processing traffic on Engage groups that may be using UDP; you're going to need UDP ports opened for the Engine to use. This is the case for groups whose traffic traverse the network using IP multicast addresses. Make sure your EBS machine has its firewall setup for multicast RX and TX and that the necessary UDP ports are opened for inbound and outbound traffic.
For groups that use Rallypoint unicast links, you'll need to make sure the machine where EBS is installed can reach out to the addresses for Rallypoints it may need to connected to.
EBS is most easily installed by using the package manager for your operating system using the appropriate installation package provided by Rally Tactical Systems. These packages will install the necessary binaries, factory default certificates, and a baseline configuration. They will also setup EBS to operate as a daemon (background service) that starts at operating system boot time. This is done using systemd on Linux platforms and launchd on OSX.
For Red Hat distributions:
sudo yum install <ebs_package_file>.rpm
For Debian-based distributions:
sudo apt install <ebs_package_file>.deb
For OSX, open the <ebs_package_file>.dmg file and double-click the install link icon.
If you need to conduct a more sophisticated installation procedure, need to run the EBS process manually (not as a background daemon for example), or generally just have more complex needs for your EBS setup, you will need to install the relevant items manually. This is a pretty straightforward process so it shouldn't be too difficult.
Let's get going by assuming we're not yet configuring (or perhaps never configuring) to run as a background service.
- Place the
engagebridged
executable anywhere you'd like. This can be in a custom directory or in a standard executable location such as /usr/sbin. As long as the code can be executed from that location, you're good to go. - Place the security-related certificate and key files in a location where EBS can read them. These include the file containing EBS' certificate, and the file containing that certificate's private key. (Be sure that this location is strictly only accessible to EBS and any other authorized applications and/or users.)
- Finally, place your configuration file in a location where EBS can read it. By default, EBS looks for
/etc/engagebridged/engagebridged_conf.json
for its configuration.
Note: If you do place your configuration file in a different location or give it a different name, then you'll need to tell EBS to use that file. Do this with the -cfg
command-line parameter. For example:
$ engagebridged -cfg:my_custom_configuration.json
Now that you've got everything installed manually, you may still want to setup EBS to run as a daemon on startup and avail yourself of the services offered by the operating system. In a Linux environment, this is easily done by setting up the required configuration for systemd or the more old-style init.rc
method. Refer to your operating system instructions on how to do this.
Setting up the service for systemd-like operation on Mac OSX systems is a little more tricky. Your best bet is to refer to Apple's documentation at :
Once the code is installed and configured (more on that below), your EBS installation should just start up and be ready to process bridging configurations. If the code is running as a daemon under systemd
, you can use the standard systemd
-related methods of interacting with your daemon. Such as:
Operation | Command Line |
---|---|
Starting the service | sudo systemctl start engagebridged |
Stopping the service | sudo systemctl stop engagebridged |
Restarting the service | sudo systemctl restart engagebridged |
Query service status | sudo systemctl status engagebridged |
Watch the log | sudo journalctl -f -u engagebridged |
If you are running engagebridged
from the command line, you will see the log output in the terminal window. To stop the process, simply press Ctrl-C
or use the kill
command to stop the process.
You can monitor EBS in a variety of ways.
The simplest is by viewing the output log displayed in a terminal window either directly from the process or, if running as a daemon, using journalctl as described above.
The output log is also sent to the standard operating system logging subsystem. This would be syslog
on Linux systems and Apple's high-performance logger on OSX systems.
All of these log messages follow the syslog
standard format including the timestamp of the message and severity level. These outputs can then be analyzed by log-processing tools such as SolarWinds, PaperTrial, and so on for purposes of generating alerts to administrative personnel or automated systems.
Note: If viewing directly in the terminal that has ANSI color-coding capabilities; the log lines are colorized to make it easier to spot
In addition to the log, EBS can be configured to produce a status report on a periodic basis. This report is in JSON format and written to a file specified in the configuration at an interval you decide (we recommend 30 seconds intervals, with 5 seconds as the minimum). This JSON file can then be analyzed to determine the health of service.
Here's an example:
{
"id":"demorp0001", // The EBS process' instance identifier
"ts":119790397, // UTC UNIX timestamp (number of seconds
// since Jan 1, 1970) of when this report
// was produced
"uptime": 149663, // Number of seconds the process has been
// up
"systemCpuLoad":4.86, // Percentage CPU load of the machine instance
// hosting EBS
"healthChecks":
{
"count":0, // Number of TCP health checks made by a
// load-balancer or other network
// infrastructure entity
"rate":0.0, // Health checks per second
"rateEma":0.0 // Exponential moving average of health
// checks per second
},
"queue":
{
"avgExecNanos":60567, // Average number of nanoseconds a queue
// operation takes to execute
"maxExecNanos":7733634, // Longest number of nanoseconds a queue
// operation took to execute
"minExecNanos":0, // Least number of nanoseconds a queue
// operation took to execute
"lowPriorityQueueDepth":0, // Current number of operations waiting in the
// low priority queue
"lowPriorityQueueMaxDepth":0 // Maximum number of operations in the low-
// priority queue
"lowPriorityQueueFailures":0, // Number of operations denied entrance to
// the low-priority queue due to load
"normalPriorityQueueDepth":0, // Current number of operations waiting in the
// normal-priority queue
"normalPriorityQueueMaxDepth":1, // Maximum number of operations in the normal-
// priority queue
"normalPriorityQueueFailures":0, // Number of operations denied entrance to
// the normal-priority queue due to load
"ops":
{
"count":3360, // Total number of process
// lifetime operations
"rate":145.0, // Operations per second
"rateEma":15.002 // Exponential moving average
// of operations per second
},
"spuriousWakeups":114, // Number of queue wakeups that
// resulting in a no-op
"wakeUps":3346 // Total number of queue wakeups
}
"bridges":
{
"count":2, // Number of configured bridges
"detail": // Detailed list of bridges
[
{
"id":"1+2", // ID of the bridge
"name":"Test #1", // Name of the bridge
"state":0, // State indicator (see table below)
"groups": // List of IDs of groups in the bridge
[
"{ac197b82-0f45-86e8-bf53-02ceea2e977c}",
"{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}"
],
},
{
"id":"2+3",
"name":"Test #2",
"state":0,
"groups":
[
"{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}",
"{d54e51d0-1cc1-e130-f955-35b8f763cf9b}"
]
}
]
},
"groups":
{
"count":5, // Number of configured
// bridges
"detail": // Detailed list of
// groups
[
{
"id":"{ac197b82-0f45-86e8-bf53-02ceea2e977c}", // Group ID
"name":"Group 1", // Group name
"state":0 // Group state
// (see table below)
},
{
"id":"{d54e51d0-1cc1-e130-f955-35b8f763cf9b}",
"name":"Group 3",
"state":0
},
{
"id":"{eef5ae84-bc51-941b-43e8-fde506415ef3}",
"name":"Group 4",
"state":0
},
{
"id":"{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}",
"name":"Group 2",
"state":0
}
]
}
}
}
Code | Description |
---|---|
-1 | An error has occurred |
0 | No status available |
1 | Bridge is operational |
2 | Bridge is in the process of being created |
3 | Bridge has been deleted |
Code | Description |
---|---|
-1 | An error has occurred |
0 | No status available |
1 | Group is operational |
2 | Group is in the process of being created |
3 | Group is attempting to join the network |
4 | Group has left the network |
5 | Group is temporarily disconnected |
6 | Group has been deleted |
Now that you've seen how to install the software, operate it, and monitor it; it's a good idea to get into how you actually configure it. Here goes ...
We want to take a moment to stress something.
It is vitally important that you safeguard access to your X.509 certificate files and, in particular, private keys. While certificates are ultimately public, private keys are just that - PRIVATE. Make sure that only your EBS instance and other authorized users and entities have access to these files.
As you've probably guessed by now, EBS is configured using a JSON file that it looks for at /etc/engagebridged/engagebridged_conf.json
or one that is specified at the command line with the -cfg
argument.
Here's an example:
{
"id":"bridgeserver01"
"certStoreFileName":"/etc/engagebridged/engagebridged.certstore",
"certStorePasswordHex":"",
"bridgingConfigurationFileName":"bcfg01.json",
"bridgingConfigurationFileCommand":"",
"bridgingConfigurationFileCheckSecs":5,
"statusReport":
{
"enabled":true,
"fileName":"/tmp/${id}_status.json",
"intervalSecs":30,
"includeGroupDetail":true,
"includeBridgeDetail":true,
"includeBridgeGroupDetail":true,
"runCmd":""
},
"externalHealthCheckResponder":
{
"listenPort":0,
"immediateClose":true
},
"enginePolicy":
{
"licensing":{
"entitlement": "<an_entitlement_is_required_to_run>",
"key":"<a_license_key_is_required_to_run>",
"activationCode":"<an_activation_code_may_be_required>",
"manufacturerId":"<add_your_manufacturer_id_if_you_have_one>"
},
"networking":
{
"defaultNic":""
},
"security":
{
"certificate":
{
"certificate":"@certstore://rtsFactoryDefaultEngage",
"key":"@certstore://rtsFactoryDefaultEngage"
}
}
}
}
Let's go through these in detail...
The root of the configuration has a number of elements that are key to the operation of EBS.
-
id
is a unique string you assign that identifies this instance of EBS. If you leave this element blank, EBS will generate a value automatically. We recommend, though, that you always set this value. The computer's host name works well here or, in the situations like Docker containers or cloud instances, the ID assigned by the container/cloud system. -
certStoreFileName
is the path to the certificate store to be used. See Engage Security for more information. -
certStorePasswordHex
is the hex representating of the password/passphrase protecting the certificate store. See Engage Security for more information. -
bridgingConfigurationFileName
is the name of the file where EBS should load details about the bridges it needs to manage. (More on this later.) -
bridgingConfigurationFileCommand
is an operating system command to run instead of polling the file named bybridgingConfigurationFileName
. Its important that this command must send its output to STDOUT and is JSON formatted as per the bridging configuration file. Also, this command must execute and complete as quickly as possible (less than 30 seconds) or EBS will attempt to terminate that process.
IMPORTANT:
bridgingConfigurationFileName
andbridgingConfigurationFileCommand
are mutually exclusive. If you set values for both elements, EBS will fail to configure and abort operation.
-
bridgingConfigurationFileCheckSecs
is the interval (in seconds) that EBS will check the file defined bybridgingConfigurationFileName
for changes or, ifbridgingConfigurationFileCommand
is defined, the interval at which to run the command.
This object contains details about the status report generation as described earlier.
-
enabled
set to true to enable - default is false. -
fileName
is the full path name of the JSON file where the status report should be written. If you leave this element blank, no status report will be produced. Include "${id}
" to have the EBS ID inserted into the name. -
intervalSecs
is the interval (in seconds) at which the status report should be produced. While you can set this as low as 1 second, we recommend no lower than 5 seconds. 30 seconds is generally a reasonable value. -
includeGroupDetail
indicates whether details about groups is to be included - default is false. -
includeBridgeDetail
indicates whether details about groups is to be included - default is false. -
includeBridgeGroupDetail
set to true to include details groups within bridges ifincludeBridgeDetail
is true - default is false.
Contains information for external health checker interoperability. "Health checkers" are generally network entities such as load balancers and network management systems that monitor EBS to determine their health and availability. In the most setups, though, health checkers do simple things like open a TCP connection to the process to verify that it's operational. And they generally close that connection right away.
-
listenPort
is the TCP port that EBS should listen on for health check TCP connections. Set it to whatever is required/desired by your health checker. -
immediateClose
indicates whether to immediately close the connection. (Some health checkers require this.)
Contains the baseline policy to be provided to the Engage Engine underneath EBS.
-
entitlement
is the product-level entitlement you may be using for EBS. -
key
is the license key required for operation of EBS. EBS will not function without a valid license key. -
activationCode
may be needed if the license key stipulates that an activation code is needed. -
manufacturerId
is manufacturer-level identifier you may be using for your own licensing system.
-
defaultNic
is the name of the default network interface card the Engage Engine should use.
-
certificate
is either the PEM content of the Engine's X.509 certificate or an alias for the certificate from the certificate store. -
key
is either the PEM content of the Engine's X.509 certificate private key or an alias for the certificate private key from the certificate store.
Licensing via activation codes is tied to the device that EBS runs on, meaning that any process to generate the activation code will require the device's identifier. You can have EBS print out the the device ID generated by the underlying Engage Engine by using the -getdeviceid
command-line parameter. When you do this, EBS will simply print the device identifier to the standard output and terminate.
For example:
$ engagebridged -getdeviceid
B87CD0E8C3130041A9107AB1DC0905C8
$
Here, B87CD0E8C3130041A9107AB1DC0905C8
is the device identifier. Because EBS only prints this single string to standard output, scripts and other automated processes can capture this string and use it to pass on to personnel or systems that generate activation codes.
Now that EBS is running it needs to be provided with information telling it what groups you wanted bridged together. This is done by providing configuration (in JSON format of course) that is likely to change over time.
Now, because this information changes, it is not contained in the core configuration of EBS. Rather, it comes from an external source. That source is, as mentioned, in JSON format and can either be read from a file or be the output of a process launched by EBS on a period basis.
To keep things simple for our example we're going to assume we have 4 Engage groups (Group 1
, Group 2
, Group 3
, and Group 4
) that we need bridged. We're going to put Group 1
and Group 2
in a bridge of their own. Group 3
and Group 4
will be in another bridge. We'll call these bridges, respectively, Bridge A
and Bridge B
.
Graphically depicted, we want something like this:
Group 1 ----------------------
|
+----------+
| Bridge A |
+----------+
|
Group 2 ----------------------
Group 3 ----------------------
|
+----------+
| Bridge B |
+----------+
|
Group 4 ----------------------
This way, when Bridge A
is active, people on Group 1
and Group 2
can talk to each other. The same applies for Group 3
and Group 4
when Bridge B
is active.
Now, there's some information the underlying Engage Engine needs to make this happen.
First, it needs to know how each of these groups is configured so that it can attach to the network infrastructure to access the packets produced by the groups. It also needs to know other information such as the encryption information for each group where applicable.
Engage needs to know the encryption info because its highly unlikely that different groups will have the same encryption and, therefore, if packets from
Group 1
are sent on toGroup 2
usingGroup 1
's encryption; entities onGroup 2
will not be able to process them. Basically, when sending packets from one group to another, Engage needs to decrypt from the source and re-encrypt for the target. You get the idea.
In addition to having information about the groups participating in the bridges, what's also needed is the makeup of the bridges themselves - obviously. This is actually quite straightforward. All that's needed is for a bridge configuration to list of group IDs to be placed in that bridge.
Here's what our bridge configuration looks like (and we're keeping it simple for now by not including Rallypoint information on a group-by-group basis):
{
"bridges":[
{
"id":"{316b92fa-d928-410b-ba25-2de5dec3f32d}", // ID of the bridge
"name":"Bridge A", // It's name
"groups":
[
"{ac197b82-0f45-86e8-bf53-02ceea2e977c}", // The ID for Group 1 (see below)
"{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}" // The ID for Group 2 (see below)
]
},
{
"id":"{7ecd2f58-790e-435f-b487-ee4e7588fa26}", // ID of the bridge
"name":"Bridge B", // It's name
"groups":
[
"{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}", // The ID for Group 3 (see below)
"{d54e51d0-1cc1-e130-f955-35b8f763cf9b}" // The ID for Group 4 (see below)
]
}
],
"groups":
[
// A group definition. Notice we only need the most basic information for the group
// configuration - being ID, type (3 is the only type support right now), crypto password, and
// networking information. Also, see how we have txOptions defined for Group 1. This tell EBS
// to set an IP Time To Live value of 42 on the packets it transmits for Group 1. Also, it tell
// EBS to assign a transmission priority of 4 ("voice") to those transmitted packets.
//
// txOptions will override the defaults that EBS uses - which is a priority of 4 and a TTL of 1.
{
"id": "{ac197b82-0f45-86e8-bf53-02ceea2e977c}",
"name": "Group 1",
"type": 3,
"cryptoPassword": "DA8E1B5DFE107CC8544984D780B320355CA62D583D62A8AB9E0D9E02BB74C0B2",
"rx":
{
"address": "239.148.27.191",
"port": 13334
},
"tx":
{
"address": "239.148.27.191",
"port": 13334
},
"txOptions":{
"priority":4,
"ttl":42
}
},
// Another group defintion
{
"id": "{f38a3c6f-201c-648e-4dbe-1e8565afe0c5}",
"name": "Group 2",
"type": 3,
"cryptoPassword": "AADDA91BC10F5F304B2631233F99245C742B2F04396D1D4F68B507AC0A215724",
"rx":
{
"address": "239.16.68.135",
"port": 13336
},
"tx":
{
"address": "239.16.68.135",
"port": 13336
}
},
// And another
{
"id": "{d54e51d0-1cc1-e130-f955-35b8f763cf9b}",
"name": "Group 3",
"type": 3,
"cryptoPassword": "ABF706C812EED032F47E56E55DCB0A5BDC7E8CC3A984CCA7E8708D3E6915C76F",
"rx":
{
"address": "239.189.61.254",
"port": 13338
},
"tx":
{
"address": "239.189.61.254",
"port": 13338
}
},
// And our last one, which, by the way, is going via a Rallypoint
// rather than being directly connected to the network
{
"id": "{eef5ae84-bc51-941b-43e8-fde506415ef3}",
"name": "Group 4",
"type": 3,
"cryptoPassword": "62022266D3E22F85B7E23E66EA6C639C0FBD96C37FEF40DD51A06E2119882F44",
"rallypoints": [
{
"host": {
"address": "myrp.testdomain.com",
"port": 7443
}
}
]
}
]
}
Pretty straight forward, no!? All that EBS needs is the most basic information about a group and it'll be in a position to bridge it with other groups.
Notice that we've not included information such as audio CODEC. That's because EBS does not process audio or any other type of traffic content - it processes packets! In fact, EBS uses an Engage feature known as "raw groups" where the content of the packets on a group is not processed by the Engine. Rather, on a raw group, packets are simply passed along with only rules governing encryption and transport for the group being taken into account. This design makes for an extremely fast bridging service with almost zero overhead that scales extremely well even on very low-end hardware.
That said, it is important that you include the
type
field in your JSON configuration and set it to a value of3
(which means a "raw" group). Even though EBS only processes raw groups, the way in which those are loaded from JSON require thetype
field to be present.
Also notice that a group's configuration can indicate that it is to be connected via a Rallypoint connection - as shown for "Group 4" in the example. That way you can use EBS to bridge not just being groups, but also between transports.
What's also very important to understand is that the bridging configuration provided to EBS is the entire bridging configuration for the EBS instance. In other words, EBS always wants to "see" what the entire bridging setup looks like, not just changes to a previous configuration. While this imposes quite a bit of work on EBS' side to internally determine changes to what's currently active, it means that systems (such as configuration managers and apps) need not concern themselves with the complexities of change management for bridging.
EBS is pretty smart when it sets up bridges to make sure that traffic flows properly to the groups its needed. Let's say we do something like this where we add Bridge C
that bridges Group 2
and Group 3
.
Group 1 ----------------------
|
+----------+
| Bridge A |
+----------+
|
Group 2 ----------------------
Group 3 ----------------------
|
+----------+
| Bridge B |
+----------+
|
Group 4 ----------------------
Group 2 ----------------------
|
+----------+
| Bridge C |
+----------+
|
Group 3 ----------------------
What'll happen here is that all traffic will flow everywhere. For example: traffic from Group 1
will go to Group 2
via Bridge A
as expected. But, because Group 2
and Group 3
are bridged, the traffic from Group 1
will also flow to Group 3
. And, because Group 3
and Group 4
are bridged, the traffic from Group 1
will also flow onward to Group 4
. That's cool - no worries there.
But ... what happens if we now, in Bridge D
, link Group 4
and Group 1
? Like so:
Group 1 ----------------------
|
+----------+
| Bridge A |
+----------+
|
Group 2 ----------------------
Group 3 ----------------------
|
+----------+
| Bridge B |
+----------+
|
Group 4 ----------------------
Group 2 ----------------------
|
+----------+
| Bridge C |
+----------+
|
Group 3 ----------------------
Group 4 ----------------------
|
+----------+
| Bridge D |
+----------+
|
Group 1 ----------------------
This will create a loop because the traffic from Group 1
will come back to itself via its link in Bridge D
with Group 4
. And that's bad!
Or, imagine you try to bridge Group 1
with itself in a bridge. That's also bad!
Or, let's say you only have one group in a bridge. There's little point in that frankly.
Or ... you try to place WAAAY too many groups in a bridge. We certainly can't tell you what's right for your environment but need to draw the line somewhere. So we've limited the number of groups in a bridge to 128. Hopefully that's enough for you.
EBS checks for this kind of stuff when it processes a bridging configuration and will flat-out fail the creation of the bridge that will cause the loop. So you're pretty safe. But it doesn't solve the problem of creating these kinds of loops across different instances of EBS. For example: you could have one instance of EBS (let's say in one datacenter) be configured for Bridge A
, Bridge B
, and Bridge C
. Then on another instance of EBS in an entirely different datacenter you have that instance configured for Bridge D
. Each EBS instance will, itself, see nothing wrong with it's own configuration but, systemwide you're going to have a loop on your hands. That's going to knock your network right out, trash the user experience, and generally create mayhem.
Bottom-line, be very careful when you're bridging using multiple instances of EBS. Bad things can happen very quickly with this kind of stuff if you're not super-careful.
Earlier on we discussed the status report produced by EBS which gives you a way to monitor what's going on in the guts of EBS. This report is in the form of JSON that is produced periodically which can then be processed by whatever monitoring systems you have in place.
There's a number of ways to monitor this kind of JSON output, and we'll continue to provide tools to help you along the way. Right now, there's a cool little Python script called ebm
(for Engage Bridge Monitor) that you can grab from the Rally Github public repository.
As we learn about use-cases that folks are interested in, we'll share what we're allowed to below.
So here's an interesting one ... a customer came to us and asked for a bridging setup where they'd like to bridge a stream of multicast RTP packets from a Land Mobile Radio (LMR) gateway to an Engage group. But ... the gateway is on a totally different network to the network that the Engage clients are on. So, what's being asked is that we not only bridge between the radio system (via the LMR Gateway), but also bridge between networking domains! With EBS (and Engage as its underlying Engine) its a breeze.
First, here's a rough visualization of the requirement: We have a network named 'a' on the left where the LMR gateway lives and is attached to the radio system. On the right we have another network named 'b' where the Engage clients are. In both of these domains we have IP multicast but we don't have a means for multicast to flow between the domains. (And we very specifically don't want multicast routing between these two domains for various logistical, technical, and security reasons.) Also, even if we could get the underlying network infrastructure to route the multicast, we don't want the LMR gateway's packets to be accessible to the Engage clients (we won't go into details why not - just trust us).
So, what we'll do is have EBS running on a computer (a Linux box in the case) which has two network interfaces. Tne first interface (n1) goes to the LMR gateway network (network a) while the second interface (n2) goes to the network b side. Then we need to congigure EBS with a bridge comprised of two groups - one pointed to the LMR gateway, the other to the Engage clients. The key here is that these groups will be bound to different network interfaces, thereby creating not just a bridge between the two groups but also a bridge between two network domains.
network a network b
+*************************************************************+ +***************************************+
* * * *
* +------------------+ +-------------+ +-------------------------------+ *
* | p25 radio system | <---> | lmr gateway | | ebs | *
* +------------------+ +-------------+ +- n1 ---------------- n2 --+ *
* ^ ^ * * ^ *
* | | * * | *
* v trunk v * * v p25 *
* ------------------------- * * ------------------------- *
* * * ^ ^ ^ *
+**************************************************************+ * | | | *
* v v v *
* c1 c2 c3 *
* *
+****************************************+
Here's what our configuration looks like. First, the minimalist 'core' configuration for EBS:
{
"id": "bridgeserver01",
"bridgingConfigurationFileName": "./bridging.json",
"certStoreFileName": "./all-rts-certs.certstore",
"enginePolicy": {
"licensing": {
"key": "<your license here>",
"entitlement": "<your entitlement here>"
}
}
}
Be aware that the core EBS confguration above is super-minimal to keep things simple. There's a lot more stuff that you could or might need to add but for purposes of this example we'll have our configurations be as simple as possible.
Next, here's the bridging configuration from the bridging.json
file:
{
"bridges": [
{
"id": "{a0e8996b-11c2-4b1d-b4e1-4944f181f7ee}",
"groups": [
"{fd5a4b9f-c8e9-4c49-baef-9f8f81fb723c}",
"{1778f110-6822-40a0-a1a1-626d87b09c31}"
]
}
],
"groups": [
{
"id": "{fd5a4b9f-c8e9-4c49-baef-9f8f81fb723c}",
"name": "Trunk",
"type": 3,
"interfaceName": "n1",
"rx": {
"address": "239.16.17.18",
"port": 15000
},
"tx": {
"address": "239.16.17.18",
"port": 15000
}
},
{
"id": "{1778f110-6822-40a0-a1a1-626d87b09c31}",
"name": "P25",
"type": 3,
"interfaceName": "n2",
"rx": {
"address": "239.119.138.104",
"port": 52296
},
"tx": {
"address": "239.119.138.104",
"port": 52296
}
}
]
}
This bridging configuration is pretty straightforward and follows the same idea from other examples in this Wiki. However, pay attention to the interfaceName
field in the group definitions. For the Trunk
group, we're telling EBS to use the n1
network interface; while for the P25
group we're telling EBS to use the n2
network interface.
Also, because we've stipulated type 3
for the groups, EBS won't be doing any processing of the packet payloads. Instead, its just going to forward the packets back and forth between the n1
and n2
interfaces for those groups.
NOTE: The
P25
group is the group that the Engage clients participate on so you need to make sure that your Engage clients have this group configured with the same multicast address and port as in the bridging configuration forP25
. Also, we've opted not to encrypt theP25
group in tius example for purposes of simplicity. But that's easy to do by adding thecryptoPassword
field to theP25
group.
Finally, here's what the P25
group needs to look like on the Engage client side. Of course, the multicast addresses and ports need to match the info from bridging.json
above.
Notice how the group's ID of {1778f110-6822-40a0-a1a1-626d87b09c31}
matches the ID of te P25
. While that's not necessary if we're only doing multicast, it becomes important if this group will be accessed via Rallypoints. So best to make sure you use the same ID.
The two most important elements to understand, though, is the type
and txAudio
items. While we specified a type
of 3
(raw) in the EBS bridging configuration; we specify a type
of 1
(audio) in the Engage client configuration. And, because of the audio group type, the Engage client will need to know how to transmit to that group. So we need a txAudio
element which, in this case, specifies G.711ulaw as the encoder (with a value of 1
). This is super important because EBS is just going to relay the packets from the Engage clients to the LMR gateway - without any processing such as transcoding. So the Engage clients need to transmit their packets in a format that the LMR gateway understands.
{
.
.
.
{
"id": "{1778f110-6822-40a0-a1a1-626d87b09c31}",
"name": "P25",
"type": 1,
"rx": {
"address": "239.119.138.104",
"port": 52296
},
"tx": {
"address": "239.119.138.104",
"port": 52296
},
"txAudio": {
"encoder": 1,
"framingMs": 20,
"maxTxSecs": 30
}
}
.
.
.
}
Now that we've covered the general operation of EBS and how to setup bridges, groups, and so on; it's time to look a little deeper at what's happening to those packets traversing the bridges we've described so far. And, so far, the answer is "nothing much". In fact, the only kind of processing done on packets being forwarded between groups in a bridge is the stripping of encryption from the source packets and re-encryption to the target group(s). And that only applies if they're even encrypted. If the groups are not encrypted, EBS is just forwarding the packet as-in with no modifications applied at all.
Now that's perfectly fine in many situations but there are situations where we want to perform some form of transformation on the stream of traffic in a bridge.
A great real-world example is where we want to bridge between traffic from a two-way radio system and a Engage-powered system on the enterprise network. The example we used earlier of bridging a P25 radio system to the enterprise is a good starting point.
If you'll remember, this is kind of we're looking at.
network a network b
+*************************************************************+ +***************************************+
* * * *
* +------------------+ +-------------+ +-------------------------------+ *
* | p25 radio system | <---> | lmr gateway | | ebs | *
* +------------------+ +-------------+ +- n1 ---------------- n2 --+ *
* ^ ^ * * ^ *
* | | * * | *
* v trunk v * * v p25 *
* ------------------------- * * ------------------------- *
* * * ^ ^ ^ *
+**************************************************************+ * | | | *
* v v v *
* c1 c2 c3 *
* *
+****************************************+
But let's get rid of some of those lines and other goop to make it a bit easier to look at. And, at the same time let's not tie ourselves to a channel named "p25" (which denotes the kinds of devices we're using) but rather to a group named "Rescue". Hence, on the "enterprise" side we'll have an Engage channel/group named Rescue and on the P25 radio system, we'll assume a particulalr talkgroup - let's say Talkgroup 1 - is designated as the Rescue talkgroup.
Finally, instead of having EBS connect on the enterprise side to IP multicast, let's rather have it going to a Rallypoint. Kinda like this:
enterprise network
+***************************************+
* *
+------------------+ +-------------+ +-------------------------------+ *
| p25 radio system | <---> | lmr gateway | | ebs | *
+------------------+ +-------------+ +- n1 ---------------- n2 --+ *
^ ^ * ^ *
| | * | *
v trunk v * | rescue *
------------------------- * v *
* +----------------------+ *
* | RP | *
* +----------------------+ *
* ^ ^ ^ *
* | | | *
* v v v *
* c1 c2 c3 *
* *
+****************************************+
The cool thing about just this simple change in thinking about naming the channel for the purpose it serves - that being to do with rescue operations - is a lot easier to understand that trying to remember something like "P25 Channel 1 is for rescue". Also, because we've removed the term "P25" from the group/channel's name; we disconnect ourselves from the fact that there's even a radio system connected to our enterprise system - or even that it's P25! So, if we decide at some point to change from using P25 radios to MANET radios, DMR, or some other kind of 3rd-party system on the "radio side", it doesn't affect our enterprise network users.
OK, that's it for the concept part. Let's look at that configuration. For purposes of this discussion, let's dispense with those hard-to-remember GUIDs and, rather, use something more readable. Frankly, Engage doesn't care what your group identifiers are - as long as they're unique. So you could use "{ac197b82-0f45-86e8-bf53-02ceea2e977c}", or "gov.state.wa.rescue", or "our-super-unique-rescue-group-id" as your group IDs - just be sure they're unique.
Our bridging configuration for this setup is shown below.
{
"bridges":[
{
"id":"Rescue-Bridge",
"name":"Bridge for Rescue traffic",
"groups":
[
"rescue-trunk",
"rescue"
]
}
],
"groups":
[
{
"id": "rescue-trunk",
"name": "Rescue Trunk To P25",
"type": 1,
"txAudio": {
"encoder":1,
"framingMs":20
},
"rx":
{
"address": "239.148.27.191",
"port": 13334
},
"tx":
{
"address": "239.148.27.191",
"port": 13334
},
"txOptions":{
"priority":4,
"ttl":64
}
},
{
"id": "rescue",
"name": "Rescue",
"type": 1,
"txAudio": {
"encoder":25,
"framingMs":60
},
"cryptoPassword": "62022266D3E22F85B7E23E66EA6C639C0FBD96C37FEF40DD51A06E2119882F44",
"rallypoints": [
{
"host": {
"address": "myrp.testdomain.com",
"port": 7443
}
}
]
}
]
}
There's two significant changes here that need to be understood in the group definitions.
-
The "type" field for the groups is now
1
(denoting "RTP audio media"), instead of3
("raw") as we had before. -
Given that we want EBS to process the groups as RTP media, we need to tell it what CODEC to use when transmitting. In the case of the
rescue-trunk
, we'll assume that the LMR gateway is configured to use G.711 ulaw at a framing size of 20 millisecons. Hence,rescue-trunk
's "encoder" is Engage CODEC1
- meaning G.711 ulaw - with a "framingMs" value of20
. On the enterprise group -rescue
, we've decided to go with Opus encoding at 16 kbps with a framing size of 60 ms. In Engage-speak, that is encoder25
with a framing milliseconds value of 60.
Check out the Engage encoder wiki for a list of CODECs and there Engage identifiers.
Finally, there's a change we need to make to EBS's core configuration - in engagebridged_conf.json
- being to tell it we want it to operate in payload processing mode.
{
"id":"bridgeserver01",
"mode":2, <---------- DESIGNATE PAYLOAD PROCESSING MODE!
"certStoreFileName":"/etc/engagebridged/engagebridged.certstore",
"certStorePasswordHex":"",
.
.
.
}
When we now fire up EBS, it'll connect over IP multicast to the LMR gateway, exchanging unencrypted G.711 ulaw traffic at 20ms intervals with the gateway and, therefore, the radio system. That traffic will be bridged to the enterprise group with the requisite transcoding between G.711 & OPUS, as well as re-framing of the packet sizes.
This business of re-framing is actually quite a big deal. Consider sending 20 ms of audio in a RTP packet... That equates to 50 packets per second. And each packet has a whole bumch of IP-related overhead in the form of packet headers. These are headers for RTP, UDP, IP, Ethernet, and so on. (Together, they're called the "IP Tax".) Now, nobody likes paying taxes so if we simply change the amount of audio in a packet - say from 20ms to 40ms, we're now going to send 25 packets per second instead of 50. That slashes our IP tax in half right away and saves a ton of bandwidth. If we send 60 ms packets, we're now only sending 16.67 packets per second - an even bigger tax reduction. Of course, larger packets mean more latency, but we're talking such small amounts that in a PTT environment, especially, it won't make any difference to users. In fact, Engage will allow you to go up to 120 ms per packet for Opus and you'll still have terrific performance and a greatly reduced bandwidth cost.
Sweet, huh!? This stuff is easy - right ?? Let's dig a little deeper and see if we can get ourselves into (and then out of) some trouble. The first issue is mismatched "RTP Payload Types".
We have to get a bit down in the weeds for this, but bear with us. It's not too bad.
The RTP standard specifies a field in the RTP header known as the "Payload Type". This is a 7-bit field that a receiver of the RTP packet can use to dynamically determine (or confirm) what type of media payload follows the RTP header. Now, when RTP came out, everyone figured that there couldn't possible be more media types that could fit into 7 bits. Furthermore, it seemed reasonable to have all the possible media types be registered with IANA on their RTP Payload Types (PT) for standard audio and video encodings page. Well ... RTP proved pretty popular and the sheer number of media types that people were using to transport via RTP grew like crazy, so the whole idea sorta fell flat, frankly. The result is that industry now has 17 IANA-defined media payload types (0-18 with some reserved) and then ranges up to a value of 127 which is little bit like the Wild West. So, for some CODECs (like G.711 ulaw) we have a predefined PT (0
in the case of G.711 ulaw), while the more modern stuff - say like Opus - there is no predefined PT value.
Now, in a signalled environment - like SIP or H.323 where two endpoints are setting up a conversation - the endpoints go through a handshaking step to agree on what CODEC they're going to use and what PT they're going to put in those RTP packets (if the CODEC's PT is not already predefined). But in an unsignalled environment (like what we have on our hands), there's no handshake. Instead, the receiver needs to "magically" know what the PT is. For example: let's say we receive an RTP packet with a PT value of 103
(that falls in IANA's "dynamic" range). What the hell does that mean??? We have no clue! Nobody does.
So, what we do in Engage is pretty much the same as everyone else does; and use whatever PT values we think is good and go forward from there. Sounds like a winning solution - right!? Well, it is if you're just in a pure Engage environment; or whatever other third-party environment you're in. But if you go between those environments, a PT value of 103
may mean Opus in one environment, and MELPe in another. That's a recipe for disaster if the systems aren't flexible enough to adapt or be adapted to that kind of interaction. Sadly, most systems are not that flexible.
But Engage is! Of course. I mean, REALLY, did you think we wouldn't be able to deal with this kind of thing? We're damn PROFESSIONALS around here at RTS ya know!