mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-25 03:24:14 +08:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1216b27d3 | ||
|
|
b969f9a373 | ||
|
|
52dcdd8c07 | ||
|
|
cb38e87f39 | ||
|
|
bff68b2211 | ||
|
|
e756afd3e3 | ||
|
|
a92443b42c | ||
|
|
8d205a9cfc | ||
|
|
1cc58b55e4 | ||
|
|
b5b5aab523 | ||
|
|
f01bd18ffc | ||
|
|
dcf2c78f55 | ||
|
|
33eb93c5a6 | ||
|
|
883b5bd0fa | ||
|
|
60f201d04f | ||
|
|
fe8857f271 | ||
|
|
94e9f52f74 | ||
|
|
c2f8359a71 | ||
|
|
7bbae7a4ac | ||
|
|
baea8f9b5b | ||
|
|
907e111bf2 | ||
|
|
f4c0f9f4e6 | ||
|
|
9766cea180 | ||
|
|
2284fcabbd | ||
|
|
b0166c10c0 | ||
|
|
e744ed1619 | ||
|
|
37865ab486 | ||
|
|
c401361389 | ||
|
|
f75c6e6be3 | ||
|
|
e658747f5c | ||
|
|
57fe67db18 | ||
|
|
ea0a900205 | ||
|
|
790fe1360d | ||
|
|
a5c45e19bb | ||
|
|
d9d62aa292 | ||
|
|
9972c46622 | ||
|
|
9bd7a6d840 | ||
|
|
d0024fcc39 | ||
|
|
dd22c6ca12 | ||
|
|
78700807de | ||
|
|
3f689b5ed6 | ||
|
|
cfa27af769 | ||
|
|
be5a00249e | ||
|
|
e22739cc8c | ||
|
|
239d1633e1 | ||
|
|
342bec0b25 | ||
|
|
3942b33e1e | ||
|
|
3c3608e87c | ||
|
|
aaf0029500 | ||
|
|
7b80bcf104 | ||
|
|
f419a955f6 | ||
|
|
d644ce7be6 | ||
|
|
887ff49b95 | ||
|
|
9c6533f45f | ||
|
|
08f903e2c1 | ||
|
|
833837cea1 | ||
|
|
dabf83ea1d | ||
|
|
0e6146f105 | ||
|
|
220a4efbf7 | ||
|
|
e5d8d81019 | ||
|
|
70442e891f | ||
|
|
ec021d54f5 | ||
|
|
421e633094 | ||
|
|
068308f052 | ||
|
|
8809a7aba7 | ||
|
|
7575dccbc0 | ||
|
|
871831fdac | ||
|
|
db41d298c3 | ||
|
|
ad372152d5 | ||
|
|
926648b090 | ||
|
|
240bdc5bb8 | ||
|
|
45e0b4f2ed | ||
|
|
332a90d087 | ||
|
|
09937f540a | ||
|
|
af8f27ef65 | ||
|
|
8a799f183b | ||
|
|
2dbf38c06c | ||
|
|
b1fcc12599 | ||
|
|
9041f616e7 | ||
|
|
dc14b18428 | ||
|
|
e41f973953 | ||
|
|
9889814f0b | ||
|
|
2922f211f7 | ||
|
|
22dd43c79e | ||
|
|
478c34d48f | ||
|
|
49f46ef075 | ||
|
|
03dc0ebb72 | ||
|
|
fa44657557 | ||
|
|
42a3ed939a | ||
|
|
6e35356954 | ||
|
|
7978a656a7 | ||
|
|
17322c3cba | ||
|
|
890b627139 | ||
|
|
0ad28b756b | ||
|
|
309c26f4b0 | ||
|
|
df4e11ae30 | ||
|
|
9de4a18a20 | ||
|
|
bfcc877a6a | ||
|
|
d9527a5a1a | ||
|
|
e707922d29 | ||
|
|
cfc2e6d9eb | ||
|
|
0ec3e0eae8 | ||
|
|
38c4c7ded0 | ||
|
|
24a82ab61c | ||
|
|
5977ec807c |
@@ -5,11 +5,11 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libbbfdm
|
||||
PKG_VERSION:=1.9-2020-09-01
|
||||
PKG_VERSION:=2.6-2020-11-13
|
||||
PKG_FIXUP:=autoreconf
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/bbf.git
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=725981c900e0844107a857b53f636ae0a3a92eda
|
||||
PKG_SOURCE_VERSION:=192a2f48b0d066e182e2bbe193a826487c90f11f
|
||||
PKG_RELEASE=$(PKG_SOURCE_VERSION)
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
44
bulkdata/Makefile
Executable file
44
bulkdata/Makefile
Executable 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)))
|
||||
43
bulkdata/files/etc/config/bulkdata
Normal file
43
bulkdata/files/etc/config/bulkdata
Normal 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 ''
|
||||
|
||||
34
bulkdata/files/etc/init.d/bulkdatad
Executable file
34
bulkdata/files/etc/init.d/bulkdatad
Executable 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
|
||||
}
|
||||
23
bulkdata/src/Makefile
Normal file
23
bulkdata/src/Makefile
Normal 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 -fPIC
|
||||
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
bulkdata/src/buci.c
Normal file
265
bulkdata/src/buci.c
Normal 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
bulkdata/src/buci.h
Normal file
31
bulkdata/src/buci.h
Normal 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
|
||||
144
bulkdata/src/bulkdata.c
Normal file
144
bulkdata/src/bulkdata.c
Normal 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;
|
||||
}
|
||||
18
bulkdata/src/bulkdata.h
Normal file
18
bulkdata/src/bulkdata.h
Normal 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 */
|
||||
105
bulkdata/src/bulkdata.md
Normal file
105
bulkdata/src/bulkdata.md
Normal 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
bulkdata/src/common.c
Normal file
450
bulkdata/src/common.c
Normal 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);
|
||||
}
|
||||
}
|
||||
69
bulkdata/src/common.h
Normal file
69
bulkdata/src/common.h
Normal 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
bulkdata/src/config.c
Normal file
524
bulkdata/src/config.c
Normal 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
bulkdata/src/config.h
Normal file
103
bulkdata/src/config.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1294
bulkdata/src/datamodel.c
Normal file
1294
bulkdata/src/datamodel.c
Normal file
File diff suppressed because it is too large
Load Diff
29
bulkdata/src/datamodel.h
Normal file
29
bulkdata/src/datamodel.h
Normal 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
bulkdata/src/http.c
Normal file
196
bulkdata/src/http.c
Normal 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
bulkdata/src/http.h
Normal file
37
bulkdata/src/http.h
Normal 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
bulkdata/src/log.c
Normal file
57
bulkdata/src/log.c
Normal 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
bulkdata/src/log.h
Normal file
28
bulkdata/src/log.h
Normal 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
bulkdata/src/report.c
Normal file
336
bulkdata/src/report.c
Normal 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, " ") == 0)
|
||||
rowseparator = '\n';
|
||||
else if(strcmp(profile->csv_encoding_row_separator, " ") == 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, ×tamp);
|
||||
/*
|
||||
* 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, " ") == 0) {
|
||||
rowseparator = '\n';
|
||||
} else if(strcmp(profile->csv_encoding_row_separator, " ") == 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, ×tamp);
|
||||
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);
|
||||
}
|
||||
}
|
||||
24
bulkdata/src/report.h
Normal file
24
bulkdata/src/report.h
Normal 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_ */
|
||||
62
bulkdata/src/times.c
Normal file
62
bulkdata/src/times.c
Normal 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;
|
||||
}
|
||||
19
bulkdata/src/times.h
Normal file
19
bulkdata/src/times.h
Normal 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 */
|
||||
@@ -7,13 +7,13 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=easy-soc-libs
|
||||
PKG_VERSION:=4.8.6
|
||||
PKG_VERSION:=5.3.15
|
||||
PKG_RELEASE:=1
|
||||
|
||||
LOCAL_DEV=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=7ee57451ac6ed25fd2c1c38e58a508b0362245a7
|
||||
PKG_SOURCE_VERSION:=a2289131c5d1f602568a2e8cac1295442504827f
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/easy-soc-libs.git
|
||||
PKG_MAINTAINER:=Anjan Chanda <anjan.chanda@iopsys.eu>
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)_$(PKG_SOURCE_VERSION).tar.xz
|
||||
@@ -136,6 +136,7 @@ define Build/InstallDev/libeasy
|
||||
$(CP) $(PKG_BUILD_DIR)/libeasy/easy.h $(1)/usr/include/easy/
|
||||
$(CP) $(PKG_BUILD_DIR)/libeasy/event.h $(1)/usr/include/easy/
|
||||
$(CP) $(PKG_BUILD_DIR)/libeasy/utils.h $(1)/usr/include/easy/
|
||||
$(CP) $(PKG_BUILD_DIR)/libeasy/debug.h $(1)/usr/include/easy/
|
||||
$(CP) $(PKG_BUILD_DIR)/libeasy/libeasy*.so* $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
|
||||
@@ -129,6 +129,8 @@ static int canyon_led_probe(struct platform_device *pdev)
|
||||
if (ret < 0) {
|
||||
dev_warn(&pdev->dev, "Could not read led-count property\n");
|
||||
leds->led_count = SK9822_DEFAULT_NUM_LEDS;
|
||||
} else {
|
||||
printk(KERN_INFO "Got led count: %u\n", leds->led_count);
|
||||
}
|
||||
|
||||
leds->led_colors = devm_kzalloc(&pdev->dev,
|
||||
@@ -140,7 +142,7 @@ static int canyon_led_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, leds);
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 16, 0)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
|
||||
leds->clock_gpio = gpiod_get_index(&pdev->dev, "led", 0);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
|
||||
leds->clock_gpio = gpiod_get_index(&pdev->dev, "led", 0, GPIOD_OUT_HIGH);
|
||||
@@ -148,6 +150,12 @@ static int canyon_led_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "Kernel version Not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
if (IS_ERR(leds->clock_gpio)) {
|
||||
dev_err(&pdev->dev, "Failed to acquire clock GPIO %ld\n",
|
||||
PTR_ERR(leds->clock_gpio));
|
||||
leds->clock_gpio = NULL;
|
||||
return PTR_ERR(leds->clock_gpio);
|
||||
}
|
||||
|
||||
gpiod_direction_output(leds->clock_gpio, 1);
|
||||
if (IS_ERR(leds->clock_gpio)) {
|
||||
@@ -160,7 +168,7 @@ static int canyon_led_probe(struct platform_device *pdev)
|
||||
gpiod_set_value(leds->clock_gpio, 0);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 16, 0)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
|
||||
leds->data_gpio = gpiod_get_index(&pdev->dev, "led", 1);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
|
||||
leds->data_gpio = gpiod_get_index(&pdev->dev, "led", 1, GPIOD_OUT_HIGH);
|
||||
@@ -168,6 +176,12 @@ static int canyon_led_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "Kernel version Not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
if (IS_ERR(leds->data_gpio)) {
|
||||
dev_err(&pdev->dev, "Failed to acquire data GPIO %ld\n",
|
||||
PTR_ERR(leds->data_gpio));
|
||||
leds->data_gpio = NULL;
|
||||
return PTR_ERR(leds->data_gpio);
|
||||
}
|
||||
|
||||
gpiod_direction_output(leds->data_gpio, 1);
|
||||
if (IS_ERR(leds->data_gpio)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
171
icwmp/Makefile
171
icwmp/Makefile
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2019 iopsys Software Solutions AB
|
||||
# Copyright (C) 2020 iopsys Software Solutions AB
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
@@ -8,11 +8,11 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=icwmp
|
||||
PKG_VERSION:=4.0-2020-07-07
|
||||
PKG_VERSION:=5.0-2020-10-13
|
||||
PKG_FIXUP:=autoreconf
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/icwmp.git
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=03a438d926247ac3eff5df1c5eeea3d6d2babd2b
|
||||
PKG_SOURCE_VERSION:=85ffabf799f769f9100068e887ed4f5ca8286620
|
||||
PKG_RELEASE=$(PKG_SOURCE_VERSION)
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
@@ -26,80 +26,33 @@ PKG_CONFIG_DEPENDS:= \
|
||||
CONFIG_CWMP_DEBUG \
|
||||
CONFIG_CWMP_DEVEL_DEBUG
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
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_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
|
||||
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/Default
|
||||
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 +libbbfdm +libbbf_api
|
||||
DEPENDS:=+libuci +libmicroxml +libubox +jshn +libubus +libblobmsg-json +libpthread +ubusd +shflags +getopt +zlib +libjson-c +libopenssl +curl +libcurl +libbbfdm +libbbf_api
|
||||
endef
|
||||
|
||||
define Package/icwmp/description
|
||||
define Package/$(PKG_NAME)/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
|
||||
define Package/$(PKG_NAME)/config
|
||||
source "$(SOURCE)/Config_cwmp.in"
|
||||
endef
|
||||
|
||||
@@ -118,31 +71,6 @@ 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
|
||||
|
||||
ifneq ($(CWMP_REVISION)_,_)
|
||||
ifneq ($(CWMP_REVISION),exported)
|
||||
ifneq ($(CWMP_REVISION),Unversioned directory)
|
||||
@@ -159,17 +87,6 @@ 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 += \
|
||||
@@ -191,17 +108,10 @@ CONFIGURE_ARGS += \
|
||||
--enable-devel
|
||||
endif
|
||||
|
||||
define Package/icwmp-$(BUILD_VARIANT)/install
|
||||
define Package/$(PKG_NAME)/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
|
||||
@@ -217,55 +127,8 @@ define Package/icwmp-tr098/install
|
||||
$(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-$(BUILD_VARIANT)/postinst
|
||||
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
|
||||
@@ -273,7 +136,7 @@ define Package/icwmp-$(BUILD_VARIANT)/postinst
|
||||
exit 0
|
||||
endef
|
||||
|
||||
define Package/icwmp-$(BUILD_VARIANT)/prerm
|
||||
define Package/$(PKG_NAME)/prerm
|
||||
#!/bin/sh
|
||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||
echo "Disabling rc.d symlink for icwmpd"
|
||||
@@ -282,11 +145,5 @@ define Package/icwmp-$(BUILD_VARIANT)/prerm
|
||||
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,$(PKG_NAME)))
|
||||
$(eval $(call BuildPackage,icwmp-tr098))
|
||||
#$(eval $(call BuildPackage,icwmp-zstream))
|
||||
|
||||
47
icwmp/files/etc/config/cwmp
Normal file
47
icwmp/files/etc/config/cwmp
Normal file
@@ -0,0 +1,47 @@
|
||||
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 periodic_notify_enable '1'
|
||||
option periodic_notify_interval '10'
|
||||
|
||||
config lwn 'lwn'
|
||||
option enable '1'
|
||||
option hostname ''
|
||||
option port '0'
|
||||
21
icwmp/files/etc/firewall.cwmp
Normal file
21
icwmp/files/etc/firewall.cwmp
Normal 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"
|
||||
224
icwmp/files/etc/init.d/icwmpd
Executable file
224
icwmp/files/etc/init.d/icwmpd
Executable file
@@ -0,0 +1,224 @@
|
||||
#!/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"
|
||||
|
||||
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
|
||||
procd_set_param command "$PROG"
|
||||
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
|
||||
#[ "$(uci -q get cwmp.cpe.notification)" == "1" ] && procd_append_param command "/usr/sbin/icwmp_notifd"
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# Copy defaults by the factory to the cwmp UCI user section.
|
||||
|
||||
|
||||
@@ -15,10 +14,19 @@ mac=$(printf "%12.12X" $((0x$baseMac)))
|
||||
# Get system serial number.
|
||||
serial=$(db -q get hw.board.serial_number)
|
||||
|
||||
uci -q batch <<-EOF
|
||||
set cwmp.acs.userid="${mac:0:6}-${serial}"
|
||||
set cwmp.cpe.userid="${mac:0:6}-${serial}"
|
||||
EOF
|
||||
# Get userid values
|
||||
acs_userid=$(uci -q get cwmp.acs.userid)
|
||||
cpe_userid=$(uci -q get cwmp.cpe.userid)
|
||||
|
||||
# Only set if they are empty
|
||||
if [ -z "$acs_userid" ]
|
||||
then
|
||||
uci -q set cwmp.acs.userid="${mac:0:6}-${serial}"
|
||||
fi
|
||||
|
||||
if [ -z "$cpe_userid" ]
|
||||
then
|
||||
uci -q set cwmp.cpe.userid="${mac:0:6}-${serial}"
|
||||
fi
|
||||
|
||||
# No need for commit here, it is done by uci_apply_defaults().
|
||||
|
||||
12
icwmp/files/etc/uci-defaults/90-cwmpfirewall
Normal file
12
icwmp/files/etc/uci-defaults/90-cwmpfirewall
Normal 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
|
||||
|
||||
1
icwmp/files/lib/upgrade/keep.d/icwmp
Normal file
1
icwmp/files/lib/upgrade/keep.d/icwmp
Normal file
@@ -0,0 +1 @@
|
||||
/etc/icwmpd/.icwmpd_backup_session.xml
|
||||
29
icwmp/files/lib/upgrade/post-rootfs-fixup.d/215-icwmp
Executable file
29
icwmp/files/lib/upgrade/post-rootfs-fixup.d/215-icwmp
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
# As part of sysupgrade we copy CWMP Backup Session XML file.
|
||||
|
||||
|
||||
# Abort on any error.
|
||||
set -e
|
||||
|
||||
|
||||
# Do nothing if user want to discard old settings.
|
||||
if [ -n "$SAVE_CONFIG" ] && [ $SAVE_CONFIG -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Source functions.
|
||||
for f in /lib/upgrade/iopsys*.sh; do
|
||||
[ -r "$f" -a -s "$f" ] || continue
|
||||
source $f
|
||||
done
|
||||
|
||||
|
||||
if [ -s "${2}/etc/icwmpd/.icwmpd_backup_session.xml" ]; then
|
||||
cat "${2}/etc/icwmpd/.icwmpd_backup_session.xml" > "/etc/icwmpd/.icwmpd_backup_session.xml"
|
||||
fi
|
||||
|
||||
# Report success.
|
||||
log "post-hooks" "CWMP Backup Session XML file migrated"
|
||||
|
||||
exit 0
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ieee1905
|
||||
PKG_VERSION:=2.0.26
|
||||
PKG_VERSION:=2.1.0
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=b7840dfe0b57cbdd47ec3167cbc7d2a6f9e41fd4
|
||||
PKG_SOURCE_VERSION:=5b712be46aadc0b54cdb72464150ab58892e9c3f
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/ieee1905.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
endif
|
||||
|
||||
@@ -5,7 +5,7 @@ config ieee1905 'ieee1905'
|
||||
option macaddress 'auto'
|
||||
option registrar 0
|
||||
option cmdu_event 1
|
||||
option map_plugin 0
|
||||
option map_plugin 1
|
||||
|
||||
config security 'security'
|
||||
list method 'PBC'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
START=96
|
||||
STOP=22
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/ieee1905d
|
||||
@@ -43,6 +43,10 @@ configure_network()
|
||||
fi
|
||||
}
|
||||
|
||||
service_running() {
|
||||
ubus -t 2 wait_for wifi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
|
||||
[ -d /usr/lib/ieee1905 ] || mkdir -p /usr/lib/ieee1905
|
||||
@@ -64,5 +68,5 @@ service_triggers() {
|
||||
procd_add_reload_trigger "network"
|
||||
procd_add_reload_trigger "wireless"
|
||||
procd_add_reload_trigger "netmode"
|
||||
procd_add_config_trigger "config.change" "ieee1905" /etc/init.d/ieee1905 restart
|
||||
procd_add_reload_trigger "ieee1905"
|
||||
}
|
||||
|
||||
13
iop/config
13
iop/config
@@ -7,6 +7,9 @@ CONFIG_PACKAGE_6in4=y
|
||||
CONFIG_PACKAGE_6rd=y
|
||||
CONFIG_PACKAGE_6to4=y
|
||||
CONFIG_PACKAGE_alsa-lib=y
|
||||
CONFIG_PACKAGE_atftp=m
|
||||
CONFIG_PACKAGE_atftpd=m
|
||||
CONFIG_PACKAGE_bulkdata=y
|
||||
CONFIG_PACKAGE_chat=y
|
||||
CONFIG_PACKAGE_comgt=y
|
||||
CONFIG_PACKAGE_comgt-directip=y
|
||||
@@ -14,11 +17,9 @@ CONFIG_PACKAGE_comgt-ncm=y
|
||||
CONFIG_PACKAGE_ddns-scripts=y
|
||||
CONFIG_PACKAGE_ds-lite=y
|
||||
CONFIG_PACKAGE_ethtool=y
|
||||
CONFIG_PACKAGE_fatrace=y
|
||||
CONFIG_PACKAGE_gdb=m
|
||||
CONFIG_PACKAGE_getopt=y
|
||||
CONFIG_PACKAGE_glib2=y
|
||||
CONFIG_PACKAGE_icwmp-curl=y
|
||||
CONFIG_PACKAGE_icwmp=y
|
||||
CONFIG_PACKAGE_ieee1905=y
|
||||
CONFIG_PACKAGE_map-1905=y
|
||||
CONFIG_PACKAGE_map-topology=y
|
||||
@@ -48,10 +49,11 @@ CONFIG_PACKAGE_juci-theme-iopsys=y
|
||||
CONFIG_PACKAGE_libdaemon=y
|
||||
CONFIG_PACKAGE_libgmp=y
|
||||
CONFIG_PACKAGE_libreadline=y
|
||||
CONFIG_PACKAGE_libreswan=m
|
||||
CONFIG_PACKAGE_libwifi=y
|
||||
CONFIG_PACKAGE_loop-detector=m
|
||||
CONFIG_PACKAGE_lscpu=y
|
||||
CONFIG_PACKAGE_map=y
|
||||
CONFIG_PACKAGE_miniupnpc=y
|
||||
CONFIG_PACKAGE_miniupnpd=y
|
||||
CONFIG_PACKAGE_mwan3=y
|
||||
CONFIG_PACKAGE_nand-utils=y
|
||||
@@ -83,8 +85,8 @@ CONFIG_PACKAGE_relayd=y
|
||||
CONFIG_PACKAGE_resolveip=y
|
||||
CONFIG_PACKAGE_rpcd=y
|
||||
CONFIG_PACKAGE_rpcd-mod-rpcsys=y
|
||||
CONFIG_PACKAGE_rsync=y
|
||||
CONFIG_PACKAGE_rulengd=y
|
||||
CONFIG_PACKAGE_rsync=y
|
||||
CONFIG_PACKAGE_strace=y
|
||||
CONFIG_PACKAGE_swmodd=m
|
||||
CONFIG_PACKAGE_tc=y
|
||||
@@ -96,6 +98,7 @@ CONFIG_PACKAGE_uqmi=y
|
||||
CONFIG_PACKAGE_usb-modeswitch=y
|
||||
CONFIG_PACKAGE_usbreset=y
|
||||
CONFIG_PACKAGE_uspd=y
|
||||
CONFIG_PACKAGE_vsftpd-tls=m
|
||||
CONFIG_PACKAGE_wget=y
|
||||
CONFIG_PACKAGE_wwan=y
|
||||
CONFIG_PACKAGE_wifimngr=y
|
||||
|
||||
@@ -188,10 +188,10 @@ function genconfig {
|
||||
bcmAllowed=0
|
||||
endptAllowed=0
|
||||
natalieAllowed=0
|
||||
mediatekAllowed=0
|
||||
#mediatekAllowed=0
|
||||
|
||||
git ls-remote git@dev.iopsys.eu:broadcom/bcmcreator.git -q 2>/dev/null && bcmAllowed=1
|
||||
git ls-remote git@dev.iopsys.eu:mediatek/linux.git -q 2>/dev/null && mediatekAllowed=1
|
||||
#git ls-remote git@dev.iopsys.eu:mediatek/linux.git -q 2>/dev/null && mediatekAllowed=1
|
||||
git ls-remote git@dev.iopsys.eu:dialog/natalie-dect-12.26.git -q 2>/dev/null && natalieAllowed=1
|
||||
git ls-remote git@dev.iopsys.eu:iopsys/endptmngr.git -q 2>/dev/null && endptAllowed=1
|
||||
}
|
||||
@@ -411,13 +411,13 @@ function genconfig {
|
||||
[ $bcmAllowed -eq 0 ] && echo "CONFIG_BCM_OPEN=y" >> .config
|
||||
[ $endptAllowed -eq 0 ] && echo "CONFIG_ENDPT_OPEN=y" >> .config
|
||||
[ $natalieAllowed -eq 0 ] && echo "CONFIG_NATALIE_OPEN=y" >> .config
|
||||
[ $mediatekAllowed -eq 0 ] && echo "CONFIG_MEDIATEK_OPEN=y" >> .config
|
||||
#[ $mediatekAllowed -eq 0 ] && echo "CONFIG_MEDIATEK_OPEN=y" >> .config
|
||||
else
|
||||
echo "# CONFIG_GITMIRROR_REWRITE is not set" >>.config
|
||||
echo "CONFIG_BCM_OPEN=y" >> .config
|
||||
echo "CONFIG_ENDPT_OPEN=y" >> .config
|
||||
echo "CONFIG_NATALIE_OPEN=y" >> .config
|
||||
echo "CONFIG_MEDIATEK_OPEN=y" >> .config
|
||||
#echo "CONFIG_MEDIATEK_OPEN=y" >> .config
|
||||
fi
|
||||
|
||||
if [ -n "$BRCM_MAX_JOBS" ]
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# shellcheck disable=SC2029
|
||||
|
||||
build_bcmkernel_consumer() {
|
||||
local tarfile bcmkernelcommith sdkversion
|
||||
sdkversion=$(grep "CONFIG_BRCM_SDK_VER.*=y" .config | awk -F'[_,=]' '{print$5}')
|
||||
sdkversion=${sdkversion:0:4}${sdkversion:(-1)}
|
||||
bcmkernelcommith=$(grep -w "PKG_SOURCE_VERSION:" $curdir/feeds/broadcom/bcmkernel/${sdkversion:0:5}*.mk | cut -d'=' -f2)
|
||||
local tarfile bcmkernelcommith sdkversion serverpath serverlink
|
||||
|
||||
sdkversion="$(grep "CONFIG_BRCM_SDK_VER.*=y" .config | awk -F'[_,=]' '{print$5}')"
|
||||
sdkversion="${sdkversion:0:4}${sdkversion:(-1)}"
|
||||
bcmkernelcommith="$(grep -w "PKG_SOURCE_VERSION:" "$curdir/feeds/broadcom/bcmkernel/${sdkversion:0:5}"*".mk" | cut -d'=' -f2)"
|
||||
|
||||
[ -n "$board" ] && [ -n "$bcmkernelcommith" ] || return
|
||||
|
||||
serverpath="$FPATH/bcmopen-$board-$bcmkernelcommith.tar.gz"
|
||||
serverlink="$FPATH/bcmopen-$board-$majver.$minver-latest"
|
||||
|
||||
# do not build bcmopen sdk if it was already built before
|
||||
[ -n "$board" -a -n "$bcmkernelcommith" ] || return
|
||||
ssh $SERVER "test -f $FPATH/bcmopen-$board-$bcmkernelcommith.tar.gz" && return
|
||||
cd ./build_dir/target-*/bcmkernel-*-${sdkversion:0:4}*/bcm963xx/release
|
||||
bash do_consumer_release -p $profile -y -F
|
||||
# if it was, check if there's a symlink in place and create it if missing
|
||||
ssh "$SERVER" "test -f '$serverpath' && { test -L '$serverlink' || ln -sf '$serverpath' '$serverlink'; }" && return
|
||||
|
||||
cd "./build_dir/target-"*"/bcmkernel-"*"-${sdkversion:0:4}"*"/bcm963xx/release"
|
||||
bash do_consumer_release -p "$profile" -y -F
|
||||
|
||||
tarfile='out/bcm963xx_*_consumer.tar.gz'
|
||||
[ $(ls -1 $tarfile |wc -l) -ne 1 ] && echo "Too many tar files: '$tarfile'" && return
|
||||
scp -pv $tarfile $SERVER:$FPATH/bcmopen-$board-$bcmkernelcommith.tar.gz
|
||||
ssh $SERVER "[ -f $FPATH/bcmopen-$board-$bcmkernelcommith.tar.gz ] && ln -sf $FPATH/bcmopen-$board-$bcmkernelcommith.tar.gz $FPATH/bcmopen-$board-$majver.$minver-latest"
|
||||
[ $(ls -1 $tarfile | wc -l) -ne 1 ] && echo "Too many tar files: '$tarfile'" && return
|
||||
|
||||
scp -pv $tarfile "$SERVER":"$serverpath"
|
||||
ssh "$SERVER" "test -f '$serverpath' && ln -sf '$serverpath' '$serverlink'"
|
||||
rm -f $tarfile
|
||||
|
||||
cd "$curdir"
|
||||
}
|
||||
|
||||
@@ -25,7 +36,7 @@ build_natalie_consumer() {
|
||||
grep -q "CONFIG_TARGET_NO_DECT=y" .config && return
|
||||
natalieversion=$(grep -w "PKG_VERSION:" ./feeds/iopsys/natalie-dect/Makefile | cut -d'=' -f2)
|
||||
nataliecommith=$(grep -w "PKG_SOURCE_VERSION:" ./feeds/iopsys/natalie-dect/Makefile | cut -d'=' -f2)
|
||||
[ -n "$profile" -a -n "$natalieversion" -a -n "$nataliecommith" ] || return
|
||||
[ -n "$profile" ] && [ -n "$natalieversion" ] && [ -n "$nataliecommith" ] || return
|
||||
ssh $SERVER "test -f $FPATH/natalie-dect-$profile-$natalieversion-$nataliecommith.tar.gz" && return
|
||||
cd ./build_dir/target-*/natalie-dect-$natalieversion/
|
||||
mkdir natalie-dect-open-$natalieversion
|
||||
@@ -44,7 +55,7 @@ build_endptmngr_consumer() {
|
||||
grep -q "CONFIG_TARGET_NO_VOICE=y" .config && return
|
||||
endptversion=$(grep -w "PKG_VERSION:" ./feeds/iopsys/endptmngr/Makefile | cut -d'=' -f2)
|
||||
endptcommith=$(grep -w "PKG_SOURCE_VERSION:" ./feeds/iopsys/endptmngr/Makefile | cut -d'=' -f2)
|
||||
[ -n "$profile" -a -n "$endptversion" -a -n "$endptcommith" ] || return
|
||||
[ -n "$profile" ] && [ -n "$endptversion" ] && [ -n "$endptcommith" ] || return
|
||||
ssh $SERVER "test -f $FPATH/endptmngr-$profile-$endptversion-$endptcommith.tar.gz" && return
|
||||
cd ./build_dir/target-*/endptmngr-$endptversion/
|
||||
mkdir endptmngr-open-$endptversion
|
||||
@@ -85,7 +96,7 @@ build_mediatek_wifi_consumer() {
|
||||
|
||||
ver=$(grep -w "PKG_VERSION:" ./feeds/mediatek/mt${chip}/Makefile | cut -d'=' -f2)
|
||||
commit=$(grep -w "PKG_SOURCE_VERSION:" ./feeds/mediatek/mt${chip}/Makefile | cut -d'=' -f2)
|
||||
[ -n "$ver" -a -n "$commit" ] || return
|
||||
[ -n "$ver" ] && [ -n "$commit" ] || return
|
||||
ssh $SERVER "test -f $FPATH/mtk${chip}e-${ver}_${commit}.tar.xz" && return
|
||||
cd build_dir/target-mipsel_1004kc*/linux-iopsys-ramips*/mtk${chip}e-$ver/ipkg-*
|
||||
mkdir -p mtk${chip}e-$ver/src
|
||||
@@ -112,7 +123,6 @@ function generate_tarballs {
|
||||
set -e
|
||||
git remote -v | grep -q http && return # do not continue if this is an open SDK environment
|
||||
|
||||
target=$(grep CONFIG_TARGET_BOARD .config | cut -d'=' -f2 | tr -d '"')
|
||||
board=$(grep CONFIG_TARGET_FAMILY .config | cut -d'=' -f2 | tr -d '"')
|
||||
profile=$(grep CONFIG_BCM_KERNEL_PROFILE .config | cut -d'=' -f2 | tr -d '"')
|
||||
majver=$(grep CONFIG_TARGET_VERSION .config | cut -d'=' -f2 | tr -d '"' | cut -f1 -d .)
|
||||
@@ -137,7 +147,7 @@ function generate_tarballs {
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -n "$stk_target" ]; then
|
||||
if [ -z "$stk_target" ]; then
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=map-1905
|
||||
PKG_VERSION:=0.0.10
|
||||
PKG_VERSION:=0.0.13
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=7cdc6d38674bef2c6003a4b60b63be8a789421be
|
||||
PKG_SOURCE_VERSION:=31e30c589cb2a522fb66261640ff1a5f8ff58ac4
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/map-1905.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
endif
|
||||
|
||||
@@ -6,11 +6,11 @@ include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=map-topology
|
||||
PKG_VERSION:=1.4.2
|
||||
PKG_VERSION:=1.5.6
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_VERSION:=e22b79c1a1ec443fcc816439658fb4e8cedf21fb
|
||||
PKG_SOURCE_VERSION:=8d008ef712227986263e9a2a1b188ce3a27e17f8
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/map-topology.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz
|
||||
@@ -23,7 +23,8 @@ include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/map-topology
|
||||
CATEGORY:=Utilities
|
||||
DEPENDS:=+libubox +ubus +libpthread +libuci +libeasy +libieee1905
|
||||
DEPENDS:=+libubox +ubus +libpthread +libuci +libeasy \
|
||||
+libieee1905 +libavahi-nodbus-support
|
||||
TITLE:=Utility to build topology of a multi-AP network
|
||||
endef
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
config topology
|
||||
config topology 'topology'
|
||||
option enabled '1'
|
||||
option depth '8'
|
||||
option interval '60'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=14
|
||||
STOP=96
|
||||
START=97
|
||||
STOP=21
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=obuspa
|
||||
PKG_VERSION:=2.0.21
|
||||
PKG_VERSION:=2.0.22
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=25b920c13160360de16300a8d04a068feb197c50
|
||||
PKG_SOURCE_VERSION:=06270b7a45b77d6d883eb244e6d854efce10f17f
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/fork/obuspa.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
endif
|
||||
|
||||
@@ -5,6 +5,8 @@ IP_RULE=""
|
||||
BR_RULE=""
|
||||
is_bcm968=0
|
||||
|
||||
POLICER_COUNT=0
|
||||
|
||||
#function to handle a queue section
|
||||
handle_queue() {
|
||||
qid="$1" #queue section ID
|
||||
@@ -69,6 +71,31 @@ handle_queue() {
|
||||
echo $q_no > /tmp/qos/queue_stats/$ifname/q_idx
|
||||
}
|
||||
|
||||
#function to handle a policer section
|
||||
handle_policer() {
|
||||
local p_sec="$1" # policer section ID
|
||||
local dir=1 # default direction, upstream
|
||||
|
||||
config_get is_enable "$p_sec" "enable"
|
||||
|
||||
#no need to configure disabled policer
|
||||
if [ $is_enable == '0' ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
config_get cir "$p_sec" "committed_rate"
|
||||
config_get cbs "$p_sec" "committed_burst_size" -1
|
||||
config_get ebs "$p_sec" "excess_burst_size" 0
|
||||
config_get pir "$p_sec" "peak_rate" 0
|
||||
config_get pbs "$p_sec" "peak_burst_size" 0
|
||||
config_get meter "$p_sec" "meter_type" 0
|
||||
|
||||
# Call tmctl which is a broadcomm command to configure policer.
|
||||
tmctl createpolicer --dir $dir --pid $POLICER_COUNT --ptype $meter --cir $cir --cbs $cbs --ebs $ebs --pir $pir --pbs $pbs
|
||||
|
||||
POLICER_COUNT=$((POLICER_COUNT + 1))
|
||||
}
|
||||
|
||||
#function to handle a shaper section
|
||||
handle_shaper() {
|
||||
sid="$1" #queue section ID
|
||||
@@ -177,6 +204,8 @@ broute_append_rule() {
|
||||
handle_ebtables_rules() {
|
||||
sid=$1
|
||||
local is_l2_rule=0
|
||||
local src_dhcp_options=""
|
||||
local dst_dhcp_options=""
|
||||
|
||||
init_broute_rule
|
||||
|
||||
@@ -186,6 +215,14 @@ handle_ebtables_rules() {
|
||||
config_get pcp_check "$sid" "pcp_check"
|
||||
config_get eth_type "$sid" "ethertype"
|
||||
config_get vid "$sid" "vid_check"
|
||||
config_get dhcp_type "$sid" "dhcp_type" # dhcpv4 or v6
|
||||
config_get src_vcid "$sid" "src_vendor_class_id" # dhcp option 60
|
||||
config_get dst_vcid "$sid" "dst_vendor_class_id" # dhcp option 60
|
||||
config_get src_clid "$sid" "src_client_id" # dhcp option 61
|
||||
config_get dst_clid "$sid" "dst_client_id" # dhcp option 61
|
||||
config_get src_ucid "$sid" "src_user_class_id" # dhcp option 77
|
||||
config_get dst_ucid "$sid" "dst_user_class_id" # dhcp option 77
|
||||
|
||||
config_get traffic_class "$sid" "traffic_class"
|
||||
|
||||
if [ -n "$src_if" ]; then
|
||||
@@ -223,6 +260,70 @@ handle_ebtables_rules() {
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# first process options that will help figure our source mac address
|
||||
# dhcp option for "vendor class id"
|
||||
if [ -n "$src_vcid" ]; then
|
||||
src_dhcp_options="$src_dhcp_options vcid=$src_vcid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# dhcp option for "client id"
|
||||
if [ -n "$src_clid" ]; then
|
||||
src_dhcp_options="$src_dhcp_options clid=$src_clid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# dhcp option for "user class id"
|
||||
if [ -n "$src_ucid" ]; then
|
||||
src_dhcp_options="$src_dhcp_options ucid=$src_ucid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# if src mac is already a classification criteria, then it
|
||||
# does not really make sense to add it as a criteria to
|
||||
# filter packets again based on source mac
|
||||
if [ -n "$src_dhcp_options" -a -z "$src_mac" ]; then
|
||||
comp="$(grep -i "$src_dhcp_options" /tmp/dhcp.client.options)"
|
||||
if [ -n "$comp" ]; then
|
||||
s_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')"
|
||||
if [ -n "$s_mac_add" ]; then
|
||||
broute_filter_on_src_mac $s_mac_add
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Now process options that will help figure our destination mac address
|
||||
# dhcp option for "vendor class id"
|
||||
if [ -n "$dst_vcid" ]; then
|
||||
dst_dhcp_options="$dst_dhcp_options vcid=$dst_vcid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# dhcp option for "client id"
|
||||
if [ -n "$dst_clid" ]; then
|
||||
dst_dhcp_options="$dst_dhcp_options clid=$dst_clid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# dhcp option for "user class id"
|
||||
if [ -n "$dst_ucid" ]; then
|
||||
dst_dhcp_options="$dst_dhcp_options ucid=$dst_ucid"
|
||||
is_l2_rule=1
|
||||
fi
|
||||
|
||||
# if dst mac is already a classification criteria, then it
|
||||
# does not really make sense to add it as a criteria to
|
||||
# filter packets again based on dstination mac
|
||||
if [ -n "$dst_dhcp_options" -a -z "$dst_mac" ]; then
|
||||
comp="$(grep -i "$dst_dhcp_options" /tmp/dhcp.client.options)"
|
||||
if [ -n "$comp" ]; then
|
||||
d_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')"
|
||||
if [ -n "$d_mac_add" ]; then
|
||||
broute_filter_on_dst_mac $d_mac_add
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $is_l2_rule -eq 0 ]; then
|
||||
return
|
||||
fi
|
||||
@@ -422,6 +523,107 @@ handle_iptables_rules() {
|
||||
fi
|
||||
}
|
||||
|
||||
assign_policer_to_port() {
|
||||
local ifname="$1"
|
||||
local pindex="$2"
|
||||
local portorder="$(db -q get hw.board.ethernetPortOrder)"
|
||||
local wanport="$(db -q get hw.board.ethernetWanPort)"
|
||||
|
||||
local i=0
|
||||
for port in $portorder; do
|
||||
if [ "$ifname" == "$port" ]; then
|
||||
if [ "$wanport" == "$port" ]; then
|
||||
bs /b/configure port/index=wan0 ingress_rate_limit={traffic_types=8,policer={policer/dir=us,index=$pindex}}
|
||||
else
|
||||
bs /b/configure port/index=lan$i ingress_rate_limit={traffic_types=8,policer={policer/dir=us,index=$pindex}}
|
||||
fi
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
}
|
||||
|
||||
handle_policer_rules() {
|
||||
local c_sec=$1
|
||||
local policer_name
|
||||
local ifname
|
||||
local pname
|
||||
local pindex=-1
|
||||
local ingress_rate=0
|
||||
local in_burst_size=0
|
||||
|
||||
config_get policer_name "$c_sec" "policer"
|
||||
if [ -z "$policer_name" ];then
|
||||
# no need to apply policer if policer not present in this
|
||||
# classification rule
|
||||
return
|
||||
fi
|
||||
|
||||
config_get ifname "$c_sec" "ifname"
|
||||
if [ -z "$ifname" ]; then
|
||||
# cannot associate policer as interface is not mentioned
|
||||
return
|
||||
fi
|
||||
|
||||
local i=0
|
||||
local max_policer_inst=$(cat /tmp/qos/max_policer_inst)
|
||||
while :
|
||||
do
|
||||
if [ $i -eq $max_policer_inst ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
pname="$(uci -q get qos.@policer[$i].name)"
|
||||
if [ "$policer_name" == "$pname" ]; then
|
||||
pindex=$i
|
||||
ingress_rate=$(uci -q get qos.@policer[$i].committed_rate)
|
||||
in_burst_rate=$(uci -q get qos.@policer[$i].committed_burst_size)
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
if [ $pindex -lt 0 ]; then
|
||||
# policer not found, no need to proceed further
|
||||
return
|
||||
fi
|
||||
|
||||
# The policer object is not available on non BCM968* chips
|
||||
if [ $is_bcm968 -eq 1 ]; then
|
||||
assign_policer_to_port $ifname $pindex
|
||||
else
|
||||
config_ingress_rate_limit $ifname $ingress_rate $in_burst_size
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
config_ingress_rate_limit() {
|
||||
local ifname="$1"
|
||||
local ingress_rate=$2
|
||||
local in_burst_size=$3
|
||||
local wanport="$(db -q get hw.board.ethernetWanPort)"
|
||||
|
||||
if [ "$ifname" == "$wanport" ]; then
|
||||
logger -t qosmngr "policing is not support on port $ifname"
|
||||
return
|
||||
fi
|
||||
|
||||
# Unit in uci file is in bps while that accepted by ethswctl is kbits
|
||||
if [ $ingress_rate -lt 1000 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
ingress_rate=$((ingress_rate / 1000))
|
||||
|
||||
if [ $in_burst_size -eq 0 ]; then
|
||||
in_burst_size=$ingress_rate
|
||||
else
|
||||
in_burst_size=$((in_burst_size / 1000))
|
||||
fi
|
||||
|
||||
ethswctl -c rxratectrl -n 1 -p $ifname -x $ingress_rate -y $in_burst_size
|
||||
}
|
||||
|
||||
#function to handle a classify section
|
||||
handle_classify() {
|
||||
cid="$1" #classify section ID
|
||||
@@ -434,6 +636,7 @@ handle_classify() {
|
||||
|
||||
handle_ebtables_rules $cid
|
||||
handle_iptables_rules $cid
|
||||
handle_policer_rules $cid
|
||||
}
|
||||
|
||||
configure_shaper() {
|
||||
@@ -496,14 +699,56 @@ configure_queue() {
|
||||
config_foreach handle_queue queue
|
||||
}
|
||||
|
||||
configure_policer() {
|
||||
# The policer object is not available on non BCM968* chips, just clean up
|
||||
# the old config if any and return
|
||||
if [ $is_bcm968 -eq 0 ]; then
|
||||
for intf in $(db get hw.board.ethernetPortOrder); do
|
||||
# setting rate and burst size to 0 disables rate limiting
|
||||
ethswctl -c rxratectrl -n 1 -p $intf -x 0 -y 0
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
# Delete policer
|
||||
local i=0
|
||||
local max_p_inst=0
|
||||
if [ -f "/tmp/qos/max_policer_inst" ]; then
|
||||
max_p_inst=$(cat /tmp/qos/max_policer_inst)
|
||||
fi
|
||||
|
||||
while :
|
||||
do
|
||||
if [ $i -eq $max_p_inst ]; then
|
||||
break
|
||||
fi
|
||||
tmctl deletepolicer --dir 1 --pid $i &>/dev/null
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
# reset the policer counter
|
||||
echo 0 > /tmp/qos/max_policer_inst
|
||||
# Load UCI file
|
||||
config_load qos
|
||||
config_foreach handle_policer policer
|
||||
echo $POLICER_COUNT > /tmp/qos/max_policer_inst
|
||||
}
|
||||
|
||||
configure_qos() {
|
||||
configure_queue
|
||||
configure_shaper
|
||||
configure_policer
|
||||
configure_classify
|
||||
}
|
||||
|
||||
reload_qos() {
|
||||
local service_name="$1"
|
||||
local cpu_model="$(grep Hardware /proc/cpuinfo | awk '{print$NF}')"
|
||||
|
||||
case $cpu_model in
|
||||
BCM968*) is_bcm968=1 ;;
|
||||
esac
|
||||
|
||||
if [ -z "$service_name" ]; then
|
||||
configure_qos
|
||||
elif [ "$service_name" == "shaper" ]; then
|
||||
@@ -512,6 +757,8 @@ reload_qos() {
|
||||
configure_queue
|
||||
elif [ "$service_name" == "classify" ]; then
|
||||
configure_classify
|
||||
elif [ "$service_name" == "policer" ]; then
|
||||
configure_policer
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -728,6 +975,7 @@ read_queue_stats() {
|
||||
itf="$1"
|
||||
q_idx="$2"
|
||||
local cpu_model="$(grep Hardware /proc/cpuinfo | awk '{print$NF}')"
|
||||
|
||||
case $cpu_model in
|
||||
BCM968*) is_bcm968=1 ;;
|
||||
esac
|
||||
|
||||
@@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=questd
|
||||
PKG_VERSION:=5.1.11
|
||||
PKG_VERSION:=5.1.12
|
||||
|
||||
PKG_SOURCE_VERSION:=6b64dafae52d0cd28a31d364aeed80380875293b
|
||||
PKG_SOURCE_VERSION:=5de841c629d13bd8a5b90081343aaf5b75afc7c2
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/questd
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ PKG_INSTALL:=1
|
||||
|
||||
PKG_SOURCE_PROTO=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/rulengd.git
|
||||
PKG_SOURCE_VERSION:=ad94ccdd82b22527ab32937ca1a2682030319ed3
|
||||
PKG_SOURCE_VERSION:=d7a4d24e653ea0dc02679e24f8ec3c817aafdb37
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=${PKG_NAME}-${PKG_VERSION}
|
||||
PKG_INSTALL:=1
|
||||
|
||||
42
stun/Makefile
Executable file
42
stun/Makefile
Executable 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)))
|
||||
14
stun/files/etc/config/stun
Normal file
14
stun/files/etc/config/stun
Normal 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'
|
||||
|
||||
36
stun/files/etc/init.d/stund
Executable file
36
stun/files/etc/init.d/stund
Executable 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
stun/src/Makefile
Normal file
16
stun/src/Makefile
Normal 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
stun/src/config.c
Normal file
297
stun/src/config.c
Normal 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
stun/src/config.h
Normal file
44
stun/src/config.h
Normal 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
stun/src/log.c
Normal file
68
stun/src/log.c
Normal 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
stun/src/log.h
Normal file
20
stun/src/log.h
Normal 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
stun/src/stun.c
Normal file
687
stun/src/stun.c
Normal 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
stun/src/stun.h
Normal file
106
stun/src/stun.h
Normal 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
stun/src/stun.md
Normal file
49
stun/src/stun.md
Normal 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
stun/src/ubus.c
Normal file
108
stun/src/ubus.c
Normal 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
stun/src/ubus.h
Normal file
22
stun/src/ubus.h
Normal 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
twamp/Makefile
Executable file
44
twamp/Makefile
Executable 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)))
|
||||
13
twamp/files/etc/config/twamp
Normal file
13
twamp/files/etc/config/twamp
Normal 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'
|
||||
|
||||
56
twamp/files/etc/init.d/twampd
Executable file
56
twamp/files/etc/init.d/twampd
Executable 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
twamp/src/Makefile
Normal file
23
twamp/src/Makefile
Normal 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 -fPIC
|
||||
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
twamp/src/datamodel.c
Normal file
309
twamp/src/datamodel.c
Normal 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}
|
||||
};
|
||||
17
twamp/src/datamodel.h
Normal file
17
twamp/src/datamodel.h
Normal 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
twamp/src/twamp.c
Normal file
858
twamp/src/twamp.c
Normal 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(¤t, 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(¤t, &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
twamp/src/twamp.h
Normal file
237
twamp/src/twamp.h
Normal 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
twamp/src/twamp.md
Normal file
57
twamp/src/twamp.md
Normal 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 |
|
||||
|
||||
55
twamp/src/twamplog.c
Normal file
55
twamp/src/twamplog.c
Normal 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();
|
||||
}
|
||||
}
|
||||
27
twamp/src/twamplog.h
Normal file
27
twamp/src/twamplog.h
Normal 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_ */
|
||||
195
twamp/src/twamptimestamp.c
Normal file
195
twamp/src/twamptimestamp.c
Normal 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
twamp/src/twampuci.c
Normal file
266
twamp/src/twampuci.c
Normal 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;
|
||||
}
|
||||
34
twamp/src/twampuci.h
Normal file
34
twamp/src/twampuci.h
Normal 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
udpechoserver/Makefile
Executable file
44
udpechoserver/Makefile
Executable 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)))
|
||||
10
udpechoserver/files/etc/config/udpechoserver
Normal file
10
udpechoserver/files/etc/config/udpechoserver
Normal 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'
|
||||
|
||||
39
udpechoserver/files/etc/init.d/udpechoserverd
Executable file
39
udpechoserver/files/etc/init.d/udpechoserverd
Executable 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
|
||||
}
|
||||
23
udpechoserver/src/Makefile
Normal file
23
udpechoserver/src/Makefile
Normal 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 -fPIC
|
||||
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)
|
||||
213
udpechoserver/src/datamodel.c
Normal file
213
udpechoserver/src/datamodel.c
Normal 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}
|
||||
};
|
||||
17
udpechoserver/src/datamodel.h
Normal file
17
udpechoserver/src/datamodel.h
Normal 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_
|
||||
230
udpechoserver/src/udpechoserver.c
Normal file
230
udpechoserver/src/udpechoserver.c
Normal 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), <);
|
||||
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", <);
|
||||
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), <);
|
||||
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", <);
|
||||
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;
|
||||
}
|
||||
56
udpechoserver/src/udpechoserver.h
Normal file
56
udpechoserver/src/udpechoserver.h
Normal 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__ */
|
||||
40
udpechoserver/src/udpechoserver.md
Normal file
40
udpechoserver/src/udpechoserver.md
Normal 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 |
|
||||
|
||||
55
udpechoserver/src/udpechoserverlog.c
Normal file
55
udpechoserver/src/udpechoserverlog.c
Normal 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();
|
||||
}
|
||||
}
|
||||
27
udpechoserver/src/udpechoserverlog.h
Normal file
27
udpechoserver/src/udpechoserverlog.h
Normal 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_ */
|
||||
182
udpechoserver/src/udpechoserveruci.c
Normal file
182
udpechoserver/src/udpechoserveruci.c
Normal 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;
|
||||
}
|
||||
25
udpechoserver/src/udpechoserveruci.h
Normal file
25
udpechoserver/src/udpechoserveruci.h
Normal 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__ */
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=uspd
|
||||
PKG_VERSION:=2.0.3
|
||||
PKG_VERSION:=2.0.6
|
||||
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=8045528fcfd44216b357fe671acc40847866bdc4
|
||||
PKG_SOURCE_VERSION:=f4cb4407c275befee87cf761a51687d3b9869e99
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/uspd.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
endif
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wfadatad
|
||||
PKG_VERSION:=2.2.2
|
||||
PKG_VERSION:=2.3.1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=b6d42b7459248e9fbbce9d828f50360e67bfe6c6
|
||||
PKG_SOURCE_VERSION:=bdcf390f970facc20d7ac51fbb59ac4912b7fbfc
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/wfadatad.git
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ STOP=11
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/wfadatad
|
||||
|
||||
service_running() {
|
||||
ubus -t 2 wait_for ieee1905
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command ${PROG}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wifimngr
|
||||
PKG_VERSION:=7.8.0
|
||||
PKG_VERSION:=8.2.9
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_VERSION:=a87fb7c82800d4d731205eee2ff956886353c086
|
||||
PKG_SOURCE_VERSION:=ff879d814caba6807f64288de22db6f78c03438c
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/wifimngr.git
|
||||
PKG_MAINTAINER:=Anjan Chanda <anjan.chanda@iopsys.eu>
|
||||
|
||||
|
||||
44
xmpp/Makefile
Executable file
44
xmpp/Makefile
Executable 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)))
|
||||
29
xmpp/files/etc/config/xmpp
Normal file
29
xmpp/files/etc/config/xmpp
Normal 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 ''
|
||||
|
||||
@@ -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 &
|
||||
|
||||
42
xmpp/files/etc/init.d/xmppd
Executable file
42
xmpp/files/etc/init.d/xmppd
Executable 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
xmpp/src/Makefile
Normal file
23
xmpp/src/Makefile
Normal 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 -fPIC
|
||||
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
xmpp/src/cmd.c
Normal file
78
xmpp/src/cmd.c
Normal 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
xmpp/src/cmd.h
Normal file
34
xmpp/src/cmd.h
Normal 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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user