Initializing Project

This commit is contained in:
Cort Buffington 2013-06-27 16:15:54 -05:00
commit 9828205557
5 changed files with 734 additions and 0 deletions

44
IPSC Decoding.txt Normal file
View File

@ -0,0 +1,44 @@
Message Types: (1st byte in the payload)
REGISTRATION EXCHANGE
---------------------
PEER:
90 00 c8 32 68 65 00 00 e0 3c 04 03 04 02 0f 9b 2d d4 d3 af 36 da c2 fe
|--SRC ID-| |MODE| | FLAGS | |IPSC VER| |IPSC VER| |-1st 10 bytes of SHA-1 Hash-|
13120104
MASTER:
91 00 04 c2 c0 6a 00 00 80 5d 00 03 04 03 04 00 5c b8 4e e4 7e 44 b6 bb df dd
|--SRC ID-| |MODE| | FLAGS ||PEERS||IPSC VER| |IPSC VER| |-1st 10 bytes of SHA-1 Hash-|
312000
----
PEER:
96 00 c8 32 68 65 00 00 e0 3c 04 03 04 02 45 d0 a9 c9 07 5c 05 ad 50 67
|--SRC ID-| |MODE| | FLAGS | |IPSC VER| |IPSC VER| |-1st 10 bytes of SHA-1 Hash-|
13120104
MASTER:
97 00 04 c2 c0 6a 00 00 80 5d 04 03 04 02 32 c0 f5 d4 02 28 f5 13 48 22
|--SRC ID-| |MODE| | FLAGS | |IPSC VER| |IPSC VER| |-1st 10 bytes of SHA-1 Hash-|
312000
----
PEER:
92 00 c8 32 68 85 26 37 9b 06 93 9a af dd 08
|--SRC ID-| |-1st 10 bytes of SHA-1 Hash-|
MASTER: (This appears to be the peer list)
93 00 04 c2 c0 00 37 00 04 c2 c3 d1 72 71 e9 c3 5a 6a 00 04 c2 c4 6c c8 a4 3d c3 50 6a 00 04 c2 c5 44 67 16 bb c3 5c 6a 00 c8 32 65 a4 71 4c 0c c3 51 6a ...SHA-1 truncated
|TPE| |--SRC ID-| |-PEER ID-| |-PEER IP-| |PRT||MDE| |-PEER ID-| |-PEER IP-| |PRT||MDE| |-PEER ID-| |-PEER IP-| |PRT||MDE| |-PEER ID-| |-PEER IP-| |PRT||MDE|
13120104 3120103 209.114.113.233 50010 1&2 3120104 108.200.164.61 50000 1&2 3120105 68.103.22.187 50012 1&2 13120101 164.113.26.12 50001 1&2
91 0004c2c0 6a 0000805d0004 0401 0400 -- be 3c 5c 6a f6 11 8a c4 60 52
91 0004c2c0 6a 0000805d0004 0403 0400 -- be c9 27 21 8f 07 a2 23 3f 07
91 0004c2c0 6a 0000805d0004 0402 0400 -- 2e da 60 20 62 2d 6e b5 9e 6c

290
README.md Normal file
View File

@ -0,0 +1,290 @@
##PROJECT: Motorola IPSC Reverse Engineering.
**PURPOSE:** Troubleshooting IPSC performance issues/possibly building an application gateway to extend IPSC beyond 15 repeaters.
**IMPACT:** Potential concern from Motorla Solutions, as IPSC is a proprietary
**METHOD:** Reverse engineering by pattern matching and process of elimination
**PROPERTY:**
This work represents the author's interpretation of the Motorola(tm) MOTOTRBO(tm) IPSC protocol. It is intended for academic purposes and not for commercial gain. It is not guaranteed to work, or be useful in any way, though it is intended to help IPSC users better understand, and thus maintain and operate, IPSC networks. This work is not affiliated with Motorola Solutions(tm), Inc. in any way. Motorola, Motorola Solutions, MOTOTRBO, ISPC and other terms in this document are registered trademarks of Motorola Solutions, Inc. Other registered trademark terms may be used. These are owned and held by their respective owners.
**PRE-REQUISITE KNOWLEDGE:**
This document assumes the reader is familiar with the concepts presented in the Motorola Solutions(tm), Inc. MOTOTRBO(tm) Systems Planner.
**CONVENTIONS USED:**
When communications exchanges are described, the symbols "->" and "<-" are used to donote the *direction* of the communcation. For example, "PEER -> MASTER" indicates communcation from the peer to the master. For each exchange outlined, the initiator of the particular communcation will be on the left for the durration of the particular item being illustrated.
###CONNECTION ESTABLISHMENT AND MAINTENANCE
**CORE CONCEPTS:**
The IPSC system contains, essentially, two types of nodes: Master and Peer. Each IPSC network has exactly one master device and zero or more peers, recommended not to exceed 15. IPSC nodes may be a number of types of systems, such as repeaters, dispatch consoles, application software, etc. For example, the Motorola RDAC applicaiton acts as a peer in the IPSC network, though it doesn't operate as a repater. The IPSC protocol supports many possible node types, and only a few have be identified. This document currently only explores repeaters - both Master and Peer, and their roles in the IPSC network.
All IPSC communication is via UDP, and only the master need a static IP address. Masters will operate behind NATs. A single UDP port, specified in programming the IPSC master device must be mapped thorugh any NAT/stateful firewalls for the master, while peers require no special treatment.
All nodes in and IPSC network maintain communication with each other at all times. The role of the master is merely to coordinate the joining of new nodes to the IPSC network. A functional IPSC network will continue without its master, as long as no new nodes need to join (or existing nodes need to re-join after a communcaitons outage, etc.). This is one of the most important core concepts in IPSC, as it is central to the NAT traversal AND tracking of active peers.
Each peer will send keep-alives to each other peer in the IPSC network at an interval specified in the devices "firewall open timer". The elegantly simple, yet effective approach of IPSC, uses this keep-alive to both open, and keep open stateful firewall and NAT translations between peers. Since each device handles all communications from a single UDP port, when a device sends a keep-alive or a registration request to another device, the source-destination address/port tuple for that commonication is opened through stateful devices. The only requirement to maintain communication is that this timer be shorter than the UDP session timeout of network control elements (firewalls, packet shapers, NATs, etc.). Moreover, it does NOT appear that all devices in the IPSC require the same setting for this. Each device would appear to maintain its own set timing without interference from different interval settings on other nodes in the IPSC.
**KNOWN IPSC PACKET TYPES:**
The following sections of this document will include various packet types. This is a list of currently knwon types and their meanings. Note: The names are arbitrarily chosen with the intention of being descriptive, and each is defined by what they've been "observed" to do in the wild.
RDAC_CTL = 0x70 RDAC packets observed to use this type
GROUP_VOICE = 0x80 This is a group voice call
GROUP_DATA = 0x83 This is a group data call
PVT_DATA = 0x84 This is a private data call
RPT_WAKE_UP = 0X85 Wake up all repeaters (similar to subscriber DMR wake-up)
MASTER_REG_REQ = 0x90 Request registraiton with master
MASTER_REG_REPLY = 0x91 Master registration reply
PEER_LIST_REQ = 0x92 Request peer list from master
PEER_LIST_REPLY = 0x93 Master peer list reply
PEER_REG_REQ = 0x94 Request registration with peer
PEER_REG_REPLY = 0x95 Peer registration reply
MASTER_ALIVE_REQ = 0x96 Master keep alive reqeust (to maseter)
MASTER_ALIVE_REPLY = 0x97 Master keep alie reply (from master
PEER_ALIVE_REQ = 0x98 Peer keep alive request
PEER_ALIVE_REPLY = 0x99 Peer keep alive reply
**AUTHENTICATION:**
Most IPSC netowrks will be operated as "authenticated". This means that a key is used to create a disest of the packets exchanged in order to authenticate them. Each node in the IPSC must have the authentication key programed in order for the mechanism to work. The process is based on the SHA-1 digest protocol, where the "key" is a 20 byte hexideximal *string* (if a shorter key is programmed, leading zeros are used to create a 20 byte key). They IPSC payload and the key are used to create the digest, of which only the most significant 10 bytes are used (the last 10 are truncated). This digest is appended to the end of the IPSC payload before transmission. An example is illustrated below:
IPSC Registration Packet Digest
90000000016a000080dc04030400 b0ec45f4c3f8fb0c0b1d
**CONNECTION CREATION:**
The IPSC network truely "forms" when the first peer registers with the master. All peers register with the master in the same way, with a slight variation from the first peer. The registration and peer maintenance process is oulined below:
* Peer Initiates connection to IPSC:
PEER -> MASTER (peer sends a registration request to the master)
PEER <- MASTER (master sends a registration reply)
PEER -> MASTER (peer sends keep alive request to the master)
PEER <- MASTER (peer recieves keep alive response from the master)
if the registration response indicated there is more than one peer (which would have been the peer) in the IPSC network...
PEER -> MASTER (peer sends peer-list request to master)
PEER <- MASTER (master sends a list of all peers in the IPSC by radio ID, their IP addresses and UDP ports)
PEER -> (ALL) (peer begins exchanging keep alives with all other nodes in the IPSC based on the programmed interval)
* Master updates the peer list to all nodes when there is a change:
MASTER -> (PEERS) (Master sends update list to each peer)
PEERn -> NEW PEER (each peer 'n' sends keep alive requests to the new peer)
* ALL nodes continue sending/receiving keep alives to maintain the IPSC
**PACKET FORMATS:**
REGISTRATIION REQUESTS, KEEP-ALIVE REQUSTS AND RESPONSES:
The fields 'LINKING', 'FLAGS' and 'VERSION' are described in detail in the next section.
TYPE(1 Byte) + SRC_ID (4 Bytes) + LINKING (1 Byte) + FLAGS (4 Bytes) + VERSION (4 Bytes) [+ AUTHENTICATION (10 Bytes)]
90 0004C2C0 6A 000080DC 04030400 [AUTHENTICATION (10 Bytes)]
PEER LIST REQUEST:
TYPE(1 Byte) + SRC_ID (4 Bytes) [+ AUTHENTICATION (10 Bytes)]
92 0004C2C0 [AUTHENTICATION (10 Bytes)]
PEER LIST RESPONSE:
TYPE(1 Byte) + SRC_ID (4 Bytes) + NUM_PEERS* (2 Bytes) + {PEER_ID, PEER_IP, PEER_PORT, PEER_LINKING}... [+ AUTHENTICATION (10 Bytes)]
93 0004c2c0 002c*
00000001 6ccf7505 c351 6a
0004c2c3 d17271e9 c35a 6a
0004c2c5 446716bb c35c 6a
00c83265 a471c50c c351 6a
d66a94568d29357205c2
*Number of Peers, oddly formatted, stripping most significant non-zero digit seems to produce the correct value, such as 0x2c = 44, or 4 peers; or 0x6e = 110, or 10 peers
CAPABILITIES (BYTES 6-14):
LINKING Status 1 byte
Byte 1 - BIT FLAGS:
xx.. .... = Peer Operational (01 only known valid value)
..xx .... = MODE: 10 digital, 01 analog
.... xx.. = IPSC Slot 1: 10 on, 01 off
.... ..xx = IPSC Slot 2: 10 on, 01 off
Service FLAGS: 4 bytes, Master replies = 6 bytes
Byte 1 - 0x00 = Unknown
Byte 2 - 0x00 = Unknown
Byte 3 - BIT FLAGS:
x... .... = Set to 1 if RDAC call
.x.. .... = Unknwon - default to 0
..x. .... = 3rd Party Application? (set by c-Bridge, SMARTPTT)
...x xxxx = Unknown - default to 0
Byte 4 = BIT FLAGS:
x... .... = RDAC related? - default to 0
.x.. .... = RDAC related? - default to 0
..x. .... = RDAC related? - default to 0
...x .... = Set if packets are authenticated
.... x... = Set if voice calls are supported
.... .x.. = Set if data calls are supported
.... ..x. = Unknown - default to 0
.... ...x = Set if master
(the following only used in registration response from master)
NUMBER of PEERS: 2 Bytes
Byte 5 - 0x00 = Unknown
Byte 6 - Number of Peers (not including us)
Protocol VERSION: 4 Bytes (These are pure guesses based on repaeter and c-Bridge code revisions)
Bytes 1-2 - 0x04, 0x03 = Current version? (numbering scheme unknown)
Bytes 3-4 = 0x04, 0x00 = Oldest supported version? (same as above)
**SAMPLE CODE:**
*Sample Python3 code to genearate the authentication digest:*
import binascii
import hmac
import hashlib
def add_authentication (_payload, _key):
_digest = binascii.unhexlify((hmac.new(_key,_payload,hashlib.sha1)).hexdigest()[:20])
_full_payload = _payload + _digest
return _full_payload
PAYLOAD = binascii.unhexlify('90000000016a000080dc04030400') # Registration packet
KEY = binascii.unhexlify('0000000000000000000000000000000000012345') # Key '12345'
FULL_PAYLOAD = add_authentication(PAYLOAD, KEY)
print(binascii.b2a_hex(FULL_PAYLOAD))
**Example Python3 code to register to a master, exchange keep alives, reqest, recieve and decode the peer list:**
import socket
import binascii
import hmac
import hashlib
# Data structure for holding IPSC information
NETWORK = {
'IPSC1': {
'LOCAL': {
'DESCRIPTION': 'K0USY Lecompton, KS - Master',
'MODE': b'\x6A',
'PORT': 50001,
'RADIO_ID': binascii.unhexlify('00000001'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000012345')
},
'MASTER': {
'IP': '24.143.49.121',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
}
}
# Known IPSC Message Types
RDAC_CTL = b'\x70'
GROUP_VOICE = b'\x80'
GROUP_DATA = b'\x83'
PVT_DATA = b'\x84'
REG_REQ = b'\x90'
REG_REPLY = b'\x91'
PEER_LIST_REQ = b'\x92'
PEER_LIST_REPLY = b'\x93'
PEER_KEEP_ALIVE_REQ = b'\x94'
PEER_KEEP_ALIVE_REPLY = b'\x95'
KEEP_ALIVE_REQ = b'\x96'
KEEP_ALIVE_REPLY = b'\x97'
# IPSC information
IPSC_TS_BOTH = b'\x6A' # Both Timeslots IPSC enabled
IPSC_OP_VER = b'\x04\x03' # 0x04, 0x03 -- seems to be current version of IPSC
IPSC_OLD_VER = b'\x04\x00' # 0x04, 0x02 -- oldest version of IPSC suppoerted
IPSC_FLAGS = b'\x00\x00\x80\xDC' # Just accept this... it works, we know some of the pieces
#********** FUNCTIONS THAT WE WILL USE
# function to send a payload to a defined socket
def send_auth_packet (_dest_addr, _dest_port, _socket, _data, _key):
_hash = binascii.unhexlify((hmac.new(_key,_data,hashlib.sha1)).hexdigest()[:20])
print("==> Sending Authenticated Packet")
print(" Destination IP:", _dest_addr)
print(" Destination UDP Port:", _dest_port)
print(" Raw Packet:", binascii.b2a_hex(_data + _hash))
_socket.sendto((_data+_hash), (_dest_addr, _dest_port))
return
# Note: This function ignores authentiation information!!!
def receive_packet(_socket):
_data = (_socket.recv(1024))
_peer_id = str(int(binascii.b2a_hex(_data[2:5]), 16))
_mode = binascii.b2a_hex(_data[5:6])
print('<== Response Recieved from Radio ID:', _peer_id)
print(' Raw Packet:', binascii.b2a_hex(_data))
# Parse returned information
_packettype = (_data[0:1])
_sock = 'IPSC1'
if (_packettype == REG_REQ):
print(" >> This is a registration packet")
elif (_packettype == REG_REPLY):
print(" >> This is a registration reply packet")
elif (_packettype == PEER_LIST_REPLY):
print(">> This packet is a peer list from the master")
_num_peers = int(str(int(binascii.b2a_hex(_data[5:7]), 16))[1:])
# print('>>There are', binascii.b2a_hex(_data[5:7]), 'peers in this IPSC (RAW)')
print('>> There are', _num_peers, 'peers in this IPSC')
for i in range(7, (_num_peers*11)+7, 11):
NETWORK[_sock]['PEERS'].append({
'RADIO_ID': binascii.b2a_hex(_data[i:i+4]),
'IP': binascii.b2a_hex(_data[i+4:i+8]),
'PORT': binascii.b2a_hex(_data[i+8:i+10]),
'MODE': binascii.b2a_hex(_data[i+10:i+11])
})
print_peer_list('IPSC1')
return _data, _packettype
def print_peer_list(_ipsc_network):
print(NETWORK[_ipsc_network]['LOCAL']['DESCRIPTION'])
for dictionary in NETWORK[_ipsc_network]['PEERS']:
hex_address = dictionary['IP']
hex_port = dictionary['PORT']
hex_radio_id = dictionary['RADIO_ID']
hex_mode = dictionary['MODE']
address = [int(hex_address[0:2], 16), int(hex_address[2:4], 16), int(hex_address[4:6], 16), int(hex_address[6:8], 16)]
port = int(hex_port, 16)
radio_id = int(hex_radio_id, 16)
print(address[0],".",address[1],".",address[2],".",address[3],"\t", sep='', end='')
print(port, radio_id, hex_mode, sep='\t')
return
#********** THE ACTUAL MEAT
# Create a socket to conetact IPSC Network #1
ipsc1_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ipsc1_sock.bind(('', NETWORK['IPSC1']['LOCAL']['PORT']))
ipsc1_sock.setblocking(0)
ipsc1_sock.settimeout(60)
CTL_SUFFIX = (IPSC_TS_BOTH + IPSC_FLAGS + IPSC_OP_VER + IPSC_OLD_VER)
REG_REQ_PACKET = (REG_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX)
KEEP_ALIVE_PACKET = (KEEP_ALIVE_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX)
PEER_LIST_REQ_PACKET = (PEER_LIST_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'])
# Send registration packet
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, REG_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
# Send keep alive packet
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, KEEP_ALIVE_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
# Request peer list from master
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, PEER_LIST_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
ipsc1_sock.close

238
ipsc.py Normal file
View File

@ -0,0 +1,238 @@
from __future__ import print_function
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from twisted.internet import task
import binascii
import hmac
import hashlib
# Data structure for holding IPSC information
NETWORK = {
'IPSC1': {
'LOCAL': {
'DESCRIPTION': 'IPSC Network #1',
'MODE': b'\x6A',
'FLAGS': b'\x00\x00\x80\xDC',
'PORT': 50001,
'RADIO_ID': binascii.unhexlify('00000001'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000000001')
},
'MASTER': {
'IP': '1.1.1.1',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
},
'IPSC2': {
'LOCAL': {
'DESCRIPTION': 'IPSC Network #1',
'MODE': b'\x6A',
'FLAGS': b'\x00\x00\x80\xDC',
'PORT': 50002,
'RADIO_ID': binascii.unhexlify('00000002'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000000022')
},
'MASTER': {
'IP': '2.2.2.2',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
},
'IPSC3': {
'LOCAL': {
'DESCRIPTION': 'IPSC Network #1',
'MODE': b'\x6A',
'FLAGS': b'\x00\x00\x80\xDC',
'PORT': 50003,
'RADIO_ID': binascii.unhexlify('00000003'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000000333')
},
'MASTER': {
'IP': '3.3.3.3',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
},
'IPSC4': {
'LOCAL': {
'DESCRIPTION': 'IPSC Network #1',
'MODE': b'\x6A',
'FLAGS': b'\x00\x00\x80\xDC',
'PORT': 50004,
'RADIO_ID': binascii.unhexlify('00000004'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000004444')
},
'MASTER': {
'IP': '4.4.4.4',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
}
}
# Known IPSC Message Types
RDAC_CTL = b'\x70'
GROUP_VOICE = b'\x80'
GROUP_DATA = b'\x83'
PVT_DATA = b'\x84'
RPT_WAKE_UP = b'\x85'
MASTER_REG_REQ = b'\x90' # FROM peer TO master
MASTER_REG_REPLY = b'\x91' # FROM master TO peer
PEER_LIST_REQ = b'\x92'
PEER_LIST_REPLY = b'\x93'
PEER_REG_REQUEST = b'\x94' # Peer registration request
PEER_REG_REPLY = b'\x95' # Peer registration reply
MASTER_ALIVE_REQ = b'\x96' # FROM peer TO master
MASTER_ALIVE_REPLY = b'\x97' # FROM master TO peer
PEER_ALIVE_REQ = b'\x98' # Peer keep alive request
PEER_ALIVE_REPLY = b'\x99' # Peer keep alive reply
# IPSC Version Information
IPSC_OP_VER = b'\x04\x03' # 0x04, 0x03 -- seems to be current version of IPSC
IPSC_OLD_VER = b'\x04\x00' # 0x04, 0x02 -- oldest version of IPSC suppoerted
IPSC_VER = IPSC_OP_VER + IPSC_OLD_VER
def hashed_packet(key, data):
hash = binascii.unhexlify((hmac.new(key,data,hashlib.sha1)).hexdigest()[:20])
return (data + hash)
def print_peer_list(_ipsc_network):
print('\t', _ipsc_network['LOCAL']['DESCRIPTION'])
for dictionary in _ipsc_network['PEERS']:
hex_address = dictionary['IP']
hex_port = dictionary['PORT']
hex_radio_id = dictionary['RADIO_ID']
hex_mode = dictionary['MODE']
address = [int(hex_address[0:2], 16), int(hex_address[2:4], 16), int(hex_address[4:6], 16), int(hex_address[6:8], 16)]
port = int(hex_port, 16)
radio_id = int(hex_radio_id, 16)
print ('\t', address[0],address[1],address[2],address[3], sep='.', end='\t')
print (port, radio_id, sep=':', end=' ')
print ("IPSC Mode:", hex_mode)
print()
class IPSC(DatagramProtocol):
def __init__(self, *args, **kwargs):
if len(args) == 1:
self._config = args[0]
args = ()
self.TS_FLAGS = (self._config['LOCAL']['MODE'] + self._config['LOCAL']['FLAGS'])
self.MASTER_REG_REQ_PKT = (MASTER_REG_REQ + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS + IPSC_VER)
self.MASTER_ALIVE_PKT = (MASTER_ALIVE_REQ + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS + IPSC_VER)
self.PEER_LIST_REQ_PKT = (PEER_LIST_REQ + self._config['LOCAL']['RADIO_ID'])
self.PEER_REG_REPLY_PKT = (PEER_REG_REPLY + self._config['LOCAL']['RADIO_ID'] + IPSC_VER)
self.PEER_ALIVE_REQ_PKT = (PEER_ALIVE_REQ + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS)
self.PEER_ALIVE_REPLY_PKT = (PEER_ALIVE_REPLY + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS)
else:
print("Unexpected arguments found.")
def masterKeepalive(self):
master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT)
self.transport.write(master_alive_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
print("->> Master Keep Alive Sent To:\t", self._config['MASTER']['IP'],":", self._config['MASTER']['PORT'], "\n")
def startProtocol(self):
print ("*** config: %s" % self._config)
print ()
print ("*** Starting up IPSC Client and Registering to the Master ***")
reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_REG_REQ_PKT)
self.transport.write(reg_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
print ("->> Sending Registration to Master:\t", self._config['MASTER']['IP'],":", self._config['MASTER']['PORT'],
"\tFrom:", binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']), "\n")
#
self._call = task.LoopingCall(self.masterKeepalive)
self._loop = self._call.start(6)
def datagramReceived(self, data, (host, port)):
dest_ip = self._config['MASTER']['IP']
dest_port = self._config['MASTER']['PORT']
#print "received %r from %s:%d" % (binascii.b2a_hex(data), host, port)
_packettype = (data[0:1])
if (_packettype == MASTER_REG_REQ):
print("<<- Registration Packet Recieved\n")
elif (_packettype == MASTER_REG_REPLY):
print("<<- Master Registration Reply From:\t", host,":",port)
master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT)
self.transport.write(master_alive_packet, (host, port))
print("->> Master Keep Alive Sent To:\t", host,":",port, "\n")
# the only time we need to ask for the peer list is after we've registered to the master
peer_list_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_LIST_REQ_PKT)
self.transport.write(peer_list_req_packet, (host, port))
print("->> Peer List Reqested from Master:\t", host,":",port, "\n")
#print binascii.b2a_hex(peer_list_req_packet)
elif (_packettype == PEER_REG_REQUEST):
print("<<- Peer Registration Request From:\t", host,":",port)
peer_reg_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_REG_REPLY_PKT)
self.transport.write(peer_reg_reply_packet, (host, port))
print("->> Peer Registration Reply Sent To:\t", host,":",port,"\n")
#print host, port
#print binascii.b2a_hex(peer_reg_reply_packet)
elif (_packettype == PEER_ALIVE_REQ):
print("<<- Received Peer Keep Alive From:\t", host,":",port)
peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT)
peer_alive_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REPLY_PKT)
self.transport.write(peer_alive_reply_packet, (host, port))
print("->> Sent Peer Keep Alive Reply To:\t\t", host,":",port,)
self.transport.write(peer_alive_req_packet, (host, port))
print("->> Sent Peer Keep Alive Request To:\t\t", host,":",port, "\n")
#print binascii.b2a_hex(peer_alive_req_packet)
elif (_packettype == MASTER_ALIVE_REPLY):
print("<<- Keep Alive Recieved from Master:\t", host,":",port, "\n")
elif (_packettype == PEER_ALIVE_REPLY):
print("<<- Keep Alive Recieved from Peer:\t", host,":",port, "\n")
elif (_packettype == RDAC_CTL):
print("<<- RDAC and/or Control Packet From:\t", host,":",port, "\n")
elif (_packettype == PEER_LIST_REPLY):
print("<<- The Peer List has been Received from Master:\t", host,":",port)
_num_peers = int(str(int(binascii.b2a_hex(data[5:7]), 16))[1:])
print(' There are', _num_peers, 'peers in this IPSC Network')
for i in range(7, (_num_peers*11)+7, 11):
self._config['PEERS'].append({
'RADIO_ID': binascii.b2a_hex(data[i:i+4]),
'IP': binascii.b2a_hex(data[i+4:i+8]),
'PORT': binascii.b2a_hex(data[i+8:i+10]),
'MODE': binascii.b2a_hex(data[i+10:i+11])
})
print_peer_list(self._config)
print()
else:
packet_type = binascii.b2a_hex(_packettype)
print("<<- Recieved Unprocessed Type", packet_type, "From:\t", host,":",port,"\n")
if __name__ == '__main__':
for ipsc_network in NETWORK:
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], IPSC(NETWORK[ipsc_network]))
reactor.run()

25
ipsc_auth.py Normal file
View File

@ -0,0 +1,25 @@
import binascii
import hmac
import hashlib
PAYLOAD = binascii.unhexlify('9000c832686a0000e03c04030402')
AUTH_KEY = binascii.unhexlify('0000000000000000000000000000000000012345')
print type(PAYLOAD)
print type(AUTH_KEY)
print ""
HASH = (hmac.new(AUTH_KEY,PAYLOAD,hashlib.sha1)).hexdigest()[:20]
#PAY_HASH = HASH.hexdigest()[:20]
print binascii.b2a_hex(PAYLOAD)
print binascii.b2a_hex(AUTH_KEY)
print HASH
PACKET = binascii.b2a_hex(PAYLOAD) + HASH
print PACKET
# 27 86 3f 89 d5 a7 15 a8 31 55

137
list.py Normal file
View File

@ -0,0 +1,137 @@
import socket
import binascii
import hmac
import hashlib
# Data structure for holding IPSC information
NETWORK = {
'IPSC1': {
'LOCAL': {
'DESCRIPTION': 'K0USY Lecompton, KS - Master',
'MODE': b'\x6A',
'PORT': 50001,
'RADIO_ID': binascii.unhexlify('00000001'),
'AUTH_KEY': binascii.unhexlify('0000000000000000000000000000000000012345')
},
'MASTER': {
'IP': '24.143.49.121',
'MODE': b'\x6A',
'PORT': 50000,
'RADIO_ID': '',
},
'PEERS': [ # each list entry will be a dictionary for IP, RADIO ID and PORT
#{'IP': '100.200.1.1', 'PORT': 50000, 'RADIO_ID': b'\x00\x00\x00\xFF'},
]
}
}
# Known IPSC Message Types
RDAC_CTL = b'\x70'
GROUP_VOICE = b'\x80'
GROUP_DATA = b'\x83'
PVT_DATA = b'\x84'
REG_REQ = b'\x90'
REG_REPLY = b'\x91'
PEER_LIST_REQ = b'\x92'
PEER_LIST_REPLY = b'\x93'
PEER_KEEP_ALIVE_REQ = b'\x94'
PEER_KEEP_ALIVE_REPLY = b'\x95'
KEEP_ALIVE_REQ = b'\x96'
KEEP_ALIVE_REPLY = b'\x97'
# IPSC information
IPSC_TS_BOTH = b'\x6A' # Both Timeslots IPSC enabled
IPSC_OP_VER = b'\x04\x03' # 0x04, 0x03 -- seems to be current version of IPSC
IPSC_OLD_VER = b'\x04\x00' # 0x04, 0x02 -- oldest version of IPSC suppoerted
IPSC_FLAGS = b'\x00\x00\x80\xDC' # Just accept this... it works, we know some of the pieces
#********** FUNCTIONS THAT WE WILL USE
# function to send a payload to a defined socket
def send_auth_packet (_dest_addr, _dest_port, _socket, _data, _key):
_hash = binascii.unhexlify((hmac.new(_key,_data,hashlib.sha1)).hexdigest()[:20])
print("==> Sending Authenticated Packet")
print(" Destination IP:", _dest_addr)
print(" Destination UDP Port:", _dest_port)
print(" Raw Packet:", binascii.b2a_hex(_data + _hash))
_socket.sendto((_data+_hash), (_dest_addr, _dest_port))
return
# Note: This function ignores authentiation information!!!
def receive_packet(_socket):
_data = (_socket.recv(1024))
_peer_id = str(int(binascii.b2a_hex(_data[2:5]), 16))
_mode = binascii.b2a_hex(_data[5:6])
print('<== Response Recieved from Radio ID:', _peer_id)
print(' Raw Packet:', binascii.b2a_hex(_data))
# Parse returned information
_packettype = (_data[0:1])
_sock = 'IPSC1'
if (_packettype == REG_REQ):
print(" >> This is a registration packet")
elif (_packettype == REG_REPLY):
print(" >> This is a registration reply packet")
elif (_packettype == PEER_LIST_REPLY):
print(">> This packet is a peer list from the master")
_num_peers = int(str(int(binascii.b2a_hex(_data[5:7]), 16))[1:])
# print('>>There are', binascii.b2a_hex(_data[5:7]), 'peers in this IPSC (RAW)')
print('>> There are', _num_peers, 'peers in this IPSC')
for i in range(7, (_num_peers*11)+7, 11):
NETWORK[_sock]['PEERS'].append({
'RADIO_ID': binascii.b2a_hex(_data[i:i+4]),
'IP': binascii.b2a_hex(_data[i+4:i+8]),
'PORT': binascii.b2a_hex(_data[i+8:i+10]),
'MODE': binascii.b2a_hex(_data[i+10:i+11])
})
print_peer_list('IPSC1')
return _data, _packettype
def print_peer_list(_ipsc_network):
print(NETWORK[_ipsc_network]['LOCAL']['DESCRIPTION'])
for dictionary in NETWORK[_ipsc_network]['PEERS']:
hex_address = dictionary['IP']
hex_port = dictionary['PORT']
hex_radio_id = dictionary['RADIO_ID']
hex_mode = dictionary['MODE']
address = [int(hex_address[0:2], 16), int(hex_address[2:4], 16), int(hex_address[4:6], 16), int(hex_address[6:8], 16)]
port = int(hex_port, 16)
radio_id = int(hex_radio_id, 16)
print(address[0],".",address[1],".",address[2],".",address[3],"\t", sep='', end='')
print(port, radio_id, hex_mode, sep='\t')
return
#********** THE ACTUAL MEAT
# Create a socket to conetact IPSC Network #1
ipsc1_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ipsc1_sock.bind(('', NETWORK['IPSC1']['LOCAL']['PORT']))
ipsc1_sock.setblocking(0)
ipsc1_sock.settimeout(60)
CTL_SUFFIX = (IPSC_TS_BOTH + IPSC_FLAGS + IPSC_OP_VER + IPSC_OLD_VER)
REG_REQ_PACKET = (REG_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX)
KEEP_ALIVE_PACKET = (KEEP_ALIVE_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'] + CTL_SUFFIX)
PEER_LIST_REQ_PACKET = (PEER_LIST_REQ + NETWORK['IPSC1']['LOCAL']['RADIO_ID'])
# Send registration packet
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, REG_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
# Send keep alive packet
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, KEEP_ALIVE_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
# Request peer list from master
send_auth_packet(NETWORK['IPSC1']['MASTER']['IP'], NETWORK['IPSC1']['MASTER']['PORT'], ipsc1_sock, PEER_LIST_REQ_PACKET, NETWORK['IPSC1']['LOCAL']['AUTH_KEY'])
receive_packet(ipsc1_sock)
ipsc1_sock.close