Engage LMR Interoperability - rallytac/pub GitHub Wiki
The Vocality LMR gateway has a number of options available for traffic management, streaming, and the like. In this section, we'll be exploring how to use engagebridged
as a bridge between Vocality RTP streams and those of Engage.
Here's the basic idea:
A: is the wireless connection from a handheld radio to a donor radio connected to the Vocality gateway.
B: is the wired audio connection from the donor radio to the gateway
C: is the bi-directional RTP stream between the gateway and engagebridged
D: is the bi-directional RTP stream between EBS and Engage endpoints
A
|. . . . . . . .|
| |
--------- --------- B --------------- C ------
| Radio | | Donor |----| Vocality GW |-----------| EBS |
--------- --------- --------------- -------
|
| D
|
-------------------
| Engage Endpoint |
-------------------
What we're going to do is setup engagebridged
to, quite literally, bridge traffic between (C) and (D). We're going to do this by having engagebridged
bridge between what it sees as two raw Engage groups/channels. We're not going to engagebridged
process the contents of the RTP packets. Rather, we're simply going to have it forward traffic from (C) to (D) (and the other way of course).
The first thing we're going to do is define the configuration for (C) - which we're going essentially view as a trunk connection to the Vocality box. This trunk is configured as a simple UDP traffic stream with no encryption or any other fancy stuff. Also, we're going to use unicast addresses for this thing - the rx
and tx
elements are key here.
Notice how in the configuration below we have an rx
address of 192.168.1.182
. This is the IP addresses of the machine running engagebridged
. The tx
address (192.168.1.19
) is the IP address of the Vocality gateway. Both are using port 15000
but that's just coincidental - they can be different.
So, what this tells engagebridged
to do is to listen for incoming traffic on UDP port 15000
on the NIC of the bridging machine. It also tells engagebridged
to send outgoing traffic to 192.168.1.19
.
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3, <---- Type 3 is `raw`
"name": "Vocality Trunk",
"rx": { "address": "192.168.1.182", "port": 15000 }, <--- The engagebridged machine
"tx": { "address": "192.168.1.19", "port": 15000} <---- The Vocality box
}
Next, we need an Engage group that users are actually going to use. For this example we're going to have a group named Moto APX
(because the radio system we're using here is a Motorola APX P25 system). This group is a run-of-the-mill Engage group with encryption and multicast addresses.
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 3, <---- Type 3 is `raw`
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 }, <---- Plain old multicast
"tx": { "address": "239.119.138.104", "port": 52296 } <---- Plain old multicast
}
So, now we have what we need for engagebridged
to link Vocality Trunk
to Moto APX
. When we put these two definitions into a bridge, engagebridged
will take any traffic it receives over unicast from Vocality and steer it out to the multicast where the Engage endpoints are residing. Similarly, any traffic produced by the Engage endpoints will be forwarded to the Vocality box.
What's super important here is that the group types are of type
3
. This tellsengagebridged
not to perform any processing on the contents of the packets - just to treat them as raw and forward those packets back and forth betweenVocality Trunk
andMoto APX
.
Our final bridging definition now looks as follows:
{
"bridges":[
{
"id":"{99840657-2d65-4217-af55-82287e1290a5}",
"groups":[
"{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"{a796871f-82e5-aad6-dc95-be325ccf09c3}"
]
}
],
"groups": [
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3,
"name": "Vocality Trunk",
"rx": { "address": "192.168.1.182", "port": 15000 },
"tx": { "address": "192.168.1.19", "port": 15000}
},
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 3,
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 },
"tx": { "address": "239.119.138.104", "port": 52296 }
}
]
}
OK, now that we have engagebridged
doing its thing and forwarding UDP packets between these two groups, we need to have a configuration for the Engage endpoints. Well, that's pretty straightforward. These guys don't need to know anything more than they have to join a group named Moto APX
and, magically, they will be chatting back and forth with the APX radios via the gateway. The group configuration for the endpoints looks as follows:
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 1, <---- Type 1 is `audio`
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 },
"tx": { "address": "239.119.138.104", "port": 52296 },
"txAudio": { "encoder": 1, "framingMs": 20 } <---- Match the vocoder on the Vocality box
}
There are two important differences here in the definition for Moto APX
for the Engage endpoints vs the definition provided to engagebridged
.
First, the group type is 1
- meaning that it is an audio group. Remember, engagebridged
doesn't want to care about what type of group it is - just the addressing and (in this case) the encryption key for Moto APX
.
Second, we have a txAudio
element in the JSON telling Engage to use an audio encoder of type 1
(which means G.711 uLaw) and have a RTP framing size of 20ms. We've set this configuration because we've configured the Vocality talkgroup for G.711 uLaw with 20ms RTP timings.
Once you have this in place, audio flows nicely and everyone's happy.
Now, let's say we're not going to use multicast for Moto APX
but, instead, go via a Rallypoint - as below:
A
|. . . . . . . .|
| |
--------- --------- B --------------- C -------
| Radio | | Donor |----| Vocality GW |-----------| EBS |
--------- --------- --------------- -------
|
| D
|
--------------
| Rallypoint |
--------------
|
|
-------------------
| Engage Endpoint |
-------------------
That's super easy to do. All we need to do is tell engagebridged
to have it's version of Moto APX
go via a Rallypoint. And also tell the same to the Engage endpoints.
The modified engagebridged
configuration would then look as follows - assuming our Rallypoint is at demo.rallytac.com
:
{
"bridges":[
{
"id":"{99840657-2d65-4217-af55-82287e1290a5}",
"groups":[
"{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"{a796871f-82e5-aad6-dc95-be325ccf09c3}"
]
}
],
"groups": [
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3,
"name": "Vocality Trunk",
"rx": { "address": "192.168.1.182", "port": 15000 },
"tx": { "address": "192.168.1.19", "port": 15000}
},
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 3,
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 },
"tx": { "address": "239.119.138.104", "port": 52296 },
"rallypoints":[ {"host": {"address":"demo.rallytac.com", "port":7443}} ] <---- Go via an RP
}
]
}
The configuration for the Engage endpoints would be:
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 1,
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 },
"tx": { "address": "239.119.138.104", "port": 52296 },
"txAudio": { "encoder": 1, "framingMs": 20 },
"rallypoints":[ {"host": {"address":"demo.rallytac.com", "port":7443}} ] <---- Go via an RP
}
So far we've done some cool stuff and things are looking good. All's been done under the assumption that the Vocality gateway and EBS are on the same network. But, what if EBS is located somewhere else - perhaps in a datacenter?
Well, we could simply keep what we have described above but, if so do so, the packet stream between the gateway and EBS is in the clear and if those packets cross a public network such as the Internet, we've got all kinds of security problems. Or we could just have simple routing issues with trying to get packets bouncing across what will invariably be different networks.
The answer to this is to tunnel the traffic from the gateway to EBS. (We don't need to worry about the link between EBS and the Rallypoint as that link won't have routing issues and, because its already secured by Engage, we won't have any security concerns.)
There are two ways to do this tunneling. The first is to use a GRE tunnel between the two. That will solve routing issues but, because GRE is not secured, our packets will still be in the clear. Now, we can get around that by having something else in the mix like a VPN connection but that makes our life more difficult. The best approach is to use a IPsec tunnel which will take care of the routing concerns and also encrypt our traffic.
A notional tunneled configuration looks as follows with only the C
link changing to use a tunnel C-t
and a far-end tunnel termination point (in this case, a Cisco router):
C-t
A **************************
|. . . . . . . .| * *
| | * ----------
--------- --------- B --------------- C | | C -------
| Radio | | Donor |----| Vocality GW |----------- | Router | --------| EBS |
--------- --------- --------------- | | -------
* ---------- |
* * |
*************************** | D
|
|
|
--------------
| Rallypoint |
--------------
|
|
-------------------
| Engage Endpoint |
-------------------
Before we get into that, though, there's something that we need to be keenly aware of - TTL!
A packet's Time-To-Live (TTL) is a numeric field in the packet that is decremented each time the packet is about to cross a network boundary. When that value reaches 0, the packet no longer propagates. This is important for networks because we don't want packets flying around all over the place wreaking havoc. Engage, being super-conservative in its approach to such things defaults to a TTL of 1
- meaning that packets transmitted by Engage will, by default, stay inside the same subnet. So, if we just use the default TTL of 1
, packets going through tunnels are almost guaranteed not to make it to their destination. In the case of our example configuration, if the group configuration for Vocality Trunk
is left at 1
, packets from EBS will not traverse the tunnel and therefore not reach the gateway. The result is that we'll have a one-way audio issue.
We solve this really easily by telling the Engage Engine inside EBS to use something other than the default TTL of 1
for the Vocality trunk. This is done in the optional txOptions
element of the group configuration. For example:
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3,
"name": "Vocality Trunk",
"rx": { "address": "192.168.1.182", "port": 15000 },
"tx": { "address": "192.168.1.19", "port": 15000},
"txOptions": { "ttl":64 } <---- Cater for tunnel routing
}
We've used
64
as a TTL here but, frankly, a TTL of2
would suffice for our example.
So, our modified bridging configuration for engagebridged
is as follows (just the addition of the TTL stuff for the Vocality trunk):
{
"bridges":[
{
"id":"{99840657-2d65-4217-af55-82287e1290a5}",
"groups":[
"{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"{a796871f-82e5-aad6-dc95-be325ccf09c3}"
]
}
],
"groups": [
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3,
"name": "Vocality Trunk",
"rx": { "address": "192.168.1.182", "port": 15000 },
"tx": { "address": "192.168.1.19", "port": 15000},
"txOptions": { "ttl":64 } <---- Add this!
},
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 3,
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rx": { "address": "239.119.138.104", "port": 52296 },
"tx": { "address": "239.119.138.104", "port": 52296 },
"rallypoints":[ {"host": {"address":"demo.rallytac.com", "port":7443}} ]
}
]
}
For the real-world tunneling use-case, we're going to describe, we need to make some adjustments to the IP addressing from the notional examples so far and deal with the real(ish) IP addresses we used to set this stuff up.
We had the radios and Vocality gateway in Seattle. The gateway has Internet connectivity and establishes its tunnel to a Cisco router in a data center in South Carolina. EBS also resides in that datacenter.
Then, the Rallypoint we're using is hosted in Amazon Web Services and is reachable at demo.rallytac.com
.
The Engage Endpoint - actually multiples of them - are scattered around the Internet. Their locations and IP addresses are not important here.
What is important is this stuff:
- Vocality Gateway:
192.168.1.19
(internal IP on the Seattle network) - Router:
104.x.x.x
(you didn't think that we'd actually provide that IP address publicly, did you ?) - EBS:
192.168.2.71
(internal IP on the South Carolina network)
An annotated drawing, then, of this setup is as follows:
C-t
A **************************
|. . . . . . . .| * *
| | * -----------
--------- --------- B --------------- C | | C ---------------
| Radio | | Donor |----| Vocality GW |----------- | Router | --------| EBS |
--------- --------- -192.168.1.19-- | | --192.168.2.71-
* -104.x.x.x- |
* * |
*************************** | D
|
|
|
--------------------
| Rallypoint |
-demo.rallytac.com-
|
|
-------------------
| Engage Endpoint |
-------------------
Our bridging configuration for engagebridged
looks as follows:
{
"bridges":[
{
"id":"{99840657-2d65-4217-af55-82287e1290a5}",
"groups":[
"{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"{a796871f-82e5-aad6-dc95-be325ccf09c3}"
]
}
],
"groups": [
{
"id": "{1a9ef287-21d1-e811-62f1-94dd9c4757a1}",
"type": 3,
"name": "Vocality Trunk",
"rx": { "address": "192.168.2.71", "port": 15000 }, <-- EBS machine in South Carolina
"tx": { "address": "192.168.1.19", "port": 15000}, <-- Vocality in Seattle
"txOptions": { "ttl":64 }
},
{
"id": "{a796871f-82e5-aad6-dc95-be325ccf09c3}",
"type": 3,
"name": "Moto APX",
"cryptoPassword": "18ECC15EBC43D587653D1FA503AD8E29686488B7AE515E02AE262E0F2523F891",
"rallypoints":[ {"host": {"address":"demo.rallytac.com", "port":7443}} ]
}
]
}
You'll notice that
Moto APX
for this configuration doesn't haveTX
andRX
multicast addressing. That's because we're not using multicast forMoto APX
- its all going through the Rallypoint mesh.
As mentioned, GRE is not ideal because its not encrypted but, let's talk about it anyway. To use a GRE tunnel, we need to tell the Vocality gateway to establish a tunnel and then forward its traffic to that tunnel. The nice folks over at Vocality made that super easy.
Item | Value | Comments |
---|---|---|
Tunnel Name | Test GRE Tunnel | |
Tunnel Type | GRE | |
This Endpoint's Tunnel IP Address | eth1 192.168.1.19 | |
Peer Endpoint's Tunnel IP Address | 104.x.x.x | |
Tunnel Interface IP Address | 172.16.0.2/24 | Networking in South Carolina |
Tunnel Subnets' IP Version | IPv4 | |
Source Network | Any | |
Destination Network | 192.168.2.0/24 |
- Add a tunnel interface be added to the router.
- In tunnel configuration mode specify the IP address and subnet mask of the tunnel interface.
- Next set the source IP address of the tunnel interface.
- Then specify the destination IP for the tunnel. Typically, this will be the destination NAT (public IP) address of the Vocality box.
- Finally, you'll need a static route to send traffic from your EBS devices’ network to the network that the Vocality box resides on. This example illustrates the command syntax. You'll need to change the addresses and interfaces to match your network and devices.
interface tunnel 0
ip address 172.16.0.1 255.255.255.0
tunnel source Gig0/0/0
tunnel destination 75.123.12.145
no shut
ip route 192.168.10.0 255.255.255.0 172.16.0.2
IPsec was a little hairier but not too bad:
Item | Value | Comments |
---|---|---|
Tunnel Name | Test IPsec Tunnel | |
Tunnel Type | IPsec | |
IPsec Mode | Tunnel | |
(TRC ) Tunnel Endpoints' IP Version |
IPv4 | |
(TRC ) This Endpoint's Tunnel IP Address |
eth1 192.168.1.19 | |
(TRC ) Peer Endpoint's Tunnel IP Address |
104.x.x.x | |
(TRC ) Tunnel Subnets' IP Version |
IPv4 | |
(TRC ) Source Network |
192.168.1.0/24 | |
(TRC ) Destination Network |
192.168.2.0/24 | |
(IKE ) IKE Version |
v1 | |
(IKE ) Authentication Algorithm |
SHA2 256-bit | |
(IKE ) Encryption Algorithm |
AES 256-bit | |
(IKE ) DH Group |
modp2048(group14) | |
(IKE ) Restrict IKE to Selection |
No | |
(IKE ) IKEv1 Connection Mode |
Main | |
(AUTH ) Authentication Method |
PSK | |
(AUTH ) Node Secret Key |
******** | Defined on the router |
(AUTH ) Peer Endpoint's Node ID |
No value needed | |
(DSC ) Authentication Algorithm |
SHA2 256-bit | |
(DSC ) Encryption Algorithm |
AES 256-bit | |
(DSC ) DH Group for PFS |
PFS deactivated | |
(DSC ) Restrict Data Security to Selection |
No | |
(RECOVERY ) Dead Peer Action |
None | |
(RECOVERY ) Peer Close Action |
None |
- Define your crypto ISAKMP policy to use AES encryption, pre-shared key authentication, and a Diffie-Helman Group.
- Next create the ISAKMP pre-shared key and address of the remote destination IP for the tunnel. Typically, this will be the destination NAT (public IP) address of the Vocality box. This key needs to be at least 8 characters.
- Your next step will be to add a crypto IPsec transform set to use AES encryption. The transform set should use tunnel mode encryption.
- You'll then need to add an access-list that will be used to define what traffic will be included in the IPsec tunnel.
- Next, configure a crypto map that will tie all the previous configuration together before applying it to an interface.
- Finally apply the crypto map to your public-facing interface. The tunnel will not come up until there is interesting traffic so a simple ping should do it.
crypto isakmp policy <number>
encr aes
authentication pre-share
group 14
crypto isakmp key <key value> address <public ip address of Vocality>
crypto ipsec transform-set <name> esp-aes esp-sha-hmac
mode tunnel
ip access-list extended <name>
permit ip <src ip net> <src wild card mask> <dst ip net> <dst wildcard mask>
crypto map <name> <number> ipsec-isakmp
set peer <public ip address of Vocality>
set transform-set <name of transform set configured above>
match address <name of ip access-list configured above>
interface <public interface name>
crypto map <crypto map name configured above>