Browse Source

Initial commit

master
Jeroen Vreeken 2 years ago
commit
773c793d5f
12 changed files with 81689 additions and 0 deletions
  1. +27
    -0
      .gitignore
  2. +9
    -0
      Makefile.am
  3. +6
    -0
      bootstrap
  4. +254
    -0
      ccs7db.c
  5. +34
    -0
      ccs7db.h
  6. +39
    -0
      configure.ac
  7. +97
    -0
      dml_config.c
  8. +27
    -0
      dml_config.h
  9. +251
    -0
      dml_dmr.c
  10. +10
    -0
      dml_dmr.conf
  11. +3361
    -0
      rptr.csv
  12. +77574
    -0
      user.csv

+ 27
- 0
.gitignore View File

@@ -0,0 +1,27 @@
dml_dmr
*.o

# http://www.gnu.org/software/autoconf

/Makefile
/Makefile.in
/autom4te.cache
/autoscan.log
/autoscan-*.log
/aclocal.m4
/compile
/config.*
/configure
/configure.scan
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4/
/missing
/stamp-h?
.deps/
.dirstamp
.libs/
*.l[ao]
*~

+ 9
- 0
Makefile.am View File

@@ -0,0 +1,9 @@
ACLOCAL_AMFLAGS=-I m4

CFLAGS+= -Wall -Werror -O3

bin_PROGRAMS = dml_dmr

dml_dmr_SOURCES = dml_dmr.c dml_config.c ccs7db.c
dml_dmr_LDFLAGS = -ldml -leth_ar


+ 6
- 0
bootstrap View File

@@ -0,0 +1,6 @@
#! /bin/sh

libtoolize \
&& aclocal \
&& automake --gnu --add-missing \
&& autoconf

+ 254
- 0
ccs7db.c View File

@@ -0,0 +1,254 @@
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2017

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#include "ccs7db.h"

#include <eth_ar/eth_ar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct ccs7db_entry {
unsigned long id;
uint8_t mac[6];
};

struct ccs7db {
size_t nr_entries;
struct ccs7db_entry **id_index;
struct ccs7db_entry **mac_index;
};

struct ccs7db *ccs7db_create(void)
{
return calloc(1, sizeof(struct ccs7db));
}

void ccs7db_destroy(struct ccs7db *db)
{
size_t i;
for (i = 0; i < db->nr_entries; i++) {
free(db->id_index[i]);
}
free(db->id_index);
free(db->mac_index);
free(db);
}

static struct ccs7db_entry *ccs7db_find_by_id_nested(struct ccs7db *db, unsigned long id, size_t start, size_t end, size_t *off)
{
size_t half = start + (end - start) / 2;

if (end - start == 0) {
if (off)
*off = start;
return NULL;
}

if (db->id_index[half]->id == id) {
if (off)
*off = half;
return db->id_index[start];
}
if (db->id_index[half]->id > id) {
return ccs7db_find_by_id_nested(db, id, start, half, off);
} else {
return ccs7db_find_by_id_nested(db, id, half + 1, end, off);
}
}

struct ccs7db_entry *ccs7db_find_by_id(struct ccs7db *db, unsigned long id, size_t *off)
{
size_t start = 0;
size_t end = db->nr_entries;
if (!end) {
if (off)
*off = 0;
return NULL;
}
return ccs7db_find_by_id_nested(db, id, start, end, off);
}

static int compare_mac(const uint8_t *mac1, const uint8_t *mac2)
{
int i;
for (i = 0; i < 6; i++) {
if (mac1[i] < mac2[i])
return -1;
if (mac1[i] > mac2[i])
return 1;
}
return 0;
}

static struct ccs7db_entry *ccs7db_find_by_mac_nested(struct ccs7db *db, const uint8_t *mac, size_t start, size_t end, size_t *off)
{
size_t half = start + (end - start) / 2;
if (end - start == 0) {
if (off)
*off = start;
return NULL;
}

int compare = compare_mac(mac, db->mac_index[half]->mac);
if (compare == 0) {
if (off)
*off = half;
return db->mac_index[half];
}
if (compare < 0) {
return ccs7db_find_by_mac_nested(db, mac, start, half, off);
} else {
return ccs7db_find_by_mac_nested(db, mac, half + 1, end, off);
}
}

struct ccs7db_entry *ccs7db_find_by_mac(struct ccs7db *db, const uint8_t *mac, size_t *off)
{
size_t end = db->nr_entries;
if (!end) {
if (off)
*off = 0;
return NULL;
}
return ccs7db_find_by_mac_nested(db, mac, 0, end, off);
}

int ccs7db_id2mac(struct ccs7db *db, uint8_t mac[6], const unsigned long id)
{
struct ccs7db_entry *entry = ccs7db_find_by_id(db, id, NULL);
if (!entry)
return -1;
memcpy(mac, entry->mac, 6);
return 0;
}

int ccs7db_mac2id(struct ccs7db *db, unsigned long *id, const uint8_t mac[6])
{
struct ccs7db_entry *entry = ccs7db_find_by_mac(db, mac, NULL);
if (!entry)
return -1;
*id = entry->id;
return 0;
}

int ccs7db_add(struct ccs7db *db, char *callsign, char *idstr)
{
uint8_t mac[6];
unsigned long id = atol(idstr);
int r = 0;
size_t id_off;
size_t mac_off;
if (strlen(idstr) > 8) {
r = -1;
goto err_id;
}
if (eth_ar_call2mac(mac, callsign, 0, false)) {
r = -1;
goto err_mac;
}
struct ccs7db_entry *entry = ccs7db_find_by_id(db, id, &id_off);
if (!entry) {
ccs7db_find_by_mac(db, mac, &mac_off);
entry = malloc(sizeof(*entry));
if (!entry) {
r = -1;
goto err_alloc;
}
struct ccs7db_entry **ids = realloc(db->id_index, sizeof(entry) * (db->nr_entries+1));
if (!ids) {
r = -1;
goto err_realloc;
}
db->id_index = ids;
struct ccs7db_entry **macs = realloc(db->mac_index, sizeof(entry) * (db->nr_entries+1));
if (!macs) {
r = -1;
goto err_realloc;
}
db->mac_index = macs;
entry->id = id;
memcpy(entry->mac, mac, 6);
memmove(&db->id_index[id_off+1], &db->id_index[id_off], (db->nr_entries - id_off) * sizeof(entry));
db->id_index[id_off] = entry;
memmove(&db->mac_index[mac_off+1], &db->mac_index[mac_off], (db->nr_entries - mac_off) * sizeof(entry));
db->mac_index[mac_off] = entry;
db->nr_entries++;
}
err_realloc:
err_alloc:
err_mac:
err_id:
return r;
}

int ccs7db_csv_load(struct ccs7db *db, const char *filename)
{
FILE *fcsv;
char csvline[256] = {0};
int r = 0;

fcsv = fopen(filename, "r");
if (!fcsv) {
r = -1;
goto err_open;
}
/* skip first line */
fgets(csvline, sizeof(csvline)-1, fcsv);
while (fgets(csvline, sizeof(csvline)-1, fcsv)) {
char *save;
char *num;
char *callsign = NULL;
char *dmrid = NULL;
num = strtok_r(csvline, "; ", &save);
if (num)
callsign = strtok_r(NULL, "; .", &save);
if (callsign)
dmrid = strtok_r(NULL, "; .", &save);
if (callsign && dmrid) {
ccs7db_add(db, callsign, dmrid);
}
}
fclose(fcsv);
err_open:
return r;
}


+ 34
- 0
ccs7db.h View File

@@ -0,0 +1,34 @@
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2017

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#ifndef _INCLUDE_CCS7DB_H_
#define _INCLUDE_CCS7DB_H_

#include <stdint.h>

struct ccs7db;

struct ccs7db *ccs7db_create(void);
void ccs7db_destroy(struct ccs7db *db);

int ccs7db_csv_load(struct ccs7db *db, const char *filename);

int ccs7db_id2mac(struct ccs7db *db, uint8_t mac[6], const unsigned long id);
int ccs7db_mac2id(struct ccs7db *db, unsigned long *id, const uint8_t mac[6]);

#endif /* _INCLUDE_CCS7DB_H_ */

+ 39
- 0
configure.ac View File

@@ -0,0 +1,39 @@
AC_INIT([dml-dmr], [0.0], [jeroen@vreeken.net], [https://dmlinking.net/])
AM_INIT_AUTOMAKE([foreign dist-xz])
AM_SILENT_RULES([yes])
LT_INIT

AC_PROG_CC

AC_CONFIG_MACRO_DIR([m4])


# Check for DML library

dnl Search for libdml
AC_SEARCH_LIBS([dml_host_create], [dml], [], [
AC_MSG_ERROR([unable to find the dml_host functions in libdml])
])

AC_CHECK_HEADERS([dml/dml_host.h],
[dml_found_headers=yes; break;])

AS_IF([test "x$dml_found_headers" != "xyes"],
[AC_MSG_ERROR([Unable to find the dml headers])])


# Check for eth_ar library

dnl Search for libeth_ar
AC_SEARCH_LIBS([eth_ar_call2mac], [eth_ar], [], [
AC_MSG_ERROR([unable to find the eth_ar_call2mac function in libeth_ar])
])

AC_CHECK_HEADERS([eth_ar/eth_ar.h],
[eth_ar_found_headers=yes; break;])

AS_IF([test "x$eth_ar_found_headers" != "xyes"],
[AC_MSG_ERROR([Unable to find the eth_ar headers])])


AC_OUTPUT([Makefile])

+ 97
- 0
dml_config.c View File

@@ -0,0 +1,97 @@
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/
#include "dml_config.h"

#include <stdio.h>
#include <string.h>

struct dml_config {
struct dml_config *next;
char *key;
char *value;
};

static struct dml_config *config_list = NULL;

int dml_config_load(char *file)
{
FILE *fd;
char *rf;
fd = fopen(file, "r");
if (!fd)
goto err_fopen;
do {
char buffer[1025];
char *key, *value;
rf = fgets(buffer, 1024, fd);
while (strlen(buffer) && buffer[strlen(buffer)-1] == '\n')
buffer[strlen(buffer)-1] = 0;
key = strtok(buffer, " \t=");
value = strtok(NULL, "\n\r");
if (key && value) {
struct dml_config *conf;
while (value[0] == ' ' ||
value[0] == '\t' ||
value[0] == '=')
value++;
conf = calloc(1, sizeof(struct dml_config));
if (!conf)
goto err_calloc;
conf->key = strdup(key);
conf->value = strdup(value);
struct dml_config **entry;
for (entry = &config_list; *entry; entry = &(*entry)->next);
*entry = conf;
}
} while (rf);

fclose(fd);

return 0;

err_calloc:
fclose(fd);
err_fopen:
return -1;
}

char *dml_config_value(char *key, char *prev_value, char *def)
{
struct dml_config *entry;
for (entry = config_list; entry; entry = entry->next) {
if (prev_value && entry->value != prev_value)
continue;
if (prev_value) {
prev_value = NULL;
continue;
}
if (!strcmp(entry->key, key))
return entry->value;
}
return def;
}

+ 27
- 0
dml_config.h View File

@@ -0,0 +1,27 @@
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/
#ifndef _INCLUDE_DML_CONFIG_H_
#define _INCLUDE_DML_CONFIG_H_

#include <stdlib.h>

int dml_config_load(char *file);

char *dml_config_value(char *key, char *prev_value, char *def);

#endif /* _INCLUDE_DML_CONFIG_H_ */

+ 251
- 0
dml_dmr.c View File

@@ -0,0 +1,251 @@
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2017

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#include "ccs7db.h"

#include "dml_config.h"

#include <dml/dml.h>
#include <dml/dml_crypto.h>
#include <dml/dml_host.h>
#include <dml/dml_packet.h>
#include <dml/dml_poll.h>
#include <stdio.h>
#include <string.h>

static struct dml_crypto_key *dk;
static uint8_t ref_id[DML_ID_SIZE];
static struct dml_host *host;
static struct dml_stream *stream_dv;
static struct ccs7db *db;

uint64_t prev_timestamp = 0;

#define DML_DATA_KEEPALIVE 10

static int watchdog(void *arg);


static void load_ccs7(struct ccs7db *db)
{
char *value = NULL;
while ((value = dml_config_value("ccs7_file", value, NULL))) {
printf("Load ccs7 file %s\n", value);
ccs7db_csv_load(db, value);
}
}

static unsigned long last_id;

static void stream_data_cb(struct dml_host *host, struct dml_stream *ds, uint64_t timestamp, void *data, size_t data_size, void *arg)
{
if (data_size < 8)
return;
// uint8_t *datab = data;
uint8_t *mac = data;
// uint8_t *voice = &datab[8];
// size_t voice_size = data_size - 8;
// int mode = datab[6];
// bool state = datab[7] & 0x1;
unsigned long id;
if (ccs7db_mac2id(db, &id, mac))
{
/* Search for alias? */
id = 0;
}
if (id != last_id) {
last_id = id;
printf("Voice from id: %lu\n", id);
}
/* Do something with voice data */
}

void send_data(void *data, size_t size, uint64_t timestamp)
{
struct timespec ts;
uint64_t tmax;
uint16_t packet_id = dml_stream_data_id_get(stream_dv);
dml_poll_timeout(&watchdog,
&(struct timespec){ DML_DATA_KEEPALIVE, 0});

if (!packet_id)
return;
if (timestamp <= prev_timestamp) {
fprintf(stderr, "Dropping packet %"PRId64"\n", timestamp);
return;
}

clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2;
ts.tv_nsec = 0;
tmax = dml_ts2timestamp(&ts);
if (timestamp > tmax)
return;
prev_timestamp = timestamp;

struct dml_connection *con = dml_host_connection_get(host);
if (con)
dml_packet_send_data(con, packet_id, data, size, timestamp, dk);
}

static void stream_req_reverse_connect_cb(struct dml_host *host, struct dml_stream *ds, struct dml_stream *ds_rev, int status, void *arg)
{
bool do_connect = true;
bool do_reject = false;

printf("Received reverse connect request (status=%d)\n", status);
if (do_connect) {
struct dml_crypto_key *key = dml_stream_crypto_get(ds_rev);
if (dml_host_mime_filter(host, ds_rev) && key) {
printf("Respond with connect\n");
dml_host_connect(host, ds_rev);
} else {
do_reject = true;
}
} else {
do_reject = true;
}
if (do_reject) {
printf("Reject request (status=%d)\n", DML_STATUS_UNAUTHORIZED);
dml_packet_send_req_reverse(dml_host_connection_get(host),
dml_stream_id_get(ds_rev),
dml_stream_id_get(ds),
DML_PACKET_REQ_REVERSE_DISC, DML_STATUS_UNAUTHORIZED);
}
}

static void stream_req_reverse_disconnect_cb(struct dml_host *host, struct dml_stream *ds, struct dml_stream *ds_rev, int status, void *arg)
{
printf("Disconnect request (status=%d)\n", status);
if (dml_stream_data_id_get(ds_rev)) {
printf("Disconnect stream\n");
dml_packet_send_req_disc(dml_host_connection_get(host), dml_stream_id_get(ds_rev));
}
}

static int watchdog(void *arg)
{
struct timespec ts;
uint64_t timestamp;
printf("No activity, sending state off packet\n");
uint8_t data[8];

memset(data, 0xff, 6);
data[6] = 0;
data[7] = false;

clock_gettime(CLOCK_REALTIME, &ts);
timestamp = dml_ts2timestamp(&ts);
if (timestamp <= prev_timestamp)
timestamp = prev_timestamp + 1;;
send_data(data, 8, timestamp);

return 0;
}


int main(int argc, char **argv)
{
char *ca;
char *certificate;
char *key;
char *server;
char *name;
char *alias;
char *description;
char *file = "dml_dmr.conf";
uint32_t bps = 64000;

if (argc > 1)
file = argv[1];

if (dml_config_load(file)) {
printf("Failed to load config file %s\n", file);
return -1;
}

db = ccs7db_create();

load_ccs7(db);

name = dml_config_value("name", NULL, "0.brandmeister.network");
alias = dml_config_value("alias", NULL, "0");
description = dml_config_value("description", NULL, "Talkgroup 0");
server = dml_config_value("server", NULL, "localhost");
certificate = dml_config_value("certificate", NULL, "");
key = dml_config_value("key", NULL, "");
ca = dml_config_value("ca", NULL, ".");

if (dml_crypto_init(NULL, ca)) {
fprintf(stderr, "Failed to init crypto\n");
return -1;
}
if (dml_crypto_load_cert(certificate)) {
printf("Could not load certificate\n");
return -1;
}

if (!(dk = dml_crypto_private_load(key))) {
printf("Could not load key\n");
return -1;
}
if (dml_id_gen(ref_id, DML_PACKET_DESCRIPTION_VERSION_0, bps,
DML_MIME_DV_C2, name, alias, description))
return -1;
stream_dv = dml_stream_by_id_alloc(ref_id);
dml_stream_mine_set(stream_dv, true);
dml_stream_crypto_set(stream_dv, dk);
dml_stream_name_set(stream_dv, name);
dml_stream_alias_set(stream_dv, alias);
dml_stream_mime_set(stream_dv, DML_MIME_DV_C2);
dml_stream_description_set(stream_dv, description);
dml_stream_bps_set(stream_dv, bps);
host = dml_host_create(server);
if (!host) {
printf("Could not create host\n");
return -1;
}
dml_host_mime_filter_set(host, 1, (char*[]){ DML_MIME_DV_C2 });
dml_host_stream_data_cb_set(host, stream_data_cb, NULL);
dml_host_stream_req_reverse_connect_cb_set(host, stream_req_reverse_connect_cb, NULL);
dml_host_stream_req_reverse_disconnect_cb_set(host, stream_req_reverse_disconnect_cb, NULL);

dml_poll_add(&watchdog, NULL, NULL, watchdog);

dml_poll_timeout(&watchdog,
&(struct timespec){ DML_DATA_KEEPALIVE, 0});

dml_poll_loop();

ccs7db_destroy(db);
return 0;
}

+ 10
- 0
dml_dmr.conf View File

@@ -0,0 +1,10 @@
name = 0.brandmeister.network
alias = 0
description = Talkgroup 0
server = localhost
certificate = cert.pem
key = k.pem
ca = ./ca/

ccs7_file = rptr.csv
ccs7_file = user.csv

+ 3361
- 0
rptr.csv
File diff suppressed because it is too large
View File


+ 77574
- 0
user.csv
File diff suppressed because it is too large
View File


Loading…
Cancel
Save