Ticket #2910: Separate BBF packages to their own packages with their own config files

This commit is contained in:
Amin Ben Ramdhane 2020-09-10 10:59:47 +01:00
parent c61aa804ce
commit ea57418313
90 changed files with 11124 additions and 316 deletions

View file

@ -5,11 +5,11 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=libbbfdm
PKG_VERSION:=2.0-2020-09-08
PKG_VERSION:=2.0-2020-09-10
PKG_FIXUP:=autoreconf
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/bbf.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=8d721664e64aefb4625160909f5fdc176578afdb
PKG_SOURCE_VERSION:=720d7041218ec32a2397470cbebceb42debec44f
PKG_RELEASE=$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)

View file

@ -1,310 +0,0 @@
#
# Copyright (C) 2019 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=icwmp
PKG_VERSION:=4.0-2020-09-09
PKG_FIXUP:=autoreconf
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/icwmp.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=bbb2a21e8ffdbf15541ab4ab5aeb13b1fc297a85
PKG_RELEASE=$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPLv2
PKG_LICENSE_FILES:=COPYING
PKG_CONFIG_DEPENDS:= \
CONFIG_CWMP_ACS_MULTI \
CONFIG_CWMP_ACS_HDM \
CONFIG_CWMP_DEBUG \
CONFIG_CWMP_DEVEL_DEBUG
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
CWMP_REVISION=$(shell svnversion ./src/ -n|cut -f2 -d:)
include $(INCLUDE_DIR)/package.mk
define Package/icwmp_stun
SECTION:=utils
CATEGORY:=Utilities
TITLE:=TR-069 stun Client
DEPENDS:=+PACKAGE_icwmp_stun:libubus +PACKAGE_icwmp_stun:libuci +PACKAGE_icwmp_stun:libubox +PACKAGE_icwmp_stun:libjson-c +PACKAGE_icwmp_stun:libopenssl +PACKAGE_icwmp_stun:libblobmsg-json
endef
define Package/icwmp_xmpp
SECTION:=utils
CATEGORY:=Utilities
TITLE:=TR-069 xmpp feature
DEPENDS:=+PACKAGE_icwmp_xmpp:libuci +PACKAGE_icwmp_xmpp:libubox +PACKAGE_icwmp_xmpp:libexpat +PACKAGE_icwmp_xmpp:libstrophe
endef
define Package/icwmp_twamp
SECTION:=utils
CATEGORY:=Utilities
TITLE:=TR-069 twamp feature
DEPENDS:=+PACKAGE_icwmp_twamp:libuci
endef
define Package/icwmp_udpechoserver
SECTION:=utils
CATEGORY:=Utilities
TITLE:=TR-069 udpechoserver feature
DEPENDS:=+PACKAGE_icwmp_udpechoserver:libuci
endef
define Package/icwmp_bulkdata
SECTION:=utils
CATEGORY:=Utilities
TITLE:=TR-069 BulkData Collection
DEPENDS:=+PACKAGE_icwmp_bulkdata:libubus +PACKAGE_icwmp_bulkdata:libuci +PACKAGE_icwmp_bulkdata:libubox +PACKAGE_icwmp_bulkdata:libjson-c +PACKAGE_icwmp_bulkdata:libcurl +PACKAGE_icwmp_bulkdata:curl +PACKAGE_icwmp_bulkdata:libblobmsg-json +PACKAGE_icwmp_bulkdata:libbbfdm
endef
define Package/icwmp-tr098
SECTION:=utils
CATEGORY:=Utilities
TITLE:=CWMP client for TR-098 Data Model
DEPENDS:=+PACKAGE_icwmp-tr098:libuci +PACKAGE_icwmp-tr098:libmicroxml +PACKAGE_icwmp-tr098:libubox +PACKAGE_icwmp-tr098:jshn +PACKAGE_icwmp-tr098:libubus +PACKAGE_icwmp-tr098:libblobmsg-json +PACKAGE_icwmp-tr098:libpthread +PACKAGE_icwmp-tr098:ubusd +PACKAGE_icwmp-tr098:shflags +PACKAGE_icwmp-tr098:getopt +PACKAGE_icwmp-tr098:zlib +PACKAGE_icwmp-tr098:libjson-c +PACKAGE_icwmp-tr098:libopenssl +PACKAGE_icwmp-tr098:curl +PACKAGE_icwmp-tr098:libcurl +PACKAGE_icwmp-tr098:libtr098
endef
define Package/icwmp_notifd
SECTION:=utils
CATEGORY:=Utilities
TITLE:=CWMP Active Notifications Detect tool
DEPENDS:=+PACKAGE_icwmp_notifd:libubox +PACKAGE_icwmp_notifd:libuci +PACKAGE_icwmp_notifd:libbbfdm +PACKAGE_icwmp_notifd:libjson-c +PACKAGE_icwmp_notifd:libubus +PACKAGE_icwmp_notifd:libblobmsg-json
endef
define Package/icwmp/Default
SECTION:=utils
CATEGORY:=Utilities
TITLE:=CWMP client
DEPENDS:=+libuci +libmicroxml +libubox +jshn +libubus +libblobmsg-json +libpthread +ubusd +shflags +getopt +zlib +libjson-c +libopenssl +curl +libbbfdm +libbbf_api
endef
define Package/icwmp/description
A free client implementation of CWMP (TR-069) protocol
endef
define Package/icwmp-curl
$(call Package/icwmp/Default)
TITLE+= (using libcurl)
DEPENDS+= +PACKAGE_icwmp-curl:libcurl
VARIANT:=curl
endef
define Package/icwmp-zstream
$(call Package/icwmp/Default)
TITLE+= (using libzstream)
DEPENDS+= +PACKAGE_icwmp-zstream:libzstream
VARIANT:=zstream
endef
define Package/icwmp-curl/config
source "$(SOURCE)/Config_cwmp.in"
endef
USE_LOCAL=$(shell ls ./src/ 2>/dev/null >/dev/null && echo 1)
ifneq ($(USE_LOCAL),)
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
endif
TARGET_CFLAGS += \
-D_GNU_SOURCE -D_AADJ
ifeq ($(CONFIG_PACKAGE_icwmp-tr098),y)
CONFIGURE_ARGS += \
--enable-icwmp_tr098
endif
ifeq ($(CONFIG_PACKAGE_icwmp_xmpp),y)
CONFIGURE_ARGS += \
--enable-icwmp_xmpp
endif
ifeq ($(CONFIG_PACKAGE_icwmp_stun),y)
CONFIGURE_ARGS += \
--enable-icwmp_stun
endif
ifeq ($(CONFIG_PACKAGE_icwmp_udpechoserver),y)
CONFIGURE_ARGS += \
--enable-icwmp_udpechoserver
endif
ifeq ($(CONFIG_PACKAGE_icwmp_twamp),y)
CONFIGURE_ARGS += \
--enable-icwmp_twamp
endif
ifeq ($(CONFIG_PACKAGE_icwmp_bulkdata),y)
CONFIGURE_ARGS += \
--enable-icwmp_bulkdata
endif
ifeq ($(CONFIG_PACKAGE_icwmp_notifd),y)
CONFIGURE_ARGS += \
--enable-icwmp_notifd
endif
ifneq ($(CWMP_REVISION)_,_)
ifneq ($(CWMP_REVISION),exported)
ifneq ($(CWMP_REVISION),Unversioned directory)
TARGET_CFLAGS += "-DCWMP_REVISION=\\\"$(CWMP_REVISION)\\\""
TARGET_LDFLAGS += "-DCWMP_REVISION=\\\"$(CWMP_REVISION)\\\""
endif
endif
endif
TARGET_LDFLAGS += \
-Wl,-rpath-link=$(STAGING_DIR)/usr/lib
CONFIGURE_ARGS += \
--with-uci-include-path=$(STAGING_DIR)/usr/include \
--with-libubox-include-path=$(STAGING_DIR)/usr/include \
--with-libubus-include-path=$(STAGING_DIR)/usr/include
ifeq ($(BUILD_VARIANT),zstream)
CONFIGURE_ARGS += \
--enable-http=zstream \
--with-zstream-include-path=$(STAGING_DIR)/usr/include
endif
ifeq ($(BUILD_VARIANT),curl)
CONFIGURE_ARGS += \
--enable-http=curl
endif
ifeq ($(CONFIG_CWMP_ACS_MULTI),y)
CONFIGURE_ARGS += \
--enable-acs=multi
endif
ifeq ($(CONFIG_CWMP_ACS_HDM),y)
CONFIGURE_ARGS += \
--enable-acs=hdm
endif
ifeq ($(CONFIG_CWMP_DEBUG),y)
CONFIGURE_ARGS += \
--enable-debug
endif
ifeq ($(CONFIG_CWMP_DEVEL_DEBUG),y)
CONFIGURE_ARGS += \
--enable-devel
endif
define Package/icwmp-$(BUILD_VARIANT)/install
$(INSTALL_DIR) $(1)/etc/icwmpd
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_BUILD_DIR)/bin/icwmpd $(1)/usr/sbin
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/firewall.cwmp $(1)/etc/firewall.cwmp
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmpd.init $(1)/etc/init.d/icwmpd
$(INSTALL_BIN) $(PKG_BUILD_DIR)/uci-defaults/* $(1)/etc/uci-defaults/
ifeq ($(CONFIG_CWMP_SCRIPTS_FULL),y)
$(INSTALL_DIR) $(1)/usr/share/icwmp
$(CP) $(PKG_BUILD_DIR)/scripts/defaults $(1)/usr/share/icwmp
$(CP) $(PKG_BUILD_DIR)/scripts/functions $(1)/usr/share/icwmp
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/scripts/icwmp.sh $(1)/usr/sbin/icwmp
endif
$(CP) ./files/* $(1)/
endef
define Package/icwmp-tr098/install
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_BUILD_DIR)/bin/icwmp_tr098d $(1)/usr/sbin
endef
define Package/icwmp_stun/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_stund $(1)/usr/sbin/icwmp_stund
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmp_stund $(1)/etc/init.d/icwmp_stund
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp_stun $(1)/etc/config
endef
define Package/icwmp_xmpp/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_xmppd $(1)/usr/sbin/icwmp_xmppd
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmp_xmppd $(1)/etc/init.d/icwmp_xmppd
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp_xmpp $(1)/etc/config
$(CP) ./xmpp-files/* $(1)/
endef
define Package/icwmp_udpechoserver/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_udpechoserverd $(1)/usr/sbin/icwmp_udpechoserverd
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmp_udpechoserverd $(1)/etc/init.d/icwmp_udpechoserverd
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp_udpechoserver $(1)/etc/config
endef
define Package/icwmp_twamp/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_twampd $(1)/usr/sbin/icwmp_twampd
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmp_twampd $(1)/etc/init.d/icwmp_twampd
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp_twamp $(1)/etc/config
endef
define Package/icwmp_bulkdata/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_bulkdatad $(1)/usr/sbin/icwmp_bulkdatad
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(PKG_BUILD_DIR)/init/icwmp_bulkdatad $(1)/etc/init.d/icwmp_bulkdatad
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) $(PKG_BUILD_DIR)/config/cwmp_bulkdata $(1)/etc/config
endef
define Package/icwmp_notifd/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_notifd $(1)/usr/sbin/icwmp_notifd
endef
define Package/icwmp-$(BUILD_VARIANT)/postinst
#!/bin/sh
echo "$(CWMP_BKP_FILE)" >> $${IPKG_INSTROOT}/etc/sysupgrade.conf
if [ -z "$${IPKG_INSTROOT}" ]; then
echo "Enabling rc.d symlink for icwmpd"
/etc/init.d/icwmpd enable
fi
exit 0
endef
define Package/icwmp-$(BUILD_VARIANT)/prerm
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
echo "Disabling rc.d symlink for icwmpd"
/etc/init.d/icwmpd disable
fi
exit 0
endef
$(eval $(call BuildPackage,icwmp-curl))
$(eval $(call BuildPackage,icwmp_stun))
$(eval $(call BuildPackage,icwmp_xmpp))
$(eval $(call BuildPackage,icwmp_udpechoserver))
$(eval $(call BuildPackage,icwmp_twamp))
$(eval $(call BuildPackage,icwmp_bulkdata))
$(eval $(call BuildPackage,icwmp-tr098))
$(eval $(call BuildPackage,icwmp_notifd))
#$(eval $(call BuildPackage,icwmp-zstream))

44
trx69/bulkdata/Makefile Executable file
View file

@ -0,0 +1,44 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=bulkdata
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=BBF BulkData Collection
DEPENDS:=+libubus +libuci +libubox +libjson-c +libcurl +curl +libblobmsg-json +libbbfdm +libbbf_api
endef
define Package/$(PKG_NAME)/description
BBF BulkData Collection
endef
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
TARGET_CFLAGS += \
-D_GNU_SOURCE
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bulkdatad $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/lib/bbfdm
$(INSTALL_BIN) $(PKG_BUILD_DIR)/*.so $(1)/usr/lib/bbfdm
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View file

@ -0,0 +1,43 @@
config bulkdata 'bulkdata'
option enable '0'
#Log levels: Critical=0, Warning=1, Notice=2, Info=3, Debug=4
option log_level '3'
config profile
option profile_id '1'
option enable '0'
option name ''
option nbre_of_retained_failed_reports '0'
option protocol 'http'
option encoding_type ''
option reporting_interval '86400'
option time_reference '0'
option csv_encoding_field_separator ','
option csv_encoding_row_separator '
'
option csv_encoding_escape_character '"'
option csv_encoding_report_format 'column'
option csv_encoding_row_time_stamp 'unix'
option json_encoding_report_format 'objecthierarchy'
option json_encoding_report_time_stamp 'unix'
option http_url ''
option http_username ''
option http_password ''
option http_compression 'none'
option http_method 'post'
option http_use_date_header '1'
option http_retry_enable '0'
option http_retry_minimum_wait_interval '5'
option http_retry_interval_multiplier '2000'
option http_persist_across_reboot '0'
config profile_parameter
option profile_id '1'
option name ''
option reference ''
config profile_http_request_uri_parameter
option profile_id '1'
option name ''
option reference ''

View file

@ -0,0 +1,34 @@
#!/bin/sh /etc/rc.common
# Bulkdata Software
# Copyright (C) 2020 iopsys Software Solutions AB
# Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
START=99
STOP=10
USE_PROCD=1
PROG="/usr/sbin/bulkdatad"
start_service() {
local bulkdata_enable=`uci -q get bulkdata.bulkdata.enable`
if [ "$bulkdata_enable" = "1" ]; then
procd_open_instance
procd_set_param command "$PROG"
procd_set_param respawn "3" "7" "0"
procd_close_instance
fi
}
boot() {
start
}
reload_service() {
stop
start
}
service_triggers()
{
procd_add_reload_trigger bulkdata
}

View file

@ -0,0 +1,23 @@
PROG = bulkdatad
LIB = libbulkdata.so
PROG_OBJS = bulkdata.o common.o config.o http.o log.o report.o times.o buci.o
LIB_OBJS = datamodel.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) -lubus -luci -lubox -ljson-c -lcurl -lblobmsg_json -lbbfdm
LIB_LDFLAGS = $(LDFLAGS) -lbbf_api
%.o: %.c
$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
all: $(PROG) $(LIB)
$(PROG): $(PROG_OBJS)
$(CC) $(PROG_CFLAGS) -o $@ $^ $(PROG_LDFLAGS)
$(LIB): $(LIB_OBJS)
$(CC) $(PROG_CFLAGS) $(LIB_LDFLAGS) -shared -o $@ $^
clean:
rm -f *.o $(PROG) $(LIB)

265
trx69/bulkdata/src/buci.c Normal file
View file

@ -0,0 +1,265 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include "buci.h"
struct uci_context *uci_ctx = NULL;
int buci_init(void)
{
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return -1;
}
return 0;
}
int buci_fini(void)
{
if (uci_ctx) {
uci_free_context(uci_ctx);
}
return 0;
}
static bool buci_validate_section(const char *str)
{
if (!*str)
return false;
for (; *str; str++) {
unsigned char c = *str;
if (isalnum(c) || c == '_')
continue;
return false;
}
return true;
}
static int buci_init_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *package, char *section, char *option, char *value)
{
memset(ptr, 0, sizeof(struct uci_ptr));
/* value */
if (value) {
ptr->value = value;
}
ptr->package = package;
if (!ptr->package)
goto error;
ptr->section = section;
if (!ptr->section) {
ptr->target = UCI_TYPE_PACKAGE;
goto lastval;
}
ptr->option = option;
if (!ptr->option) {
ptr->target = UCI_TYPE_SECTION;
goto lastval;
} else {
ptr->target = UCI_TYPE_OPTION;
}
lastval:
if (ptr->section && !buci_validate_section(ptr->section))
ptr->flags |= UCI_LOOKUP_EXTENDED;
return 0;
error:
return -1;
}
struct uci_section *buci_walk_section(char *package, char *section_type, struct uci_section *prev_section)
{
struct uci_ptr ptr;
struct uci_element *e;
struct uci_section *next_section;
if (section_type == NULL) {
if (prev_section) {
e = &prev_section->e;
if (e->list.next == &prev_section->package->sections)
return NULL;
e = container_of(e->list.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
}
else {
if (buci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL)) {
return NULL;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return NULL;
}
if (ptr.p->sections.next == &ptr.p->sections)
return NULL;
e = container_of(ptr.p->sections.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
}
}
else {
struct uci_list *ul, *shead = NULL;
if (prev_section) {
ul = &prev_section->e.list;
shead = &prev_section->package->sections;
}
else {
if (buci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL)) {
return NULL;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return NULL;
}
ul = &ptr.p->sections;
shead = &ptr.p->sections;
}
while (ul->next != shead) {
e = container_of(ul->next, struct uci_element, list);
next_section = uci_to_section(e);
if (strcmp(next_section->type, section_type) == 0)
return next_section;
ul = ul->next;
}
return NULL;
}
return NULL;
}
void buci_print_list(struct uci_list *uh, char **val, char *delimiter)
{
struct uci_element *e;
static char buffer[512];
char *buf = buffer;
*buf = '\0';
uci_foreach_element(uh, e) {
if (*buf) {
strcat(buf, delimiter);
strcat(buf, e->name);
}
else {
strcpy(buf, e->name);
}
}
*val = buf;
}
struct uci_element *buci_lookup_list(struct uci_list *list, const char *name)
{
struct uci_element *e;
uci_foreach_element(list, e) {
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
int uci_lookup_ptr_bysection(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *section, char *option, char *value)
{
struct uci_element *e;
memset(ptr, 0, sizeof(struct uci_ptr));
ptr->package = section->package->e.name;
ptr->section = section->e.name;
ptr->option = option;
ptr->value = value;
ptr->flags |= UCI_LOOKUP_DONE;
ptr->p = section->package;
ptr->s = section;
if (ptr->option) {
e = buci_lookup_list(&ptr->s->options, ptr->option);
if (!e)
return UCI_OK;
ptr->o = uci_to_option(e);
ptr->last = e;
ptr->target = UCI_TYPE_OPTION;
}
else {
ptr->last = &ptr->s->e;
ptr->target = UCI_TYPE_SECTION;
}
ptr->flags |= UCI_LOOKUP_COMPLETE;
return UCI_OK;
}
char *buci_get_value_bysection(struct uci_section *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (uci_lookup_ptr_bysection(uci_ctx, &ptr, section, option, NULL) != UCI_OK) {
return val;
}
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
buci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}
char *buci_get_value(char *package, char *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (!section || !option)
return val;
if (buci_init_ptr(uci_ctx, &ptr, package, section, option, NULL)) {
return val;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return val;
}
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
buci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}

31
trx69/bulkdata/src/buci.h Normal file
View file

@ -0,0 +1,31 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __BUCI_H
#define __BUCI_H
#include <uci.h>
int buci_init(void);
int buci_fini(void);
struct uci_section *buci_walk_section(char *package, char *section_type, struct uci_section *prev_section);
void buci_print_list(struct uci_list *uh, char **val, char *delimiter);
struct uci_element *buci_lookup_list(struct uci_list *list, const char *name);
int uci_lookup_ptr_bysection(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *section, char *option, char *value);
char *buci_get_value_bysection(struct uci_section *section, char *option);
char *buci_get_value(char *package, char *section, char *option);
#define buci_foreach_section(package, section_type, section) \
for (section = buci_walk_section(package, section_type, NULL); \
section != NULL; \
section = buci_walk_section(package, section_type, section))
#endif //__BUCI_H

View file

@ -0,0 +1,144 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Omar Kallel <omar.kallel@pivasoftware.com>
*/
#include <stdio.h>
#include <string.h>
#include <libubox/uloop.h>
#include "http.h"
#include "config.h"
#include "log.h"
#include "common.h"
#include "report.h"
#include "times.h"
#include "bulkdata.h"
struct bulkdata bulkdata_main = {0};
int profiles_number = 0;
void bulkdata_profile_cb(struct uloop_timeout *timeout);
int get_retry_period(int min)
{
srand(time(NULL));
return rand()%min + min;
}
static void bulkdata_run_profiles(struct bulkdata *bulkdata)
{
unsigned int next_period;
int i = 0;
for (i = 0; i < profiles_number; i++) {
bulkdata->profile[i].utimer.cb = bulkdata_profile_cb;
LIST_HEAD(failedreports);
bulkdata->profile[i].failed_reports = &failedreports;
next_period = get_next_period(bulkdata->profile[i].time_reference, bulkdata->profile[i].reporting_interval);
bulkdata_log(SINFO, "The session of profile_id %d will be start in %d sec", bulkdata->profile[i].profile_id, next_period);
uloop_timeout_set(&bulkdata->profile[i].utimer, next_period * 1000);
}
}
int http_send_report(struct profile *profile, char *report)
{
char *msg_in = NULL;
int http_code;
http_client_init(profile);
bulkdata_log(SINFO, "Send the report of profile_id %d to Bulkdata Collector", profile->profile_id);
http_code = http_send_message(profile, report, strlen(report), &msg_in);
http_client_exit();
return http_code;
}
void bulkdata_profile_cb(struct uloop_timeout *timeout)
{
struct profile *profile;
unsigned int http_code, retry_period;
char *report = NULL;
profile = container_of(timeout, struct profile, utimer);
time_t now = time(NULL);
bulkdata_log(SINFO, "New session of profile_id %d started", profile->profile_id);
if(profile->retry_count == 0 || profile->next_retry > now || !profile->http_retry_enable) //Perdiodic execution
create_encoding_bulkdata_report(profile, &report);
else
create_failed_report(profile, &report);
bulkdata_log(SDEBUG, "The content of the profile_id report %d is :\n==========\n%s\n==========\n", profile->profile_id, report);
http_code= http_send_report(profile, report);
if(http_code != 200){
if(profile->retry_count == 0 || profile->next_retry > now || !profile->http_retry_enable) { //Perdiodic execution
retry_period = get_retry_period(profile->http_retry_minimum_wait_interval);
profile->next_period = now + profile->reporting_interval;
profile->next_retry = now + retry_period;
profile->retry_count = 1;
profile->min_retry = profile->http_retry_minimum_wait_interval * 2;
if((profile->next_retry < profile->next_period) && profile->http_retry_enable) {
bulkdata_log(SINFO, "Retry session of profile_id %d in %d sec", profile->profile_id, retry_period);
uloop_timeout_set(timeout, 1000 * retry_period);
}
else {
bulkdata_log(SINFO, "Start New session of profile_id %d in %d sec", profile->profile_id, profile->reporting_interval);
uloop_timeout_set(timeout, 1000 * profile->reporting_interval);
}
} else { //Retry execution
retry_period= get_retry_period(profile->min_retry);
profile->min_retry*=2;
profile->next_retry+=retry_period;
profile->retry_count++;
if(profile->next_retry < profile->next_period) {
bulkdata_log(SINFO, "Retry session of profile_id %d in %d sec", profile->profile_id, retry_period);
uloop_timeout_set(timeout, 1000 * retry_period);
}
else {
bulkdata_log(SINFO, "Retry session of profile_id %d in %d sec", profile->profile_id, (profile->next_period-profile->next_retry+retry_period));
uloop_timeout_set(timeout, 1000 * (profile->next_period-profile->next_retry+retry_period));
}
}
if(profile->new_report){
bulkdata_add_failed_report(profile, profile->new_report);
FREE(profile->new_report);
}
FREE(report);
} else {
if(profile->retry_count == 0 || profile->next_retry > now || !profile->http_retry_enable) {
bulkdata_log(SINFO, "Start New session of profile_id %d in %d sec", profile->profile_id, profile->reporting_interval);
uloop_timeout_set(timeout, 1000 * profile->reporting_interval);
}
else {
bulkdata_log(SINFO, "Retry session of profile_id %d in %d sec", profile->profile_id, (profile->next_period-profile->next_retry));
uloop_timeout_set(timeout, 1000 * (profile->next_period-profile->next_retry));
}
FREE(profile->new_report);
FREE(report);
empty_failed_reports_list(profile);
profile->retry_count= 0;
}
}
int main(void)
{
struct bulkdata *bulkdata = &bulkdata_main;
if (bulkdata_config_init(bulkdata) == -1)
return -1;
bulkdata_log(SINFO, "Start bulkdatad daemon");
uloop_init();
bulkdata_run_profiles(bulkdata);
uloop_run();
uloop_done();
bulkdata_config_fini(bulkdata);
bulkdata_log(SINFO, "Stop bulkdatad daemon");
return 0;
}

View file

@ -0,0 +1,18 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __BULKDATA_H
#define __BULKDATA_H
extern struct bulkdata bulkdata_main;
extern int profiles_number;
#endif /* __BULKDATA_H */

View file

@ -0,0 +1,105 @@
# README #
bulkdatad is an implementation of The HTTP bulk data collection mechanism which is an extended feature of CPE and other agents implementing TR-069(CWMP) or TR-369(USP), defined by the Broadband Forum. It provides a means by which an Auto-Configuration Server (ACS), or USP Controller, can configure an agent to periodically send a JSON or CSV formatted set of Device information to an HTTP server running a data collection application.
## Configuration File ##
The bulkdatad UCI configuration is located in **'/etc/config/bulkdata'**, and contains 4 sections: **bulkdata**, **profile**, **profile\_parameter** and **profile\_http\_request\_uri\_parameter**.
```
config bulkdata 'bulkdata'
option enable '0'
option log_level '3'
config profile
option profile_id '1'
option enable '0'
option csv_encoding_row_time_stamp 'unix'
option json_encoding_report_time_stamp 'unix'
option http_retry_minimum_wait_interval '5'
option http_retry_interval_multiplier '2000'
config profile_parameter
option profile_id '1'
option name ''
option reference ''
config profile_http_request_uri_parameter
option profile_id '1'
option name ''
option reference ''
```
### bulkdata section ###
It defines **bulkdata configuration**: enable and log\_level.
| Name | Type | Description |
| ----------- | ------- | ----------------------------------------------------------------------------------------------- |
| `enable` | boolean | Enables the BulkData feature if set to **1**. |
| `log_level` | integer | Specifies the log type to use, by default **'INFO'**. The possible types are **'EMERG', 'ALERT', 'CRITIC' ,'ERROR', 'WARNING', 'NOTICE', 'INFO' and 'DEBUG'**. |
### profile section ###
It defines **the profile section configuration**: enable, name,... The possible options for **profile** section are listed below:
| Name | Type | Description |
| ---------------------------------- | ------- | ---------------------------------------------- |
| `profile_id` | integer | The profile id to use. |
| `enable` | boolean | If set to **1**, enables the bulkdata profile. |
| `name` | string | The name of the profile. |
| `nbre_of_retained_failed_reports` | integer | The number of failed reports to be retained and transmitted at the end of the current reporting interval. |
| `protocol` | string | The protocol used for the collection profile. |
| `encoding_type` | string | The encoding type used for the collection profile. |
| `reporting_interval` | integer | The reporting interval in seconds. |
| `time_reference` | integer | The time reference to determine when the profile will be transmitted to the ACS collector. |
| `csv_encoding_field_separator` | string | The field separator to use when encoding CSV data. |
| `csv_encoding_row_separator` | string | The row separator to use when encoding CSV data. |
| `csv_encoding_escape_character` | string | The escape character to use when encoding CSV data. |
| `csv_encoding_report_format` | string | Describes how reports will be formatted. Two possible formats are supported: **'ParameterPerRow' and 'ParameterPerColumn'**. |
| `csv_encoding_row_time_stamp` | string | The format of the timestamp to use for data inserted into the row. The row time stamp supported are **'Unix-Epoch', 'ISO-8601' and 'None'**. |
| `json_encoding_report_format` | string | Describes the report format. The supported report formats are **'ObjectHierarchy' and 'NameValuePair'**. |
| `json_encoding_report_time_stamp` | string | The format of the timestamp to use for the JSON Object named "CollectionTime". The supported timestamp are **'Unix-Epoch', 'ISO-8601' and 'None'**. |
| `http_url` | string | The URL of the collection server. |
| `http_username` | string | The username of the collection server. |
| `http_password` | string | The password of the collection server. |
| `http_compression` | string | The HTTP Compression mechanism used by the collection server. The supported compression mechanism are **'GZIP', 'Compress' and 'Deflate'**. |
| `http_method` | string | The HTTP method used by the collection server. Two methods are supported: **'POST' and 'PUT'**. |
| `http_use_date_header` | boolean | If set to **1**, the CPE encodes the HTTP Date Header. |
| `http_retry_enable` | boolean | If set to **1**, the CPE retries unsuccessful attempts to transfer data. |
| `http_retry_minimum_wait_interval` | integer | The data transfer retry wait interval. |
| `http_retry_interval_multiplier` | integer | The retry interval multiplier. |
| `http_persist_across_reboot` | boolean | If set to **1**, failed data transfers must be persisted across reboots. |
### profile_parameter section ###
It defines **the profile\_parameter section configuration**: profile\_id, name, reference.
| Name | Type | Description |
| ------------ | ------- | --------------------------------------- |
| `profile_id` | integer | The id of the used profile. |
| `name` | string | The name of the profile parameter. |
| `reference` | string | The reference of the profile parameter. |
### profile_http_request_uri_parameter section ###
It defines **the profile\_http\_request\_uri\_parameter section configuration**: profile\_id, name, reference.
| Name | Type | Description |
| ------------ | ------- | --------------------------------------- |
| `profile_id` | integer | The id of the used profile. |
| `name` | string | The name of the Request-URI parameter. |
| `reference` | string | The reference of the profile parameter. |
## Dependencies ##
To successfully build bulkdatad, the following libraries are needed:
| Dependency | Link | License |
| ----------- | ------------------------------------------- | -------------- |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |
| libubox | https://git.openwrt.org/project/libubox.git | BSD |
| libjson-c | https://s3.amazonaws.com/json-c_releases | MIT |
| libcurl | https://dl.uxnr.de/mirror/curl | MIT |
| libbbfdm | https://dev.iopsys.eu/iopsys/bbf.git | LGPL 2.1 |

450
trx69/bulkdata/src/common.c Normal file
View file

@ -0,0 +1,450 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Omar Kallel <omar.kallel@pivasoftware.com>
*/
#include "common.h"
static pathnode *head = NULL;
static pathnode *temphead = NULL;
int bulkdata_dm_ctx_init(struct dmctx *ctx)
{
struct bulkdata *bulkdata = &bulkdata_main;
dm_ctx_init(ctx, DM_CWMP, bulkdata->amd_version, bulkdata->instance_mode);
return 0;
}
int bulkdata_dm_ctx_clean(struct dmctx *ctx)
{
dm_ctx_clean(ctx);
return 0;
}
static char **str_split(const char* str, const char* delim, size_t* numtokens)
{
char *s = strdup(str);
size_t tokens_alloc = 1;
size_t tokens_used = 0;
char **tokens = calloc(tokens_alloc, sizeof(char*));
char *token, *strtok_ctx;
for (token = strtok_r(s, delim, &strtok_ctx); token != NULL; token = strtok_r(NULL, delim, &strtok_ctx)) {
if (tokens_used == tokens_alloc) {
tokens_alloc *= 2;
tokens = realloc(tokens, tokens_alloc * sizeof(char*));
}
tokens[tokens_used++] = strdup(token);
}
// cleanup
if (tokens_used == 0) {
FREE(tokens);
} else {
tokens = realloc(tokens, tokens_used * sizeof(char*));
}
*numtokens = tokens_used;
FREE(s);
return tokens;
}
static bool bulkdata_match(const char *string, const char *pattern)
{
regex_t re;
if (regcomp(&re, pattern, REG_EXTENDED) != 0) return 0;
int status = regexec(&re, string, 0, NULL, 0);
regfree(&re);
if (status != 0) return false;
return true;
}
static bool is_res_required(char *str, int *start, int *len)
{
char temp_char[NAME_MAX] = {'\0'};
if (bulkdata_match(str, GLOB_CHAR)) {
int s_len = strlen(str);
int b_len = s_len, p_len = s_len;
char *star = strchr(str, '*');
if(star)
s_len = star - str;
*start = MIN(MIN(s_len, p_len), b_len);
if (*start == s_len)
*len = 1;
strncpy(temp_char, str+*start, *len);
if (bulkdata_match(temp_char, "[*+]+"))
return true;
}
*start = strlen(str);
return false;
}
static void insert(char *data, bool active)
{
pathnode *link = (pathnode*) calloc(1, sizeof(pathnode));
if(!link) {
return;
}
link->ref_path = data;
if(active) {
link->next = head;
head = link;
} else {
link->next = temphead;
temphead = link;
}
}
static void swap_heads(void)
{
pathnode *temp = head;
head = temphead;
temphead = temp;
}
static void deleteList(void)
{
pathnode *ptr = head, *temp;
while(ptr != NULL) {
temp = ptr;
free(ptr->ref_path);
if(ptr->next != NULL) {
ptr = ptr->next;
} else {
ptr = NULL;
}
free(temp);
}
head = NULL;
swap_heads();
}
void bulkdata_add_data_to_list(struct list_head *dup_list, char *name, char *value, char *type)
{
struct resultsnode *link;
link = calloc(1, sizeof(struct resultsnode));
list_add_tail(&link->list, dup_list);
link->name = strdup(name);
link->data = strdup(value);
link->type = strdup(type);
}
void bulkdata_delete_data_from_list(struct resultsnode *link)
{
list_del(&link->list);
FREE(link->name);
FREE(link->data);
FREE(link->type);
FREE(link);
}
void bulkdata_free_data_from_list(struct list_head *dup_list)
{
struct resultsnode *link;
while (dup_list->next != dup_list) {
link = list_entry(dup_list->next, struct resultsnode, list);
bulkdata_delete_data_from_list(link);
}
}
static bool bulkdata_get(int operation, char *path, struct dmctx *dm_ctx)
{
int fault = 0;
switch(operation) {
case CMD_GET_NAME:
fault = dm_entry_param_method(dm_ctx, CMD_GET_NAME, path, "true", NULL);
break;
case CMD_GET_VALUE:
fault = dm_entry_param_method(dm_ctx, CMD_GET_VALUE, path, NULL, NULL);
break;
default:
return false;
}
if (dm_ctx->list_fault_param.next != &dm_ctx->list_fault_param) {
return false;
}
if (fault) {
return false;
}
return true;
}
char *bulkdata_get_value_param(char *path)
{
struct dmctx ctx = {0};
struct dm_parameter *n;
char *value = NULL;
bulkdata_dm_ctx_init(&ctx);
if(bulkdata_get(CMD_GET_VALUE, path, &ctx)) {
list_for_each_entry(n, &ctx.list_parameter, list) {
value = strdup(n->data);
break;
}
}
bulkdata_dm_ctx_clean(&ctx);
return value;
}
void bulkdata_get_value(char *path, struct list_head *list)
{
struct dmctx ctx = {0};
struct dm_parameter *n;
bulkdata_dm_ctx_init(&ctx);
if(bulkdata_get(CMD_GET_VALUE, path, &ctx)) {
list_for_each_entry(n, &ctx.list_parameter, list) {
bulkdata_add_data_to_list(list, n->name, n->data, n->type);
}
}
bulkdata_dm_ctx_clean(&ctx);
}
bool bulkdata_get_name(char *path)
{
struct dmctx ctx = {0};
struct dm_parameter *n;
bool ret = false;
bulkdata_dm_ctx_init(&ctx);
if(bulkdata_get(CMD_GET_NAME, path, &ctx)) {
list_for_each_entry(n, &ctx.list_parameter, list) {
insert(strdup(n->name), false);
}
ret = true;
}
bulkdata_dm_ctx_clean(&ctx);
return ret;
}
static void fill_node_path(void)
{
pathnode *p=head;
while(p!=NULL) {
bulkdata_get_name(p->ref_path);
p=p->next;
}
deleteList();
}
static void bulkdata_filter_results(char *path, int start, int end)
{
int startpos = start, m_index = 0, m_len = 0;
char *pp = path + startpos;
char exp[NAME_MAX] = {'\0'};
if(start >= end) {
return;
}
if(!is_res_required(pp, &m_index, &m_len)) {
//append rest of the path to the final list
if(pp == path ) {
insert(strdup(pp), true);
return;
}
pathnode *p = head;
while(p != NULL) {
char name[NAME_MAX] = {'\0'};
strcpy(name, p->ref_path);
strcat(name, pp);
insert(strdup(name), false);
p = p->next;
}
deleteList();
return;
}
// Get the string before the match
char name[NAME_MAX]={'\0'};
strncpy(name, pp, m_index);
pathnode *p = head;
if(p == NULL) {
insert(strdup(name), false);
}
while(p != NULL) {
char ref_name[NAME_MAX] = {'\0'};
sprintf(ref_name, "%s%s", p->ref_path, name);
insert(strdup(ref_name), false);
p = p->next;
}
deleteList();
startpos += m_index;
strncpy(exp, pp+m_index, m_len);
pp = path + startpos;
fill_node_path();
startpos += 2;
bulkdata_filter_results(path, startpos, end);
}
static void bulkdata_parse_results(struct list_head *list)
{
pathnode *p = head;
while(p != NULL) {
bulkdata_get_value(p->ref_path, list);
p = p->next;
}
deleteList();
}
void bulkdata_get_value_results(char *path, struct list_head *list)
{
bulkdata_filter_results(path, 0, strlen(path));
bulkdata_parse_results(list);
}
char *create_request_url(struct profile *profile)
{
int i = 0, http_uri_number = profile->profile_http_request_uri_parameter_number;
char *value, *uri_param = NULL, *uri_tmp = NULL, *http_url = NULL;
for (i = 0; i < http_uri_number; i++)
{
if((profile->profile_http_uri_parameter[i].reference == NULL) || (profile->profile_http_uri_parameter[i].name == NULL))
continue;
value = bulkdata_get_value_param(profile->profile_http_uri_parameter[i].reference);
if(!uri_param) {
asprintf(&uri_param, "&%s=%s", profile->profile_http_uri_parameter[i].name, value);
free(value);
}
else {
uri_tmp = strdup(uri_param);
free(uri_param);
asprintf(&uri_param, "%s&%s=%s", uri_tmp, profile->profile_http_uri_parameter[i].name, value);
free(value);
free(uri_tmp);
}
}
if(uri_param) {
asprintf(&http_url, "%s%s", profile->http_url, uri_param);
free(uri_param);
} else {
asprintf(&http_url, "%s", profile->http_url);
}
return http_url;
}
char *get_bulkdata_profile_parameter_name(char *paramref, char *paramname, char *param)
{
char **paramarr, *idx1 = NULL, *idx2 = NULL, *res = NULL, *instance = NULL, *tmp = NULL, *retparam = NULL, *s = NULL;
int i, j = 0;
size_t length;
if(paramname == NULL || strlen(paramname) <= 0)
return strdup(param);
paramarr = str_split(paramref, "*", &length);
res = strdup(paramname);
for(i = 0; i < length; i++) {
if(i == length - 1)
break;
j++;
idx1 = strstr(param, paramarr[i]);
idx2 = strstr(param, paramarr[i+1]);
instance = (char*)calloc(idx2 - idx1 - strlen(paramarr[i]) + 1, sizeof(char));
memcpy(instance, idx1 + strlen(paramarr[i]), idx2 - idx1 - strlen(paramarr[i]));
tmp = strdup(res);
FREE(res);
asprintf(&res, "%s.%s", tmp, instance);
FREE(tmp);
FREE(instance);
}
if ((s = strstr(param,paramarr[j]) ) != NULL && strlen(s) == strlen(paramarr[j]))
asprintf(&retparam, "%s", res);
else
asprintf(&retparam, "%s.%s", res, strstr(param, paramarr[j]) + strlen(paramarr[j]));
FREE(res);
for(int k = 0; k < length; k++)
FREE(paramarr[k]);
FREE(paramarr);
return retparam;
}
void append_string_to_string(char *strappend, char **target)
{
char *tmp = NULL;
if(strappend == NULL || strlen(strappend) <= 0)
return;
if(*target == NULL || strlen(*target) <= 0) {
*target = strdup(strappend);
return;
} else {
tmp = strdup(*target);
FREE(*target);
}
asprintf(target, "%s%s", tmp, strappend);
FREE(tmp);
}
void bulkdata_add_failed_report(struct profile *profile, char *freport)
{
struct failed_reports *report, *retreport, *rtmp;
if(profile->nbre_failed_reports < profile->nbre_of_retained_failed_reports || profile->nbre_of_retained_failed_reports < 0) {
profile->nbre_failed_reports++;
} else {
list_for_each_entry_safe(retreport, rtmp, profile->failed_reports, list) {
bulkdata_delete_failed_report(retreport);
break;
}
}
report = calloc(1, sizeof(struct failed_reports));
list_add_tail(&report->list, profile->failed_reports);
report->freport= strdup(freport);
}
void bulkdata_delete_failed_report(struct failed_reports *report)
{
if(report != NULL) {
list_del(&report->list);
FREE(report->freport);
FREE(report);
}
}
struct failed_reports* empty_failed_reports_list(struct profile *profile)
{
struct failed_reports *report, *rtmp;
if(list_empty(profile->failed_reports))
return NULL;
list_for_each_entry_safe(report, rtmp, profile->failed_reports, list) {
list_del(&report->list);
FREE(report->freport);
FREE(report);
}
return NULL;
}
void add_failed_reports_to_report_csv(struct profile *profile, char **report, int isnext)
{
struct failed_reports *retreport = NULL;
int j = 0;
if(list_empty(profile->failed_reports))
return;
list_for_each_entry(retreport, profile->failed_reports, list) {
if(!j && isnext) {
j = 1;
continue;
}
append_string_to_string(retreport->freport, report);
}
}

View file

@ -0,0 +1,69 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __COMMON_H
#define __COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <regex.h>
#include <sys/param.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <libbbfdm/dmentry.h>
#include <libbbfdm/dmbbfcommon.h>
#include "config.h"
#include "log.h"
#include "bulkdata.h"
typedef struct pathnode {
char *ref_path;
struct pathnode *next;
} pathnode;
typedef struct resultsnode {
struct list_head list;
char *name;
char *data;
char *type;
} resultsnode;
struct failed_reports {
struct list_head list;
char *freport;
};
#define GLOB_CHAR "[[+*]+"
int bulkdata_dm_ctx_init(struct dmctx *ctx);
int bulkdata_dm_ctx_clean(struct dmctx *ctx);
char *bulkdata_get_value_param(char *path);
void bulkdata_get_value(char *path, struct list_head *list);
void bulkdata_free_data_from_list(struct list_head *dup_list);
void bulkdata_get_value_results(char *path, struct list_head *list);
char *create_request_url(struct profile *profile);
char *get_bulkdata_profile_parameter_name(char *paramref, char *paramname, char *param);
void append_string_to_string(char *strappend, char **target);
void bulkdata_add_failed_report(struct profile *profile, char *freport);
void bulkdata_delete_failed_report(struct failed_reports *report);
struct failed_reports *empty_failed_reports_list(struct profile *profile);
void add_failed_reports_to_report_csv(struct profile *profile, char **report, int isnext);
#endif //__COMMON_H

524
trx69/bulkdata/src/config.c Normal file
View file

@ -0,0 +1,524 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Omar Kallel <omar.kallel@pivasoftware.com>
*/
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <libbbfdm/deviceinfo.h>
#include "log.h"
#include "config.h"
#include "buci.h"
#include "common.h"
#include "bulkdata.h"
int get_log_level_config(struct bulkdata *bulkdata)
{
char *value = NULL;
buci_init();
value = buci_get_value("bulkdata", "bulkdata", "log_level");
if(value != NULL && *value != '\0')
bulkdata->log_level = atoi(value);
else
bulkdata->log_level = DEFAULT_LOGLEVEL;
bulkdata_log(SDEBUG,"Log Level of Bulkdata is : %d", bulkdata->log_level);
buci_fini();
return 0;
}
int get_amd_version_config(struct bulkdata *bulkdata)
{
char *value = NULL;
buci_init();
value = buci_get_value("cwmp", "cpe", "amd_version");
if(value != NULL && *value != '\0')
bulkdata->amd_version = atoi(value);
else
bulkdata->amd_version = DEFAULT_AMD_VERSION;
bulkdata_log(SDEBUG,"CWMP Amendment Version is : %d", bulkdata->amd_version);
buci_fini();
return 0;
}
int get_instance_mode_config(struct bulkdata *bulkdata)
{
char *value = NULL;
buci_init();
value = buci_get_value("cwmp", "cpe", "instance_mode");
if(value != NULL && *value != '\0') {
if(!strcmp(value, "InstanceNumber"))
bulkdata->instance_mode = INSTANCE_MODE_NUMBER;
else
bulkdata->instance_mode = INSTANCE_MODE_ALIAS;
}
else
bulkdata->instance_mode = DEFAULT_INSTANCE_MODE;
bulkdata_log(SDEBUG,"CWMP Instance Mode is : %d", bulkdata->instance_mode);
buci_fini();
return 0;
}
int get_device_id_config(struct bulkdata *bulkdata)
{
struct dmctx dmctx = {0};
bulkdata_dm_ctx_init(&dmctx);
bulkdata->device_id.manufacturer_oui = strdup(get_deviceid_manufactureroui());
bulkdata->device_id.product_class = strdup(get_deviceid_productclass());
bulkdata->device_id.serial_number = strdup(get_deviceid_serialnumber());
bulkdata_dm_ctx_clean(&dmctx);
return 0;
}
int load_profile_config(struct bulkdata *bulkdata, struct uci_section *s, int i)
{
char *value = NULL;
value = buci_get_value_bysection(s, "profile_id");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].profile_id = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The profile_id of profile_id %d is : %d", i, bulkdata->profile[i].profile_id);
} else
return -1;
value = buci_get_value_bysection(s, "nbre_of_retained_failed_reports");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].nbre_of_retained_failed_reports = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The nombre of retained failed reports of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].nbre_of_retained_failed_reports);
}
value = buci_get_value_bysection(s, "protocol");
if(value != NULL && *value != '\0' && strcasecmp(value, "http")==0) {
bulkdata->profile[i].protocol = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The protocol of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].protocol);
} else
return -1;
value = buci_get_value_bysection(s, "encoding_type");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].encoding_type = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The encoding type of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].encoding_type);
} else
return -1;
value = buci_get_value_bysection(s, "reporting_interval");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].reporting_interval = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The reporting interval of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].reporting_interval);
} else
return -1;
value = buci_get_value_bysection(s, "time_reference");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].time_reference = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The time reference of profile_id %d is : %ld", bulkdata->profile[i].profile_id, bulkdata->profile[i].time_reference);
} else
return -1;
value = buci_get_value_bysection(s, "csv_encoding_field_separator");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].csv_encoding_field_separator = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The csv encoding field separator of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].csv_encoding_field_separator);
} else
return -1;
value = buci_get_value_bysection(s, "csv_encoding_row_separator");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].csv_encoding_row_separator = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The csv encoding row separator of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].csv_encoding_row_separator);
} else
return -1;
value = buci_get_value_bysection(s, "csv_encoding_escape_character");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].csv_encoding_escape_character = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The csv encoding escape character of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].csv_encoding_escape_character);
}
value = buci_get_value_bysection(s, "csv_encoding_report_format");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].csv_encoding_report_format = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The csv encoding report format of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].csv_encoding_report_format);
} else
return -1;
value = buci_get_value_bysection(s, "csv_encoding_row_time_stamp");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].csv_encoding_row_time_stamp = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The csv encoding row time stamp of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].csv_encoding_row_time_stamp);
} else
return -1;
value = buci_get_value_bysection(s, "json_encoding_report_format");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].json_encoding_report_format = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The json encoding report format of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].json_encoding_report_format);
} else
return -1;
value = buci_get_value_bysection(s, "json_encoding_report_time_stamp");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].json_encoding_report_time_stamp = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The json encoding report time stamp of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].json_encoding_report_time_stamp);
} else
return -1;
value = buci_get_value_bysection(s, "http_url");
if(value != NULL && *value != '\0') {
char *url = NULL;
asprintf(&url, "%s?oui=%s&pc=%s&sn=%s", value, bulkdata->device_id.manufacturer_oui, bulkdata->device_id.serial_number, bulkdata->device_id.serial_number);
bulkdata->profile[i].http_url = strdup(url);
free(url);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP url of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_url);
} else
return -1;
value = buci_get_value_bysection(s, "http_username");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_username = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP username of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_username);
} else {
bulkdata->profile[i].http_username = NULL;
}
value = buci_get_value_bysection(s, "http_password");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_password = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP password of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_password);
} else {
bulkdata->profile[i].http_password = NULL;
}
value = buci_get_value_bysection(s, "http_compression");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_compression = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP compression of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_compression);
} else
return -1;
value = buci_get_value_bysection(s, "http_method");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_method = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP method of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_method);
} else
return -1;
value = buci_get_value_bysection(s, "http_use_date_header");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_use_date_header = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP use date header of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_use_date_header);
}
value = buci_get_value_bysection(s, "http_retry_enable");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_retry_enable = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP retry enable of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_retry_enable);
} else
return -1;
value = buci_get_value_bysection(s, "http_retry_minimum_wait_interval");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_retry_minimum_wait_interval = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP retry minimum wait interval of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_retry_minimum_wait_interval);
}
value = buci_get_value_bysection(s, "http_retry_interval_multiplier");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_retry_interval_multiplier = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP retry interval multiplier of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_retry_interval_multiplier);
}
value = buci_get_value_bysection(s, "http_persist_across_reboot");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_persist_across_reboot = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP persist across reboot of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_persist_across_reboot);
} else
return -1;
value = buci_get_value_bysection(s, "http_ssl_capath");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_ssl_capath = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP ssl capath of profile_id %d is : %s", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_ssl_capath);
} else {
bulkdata->profile[i].http_ssl_capath = NULL;
}
value = buci_get_value_bysection(s, "http_insecure_enable");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].http_insecure_enable = atoi(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP insecure enable of profile_id %d is : %d", bulkdata->profile[i].profile_id, bulkdata->profile[i].http_insecure_enable);
}
bulkdata->profile[i].retry_count = 0;
bulkdata->profile[i].nbre_failed_reports = 0;
return 0;
}
int get_profiles_enable(struct bulkdata *bulkdata)
{
struct uci_section *s;
char *enable;
int i = 0, nbr_profiles = 0;
buci_init();
buci_foreach_section("bulkdata", "profile", s) {
enable = buci_get_value_bysection(s, "enable");
if(strcmp(enable, "1") == 0) {
nbr_profiles++;
}
}
if(nbr_profiles != 0)
bulkdata->profile = calloc(2, sizeof(struct profile));
buci_foreach_section("bulkdata", "profile", s) {
enable = buci_get_value_bysection(s, "enable");
if(strcmp(enable, "1") == 0) {
if(load_profile_config(bulkdata, s, i) == -1) {
bulkdata_log(SCRIT,"Not able to start bulkdata: some required bulkdata configurations in profile must be set");
return -1;
}
i++;
}
}
profiles_number = nbr_profiles;
buci_fini();
return 0;
}
int load_profile_parameter_config(struct bulkdata *bulkdata, struct uci_section *s, int i, int j)
{
char *value = NULL;
value = buci_get_value_bysection(s, "name");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].profile_parameter[j].name = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The parameter name %d of profile_id %d is : %s", j+1, bulkdata->profile[i].profile_id, bulkdata->profile[i].profile_parameter[j].name);
} else {
bulkdata->profile[i].profile_parameter[j].name = NULL;
}
value = buci_get_value_bysection(s, "reference");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].profile_parameter[j].reference = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The parameter reference %d of profile_id %d is : %s", j+1, bulkdata->profile[i].profile_id, bulkdata->profile[i].profile_parameter[j].reference);
} else
return -1;
return 0;
}
int get_profiles_parameters(struct bulkdata *bulkdata)
{
struct uci_section *s;
char *profile_id;
int i, j, nbr_profile_parameters;
buci_init();
for (i = 0; i < profiles_number; i++) {
j = 0;
nbr_profile_parameters = 0;
buci_foreach_section("bulkdata", "profile_parameter", s) {
profile_id = buci_get_value_bysection(s, "profile_id");
if(bulkdata->profile[i].profile_id != atoi(profile_id))
continue;
nbr_profile_parameters++;
if(nbr_profile_parameters == 1) {
bulkdata->profile[i].profile_parameter = calloc(1, sizeof(struct profile_parameter));
} else {
bulkdata->profile[i].profile_parameter = realloc(bulkdata->profile[i].profile_parameter, nbr_profile_parameters * sizeof(struct profile_parameter));
}
if(load_profile_parameter_config(bulkdata, s, i, j) == -1) {
bulkdata_log(SCRIT,"Not able to start bulkdata: some required bulkdata configurations in profile_parameter must be set");
return -1;
}
j++;
}
bulkdata->profile[i].profile_parameter_number = nbr_profile_parameters;
}
buci_fini();
return 0;
}
int load_profile_http_request_uri_parameter_config(struct bulkdata *bulkdata, struct uci_section *s, int i, int j)
{
char *value = NULL;
value = buci_get_value_bysection(s, "name");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].profile_http_uri_parameter[j].name = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP resuest URI parameter name %d of profile_id %d is : %s", j+1, bulkdata->profile[i].profile_id, bulkdata->profile[i].profile_http_uri_parameter[j].name);
} else
return -1;
value = buci_get_value_bysection(s, "reference");
if(value != NULL && *value != '\0') {
bulkdata->profile[i].profile_http_uri_parameter[j].reference = strdup(value);
value = NULL;
bulkdata_log(SDEBUG,"The HTTP resuest URI parameter reference %d of profile_id %d is : %s", j+1, bulkdata->profile[i].profile_id, bulkdata->profile[i].profile_http_uri_parameter[j].reference);
} else
return -1;
return 0;
}
int get_profile_http_request_uri_parameter(struct bulkdata *bulkdata)
{
struct uci_section *s;
char *profile_id;
int i, j, nbr_profile_http_request_uri_parameter;
buci_init();
for (i = 0; i < profiles_number; i++) {
j = 0;
nbr_profile_http_request_uri_parameter = 0;
buci_foreach_section("bulkdata", "profile_http_request_uri_parameter", s) {
profile_id = buci_get_value_bysection(s, "profile_id");
if(bulkdata->profile[i].profile_id != atoi(profile_id))
continue;
nbr_profile_http_request_uri_parameter++;
if(nbr_profile_http_request_uri_parameter == 1) {
bulkdata->profile[i].profile_http_uri_parameter = calloc(1, sizeof(struct profile_http_request_uri_parameter));
} else {
bulkdata->profile[i].profile_http_uri_parameter = realloc(bulkdata->profile[i].profile_http_uri_parameter, nbr_profile_http_request_uri_parameter * sizeof(struct profile_http_request_uri_parameter));
}
if(load_profile_http_request_uri_parameter_config(bulkdata, s, i, j)== -1) {
bulkdata_log(SCRIT,"Not able to start bulkdata: some required bulkdata configurations in profile_http_request_uri_parameter must be set");
return -1;
}
j++;
}
bulkdata->profile[i].profile_http_request_uri_parameter_number = nbr_profile_http_request_uri_parameter;
}
buci_fini();
return 0;
}
int bulkdata_config_init(struct bulkdata *bulkdata)
{
get_log_level_config(bulkdata);
get_amd_version_config(bulkdata);
get_instance_mode_config(bulkdata);
get_device_id_config(bulkdata);
if (get_profiles_enable(bulkdata) == -1)
return -1;
if (get_profiles_parameters(bulkdata) == -1)
return -1;
if (get_profile_http_request_uri_parameter(bulkdata) == -1)
return -1;
return 0;
}
int free_device_id_config(struct bulkdata *bulkdata)
{
FREE(bulkdata->device_id.manufacturer_oui);
FREE(bulkdata->device_id.product_class);
FREE(bulkdata->device_id.serial_number);
return 0;
}
int free_profiles_enable(struct bulkdata *bulkdata)
{
for(int i = 0; i < profiles_number; i++) {
FREE(bulkdata->profile[i].protocol);
FREE(bulkdata->profile[i].encoding_type);
FREE(bulkdata->profile[i].csv_encoding_field_separator);
FREE(bulkdata->profile[i].csv_encoding_row_separator);
FREE(bulkdata->profile[i].csv_encoding_escape_character);
FREE(bulkdata->profile[i].csv_encoding_report_format);
FREE(bulkdata->profile[i].csv_encoding_row_time_stamp);
FREE(bulkdata->profile[i].json_encoding_report_format);
FREE(bulkdata->profile[i].json_encoding_report_time_stamp);
FREE(bulkdata->profile[i].http_url);
FREE(bulkdata->profile[i].http_username);
FREE(bulkdata->profile[i].http_password);
FREE(bulkdata->profile[i].http_compression);
FREE(bulkdata->profile[i].http_method);
FREE(bulkdata->profile[i].http_ssl_capath);
}
FREE(bulkdata->profile);
return 0;
}
int free_profiles_parameters(struct bulkdata *bulkdata)
{
for(int i = 0; i < profiles_number; i++) {
for(int j = 0; j < bulkdata->profile[i].profile_parameter_number; j++) {
FREE(bulkdata->profile[i].profile_parameter[j].name);
FREE(bulkdata->profile[i].profile_parameter[j].reference);
}
FREE(bulkdata->profile[i].profile_parameter);
}
return 0;
}
int free_profile_http_request_uri_parameter(struct bulkdata *bulkdata)
{
for(int i = 0; i < profiles_number; i++) {
for(int j = 0; j < bulkdata->profile[i].profile_http_request_uri_parameter_number; j++) {
FREE(bulkdata->profile[i].profile_http_uri_parameter[j].name);
FREE(bulkdata->profile[i].profile_http_uri_parameter[j].reference);
}
FREE(bulkdata->profile[i].profile_http_uri_parameter);
}
return 0;
}
int bulkdata_config_fini(struct bulkdata *bulkdata)
{
free_device_id_config(bulkdata);
free_profiles_parameters(bulkdata);
free_profile_http_request_uri_parameter(bulkdata);
free_profiles_enable(bulkdata);
return 0;
}

103
trx69/bulkdata/src/config.h Normal file
View file

@ -0,0 +1,103 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <libubox/uloop.h>
#define DEFAULT_AMD_VERSION 2
#define DEFAULT_INSTANCE_MODE 0
typedef struct device_id {
char *manufacturer_oui;
char *product_class;
char *serial_number;
} device_id;
typedef struct profile_parameter {
int profile_id;
char *name;
char *reference;
} profile_parameter;
typedef struct profile_http_request_uri_parameter {
int profile_id;
char *name;
char *reference;
} profile_http_request_uri_parameter;
typedef struct profile {
struct uloop_timeout utimer;
int profile_id;
int nbre_of_retained_failed_reports;
int nbre_failed_reports;
int reporting_interval;
int profile_parameter_number;
int profile_http_request_uri_parameter_number;
int http_retry_minimum_wait_interval;
int http_retry_interval_multiplier;
int min_retry;
int retry_count;
char *protocol;
char *encoding_type;
char *csv_encoding_field_separator;
char *csv_encoding_row_separator;
char *csv_encoding_escape_character;
char *csv_encoding_report_format;
char *csv_encoding_row_time_stamp;
char *json_encoding_report_format;
char *json_encoding_report_time_stamp;
char *http_url;
char *http_username;
char *http_password;
char *http_compression;
char *http_method;
char *http_ssl_capath;
char *new_report;
time_t time_reference;
time_t next_retry;
time_t next_period;
bool http_persist_across_reboot;
bool http_insecure_enable;
bool enable;
bool http_use_date_header;
bool http_retry_enable;
struct profile_parameter *profile_parameter;
struct profile_http_request_uri_parameter *profile_http_uri_parameter;
struct list_head *failed_reports;
} profile;
typedef struct bulkdata {
struct device_id device_id;
struct profile *profile;
int log_level;
int amd_version;
unsigned int instance_mode;
} bulkdata;
int bulkdata_config_init(struct bulkdata *bulkdata);
int bulkdata_config_fini(struct bulkdata *bulkdata);
#ifndef FREE
#define FREE(x) do { if(x) {free(x); x = NULL;} } while (0)
#endif
#endif //__CONFIG_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _BULKDATA_H_
#define _BULKDATA_H_
#include <libbbf_api/dmcommon.h>
extern DMOBJ tDeviceBulkDataObj[];
extern DMOBJ tBulkDataObj[];
extern DMLEAF tBulkDataParams[];
extern DMOBJ tBulkDataProfileObj[];
extern DMLEAF tBulkDataProfileParams[];
extern DMLEAF tBulkDataProfileParameterParams[];
extern DMLEAF tBulkDataProfileCSVEncodingParams[];
extern DMLEAF tBulkDataProfileJSONEncodingParams[];
extern DMOBJ tBulkDataProfileHTTPObj[];
extern DMLEAF tBulkDataProfileHTTPParams[];
extern DMLEAF tBulkDataProfileHTTPRequestURIParameterParams[];
#endif //__BULKDATA_H_

196
trx69/bulkdata/src/http.c Normal file
View file

@ -0,0 +1,196 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Omar Kallel <omar.kallel@pivasoftware.com>
*/
#include "http.h"
static struct http_client http_c;
static CURL *curl;
int http_client_init(struct profile *profile)
{
char *url = create_request_url(profile);
if(url) {
asprintf(&http_c.url, "%s", url);
free(url);
}
bulkdata_log(SINFO, "ACS url: %s", http_c.url);
curl_global_init(CURL_GLOBAL_SSL);
curl = curl_easy_init();
if (!curl) return -1;
return 0;
}
void http_client_exit(void)
{
FREE(http_c.url);
if (http_c.header_list) {
curl_slist_free_all(http_c.header_list);
http_c.header_list = NULL;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
}
static size_t http_get_response(void *buffer, size_t size, size_t rxed, char **msg_in)
{
char *c;
if (asprintf(&c, "%s%.*s", *msg_in, size * rxed, (char *)buffer) == -1) {
FREE(*msg_in);
return -1;
}
free(*msg_in);
*msg_in = c;
return size * rxed;
}
int http_send_message(struct profile *profile, char *msg_out, int msg_out_len, char **msg_in)
{
CURLcode res;
long http_code = 0;
char errbuf[CURL_ERROR_SIZE];
http_c.header_list = NULL;
http_c.header_list = curl_slist_append(http_c.header_list, "User-Agent: iopsys-bulkdata");
if (!http_c.header_list) return -1;
if (profile->http_use_date_header) {
if (bulkdata_get_time() != NULL) {
http_c.header_list = curl_slist_append(http_c.header_list, bulkdata_get_time());
if (!http_c.header_list) return -1;
}
}
if (strcmp(profile->encoding_type, "json") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "Content-Type: application/json; charset=\"utf-8\"");
if (!http_c.header_list) return -1;
if(strcmp (profile->json_encoding_report_format, "objecthierarchy") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "BBF-Report-Format: \"ObjectHierarchy\"");
if (!http_c.header_list) return -1;
} else if(strcmp(profile->json_encoding_report_format, "namevaluepair") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "BBF-Report-Format: \"NameValuePair\"");
if (!http_c.header_list) return -1;
}
} else if(strcmp(profile->encoding_type, "csv") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "Content-Type: text/csv; charset=\"utf-8\"");
if (!http_c.header_list) return -1;
if(strcmp (profile->csv_encoding_report_format, "row") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "BBF-Report-Format: \"ParameterPerRow\"");
if (!http_c.header_list) return -1;
} else if(strcmp (profile->csv_encoding_report_format, "column") == 0) {
http_c.header_list = curl_slist_append(http_c.header_list, "BBF-Report-Format: \"ParameterPerColumn\"");
if (!http_c.header_list) return -1;
}
}
curl_easy_setopt(curl, CURLOPT_URL, http_c.url);
curl_easy_setopt(curl, CURLOPT_USERNAME, profile->http_username);
curl_easy_setopt(curl, CURLOPT_PASSWORD, profile->http_password);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_TIMEOUT);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, HTTP_TIMEOUT);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
if(strcasecmp(profile->http_compression, "gzip") == 0) {
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
http_c.header_list = curl_slist_append(http_c.header_list, "Content-Encoding: gzip");
} else if(strcasecmp(profile->http_compression, "compress") == 0) {
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "compress");
http_c.header_list = curl_slist_append(http_c.header_list, "Content-Encoding: compress");
} else if(strcasecmp(profile->http_compression, "deflate") == 0) {
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
http_c.header_list = curl_slist_append(http_c.header_list, "Content-Encoding: deflate");
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_c.header_list);
if(strcasecmp(profile->http_method, "put") == 0)
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg_out);
if (msg_out)
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) msg_out_len);
else
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_get_response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, msg_in);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
if (profile->http_ssl_capath)
curl_easy_setopt(curl, CURLOPT_CAPATH, profile->http_ssl_capath);
if (profile->http_insecure_enable) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
}
*msg_in = (char *) calloc (1, sizeof(char));
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
size_t len = strlen(errbuf);
if(len) {
if (errbuf[len - 1] == '\n') errbuf[len - 1] = '\0';
bulkdata_log(SCRIT, "libcurl: (%d) %s", res, errbuf);
} else {
bulkdata_log(SCRIT, "libcurl: (%d) %s", res, curl_easy_strerror(res));
}
}
if (!strlen(*msg_in))
FREE(*msg_in);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if(http_code == 200)
bulkdata_log(SINFO, "Receive HTTP 200 OK from Bulkdata Collector");
else if(http_code == 401)
bulkdata_log(SINFO, "Receive HTTP 401 Unauthorized from Bulkdata Collector");
else if(http_code == 204)
bulkdata_log(SINFO, "Receive HTTP 204 No Content from Bulkdata Collector");
else
bulkdata_log(SINFO, "Receive HTTP %d from Bulkdata Collector", http_code);
if(http_code == 415)
{
strcpy(profile->http_compression, "None");
goto error;
}
if (http_code != 200 && http_code != 204)
goto error;
curl_easy_reset(curl);
if (http_c.header_list) {
curl_slist_free_all(http_c.header_list);
http_c.header_list = NULL;
}
if (res) goto error;
return http_code;
error:
FREE(*msg_in);
if (http_c.header_list) {
curl_slist_free_all(http_c.header_list);
http_c.header_list = NULL;
}
return -1;
}

37
trx69/bulkdata/src/http.h Normal file
View file

@ -0,0 +1,37 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __HTTP_H
#define __HTTP_H
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <curl/curl.h>
#include "config.h"
#include "log.h"
#include "times.h"
#include "common.h"
#define HTTP_TIMEOUT 30
struct http_client
{
struct curl_slist *header_list;
char *url;
};
int http_client_init(struct profile *profile);
void http_client_exit(void);
int http_send_message(struct profile *profile, char *msg_out, int msg_out_len, char **msg_in);
#endif //__HTTP_H

57
trx69/bulkdata/src/log.c Normal file
View file

@ -0,0 +1,57 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <syslog.h>
#include <time.h>
#include "bulkdata.h"
#include "config.h"
#include "log.h"
static const int log_syslogmap[] = {
[SCRIT] = LOG_CRIT,
[SWARNING] = LOG_WARNING,
[SNOTICE] = LOG_NOTICE,
[SINFO] = LOG_INFO,
[SDEBUG] = LOG_DEBUG
};
static const char* log_str[] = {
[SCRIT] = "CRITICAL",
[SWARNING] = "WARNING",
[SNOTICE] = "NOTICE",
[SINFO] = "INFO",
[SDEBUG] = "DEBUG"
};
void bulkdata_log(int priority, const char *format, ...)
{
va_list vl;
if (priority <= bulkdata_main.log_level) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
va_start(vl, format);
printf("%d-%02d-%02d %02d:%02d:%02d [bulkdata] %s - ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, log_str[priority]);
vprintf(format, vl);
va_end(vl);
printf("\n");
openlog("bulkdata", 0, LOG_DAEMON);
va_start(vl, format);
vsyslog(log_syslogmap[priority], format, vl);
va_end(vl);
closelog();
}
}

28
trx69/bulkdata/src/log.h Normal file
View file

@ -0,0 +1,28 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __LOG_H
#define __LOG_H
#define DEFAULT_LOGLEVEL SINFO
enum bulkdata_log_level_enum {
SCRIT,
SWARNING,
SNOTICE,
SINFO,
SDEBUG,
__MAX_SLOG
};
void bulkdata_log(int priority, const char *format, ...);
#endif //__LOG_H

336
trx69/bulkdata/src/report.c Normal file
View file

@ -0,0 +1,336 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
*/
#include "report.h"
static void add_new_json_obj(json_object *json_obj, char *name, char *data, char *type)
{
json_object *jobj;
if(strstr(type, "unsignedInt") || strstr(type, "int") || strstr(type, "long"))
jobj = json_object_new_int64(atoi(data));
else if(strstr(type, "bool"))
jobj = json_object_new_boolean(atoi(data));
else
jobj = json_object_new_string(data);
json_object_object_add(json_obj, name, jobj);
}
static void create_json_bulkdata_report_object_hierarchy(struct profile *profile, char **report)
{
struct json_object *json_obj, *json_obj1, *json_obj2;
struct resultsnode *p;
int i, j, profile_param_number = profile->profile_parameter_number;
char *param_name, *result, *pch, *pchr, *collection_time = NULL;
char buf[1024] = {0};
json_obj = json_object_new_object();
get_time_stamp(profile->json_encoding_report_time_stamp, &collection_time);
if(collection_time) {
if(strcmp(profile->json_encoding_report_time_stamp, "iso8601") == 0)
json_object_object_add(json_obj, "CollectionTime", json_object_new_string(collection_time));
else
json_object_object_add(json_obj, "CollectionTime", json_object_new_int64(atoi(collection_time)));
free(collection_time);
}
json_obj2 = json_obj;
for (i = 0; i < profile_param_number; i++) {
LIST_HEAD(results_list);
bulkdata_get_value_results(profile->profile_parameter[i].reference, &results_list);
list_for_each_entry(p, &results_list, list) {
char *argv[128] = {0};
j = 0;
param_name = get_bulkdata_profile_parameter_name(profile->profile_parameter[i].reference, profile->profile_parameter[i].name, p->name);
strcpy(buf, param_name);
for (pch = strtok_r(buf, ".", &pchr); pch != NULL; pch = strtok_r(NULL, ".", &pchr)) {
argv[j] = pch;
json_obj1 = (json_object *)dmjson_select_obj(json_obj, argv);
if (json_obj1)
json_obj2 = json_obj1;
else {
if (pchr != NULL && *pchr != '\0') {
json_object *new_obj = json_object_new_object();
json_object_object_add(json_obj2, pch, new_obj);
json_obj2 = new_obj;
}
else
add_new_json_obj(json_obj2, pch, p->data, p->type);
}
j++;
}
}
bulkdata_free_data_from_list(&results_list);
FREE(param_name);
}
result = (char *)json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY);
*report = strdup(result);
json_object_put(json_obj);
}
static void create_json_bulkdata_report_name_value_pair(struct profile *profile, char **report)
{
struct json_object *json_obj;
struct resultsnode *p;
char *param_name, *result, *collection_time = NULL;
int i = 0, profile_param_number = profile->profile_parameter_number;
json_obj = json_object_new_object();
get_time_stamp(profile->json_encoding_report_time_stamp, &collection_time);
if(collection_time) {
if(strcmp(profile->json_encoding_report_time_stamp, "iso8601") == 0)
json_object_object_add(json_obj, "CollectionTime", json_object_new_string(collection_time));
else
json_object_object_add(json_obj, "CollectionTime", json_object_new_int64(atoi(collection_time)));
free(collection_time);
}
for (i = 0; i < profile_param_number; i++) {
LIST_HEAD(results_list);
bulkdata_get_value_results(profile->profile_parameter[i].reference, &results_list);
list_for_each_entry(p, &results_list, list) {
param_name = get_bulkdata_profile_parameter_name(profile->profile_parameter[i].reference, profile->profile_parameter[i].name, p->name);
add_new_json_obj(json_obj, param_name, p->data, p->type);
FREE(param_name);
}
bulkdata_free_data_from_list(&results_list);
}
result = (char *)json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY);
*report = strdup(result);
json_object_put(json_obj);
}
static void add_failed_reports_to_report_json(struct profile *profile, char *new_report, char **report, int isnext)
{
json_object *json_obj, *json_array, *json_string;
struct failed_reports *retreport = NULL;
char *msgout = NULL;
int j = 0;
json_obj = json_object_new_object();
json_array = json_object_new_array();
json_object_object_add(json_obj,"Report", json_array);
if(list_empty(profile->failed_reports))
goto new_report;
list_for_each_entry(retreport, profile->failed_reports, list) {
if(!j && isnext) {
j = 1;
continue;
}
json_string = json_tokener_parse(retreport->freport);
json_object_array_add(json_array, json_string);
}
new_report :
if(new_report) {
json_string = json_tokener_parse(new_report);
json_object_array_add(json_array, json_string);
}
msgout = (char *)json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY);
*report = strdup(msgout);
json_object_put(json_obj);
}
static void create_report_json(char *new_report, char **report)
{
json_object *json_obj, *json_array, *json_string;
char *msgout = NULL;
json_obj = json_object_new_object();
json_array = json_object_new_array();
json_object_object_add(json_obj,"Report", json_array);
if(new_report) {
json_string = json_tokener_parse(new_report);
json_object_array_add(json_array, json_string);
}
msgout = (char *)json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY);
*report = strdup(msgout);
json_object_put(json_obj);
}
int create_json_bulkdata_report(struct profile *profile, char **report)
{
/*
* create json msg of current report
* parse failed reports list and add it to the report
* then add new report to the report
*/
char *msgout;
profile->new_report = NULL;
if(strcmp(profile->json_encoding_report_format, "objecthierarchy") == 0) {
create_json_bulkdata_report_object_hierarchy(profile, &msgout);
} else if(strcmp(profile->json_encoding_report_format, "namevaluepair") == 0) {
create_json_bulkdata_report_name_value_pair(profile, &msgout);
}
if(profile->nbre_of_retained_failed_reports != 0) {
if(profile->nbre_failed_reports >= profile->nbre_of_retained_failed_reports && profile->nbre_of_retained_failed_reports > 0)
add_failed_reports_to_report_json(profile, msgout, report, 1);
else
add_failed_reports_to_report_json(profile, msgout, report, 0);
} else {
create_report_json(msgout, report);
}
append_string_to_string(msgout, &profile->new_report);
FREE(msgout);
return 0;
}
int create_csv_bulkdata_report(struct profile *profile, char **report)
{
/*
* create csv msg of current report
* parse failed reports list and add it to the report
*/
int i;
struct resultsnode *p;
char *str1 = NULL, *str2 = NULL, *str = NULL, *paramprofilename, *timestamp = NULL, *type = NULL, rowseparator = '\0', separator = '\0';
if(strcmp(profile->csv_encoding_row_separator, "&#10;") == 0)
rowseparator = '\n';
else if(strcmp(profile->csv_encoding_row_separator, "&#13;") == 0)
rowseparator = '\r';
if(profile->csv_encoding_field_separator)
separator = profile->csv_encoding_field_separator[0];
get_time_stamp(profile->csv_encoding_row_time_stamp, &timestamp);
/*
* Create header ReportTimestamp,ParameterName,ParameterValue,ParameterType in case of ParameterPerRow
*/
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
if(timestamp == NULL)
asprintf(&str, "ParameterName%cParameterValue%cParameterType%c", separator, separator, rowseparator);
else
asprintf(&str, "ReportTimestamp%cParameterName%cParameterValue%cParameterType%c", separator, separator, separator, rowseparator);
append_string_to_string(str, report);
FREE(str);
if(profile->nbre_of_retained_failed_reports != 0) {
if(profile->nbre_failed_reports >= profile->nbre_of_retained_failed_reports && profile->nbre_of_retained_failed_reports > 0)
add_failed_reports_to_report_csv(profile, report, 1);
else
add_failed_reports_to_report_csv(profile, report, 0);
}
}
if(strcmp(profile->csv_encoding_report_format, "column") == 0 && timestamp != NULL) {
if(profile->nbre_of_retained_failed_reports != 0) {
if(profile->nbre_failed_reports >= profile->nbre_of_retained_failed_reports && profile->nbre_of_retained_failed_reports > 0)
add_failed_reports_to_report_csv(profile, report, 1);
else
add_failed_reports_to_report_csv(profile, report, 0);
}
append_string_to_string("ReportTimestamp", &str1);
append_string_to_string(timestamp, &str2);
}
/*
* Add New reports
*/
profile->new_report = NULL;
for(i = 0; i < profile->profile_parameter_number; i++) {
LIST_HEAD(results_list);
bulkdata_get_value_results(profile->profile_parameter[i].reference, &results_list);
list_for_each_entry(p, &results_list, list) {
paramprofilename = get_bulkdata_profile_parameter_name(profile->profile_parameter[i].reference, profile->profile_parameter[i].name, p->name);
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
type = strstr(p->type, ":");
if(timestamp == NULL)
asprintf(&str, "%s%c%s%c%s%c", paramprofilename, separator, p->data, separator, type+1, rowseparator);
else
asprintf(&str, "%s%c%s%c%s%c%s%c", timestamp, separator, paramprofilename, separator, p->data, separator, type+1, rowseparator);
append_string_to_string(str, report);
append_string_to_string(str, &profile->new_report);
FREE(str);
} else if(strcmp(profile->csv_encoding_report_format, "column") == 0) {
if(str1 == NULL || strlen(str1) <= 0)
asprintf(&str, "%s", paramprofilename);
else
asprintf(&str, "%c%s", separator, paramprofilename);
append_string_to_string(str, &str1);
FREE(str);
if(str2 == NULL || strlen(str2) <= 0)
asprintf(&str, "%s", p->data);
else
asprintf(&str, "%c%s", separator, p->data);
append_string_to_string(str, &str2);
FREE(str);
}
FREE(paramprofilename);
}
bulkdata_free_data_from_list(&results_list);
}
if(strcmp(profile->csv_encoding_report_format, "column") == 0) {
asprintf(&str, "%c", rowseparator);
append_string_to_string(str, &str1);
append_string_to_string(str, &str2);
append_string_to_string(str1, report);
append_string_to_string(str2, report);
append_string_to_string(str1, &profile->new_report);
append_string_to_string(str2, &profile->new_report);
}
FREE(str);
FREE(str1);
FREE(str2);
FREE(timestamp);
return 0;
}
static void create_json_failed_report(struct profile *profile, char **report)
{
add_failed_reports_to_report_json(profile, NULL, report, 0);
}
static void create_csv_failed_report(struct profile *profile, char **report)
{
char rowseparator = '\0', separator = '\0', *timestamp = NULL;
if(strcmp(profile->csv_encoding_row_separator, "&#10;") == 0) {
rowseparator = '\n';
} else if(strcmp(profile->csv_encoding_row_separator, "&#13;") == 0) {
rowseparator = '\r';
}
if(profile->csv_encoding_field_separator)
separator = profile->csv_encoding_field_separator[0];
get_time_stamp(profile->csv_encoding_row_time_stamp, &timestamp);
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
if(timestamp == NULL)
asprintf(report, "ParameterName%cParameterValue%cParameterType%c", separator, separator, rowseparator);
else
asprintf(report, "ReportTimestamp%cParameterName%cParameterValue%cParameterType%c", separator, separator, separator, rowseparator);
}
add_failed_reports_to_report_csv(profile, report, 0);
}
void create_encoding_bulkdata_report(struct profile *profile, char **report)
{
if(strcasecmp(profile->encoding_type, "json") == 0) {
create_json_bulkdata_report(profile, report);
} else if(strcasecmp(profile->encoding_type, "csv") == 0) {
create_csv_bulkdata_report(profile, report);
}
}
void create_failed_report(struct profile *profile, char **report)
{
if(strcasecmp(profile->encoding_type, "json") == 0) {
create_json_failed_report(profile, report);
} else if(strcasecmp(profile->encoding_type, "csv") == 0) {
create_csv_failed_report(profile, report);
}
}

View file

@ -0,0 +1,24 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
*/
#ifndef __REPORT_H_
#define __REPORT_H_
#include <json-c/json.h>
#include "common.h"
#include "times.h"
#include "config.h"
void create_encoding_bulkdata_report(struct profile *profile, char **report);
void create_failed_report(struct profile *profile, char **report);
#endif /* __REPORT_H_ */

View file

@ -0,0 +1,62 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "times.h"
const char *bulkdata_get_time(void)
{
static char local_time[64];
time_t t_time = time(NULL);
struct tm *t_tm = localtime(&t_time);
if (t_tm == NULL)
return NULL;
if (strftime(local_time, sizeof(local_time),"Date: %a, %d %b %Y %X%z GMT", t_tm) == 0)
return NULL;
return local_time;
}
void get_time_stamp(const char *format, char **timestamp)
{
time_t now = time(NULL);
if (strcmp(format, "unix") == 0) {
asprintf(timestamp, "%ld", now);
} else if (strcmp(format, "iso8601") == 0) {
char buf[32] = {0};
struct tm *ts = localtime(&now);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%Z", ts);
asprintf(timestamp, "%s", buf);
} else
timestamp = NULL;
}
unsigned int get_next_period(time_t time_reference, int reporting_interval)
{
unsigned int next_period;
time_t now = time(NULL);
if (now > time_reference)
next_period = reporting_interval - ((now - time_reference) % reporting_interval);
else
next_period = (time_reference - now) % reporting_interval;
if (next_period == 0)
next_period = reporting_interval;
return next_period;
}

View file

@ -0,0 +1,19 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
*/
#ifndef __TIMES_H
#define __TIMES_H
const char *bulkdata_get_time(void);
void get_time_stamp(const char *format, char **timestamp);
unsigned int get_next_period(time_t time_reference, int reporting_interval);
#endif /* __TIMES_H */

View file

@ -1,4 +1,4 @@
if PACKAGE_icwmp-curl || PACKAGE_icwmp-zstream
if PACKAGE_icwmp
config CWMP_SCRIPTS_FULL
bool "Install all icwmp scripts"
@ -22,6 +22,6 @@ config CWMP_DEBUG
config CWMP_DEVEL_DEBUG
bool "Compile with development debug options"
default n
endif

169
trx69/icwmp/Makefile Executable file
View file

@ -0,0 +1,169 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=icwmp
PKG_VERSION:=5.0-2020-09-10
PKG_FIXUP:=autoreconf
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/icwmp.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=0d3861337a960ab4cbe569c161bc10fae24b61b7
PKG_RELEASE=$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPLv2
PKG_LICENSE_FILES:=COPYING
PKG_CONFIG_DEPENDS:= \
CONFIG_CWMP_ACS_MULTI \
CONFIG_CWMP_ACS_HDM \
CONFIG_CWMP_DEBUG \
CONFIG_CWMP_DEVEL_DEBUG
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
CWMP_REVISION=$(shell svnversion ./src/ -n|cut -f2 -d:)
include $(INCLUDE_DIR)/package.mk
define Package/icwmp-tr098
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=CWMP client for TR-098 Data Model
DEPENDS:=+PACKAGE_icwmp-tr098:libuci +PACKAGE_icwmp-tr098:libmicroxml +PACKAGE_icwmp-tr098:libubox +PACKAGE_icwmp-tr098:jshn +PACKAGE_icwmp-tr098:libubus +PACKAGE_icwmp-tr098:libblobmsg-json +PACKAGE_icwmp-tr098:libpthread +PACKAGE_icwmp-tr098:ubusd +PACKAGE_icwmp-tr098:shflags +PACKAGE_icwmp-tr098:getopt +PACKAGE_icwmp-tr098:zlib +PACKAGE_icwmp-tr098:libjson-c +PACKAGE_icwmp-tr098:libopenssl +PACKAGE_icwmp-tr098:curl +PACKAGE_icwmp-tr098:libcurl +PACKAGE_icwmp-tr098:libtr098
endef
define Package/icwmp-notifd
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=CWMP Active Notifications Detect tool
DEPENDS:=+libubox +libuci +libbbfdm +libjson-c +libubus +libblobmsg-json
endef
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=CWMP client
DEPENDS:=+libuci +libmicroxml +libubox +jshn +libubus +libblobmsg-json +libpthread +ubusd +shflags +getopt +zlib +libjson-c +libopenssl +curl +libcurl +libbbfdm +libbbf_api
endef
define Package/$(PKG_NAME)/description
A free client implementation of CWMP (TR-069) protocol
endef
define Package/$(PKG_NAME)/config
source "$(SOURCE)/Config_cwmp.in"
endef
USE_LOCAL=$(shell ls ./src/ 2>/dev/null >/dev/null && echo 1)
ifneq ($(USE_LOCAL),)
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
endif
TARGET_CFLAGS += \
-D_GNU_SOURCE -D_AADJ
ifeq ($(CONFIG_PACKAGE_icwmp-tr098),y)
CONFIGURE_ARGS += \
--enable-icwmp_tr098
endif
ifeq ($(CONFIG_PACKAGE_icwmp-notifd),y)
CONFIGURE_ARGS += \
--enable-icwmp_notifd
endif
ifneq ($(CWMP_REVISION)_,_)
ifneq ($(CWMP_REVISION),exported)
ifneq ($(CWMP_REVISION),Unversioned directory)
TARGET_CFLAGS += "-DCWMP_REVISION=\\\"$(CWMP_REVISION)\\\""
TARGET_LDFLAGS += "-DCWMP_REVISION=\\\"$(CWMP_REVISION)\\\""
endif
endif
endif
TARGET_LDFLAGS += \
-Wl,-rpath-link=$(STAGING_DIR)/usr/lib
CONFIGURE_ARGS += \
--with-uci-include-path=$(STAGING_DIR)/usr/include \
--with-libubox-include-path=$(STAGING_DIR)/usr/include \
--with-libubus-include-path=$(STAGING_DIR)/usr/include
ifeq ($(CONFIG_CWMP_ACS_MULTI),y)
CONFIGURE_ARGS += \
--enable-acs=multi
endif
ifeq ($(CONFIG_CWMP_ACS_HDM),y)
CONFIGURE_ARGS += \
--enable-acs=hdm
endif
ifeq ($(CONFIG_CWMP_DEBUG),y)
CONFIGURE_ARGS += \
--enable-debug
endif
ifeq ($(CONFIG_CWMP_DEVEL_DEBUG),y)
CONFIGURE_ARGS += \
--enable-devel
endif
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/etc/icwmpd
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_BUILD_DIR)/bin/icwmpd $(1)/usr/sbin
ifeq ($(CONFIG_CWMP_SCRIPTS_FULL),y)
$(INSTALL_DIR) $(1)/usr/share/icwmp
$(CP) $(PKG_BUILD_DIR)/scripts/defaults $(1)/usr/share/icwmp
$(CP) $(PKG_BUILD_DIR)/scripts/functions $(1)/usr/share/icwmp
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/scripts/icwmp.sh $(1)/usr/sbin/icwmp
endif
$(CP) ./files/* $(1)/
endef
define Package/icwmp-tr098/install
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_BUILD_DIR)/bin/icwmp_tr098d $(1)/usr/sbin
endef
define Package/icwmp-notifd/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/icwmp_notifd $(1)/usr/sbin/icwmp_notifd
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
echo "$(CWMP_BKP_FILE)" >> $${IPKG_INSTROOT}/etc/sysupgrade.conf
if [ -z "$${IPKG_INSTROOT}" ]; then
echo "Enabling rc.d symlink for icwmpd"
/etc/init.d/icwmpd enable
fi
exit 0
endef
define Package/$(PKG_NAME)/prerm
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
echo "Disabling rc.d symlink for icwmpd"
/etc/init.d/icwmpd disable
fi
exit 0
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
$(eval $(call BuildPackage,icwmp-tr098))
$(eval $(call BuildPackage,icwmp-notifd))

View file

@ -0,0 +1,46 @@
config acs 'acs'
option url ''
option userid '$OUI-$SER'
option passwd 'iopsys'
option periodic_inform_enable 'true'
option periodic_inform_interval '1800'
option periodic_inform_time '0'
option ParameterKey ''
option dhcp_discovery 'enable'
# compression possible configs: GZIP, Deflate, Disabled
option compression 'Disabled'
#­ possible configs interval :[1:65535]
option retry_min_wait_interval '5'
#­ possible configs interval :[1000:65535]
option retry_interval_multiplier '2000'
option https_ssl_capath ''
option ipv6_enable '0'
config cpe 'cpe'
option interface 'eth0.1'
option default_wan_interface 'wan'
option log_to_console 'disable'
option log_to_file 'enable'
# log_severity: INFO (Default)
# log_severity possible configs: EMERG, ALERT, CRITIC ,ERROR, WARNING, NOTICE, INFO, DEBUG
option log_severity 'INFO'
option log_file_name '/var/log/icwmpd.log'
option log_max_size '102400'
option userid '$OUI-$SER'
option passwd 'iopsys'
option port '7547'
option ubus_socket '/var/run/ubus.sock'
option provisioning_code ''
option amd_version '5'
# compression possible configs: InstanceNumber, InstanceAlias
option instance_mode 'InstanceNumber'
option session_timeout '60'
option notification '1'
option datamodel 'tr181'
option exec_download '0'
option polling_period '5'
config lwn 'lwn'
option enable '1'
option hostname ''
option port '0'

View file

@ -0,0 +1,21 @@
#created by the icwmp package
zone_name=""
port=""
if [ "$zone_name" = "" ]; then
exit 0
elif [ "$zone_name" = "icwmp" ]; then
iptables -nL zone_icwmp_input 2> /dev/null
if [ $? != 0 ]; then
iptables -N zone_icwmp_input
iptables -t filter -A INPUT -j zone_icwmp_input
iptables -I zone_icwmp_input -p tcp --dport $port -j REJECT
else
iptables -F zone_icwmp_input
iptables -I zone_icwmp_input -p tcp --dport $port -j REJECT
fi
else
iptables -F zone_icwmp_input 2> /dev/null
iptables -t filter -D INPUT -j zone_icwmp_input 2> /dev/null
iptables -X zone_icwmp_input 2> /dev/null
fi
#iptables -I FW_ZONE -p tcp -s ACS_ADDRESS --dport PORT -j ACCEPT --comment "Open ACS port"

View file

@ -0,0 +1,237 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2015-2019 iopsys Software Solutions AB
. /lib/functions.sh
include /lib/network
. /usr/share/libubox/jshn.sh
START=99
STOP=10
USE_PROCD=1
PROG="/usr/sbin/icwmpd"
PROG098="/usr/sbin/icwmp_tr098d"
EXTRA_COMMANDS="notify"
EXTRA_HELP=" start [GetRPCMethods] Start icwmpd service and send GetRPCMethods"
validate_url() {
# SCHEMA_LIST: contain list of possible schemas that could be present in the acs url
# Example: SCHEMA_LIST="http https"
SCHEMA_LIST="http"
for schema in $SCHEMA_LIST; do
dest=`echo $1 | sed 's/$schema:\/\///g' | cut -f1 -d \/ | cut -f1 -d:`
if [ "_$dest" != "_" ]; then
return 0
fi
done
return 1
}
build_dmmap_instance() {
[ ! -e /etc/icwmpd/.icwmpd_backup_session.xml ] && /usr/sbin/icwmp get name "" 0 >/dev/null 2>&1
}
get_acs_url() {
local default_acs="http://10.10.1.6:8000/openacs/acs"
local acs_dhcp_discovery="$(uci -q get cwmp.acs.dhcp_discovery)"
local url="$(uci -q get cwmp.acs.url)"
local dhcp_url="$(uci -P /var/state -q get cwmp.acs.dhcp_url)"
if [ "$acs_dhcp_discovery" == "enable" -a -n "$dhcp_url" -o -z "$url" ]; then
url="$dhcp_url"
echo "ACS URL from DHCP server: $url"
[ -n "$url" ] && uci -P /var/state -q set cwmp.acs.url="$url" || url="$default_acs"
elif [ -n "$url" ];then
url="$(uci -q get cwmp.acs.url)"
echo "ACS URL from configuration: $url"
else
url="$default_acs"
echo "Using default ACS URL: $url"
[ -n "$url" ] && uci -P /var/state -q set cwmp.acs.url="$url"
fi
validate_url "$url"
if [ "$?" != "0" ];then
echo "Invalid ACS URL: $url"
exit 1
fi
}
enable_dhcp_option43() {
local wan=$1
local discovery=0
case $2 in
enable|1) discovery=1 ;;
esac
### Ask for DHCP Option 43 only if CWMP is enabled ###
local enabled
local newreqopts=
local baseopts=
local reqopts="$(uci -q get network.$wan.reqopts)"
local proto="$(uci -q get network.$wan.proto)"
local tropts="43"
local oldreqopts="$reqopts"
local ropt iopt
for ropt in $reqopts; do
case $ropt in
43) ;;
*) baseopts="$baseopts $ropt" ;;
esac
done
ropt=""
reqopts="$baseopts $tropts"
for ropt in $reqopts; do
case $ropt in
43) [ $discovery -eq 1 ] && newreqopts="$newreqopts $ropt" ;;
*) newreqopts="$newreqopts $ropt" ;;
esac
done
if [ $proto == "dhcp" ]; then
newreqopts="$(echo $newreqopts | tr ' ' '\n' | sort -n | tr '\n' ' ' | sed 's/^[ \t]*//;s/[ \t]*$//')"
oldreqopts="$(echo $oldreqopts | tr ' ' '\n' | sort -n | tr '\n' ' ' | sed 's/^[ \t]*//;s/[ \t]*$//')"
[ "$newreqopts" == "$oldreqopts" ] && return
uci -q set network.$wan.reqopts="$newreqopts"
uci commit network
ubus call network reload
fi
########################################################
}
wait_for_option43() {
local time=$1
local default_wan_interface dhcp_discovery url
config_get default_wan_interface cpe default_wan_interface "wan"
config_get dhcp_discovery acs dhcp_discovery "0"
config_get url acs url
enable_dhcp_option43 $default_wan_interface $dhcp_discovery
local tm=0
if [ "$dhcp_discovery" == "enable" -o "$dhcp_discovery" == "1" ]
then
echo "Waiting for discovery of ACS URL from dhcp server ..."
while [ $tm -le $time ]
do
acs_url=`uci -P /var/state -q get cwmp.acs.dhcp_url`
if [ "$acs_url" != "" ]
then
break
else
sleep 1
fi
tm=$((tm+1))
done
fi
}
wait_for_wifi() {
local time=$1
local tm=1
while [ ! -f /tmp/wifi.started ]; do
sleep 1
[ $tm -ge $time ] && break
tm=$((tm+1))
done
}
wait_for_resolvfile() {
local time=$1
local tm=1
local resolvfile="$(uci -q get dhcp.@dnsmasq[0].resolvfile)"
[ -n "$resolvfile" ] || return
while [ ! -f $resolvfile ]; do
sleep 1
[ $tm -ge $time ] && break
tm=$((tm+1))
done
}
wait_for_asterisk() {
local time=$1
local tm=1
while [ -z "$(pidof asterisk)" ]; do
sleep 1
[ $tm -ge $time ] && break
tm=$((tm+1))
done
}
set_wan_interface() {
local l3_device=""
local default_wan_interface=""
config_get default_wan_interface cpe default_wan_interface "wan"
json_load "$(ifstatus $default_wan_interface)"
json_get_var l3_device l3_device
if [ "$l3_device" != "" ];then
uci -q set cwmp.cpe.interface="$l3_device"
uci -q commit cwmp
fi
}
start_service() {
if [ ! -f /tmp/.icwmpd_boot ]; then
touch /etc/icwmpd/.icwmpd_boot
touch /tmp/.icwmpd_boot
else
[ -f /sbin/netifd ] && echo "Waiting for Network to be started ..." && ubus -t 5 wait_for network.interface
[ -f /etc/config/wireless ] && echo "Waiting for WiFi to be started ..." && wait_for_wifi 20
[ -f /usr/sbin/dnsmasq ] && echo "Waiting for DNS Proxy to be started ..." && ubus -t 5 wait_for dnsmasq
[ -f /etc/config/dhcp ] && echo "Waiting for DNS Server(s) ..." && wait_for_resolvfile 20
[ -f /usr/sbin/asterisk ] && echo "Waiting for Voice to be started ..." && wait_for_asterisk 5
config_load cwmp
build_dmmap_instance
set_wan_interface
wait_for_option43 20
get_acs_url
procd_open_instance
if [ "$(uci -q get cwmp.cpe.datamodel)" == "tr098" ]; then
procd_set_param command "$PROG098"
else
procd_set_param command "$PROG"
fi
if [ "$1" = "GetRPCMethods" ];then
procd_append_param command -g
elif [ -f /etc/icwmpd/.icwmpd_boot ]; then
procd_append_param command -b
fi
procd_set_param respawn "3" "7" "0"
procd_close_instance
procd_open_instance
if [ "$(uci -q get cwmp.cpe.notification)" == "1" ]; then
procd_append_param command "/usr/sbin/icwmp_notifd"
fi
procd_close_instance
fi
}
reload_service() {
stop
start
}
notify() {
ubus -t 1 call tr069 notify >/dev/null 2>&1 &
}
service_triggers() {
procd_add_config_trigger "config.change" "cwmp" /etc/init.d/icwmpd reload
if [ "$(uci -q get cwmp.cpe.notification)" == "1" ]; then
for conf in $(ls /etc/config/); do
[ "$conf" == "cwmp" ] && continue
procd_add_config_trigger "config.change" "$conf" /etc/init.d/icwmpd notify
done
fi
}

View file

@ -0,0 +1,12 @@
#!/bin/sh
uci -q batch <<-EOT
delete firewall.cwmp
set firewall.cwmp=include
set firewall.cwmp.path=/etc/firewall.cwmp
set firewall.cwmp.reload=1
commit firewall
EOT
exit 0

42
trx69/stun/Makefile Executable file
View file

@ -0,0 +1,42 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=stun
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=BBF stun Client
DEPENDS:=+libubus +libuci +libubox +libjson-c +libopenssl +libblobmsg-json
endef
define Package/$(PKG_NAME)/description
CWMP STUN Client
endef
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
TARGET_CFLAGS += \
-D_GNU_SOURCE
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/stund $(1)/usr/sbin/
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View file

@ -0,0 +1,14 @@
config stun 'stun'
option enable '0'
option username 'stun'
option password 'stun'
option server_address 'stun.l.google.com'
option server_port '19302'
option min_keepalive '30'
option max_keepalive '3600'
# option client_port 7547
#if client_port option is not set or < 0 then use a random port for connection request source port
#Log levels: Critical=0, Warning=1, Notice=2, Info=3, Debug=4
option log_level '3'

View file

@ -0,0 +1,36 @@
#!/bin/sh /etc/rc.common
# STUN client software
# Copyright (C) 2020 iopsys Software Solutions AB
# Author: Omar Kallel <omar.kallel@pivasoftware.com>
START=90
USE_PROCD=1
PROG="/usr/sbin/stund"
start_service() {
local enable=`uci -q get stun.stun.enable`
if [ "$enable" == "1" ]; then
local server=`uci -q get stun.stun.server_address`
[ "$server" = "" ] && exit 0
procd_open_instance
procd_set_param command "$PROG"
procd_set_param respawn "3" "7" "0"
procd_close_instance
fi
}
boot() {
start
}
reload_service() {
logger -p crit -t "stun" "reloading service()"
stop
start
}
service_triggers()
{
procd_add_reload_trigger stun
}

16
trx69/stun/src/Makefile Normal file
View file

@ -0,0 +1,16 @@
PROG = stund
OBJS = stun.o config.o log.o ubus.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) -lubus -luci -lubox -ljson-c -lcrypto -lblobmsg_json
%.o: %.c
$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
all: $(PROG)
$(PROG): $(OBJS)
$(CC) $(PROG_CFLAGS) -o $@ $^ $(PROG_LDFLAGS)
clean:
rm -f *.o $(PROG)

297
trx69/stun/src/config.c Normal file
View file

@ -0,0 +1,297 @@
/*
* config.c -- contains functions that allows reading and loading of uci config parameters of stun
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "config.h"
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
static struct uci_context *uci_ctx = NULL;
static struct uci_context *uci_ctx_state = NULL;
struct stun_config conf;
int config_fini(void)
{
free(conf.server_address);
free(conf.password);
free(conf.username);
conf.server_address = NULL;
conf.username = NULL;
conf.password = NULL;
return 0;
}
int config_init(void)
{
char *v;
suci_init();
memset(&conf, 0, sizeof(struct stun_config));
v = suci_get_value("stun", "stun", "server_address");
if (!*v) {
stun_log(SCRIT, "Missing Server Address in the STUN config");
goto error;
}
conf.server_address = strdup(v);
v = suci_get_value("stun", "stun", "username");
if (*v)
conf.username = strdup(v);
v = suci_get_value("stun", "stun", "password");
if (*v)
conf.password = strdup(v);
v = suci_get_value("stun", "stun", "server_port");
if (*v)
conf.server_port = atoi(v);
else
conf.server_port = DEFAULT_SERVERPORT;
v = suci_get_value("stun", "stun", "log_level");
if (*v)
conf.loglevel = atoi(v);
else
conf.loglevel = DEFAULT_LOGLEVEL;
v = suci_get_value("stun", "stun", "min_keepalive");
if (*v)
conf.min_keepalive = atoi(v);
else
conf.min_keepalive = DEFAULT_MINKEEPALIVE;
v = suci_get_value("stun", "stun", "max_keepalive");
if (*v)
conf.max_keepalive = atoi(v);
else
conf.max_keepalive = DEFAULT_MAXKEEPALIVE;
v = suci_get_value("stun", "stun", "client_port");
if (*v)
conf.client_port = atoi(v);
if (conf.max_keepalive <= 0)
conf.max_keepalive = DEFAULT_MAXKEEPALIVE;
if (conf.min_keepalive <= 0)
conf.min_keepalive = DEFAULT_MINKEEPALIVE;
if (conf.server_port <= 0)
conf.server_port = DEFAULT_SERVERPORT;
if (conf.loglevel >= __MAX_SLOG || conf.loglevel < 0) {
conf.loglevel = DEFAULT_LOGLEVEL;
}
stun_log(SINFO, "STUN CONFIG - Server Address: %s", conf.server_address);
stun_log(SINFO, "STUN CONFIG - Username: %s", conf.username ? conf.username : "<not defined>");
stun_log(SINFO, "STUN CONFIG - Server port: %d", conf.server_port);
stun_log(SINFO, "STUN CONFIG - min keepalive: %d", conf.min_keepalive);
stun_log(SINFO, "STUN CONFIG - max keepalive: %d", conf.max_keepalive);
stun_log(SINFO, "STUN CONFIG - Client port: %d", (conf.client_port > 0) ? conf.client_port : -1);
stun_log(SINFO, "STUN CONFIG - LOG Level: %d (Critical=0, Warning=1, Notice=2, Info=3, Debug=4)", conf.loglevel);
suci_fini();
return 0;
error:
suci_fini();
config_fini();
exit(1);
}
int suci_init(void)
{
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return -1;
}
uci_ctx_state = uci_alloc_context();
if (!uci_ctx_state) {
return -1;
}
uci_add_delta_path(uci_ctx_state, uci_ctx_state->savedir);
uci_set_savedir(uci_ctx_state, VAR_STATE);
return 0;
}
int suci_fini(void)
{
if (uci_ctx) {
uci_free_context(uci_ctx);
}
if (uci_ctx_state) {
uci_free_context(uci_ctx_state);
}
uci_ctx = NULL;
uci_ctx_state = NULL;
return 0;
}
static bool suci_validate_section(const char *str)
{
if (!*str)
return false;
for (; *str; str++) {
unsigned char c = *str;
if (isalnum(c) || c == '_')
continue;
return false;
}
return true;
}
static int suci_init_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *package, char *section, char *option, char *value)
{
memset(ptr, 0, sizeof(struct uci_ptr));
/* value */
if (value) {
ptr->value = value;
}
ptr->package = package;
if (!ptr->package)
goto error;
ptr->section = section;
if (!ptr->section) {
ptr->target = UCI_TYPE_PACKAGE;
goto lastval;
}
ptr->option = option;
if (!ptr->option) {
ptr->target = UCI_TYPE_SECTION;
goto lastval;
} else {
ptr->target = UCI_TYPE_OPTION;
}
lastval:
if (ptr->section && !suci_validate_section(ptr->section))
ptr->flags |= UCI_LOOKUP_EXTENDED;
return 0;
error:
return -1;
}
void suci_print_list(struct uci_list *uh, char **val, char *delimiter)
{
struct uci_element *e;
static char buffer[512];
char *buf = buffer;
*buf = '\0';
uci_foreach_element(uh, e) {
if (*buf) {
strcat(buf, delimiter);
strcat(buf, e->name);
}
else {
strcpy(buf, e->name);
}
}
*val = buf;
}
char *suci_get_value(char *package, char *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (!section || !option)
return val;
if (suci_init_ptr(uci_ctx, &ptr, package, section, option, NULL))
return val;
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK)
return val;
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
suci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}
char *suci_set_value(char *package, char *section, char *option, char *value)
{
struct uci_ptr ptr;
int ret = UCI_OK;
if (!section)
return "";
if (suci_init_ptr(uci_ctx, &ptr, package, section, option, value))
return "";
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK)
return "";
uci_set(uci_ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(uci_ctx, ptr.p);
if (ptr.o && ptr.o->v.string)
return ptr.o->v.string;
return "";
}
/*************************---/var/state--***************************/
char *suci_get_value_state(char *package, char *section, char *option)
{
char *val;
struct uci_context *save_uci_ctx = uci_ctx;
uci_ctx = uci_ctx_state;
val = suci_get_value(package, section, option);
uci_ctx = save_uci_ctx;
return val;
}
char *suci_set_value_state(char *package, char *section, char *option, char *value)
{
char *val;
struct uci_context *save_uci_ctx = uci_ctx;
uci_ctx = uci_ctx_state;
val = suci_set_value(package, section, option, value);
uci_ctx = save_uci_ctx;
return val;
}

44
trx69/stun/src/config.h Normal file
View file

@ -0,0 +1,44 @@
/* TR-069 STUN client software
* Copyright (C) 2020 PIVA SOFTWARE <www.pivasoftware.com> - All Rights Reserved
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#include <uci.h>
#include "log.h"
#define VAR_STATE "/var/state"
#define DEFAULT_SERVERPORT 3478
#define DEFAULT_CLIENTPORT 7547
#define DEFAULT_MINKEEPALIVE 30
#define DEFAULT_RETRYTIME 3
#define DEFAULT_MAXKEEPALIVE 3600
#define DEFAULT_LOGLEVEL SINFO
struct stun_config {
char *server_address;
char *username;
char *password;
int server_port;
int client_port;
int max_keepalive;
int min_keepalive;
int loglevel;
};
extern struct stun_config conf;
int config_init(void);
int config_fini(void);
int suci_init(void);
int suci_fini(void);
void suci_print_list(struct uci_list *uh, char **val, char *delimiter);
char *suci_get_value(char *package, char *section, char *option);
char *suci_set_value(char *package, char *section, char *option, char *value);
char *suci_get_value_state(char *package, char *section, char *option);
char *suci_set_value_state(char *package, char *section, char *option, char *value);
#endif //__CONFIG_H

68
trx69/stun/src/log.c Normal file
View file

@ -0,0 +1,68 @@
/*
* log.c : conatains function used log traces
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <syslog.h>
#include <time.h>
#include "log.h"
#include "config.h"
static const int log_syslogmap[] = {
[SCRIT] = LOG_CRIT,
[SWARNING] = LOG_WARNING,
[SNOTICE] = LOG_NOTICE,
[SINFO] = LOG_INFO,
[SDEBUG] = LOG_DEBUG
};
static const char* log_str[] = {
[SCRIT] = "CRITICAL",
[SWARNING] = "WARNING",
[SNOTICE] = "NOTICE",
[SINFO] = "INFO",
[SDEBUG] = "DEBUG"
};
void stun_log(int priority, const char *format, ...)
{
va_list vl;
if (priority <= conf.loglevel) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
va_start(vl, format);
printf("%d-%02d-%02d %02d:%02d:%02d [stun] %s - ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, log_str[priority]);
vprintf(format, vl);
va_end(vl);
printf("\n");
openlog("stun", 0, LOG_DAEMON);
va_start(vl, format);
vsyslog(log_syslogmap[priority], format, vl);
va_end(vl);
closelog();
}
}

20
trx69/stun/src/log.h Normal file
View file

@ -0,0 +1,20 @@
/* TR-069 STUN client software
* Copyright (C) 2020 PIVA SOFTWARE <www.pivasoftware.com> - All Rights Reserved
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*/
#ifndef __SLOG_H
#define __SLOG_H
enum stun_log_level_enum {
SCRIT,
SWARNING,
SNOTICE,
SINFO,
SDEBUG,
__MAX_SLOG
};
void stun_log(int priority, const char *format, ...);
#endif //__SLOG_H

687
trx69/stun/src/stun.c Normal file
View file

@ -0,0 +1,687 @@
/*
* stun.c -- the main file of stun application
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* TR-069 STUN client software
* Copyright (C) 2020 PIVA SOFTWARE <www.pivasoftware.com> - All Rights Reserved
* Author: Mohamed Kallel <mohamed.kallel@pivasoftware.com>
* Omar Kallel <omar.kallel@pivasoftware.com>
* Anis Ellouze <anis.ellouze@pivasoftware.com>
*/
#include <stdio.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/un.h>
#include <stdlib.h>
#include <libubox/uloop.h>
#include <openssl/hmac.h>
#include <string.h>
#include <ifaddrs.h>
#include "stun.h"
#include "config.h"
#include "ubus.h"
struct env_var env = {0};
int keepalive_timeout = DEFAULT_MINKEEPALIVE;
int retry_timeout = DEFAULT_RETRYTIME;
int bindcrreusaddr = 1;
int bindcrreusport = 1;
const char *BBFCR = "dslforum.org/TR-111 ";
static void stun_notify_cb(struct uloop_timeout *timeout);
static void stun_inform_cb(struct uloop_timeout *timeout);
static void listening_crport_cb(struct uloop_timeout *timeout);
static void binding_request_crport_cb(struct uloop_timeout *timeout);
static struct udp_listen listen_crport = {
.fd = -1,
.utimer = {.cb = listening_crport_cb}
};
static struct binding_request br_crport = {
.binding_cr = 1,
.udp_listen = &listen_crport,
.utimer = {.cb = binding_request_crport_cb}
};
static struct binding_request br_crport_keepalive = {
.is_keealive = 1,
.binding_cr = 1,
.udp_listen = &listen_crport,
.utimer = {.cb = binding_request_crport_cb}
};
struct uloop_timeout stun_notify_timer = {.cb = stun_notify_cb};
struct uloop_timeout stun_inform_timer = {.cb = stun_inform_cb};
void stun_notify(int afterms)
{
uloop_timeout_set(&stun_notify_timer, afterms);
}
void stun_inform(int afterms)
{
uloop_timeout_set(&stun_inform_timer, afterms);
}
static void stun_notify_cb(struct uloop_timeout *timeout)
{
stun_log(SINFO, "ubus call tr069 notify");
if (subus_call("tr069", "notify", 0, UBUS_ARGS{}) < 0) {
stun_log(SINFO, "ubus call tr069 notify failed! retry after 1s");
stun_notify(1000);
}
}
static void stun_inform_cb(struct uloop_timeout *timeout)
{
stun_log(SINFO, "ubus call tr069 inform '{\"event\": \"6 connection request\"}'");
if (subus_call("tr069", "inform", 1, UBUS_ARGS{{"event", "6 connection request"}}) < 0) {
stun_log(SINFO, "ubus call tr069 inform '{\"event\": \"6 connection request\"}' failed! retry after 1s");
stun_inform(1000);
}
}
static int stunid_cmp(stunid *left, stunid *right)
{
return memcmp(left, right, sizeof(*left));
}
static void *stunid_cpy(stunid *left, stunid *right)
{
return memcpy(left, right, sizeof(*left));
}
static void stunid_rand(stunid *id)
{
int i;
srand(time(NULL));
for (i = 0; i < 4; i++)
{
id->id[i] = rand();
}
}
ssize_t timeout_recvfrom(int sock, char *buf, int length, struct sockaddr_in *connection, int timeoutinseconds)
{
fd_set socks;
ssize_t r = 0;
struct timeval t = {0};
int clen = sizeof(*connection);
stun_log(SDEBUG, "udp revcfrom, timeout: %ds", timeoutinseconds);
FD_ZERO(&socks);
FD_SET(sock, &socks);
t.tv_sec = timeoutinseconds;
if (select(sock + 1, &socks, NULL, NULL, &t) &&
(r = recvfrom(sock, buf, length, 0, (struct sockaddr *)connection, (socklen_t *)&clen)) != -1) {
return r;
}
else {
return -1;
}
}
static int stun_send(int s, char *buf)
{
struct stun_header *sh;
struct hostent *he;
struct sockaddr_in dst = {0};
sh = (struct stun_header *)buf;
if ((he = gethostbyname(conf.server_address)) == NULL) {
return -1;
}
memcpy(&(dst.sin_addr), he->h_addr_list[0], he->h_length);
dst.sin_port = htons(conf.server_port);
dst.sin_family = AF_INET;
stun_log(SINFO, "send STUN message to %s:%u (%s:%u)", conf.server_address, conf.server_port, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port));
return sendto(s, buf, ntohs(sh->len) + sizeof(*sh), 0, (struct sockaddr *)&dst, sizeof(dst));
}
static int net_socket(int srcport)
{
int sock = -1;
stun_log(SINFO, "Open UDP socket");
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock >= 0) {
if (srcport > 0) {
struct sockaddr_in bindcraddr = {0};
int i = 0;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &bindcrreusaddr, sizeof(int)) < 0) {
stun_log(SWARNING, "setsockopt(SO_REUSEADDR) failed");
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &bindcrreusport, sizeof(int)) < 0) {
stun_log(SWARNING, "setsockopt(SO_REUSEPORT) failed");
}
bindcraddr.sin_family = AF_INET;
bindcraddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindcraddr.sin_port = htons((unsigned short)srcport);
for(;i<9;i++)
{
if (bind(sock, (struct sockaddr *)&bindcraddr, sizeof(bindcraddr)) < 0) {
continue;
}
stun_log(SINFO, "binding socket source port to %u", srcport);
break;
}
}
fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
}
return sock;
}
static void stun_close_socket(struct udp_listen *udp_listen)
{
uloop_timeout_cancel(&udp_listen->utimer);
if (udp_listen->fd > 0) {
stun_log(SINFO, "STUN close socket %d", udp_listen->fd);
close(udp_listen->fd);
}
udp_listen->fd = -1;
}
static void stun_socket(struct udp_listen *udp_listen)
{
if (udp_listen->fd > 0) {
stun_close_socket(udp_listen);
}
udp_listen->fd = net_socket(conf.client_port);
stun_log(SINFO, "STUN new socket %d", udp_listen->fd);
uloop_timeout_set(&udp_listen->utimer, 0);
}
static int trailing_buffer_alloc(int mp, char *buf, int len, char trail, char **bufm, int *mlen)
{
*bufm = NULL;
*mlen = len % mp;
*mlen = len + (*mlen ? (mp - *mlen) : 0);
*bufm = calloc(1, *mlen);
if (*bufm == NULL)
return -1;
memcpy(*bufm, buf, len);
if (trail) {
while(len < *mlen) {
(*bufm)[len++] = trail;
}
}
return 0;
}
static int append_attribute_buffer(unsigned char **start, char *buf, int len, unsigned short attr_type, int free)
{
if ((sizeof(struct stun_attribute) + len) > free)
return -1;
struct stun_attribute *sa = (struct stun_attribute *)*start;
sa->len = htons((unsigned short)len);
sa->type = htons(attr_type);
memcpy(sa->value, buf, len);
*start += sizeof(struct stun_attribute) + len;
return 0;
}
static int stun_hmac(int trailing_mp, unsigned char *data, int len, char *password, char *hmac)
{
char *bufmp = NULL;
int lenmp;
unsigned char* digest;
if (trailing_mp) {
if (trailing_buffer_alloc(trailing_mp, (char *)data, len, 0, &bufmp, &lenmp))
return -1;
digest = HMAC(EVP_sha1(), password, strlen(password), (unsigned char *)bufmp, lenmp, NULL, NULL);
free(bufmp);
}
else {
digest = HMAC(EVP_sha1(), password, strlen(password), data, len, NULL, NULL);
}
memcpy(hmac, digest, 20);
return 0;
}
static void hex_to_str(char *hex, int len, char *str)
{
while (len--) {
sprintf(str, "%02X", *hex++);
str += 2;
}
}
static int generate_stun_packet(struct binding_request *br, char *req_buf, int maxlen)
{
struct stun_header *req;
unsigned char *stunmsg;
req = (struct stun_header *) req_buf;
stunmsg = req->stunmsg;
stun_log(SINFO, "STUN generate BINDING-REQUEST");
req->type = htons(BINDING_REQUSET);
stunid_rand(&(req->id));
stunid_cpy(&(br->id), &(req->id));
stun_log(SINFO, "STUN request id: %d%d%d%d", req->id.id[0], req->id.id[1], req->id.id[2], req->id.id[3]);
if (conf.username) {
char *buf4;
int len4;
if (trailing_buffer_alloc(4, conf.username, strlen(conf.username), ' ', &buf4, &len4))
return -1;
append_attribute_buffer(&stunmsg, buf4, len4, ATTR_USERNAME, (maxlen - (stunmsg - (unsigned char *)req)));
stun_log(SINFO, "STUN append USERNAME: **%.*s**", len4, buf4);
free(buf4);
}
if (br->binding_cr) {
stun_log(SINFO, "STUN append CONNECTION-REQUEST-BINDING: **%s**", BBFCR);
append_attribute_buffer(&stunmsg, (char *)BBFCR, strlen(BBFCR), ATTR_CONNECTION_REQUEST_BINDING, (maxlen - (stunmsg - (unsigned char *)req)));
}
if (br->binding_change) {
stun_log(SINFO, "STUN append BINDING-CHANGE");
append_attribute_buffer(&stunmsg, "", 0, ATTR_BINDING_CHANGE, (maxlen - (stunmsg - (unsigned char *)req)));
}
if (br->msg_integrity) {
if (conf.username) {
char *password;
char hmac[20] = {0};
char hmacstr[64];
req->len = htons((stunmsg - (unsigned char *)req) - sizeof(struct stun_header) + sizeof(struct stun_attribute) + 20);
password = conf.password ? conf.password : "";
stun_hmac(64, (unsigned char *)req, stunmsg - (unsigned char *)req, password, hmac);
append_attribute_buffer(&stunmsg, hmac, sizeof(hmac), ATTR_MESSAGE_INTEGRITY, (maxlen - (stunmsg - (unsigned char *)req)));
hex_to_str(hmac, 20, hmacstr);
stun_log(SINFO, "STUN append MESSAGE-INTEGRITY: ***%s***", hmacstr);
}
else {
req->len = htons((stunmsg - (unsigned char *)req) - sizeof(struct stun_header));
br->msg_integrity = 0;
return -1;
}
}
else {
req->len = htons((stunmsg - (unsigned char *)req) - sizeof(struct stun_header));
}
stun_log(SINFO, "STUN request length: %d", ntohs(req->len));
return 0;
}
static int stun_get_mapped_address(char *buf, unsigned int *ip, unsigned short *port)
{
struct stun_header *sh = (struct stun_header *)buf;
struct stun_attribute *sa = (struct stun_attribute *)sh->stunmsg;
char *p;
while (((char *)sa - (char *)sh - sizeof(*sh)) < ntohs(sh->len)) {
if(ntohs(sa->type) == ATTR_MAPPED_ADDRESS) {
struct stun_address *ma = (struct stun_address *)sa->value;
*port = ma->port;
*ip = ma->address;
return 0;
}
p = (char *)sa;
p += sizeof(struct stun_attribute) + ntohs(sa->len);
sa = (struct stun_attribute *)p;
}
return -1;
}
static int stun_get_error_code(char *buf)
{
struct stun_header *sh = (struct stun_header *)buf;
struct stun_attribute *sa = (struct stun_attribute *)sh->stunmsg;
char *p;
while (((char *)sa - (char *)sh - sizeof(*sh)) < ntohs(sh->len)) {
if(ntohs(sa->type) == ATTR_ERROR_CODE) {
unsigned int class, number;
unsigned int ui = ntohl(*((unsigned int *)sa->value));
class = (ui >> 8) & 0x7;
number = ui & 0xff;
return (int)(class * 100 + number);
}
p = (char *)sa;
p += sizeof(struct stun_attribute) + ntohs(sa->len);
sa = (struct stun_attribute *)p;
}
return 0;
}
static void handle_udp_cr(char *resp_buf)
{
char *str;
char un[64], cn[64], sig[64], buf[256];
char *crusername;
char *crpassword;
unsigned int crid = 0, ts = 0;
int valid = 1;
stun_log(SINFO, "Handle UDP Connection Request");
if ((str = strstr(resp_buf, "ts="))) {
sscanf(str, "ts=%u", &ts);
stun_log(SINFO, "UDP CR ts = %u", ts);
}
else {
stun_log(SWARNING, "UDP CR ts not found");
return;
}
if ((str = strstr(resp_buf, "id="))) {
sscanf(str, "id=%u", &crid);
stun_log(SINFO, "UDP CR id = %u", crid);
}
else {
return;
stun_log(SWARNING, "UDP CR id not found");
}
if (crid && ts && crid != env.last_crid && ts > env.last_ts) {
stun_log(SINFO, "NEW UDP CR");
env.last_crid = crid;
env.last_ts = ts;
if ((str = strstr(resp_buf, "un="))) {
sscanf(str, "un=%63[^?& \t\n\r]", un);
stun_log(SINFO, "UDP CR un = %s", un);
}
else {
stun_log(SWARNING, "UDP CR un not found");
return;
}
if ((str = strstr(resp_buf, "cn="))) {
sscanf(str, "cn=%63[^?& \t\n\r]", cn);
stun_log(SINFO, "UDP CR cn = %s", cn);
}
else {
stun_log(SWARNING, "UDP CR cn not found");
return;
}
if ((str = strstr(resp_buf, "sig="))) {
sscanf(str, "sig=%63[^?& \t\n\r]",sig);
stun_log(SINFO, "UDP CR sig = %s", sig);
}
else {
stun_log(SWARNING, "UDP CR sig not found");
return;
}
suci_init();
crusername = suci_get_value("cwmp", "cpe", "userid");
crpassword = suci_get_value("cwmp", "cpe", "password");
if (*crusername && *crpassword) {
if (strcmp(crusername, un) != 0) {
stun_log(SINFO, "UDP CR username mismatch!");
valid = 0;
}
else {
char hmac[20], hmacstr[64];
snprintf(buf, sizeof(buf), "%u%u%s%s", ts, crid, un, cn);
stun_hmac(0, (unsigned char *)buf, strlen(buf), crpassword, hmac);
hex_to_str(hmac, 20, hmacstr);
if (strcasecmp(hmacstr, sig) != 0) {
stun_log(SINFO, "UDP CR sig mismatch!");
valid = 0;
}
}
}
suci_fini();
if (valid) {
stun_inform(0);
}
} else {
if (!ts || !crid)
stun_log(SINFO, "UDP CR ts or id not found");
else
stun_log(SINFO, "UDP CR ts is old or id is already received");
}
}
static void save_udpcr_var_state(unsigned int ip, unsigned short port)
{
struct in_addr ip_addr;
char buf[64];
ip_addr.s_addr = env.address;
snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(ip_addr), ntohs(env.port));
stun_log(SINFO, "Save New UDPConnectionRequestAddress to /var/state %s", buf);
suci_init();
suci_set_value_state("stun", "stun", "crudp_address", buf);
suci_fini();
}
static int is_udpcr_changed(unsigned int ip, unsigned short port)
{
struct in_addr ip_addr;
char buf[64];
char *v;
int changed = 0;
ip_addr.s_addr = ip;
snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(ip_addr), ntohs(port));
suci_init();
v = suci_get_value_state("stun", "stun", "crudp_address");
if (strcmp(buf, v) != 0)
changed = 1;
suci_fini();
return changed;
}
static void save_natdetected_var_state(unsigned int ip)
{
struct ifaddrs * ifaddrlist = NULL;
struct ifaddrs * ifa = NULL;
int islocal = 0;
char *nd;
getifaddrs(&ifaddrlist);
for (ifa = ifaddrlist; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa ->ifa_addr && ifa ->ifa_addr->sa_family == AF_INET) {
if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == ip) {
islocal = 1;
break;
}
}
}
if (ifaddrlist != NULL)
freeifaddrs(ifaddrlist);
suci_init();
nd = suci_get_value_state("stun", "stun", "nat_detected");
if (islocal && *nd != '\0') {
stun_log(SINFO, "Device is not behind NAT, set NATDetected to false");
suci_set_value_state("stun", "stun", "nat_detected", "");
}
else if (!islocal && *nd == '\0') {
stun_log(SINFO, "Device is behind NAT, set NATDetected to true");
suci_set_value_state("stun", "stun", "nat_detected", "1");
}
suci_fini();
}
static void binding_request_crport_cb(struct uloop_timeout *timeout)
{
struct binding_request *br;
struct udp_listen *udp_listen;
char req_buf[2048] = {0};
int r;
br = binding_request_entry(timeout);
udp_listen = br->udp_listen;
udp_listen->br = br;
stun_log(SINFO, "Binding Request cb start %s", br->is_keealive ? "(KeepAlive)" : "");
if (udp_listen->fd <= 0) {
stun_socket(udp_listen);
}
if (br->resp_success > 0)
br->resp_success = 0;
if (generate_stun_packet(br, req_buf, sizeof(req_buf) - 1)) {
br->retry_interval = (br->retry_interval) ? 2 * br->retry_interval : retry_timeout;
br->retry_interval = (br->retry_interval > 1500) ? 1500 : br->retry_interval;
uloop_timeout_set(&br->utimer, br->retry_interval * 1000);
return;
}
r = stun_send(udp_listen->fd, req_buf);
if (r < 0) {
stun_close_socket(udp_listen);
udp_listen->br = NULL;
br->retry_interval = (br->retry_interval) ? 2 * br->retry_interval : retry_timeout;
br->retry_interval = (br->retry_interval > 1500) ? 1500 : br->retry_interval;
stun_log(SINFO, "Failed send of Binding Request! retry in %ds", br->retry_interval);
uloop_timeout_set(timeout, br->retry_interval * 1000);
}
else {
br->retry_interval = 0;
stun_log(SINFO, "Success send of Binding Request.");
stun_log(SINFO, "Start KeepAlive Binding Request in %ds.", keepalive_timeout);
uloop_timeout_set(&br_crport_keepalive.utimer, keepalive_timeout * 1000);
}
}
static void listening_crport_cb(struct uloop_timeout *timeout)
{
struct udp_listen *udp_listen;
struct sockaddr_in src = {0};
char resp_buf[2048] = {0};
unsigned int ip = 0;
unsigned short port = 0;
int r;
stun_log(SDEBUG, "Binding listening CR cb start");
udp_listen = udp_listen_entry(timeout);
if (udp_listen->fd < 0) {
stun_log(SINFO, "Binding listening CR: Socket = -1");
uloop_timeout_set(timeout, 19);
return;
}
r = timeout_recvfrom(udp_listen->fd, resp_buf, sizeof(resp_buf) - 1, &src, 1);
if (r > 0) {
stun_log(SINFO, "Binding listening CR: get UDP packet");
struct stun_header *sh = (struct stun_header *)resp_buf;
if (ntohs(sh->type) == BINDING_ERROR) {
int code = stun_get_error_code(resp_buf);
stun_log(SINFO, "get BINDING-ERROR: code is %d", code);
if (udp_listen->br != NULL && stunid_cmp(&(sh->id), &(udp_listen->br->id)) == 0 && code == 401) {
udp_listen->br->msg_integrity = 1;
udp_listen->br->auth_fail++;
stun_log(SINFO, "Cancel scheduled Keepalive Binding Request");
uloop_timeout_cancel(&br_crport_keepalive.utimer);
stun_log(SINFO, "Trying new Binding Request in %ds",
(udp_listen->br->auth_fail < 3) ? 0 : (udp_listen->br->auth_fail - 2)*3);
uloop_timeout_set(&udp_listen->br->utimer,
(udp_listen->br->auth_fail < 3) ? 0 : ((udp_listen->br->auth_fail - 2) * 3000));
udp_listen->br = NULL;
}
else if (code != 401) {
stun_log(SINFO, "Unsupported error code");
}
goto end;
}
else if (ntohs(sh->type) == BINDING_RESPONSE) {
struct in_addr ip_addr;
stun_log(SINFO, "get BINDING-RESPONSE");
if (udp_listen->br != NULL && stunid_cmp(&(sh->id), &(udp_listen->br->id)) == 0) {
udp_listen->br->resp_success = 1;
udp_listen->br->msg_integrity = 0;
udp_listen->br->auth_fail = 0;
stun_get_mapped_address(resp_buf, &ip, &port);
ip_addr.s_addr = ip;
stun_log(SINFO, "Mapped Address is: %s:%u", inet_ntoa(ip_addr), ntohs(port));
save_natdetected_var_state(ip);
if (is_udpcr_changed(ip, port)) {
env.address = ip;
env.port = port;
udp_listen->br->resp_success = 0;
udp_listen->br->binding_change = 1;
uloop_timeout_set(&udp_listen->br->utimer, 0);
save_udpcr_var_state(ip, port);
stun_notify(0);
}
else {
udp_listen->br = NULL;
}
}
goto end;
}
else if (strstr(resp_buf, "http") || strstr(resp_buf, "HTTP")) {
stun_log(SINFO, "get UDP Connection Request");
handle_udp_cr(resp_buf);
}
else {
stun_log(SINFO, "get non supported STUN/UDP message");
}
}
else {
/* timed out */
if (!udp_listen->br || udp_listen->br->resp_success == 1)
goto end;
stun_log(SINFO, "Timed OUT!");
udp_listen->br->resp_success--;
if (udp_listen->br->resp_success < -2) {
int rs = -udp_listen->br->resp_success;
if ((rs % 9) == 0) {
stun_log(SINFO, "Retry sending in a new socket");
stun_close_socket(udp_listen);
uloop_timeout_set(&udp_listen->br->utimer, 0);
udp_listen->br = NULL;
}
else if ((rs % 3) == 0) {
stun_log(SINFO, "Retry sending.");
uloop_timeout_set(&udp_listen->br->utimer, 0);
}
}
}
end:
uloop_timeout_set(timeout, 1);
}
int main()
{
stun_log(SINFO, "Start stund daemon");
config_init();
keepalive_timeout = conf.min_keepalive;
uloop_init();
uloop_timeout_set(&br_crport.utimer, 100);
uloop_run();
uloop_done();
config_fini();
stun_log(SINFO, "Stop stund daemon");
return 0;
}

106
trx69/stun/src/stun.h Normal file
View file

@ -0,0 +1,106 @@
#ifndef _STUN_H
#define _STUN_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libubox/uloop.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#ifndef container_of
#define container_of(ptr, type, member) \
({ \
const typeof(((type *) NULL)->member) *__mptr = (ptr); \
(type *) ((char *) __mptr - offsetof(type, member)); \
})
#endif
#define binding_request_entry(X) container_of(X, struct binding_request, utimer)
#define udp_listen_entry(X) container_of(X, struct udp_listen, utimer)
#define BINDING_REQUSET 0x0001
#define BINDING_RESPONSE 0x0101
#define BINDING_ERROR 0x0111
#define ATTR_MAPPED_ADDRESS 0x0001
#define ATTR_RESPONSE_ADDRESS 0x0002
#define ATTR_CHANGE_REQUEST 0x0003
#define ATTR_SOURCE_ADDRESS 0x0004
#define ATTR_CHANGED_ADDRESS 0x0005
#define ATTR_USERNAME 0x0006
#define ATTR_PASSWORD 0x0007
#define ATTR_MESSAGE_INTEGRITY 0x0008
#define ATTR_ERROR_CODE 0x0009
#define ATTR_UNKNOWN_ATTRIBUTES 0x000a
#define ATTR_REFLECTED_FROM 0x000b
#define ATTR_CONNECTION_REQUEST_BINDING 0xC001
#define ATTR_BINDING_CHANGE 0xC002
#define STUN_HEADER_LEN 20
struct binding_request;
typedef struct { unsigned int id[4]; } __attribute__((packed)) stunid;
struct udp_listen {
stunid expected_id;
int fd;
struct uloop_timeout utimer;
struct binding_request *br;
};
struct binding_request {
stunid id;
unsigned char is_keealive;
unsigned char msg_integrity; /* if true ==> MESSAGE-INTEGRITY should be append in the STUN msg */
unsigned char binding_change; /* if true ==> BINDING-CHANGE should be append in the STUN msg */
unsigned char binding_cr; /* if true ==> CONNECTION-REQUEST-BINDING should be append in the STUN msg */
int resp_success;
int auth_fail;
int retry_interval;
struct udp_listen *udp_listen;
struct uloop_timeout utimer;
};
struct stun_header {
unsigned short type;
unsigned short len;
stunid id;
unsigned char stunmsg[0];
} __attribute__((packed));
struct stun_attribute {
unsigned short type;
unsigned short len;
unsigned char value[0];
} __attribute__((packed));
struct stun_address {
unsigned char na;
unsigned char family;
unsigned short port;
unsigned int address;
} __attribute__((packed));
struct env_var {
unsigned short port;
unsigned int address;
unsigned int last_ts;
unsigned int last_crid;
};
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* _STUN_H */

49
trx69/stun/src/stun.md Normal file
View file

@ -0,0 +1,49 @@
# README #
In order to reach the devices that are connected behind NAT, the cwmp protocol introduces alternative method of executing Connection Request via NAT based on STUN. The stund is an implementation of STUN functionality that performs this feature.
## Configuration File ##
The stund UCI configuration is located in **'/etc/config/stun'** and contains only one section **stun**:
```
config stun 'stun'
option enable '0'
option username 'stun'
option password 'stun'
option server_address 'stun.l.google.com'
option server_port '19302'
option min_keepalive '30'
option max_keepalive '3600'
option client_port 7547
option log_level '1'
```
### stun section ###
It defines **the stun section configuration** (like username, password, etc...). The possible options for **stun** section are listed in the table below.
| Name | Type | Description |
| ---------------- | ------- | ------------------------------------------------- |
| `enable` | boolean | if set to **1**, the STUN client will be enabled. |
| `username` | string | The STUN username to be used in Binding Requests. |
| `password` | string | The STUN Password to be used in computing the MESSAGE-INTEGRITY. |
| `server_address` | string | The host name or IP address of the STUN server to send Binding Requests. |
| `server_port` | integer | The port number of the STUN server to send Binding Requests. |
| `min_keepalive` | integer | The minimum period that STUN Binding Requests must be sent by the CPE for the purpose of maintaining the binding in the Gateway. |
| `max_keepalive` | integer | The maximum period that STUN Binding Requests must be sent by the CPE for the purpose of maintaining the binding in the Gateway. |
| `client_port` | integer | The client source port of the STUN UDP binding. |
| `log_level` | integer | The log type to use, by default it is set to **'INFO'**. The possible types are **'EMERG', 'ALERT', 'CRITIC' ,'ERROR', 'WARNING', 'NOTICE', 'INFO' and 'DEBUG'**. |
## Dependencies ##
To successfully build stund, the following libraries are needed:
| Dependency | Link | License |
| --------------- | ------------------------------------------- | -------------- |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |
| libubox | https://git.openwrt.org/project/libubox.git | BSD |
| libubus | https://git.openwrt.org/project/ubus.git | LGPL 2.1 |
| libjson-c | https://s3.amazonaws.com/json-c_releases | MIT |
| libopenssl | http://ftp.fi.muni.cz/pub/openssl/source/ | OpenSSL |

108
trx69/stun/src/ubus.c Normal file
View file

@ -0,0 +1,108 @@
/*
* ubus.c -- This file conatains functions that allow to make ubus calls
*
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <json-c/json.h>
#include <libubox/blobmsg_json.h>
#include <libubus.h>
#include "ubus.h"
static struct ubus_context *ubus_ctx = NULL;
static struct blob_buf b;
int subus_init(void)
{
ubus_ctx = ubus_connect(NULL);
if (!ubus_ctx) {
return -1;
}
return 0;
}
int subus_fini(void)
{
if (ubus_ctx) {
ubus_free(ubus_ctx);
}
ubus_ctx = NULL;
return 0;
}
void sadd_json_obj(json_object *json_obj_out, char *object, char *string)
{
json_object *json_obj_tmp = json_object_new_string(string);
json_object_object_add(json_obj_out, object, json_obj_tmp);
}
static int subus_call_req(char *path, char *method, int argc, struct sarg sarg[])
{
uint32_t id;
int i, r = 1;
char *arg;
json_object *json_obj_out = json_object_new_object();
if (json_obj_out == NULL)
return r;
blob_buf_init(&b, 0);
if (argc) {
for (i = 0; i < argc; i++) {
sadd_json_obj(json_obj_out, sarg[i].key, sarg[i].val);
}
arg = (char *)json_object_to_json_string(json_obj_out);
if (!blobmsg_add_json_from_string(&b, arg)) {
goto end;
}
}
if (ubus_lookup_id(ubus_ctx, path, &id))
goto end;
r = ubus_invoke(ubus_ctx, id, method, b.head, NULL, NULL, 1);
end:
json_object_put(json_obj_out);
blob_buf_free(&b);
return r;
}
int subus_call(char *path, char *method, int argc, struct sarg dmarg[])
{
int r = -1;
subus_init();
if (ubus_ctx) {
r = subus_call_req(path, method, argc, dmarg);
if (r > 0) r = -1;
}
subus_fini();
return r;
}

22
trx69/stun/src/ubus.h Normal file
View file

@ -0,0 +1,22 @@
/* TR-069 STUN client software
* Copyright (C) 2020 PIVA SOFTWARE <www.pivasoftware.com> - All Rights Reserved
* Author: Omar Kallel <omar.kallel@pivasoftware.com>
*/
#ifndef __SUBUS_H
#define __SUBUS_H
#include <libubox/blobmsg_json.h>
#include <json-c/json.h>
#include <libubus.h>
struct sarg {
char *key;
char *val;
};
#define UBUS_ARGS (struct sarg[])
int subus_call(char *path, char *method, int argc, struct sarg sarg[]);
#endif //__SUBUS_H

44
trx69/twamp/Makefile Executable file
View file

@ -0,0 +1,44 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=twamp
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=BBF twamp feature
DEPENDS:=+libuci +libbbf_api
endef
define Package/$(PKG_NAME)/description
BBF twamp feature
endef
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
TARGET_CFLAGS += \
-D_GNU_SOURCE
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/twampd $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/lib/bbfdm
$(INSTALL_BIN) $(PKG_BUILD_DIR)/*.so $(1)/usr/lib/bbfdm
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View file

@ -0,0 +1,13 @@
config twamp 'twamp'
option id '1'
#Log levels: Critical=0, Warning=1, Notice=2, Info=3, Debug=4
option log_level '3'
config twamp_reflector
option id '1'
option enable '0'
option interface 'lan'
option port '862'
option max_ttl '1'

View file

@ -0,0 +1,56 @@
#!/bin/sh /etc/rc.common
#TWAMP Reflector software
# Copyright (C) 2020 iopsys Software Solutions AB
#Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
START=99
STOP=10
USE_PROCD=1
PROG="/usr/sbin/twampd"
start_service() {
local id=`uci -q get twamp.twamp.id`
local connection=`uci show twamp | grep "twamp.@twamp_reflector.*id=\'$id\'" | cut -d "." -f 2`
local enable=`uci -q get twamp.$connection.enable`
local interface=`uci -q get twamp.$connection.interface`
local port=`uci -q get twamp.$connection.port`
local port_list=`uci -q get twamp.$connection.port_list`
port_list=`echo $port_list | tr "," " "`
if [ "$enable" = "1" ]; then
if [ $interface = "wan" ]; then
for str in $port_list; do
if [ "${str/-}" != "$str" ] ; then
str=`echo $str | tr "-" ":"`
fi
iptables -I zone_wan_input -p udp --dport "$str" -j ACCEPT -m comment --comment "Open UDP allowed port"
iptables -I zone_wan_input -p tcp --dport "$port" -j ACCEPT -m comment --comment "Open TCP allowed port"
done
fi
procd_open_instance
procd_set_param command "$PROG"
procd_set_param respawn "3" "7" "0"
procd_close_instance
else
if [ $interface = "wan" ]; then
if [ "${str/-}" != "$str" ] ; then
str=`echo $str | tr "-" ":"`
fi
iptables -I zone_wan_input -p udp --dport "$str" -j REJECT -m comment --comment "Close UDP allowed port"
iptables -I zone_wan_input -p tcp --dport "$port" -j REJECT -m comment --comment "Close TCP allowed port"
fi
fi
}
boot() {
start
}
reload_service() {
stop
start
}
service_triggers() {
procd_add_reload_trigger twamp
}

23
trx69/twamp/src/Makefile Normal file
View file

@ -0,0 +1,23 @@
PROG = twampd
LIB = libtwamp.so
PROG_OBJS = twamp.o twamplog.o twampuci.o twamptimestamp.o
LIB_OBJS = datamodel.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) -luci
LIB_LDFLAGS = $(LDFLAGS) -lbbf_api
%.o: %.c
$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
all: $(PROG) $(LIB)
$(PROG): $(PROG_OBJS)
$(CC) $(PROG_CFLAGS) -o $@ $^ $(PROG_LDFLAGS)
$(LIB): $(LIB_OBJS)
$(CC) $(PROG_CFLAGS) $(LIB_LDFLAGS) -shared -o $@ $^
clean:
rm -f *.o $(PROG) $(LIB)

309
trx69/twamp/src/datamodel.c Normal file
View file

@ -0,0 +1,309 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <libbbf_api/dmbbf.h>
#include <libbbf_api/dmcommon.h>
#include <libbbf_api/dmuci.h>
#include <libbbf_api/dmubus.h>
#include <libbbf_api/dmjson.h>
#include "datamodel.h"
/* ********** RootDynamicObj ********** */
LIB_MAP_OBJ tRootDynamicObj[] = {
/* parentobj, nextobject */
{"Device.IP.Interface.", tDeviceTWAMPReflectorObj},
{0}
};
static char *get_last_instance_with_option(char *package, char *section, char *option, char *val, char *opt_inst)
{
struct uci_section *s;
char *inst = NULL;
uci_foreach_option_eq(package, section, option, val, s) {
inst = update_instance(inst, 4, s, opt_inst, package, section);
}
return inst;
}
static char *get_last_id(char *package, char *section)
{
struct uci_section *s;
char *id;
int cnt = 0;
uci_foreach_sections(package, section, s) {
cnt++;
}
dmasprintf(&id, "%d", cnt+1);
return id;
}
struct ip_args
{
struct uci_section *ip_sec;
char *ip_4address;
};
static int addObjIPInterfaceTWAMPReflector(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
struct uci_section *connection;
char *value1, *last_inst, *id;
last_inst = get_last_instance_with_option("twamp", "twamp_reflector", "interface", section_name(((struct ip_args *)data)->ip_sec), "twamp_inst");
id = get_last_id("twamp", "twamp_reflector");
dmuci_add_section("twamp", "twamp_reflector", &connection, &value1);
dmasprintf(instance, "%d", last_inst?atoi(last_inst)+1:1);
dmuci_set_value_by_section(connection, "twamp_inst", *instance);
dmuci_set_value_by_section(connection, "id", id);
dmuci_set_value_by_section(connection, "enable", "0");
dmuci_set_value_by_section(connection, "interface", section_name(((struct ip_args *)data)->ip_sec));
dmuci_set_value_by_section(connection, "port", "862");
dmuci_set_value_by_section(connection, "max_ttl", "1");
return 0;
}
static int delObjIPInterfaceTWAMPReflector(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
int found = 0;
struct uci_section *s, *ss = NULL;
char *interface;
struct uci_section *section = (struct uci_section *)data;
switch (del_action) {
case DEL_INST:
dmuci_delete_by_section(section, NULL, NULL);
return 0;
case DEL_ALL:
uci_foreach_sections("twamp", "twamp_reflector", s) {
dmuci_get_value_by_section_string(s, "interface", &interface);
if(strcmp(interface, section_name(((struct ip_args *)data)->ip_sec)) != 0)
continue;
if (found != 0) {
dmuci_delete_by_section(ss, NULL, NULL);
}
ss = s;
found++;
}
if (ss != NULL) {
dmuci_delete_by_section(ss, NULL, NULL);
}
return 0;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "enable", value);
return 0;
}
static int set_IPInterfaceTWAMPReflector_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
struct uci_section *s;
char *interface, *device, *id, *ipv4addr = "";
json_object *res, *jobj;
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
break;
case VALUESET:
string_to_bool(value, &b);
if(b) {
dmuci_get_value_by_section_string((struct uci_section *)data, "interface", &interface);
dmuci_get_value_by_section_string((struct uci_section *)data, "id", &id);
dmuci_set_value_by_section((struct uci_section *)data, "enable", "1");
dmuci_set_value("twamp", "twamp", "id", id);
uci_foreach_sections("network", "interface", s) {
if(strcmp(section_name(s), interface) != 0)
continue;
dmuci_get_value_by_section_string(s, "ipaddr", &ipv4addr);
break;
}
if (ipv4addr[0] == '\0') {
dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", interface, String}}, 1, &res);
if (res) {
jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
ipv4addr = dmjson_get_value(jobj, 1, "address");
if (ipv4addr[0] == '\0')
dmuci_set_value_by_section((struct uci_section *)data, "ip_version", "6");
else
dmuci_set_value_by_section((struct uci_section *)data, "ip_version", "4");
}
} else
dmuci_set_value_by_section((struct uci_section *)data, "ip_version", "4");
dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", interface, String}}, 1, &res);
if (res) {
device = dmjson_get_value(res, 1, "device");
dmuci_set_value_by_section((struct uci_section *)data, "device", device);
}
dmuci_set_value_by_section((struct uci_section *)data, "device", get_device(interface));
} else {
dmuci_set_value_by_section((struct uci_section *)data, "enable", "0");
}
break;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
char *enable;
dmuci_get_value_by_section_string((struct uci_section *)data, "enable", &enable);
if (strcmp(enable, "1") == 0)
*value = "Active";
else
*value = "Disabled";
return 0;
}
static int get_IPInterfaceTWAMPReflector_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "twamp_alias", value);
if ((*value)[0] == '\0')
dmasprintf(value, "cpe-%s", instance);
return 0;
}
static int set_IPInterfaceTWAMPReflector_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
return FAULT_9007;
break;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "twamp_alias", value);
break;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_Port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "port", value);
return 0;
}
static int set_IPInterfaceTWAMPReflector_Port(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,"65535"}}, 1))
return FAULT_9007;
break;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "port", value);
break;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_MaximumTTL(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "max_ttl", value);
return 0;
}
static int set_IPInterfaceTWAMPReflector_MaximumTTL(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{"1","255"}}, 1))
return FAULT_9007;
break;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "max_ttl", value);
break;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_IPAllowedList(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "ip_list", value);
return 0;
}
static int set_IPInterfaceTWAMPReflector_IPAllowedList(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string_list(value, -1, -1, 255, -1, -1, NULL, 0, NULL, 0))
return FAULT_9007;
break;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "ip_list", value);
break;
}
return 0;
}
static int get_IPInterfaceTWAMPReflector_PortAllowedList(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "port_list", value);
return 0;
}
static int set_IPInterfaceTWAMPReflector_PortAllowedList(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string_list(value, -1, -1, 255, -1, -1, NULL, 0, NULL, 0))
return FAULT_9007;
break;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "port_list", value);
break;
}
return 0;
}
static int browseIPInterfaceTWAMPReflectorInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
struct uci_section *s = NULL;
char *inst = NULL, *max_inst = NULL;
uci_foreach_option_eq("twamp", "twamp_reflector", "interface", section_name(((struct ip_args *)prev_data)->ip_sec), s) {
inst = handle_update_instance(2, dmctx, &max_inst, update_instance_alias, 5,
s, "twamp_inst", "twamp_alias", "twamp", "twamp_reflector");
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)s, inst) == DM_STOP)
break;
}
return 0;
}
/* *** Device.IP.Interface. *** */
DMOBJ tDeviceTWAMPReflectorObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, forced_inform, notification, nextdynamicobj, nextobj, leaf, linker, bbfdm_type*/
{"TWAMPReflector", &DMWRITE, addObjIPInterfaceTWAMPReflector, delObjIPInterfaceTWAMPReflector, NULL, browseIPInterfaceTWAMPReflectorInst, NULL, NULL, NULL, NULL, tIPInterfaceTWAMPReflectorParams, NULL, BBFDM_BOTH},
{0}
};
/* *** Device.IP.Interface.{i}.TWAMPReflector.{i}. *** */
DMLEAF tIPInterfaceTWAMPReflectorParams[] = {
/* PARAM, permission, type, getvalue, setvalue, forced_inform, notification, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterfaceTWAMPReflector_Enable, set_IPInterfaceTWAMPReflector_Enable, NULL, NULL, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_IPInterfaceTWAMPReflector_Status, NULL, NULL, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_Alias, set_IPInterfaceTWAMPReflector_Alias, NULL, NULL, BBFDM_BOTH},
{"Port", &DMWRITE, DMT_UNINT, get_IPInterfaceTWAMPReflector_Port, set_IPInterfaceTWAMPReflector_Port, NULL, NULL, BBFDM_BOTH},
{"MaximumTTL", &DMWRITE, DMT_UNINT, get_IPInterfaceTWAMPReflector_MaximumTTL, set_IPInterfaceTWAMPReflector_MaximumTTL, NULL, NULL, BBFDM_BOTH},
{"IPAllowedList", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_IPAllowedList, set_IPInterfaceTWAMPReflector_IPAllowedList, NULL, NULL, BBFDM_BOTH},
{"PortAllowedList", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_PortAllowedList, set_IPInterfaceTWAMPReflector_PortAllowedList, NULL, NULL, BBFDM_BOTH},
{0}
};

View file

@ -0,0 +1,17 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _TWAMP_H_
#define _TWAMP_H_
extern DMOBJ tDeviceTWAMPReflectorObj[];
extern DMLEAF tIPInterfaceTWAMPReflectorParams[];
#endif //_TWAMP_H_

858
trx69/twamp/src/twamp.c Normal file
View file

@ -0,0 +1,858 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* Name: Emma Mirică
* Project: TWAMP Protocol
* Class: OSS
* Email: emma.mirica@cti.pub.ro
* Contributions: stephanDB
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>
#include "twamp.h"
#include "twamplog.h"
#include "twampuci.h"
struct twamp_config cur_twamp_conf = {0};
TWAMPTimestamp ZeroT = { 0, 0 };
struct active_session {
int socket;
RequestSession req;
uint16_t server_oct;
uint32_t sid_addr;
TWAMPTimestamp sid_time;
uint32_t sid_rand;
uint32_t seq_nb;
uint32_t snd_nb;
uint32_t fw_msg;
uint32_t fw_lst_msg;
};
struct client_info {
ClientStatus status;
int socket;
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
int mode;
int sess_no;
struct timeval shutdown_time;
struct active_session sessions[MAX_SESSIONS_PER_CLIENT];
};
static int fd_max = 0;
static enum Mode authmode = kModeUnauthenticated;
static int used_sockets = 0;
static fd_set read_fds;
static int socket_family = AF_INET;
static int check_ipv4_address(char *ip, char *maskstr, char *address)
{
struct sockaddr_in sa = {0};
unsigned long netaddress, maxaddress;
unsigned long mask = ~((1 << (32 - atoi(maskstr))) - 1);
inet_pton(AF_INET, address, &(sa.sin_addr));
netaddress = (ntohl(sa.sin_addr.s_addr) & mask);
sa.sin_addr.s_addr = 0;
inet_pton(AF_INET, ip, &(sa.sin_addr));
maxaddress = (ntohl(sa.sin_addr.s_addr) & mask);
if (maxaddress == netaddress)
return 1;
return 0;
}
static char *check_ipv6_address_active(char *ip)
{
unsigned char buf[sizeof(struct in6_addr)];
char str[INET6_ADDRSTRLEN], *res;
int s;
s = inet_pton(AF_INET6, ip, buf);
if (s <= 0) {
if (s == 0)
twamp_log(SCRIT, "Not in presentation format");
else
twamp_log(SCRIT, "inet_pton");
return "";
}
if (inet_ntop(AF_INET6, buf, str, INET6_ADDRSTRLEN) == NULL) {
twamp_log(SCRIT, "inet_ntop");
return "";
}
res = strdup(str);
return res;
}
static int check_ipv6_address(char *ip, char *maskstr, char *address)
{
struct sockaddr_in6 sa = {0};
unsigned long netaddress, maxaddress;
unsigned long mask = ~((1 << (128 - atoi(maskstr))) - 1);
inet_pton(AF_INET6, address, &(sa.sin6_addr));
netaddress = (ntohl((uint32_t)sa.sin6_addr.s6_addr) & mask);
inet_pton(AF_INET6, ip, &(sa.sin6_addr));
maxaddress = (ntohl((uint32_t)sa.sin6_addr.s6_addr) & mask);
if (maxaddress == netaddress)
return 1;
return 0;
}
static int check_ip_address_allowed(char *address)
{
char *pch, *spch, *ip, *mask, *str, *addr;
for (pch = strtok_r(cur_twamp_conf.ip_list, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch))
{
if(strstr(pch, ".")) {
if(strstr(pch, "/")) {
ip = strtok_r(pch, "/", &str);
mask = strtok_r(NULL, "", &str);
if(check_ipv4_address(ip, mask, address))
return 1;
continue;
}
if (strcmp(pch, address) == 0)
return 1;
} else {
addr = check_ipv6_address_active(address);
if(strstr(pch, "/")) {
ip = strtok_r(pch, "/", &str);
mask = strtok_r(NULL, "", &str);
ip = check_ipv6_address_active(ip);
if(check_ipv6_address(ip, mask, addr))
return 1;
continue;
}
pch = check_ipv6_address_active(pch);
if (strcmp(pch, addr) == 0)
return 1;
}
}
return 0;
}
static int check_port_allowed(int port)
{
char *pch, *spch, *min, *max, *str;
for (pch = strtok_r(cur_twamp_conf.port_list, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch))
{
if(strstr(pch, "-")) {
min = strtok_r(pch, "-", &str);
max = strtok_r(NULL, "", &str);
if(port >= atoi(min) && port <= atoi(max))
return 1;
continue;
}
if (port == atoi(pch))
return 1;
}
return 0;
}
/* The cleanup_client function will close every connection (TWAMP-Control ot TWAMP-Test that this server has with the client defined by the client_infor
* structure received as a parameter.
*/
static void cleanup_client(struct client_info *client)
{
char str_client[INET6_ADDRSTRLEN];
inet_ntop(socket_family, (socket_family == AF_INET6) ? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
twamp_log(SINFO, "Cleanup client %s", str_client);
FD_CLR(client->socket, &read_fds);
close(client->socket);
used_sockets--;
int i;
for (i = 0; i < client->sess_no; i++)
/* If socket is -1 the session has already been closed */
if (client->sessions[i].socket > 0) {
FD_CLR(client->sessions[i].socket, &read_fds);
close(client->sessions[i].socket);
client->sessions[i].socket = -1;
used_sockets--;
}
memset(client, 0, sizeof(struct client_info));
client->status = kOffline;
}
/* The TWAMP server can only accept max_clients and it will recycle the positions for the available clients. */
static int find_empty_client(struct client_info *clients, int max_clients)
{
int i;
for (i = 0; i < max_clients; i++)
if (clients[i].status == kOffline)
return i;
return -1;
}
/* Sends a ServerGreeting message to the Control-Client after the TCP connection has been established. */
static int send_greeting(uint16_t mode_mask, struct client_info *client)
{
int socket = client->socket;
char str_client[INET6_ADDRSTRLEN]; /* String for Client IP address */
inet_ntop(socket_family, (socket_family == AF_INET6) ? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
int i;
ServerGreeting greet;
memset(&greet, 0, sizeof(greet));
greet.Modes = htonl(client->mode & mode_mask);
for (i = 0; i < 16; i++)
greet.Challenge[i] = rand() % 16;
for (i = 0; i < 16; i++)
greet.Salt[i] = rand() % 16;
greet.Count = htonl(1 << 10);
int rv = send(socket, &greet, sizeof(greet), 0);
if (rv < 0) {
twamp_log(SCRIT,"Failed to send ServerGreeting message");
cleanup_client(client);
} else if ((authmode & 0x000F) == 0) {
twamp_log(SCRIT,"Sent ServerGreeting message with Mode 0! Abort");
cleanup_client(client);
} else {
twamp_log(SINFO,"Sent ServerGreeting message to %s", str_client);
}
return rv;
}
/* After a ServerGreeting the Control-Client should respond with a SetUpResponse. This function treats this message */
static int receive_greet_response(struct client_info *client)
{
int socket = client->socket;
char str_client[INET6_ADDRSTRLEN]; /* String for Client IP address */
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
SetUpResponse resp;
memset(&resp, 0, sizeof(resp));
int rv = recv(socket, &resp, sizeof(resp), 0);
if (rv <= 32) {
twamp_log(SCRIT,"Failed to receive SetUpResponse");
cleanup_client(client);
} else {
twamp_log(SINFO, "Received SetUpResponse message from %s with mode %d", str_client, ntohl(resp.Mode));
if ((ntohl(resp.Mode) & client->mode & 0x000F) == 0) {
twamp_log(SCRIT,"The client does not support any usable Mode");
rv = 0;
}
client->mode = ntohl(resp.Mode);
}
return rv;
}
/* Sent a ServerStart message to the Control-Client to endthe TWAMP-Control session establishment phase */
static int send_start_serv(struct client_info *client, TWAMPTimestamp StartTime)
{
int socket = client->socket;
char str_client[INET6_ADDRSTRLEN];
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
ServerStart msg;
memset(&msg, 0, sizeof(msg));
if ((StartTime.integer == 0) && (StartTime.fractional == 0)) {
msg.Accept = kAspectNotSupported;
} else {
msg.Accept = kOK;
}
msg.StartTime = StartTime;
int rv = send(socket, &msg, sizeof(msg), 0);
if (rv <= 0) {
twamp_log(SCRIT,"Failed to send ServerStart message");
cleanup_client(client);
} else {
client->status = kConfigured;
twamp_log(SINFO, "ServerStart message sent to %s", str_client);
if (msg.Accept == kAspectNotSupported) {
cleanup_client(client);
}
}
return rv;
}
/* Sends a StartACK for the StartSessions message */
static int send_start_ack(struct client_info *client)
{
char str_client[INET6_ADDRSTRLEN];
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
StartACK ack;
memset(&ack, 0, sizeof(ack));
ack.Accept = kOK;
int rv = send(client->socket, &ack, sizeof(ack), 0);
if (rv <= 0) {
twamp_log(SCRIT,"Failed to send StartACK message");
} else
twamp_log(SINFO,"StartACK message sent to %s", str_client);
return rv;
}
/* This function treats the case when a StartSessions is received from the Control-Client to start a number of TWAMP-Test sessions */
static int receive_start_sessions(struct client_info *client)
{
int i;
int rv = send_start_ack(client);
if (rv <= 0)
return rv;
/* Now it can receive packets on the TWAMP-Test sockets */
for (i = 0; i < client->sess_no; i++) {
FD_SET(client->sessions[i].socket, &read_fds);
if (fd_max < client->sessions[i].socket)
fd_max = client->sessions[i].socket;
}
client->status = kTesting;
return rv;
}
/* This functions treats the case when a StopSessions is received from the Control-Client to end all the Test sessions. */
static int receive_stop_sessions(struct client_info *client)
{
/* If a StopSessions message was received, it can still receive Test packets until the timeout has expired */
gettimeofday(&client->shutdown_time, NULL);
return 0;
}
/* Computes the response to a RequestTWSession message */
static int send_accept_session(struct client_info *client, RequestSession * req)
{
char str_client[INET6_ADDRSTRLEN]; /* String for Client IP address */
AcceptSession acc;
memset(&acc, 0, sizeof(acc));
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
/* Check if there are any slots available */
if ((used_sockets < 64) && (client->sess_no < MAX_SESSIONS_PER_CLIENT)) {
int testfd = socket(socket_family, SOCK_DGRAM, 0);
if (testfd < 0) {
twamp_log(SCRIT,"Error opening socket");
return -1;
}
int check_time = CHECK_TIMES;
if(socket_family == AF_INET6) {
struct sockaddr_in6 local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin6_family = AF_INET6;
local_addr.sin6_addr = in6addr_any;
local_addr.sin6_port = req->ReceiverPort;
while (check_time-- && bind(testfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
local_addr.sin6_port = htons(20000 + rand() % 1000);
if (check_time > 0) {
req->ReceiverPort = local_addr.sin6_port;
}
} else {
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = req->ReceiverPort;
while (check_time-- && bind(testfd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)) < 0)
local_addr.sin_port = htons(20000 + rand() % 1000);
if (check_time > 0) {
req->ReceiverPort = local_addr.sin_port;
}
}
if (check_time > 0) {
acc.Accept = kOK;
acc.Port = req->ReceiverPort;
client->sessions[client->sess_no].socket = testfd;
client->sessions[client->sess_no].req = *req;
/* SID construction */
memcpy(acc.SID, &req->ReceiverAddress, 4);
TWAMPTimestamp sidtime = get_timestamp();
memcpy(&acc.SID[4], &sidtime, 8);
int k;
for (k = 0; k < 4; k++)
acc.SID[12 + k] = rand() % 256;
memcpy(&client->sessions[client->sess_no].sid_addr, &acc.SID, 4);
client->sessions[client->sess_no].sid_time = sidtime;
memcpy(&client->sessions[client->sess_no].sid_rand, &acc.SID[12], 4);
twamp_log(SINFO, "SID: 0x%04X.%04X.%04X.%04X",
ntohl(client->sessions[client->sess_no].sid_addr),
ntohl(client->sessions[client->sess_no].sid_time.integer),
ntohl(client->sessions[client->sess_no].sid_time.fractional),
ntohl(client->sessions[client->sess_no].sid_rand));
/* Set socket options */
set_socket_option(testfd, cur_twamp_conf.max_ttl);
set_socket_tos(testfd, (client->sessions[client->sess_no].req.TypePDescriptor << 2));
client->sess_no++;
} else {
twamp_log(SINFO, "kTemporaryResourceLimitation: check_time [%d]", check_time);
acc.Accept = kTemporaryResourceLimitation;
acc.Port = 0;
}
} else {
twamp_log(SINFO, "kTemporaryResourceLimitation: used_sockets [%d], sess_no [%d]", used_sockets, client->sess_no);
acc.Accept = kTemporaryResourceLimitation;
acc.Port = 0;
}
int rv = send(client->socket, &acc, sizeof(acc), 0);
return rv;
}
/* This function treats the case when a RequestTWSession is received */
static int receive_request_session(struct client_info *client, RequestSession * req)
{
char str_client[INET6_ADDRSTRLEN]; /* String for Client IP address */
if(socket_family == AF_INET6) {
inet_ntop(AF_INET6, &(client->addr6.sin6_addr), str_client, sizeof(str_client));
twamp_log(SINFO, "Server received RequestTWSession message");
} else {
char str_server[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(client->addr.sin_addr), str_client, INET_ADDRSTRLEN);
struct in_addr se_addr;
se_addr.s_addr = req->ReceiverAddress;
inet_ntop(AF_INET, &(se_addr), str_server, INET_ADDRSTRLEN);
twamp_log(SINFO, "Server %s received RequestTWSession message with port %d", str_server, ntohs(req->ReceiverPort));
}
/* Check port test packets if its allowed by PortAllowedList parameter */
if(cur_twamp_conf.port_list[0] != '\0') {
if(!check_port_allowed(ntohs(req->ReceiverPort))) {
twamp_log(SINFO, "Port %d is not allowed", ntohs(req->ReceiverPort));
return -1;
}
}
int rv = send_accept_session(client, req);
if (rv <= 0) {
twamp_log(SCRIT,"Failed to send the Accept-Session message");
}
return rv;
}
/* This function will receive a TWAMP-Test packet and will send a response. In TWAMP the Session-Sender (in our case the Control-Client, meaning the
* TWAMP-Client) is always sending TWAMP-Test packets and the Session-Reflector (Server) is receiving TWAMP-Test packets.
*/
static int receive_test_message(struct client_info *client, int session_index)
{
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
socklen_t len = sizeof(addr);
char str_client[INET6_ADDRSTRLEN];
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(client->addr6.sin6_addr) : (void*) &(client->addr.sin_addr), str_client, sizeof(str_client));
ReflectorUPacket pack_reflect;
memset(&pack_reflect, 0, sizeof(pack_reflect));
SenderUPacket pack;
memset(&pack, 0, sizeof(pack));
/* New for recvmsg */
struct msghdr *message = malloc(sizeof(struct msghdr));
struct cmsghdr *c_msg;
char *control_buffer = malloc(TST_PKT_SIZE);
uint16_t control_length = TST_PKT_SIZE;
memset(message, 0, sizeof(*message));
message->msg_name = (socket_family == AF_INET6)? (void*)&addr6: (void*)&addr;
message->msg_namelen = len;
message->msg_iov = malloc(sizeof(struct iovec));
message->msg_iov->iov_base = &pack;
message->msg_iov->iov_len = TST_PKT_SIZE;
message->msg_iovlen = 1;
message->msg_control = control_buffer;
message->msg_controllen = control_length;
int rv = recvmsg(client->sessions[session_index].socket, message, 0);
pack_reflect.receive_time = get_timestamp();
char str_server[INET6_ADDRSTRLEN]; /* String for Client IP address */
inet_ntop(socket_family, (socket_family == AF_INET6)? (void*) &(addr6.sin6_addr) : (void*) &(addr.sin_addr), str_server, sizeof(str_server));
if (rv <= 0) {
twamp_log(SCRIT,"Failed to receive TWAMP-Test packet");
return rv;
} else if (rv < 14) {
twamp_log(SCRIT,"Short TWAMP-Test packet");
return rv;
}
/* Get TTL/TOS values from IP header */
uint8_t fw_ttl = 0;
uint8_t fw_tos = 0;
for (c_msg = CMSG_FIRSTHDR(message); c_msg; c_msg = (CMSG_NXTHDR(message, c_msg))) {
if ((c_msg->cmsg_level == IPPROTO_IP && c_msg->cmsg_type == IP_TTL) || (c_msg->cmsg_level == IPPROTO_IPV6 && c_msg->cmsg_type == IPV6_HOPLIMIT)) {
fw_ttl = *(int *)CMSG_DATA(c_msg);
} else if (c_msg->cmsg_level == IPPROTO_IP && c_msg->cmsg_type == IP_TOS) {
fw_tos = *(int *)CMSG_DATA(c_msg);
} else {
twamp_log(SINFO, "Warning, unexpected data of level %i and type %i", c_msg->cmsg_level, c_msg->cmsg_type);
}
}
twamp_log(SINFO, "Received TWAMP-Test message from %s", inet_ntoa(addr.sin_addr));
pack_reflect.seq_number = htonl(client->sessions[session_index].seq_nb++);
pack_reflect.error_estimate = htons(0x8001); // Sync = 1, Multiplier = 1
pack_reflect.sender_seq_number = pack.seq_number;
pack_reflect.sender_time = pack.time;
pack_reflect.sender_error_estimate = pack.error_estimate;
pack_reflect.sender_ttl = fw_ttl; // Copy from the IP header packet from Sender
if ((client->mode & kModeDSCPECN) == kModeDSCPECN) {
pack_reflect.sender_tos = fw_tos; // Copy from the IP header packet from Sender
}
if(socket_family == AF_INET6) {
addr.sin_port = client->sessions[session_index].req.SenderPort;
} else {
addr6.sin6_port = client->sessions[session_index].req.SenderPort;
}
/* FW Loss Calculation */
if (client->sessions[session_index].fw_msg == 0) {
client->sessions[session_index].fw_msg = 1;
/* Response packet for TOS with ECN */
if ((fw_tos & 0x03) > 0) {
uint8_t ecn_tos = (fw_tos & 0x03) - (((fw_tos & 0x2) >> 1) & (fw_tos & 0x1));
set_socket_tos(client->sessions[session_index].socket, (client->sessions[session_index].req.TypePDescriptor << 2) + ecn_tos);
}
} else {
client->sessions[session_index].fw_msg = client->sessions[session_index].fw_msg + ntohl(pack.seq_number) - client->sessions[session_index].snd_nb;
client->sessions[session_index].fw_lst_msg = client->sessions[session_index].fw_lst_msg + ntohl(pack.seq_number) - client->sessions[session_index].snd_nb - 1;
}
client->sessions[session_index].snd_nb = ntohl(pack.seq_number);
/* Response packet */
pack_reflect.time = get_timestamp();
if(socket_family == AF_INET6) {
if (rv < 41) {
rv = sendto(client->sessions[session_index].socket, &pack_reflect, 41, 0, (struct sockaddr *)&addr6, sizeof(addr6));
} else {
rv = sendto(client->sessions[session_index].socket, &pack_reflect, rv, 0, (struct sockaddr *)&addr6, sizeof(addr6));
}
} else {
if (rv < 41) {
rv = sendto(client->sessions[session_index].socket, &pack_reflect, 41, 0, (struct sockaddr *)&addr, sizeof(addr));
} else {
rv = sendto(client->sessions[session_index].socket, &pack_reflect, rv, 0, (struct sockaddr *)&addr, sizeof(addr));
}
}
if (rv <= 0) {
twamp_log(SCRIT,"Failed to send TWAMP-Test packet");
}
/* Print the FW metrics */
print_metrics_server(str_client, socket_family == AF_INET6 ? ntohs(addr6.sin6_port): ntohs(addr.sin_port), ntohs(client->sessions[session_index].req.ReceiverPort), (client->sessions[session_index].req.TypePDescriptor << 2), fw_tos, &pack_reflect);
if ((client->sessions[session_index].fw_msg % 10) == 0) {
twamp_log(SINFO,"FW Lost packets: %u/%u", client->sessions[session_index].fw_lst_msg, client->sessions[session_index].fw_msg);
twamp_log(SINFO,"FW Loss Ratio: %3.2f%%", (float)100 * client->sessions[session_index].fw_lst_msg / client->sessions[session_index].fw_msg);
//printf("FW Lost packets: %u/%u, FW Loss Ratio: %3.2f%%\n", client->sessions[session_index].fw_lst_msg, client->sessions[session_index].fw_msg, (float)100 * client->sessions[session_index].fw_lst_msg / client->sessions[session_index].fw_msg);
}
return rv;
}
int twamp_connect(void)
{
int listenfd, newsockfd;
struct client_info clients[MAX_CLIENTS];
struct sockaddr_in client_addr;
struct sockaddr_in6 client_addr6;
int rv;
/* Obtain start server time in TWAMP format */
TWAMPTimestamp StartTime = get_timestamp();
if(cur_twamp_conf.ip_version == 4)
socket_family = AF_INET;
else
socket_family = AF_INET6;
listenfd = socket(socket_family, SOCK_STREAM, 0);
if (listenfd < 0) {
twamp_log(SCRIT,"Error opening socket");
return -1;
}
int ret = setsockopt(listenfd, SOL_SOCKET, SO_BINDTODEVICE, cur_twamp_conf.device, strlen(cur_twamp_conf.device)+1);
if(ret) {
twamp_log(SCRIT,"Error on setsockopt with ret %d", ret);
return -1;
}
if(socket_family == AF_INET6) {
/* Set Server address and bind on the TWAMP port */
struct sockaddr_in6 serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_addr = in6addr_any;
serv_addr.sin6_port = htons(cur_twamp_conf.port);
if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
twamp_log(SCRIT,"Error on binding");
return -1;
}
} else {
/* Set Server address and bind on the TWAMP port */
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(cur_twamp_conf.port);
if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0) {
twamp_log(SCRIT,"Error on binding");
return -1;
}
}
used_sockets++;
/* Start listening on the TWAMP port for new TWAMP-Control connections */
if (listen(listenfd, MAX_CLIENTS)) {
twamp_log(SCRIT,"Error on listen");
return -1;
}
FD_ZERO(&read_fds);
FD_SET(listenfd, &read_fds);
fd_max = listenfd;
memset(clients, 0, MAX_CLIENTS * sizeof(struct client_info));
fd_set tmp_fds;
FD_ZERO(&tmp_fds);
while (1) {
tmp_fds = read_fds;
if (select(fd_max + 1, &tmp_fds, NULL, NULL, NULL) < 0) {
twamp_log(SCRIT,"Error in select");
close(listenfd);
return -1;
}
/* If an event happened on the listenfd, then a new TWAMP-Control connection is received */
if (FD_ISSET(listenfd, &tmp_fds)) {
uint32_t client_len = (socket_family == AF_INET6)? sizeof(client_addr6) : sizeof(client_addr);
if ((newsockfd = accept(listenfd, (socket_family == AF_INET6) ? (struct sockaddr *)&client_addr6 : (struct sockaddr *)&client_addr, &client_len)) < 0) {
twamp_log(SCRIT,"Error in accept");
} else {
/* Add a new client if there are any slots available */
int pos = find_empty_client(clients, MAX_CLIENTS);
uint16_t mode_mask = 0;
if (pos != -1) {
clients[pos].status = kConnected;
clients[pos].socket = newsockfd;
clients[pos].addr = client_addr;
clients[pos].addr6 = client_addr6;
clients[pos].mode = authmode;
clients[pos].sess_no = 0;
used_sockets++;
FD_SET(newsockfd, &read_fds);
if (newsockfd > fd_max)
fd_max = newsockfd;
mode_mask = 0x01FF;
}
char str_client[INET6_ADDRSTRLEN]; /* String for Client IP address */
inet_ntop(socket_family, (socket_family == AF_INET6) ? (void*) &(clients[pos].addr6.sin6_addr) : (void*) &(clients[pos].addr.sin_addr), str_client, sizeof(str_client));
twamp_log(SINFO,"Receive a TCP connection from %s", str_client);
/* Check ip test packets if its allowed by IPAllowedList parameter */
if(cur_twamp_conf.ip_list[0] != '\0') {
if(!check_ip_address_allowed(str_client)) {
twamp_log(SINFO, "IP Address %d is not allowed", str_client);
close(listenfd);
return -1;
}
}
rv = send_greeting(mode_mask, &clients[pos]);
}
}
/* Receives other packets from the established TWAMP-Control sessions */
uint8_t buffer[4096];
int i, j;
for (i = 0; i < MAX_CLIENTS; i++)
/* It can only receive TWAMP-Control messages from Online clients */
if (clients[i].status != kOffline)
if (FD_ISSET(clients[i].socket, &tmp_fds)) {
switch (clients[i].status) {
case kConnected:
/* If a TCP session has been established and a ServerGreeting has been sent, wait for the SetUpResponse and finish the TWAMP-Control setup */
rv = receive_greet_response(&clients[i]);
if (rv > 32) {
rv = send_start_serv(&clients[i], StartTime);
} else {
rv = send_start_serv(&clients[i], ZeroT);
}
break;
case kConfigured:
/* Reset the buffer to receive a new message */
memset(buffer, 0, 4096);
rv = recv(clients[i].socket, buffer, 4096, 0);
if (rv <= 0) {
cleanup_client(&clients[i]);
break;
}
/* Check the message received: It can only be StartSessions or RequestTWSession */
switch (buffer[0]) {
case kStartSessions:
rv = receive_start_sessions(&clients[i]);
break;
case kRequestTWSession:
rv = receive_request_session(&clients[i], (RequestSession *) buffer);
break;
default:
break;
}
if (rv <= 0)
cleanup_client(&clients[i]);
break;
case kTesting:
/* In this state can only receive a StopSessions msg */
memset(buffer, 0, 4096);
rv = recv(clients[i].socket, buffer, 4096, 0);
if (rv <= 0) {
cleanup_client(&clients[i]);
break;
}
if (buffer[0] == kStopSessions) {
rv = receive_stop_sessions(&clients[i]);
}
break;
default:
break;
}
}
/* Check for TWAMP-Test packets */
for (i = 0; i < MAX_CLIENTS; i++) {
struct timeval current;
gettimeofday(&current, NULL);
if (clients[i].status == kTesting) {
uint8_t has_active_test_sessions = 0;
for (j = 0; j < clients[i].sess_no; j++) {
rv = get_actual_shutdown(&current, &clients[i].shutdown_time, &clients[i].sessions[j].req.Timeout);
if (rv > 0) {
has_active_test_sessions = 1;
if (FD_ISSET(clients[i].sessions[j].socket, &tmp_fds)) {
rv = receive_test_message(&clients[i], j);
}
} else {
FD_CLR(clients[i].sessions[j].socket, &read_fds);
close(clients[i].sessions[j].socket);
used_sockets--;
clients[i].sessions[j].socket = -1;
/* print loss result */
twamp_log(SINFO, "Session: %u, FW Lost packets: %u/%u, FW Loss Ratio: %3.2f%%", j, clients[i].sessions[j].fw_lst_msg, clients[i].sessions[j].fw_msg, (float)100 * clients[i].sessions[j].fw_lst_msg / clients[i].sessions[j].fw_msg);
}
}
if (!has_active_test_sessions) {
memset(&clients[i].shutdown_time, 0, sizeof(clients[i].shutdown_time));
clients[i].sess_no = 0;
clients[i].status = kConfigured;
}
}
}
}
return 0;
}
char *get_twamp_reflector_option(char *instance, char *option)
{
struct uci_section *s;
char *v, *twamp_id;
dmuci_foreach_section("twamp", "twamp_reflector", s) {
twamp_id = dmuci_get_value_bysection(s, "id");
if(strcmp(twamp_id, instance) == 0)
{
v = dmuci_get_value_bysection(s, option);
return v;
}
}
v = "";
return v;
}
int twamp_init(void)
{
char *id, *value = NULL;
int a;
value = dmuci_get_value("twamp", "twamp", "log_level");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_twamp_conf.loglevel = a;
}
else
cur_twamp_conf.loglevel = DEFAULT_LOGLEVEL;
twamp_log(SDEBUG,"TWAMP Reflector Log Level:%d", cur_twamp_conf.loglevel);
id = dmuci_get_value("twamp", "twamp", "id");
cur_twamp_conf.enable = atoi(get_twamp_reflector_option(id, "enable"));
cur_twamp_conf.interface = strdup(get_twamp_reflector_option(id, "interface"));
cur_twamp_conf.device = strdup(get_twamp_reflector_option(id, "device"));
cur_twamp_conf.ip_version = atoi(get_twamp_reflector_option(id, "ip_version"));
cur_twamp_conf.port = atoi(get_twamp_reflector_option(id, "port"));
cur_twamp_conf.max_ttl = atoi(get_twamp_reflector_option(id, "max_ttl"));
cur_twamp_conf.ip_list = strdup(get_twamp_reflector_option(id, "ip_list"));
cur_twamp_conf.port_list = strdup(get_twamp_reflector_option(id, "port_list"));
twamp_log(SDEBUG,"TWAMP Reflector Enable: %d", cur_twamp_conf.enable);
twamp_log(SDEBUG,"TWAMP Reflector Interface: %s", cur_twamp_conf.interface);
twamp_log(SDEBUG,"TWAMP Reflector Device: %s", cur_twamp_conf.device);
twamp_log(SDEBUG,"TWAMP Reflector Port: %d", cur_twamp_conf.port);
twamp_log(SDEBUG,"TWAMP Reflector MaximumTTL: %d", cur_twamp_conf.max_ttl);
twamp_log(SDEBUG,"TWAMP Reflector IPAllowedList: %s", cur_twamp_conf.ip_list);
twamp_log(SDEBUG,"TWAMP Reflector PortAllowedList: %s", cur_twamp_conf.port_list);
return 0;
}
void twamp_exit(void)
{
free(cur_twamp_conf.interface);
free(cur_twamp_conf.device);
free(cur_twamp_conf.ip_list);
free(cur_twamp_conf.port_list);
}
int main(int argc, char *argv[])
{
dmuci_init();
twamp_init();
dmuci_fini();
twamp_log(SINFO,"START TWAMP Reflector");
twamp_connect();
twamp_exit();
twamp_log(SINFO,"EXIT TWAMP Reflector");
return 0;
}

237
trx69/twamp/src/twamp.h Normal file
View file

@ -0,0 +1,237 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* Name: Emma Mirică
* Project: TWAMP Protocol
* Class: OSS
* Email: emma.mirica@cti.pub.ro
* Contributions: stephanDB
*
*/
/*
* The following logical-model will be implemented:
*
* +----------------+ +-------------------+
* | Control-Client |<--TWAMP-Control-->| Server |
* | | | |
* | Session-Sender |<--TWAMP-Test----->| Session-Reflector |
* +----------------+ +-------------------+
*
*/
#ifndef _TWAMP_H__
#define _TWAMP_H__
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
#include <sys/time.h>
#define CHECK_TIMES 100
#define LOSTTIME 2 /* SECONDS - Timeout for TWAMP test packet */
#define MAX_CLIENTS 10
#define MAX_SESSIONS_PER_CLIENT 10
enum CommandNumber {
kReserved,
kForbidden,
kStartSessions,
kStopSessions,
kReserved4,
kRequestTWSession,
kExperimentation
};
enum Mode {
kModeReserved = 0,
kModeUnauthenticated = 1,
kModeAuthenticated = 2,
kModeEncrypted = 4,
kModeHybrid = 8, /* Unauthenticated test, encrypted control */
kModeIndividual = 16,
kModeReflectOctets = 32,
KModeSymmetrical = 64,
KModeIKEv2Derived = 128,
kModeDSCPECN = 256
};
enum AcceptCode {
kOK,
kFailure,
kInternalError,
kAspectNotSupported,
kPermanentResourceLimitation,
kTemporaryResourceLimitation
};
typedef enum {
kOffline = 0,
kConnected,
kConfigured,
kTesting
} ClientStatus;
typedef struct twamp_timestamp {
uint32_t integer;
uint32_t fractional;
} TWAMPTimestamp;
/*****************************************/
/* */
/* TWAMP-Control specific messages */
/* */
/*****************************************/
/* First messsage sent by the Server to the Control-Client to establish a connection */
typedef struct server_greeting {
uint8_t Unused[12];
/* Modes = bit-wise OR between Mode values
* First 23 bits MUST be zero in TWAMP (29 in first version)*/
uint32_t Modes;
uint8_t Challenge[16]; /* Random sequence of bytes generated by the server */
uint8_t Salt[16];
uint32_t Count; /* MUST be a power of 2. Minimum 1024 */
uint8_t MBZ[12];
} ServerGreeting;
/* The Control-Client's response to the Server's greeting */
typedef struct control_client_greeting_response {
uint32_t Mode; /* if 0 -> the Server does not wish to communicate */
uint8_t KeyID[80];
uint8_t Token[64];
uint8_t ClientIV[16];
} SetUpResponse;
/* The Server sends a start message to conclude the TWAMP-Control session */
typedef struct server_start {
uint8_t MBZ1[15];
uint8_t Accept; /* 0 means Continue. See 3.3 of RFC 4656 */
uint8_t ServerIV[16];
TWAMPTimestamp StartTime; /* TWAMPTimestamp; 0 if Accept is NonZero. */
uint8_t MBZ2[8];
} ServerStart;
/* The Control-Client sends a RequestSession packet for each TWAMP-Test session */
typedef struct request_session {
uint8_t Type; /* 5 / CommandNumber */
uint8_t IPVN; /* MBZ | IPVN */
uint8_t ConfSender; /* 0 */
uint8_t ConfReceiver; /* 0 */
uint32_t SlotsNo; /* 0 */
uint32_t PacketsNo; /* 0 */
uint16_t SenderPort;
uint16_t ReceiverPort;
uint32_t SenderAddress;
uint8_t MBZ1[12]; /* Sender Address Cont */
uint32_t ReceiverAddress;
uint8_t MBZ2[12]; /* Receiver Address Cont */
uint8_t SID[16]; /* 0 */
uint32_t PaddingLength;
TWAMPTimestamp StartTime;
TWAMPTimestamp Timeout;
uint32_t TypePDescriptor;
uint16_t OctetsToBeReflected;
uint16_t PadLenghtToReflect;
uint8_t MBZ3[4];
uint8_t HMAC[16];
} RequestSession;
/* The Server's response to the RequestSession packet */
typedef struct accept_session_packet {
uint8_t Accept; /* 3 if not supported */
uint8_t MBZ1;
uint16_t Port;
uint8_t SID[16]; /* Generated by server */
//uint16_t ReflectedOctets;
//uint16_t ServerOctets;
uint8_t MBZ2[8];
uint8_t HMAC[16];
} AcceptSession;
/* The Control-Client sends a StartSessions message to start all accepted TWAMP-Test sessions */
typedef struct start_message1 {
uint8_t Type; /* 2 */
uint8_t MBZ[15];
uint8_t HMAC[16];
} StartSessions;
/* When it receives a StartSessions, the Server responds with a StartACK */
typedef struct start_ack {
uint8_t Accept;
uint8_t MBZ[15];
uint8_t HMAC[16];
} StartACK;
/* The Control-Client sends a StopSessions message to stop all active TWAMP-Test sessions */
typedef struct twamp_stop {
uint8_t Type; /* 3 */
uint8_t Accept;
uint8_t MBZ1[2];
uint32_t SessionsNo;
uint8_t MBZ2[8];
uint8_t HMAC[16];
} StopSessions;
/*****************************************/
/* */
/* TWAMP-Test specific messages */
/* */
/*****************************************/
#define TST_PKT_SIZE 1472 //1472 (MTU 1514)
/* Session-Sender TWAMP-Test packet for Unauthenticated mode */
typedef struct test_packet {
uint32_t seq_number;
TWAMPTimestamp time;
uint16_t error_estimate;
uint8_t padding[TST_PKT_SIZE - 14];
} SenderUPacket;
/* Session-Reflector TWAMP-Test packet for Unauthenticated mode */
typedef struct reflector_unauth_packet {
uint32_t seq_number;
TWAMPTimestamp time;
uint16_t error_estimate;
uint8_t mbz1[2];
TWAMPTimestamp receive_time;
uint32_t sender_seq_number;
TWAMPTimestamp sender_time;
uint16_t sender_error_estimate;
uint8_t mbz2[2];
uint8_t sender_ttl;
uint8_t sender_tos;
uint8_t padding[TST_PKT_SIZE - 42];
} ReflectorUPacket;
struct twamp_config
{
bool enable;
char *interface;
char *device;
int ip_version;
int port;
int max_ttl;
char *ip_list;
char *port_list;
int loglevel;
};
extern struct twamp_config cur_twamp_conf;
void timeval_to_timestamp(const struct timeval *tv, TWAMPTimestamp * ts);
void timestamp_to_timeval(const TWAMPTimestamp * ts, struct timeval *tv);
uint64_t get_usec(const TWAMPTimestamp * ts);
TWAMPTimestamp get_timestamp();
int get_actual_shutdown(const struct timeval *tv, const struct timeval *ts, const TWAMPTimestamp * t);
void print_metrics_server(char *addr_cl, uint16_t snd_port, uint16_t rcv_port, uint8_t snd_tos, uint8_t fw_tos, const ReflectorUPacket * pack);
void set_socket_option(int socket, uint8_t ip_ttl);
void set_socket_tos(int socket, uint8_t ip_tos);
#endif /* _TWAMP_H__ */

57
trx69/twamp/src/twamp.md Normal file
View file

@ -0,0 +1,57 @@
# README #
twampd is an implementation of the Two-Way Active Measurement Protocol (TWAMP) reflector.
## Configuration File ##
The twampd UCI configuration is located in **'/etc/config/twamp'**, and contains 2 sections the **twamp** and the **twamp\_reflector**.
```
config twamp 'twamp'
option id '1'
option log_level '3'
config twamp_reflector
option id '1'
option enable ''
option interface ''
option device ''
option ip_version ''
option port ''
option max_ttl ''
option ip_list ''
option port_list ''
```
### twamp section ###
It defines **the twamp configuration**: id, log_level. The possible options for **twamp** section are:
| Name | Type | Description |
| --------- | ------- | ------------------------------------------- |
| `id` | integer | Specifies the id of TWAMP reflector to use. |
| `log_level` | integer | Specifies the log type to use, by default **'INFO'**. The possible types are **'EMERG', 'ALERT', 'CRITIC' ,'ERROR', 'WARNING', 'NOTICE', 'INFO' and 'DEBUG'**. |
### twamp_reflector section ###
It describes **the twamp reflector configuration**: id, ip\_version, etc... The possible options for **twamp_reflector** section are:
| Name | Type | Description |
| ---------- | ------- | ------------------------------ |
| `id` | integer | Specifies the TWAMP id to use. |
| `enable` | boolean | If set to **1**, it enables the TWAMP reflector. |
| `ip_version` | integer | Specifies the IP version to use, **4** by default. The possible versions are **4** and **6**. |
| `port` | integer | Specifies the port to listen on. |
| `max_ttl` | integer | Specifies the maximum TTL of a received packet, that the TWAMP reflector will reflect to the TWAMP controller. |
| `ip_list` | string | Specifies the allowed source IP addresses and subnets to handle test packets. |
| `port_list` | string | Specifies the range of source ports allowed to use for twamp\_reflector tests. |
## Dependencies ##
To successfully build twampd, the following libraries are needed:
| Dependency | Link | License |
| ----------- | ------------------------------------------- | -------------- |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |

View file

@ -0,0 +1,55 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <stdio.h>
#include <time.h>
#include <syslog.h>
#include <stdarg.h>
#include "twamplog.h"
#include "twamp.h"
#define DEBUG
static const int log_syslogmap[] = {
[SCRIT] = LOG_CRIT,
[SWARNING] = LOG_WARNING,
[SNOTICE] = LOG_NOTICE,
[SINFO] = LOG_INFO,
[SDEBUG] = LOG_DEBUG
};
static const char* log_str[] = {
[SCRIT] = "CRITICAL",
[SWARNING] = "WARNING",
[SNOTICE] = "NOTICE",
[SINFO] = "INFO",
[SDEBUG] = "DEBUG"
};
void twamp_log(int priority, const char *format, ...)
{
va_list vl;
if (priority <= cur_twamp_conf.loglevel) {
#ifdef DEBUG
time_t t = time(NULL);
struct tm tm = *localtime(&t);
va_start(vl, format);
printf("%d-%02d-%02d %02d:%02d:%02d [twamp] %s - ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, log_str[priority]);
vprintf(format, vl);
va_end(vl);
printf("\n");
#endif
openlog("twamp", 0, LOG_DAEMON);
va_start(vl, format);
vsyslog(log_syslogmap[priority], format, vl);
va_end(vl);
closelog();
}
}

View file

@ -0,0 +1,27 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _TWAMPLOG_H_
#define _TWAMPLOG_H_
#define DEFAULT_LOGLEVEL SINFO
enum udpechoserver_log_level_enum {
SCRIT,
SWARNING,
SNOTICE,
SINFO,
SDEBUG,
__MAX_SLOG
};
void twamp_log(int priority, const char *format, ...);
#endif /* _TWAMPLOG_H_ */

View file

@ -0,0 +1,195 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* Name: Emma Mirică
* Project: TWAMP Protocol
* Class: OSS
* Email: emma.mirica@cti.pub.ro
* Contributions: stephanDB
*
*/
#include <inttypes.h>
#include <sys/time.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
#include "twamp.h"
#include "twamplog.h"
void timeval_to_timestamp(const struct timeval *tv, TWAMPTimestamp * ts)
{
if (!tv || !ts)
return;
/* Unix time to NTP */
ts->integer = tv->tv_sec + 2208988800uL;
ts->fractional = (uint32_t) ((double)tv->tv_usec * ((double)(1uLL << 32)
/ (double)1e6));
ts->integer = htonl(ts->integer);
ts->fractional = htonl(ts->fractional);
}
void timestamp_to_timeval(const TWAMPTimestamp * ts, struct timeval *tv)
{
if (!tv || !ts)
return;
TWAMPTimestamp ts_host_ord;
ts_host_ord.integer = ntohl(ts->integer);
ts_host_ord.fractional = ntohl(ts->fractional);
/* NTP to Unix time */
tv->tv_sec = ts_host_ord.integer - 2208988800uL;
tv->tv_usec = (uint32_t) (double)ts_host_ord.fractional * (double)1e6 / (double)(1uLL << 32);
}
TWAMPTimestamp get_timestamp()
{
struct timeval tv;
gettimeofday(&tv, NULL);
TWAMPTimestamp ts;
timeval_to_timestamp(&tv, &ts);
return ts;
}
uint64_t get_usec(const TWAMPTimestamp * ts)
{
struct timeval tv;
timestamp_to_timeval(ts, &tv);
return tv.tv_sec * 1000000 + tv.tv_usec;
}
int get_actual_shutdown(const struct timeval *tv, const struct timeval *ts,
const TWAMPTimestamp * t)
{
/* If ts is 0 then no StopSessions message was received */
if ((ts->tv_sec * 1000000 + ts->tv_usec) == 0)
return 1;
/* Else compute time difference */
uint64_t current = tv->tv_sec * 1000000 + tv->tv_usec;
uint64_t shutdown = ts->tv_sec * 1000000 + ts->tv_usec;
uint64_t timeout = get_usec(t);
/* This should be ok, as no difference is computed */
if (current > shutdown + timeout)
return 1;
return 0;
}
void print_metrics_server(char *addr_cl, uint16_t snd_port, uint16_t rcv_port,
uint8_t snd_tos, uint8_t fw_tos,
const ReflectorUPacket * pack)
{
/* Compute timestamps in usec */
uint64_t t_sender_usec1 = get_usec(&pack->sender_time);
uint64_t t_receive_usec1 = get_usec(&pack->receive_time);
uint64_t t_reflsender_usec1 = get_usec(&pack->time);
/* Compute delays */
int64_t fwd1 = t_receive_usec1 - t_sender_usec1;
int64_t intd1 = t_reflsender_usec1 - t_receive_usec1;
char sync1 = 'Y';
if (fwd1 < 0) {
sync1 = 'N';
}
/* Sequence number */
uint32_t snd_nb = ntohl(pack->sender_seq_number);
uint32_t rcv_nb = ntohl(pack->seq_number);
/* Sender TOS with ECN from FW TOS */
snd_tos = snd_tos + (fw_tos & 0x3) - (((fw_tos & 0x2) >> 1) & (fw_tos & 0x1));
/* Print different metrics */
twamp_log(SINFO,"Snd@: %s", addr_cl);
twamp_log(SINFO,"Time: %.0f", (double)t_sender_usec1 * 1e-3);
twamp_log(SINFO,"Snd#: %d", snd_nb);
twamp_log(SINFO,"Rcv#: %d", rcv_nb);
twamp_log(SINFO,"SndPt: %d", snd_port);
twamp_log(SINFO,"RcvPt: %d", rcv_port);
twamp_log(SINFO,"Sync: %c", sync1);
twamp_log(SINFO,"TTL: %d", pack->sender_ttl);
twamp_log(SINFO,"SndTOS: %d", snd_tos);
twamp_log(SINFO,"FW_TOS: %d", fw_tos);
twamp_log(SINFO,"Int D: %.3f", (double)intd1 * 1e-3);
}
void set_socket_option(int socket, uint8_t ip_ttl)
{
/* Set socket options : timeout, IPTTL, IP_RECVTTL, IP_RECVTOS */
uint8_t One = 1;
int result;
/* Set Timeout */
struct timeval timeout = { LOSTTIME, 0 }; //set timeout for 2 seconds
/* Set receive UDP message timeout value */
#ifdef SO_RCVTIMEO
result = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO,
(char *)&timeout, sizeof(struct timeval));
if (result != 0) {
twamp_log(SDEBUG, "[PROBLEM] Cannot set the timeout value for reception.\n");
}
#else
twamp_log(SDEBUG, "No way to set the timeout value for incoming packets on that platform.\n");
#endif
/* Set IPTTL value to twamp standard: 255 */
#ifdef IP_TTL
result = setsockopt(socket, IPPROTO_IP, IP_TTL, &ip_ttl, sizeof(ip_ttl));
if (result != 0) {
twamp_log(SDEBUG, "[PROBLEM] Cannot set the TTL value for emission.\n");
}
#else
twamp_log(SDEBUG, "No way to set the TTL value for leaving packets on that platform.\n");
#endif
/* Set receive IP_TTL option */
#ifdef IP_RECVTTL
result = setsockopt(socket, IPPROTO_IP, IP_RECVTTL, &One, sizeof(One));
if (result != 0) {
twamp_log(SDEBUG, "[PROBLEM] Cannot set the socket option for TTL reception.\n");
}
#else
twamp_log(SDEBUG, "No way to ask for the TTL of incoming packets on that platform.\n");
#endif
/* Set receive IP_TOS option */
#ifdef IP_RECVTOS
result = setsockopt(socket, IPPROTO_IP, IP_RECVTOS, &One, sizeof(One));
if (result != 0) {
twamp_log(SDEBUG, "[PROBLEM] Cannot set the socket option for TOS reception.\n");
}
#else
twamp_log(SDEBUG, "No way to ask for the TOS of incoming packets on that platform.\n");
#endif
}
void set_socket_tos(int socket, uint8_t ip_tos)
{
/* Set socket options : IP_TOS */
int result;
/* Set IP TOS value */
#ifdef IP_TOS
result = setsockopt(socket, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos));
if (result != 0) {
twamp_log(SDEBUG, "[PROBLEM] Cannot set the TOS value for emission.\n");
}
#else
twamp_log(SDEBUG, "No way to set the TOS value for leaving packets on that platform.\n");
#endif
}

266
trx69/twamp/src/twampuci.c Normal file
View file

@ -0,0 +1,266 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <uci.h>
#include "twampuci.h"
struct uci_context *uci_ctx;
int dmuci_init(void)
{
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return -1;
}
return 0;
}
int dmuci_fini(void)
{
if (uci_ctx) {
uci_free_context(uci_ctx);
}
return 0;
}
static bool dmuci_validate_section(const char *str)
{
if (!*str)
return false;
for (; *str; str++) {
unsigned char c = *str;
if (isalnum(c) || c == '_')
continue;
return false;
}
return true;
}
static int dmuci_init_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *package, char *section, char *option, char *value)
{
memset(ptr, 0, sizeof(struct uci_ptr));
/* value */
if (value) {
ptr->value = value;
}
ptr->package = package;
if (!ptr->package)
goto error;
ptr->section = section;
if (!ptr->section) {
ptr->target = UCI_TYPE_PACKAGE;
goto lastval;
}
ptr->option = option;
if (!ptr->option) {
ptr->target = UCI_TYPE_SECTION;
goto lastval;
} else {
ptr->target = UCI_TYPE_OPTION;
}
lastval:
if (ptr->section && !dmuci_validate_section(ptr->section))
ptr->flags |= UCI_LOOKUP_EXTENDED;
return 0;
error:
return -1;
}
struct uci_section *dmuci_walk_section(char *package, char *section_type, struct uci_section *prev_section)
{
struct uci_ptr ptr;
struct uci_element *e;
struct uci_section *next_section;
if (section_type == NULL) {
if (prev_section) {
e = &prev_section->e;
if (e->list.next == &prev_section->package->sections)
return NULL;
e = container_of(e->list.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
}
else {
if (dmuci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL)) {
return NULL;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return NULL;
}
if (ptr.p->sections.next == &ptr.p->sections)
return NULL;
e = container_of(ptr.p->sections.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
}
}
else {
struct uci_list *ul;
struct uci_list *shead = NULL;
if (prev_section) {
ul = &prev_section->e.list;
shead = &prev_section->package->sections;
}
else {
if (dmuci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL)) {
return NULL;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return NULL;
}
ul = &ptr.p->sections;
shead = &ptr.p->sections;
}
while (ul->next != shead) {
e = container_of(ul->next, struct uci_element, list);
next_section = uci_to_section(e);
if (strcmp(next_section->type, section_type) == 0)
return next_section;
ul = ul->next;
}
return NULL;
}
return NULL;
}
void dmuci_print_list(struct uci_list *uh, char **val, char *delimiter)
{
struct uci_element *e;
static char buffer[512];
char *buf = buffer;
*buf = '\0';
uci_foreach_element(uh, e) {
if (*buf) {
strcat(buf, delimiter);
strcat(buf, e->name);
}
else {
strcpy(buf, e->name);
}
}
*val = buf;
}
struct uci_element *dmuci_lookup_list(struct uci_list *list, const char *name)
{
struct uci_element *e;
uci_foreach_element(list, e) {
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
int uci_lookup_ptr_bysection(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *section, char *option, char *value)
{
struct uci_element *e;
memset(ptr, 0, sizeof(struct uci_ptr));
ptr->package = section->package->e.name;
ptr->section = section->e.name;
ptr->option = option;
ptr->value = value;
ptr->flags |= UCI_LOOKUP_DONE;
ptr->p = section->package;
ptr->s = section;
if (ptr->option) {
e = dmuci_lookup_list(&ptr->s->options, ptr->option);
if (!e)
return UCI_OK;
ptr->o = uci_to_option(e);
ptr->last = e;
ptr->target = UCI_TYPE_OPTION;
}
else {
ptr->last = &ptr->s->e;
ptr->target = UCI_TYPE_SECTION;
}
ptr->flags |= UCI_LOOKUP_COMPLETE;
return UCI_OK;
}
char *dmuci_get_value_bysection(struct uci_section *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (uci_lookup_ptr_bysection(uci_ctx, &ptr, section, option, NULL) != UCI_OK) {
return val;
}
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
dmuci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}
char *dmuci_get_value(char *package, char *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (!section || !option)
return val;
if (dmuci_init_ptr(uci_ctx, &ptr, package, section, option, NULL)) {
return val;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return val;
}
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
dmuci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}

View file

@ -0,0 +1,34 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _TWAMPUCI_H__
#define _TWAMPUCI_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <uci.h>
int dmuci_init(void);
int dmuci_fini(void);
struct uci_section *dmuci_walk_section(char *package, char *section_type, struct uci_section *prev_section);
void dmuci_print_list(struct uci_list *uh, char **val, char *delimiter);
struct uci_element *dmuci_lookup_list(struct uci_list *list, const char *name);
int uci_lookup_ptr_bysection(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *section, char *option, char *value);
char *dmuci_get_value_bysection(struct uci_section *section, char *option);
char *dmuci_get_value(char *package, char *section, char *option);
#define dmuci_foreach_section(package, section_type, section) \
for (section = dmuci_walk_section(package, section_type, NULL); \
section != NULL; \
section = dmuci_walk_section(package, section_type, section))
#endif /* _TWAMPUCI_H__ */

44
trx69/udpechoserver/Makefile Executable file
View file

@ -0,0 +1,44 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=udpechoserver
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=BBF udp echo server
DEPENDS:=+libuci +libbbf_api
endef
define Package/$(PKG_NAME)/description
BBF UDP_echo_server
endef
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
TARGET_CFLAGS += \
-D_GNU_SOURCE
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/udpechoserverd $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/lib/bbfdm
$(INSTALL_BIN) $(PKG_BUILD_DIR)/*.so $(1)/usr/lib/bbfdm
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View file

@ -0,0 +1,10 @@
config udpechoserver 'udpechoserver'
option enable '0'
option interface ''
option address ''
option server_port '0'
option plus '0'
#Log levels: Critical=0, Warning=1, Notice=2, Info=3, Debug=4
option log_level '3'

View file

@ -0,0 +1,39 @@
#!/bin/sh /etc/rc.common
#UDP Echo Server software
# Copyright (C) 2020 iopsys Software Solutions AB
#Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
START=99
STOP=10
USE_PROCD=1
PROG="/usr/sbin/udpechoserverd"
start_service() {
local enable_udpechoserver=`uci -q get udpechoserver.udpechoserver.enable`
local port_udpechoserver=`uci -q get udpechoserver.udpechoserver.server_port`
if [ "$port_udpechoserver" != "0" ]; then
if [ "$enable_udpechoserver" = "1" ]; then
iptables -I zone_wan_input -p udp --dport "$port_udpechoserver" -j ACCEPT -m comment --comment "Open UDPechoserver port"
procd_open_instance
procd_set_param command "$PROG"
procd_set_param respawn "3" "7" "0"
procd_close_instance
else
iptables -I zone_wan_input -p udp --dport "$port_udpechoserver" -j REJECT -m comment --comment "Close UDPechoserver port"
fi
fi
}
boot() {
start
}
reload_service() {
stop
start
}
service_triggers() {
procd_add_reload_trigger udpechoserver
}

View file

@ -0,0 +1,23 @@
PROG = udpechoserverd
LIB = libudpechoserver.so
PROG_OBJS = udpechoserver.o udpechoserverlog.o udpechoserveruci.o
LIB_OBJS = datamodel.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) -luci
LIB_LDFLAGS = $(LDFLAGS) -lbbf_api
%.o: %.c
$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
all: $(PROG) $(LIB)
$(PROG): $(PROG_OBJS)
$(CC) $(PROG_CFLAGS) -o $@ $^ $(PROG_LDFLAGS)
$(LIB): $(LIB_OBJS)
$(CC) $(PROG_CFLAGS) $(LIB_LDFLAGS) -shared -o $@ $^
clean:
rm -f *.o $(PROG) $(LIB)

View file

@ -0,0 +1,213 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <libbbf_api/dmbbf.h>
#include <libbbf_api/dmcommon.h>
#include <libbbf_api/dmuci.h>
#include <libbbf_api/dmubus.h>
#include <libbbf_api/dmjson.h>
#include "datamodel.h"
/* ********** RootDynamicObj ********** */
LIB_MAP_OBJ tRootDynamicObj[] = {
/* parentobj, nextobject */
{"Device.IP.Diagnostics.", tDeviceUDPEchoConfigObj},
{0}
};
static int get_IPDiagnosticsUDPEchoConfig_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_option_value_string("udpechoserver", "udpechoserver", "enable", value);
return 0;
}
static int set_IPDiagnosticsUDPEchoConfig_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
char file[32] = "/var/state/udpechoserver";
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
return 0;
case VALUESET:
string_to_bool(value, &b);
if (b) {
if( access( file, F_OK ) != -1 )
dmcmd("/bin/rm", 1, file);
dmuci_set_value("udpechoserver", "udpechoserver", "enable", "1");
} else
dmuci_set_value("udpechoserver", "udpechoserver", "enable", "0");
return 0;
}
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_option_value_string("udpechoserver", "udpechoserver", "interface", value);
return 0;
}
static int set_IPDiagnosticsUDPEchoConfig_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 256, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value("udpechoserver", "udpechoserver", "interface", value);
return 0;
}
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_SourceIPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_option_value_string("udpechoserver", "udpechoserver", "address", value);
return 0;
}
static int set_IPDiagnosticsUDPEchoConfig_SourceIPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 45, NULL, 0, IPAddress, 2))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value("udpechoserver", "udpechoserver", "address", value);
return 0;
}
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_UDPPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_option_value_string("udpechoserver", "udpechoserver", "server_port", value);
return 0;
}
static int set_IPDiagnosticsUDPEchoConfig_UDPPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,NULL}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value("udpechoserver", "udpechoserver", "server_port", value);
return 0;
}
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_option_value_string("udpechoserver", "udpechoserver", "plus", value);
return 0;
}
static int set_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
return 0;
case VALUESET:
string_to_bool(value, &b);
dmuci_set_value("udpechoserver", "udpechoserver", "plus", b ? "1" : "0");
return 0;
}
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_EchoPlusSupported(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = "true";
return 0;
}
static inline char *udpechoconfig_get(char *option, char *def)
{
char *tmp;
dmuci_get_varstate_string("udpechoserver", "udpechoserver", option, &tmp);
if(tmp && tmp[0] == '\0')
return dmstrdup(def);
else
return tmp;
}
static int get_IPDiagnosticsUDPEchoConfig_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("PacketsReceived", "0");
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_PacketsResponded(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("PacketsResponded", "0");
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("BytesReceived", "0");
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_BytesResponded(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("BytesResponded", "0");
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_TimeFirstPacketReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("TimeFirstPacketReceived", "0");
return 0;
}
static int get_IPDiagnosticsUDPEchoConfig_TimeLastPacketReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = udpechoconfig_get("TimeLastPacketReceived", "0");
return 0;
}
/* *** Device.IP.Diagnostics. *** */
DMOBJ tDeviceUDPEchoConfigObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, forced_inform, notification, nextdynamicobj, nextobj, leaf, linker, bbfdm_type*/
{"UDPEchoConfig", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tIPDiagnosticsUDPEchoConfigParams, NULL, BBFDM_BOTH},
{0}
};
/* *** Device.IP.Diagnostics.UDPEchoConfig. *** */
DMLEAF tIPDiagnosticsUDPEchoConfigParams[] = {
/* PARAM, permission, type, getvalue, setvalue, forced_inform, notification, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_Enable, set_IPDiagnosticsUDPEchoConfig_Enable, NULL, NULL, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_IPDiagnosticsUDPEchoConfig_Interface, set_IPDiagnosticsUDPEchoConfig_Interface, NULL, NULL, BBFDM_BOTH},
{"SourceIPAddress", &DMWRITE, DMT_STRING, get_IPDiagnosticsUDPEchoConfig_SourceIPAddress, set_IPDiagnosticsUDPEchoConfig_SourceIPAddress, NULL, NULL, BBFDM_BOTH},
{"UDPPort", &DMWRITE, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_UDPPort, set_IPDiagnosticsUDPEchoConfig_UDPPort, NULL, NULL, BBFDM_BOTH},
{"EchoPlusEnabled", &DMWRITE, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled, set_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled, NULL, NULL, BBFDM_BOTH},
{"EchoPlusSupported", &DMREAD, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_EchoPlusSupported, NULL, NULL, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_PacketsReceived, NULL, NULL, NULL, BBFDM_BOTH},
{"PacketsResponded", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_PacketsResponded, NULL, NULL, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_BytesReceived, NULL, NULL, NULL, BBFDM_BOTH},
{"BytesResponded", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_BytesResponded, NULL, NULL, NULL, BBFDM_BOTH},
{"TimeFirstPacketReceived", &DMREAD, DMT_TIME, get_IPDiagnosticsUDPEchoConfig_TimeFirstPacketReceived, NULL, NULL, NULL, BBFDM_BOTH},
{"TimeLastPacketReceived", &DMREAD, DMT_TIME, get_IPDiagnosticsUDPEchoConfig_TimeLastPacketReceived, NULL, NULL, NULL, BBFDM_BOTH},
{0}
};

View file

@ -0,0 +1,17 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _UDPECHOSERVER_H_
#define _UDPECHOSERVER_H_
extern DMOBJ tDeviceUDPEchoConfigObj[];
extern DMLEAF tIPDiagnosticsUDPEchoConfigParams[];
#endif //_UDPECHOSERVER_H_

View file

@ -0,0 +1,230 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <netdb.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "udpechoserver.h"
#include "udpechoserverlog.h"
#include "udpechoserveruci.h"
struct udpechoserver_config cur_udpechoserver_conf = {0};
struct udpechoserver_result cur_udpechoserver_result={0};
static void set_udpechoserver_stats(void)
{
char PacketsReceived[8], PacketsResponded[8], BytesReceived[8], BytesResponded[8];
sprintf(PacketsReceived, "%d", cur_udpechoserver_result.PacketsReceived);
sprintf(PacketsResponded, "%d", cur_udpechoserver_result.PacketsResponded);
sprintf(BytesReceived, "%d", cur_udpechoserver_result.BytesReceived);
sprintf(BytesResponded, "%d", cur_udpechoserver_result.BytesResponded);
dmuci_init();
dmuci_set_value_state("udpechoserver", "udpechoserver", "PacketsReceived", PacketsReceived);
dmuci_set_value_state("udpechoserver", "udpechoserver", "PacketsResponded", PacketsResponded);
dmuci_set_value_state("udpechoserver", "udpechoserver", "BytesReceived", BytesReceived);
dmuci_set_value_state("udpechoserver", "udpechoserver", "BytesResponded", BytesResponded);
dmuci_set_value_state("udpechoserver", "udpechoserver", "TimeFirstPacketReceived", cur_udpechoserver_result.TimeFirstPacket);
dmuci_set_value_state("udpechoserver", "udpechoserver", "TimeLastPacketReceived", cur_udpechoserver_result.TimeLastPacket);
dmuci_fini();
}
int udpechoserver_connect(void)
{
int sock, fromlen, n, ret;
struct sockaddr_in server, from;
struct timeval tv_recv, tv_reply;
struct tm lt;
char buf[1024];
char str[INET_ADDRSTRLEN];
char s_now[default_date_size];
struct udpechoserver_plus *plus;
memset(&cur_udpechoserver_result, 0, sizeof(cur_udpechoserver_result));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
udpechoserver_log(SCRIT,"Socket error = %d", sock);
return -1;
}
bzero(&server, sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=htons(cur_udpechoserver_conf.server_port);
if (bind(sock, (struct sockaddr *)&server, sizeof(server)) > 0) {
udpechoserver_log(SCRIT,"Error in bind function");
return -1;
}
udpechoserver_log(SINFO,"CONNECT UDPECHOSERVER");
while(true)
{
fromlen = sizeof(from);
n = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);
if (n < 0)
udpechoserver_log(SCRIT,"Error in receive message");
inet_ntop(AF_INET, &(from.sin_addr.s_addr), str, INET_ADDRSTRLEN);
udpechoserver_log(SINFO,"UDPECHOSERVER receive massage from %s with data size is %d Bytes", str, n);
if(cur_udpechoserver_conf.address && cur_udpechoserver_conf.address[0] != '\0') {
if(strcmp(cur_udpechoserver_conf.address, str) != 0) {
udpechoserver_log(SINFO,"UDPECHOSERVER can not respond to a UDP echo from this source IP address %s", str);
continue;
}
}
gettimeofday(&tv_recv, NULL);
if(cur_udpechoserver_result.TimeFirstPacketReceived.tv_sec == 0 && cur_udpechoserver_result.TimeFirstPacketReceived.tv_usec == 0)
cur_udpechoserver_result.TimeFirstPacketReceived = tv_recv;
cur_udpechoserver_result.TimeLastPacketReceived = tv_recv;
cur_udpechoserver_result.PacketsReceived++;
cur_udpechoserver_result.BytesReceived+=n;
(void) localtime_r(&(cur_udpechoserver_result.TimeFirstPacketReceived.tv_sec), &lt);
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &lt);
sprintf(cur_udpechoserver_result.TimeFirstPacket,"%s.%06ld", s_now, (long) cur_udpechoserver_result.TimeFirstPacketReceived.tv_usec);
(void) localtime_r(&(cur_udpechoserver_result.TimeLastPacketReceived.tv_sec), &lt);
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &lt);
sprintf(cur_udpechoserver_result.TimeLastPacket,"%s.%06ld", s_now, (long) cur_udpechoserver_result.TimeLastPacketReceived.tv_usec);
if(cur_udpechoserver_conf.plus) {
if(n >= sizeof(struct udpechoserver_plus)) {
plus = (struct udpechoserver_plus *)buf;
plus->TestRespSN = htonl(cur_udpechoserver_result.TestRespSN);
plus->TestRespRecvTimeStamp = htonl(tv_recv.tv_sec*1000000+tv_recv.tv_usec);
plus->TestRespReplyFailureCount = htonl(cur_udpechoserver_result.TestRespReplyFailureCount);
gettimeofday(&tv_reply, NULL);
plus->TestRespReplyTimeStamp = htonl(tv_reply.tv_sec*1000000+tv_reply.tv_usec);
}
}
ret = sendto(sock, buf, n, 0, (struct sockaddr *)&from, fromlen);
if (n != ret) {
cur_udpechoserver_result.TestRespReplyFailureCount++;
udpechoserver_log(SCRIT,"Error in send massage");
}
else {
cur_udpechoserver_result.TestRespSN++;
cur_udpechoserver_result.PacketsResponded++;
cur_udpechoserver_result.BytesResponded+=ret;
}
udpechoserver_log(SINFO,"UDPECHOSERVER sent massage to %s with data size is %d Bytes", str, ret);
set_udpechoserver_stats();
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : PacketsReceived = %d", cur_udpechoserver_result.PacketsReceived);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : PacketsResponded = %d", cur_udpechoserver_result.PacketsResponded);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : BytesReceived = %d", cur_udpechoserver_result.BytesReceived);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : BytesResponded = %d", cur_udpechoserver_result.BytesResponded);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TestRespReplyFailureCount = %d", cur_udpechoserver_result.TestRespReplyFailureCount);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TimeFirstPacketReceived = %s", cur_udpechoserver_result.TimeFirstPacket);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TimeLastPacketReceived = %s", cur_udpechoserver_result.TimeLastPacket);
}
}
int udpechoserver_init(void)
{
char *value = NULL;
int a;
value = dmuci_get_value("udpechoserver", "udpechoserver", "log_level");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_udpechoserver_conf.loglevel = a;
}
else
cur_udpechoserver_conf.loglevel = DEFAULT_LOGLEVEL;
udpechoserver_log(SDEBUG,"Log Level of UDP ECHO SERVER is :%d", cur_udpechoserver_conf.loglevel);
value = dmuci_get_value("udpechoserver", "udpechoserver", "enable");
if(value != NULL && *value != '\0') {
if ((strcasecmp(value,"true")==0) || (strcmp(value,"1")==0)) {
cur_udpechoserver_conf.enable = true;
udpechoserver_log(SDEBUG,"UDP echo server is Enabled");
}
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "interface");
if(value != NULL && *value != '\0') {
if (cur_udpechoserver_conf.interface != NULL)
free(cur_udpechoserver_conf.interface);
cur_udpechoserver_conf.interface = strdup(value);
udpechoserver_log(SDEBUG,"UDP echo server interface is :%s", cur_udpechoserver_conf.interface);
}
else {
cur_udpechoserver_conf.interface = strdup("");
udpechoserver_log(SDEBUG,"UDP echo server interface is empty");
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "address");
if(value != NULL && *value != '\0') {
if (cur_udpechoserver_conf.address != NULL)
free(cur_udpechoserver_conf.address);
cur_udpechoserver_conf.address = strdup(value);
udpechoserver_log(SDEBUG,"UDP echo server address is :%s", cur_udpechoserver_conf.address);
}
else {
cur_udpechoserver_conf.address = strdup("");
udpechoserver_log(SDEBUG,"UDP echo server address is empty");
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "server_port");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_udpechoserver_conf.server_port = a;
udpechoserver_log(SDEBUG,"UDP echo server port is :%d", cur_udpechoserver_conf.server_port);
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "plus");
if(value != NULL && *value != '\0') {
if ((strcasecmp(value,"true")==0) || (strcmp(value,"1")==0)) {
cur_udpechoserver_conf.plus = true;
udpechoserver_log(SDEBUG,"UDP echo server plus is Enabled");
}
else {
cur_udpechoserver_conf.plus = false;
udpechoserver_log(SDEBUG,"UDP echo server plus is Disabled");
}
}
return 0;
}
void udpechoserver_exit(void)
{
free(cur_udpechoserver_conf.address);
free(cur_udpechoserver_conf.interface);
}
int main(void)
{
dmuci_init();
udpechoserver_init();
dmuci_fini();
udpechoserver_log(SINFO,"START UDPECHOSERVER");
udpechoserver_connect();
udpechoserver_exit();
udpechoserver_log(SINFO,"EXIT UDPECHOSERVER");
return 0;
}

View file

@ -0,0 +1,56 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _UDPECHOSERVER_H__
#define _UDPECHOSERVER_H__
#include <stdbool.h>
#include <sys/time.h>
#define default_date_format "AAAA-MM-JJTHH:MM:SS.000000Z"
#define default_date_size sizeof(default_date_format) + 1
struct udpechoserver_config
{
bool enable;
char *interface;
char *address;
int server_port;
bool plus;
int loglevel;
};
struct udpechoserver_plus
{
unsigned int TestGenSN;
unsigned int TestRespSN;
unsigned int TestRespRecvTimeStamp;
unsigned int TestRespReplyTimeStamp;
unsigned int TestRespReplyFailureCount;
};
struct udpechoserver_result
{
unsigned int TestRespSN;
unsigned int TestRespReplyFailureCount;
unsigned int PacketsReceived;
unsigned int PacketsResponded;
unsigned int BytesReceived;
unsigned int BytesResponded;
struct timeval TimeFirstPacketReceived;
struct timeval TimeLastPacketReceived;
char TimeFirstPacket[default_date_size];
char TimeLastPacket[default_date_size];
};
extern struct udpechoserver_config cur_udpechoserver_conf;
extern struct udpechoserver_result cur_udpechoserver_result;
#endif /* _UDPECHOSERVER_H__ */

View file

@ -0,0 +1,40 @@
# README #
udpechoserverd is an implementation of udp echo server to perform the UDP Echo Service and UDP Echo Plus Service.
## Configuration File ##
The udpechoserverd UCI configuration is located in **'/etc/config/udpechoserver'** and contains only one section: **udpechoserver**.
```
config udpechoserver 'udpechoserver'
option enable '0'
option interface ''
option address ''
option server_port ''
option plus '0'
option log_level '3'
```
### udpechoserver section ###
It defines **the udpechoserver configuration** such as enable, interface, address, etc... Possible options to be used in **the udpechoserver** section are listed in the table below.
| Name | Type | Description |
| ------------- | ------- | ------------------------------------------------- |
| `enable` | boolean | If set to **1**, UDP Echo Server will be enabled. |
| `interface` | string | Specifies the interface on which the CPE MUST listen and receive UDP echo requests. |
| `address` | string | Specifies the source IP address which can make an UDP echo requests. |
| `server_port` | integer | The UDP port on which the UDP Echo server MUST listen and respond to UDP echo requests. |
| `plus` | boolean | If set to **1**, the CPE will perform necessary packet processing for UDP Echo Plus packets. |
| `log_level` | integer | Specifies the log type to use, by default it is set to **'INFO'**. The possible types are **'EMERG', 'ALERT', 'CRITIC' ,'ERROR', 'WARNING', 'NOTICE', 'INFO' and 'DEBUG'**. |
## Dependencies ##
To successfully build udpechoserverd, the following libraries are needed:
| Dependency | Link | License |
| ----------- | ------------------------------------------- | -------------- |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |

View file

@ -0,0 +1,55 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <stdio.h>
#include <time.h>
#include <syslog.h>
#include <stdarg.h>
#include "udpechoserverlog.h"
#include "udpechoserver.h"
#define DEBUG
static const int log_syslogmap[] = {
[SCRIT] = LOG_CRIT,
[SWARNING] = LOG_WARNING,
[SNOTICE] = LOG_NOTICE,
[SINFO] = LOG_INFO,
[SDEBUG] = LOG_DEBUG
};
static const char* log_str[] = {
[SCRIT] = "CRITICAL",
[SWARNING] = "WARNING",
[SNOTICE] = "NOTICE",
[SINFO] = "INFO",
[SDEBUG] = "DEBUG"
};
void udpechoserver_log(int priority, const char *format, ...)
{
va_list vl;
if (priority <= cur_udpechoserver_conf.loglevel) {
#ifdef DEBUG
time_t t = time(NULL);
struct tm tm = *localtime(&t);
va_start(vl, format);
printf("%d-%02d-%02d %02d:%02d:%02d [udpechoserver] %s - ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, log_str[priority]);
vprintf(format, vl);
va_end(vl);
printf("\n");
#endif
openlog("udpechoserver", 0, LOG_DAEMON);
va_start(vl, format);
vsyslog(log_syslogmap[priority], format, vl);
va_end(vl);
closelog();
}
}

View file

@ -0,0 +1,27 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _UDPECHOSERVERLOG_H_
#define _UDPECHOSERVERLOG_H_
#define DEFAULT_LOGLEVEL SINFO
enum udpechoserver_log_level_enum {
SCRIT,
SWARNING,
SNOTICE,
SINFO,
SDEBUG,
__MAX_SLOG
};
void udpechoserver_log(int priority, const char *format, ...);
#endif /* _UDPECHOSERVERLOG_H_ */

View file

@ -0,0 +1,182 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <string.h>
#include <ctype.h>
#include <uci.h>
#include "udpechoserveruci.h"
static struct uci_context *uci_ctx = NULL;
static struct uci_context *uci_ctx_state = NULL;
int dmuci_init(void)
{
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return -1;
}
uci_ctx_state = uci_alloc_context();
if (!uci_ctx_state) {
return -1;
}
uci_add_delta_path(uci_ctx_state, uci_ctx_state->savedir);
uci_set_savedir(uci_ctx_state, VAR_STATE);
return 0;
}
int dmuci_fini(void)
{
if (uci_ctx) {
uci_free_context(uci_ctx);
}
if (uci_ctx_state) {
uci_free_context(uci_ctx_state);
}
uci_ctx = NULL;
uci_ctx_state = NULL;
return 0;
}
static bool dmuci_validate_section(const char *str)
{
if (!*str)
return false;
for (; *str; str++) {
unsigned char c = *str;
if (isalnum(c) || c == '_')
continue;
return false;
}
return true;
}
static int dmuci_init_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *package, char *section, char *option, char *value)
{
memset(ptr, 0, sizeof(struct uci_ptr));
/* value */
if (value) {
ptr->value = value;
}
ptr->package = package;
if (!ptr->package)
goto error;
ptr->section = section;
if (!ptr->section) {
ptr->target = UCI_TYPE_PACKAGE;
goto lastval;
}
ptr->option = option;
if (!ptr->option) {
ptr->target = UCI_TYPE_SECTION;
goto lastval;
} else {
ptr->target = UCI_TYPE_OPTION;
}
lastval:
if (ptr->section && !dmuci_validate_section(ptr->section))
ptr->flags |= UCI_LOOKUP_EXTENDED;
return 0;
error:
return -1;
}
void dmuci_print_list(struct uci_list *uh, char **val, char *delimiter)
{
struct uci_element *e;
static char buffer[512];
char *buf = buffer;
*buf = '\0';
uci_foreach_element(uh, e) {
if (*buf) {
strcat(buf, delimiter);
strcat(buf, e->name);
}
else {
strcpy(buf, e->name);
}
}
*val = buf;
}
char *dmuci_get_value(char *package, char *section, char *option)
{
struct uci_ptr ptr;
char *val = "";
if (!section || !option)
return val;
if (dmuci_init_ptr(uci_ctx, &ptr, package, section, option, NULL)) {
return val;
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return val;
}
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
dmuci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}
char *dmuci_set_value(char *package, char *section, char *option, char *value)
{
struct uci_ptr ptr;
int ret = UCI_OK;
if (!section)
return "";
if (dmuci_init_ptr(uci_ctx, &ptr, package, section, option, value)) {
return "";
}
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK) {
return "";
}
uci_set(uci_ctx, &ptr);
if (ret == UCI_OK)
ret = uci_save(uci_ctx, ptr.p);
if (ptr.o && ptr.o->v.string)
return ptr.o->v.string;
return "";
}
/************************* /var/state ***************************/
char *dmuci_set_value_state(char *package, char *section, char *option, char *value)
{
char *val;
struct uci_context *save_uci_ctx = uci_ctx;
uci_ctx = uci_ctx_state;
val = dmuci_set_value(package, section, option, value);
uci_ctx = save_uci_ctx;
return val;
}

View file

@ -0,0 +1,25 @@
/*
* 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 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2020 iopsys Software Solutions AB
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _UDPECHOSERVERUCI_H__
#define _UDPECHOSERVERUCI_H__
#include <uci.h>
#define VAR_STATE "/var/state"
int dmuci_init(void);
int dmuci_fini(void);
void dmuci_print_list(struct uci_list *uh, char **val, char *delimiter);
char *dmuci_get_value(char *package, char *section, char *option);
char *dmuci_set_value(char *package, char *section, char *option, char *value);
char *dmuci_set_value_state(char *package, char *section, char *option, char *value);
#endif /* _UDPECHOSERVERUCI_H__ */

44
trx69/xmpp/Makefile Executable file
View file

@ -0,0 +1,44 @@
#
# Copyright (C) 2020 iopsys Software Solutions AB
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=xmpp
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=TRx69
TITLE:=BBF xmpp Client
DEPENDS:=+libuci +libubox +libstrophe +libbbf_api
endef
define Package/$(PKG_NAME)/description
BBF XMPP Client
endef
define Build/Prepare
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
TARGET_CFLAGS += \
-D_GNU_SOURCE
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/xmppd $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/usr/lib/bbfdm
$(INSTALL_BIN) $(PKG_BUILD_DIR)/*.so $(1)/usr/lib/bbfdm
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View file

@ -0,0 +1,29 @@
config xmpp 'xmpp'
option enable '0'
option id '0'
option allowed_jid ''
#Log levels: Critical=0, Warning=1, Notice=2, Info=3, Debug=4
option loglevel '3'
config connection
option xmpp_id '1'
option enable '0'
option username ''
option password ''
option domain ''
option resource ''
option usetls '0'
option interval '30'
option attempt '16'
option initial_retry_interval '1'
option retry_interval_multiplier '1000'
option retry_max_interval '1'
option serveralgorithm 'DNS-SRV'
config connection_server
option con_id '1'
option enable '0'
option port '5222'
option server_address ''

View file

@ -1,6 +1,6 @@
#!/bin/sh
[ -f /etc/config/cwmp_xmpp ] || exit 0
[ -f /etc/config/xmpp ] || exit 0
[ "$ACTION" == "ifup" ] || exit 0
[ "$INTERFACE" == "loopback" ] && exit 0
@ -17,5 +17,5 @@ local proto="$(uci -q get network.$INTERFACE.proto)"
local ifname="$(uci -q get network.$INTERFACE.ifname)"
[ "${ifname:0:1}" == "@" ] && exit 0
/etc/init.d/icwmp_xmppd reload &
/etc/init.d/xmppd reload &

View file

@ -0,0 +1,42 @@
#!/bin/sh /etc/rc.common
# XMPP client software
# Copyright (C) 2020 iopsys Software Solutions AB
# Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
START=99
STOP=10
USE_PROCD=1
PROG="/usr/sbin/xmppd"
start_service() {
local xmpp_enable=`uci -q get xmpp.xmpp.enable`
local xmpp_id=`uci -q get xmpp.xmpp.id`
if ([ "$xmpp_enable" = "1" ] && [ "$xmpp_id" != "0" ]); then
local con=`uci show xmpp | grep "xmpp.@connection.*xmpp_id=\'$xmpp_id\'" | cut -d "." -f 2`
local con_srv=`uci show xmpp | grep "xmpp.@connection.*con_id=\'$xmpp_id\'" | cut -d "." -f 2`
local serveralgorithm=`uci get xmpp.$con.serveralgorithm`
local con_enable=`uci -q get xmpp.$con.enable`
local srv_enable=`uci -q get xmpp.$con_srv.enable`
if ([ "$serveralgorithm" = "DNS-SRV" ] && [ "$con_enable" = "1" ]) || ([ "$serveralgorithm" = "ServerTable" ] && [ "$con_enable" = "1" ] && [ "$srv_enable" = "1" ]); then
procd_open_instance
procd_set_param command "$PROG"
procd_set_param respawn "3" "7" "0"
procd_close_instance
fi
fi
}
boot() {
start
}
reload_service() {
stop
start
}
service_triggers()
{
procd_add_reload_trigger xmpp
}

23
trx69/xmpp/src/Makefile Normal file
View file

@ -0,0 +1,23 @@
PROG = xmppd
LIB = libxmpp.so
PROG_OBJS = xmpp.o xuci.o cmd.o log.o
LIB_OBJS = datamodel.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) -luci -lubox -lstrophe
LIB_LDFLAGS = $(LDFLAGS) -lbbf_api
%.o: %.c
$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
all: $(PROG) $(LIB)
$(PROG): $(PROG_OBJS)
$(CC) $(PROG_CFLAGS) -o $@ $^ $(PROG_LDFLAGS)
$(LIB): $(LIB_OBJS)
$(CC) $(PROG_CFLAGS) $(LIB_LDFLAGS) -shared -o $@ $^
clean:
rm -f *.o $(PROG) $(LIB)

78
trx69/xmpp/src/cmd.c Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static int pfds[2];
int xmpp_cmd(unsigned char dowait, int argc, ...)
{
int pid;
va_list arg;
if (pipe(pfds) < 0)
return -1;
if ((pid = fork()) == -1)
return -1;
if (pid == 0) {
/* child */
int i;
const char *argv[64];
va_start(arg, argc);
for (i = 0; i < argc; i++)
{
argv[i] = va_arg(arg, char *);
}
argv[i] = NULL;
va_end(arg);
close(pfds[0]);
dup2(pfds[1], 1);
close(pfds[1]);
execvp(argv[0], (char **) argv);
exit(ESRCH);
} else if (pid < 0)
return -1;
/* parent */
close(pfds[1]);
if (dowait) {
int status;
while (wait(&status) != pid);
}
return 0;
}
void xmpp_cmd_close(void)
{
close(pfds[0]);
}

34
trx69/xmpp/src/cmd.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef __XMPPCMD_H
#define __XMPPCMD_H
int xmpp_cmd(unsigned char wait, int argc, ...);
void xmpp_cmd_close(void);
#define XMPP_CMD(ARGC, args...) \
do { \
xmpp_cmd(1, ARGC, ##args); \
xmpp_cmd_close(); \
} while(0)
#endif //__XMPPCMD_H

647
trx69/xmpp/src/datamodel.c Normal file
View file

@ -0,0 +1,647 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#include <libbbf_api/dmbbf.h>
#include <libbbf_api/dmcommon.h>
#include <libbbf_api/dmuci.h>
#include <libbbf_api/dmubus.h>
#include <libbbf_api/dmjson.h>
#include "datamodel.h"
/* ********** RootDynamicObj ********** */
LIB_MAP_OBJ tRootDynamicObj[] = {
/* parentobj, nextobject */
{"Device.", tDeviceXMPPObj},
{0}
};
static int add_xmpp_connection(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
struct uci_section *xmpp_con, *xmpp_con_srv, *dmmap_xmpp;
char *con_name, *con_srv_name, *last_inst, *v, *id;
check_create_dmmap_package("dmmap_xmpp");
last_inst = get_last_instance_bbfdm("dmmap_xmpp", "connection", "con_inst");
dmasprintf(&id, "%d", (last_inst) ? atoi(last_inst) + 1 : 1);
dmuci_add_section_and_rename("xmpp", "connection", &xmpp_con, &con_name);
dmuci_set_value_by_section(xmpp_con, "xmpp_id", id);
dmuci_set_value_by_section(xmpp_con, "enable", "0");
dmuci_set_value_by_section(xmpp_con, "interval", "30");
dmuci_set_value_by_section(xmpp_con, "attempt", "16");
dmuci_set_value_by_section(xmpp_con, "serveralgorithm", "DNS-SRV");
dmuci_add_section_and_rename("xmpp", "connection_server", &xmpp_con_srv, &con_srv_name);
dmuci_set_value_by_section(xmpp_con_srv, "con_id", id);
dmuci_set_value_by_section(xmpp_con_srv, "enable", "0");
dmuci_set_value_by_section(xmpp_con_srv, "port", "5222");
dmuci_add_section_bbfdm("dmmap_xmpp", "connection_server", &dmmap_xmpp, &v);
dmuci_set_value_by_section(dmmap_xmpp, "section_name", section_name(xmpp_con_srv));
dmuci_set_value_by_section(dmmap_xmpp, "con_srv_inst", "1");
dmuci_add_section_bbfdm("dmmap_xmpp", "connection", &dmmap_xmpp, &v);
dmuci_set_value_by_section(dmmap_xmpp, "section_name", section_name(xmpp_con));
*instance = update_instance(last_inst, 4, dmmap_xmpp, "con_inst", "dmmap_xmpp", "connection");
return 0;
}
static int delete_xmpp_connection(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
struct uci_section *s = NULL, *ss = NULL, *dmmap_section = NULL, *stmp = NULL;
char *prev_con_id;
int found = 0;
switch (del_action) {
case DEL_INST:
dmuci_get_value_by_section_string((struct uci_section *)data, "xmpp_id", &prev_con_id);
uci_foreach_option_eq_safe("xmpp", "connection_server", "con_id", prev_con_id, stmp, s) {
get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name(s), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section(s, NULL, NULL);
break;
}
get_dmmap_section_of_config_section("dmmap_xmpp", "connection", section_name((struct uci_section *)data), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section((struct uci_section *)data, NULL, NULL);
return 0;
case DEL_ALL:
uci_foreach_sections("xmpp", "connection", s) {
if (found != 0) {
get_dmmap_section_of_config_section("dmmap_xmpp", "connection", section_name(ss), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section(ss, NULL, NULL);
}
ss = s;
found++;
}
if (ss != NULL) {
get_dmmap_section_of_config_section("dmmap_xmpp", "connection", section_name(ss), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section(ss, NULL, NULL);
}
found = 0;
uci_foreach_sections("xmpp", "connection_server", s) {
if (found != 0) {
get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name(ss), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section(ss, NULL, NULL);
}
ss = s;
found++;
}
if (ss != NULL) {
get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name(ss), &dmmap_section);
dmuci_delete_by_section(dmmap_section, NULL, NULL);
dmuci_delete_by_section(ss, NULL, NULL);
}
return 0;
}
return 0;
}
/*#Device.XMPP.ConnectionNumberOfEntries!UCI:xmpp/xmpp_connection/*/
static int get_xmpp_connection_nbr_entry(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
struct uci_section *s;
int cnt = 0;
uci_foreach_sections("xmpp", "connection", s) {
cnt++;
}
dmasprintf(value, "%d", cnt); // MEM WILL BE FREED IN DMMEMCLEAN
return 0;
}
static int get_xmpp_connection_supported_server_connect_algorithms(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = "DNS-SRV,ServerTable";
return 0;
}
/*#Device.XMPP.Connection.{i}.Enable!UCI:xmpp/xmpp_connection,@i-1/enable*/
static int get_connection_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "enable", value);
return 0;
}
static int set_connection_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
return 0;
case VALUESET:
string_to_bool(value, &b);
dmuci_set_value_by_section((struct uci_section *)data, "enable", b ? "1" : "0");
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Alias!UCI:dmmap_xmpp/connection,@i-1/con_alias*/
static int get_xmpp_connection_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
struct uci_section *dmmap_section = NULL;
get_dmmap_section_of_config_section("dmmap_xmpp", "connection", section_name((struct uci_section *)data), &dmmap_section);
dmuci_get_value_by_section_string(dmmap_section, "con_alias", value);
if ((*value)[0] == '\0')
dmasprintf(value, "cpe-%s", instance);
return 0;
}
static int set_xmpp_connection_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
struct uci_section *dmmap_section = NULL;
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
get_dmmap_section_of_config_section("dmmap_xmpp", "connection", section_name((struct uci_section *)data), &dmmap_section);
dmuci_set_value_by_section(dmmap_section, "con_alias", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Username!UCI:xmpp/xmpp_connection,@i-1/username*/
static int get_xmpp_connection_username(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "username", value);
return 0;
}
static int set_xmpp_connection_username(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 256, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "username", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Password!UCI:xmpp/xmpp_connection,@i-1/password*/
static int get_xmpp_connection_password(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = "";
return 0;
}
static int set_xmpp_connection_password(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 256, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "password", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Domain!UCI:xmpp/xmpp_connection,@i-1/domain*/
static int get_xmpp_connection_domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "domain", value);
return 0;
}
static int set_xmpp_connection_domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "domain", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Resource!UCI:xmpp/xmpp_connection,@i-1/resource*/
static int get_xmpp_connection_resource(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "resource", value);
return 0;
}
static int set_xmpp_connection_resource(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "resource", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.ServerConnectAlgorithm!UCI:xmpp/xmpp_connection,@i-1/serveralgorithm*/
static int get_xmpp_connection_server_connect_algorithm(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "serveralgorithm", value);
return 0;
}
static int set_xmpp_connection_server_connect_algorithm(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, -1, ServerConnectAlgorithm, 4, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "serveralgorithm", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.KeepAliveInterval!UCI:xmpp/xmpp_connection,@i-1/interval*/
static int get_xmpp_connection_keepalive_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "interval", value);
return 0;
}
static int set_xmpp_connection_keepalive_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_long(value, RANGE_ARGS{{"-1",NULL}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "interval", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.ServerConnectAttempts!UCI:xmpp/xmpp_connection,@i-1/attempt*/
static int get_xmpp_connection_server_attempts(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "attempt", value);
return 0;
}
static int set_xmpp_connection_server_attempts(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,NULL}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "attempt", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.ServerRetryInitialInterval!UCI:xmpp/xmpp_connection,@i-1/initial_retry_interval*/
static int get_xmpp_connection_retry_initial_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "initial_retry_interval", value);
return 0;
}
static int set_xmpp_connection_retry_initial_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{"1","65535"}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "initial_retry_interval", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.ServerRetryIntervalMultiplier!UCI:xmpp/xmpp_connection,@i-1/retry_interval_multiplier*/
static int get_xmpp_connection_retry_interval_multiplier(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "retry_interval_multiplier", value);
return 0;
}
static int set_xmpp_connection_retry_interval_multiplier(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{"1000","65535"}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "retry_interval_multiplier", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.ServerRetryMaxInterval!UCI:xmpp/xmpp_connection,@i-1/retry_max_interval*/
static int get_xmpp_connection_retry_max_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "retry_max_interval", value);
return 0;
}
static int set_xmpp_connection_retry_max_interval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{"1",NULL}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "retry_max_interval", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.UseTLS!UCI:xmpp/xmpp_connection,@i-1/usetls*/
static int get_xmpp_connection_server_usetls(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "usetls", value);
return 0;
}
static int set_xmpp_connection_server_usetls(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
return 0;
case VALUESET:
string_to_bool(value, &b);
dmuci_set_value_by_section((struct uci_section *)data, "usetls", b ? "1" : "0");
return 0;
}
return 0;
}
static int get_xmpp_connection_jabber_id(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
char *resource, *domain, *username;
dmuci_get_value_by_section_string((struct uci_section *)data, "resource", &resource);
dmuci_get_value_by_section_string((struct uci_section *)data, "domain", &domain);
dmuci_get_value_by_section_string((struct uci_section *)data, "username", &username);
if (*resource != '\0' || *domain != '\0' || *username != '\0')
dmasprintf(value, "%s@%s/%s", username, domain, resource);
else
*value = "";
return 0;
}
/*#Device.XMPP.Connection.{i}.Status!UCI:xmpp/xmpp_connection,@i-1/enable*/
static int get_xmpp_connection_status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
char *status;
dmuci_get_value_by_section_string((struct uci_section *)data, "enable", &status);
*value = (strcmp(status, "1") == 0) ? "Enabled" : "Disabled";
return 0;
}
static int get_xmpp_connection_server_number_of_entries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
*value = "1";
return 0;
}
/*#Device.XMPP.Connection.{i}.Server.{i}.Enable!UCI:xmpp/xmpp_connection,@i-1/enable*/
static int get_xmpp_connection_server_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "enable", value);
return 0;
}
static int set_xmpp_connection_server_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
bool b;
switch (action) {
case VALUECHECK:
if (dm_validate_boolean(value))
return FAULT_9007;
return 0;
case VALUESET:
string_to_bool(value, &b);
dmuci_set_value_by_section((struct uci_section *)data, "enable", b ? "1" : "0");
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Server.{i}.Alias!UCI:dmmap_xmpp/connection_server,@i-1/con_srv_alias*/
static int get_xmpp_connection_server_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
struct uci_section *dmmap_section = NULL;
get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name((struct uci_section *)data), &dmmap_section);
dmuci_get_value_by_section_string(dmmap_section, "con_srv_alias", value);
if ((*value)[0] == '\0')
dmasprintf(value, "cpe-%s", instance);
return 0;
}
static int set_xmpp_connection_server_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
struct uci_section *dmmap_section = NULL;
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 64, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
get_dmmap_section_of_config_section("dmmap_xmpp", "connection_server", section_name((struct uci_section *)data), &dmmap_section);
dmuci_set_value_by_section(dmmap_section, "con_srv_alias", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Server.{i}.ServerAddress!UCI:xmpp/xmpp_connection,@i-1/server_address*/
static int get_xmpp_connection_server_server_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "server_address", value);
return 0;
}
static int set_xmpp_connection_server_server_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_string(value, -1, 256, NULL, 0, NULL, 0))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "server_address", value);
return 0;
}
return 0;
}
/*#Device.XMPP.Connection.{i}.Server.{i}.Port!UCI:xmpp/xmpp_connection,@i-1/port*/
static int get_xmpp_connection_server_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
dmuci_get_value_by_section_string((struct uci_section *)data, "port", value);
return 0;
}
static int set_xmpp_connection_server_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
switch (action) {
case VALUECHECK:
if (dm_validate_unsignedInt(value, RANGE_ARGS{{"0","65535"}}, 1))
return FAULT_9007;
return 0;
case VALUESET:
dmuci_set_value_by_section((struct uci_section *)data, "port", value);
return 0;
}
return 0;
}
/*************************************************************
* ENTRY METHOD
**************************************************************/
/*#Device.XMPP.Connection.{i}.!UCI:xmpp/xmpp_connection/dmmap_cwmp_xmpp*/
static int browsexmpp_connectionInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
char *inst, *max_inst = NULL;
struct dmmap_dup *p;
LIST_HEAD(dup_list);
synchronize_specific_config_sections_with_dmmap("xmpp", "connection", "dmmap_xmpp", &dup_list);
list_for_each_entry(p, &dup_list, list) {
inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
p->dmmap_section, "con_inst", "con_alias", "dmmap_xmpp", "connection");
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
break;
}
free_dmmap_config_dup_list(&dup_list);
return 0;
}
/*#Device.XMPP.Connection.{i}.!UCI:xmpp/xmpp_connection_server/dmmap_cwmp_xmpp*/
static int browsexmpp_connection_serverInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
char *inst, *max_inst = NULL, *con_id;
struct dmmap_dup *p;
LIST_HEAD(dup_list);
dmuci_get_value_by_section_string((struct uci_section *)prev_data, "xmpp_id", &con_id);
synchronize_specific_config_sections_with_dmmap_eq("xmpp", "connection_server", "dmmap_xmpp", "con_id", con_id, &dup_list);
list_for_each_entry(p, &dup_list, list) {
inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 5,
p->dmmap_section, "con_srv_inst", "con_srv_alias", "xmpp", "connection_server");
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
break;
}
free_dmmap_config_dup_list(&dup_list);
return 0;
}
/* *** Device.XMPP. *** */
DMOBJ tDeviceXMPPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, forced_inform, notification, nextdynamicobj, nextobj, leaf, linker, bbfdm_type*/
{"XMPP", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tXMPPObj, tXMPPParams, NULL, BBFDM_BOTH},
{0}
};
DMOBJ tXMPPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, forced_inform, notification, nextdynamicobj, nextobj, leaf, linker, bbfdm_type*/
{"Connection", &DMWRITE, add_xmpp_connection, delete_xmpp_connection, NULL, browsexmpp_connectionInst, NULL, NULL, NULL, tXMPPConnectionObj, tXMPPConnectionParams, NULL, BBFDM_BOTH},
{0}
};
DMLEAF tXMPPParams[] = {
/* PARAM, permission, type, getvalue, setvalue, forced_inform, notification, bbfdm_type*/
{"ConnectionNumberOfEntries", &DMREAD, DMT_UNINT, get_xmpp_connection_nbr_entry, NULL, NULL, NULL, BBFDM_BOTH},
{"SupportedServerConnectAlgorithms", &DMREAD, DMT_STRING, get_xmpp_connection_supported_server_connect_algorithms, NULL, NULL, NULL, BBFDM_BOTH},
{0}
};
/* *** Device.XMPP.Connection.{i}. *** */
DMOBJ tXMPPConnectionObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, forced_inform, notification, nextdynamicobj, nextobj, leaf, linker, bbfdm_type*/
{"Server", &DMREAD, NULL, NULL, NULL, browsexmpp_connection_serverInst, NULL, NULL, NULL, NULL, tXMPPConnectionServerParams, NULL, BBFDM_BOTH},
{0}
};
DMLEAF tXMPPConnectionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, forced_inform, notification, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_connection_enable, set_connection_enable, NULL, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_xmpp_connection_alias, set_xmpp_connection_alias, NULL, NULL, BBFDM_BOTH},
{"Username", &DMWRITE, DMT_STRING, get_xmpp_connection_username, set_xmpp_connection_username, NULL, NULL, BBFDM_BOTH},
{"Password", &DMWRITE, DMT_STRING, get_xmpp_connection_password, set_xmpp_connection_password, NULL, NULL, BBFDM_BOTH},
{"Domain", &DMWRITE, DMT_STRING, get_xmpp_connection_domain, set_xmpp_connection_domain, NULL, NULL, BBFDM_BOTH},
{"Resource", &DMWRITE, DMT_STRING, get_xmpp_connection_resource, set_xmpp_connection_resource, NULL, NULL, BBFDM_BOTH},
{"ServerConnectAlgorithm", &DMWRITE, DMT_STRING, get_xmpp_connection_server_connect_algorithm, set_xmpp_connection_server_connect_algorithm, NULL, NULL, BBFDM_BOTH},
{"KeepAliveInterval", &DMWRITE, DMT_LONG, get_xmpp_connection_keepalive_interval, set_xmpp_connection_keepalive_interval, NULL, NULL, BBFDM_BOTH},
{"ServerConnectAttempts", &DMWRITE, DMT_UNINT, get_xmpp_connection_server_attempts, set_xmpp_connection_server_attempts, NULL, NULL, BBFDM_BOTH},
{"ServerRetryInitialInterval", &DMWRITE, DMT_UNINT, get_xmpp_connection_retry_initial_interval, set_xmpp_connection_retry_initial_interval, NULL, NULL, BBFDM_BOTH},
{"ServerRetryIntervalMultiplier", &DMWRITE, DMT_UNINT, get_xmpp_connection_retry_interval_multiplier, set_xmpp_connection_retry_interval_multiplier, NULL, NULL, BBFDM_BOTH},
{"ServerRetryMaxInterval", &DMWRITE, DMT_UNINT, get_xmpp_connection_retry_max_interval, set_xmpp_connection_retry_max_interval, NULL, NULL, BBFDM_BOTH},
{"UseTLS", &DMWRITE, DMT_BOOL, get_xmpp_connection_server_usetls, set_xmpp_connection_server_usetls, NULL, NULL, BBFDM_BOTH},
{"JabberID", &DMREAD, DMT_STRING, get_xmpp_connection_jabber_id, NULL, NULL, NULL, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_xmpp_connection_status, NULL, NULL, NULL, BBFDM_BOTH},
{"ServerNumberOfEntries", &DMREAD, DMT_UNINT, get_xmpp_connection_server_number_of_entries, NULL, NULL, NULL, BBFDM_BOTH},
{0}
};
/* *** Device.XMPP.Connection.{i}.Server.{i}. *** */
DMLEAF tXMPPConnectionServerParams[] = {
/* PARAM, permission, type, getvalue, setvalue, forced_inform, notification, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_xmpp_connection_server_enable, set_xmpp_connection_server_enable, NULL, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_xmpp_connection_server_alias, set_xmpp_connection_server_alias, NULL, NULL, BBFDM_BOTH},
{"ServerAddress", &DMWRITE, DMT_STRING, get_xmpp_connection_server_server_address, set_xmpp_connection_server_server_address, NULL, NULL, BBFDM_BOTH},
{"Port", &DMWRITE, DMT_UNINT, get_xmpp_connection_server_port, set_xmpp_connection_server_port, NULL, NULL, BBFDM_BOTH},
{0}
};

View file

@ -0,0 +1,21 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*/
#ifndef _XMPP_H_
#define _XMPP_H_
extern DMOBJ tDeviceXMPPObj[];
extern DMOBJ tXMPPObj[];
extern DMLEAF tXMPPParams[];
extern DMLEAF tXMPPConnectionParams[];
extern DMOBJ tXMPPConnectionObj[];
extern DMLEAF tXMPPConnectionServerParams[];
#endif //_XMPP_H_

116
trx69/xmpp/src/log.c Normal file
View file

@ -0,0 +1,116 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <syslog.h>
#include <strophe.h>
#include "log.h"
#include "xmpp.h"
static const int log_syslogmap[] = {
[SCRIT] = LOG_CRIT,
[SWARNING] = LOG_WARNING,
[SNOTICE] = LOG_NOTICE,
[SINFO] = LOG_INFO,
[SDEBUG] = LOG_DEBUG
};
static const char* log_str[] = {
[SCRIT] = "CRITICAL",
[SWARNING] = "WARNING",
[SNOTICE] = "NOTICE",
[SINFO] = "INFO",
[SDEBUG] = "DEBUG"
};
void xmpp_syslog(int priority, const char *format, ...)
{
va_list vl;
if (priority <= cur_xmpp_conf.xmpp_loglevel) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
va_start(vl, format);
printf("%d-%02d-%02d %02d:%02d:%02d [xmpp] %s - ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, log_str[priority]);
vprintf(format, vl);
va_end(vl);
printf("\n");
openlog("xmpp", 0, LOG_DAEMON);
va_start(vl, format);
vsyslog(log_syslogmap[priority], format, vl);
va_end(vl);
closelog();
}
}
void xmpp_syslog_handler(void * const userdata,
const xmpp_log_level_t level,
const char * const area,
const char * const msg)
{
int priority = SDEBUG;
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
if (level >= filter_level) {
switch (level) {
case XMPP_LEVEL_ERROR:
priority = SCRIT;
break;
case XMPP_LEVEL_WARN:
priority = SWARNING;
break;
case XMPP_LEVEL_INFO:
priority = SINFO;
break;
case XMPP_LEVEL_DEBUG:
priority = SDEBUG;
break;
}
xmpp_syslog(priority, "%s %s", area, msg);
}
}
xmpp_log_level_t xmpp_log_get_level(int conf_loglevel)
{
xmpp_log_level_t xmpp_level = XMPP_LEVEL_DEBUG;
switch (conf_loglevel) {
case SCRIT:
xmpp_level = XMPP_LEVEL_ERROR;
break;
case SWARNING:
xmpp_level = XMPP_LEVEL_WARN;
break;
case SINFO:
case SNOTICE:
xmpp_level = XMPP_LEVEL_INFO;
break;
case SDEBUG:
xmpp_level = XMPP_LEVEL_DEBUG;
break;
}
return xmpp_level;
}

42
trx69/xmpp/src/log.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef _XMPPLOG_H_
#define _XMPPLOG_H_
#define DEFAULT_LOGLEVEL SINFO
enum xmpp_log_level_enum {
SCRIT,
SWARNING,
SNOTICE,
SINFO,
SDEBUG,
__MAX_SLOG
};
void xmpp_syslog(int priority, const char *format, ...);
void xmpp_syslog_handler(void * const userdata,
const xmpp_log_level_t level,
const char * const area,
const char * const msg);
xmpp_log_level_t xmpp_log_get_level(int conf_loglevel);
#endif /* _XMPPLOG_H_ */

519
trx69/xmpp/src/xmpp.c Normal file
View file

@ -0,0 +1,519 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is FREE software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the FREE Software Foundation.
*
* 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, write to the FREE Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strophe.h>
#include "xmpp.h"
#include "cmd.h"
#include "log.h"
#include "xuci.h"
struct xmpp_config cur_xmpp_conf = {0};
struct xmpp_connection cur_xmpp_con = {0};
static void xmpp_connecting(void);
static void xmpp_exit(xmpp_ctx_t *ctx, xmpp_conn_t *conn);
static void xmpp_con_exit(void);
static int send_stanza_cr_response(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void * const userdata)
{
xmpp_stanza_t *reply = xmpp_stanza_new((xmpp_ctx_t *)userdata);
if (!reply) {
xmpp_syslog(SCRIT,"XMPP CR response Error");
return -1;
}
xmpp_stanza_set_name(reply, "iq");
xmpp_stanza_set_type(reply, "result");
xmpp_stanza_set_attribute(reply, "from", xmpp_stanza_get_attribute(stanza, "to"));
xmpp_stanza_set_attribute(reply, "id", xmpp_stanza_get_attribute(stanza, "id"));
xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
xmpp_send(conn, reply);
xmpp_stanza_release(reply);
return 0;
}
static int send_stanza_cr_error(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata, int xmpp_error)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
char *username = NULL, *password = NULL;
xmpp_stanza_t *cr_stanza = xmpp_stanza_new(ctx);
if (!cr_stanza) {
xmpp_syslog(SCRIT,"XMPP CR response Error");
return -1;
}
xmpp_stanza_set_name(cr_stanza, "iq");
xmpp_stanza_set_type(cr_stanza, "error");
xmpp_stanza_set_attribute(cr_stanza, "id", xmpp_stanza_get_attribute(stanza, "id"));
xmpp_stanza_set_attribute(cr_stanza, "to", xmpp_stanza_get_attribute(stanza, "from"));
xmpp_stanza_set_attribute(cr_stanza, "from", xmpp_stanza_get_attribute(stanza, "to"));
// Connection Request Message
xmpp_stanza_t *stanza_cr = xmpp_stanza_get_child_by_name(stanza, "connectionRequest");
if (stanza_cr) {
// Username
xmpp_stanza_t *stanza_username = xmpp_stanza_get_child_by_name(stanza_cr, "username");
if (stanza_username)
username = xmpp_stanza_get_text(stanza_username);
//Password
xmpp_stanza_t *stanza_password = xmpp_stanza_get_next(stanza_username);
if (strcmp(xmpp_stanza_get_name(stanza_password), "password") == 0)
password = xmpp_stanza_get_text(stanza_password);
}
xmpp_stanza_t *stanza_cr_msg = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(stanza_cr_msg, "connectionRequest");
xmpp_stanza_set_ns(stanza_cr_msg, XMPP_CR_NS);
xmpp_stanza_t *stanza_username_msg = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(stanza_username_msg, "username");
xmpp_stanza_t *username_msg = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(username_msg, username);
xmpp_stanza_add_child(stanza_username_msg, username_msg);
xmpp_stanza_release(username_msg);
xmpp_stanza_add_child(stanza_cr_msg, stanza_username_msg);
xmpp_stanza_release(stanza_username_msg);
xmpp_stanza_t *stanza_password_msg = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(stanza_password_msg, "password");
xmpp_stanza_t *password_msg = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(password_msg, password);
xmpp_stanza_add_child(stanza_password_msg, password_msg);
xmpp_stanza_release(password_msg);
xmpp_stanza_add_child(stanza_cr_msg, stanza_password_msg);
xmpp_stanza_release(stanza_password_msg);
xmpp_stanza_add_child(cr_stanza, stanza_cr_msg);
xmpp_stanza_release(stanza_cr_msg);
xmpp_stanza_t *stanza_error = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(stanza_error, "error");
if (xmpp_error == XMPP_SERVICE_UNAVAILABLE)
xmpp_stanza_set_attribute(stanza_error, "code", "503");
xmpp_stanza_set_type(stanza_error, "cancel");
xmpp_stanza_t *stanza_service = xmpp_stanza_new(ctx);
if (xmpp_error == XMPP_SERVICE_UNAVAILABLE)
xmpp_stanza_set_name(stanza_service, "service-unavailable");
else if (xmpp_error == XMPP_NOT_AUTHORIZED)
xmpp_stanza_set_name(stanza_service, "not-autorized");
xmpp_stanza_set_attribute(stanza_service, "xmlns", XMPP_ERROR_NS);
xmpp_stanza_add_child(stanza_error, stanza_service);
xmpp_stanza_release(stanza_service);
xmpp_stanza_add_child(cr_stanza, stanza_error);
xmpp_stanza_release(stanza_error);
xmpp_send(conn, cr_stanza);
xmpp_stanza_release(cr_stanza);
// Free allocated memory
FREE(username);
FREE(password);
return 0;
}
static bool check_xmpp_jid_authorized(const char *from)
{
// XMPP config : allowed jabber id is empty
if (cur_xmpp_conf.xmpp_allowed_jid == NULL || cur_xmpp_conf.xmpp_allowed_jid[0] == '\0') {
xmpp_syslog(SDEBUG,"xmpp connection request handler : allowed jid is empty");
return true;
}
// XMPP config : allowed jabber id is not empty
char *spch = NULL;
xmpp_syslog(SDEBUG,"xmpp connection request handler : check each jabber id");
char *allowed_jid = strdup(cur_xmpp_conf.xmpp_allowed_jid);
char *pch = strtok_r(allowed_jid, ",", &spch);
while (pch != NULL) {
if (strncmp(pch, from, strlen(pch)) == 0) {
FREE(allowed_jid);
xmpp_syslog(SDEBUG,"xmpp connection request handler : jabber id is authorized");
return true;
}
pch = strtok_r(NULL, ",", &spch);
}
FREE(allowed_jid);
xmpp_syslog(SDEBUG,"xmpp connection request handler : jabber id is not authorized");
return false;
}
static int cr_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
{
bool valid_ns = true, auth_status = false, service_available = false;
if (xmpp_stanza_get_child_by_name(stanza, "connectionRequest")) {
const char *from = (char *)xmpp_stanza_get_attribute(stanza, "from");
if (!check_xmpp_jid_authorized(from)) {
service_available = false;
xmpp_syslog(SDEBUG,"xmpp connection request handler not authorized by allowed jid");
goto xmpp_end;
}
} else {
xmpp_syslog(SDEBUG,"xmpp connection request handler does not contain an iq type");
return 1;
}
xmpp_stanza_t *stanza_cr = xmpp_stanza_get_child_by_name(stanza, "connectionRequest");
if (stanza_cr) {
service_available = true;
char *name_space = (char *)xmpp_stanza_get_attribute(stanza_cr, "xmlns");
if (strcmp(name_space, XMPP_CR_NS) != 0) {
valid_ns = false;
goto xmpp_end; //send error response
}
xmpp_stanza_t *mech = xmpp_stanza_get_child_by_name(stanza_cr, "username");
if (mech) {
char *text = xmpp_stanza_get_text(mech);
xmpp_uci_init();
const char *username = xmpp_uci_get_value("cwmp", "cpe", "userid");
if (strcmp(text, username) == 0) {
FREE(text);
mech = xmpp_stanza_get_next(mech);
if (strcmp(xmpp_stanza_get_name(mech), "password") == 0) {
text = xmpp_stanza_get_text(mech);
const char *password = xmpp_uci_get_value("cwmp", "cpe", "passwd");
auth_status = (strcmp(text, password) == 0) ? true : false;
}
}
xmpp_uci_fini();
FREE(text);
}
} else {
service_available = false;
goto xmpp_end; //send error response
}
xmpp_end:
if (!valid_ns) {
xmpp_syslog(SINFO, "XMPP Invalid Name space");
send_stanza_cr_error(conn, stanza, userdata, XMPP_SERVICE_UNAVAILABLE);
} else if (!service_available) {
xmpp_syslog(SINFO, "XMPP Service Unavailable");
send_stanza_cr_error(conn, stanza, userdata, XMPP_SERVICE_UNAVAILABLE);
} else if (!auth_status) {
xmpp_syslog(SINFO, "XMPP Not Authorized");
send_stanza_cr_error(conn, stanza, userdata, XMPP_NOT_AUTHORIZED);
} else {
xmpp_syslog(SINFO, "XMPP Authorized");
send_stanza_cr_response(conn, stanza, userdata);
XMPP_CMD(7, "ubus", "-t", "3", "call", "tr069", "inform", "{\"event\" : \"6 connection request\"}");
}
return 1;
}
static int ping_keepalive_handler(xmpp_conn_t * const conn, void * const userdata)
{
xmpp_stanza_t *ping_ka, *ping;
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
char jid[512] = {0};
xmpp_syslog(SDEBUG, "XMPP PING OF KEEPALIVE ");
snprintf(jid, sizeof(jid), "%s@%s/%s", cur_xmpp_con.username, cur_xmpp_con.domain, cur_xmpp_con.resource);
ping_ka = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(ping_ka, "iq");
xmpp_stanza_set_type(ping_ka, "get");
xmpp_stanza_set_id(ping_ka, "s2c1");
xmpp_stanza_set_attribute(ping_ka, "from", jid);
xmpp_stanza_set_attribute(ping_ka, "to", cur_xmpp_con.domain);
ping = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(ping, "ping");
xmpp_stanza_set_attribute(ping, "xmlns", "urn:xmpp:ping");
xmpp_stanza_add_child(ping_ka, ping);
xmpp_stanza_release(ping);
xmpp_send(conn, ping_ka);
xmpp_stanza_release(ping_ka);
return 1;
}
static void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
const int error, xmpp_stream_error_t * const stream_error,
void * const userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
static int attempt = 0;
if (status == XMPP_CONN_CONNECT) {
xmpp_syslog(SINFO,"XMPP Connection Established");
attempt = 0;
xmpp_handler_add(conn, cr_handler, NULL, "iq", NULL, ctx);
xmpp_timed_handler_add(conn, ping_keepalive_handler, cur_xmpp_con.keepalive_interval * 1000, userdata);
xmpp_stanza_t *pres = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(pres, "presence");
xmpp_send(conn, pres);
xmpp_stanza_release(pres);
xmpp_conn_set_keepalive(conn, 30, cur_xmpp_con.keepalive_interval);
} else {
xmpp_syslog(SINFO,"XMPP Connection Lost");
xmpp_exit(ctx, conn);
xmpp_syslog(SINFO,"XMPP Connection Retry");
srand(time(NULL));
if (attempt == 0 && cur_xmpp_con.connect_attempt != 0) {
if (cur_xmpp_con.retry_initial_interval != 0)
sleep(rand()%cur_xmpp_con.retry_initial_interval);
} else if(attempt > cur_xmpp_con.connect_attempt) {
xmpp_syslog(SINFO,"XMPP Connection Aborted");
xmpp_exit(ctx, conn);
xmpp_con_exit();
exit(EXIT_FAILURE);
} else if( attempt >= 1 && cur_xmpp_con.connect_attempt != 0 ) {
int delay = cur_xmpp_con.retry_initial_interval * (cur_xmpp_con.retry_interval_multiplier/1000) * (attempt -1);
if (delay > cur_xmpp_con.retry_max_interval)
sleep(cur_xmpp_con.retry_max_interval);
else
sleep(delay);
} else
sleep(DEFAULT_XMPP_RECONNECTION_RETRY);
attempt += 1;
xmpp_connecting();
}
}
static void xmpp_connecting(void)
{
xmpp_log_t log_xmpp;
char jid[512] = {0};
static int attempt = 0;
int connected = 0;
long flags = 0;
xmpp_initialize();
int xmpp_mesode_log_level = xmpp_log_get_level(cur_xmpp_conf.xmpp_loglevel);
log_xmpp.handler = &xmpp_syslog_handler;
log_xmpp.userdata = &(xmpp_mesode_log_level);
xmpp_ctx_t *ctx = xmpp_ctx_new(NULL, &log_xmpp);
xmpp_conn_t *conn = xmpp_conn_new(ctx);
if(cur_xmpp_con.usetls)
flags |= XMPP_CONN_FLAG_MANDATORY_TLS; /* Set flag XMPP_CONN_FLAG_MANDATORY_TLS to oblige the verification of tls */
else
flags |= XMPP_CONN_FLAG_TRUST_TLS; /* Set flag XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification */
xmpp_conn_set_flags(conn, flags);
snprintf(jid, sizeof(jid), "%s@%s/%s", cur_xmpp_con.username, cur_xmpp_con.domain, cur_xmpp_con.resource);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, cur_xmpp_con.password);
/* initiate connection */
if( strcmp(cur_xmpp_con.serveralgorithm,"DNS-SRV") == 0)
connected = xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
else
connected = xmpp_connect_client(conn, cur_xmpp_con.serveraddress[0] ? cur_xmpp_con.serveraddress : NULL,
cur_xmpp_con.port, conn_handler, ctx);
if (connected < 0 ) {
xmpp_exit(ctx, conn);
xmpp_syslog(SINFO,"XMPP Connection Retry");
srand(time(NULL));
if (attempt == 0 && cur_xmpp_con.connect_attempt != 0) {
if (cur_xmpp_con.retry_initial_interval != 0)
sleep(rand()%cur_xmpp_con.retry_initial_interval);
} else if (attempt > cur_xmpp_con.connect_attempt) {
xmpp_syslog(SINFO,"XMPP Connection Aborted");
xmpp_exit(ctx, conn);
xmpp_con_exit();
exit(EXIT_FAILURE);
} else if (attempt >= 1 && cur_xmpp_con.connect_attempt != 0) {
int delay = cur_xmpp_con.retry_initial_interval * (cur_xmpp_con.retry_interval_multiplier/1000) * (attempt -1);
if (delay > cur_xmpp_con.retry_max_interval)
sleep(cur_xmpp_con.retry_max_interval);
else
sleep(delay);
} else
sleep(DEFAULT_XMPP_RECONNECTION_RETRY);
attempt += 1;
xmpp_connecting();
} else {
attempt = 0;
xmpp_syslog(SDEBUG,"XMPP Handle Connection");
xmpp_run(ctx);
}
}
static void xmpp_exit(xmpp_ctx_t *ctx, xmpp_conn_t *conn)
{
xmpp_stop(ctx);
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
}
static void xmpp_con_exit(void)
{
FREE(cur_xmpp_con.username);
FREE(cur_xmpp_con.password);
FREE(cur_xmpp_con.domain);
FREE(cur_xmpp_con.resource);
FREE(cur_xmpp_con.serveraddress);
FREE(cur_xmpp_con.serveralgorithm);
FREE(cur_xmpp_conf.xmpp_allowed_jid);
}
static void xmpp_global_conf(void)
{
xmpp_uci_init();
// XMPP Log Level
const char *loglevel = xmpp_uci_get_value("xmpp", "xmpp", "loglevel");
if (loglevel != NULL && *loglevel != '\0')
cur_xmpp_conf.xmpp_loglevel = atoi(loglevel);
else
cur_xmpp_conf.xmpp_loglevel = DEFAULT_LOGLEVEL;
xmpp_syslog(SDEBUG,"Log Level of XMPP connection is :%d", cur_xmpp_conf.xmpp_loglevel);
// XMPP Allowed Jabber id
const char *allowed_jid = xmpp_uci_get_value("xmpp", "xmpp", "allowed_jid");
if (allowed_jid != NULL && *allowed_jid != '\0') {
cur_xmpp_conf.xmpp_allowed_jid = strdup(allowed_jid);
xmpp_syslog(SDEBUG,"XMPP connection allowed jaber id :%s", cur_xmpp_conf.xmpp_allowed_jid);
} else {
cur_xmpp_conf.xmpp_allowed_jid = strdup("");
xmpp_syslog(SDEBUG,"XMPP connection allowed jaber id is empty");
}
xmpp_uci_fini();
}
static const char *get_connection_config(const char *option, const char *identifier)
{
struct uci_section *s;
xmpp_uci_foreach_section("xmpp", "connection", s) {
const char *xmpp_id = xmpp_uci_get_value_bysection(s, "xmpp_id");
if (strcmp(xmpp_id, identifier) == 0)
return xmpp_uci_get_value_bysection(s, option);
}
return "";
}
static const char *get_connection_server_config(const char *option, const char *identifier)
{
struct uci_section *s;
xmpp_uci_foreach_section("xmpp", "connection_server", s) {
const char *con_id = xmpp_uci_get_value_bysection(s, "con_id");
if (strcmp(con_id, identifier) == 0)
return xmpp_uci_get_value_bysection(s, option);
}
return "";
}
static void xmpp_con_init(void)
{
xmpp_uci_init();
const char *identifier = xmpp_uci_get_value("xmpp", "xmpp", "id");
cur_xmpp_con.username = strdup(get_connection_config("username", identifier));
cur_xmpp_con.password = strdup(get_connection_config("password", identifier));
cur_xmpp_con.domain = strdup(get_connection_config("domain", identifier));
cur_xmpp_con.resource = strdup(get_connection_config("resource", identifier));
cur_xmpp_con.usetls = atoi(get_connection_config("usetls", identifier));
cur_xmpp_con.serveralgorithm = strdup(get_connection_config("serveralgorithm", identifier));
cur_xmpp_con.serveraddress = strdup(get_connection_server_config("server_address", identifier));
cur_xmpp_con.port = atoi(get_connection_server_config("port", identifier));
cur_xmpp_con.keepalive_interval = atoi(get_connection_config("interval", identifier));
cur_xmpp_con.connect_attempt = atoi(get_connection_config("attempt", identifier));
if (cur_xmpp_con.connect_attempt) {
cur_xmpp_con.retry_initial_interval = atoi(get_connection_config("initial_retry_interval", identifier));
cur_xmpp_con.retry_initial_interval = (cur_xmpp_con.retry_initial_interval) ? cur_xmpp_con.retry_initial_interval : DEFAULT_RETRY_INITIAL_INTERVAL;
cur_xmpp_con.retry_interval_multiplier = atoi(get_connection_config("retry_interval_multiplier", identifier));
cur_xmpp_con.retry_interval_multiplier = (cur_xmpp_con.retry_interval_multiplier) ? cur_xmpp_con.retry_interval_multiplier : DEFAULT_RETRY_INTERVAL_MULTIPLIER;
cur_xmpp_con.retry_max_interval = atoi(get_connection_config("retry_max_interval", identifier));
cur_xmpp_con.retry_max_interval = (cur_xmpp_con.retry_max_interval) ? cur_xmpp_con.retry_max_interval : DEFAULT_RETRY_MAX_INTERVAL;
}
xmpp_syslog(SDEBUG,"XMPP Connection id: %s", identifier);
xmpp_syslog(SDEBUG,"XMPP username: %s", cur_xmpp_con.username);
xmpp_syslog(SDEBUG,"XMPP password: %s", cur_xmpp_con.password);
xmpp_syslog(SDEBUG,"XMPP domain: %s", cur_xmpp_con.domain);
xmpp_syslog(SDEBUG,"XMPP resource: %s", cur_xmpp_con.resource);
xmpp_syslog(SDEBUG,"XMPP use_tls: %d", cur_xmpp_con.usetls);
xmpp_syslog(SDEBUG,"XMPP serveralgorithm: %s", cur_xmpp_con.serveralgorithm);
xmpp_syslog(SDEBUG,"XMPP server_address: %s", cur_xmpp_con.serveraddress);
xmpp_syslog(SDEBUG,"XMPP port: %d", cur_xmpp_con.port);
xmpp_syslog(SDEBUG,"XMPP keepalive_interval: %d", cur_xmpp_con.keepalive_interval);
xmpp_syslog(SDEBUG,"XMPP connect_attempt: %d", cur_xmpp_con.connect_attempt);
xmpp_syslog(SDEBUG,"XMPP retry_initial_interval: %d", cur_xmpp_con.retry_initial_interval);
xmpp_syslog(SDEBUG,"XMPP retry_interval_multiplier: %d", cur_xmpp_con.retry_interval_multiplier);
xmpp_syslog(SDEBUG,"XMPP retry_max_interval: %d", cur_xmpp_con.retry_max_interval);
xmpp_uci_fini();
}
int main(void)
{
xmpp_global_conf();
xmpp_syslog(SINFO,"START XMPP");
xmpp_con_init();
xmpp_connecting();
xmpp_syslog(SINFO,"EXIT XMPP");
xmpp_con_exit();
return 0;
}

68
trx69/xmpp/src/xmpp.h Normal file
View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef _XMPP_H_
#define _XMPP_H_
#include <stdbool.h>
#define XMPP_CR_NS "urn:broadband-forum-org:cwmp:xmppConnReq-1-0"
#define XMPP_ERROR_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
#define DEFAULT_RETRY_INITIAL_INTERVAL 60
#define DEFAULT_RETRY_INTERVAL_MULTIPLIER 2000
#define DEFAULT_RETRY_MAX_INTERVAL 60
#define DEFAULT_XMPP_RECONNECTION_RETRY 5
#ifndef FREE
#define FREE(x) do { if(x) {free(x); x = NULL;} } while (0)
#endif
enum xmpp_cr_error {
XMPP_CR_NO_ERROR = 0,
XMPP_SERVICE_UNAVAILABLE,
XMPP_NOT_AUTHORIZED
};
struct xmpp_config
{
char *xmpp_allowed_jid;
int xmpp_loglevel;
};
struct xmpp_connection
{
char *username;
char *password;
char *domain;
char *resource;
char *serveralgorithm;
char *serveraddress;
int keepalive_interval;
int connect_attempt;
int retry_initial_interval;
int retry_interval_multiplier;
int retry_max_interval;
int port;
bool usetls;
};
extern struct xmpp_config cur_xmpp_conf;
#endif /* _XMPP_H_ */

84
trx69/xmpp/src/xmpp.md Normal file
View file

@ -0,0 +1,84 @@
# README #
In order to reach the devices that are connected behind NAT, the cwmp protocol introduces alternative method of executing Connection Request via NAT based on XMPP. The xmppd is an implementation of XMPP functionality that performs this feature.
## Configuration File ##
The xmppd UCI configuration is located in **'/etc/config/xmpp'**, and contains 3 sections: **xmpp**, **xmpp\_connection** and **xmpp\_connection\_server**.
```
config xmpp 'xmpp'
option enable '0'
option id '0'
option allowed_jid ''
option loglevel '3'
config connection
option enable '0'
option username ''
option password ''
option domain ''
option resource ''
option serveralgorithm 'DNS-SRV'
config connection_server
option con_id '1'
option enable '0'
option port '5222'
```
### xmpp section ###
It defines **the xmpp section configuration**: enable, id, etc... The possible options for **xmpp** section are listed in the table below.
| Name | Type | Description |
| --------------| ------- | --------------------------------------------- |
| `enable` | boolean | If set to **1**, it enables the XMPP feature. |
| `id` | integer | The id of XMPP connection. |
| `allowed_jid` | string | The list of Jabber IDs or addresses that are allowed to initiate an XMPP Connection Request. |
| `loglevel` | integer | The log type to use, by default it is set to **'INFO'**. The possible types are **'EMERG', 'ALERT', 'CRITIC' ,'ERROR', 'WARNING', 'NOTICE', 'INFO' and 'DEBUG'**. |
### connection section ###
It defines the xmpp\_connection section configuration**: enable, connection\_instance, username, etc... The possible options for **xmpp\_connection** section are listed in the table below.
| Name | Type | Description |
| --------------------------- | ------- | ----------------------------------------------------- |
| `con_inst` | integer | The instance number of the XMPP connection. |
| `enable` | boolean | If set to **1**, it enables the XMPP connection. |
| `con_alias` | string | The alias of the XMPP connection. |
| `username` | string | The username of the XMPP connection. |
| `password` | string | The password of the XMPP connection. |
| `domain` | string | The proposed domain-part of the Jabber ID of the XMPP connection. |
| `resource` | string | The proposed resource-part of the Jabber ID of the XMPP connection. |
| `usetls` | boolean | If set to **1**, the CPE will initiate TLS negotiation. |
| `interval` | integer | The number of seconds, that keep alive events are sent by the XMPP connection. |
| `attempt` | string | The number of times that the Connection attempts to connect to a given IP address. |
| `initial_retry_interval` | integer | The maximum first reconnection wait interval in seconds. |
| `retry_interval_multiplier` | integer | The reconnection interval multiplier. |
| `retry_max_interval` | integer | The maximum reconnection wait interval in seconds. |
| `serveralgorithm` | string | The algorithm used when connecting with the XMPP server. Two algorithms are supported: **'DNS-SRV' and 'ServerTable'**. |
### connection_server section ###
It defines **the xmpp\_connection\_server section configuration**: id\_connection, enable, port, etc... The possible options for **xmpp\_connection\_server** section are listed in the table below.
| Name | Type | Description |
| ---------------------------- | ------- | ------------------------------ |
| `con_id` | string | The id XMPP connection to use. |
| `con_srv_inst` | integer | The instance number of the XMPP server. |
| `enable` | boolean | If set to **1**, it enables this XMPP server. |
| `con_srv_alias` | string | The alias of the XMPP connection. |
| `port` | integer | The port of the XMPP connection. |
| `server_address` | string | The server address of the XMPP connection. |
## Dependencies ##
To successfully build xmpp, the following libraries are needed:
| Dependency | Link | License |
| ----------- | --------------------------------------- | -------------- |
| libuci | https://git.openwrt.org/project/uci.git | LGPL 2.1 |
| libstrophe | https://github.com/strophe/libstrophe | GPL 3.0 |

252
trx69/xmpp/src/xuci.c Normal file
View file

@ -0,0 +1,252 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "xuci.h"
#include <string.h>
#include <ctype.h>
static struct uci_context *uci_ctx = NULL;
void xmpp_uci_init(void)
{
uci_ctx = uci_alloc_context();
}
void xmpp_uci_fini(void)
{
if (uci_ctx)
uci_free_context(uci_ctx);
}
static bool xmpp_uci_validate_section(const char *str)
{
if (!*str)
return false;
for (; *str; str++) {
unsigned char c = *str;
if (isalnum(c) || c == '_')
continue;
return false;
}
return true;
}
static int xmpp_uci_init_ptr(struct uci_context *ctx, struct uci_ptr *ptr, const char *package, const char *section, const char *option, const char *value)
{
memset(ptr, 0, sizeof(struct uci_ptr));
/* value */
if (value) {
ptr->value = value;
}
ptr->package = package;
if (!ptr->package)
goto error;
ptr->section = section;
if (!ptr->section) {
ptr->target = UCI_TYPE_PACKAGE;
goto lastval;
}
ptr->option = option;
if (!ptr->option) {
ptr->target = UCI_TYPE_SECTION;
goto lastval;
} else {
ptr->target = UCI_TYPE_OPTION;
}
lastval:
if (ptr->section && !xmpp_uci_validate_section(ptr->section))
ptr->flags |= UCI_LOOKUP_EXTENDED;
return 0;
error:
return -1;
}
struct uci_section *xmpp_uci_walk_section(const char *package, const char *section_type, struct uci_section *prev_section)
{
struct uci_ptr ptr;
struct uci_element *e;
struct uci_section *next_section;
if (section_type == NULL) {
if (prev_section) {
e = &prev_section->e;
if (e->list.next == &prev_section->package->sections)
return NULL;
e = container_of(e->list.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
} else {
if (xmpp_uci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL))
return NULL;
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK)
return NULL;
if (ptr.p->sections.next == &ptr.p->sections)
return NULL;
e = container_of(ptr.p->sections.next, struct uci_element, list);
next_section = uci_to_section(e);
return next_section;
}
} else {
struct uci_list *ul, *shead = NULL;
if (prev_section) {
ul = &prev_section->e.list;
shead = &prev_section->package->sections;
} else {
if (xmpp_uci_init_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL))
return NULL;
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK)
return NULL;
ul = &ptr.p->sections;
shead = &ptr.p->sections;
}
while (ul->next != shead) {
e = container_of(ul->next, struct uci_element, list);
next_section = uci_to_section(e);
if (strcmp(next_section->type, section_type) == 0)
return next_section;
ul = ul->next;
}
return NULL;
}
return NULL;
}
static void xmpp_uci_print_list(struct uci_list *uh, char **val, char *delimiter)
{
struct uci_element *e;
static char buffer[512];
char *buf = buffer;
*buf = '\0';
uci_foreach_element(uh, e) {
if (*buf) {
strcat(buf, delimiter);
strcat(buf, e->name);
} else {
strcpy(buf, e->name);
}
}
*val = buf;
}
static struct uci_element *xmpp_uci_lookup_list(struct uci_list *list, const char *name)
{
struct uci_element *e;
uci_foreach_element(list, e) {
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
static int uci_lookup_ptr_bysection(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *section, const char *option, const char *value)
{
struct uci_element *e;
memset(ptr, 0, sizeof(struct uci_ptr));
ptr->package = section->package->e.name;
ptr->section = section->e.name;
ptr->option = option;
ptr->value = value;
ptr->flags |= UCI_LOOKUP_DONE;
ptr->p = section->package;
ptr->s = section;
if (ptr->option) {
e = xmpp_uci_lookup_list(&ptr->s->options, ptr->option);
if (!e)
return UCI_OK;
ptr->o = uci_to_option(e);
ptr->last = e;
ptr->target = UCI_TYPE_OPTION;
} else {
ptr->last = &ptr->s->e;
ptr->target = UCI_TYPE_SECTION;
}
ptr->flags |= UCI_LOOKUP_COMPLETE;
return UCI_OK;
}
const char *xmpp_uci_get_value_bysection(struct uci_section *section, const char *option)
{
struct uci_ptr ptr;
char *val = "";
if (uci_lookup_ptr_bysection(uci_ctx, &ptr, section, option, NULL) != UCI_OK)
return val;
if (!ptr.o)
return val;
if(ptr.o->type == UCI_TYPE_LIST) {
xmpp_uci_print_list(&ptr.o->v.list, &val, " ");
return val;
}
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}
const char *xmpp_uci_get_value(const char *package, const char *section, const char *option)
{
struct uci_ptr ptr;
char *val = "";
if (!section || !option)
return val;
if (xmpp_uci_init_ptr(uci_ctx, &ptr, package, section, option, NULL))
return val;
if (uci_lookup_ptr(uci_ctx, &ptr, NULL, true) != UCI_OK)
return val;
if (!ptr.o)
return val;
if (ptr.o->v.string)
return ptr.o->v.string;
else
return val;
}

38
trx69/xmpp/src/xuci.h Normal file
View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef _XMPPUCI_H_
#define _XMPPUCI_H_
#include <uci.h>
void xmpp_uci_init(void);
void xmpp_uci_fini(void);
struct uci_section *xmpp_uci_walk_section(const char *package, const char *section_type, struct uci_section *prev_section);
const char *xmpp_uci_get_value_bysection(struct uci_section *section, const char *option);
const char *xmpp_uci_get_value(const char *package, const char *section, const char *option);
#define xmpp_uci_foreach_section(package, section_type, section) \
for (section = xmpp_uci_walk_section(package, section_type, NULL); \
section != NULL; \
section = xmpp_uci_walk_section(package, section_type, section))
#endif /* _XMPPUCI_H_ */