Initial import

pull/1/head
Wijnand Modderman-Lenstra 9 years ago
parent 6573e38144
commit 07cd122018

@ -0,0 +1,39 @@
package main
import (
"flag"
"io/ioutil"
"os"
"pd0mz/dmr/ipsc"
"gopkg.in/yaml.v2"
)
func main() {
configFile := flag.String("config", "dmr.yaml", "configuration file")
flag.Parse()
f, err := os.Open(*configFile)
if err != nil {
panic(err)
}
defer f.Close()
d, err := ioutil.ReadAll(f)
if err != nil {
panic(err)
}
network := &ipsc.Network{}
if err := yaml.Unmarshal(d, network); err != nil {
panic(err)
}
repeater, err := ipsc.New(network)
if err != nil {
panic(err)
}
repeater.Dump = true
panic(repeater.Run())
}

@ -0,0 +1,4 @@
radio_id: 2042214
auth_key: 7061737377307264
master: brandmeister.pd0zry.ampr.org:55000
listen: 0.0.0.0:55000

@ -0,0 +1,332 @@
package ipsc
import (
"crypto/hmac"
"crypto/sha1"
"encoding/binary"
"encoding/hex"
"fmt"
"log"
"net"
"time"
)
type Network struct {
Disabled bool
RadioID uint32
AliveTimer time.Duration
MaxMissed int
IPSCMode string
PeerOperDisabled bool
TS1LinkDisabled bool
TS2LinkDisabled bool
CSBKCall bool
RepeaterCallMonitoring bool `yaml:"rcm"`
ConsoleApplicationDisabled bool
XNLCall bool
XNLMaster bool
DataCall bool
VoiceCall bool
MasterPeer bool
AuthKey string
Master string
Listen string
}
type IPSC struct {
Network *Network
Dump bool
authKey []byte
local struct {
addr *net.UDPAddr
radioID []byte
mode byte
flags []byte
tsFlags []byte
}
master struct {
addr *net.UDPAddr
radioID []byte
mode byte
flags []byte
status struct {
connected bool
peerList bool
keepAliveSent int
keepAliveMissed int
keepAliveOutstanding int
keepAliveReceived int
keepAliveRXTime int
}
}
conn *net.UDPConn
}
func New(network *Network) (*IPSC, error) {
c := &IPSC{
Network: network,
}
c.local.radioID = make([]byte, 4)
c.local.flags = make([]byte, 4)
c.master.radioID = make([]byte, 4)
c.master.flags = make([]byte, 4)
binary.BigEndian.PutUint32(c.local.radioID, c.Network.RadioID)
var err error
if c.Network.AuthKey != "" {
if c.authKey, err = hex.DecodeString(c.Network.AuthKey); err != nil {
return nil, err
}
}
if c.Network.AliveTimer == 0 {
c.Network.AliveTimer = time.Second * 5
}
if !c.Network.PeerOperDisabled {
c.local.mode |= FlagPeerOperational
}
switch c.Network.IPSCMode {
case "analog":
c.local.mode |= FlagPeerModeAnalog
case "", "digital":
c.local.mode |= FlagPeerModeDigital
case "none":
c.local.mode &= 0xff ^ MaskPeerMode
default:
return nil, fmt.Errorf("unknown IPSCMode %q", c.Network.IPSCMode)
}
if c.Network.TS1LinkDisabled {
c.local.mode |= FlagIPSCTS1Off
} else {
c.local.mode |= FlagIPSCTS1On
}
if c.Network.TS2LinkDisabled {
c.local.mode |= FlagIPSCTS2Off
} else {
c.local.mode |= FlagIPSCTS2On
}
if c.Network.CSBKCall {
c.local.flags[2] |= FlagCSBKMessage
}
if c.Network.RepeaterCallMonitoring {
c.local.flags[2] |= FlagRepeaterCallMonitoring
}
if !c.Network.ConsoleApplicationDisabled {
c.local.flags[2] |= FlagConsoleApplication
}
if c.Network.XNLCall {
c.local.flags[3] |= FlagXNLStatus
if c.Network.XNLMaster {
c.local.flags[3] |= FlagXNLMaster
} else {
c.local.flags[3] |= FlagXNLSlave
}
}
if len(c.Network.AuthKey) > 0 {
c.local.flags[3] |= FlagPacketAuthenticated
}
if c.Network.DataCall {
c.local.flags[3] |= FlagDataCall
}
if c.Network.VoiceCall {
c.local.flags[3] |= FlagVoiceCall
}
if c.Network.MasterPeer {
c.local.flags[3] |= FlagMasterPeer
}
if c.Network.Listen == "" {
c.Network.Listen = ":62030"
}
if c.local.addr, err = net.ResolveUDPAddr("udp", c.Network.Listen); err != nil {
return nil, err
}
if c.Network.Master != "" {
if c.master.addr, err = net.ResolveUDPAddr("udp", c.Network.Master); err != nil {
return nil, err
}
}
c.local.tsFlags = append([]byte{c.local.mode}, c.local.flags...)
return c, nil
}
func (c *IPSC) Run() error {
var err error
if c.conn, err = net.ListenUDP("udp", c.local.addr); err != nil {
return err
}
go c.peerMaintenance()
for {
var peer *net.UDPAddr
var n int
var b = make([]byte, 512)
if n, peer, err = c.conn.ReadFromUDP(b); err != nil {
log.Printf("error reading from %s: %v\n", peer, err)
continue
}
if c.Dump {
c.dump(peer, b[:n])
}
if !c.authenticate(b[:n]) {
log.Printf("authentication failed, dropping packet from %s\n", peer)
continue
}
}
return nil
}
func (c *IPSC) authenticate(data []byte) bool {
if c.authKey == nil || len(c.authKey) == 0 {
return true
}
payload := c.payload(data)
hash := data[len(data)-10:]
mac := hmac.New(sha1.New, c.authKey)
mac.Write(payload)
return hmac.Equal(hash, mac.Sum(nil))
}
func (c *IPSC) payload(data []byte) []byte {
if c.authKey == nil || len(c.authKey) == 0 {
return data
}
return data[:len(data)-10]
}
func (c *IPSC) dump(addr *net.UDPAddr, data []byte) {
if len(data) < 7 {
fmt.Printf("%d bytes of unreadable data from %s:\n", len(data), addr)
fmt.Printf(hex.Dump(data))
return
}
fmt.Printf("%d bytes of data %s:\n", len(data), addr)
fmt.Printf(hex.Dump(data))
packetType := data[0]
peerID := binary.BigEndian.Uint32(data[1:5])
seq := data[5:6]
switch packetType {
case CallConfirmation:
fmt.Println("call confirmation:")
case TextMessageAck:
fmt.Println("text message acknowledgement:")
case CallMonStatus:
fmt.Println("call monitor status:")
case CallMonRepeat:
fmt.Println("call monitor repeater:")
case CallMonNACK:
fmt.Println("call monitor nack:")
case XCMPXNLControl:
fmt.Println("XCMP/XNL control message:")
case GroupVoice:
fmt.Println("group voice:")
case PVTVoice:
fmt.Println("PVT voice:")
case GroupData:
fmt.Println("group data:")
case PVTData:
fmt.Println("PVT data:")
case RPTWakeUp:
fmt.Println("RPT wake up:")
case UnknownCollision:
fmt.Println("unknown collision:")
case MasterRegistrationRequest:
fmt.Println("master registration request:")
case MasterRegistrationReply:
fmt.Println("master registration reply:")
case PeerListRequest:
fmt.Println("peer list request:")
case PeerListReply:
fmt.Println("peer list reply:")
case PeerRegistrationRequest:
fmt.Println("peer registration request:")
case PeerRegistrationReply:
fmt.Println("peer registration reply:")
case MasterAliveRequest:
fmt.Println("master alive request:")
case MasterAliveReply:
fmt.Println("master alive reply:")
case PeerAliveRequest:
fmt.Println("peer alive request:")
case PeerAliveReply:
fmt.Println("peer alive reply:")
case DeregistrationRequest:
fmt.Println("de-registration request:")
case DeregistrationReply:
fmt.Println("de-registration reply:")
default:
fmt.Printf("unknown packet type 0x%02x:\n", packetType)
}
fmt.Printf("\tpeer id: %d\n", peerID)
fmt.Printf("\tsequence: %v\n", seq)
}
func (c *IPSC) hashedPacket(key, data []byte) []byte {
if key == nil || len(key) == 0 {
return data
}
mac := hmac.New(sha1.New, key)
mac.Write(data)
hash := make([]byte, 20)
hex.Encode(hash, mac.Sum(nil))
return append(data, hash...)
}
func (c *IPSC) peerMaintenance() {
for {
var p []byte
if c.master.status.connected {
log.Println("sending keep-alive to master")
r := []byte{MasterAliveRequest}
r = append(r, c.local.radioID...)
r = append(r, c.local.tsFlags...)
r = append(r, []byte{LinkTypeIPSC, Version17, LinkTypeIPSC, Version16}...)
p = c.hashedPacket(c.authKey, r)
} else {
log.Println("registering with master")
r := []byte{MasterRegistrationRequest}
r = append(r, c.local.radioID...)
r = append(r, c.local.tsFlags...)
r = append(r, []byte{LinkTypeIPSC, Version17, LinkTypeIPSC, Version16}...)
p = c.hashedPacket(c.authKey, r)
}
if err := c.sendToMaster(p); err != nil {
log.Fatalf("error sending registration request to master: %v\n", err)
return
}
time.Sleep(c.Network.AliveTimer)
}
}
func (c *IPSC) sendToMaster(data []byte) error {
if c.Dump {
c.dump(c.master.addr, data)
}
for len(data) > 0 {
n, err := c.conn.WriteToUDP(data, c.master.addr)
if err != nil {
return err
}
data = data[n:]
}
return nil
}

@ -0,0 +1,92 @@
package ipsc
type Mask uint8
// IPSC mask values
// Linking status
/*
Byte 1 - BIT FLAGS:
xx.. .... = Peer Operational (01 only known valid value)
..xx .... = Peer MODE: 00 - No Radio, 01 - Analog, 10 - Digital
.... xx.. = IPSC Slot 1: 10 on, 01 off
.... ..xx = IPSC Slot 2: 10 on, 01 off
*/
const (
FlagPeerOperational = 0x40
MaskPeerMode = 0x30
FlagPeerModeAnalog = 0x10
FlagPeerModeDigital = 0x20
MaskIPSCTS1 = 0x0c
MaskIPSCTS2 = 0x03
FlagIPSCTS1On = 0x08
FlagIPSCTS1Off = 0x04
FlagIPSCTS2On = 0x02
FlagIPSCTS2Off = 0x01
)
// Service flags
/*
Byte 1 - 0x00 = Unknown
Byte 2 - 0x00 = Unknown
Byte 3 - BIT FLAGS:
x... .... = CSBK Message
.x.. .... = Repeater Call Monitoring
..x. .... = 3rd Party "Console" Application
...x xxxx = Unknown - default to 0
*/
const (
FlagCSBKMessage = 0x80
FlagRepeaterCallMonitoring = 0x40
FlagConsoleApplication = 0x20
)
/*
Byte 4 = BIT FLAGS:
x... .... = XNL Connected (1=true)
.x.. .... = XNL Master Device
..x. .... = XNL Slave Device
...x .... = Set if packets are authenticated
.... x... = Set if data calls are supported
.... .x.. = Set if voice calls are supported
.... ..x. = Unknown - default to 0
.... ...x = Set if master
*/
const (
FlagXNLStatus = 0x80
FlagXNLMaster = 0x40
FlagXNLSlave = 0x20
FlagPacketAuthenticated = 0x10
FlagDataCall = 0x08
FlagVoiceCall = 0x04
FlagMasterPeer = 0x01
)
// Timeslot call and status byte
/*
Byte 17 of Group and Private Voice/Data Packets
..x.. ....TS Value (0=TS1, 1=TS2)
.x... ....TS In Progress/End (0=In Progress, 1=End)
Possible values: 0x00=TS1, 0x20=TS2, 0x40=TS1 End, 0x60=TS2 End
*/
// RTP mask values
/*
Bytes 1 and 2 of the RTP header are bit-fields, the rest are at least
one byte long, and do not need masked.
*/
const (
// Byte 1
RTPVersionMask Mask = 0xc0
RTPPadMask Mask = 0x20
RTPExtMask Mask = 0x10
RTPCSICMask Mask = 0x0f
// Byte 2
RTPMRKRMask Mask = 0x80
RTPPayTypeMask Mask = 0xf7
)

@ -0,0 +1,74 @@
package ipsc
const (
// IPSC Version Information
Version14 byte = 0x00
Version15 byte = 0x00
Version15A byte = 0x00
Version16 byte = 0x01
Version17 byte = 0x02
Version18 byte = 0x02
Version19 byte = 0x03
Version22 byte = 0x04
// Known IPSC Message Types
CallConfirmation byte = 0x05 // Confirmation FROM the recipient of a confirmed call.
TextMessageAck byte = 0x54 // Doesn't seem to mean success, though. This code is sent success or failure
CallMonStatus byte = 0x61 // |
CallMonRepeat byte = 0x62 // | Exact meaning unknown
CallMonNACK byte = 0x63 // |
XCMPXNLControl byte = 0x70 // XCMP/XNL control message
GroupVoice byte = 0x80
PVTVoice byte = 0x81
GroupData byte = 0x83
PVTData byte = 0x84
RPTWakeUp byte = 0x85 // Similar to OTA DMR "wake up"
UnknownCollision byte = 0x86 // Seen when two dmrlinks try to transmit at once
MasterRegistrationRequest byte = 0x90 // FROM peer TO master
MasterRegistrationReply byte = 0x91 // FROM master TO peer
PeerListRequest byte = 0x92 // From peer TO master
PeerListReply byte = 0x93 // From master TO peer
PeerRegistrationRequest byte = 0x94 // Peer registration request
PeerRegistrationReply byte = 0x95 // Peer registration reply
MasterAliveRequest byte = 0x96 // FROM peer TO master
MasterAliveReply byte = 0x97 // FROM master TO peer
PeerAliveRequest byte = 0x98 // Peer keep alive request
PeerAliveReply byte = 0x99 // Peer keep alive reply
DeregistrationRequest byte = 0x9a // Request de-registration from system
DeregistrationReply byte = 0x9b // De-registration reply
// Link Type Values
LinkTypeIPSC byte = 0x04
)
var AnyPeerRequired = map[byte]bool{
GroupVoice: true,
PVTVoice: true,
GroupData: true,
PVTData: true,
CallMonStatus: true,
CallMonRepeat: true,
CallMonNACK: true,
XCMPXNLControl: true,
RPTWakeUp: true,
DeregistrationRequest: true,
}
var PeerRequired = map[byte]bool{
PeerAliveRequest: true,
PeerAliveReply: true,
PeerRegistrationRequest: true,
PeerRegistrationReply: true,
}
var MasterRequired = map[byte]bool{
PeerListReply: true,
MasterAliveReply: true,
}
var UserGenerated = map[byte]bool{
GroupVoice: true,
PVTVoice: true,
GroupData: true,
PVTData: true,
}
Loading…
Cancel
Save