mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2026-01-27 17:37:18 +01:00
move bulkdata, twamp and udpechoserver to their individual repos
This commit is contained in:
parent
88892e5f7c
commit
7d991d89be
44 changed files with 35 additions and 6768 deletions
|
|
@ -10,8 +10,18 @@ include $(TOPDIR)/rules.mk
|
|||
PKG_NAME:=bulkdata
|
||||
PKG_VERSION:=1.0.0
|
||||
|
||||
PKG_SOURCE_VERSION:=4ac9d7a7e90f5de5f43d6bf3d1a844114a46b134
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/bulkdata.git
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
|
|
@ -26,10 +36,6 @@ define Package/$(PKG_NAME)/description
|
|||
BBF BulkData Collection
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-D_GNU_SOURCE
|
||||
|
||||
|
|
@ -41,4 +47,4 @@ define Package/$(PKG_NAME)/install
|
|||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
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 -lbbf_api
|
||||
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)
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
# 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 |
|
||||
|
||||
|
|
@ -1,450 +0,0 @@
|
|||
/*
|
||||
* 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, 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,523 +0,0 @@
|
|||
/*
|
||||
* 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 "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 = bulkdata_get_value_param("Device.DeviceInfo.ManufacturerOUI");
|
||||
bulkdata->device_id.product_class = bulkdata_get_value_param("Device.DeviceInfo.ProductClass");
|
||||
bulkdata->device_id.serial_number = bulkdata_get_value_param("Device.DeviceInfo.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;
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Copyright (C) 2020 iopsys Software Solutions AB
|
||||
* Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
#define __CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#define DEFAULT_AMD_VERSION 2
|
||||
#define DEFAULT_INSTANCE_MODE 0
|
||||
|
||||
typedef struct device_id {
|
||||
char *manufacturer_oui;
|
||||
char *product_class;
|
||||
char *serial_number;
|
||||
} device_id;
|
||||
|
||||
typedef struct profile_parameter {
|
||||
int profile_id;
|
||||
char *name;
|
||||
char *reference;
|
||||
} profile_parameter;
|
||||
|
||||
typedef struct profile_http_request_uri_parameter {
|
||||
int profile_id;
|
||||
char *name;
|
||||
char *reference;
|
||||
} profile_http_request_uri_parameter;
|
||||
|
||||
typedef struct profile {
|
||||
struct uloop_timeout utimer;
|
||||
int profile_id;
|
||||
int nbre_of_retained_failed_reports;
|
||||
int nbre_failed_reports;
|
||||
int reporting_interval;
|
||||
int profile_parameter_number;
|
||||
int profile_http_request_uri_parameter_number;
|
||||
int http_retry_minimum_wait_interval;
|
||||
int http_retry_interval_multiplier;
|
||||
int min_retry;
|
||||
int retry_count;
|
||||
char *protocol;
|
||||
char *encoding_type;
|
||||
char *csv_encoding_field_separator;
|
||||
char *csv_encoding_row_separator;
|
||||
char *csv_encoding_escape_character;
|
||||
char *csv_encoding_report_format;
|
||||
char *csv_encoding_row_time_stamp;
|
||||
char *json_encoding_report_format;
|
||||
char *json_encoding_report_time_stamp;
|
||||
char *http_url;
|
||||
char *http_username;
|
||||
char *http_password;
|
||||
char *http_compression;
|
||||
char *http_method;
|
||||
char *http_ssl_capath;
|
||||
char *new_report;
|
||||
time_t time_reference;
|
||||
time_t next_retry;
|
||||
time_t next_period;
|
||||
bool http_persist_across_reboot;
|
||||
bool http_insecure_enable;
|
||||
bool enable;
|
||||
bool http_use_date_header;
|
||||
bool http_retry_enable;
|
||||
struct profile_parameter *profile_parameter;
|
||||
struct profile_http_request_uri_parameter *profile_http_uri_parameter;
|
||||
struct list_head *failed_reports;
|
||||
} profile;
|
||||
|
||||
typedef struct bulkdata {
|
||||
struct device_id device_id;
|
||||
struct profile *profile;
|
||||
int log_level;
|
||||
int amd_version;
|
||||
unsigned int instance_mode;
|
||||
} bulkdata;
|
||||
|
||||
int bulkdata_config_init(struct bulkdata *bulkdata);
|
||||
int bulkdata_config_fini(struct bulkdata *bulkdata);
|
||||
|
||||
#ifndef FREE
|
||||
#define FREE(x) do { if(x) {free(x); x = NULL;} } while (0)
|
||||
#endif
|
||||
|
||||
#endif //__CONFIG_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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_
|
||||
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* 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, (int) (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;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,336 +0,0 @@
|
|||
/*
|
||||
* 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 *)bbf_api_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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* 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_ */
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
|
@ -10,8 +10,18 @@ include $(TOPDIR)/rules.mk
|
|||
PKG_NAME:=twamp
|
||||
PKG_VERSION:=1.0.0
|
||||
|
||||
PKG_SOURCE_VERSION:=f6e914508aa1f7458f87bd68026f56262595d9a5
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/twamp.git
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
|
|
@ -26,10 +36,6 @@ define Package/$(PKG_NAME)/description
|
|||
BBF twamp feature
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-D_GNU_SOURCE
|
||||
|
||||
|
|
@ -41,4 +47,4 @@ define Package/$(PKG_NAME)/install
|
|||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
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)
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
/* ********** DynamicObj ********** */
|
||||
DM_MAP_OBJ tDynamicObj[] = {
|
||||
/* parentobj, nextobject, parameter */
|
||||
{"Device.IP.Interface.{i}.", tDeviceTWAMPReflectorObj, NULL},
|
||||
{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, 2, s, opt_inst);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static int addObjIPInterfaceTWAMPReflector(char *refparam, struct dmctx *ctx, void *data, char **instance)
|
||||
{
|
||||
struct uci_section *connection = NULL;
|
||||
|
||||
char *last_inst = get_last_instance_with_option("twamp", "twamp_reflector", "interface", section_name((struct uci_section *)data), "twamp_inst");
|
||||
char *id = get_last_id("twamp", "twamp_reflector");
|
||||
|
||||
dmuci_add_section("twamp", "twamp_reflector", &connection);
|
||||
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 uci_section *)data));
|
||||
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;
|
||||
|
||||
switch (del_action) {
|
||||
case DEL_INST:
|
||||
dmuci_delete_by_section((struct uci_section *)data, 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 uci_section *)data)) != 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)
|
||||
{
|
||||
*value = dmuci_get_value_by_section_fallback_def((struct uci_section *)data, "enable", "1");
|
||||
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, NULL))
|
||||
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)
|
||||
{
|
||||
*value = dmuci_get_value_by_section_fallback_def((struct uci_section *)data, "port", "862");
|
||||
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)
|
||||
{
|
||||
*value = dmuci_get_value_by_section_fallback_def((struct uci_section *)data, "max_ttl", "1");
|
||||
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, NULL))
|
||||
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, NULL))
|
||||
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 uci_section *)prev_data), s) {
|
||||
|
||||
inst = handle_update_instance(2, dmctx, &max_inst, update_instance_alias, 3,
|
||||
s, "twamp_inst", "twamp_alias");
|
||||
|
||||
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)s, inst) == DM_STOP)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *** Device.IP.Interface.{i}. *** */
|
||||
DMOBJ tDeviceTWAMPReflectorObj[] = {
|
||||
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
|
||||
{"TWAMPReflector", &DMWRITE, addObjIPInterfaceTWAMPReflector, delObjIPInterfaceTWAMPReflector, NULL, browseIPInterfaceTWAMPReflectorInst, NULL, NULL, NULL, tIPInterfaceTWAMPReflectorParams, NULL, BBFDM_BOTH, LIST_KEY{"Alias", "Port", NULL}},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* *** Device.IP.Interface.{i}.TWAMPReflector.{i}. *** */
|
||||
DMLEAF tIPInterfaceTWAMPReflectorParams[] = {
|
||||
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
|
||||
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterfaceTWAMPReflector_Enable, set_IPInterfaceTWAMPReflector_Enable, BBFDM_BOTH},
|
||||
{"Status", &DMREAD, DMT_STRING, get_IPInterfaceTWAMPReflector_Status, NULL, BBFDM_BOTH},
|
||||
{"Alias", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_Alias, set_IPInterfaceTWAMPReflector_Alias, BBFDM_BOTH},
|
||||
{"Port", &DMWRITE, DMT_UNINT, get_IPInterfaceTWAMPReflector_Port, set_IPInterfaceTWAMPReflector_Port, BBFDM_BOTH},
|
||||
{"MaximumTTL", &DMWRITE, DMT_UNINT, get_IPInterfaceTWAMPReflector_MaximumTTL, set_IPInterfaceTWAMPReflector_MaximumTTL, BBFDM_BOTH},
|
||||
{"IPAllowedList", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_IPAllowedList, set_IPInterfaceTWAMPReflector_IPAllowedList, BBFDM_BOTH},
|
||||
{"PortAllowedList", &DMWRITE, DMT_STRING, get_IPInterfaceTWAMPReflector_PortAllowedList, set_IPInterfaceTWAMPReflector_PortAllowedList, BBFDM_BOTH},
|
||||
{0}
|
||||
};
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -1,858 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# 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 |
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* 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_ */
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
||||
|
|
@ -10,24 +10,30 @@ include $(TOPDIR)/rules.mk
|
|||
PKG_NAME:=udpechoserver
|
||||
PKG_VERSION:=1.0.0
|
||||
|
||||
PKG_SOURCE_VERSION:=a3f0860f4268482f9ec9dad0c67745f615293fca
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/iopsys/udpechoserver.git
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
SUBMENU:=TRx69
|
||||
TITLE:=BBF udp echo server
|
||||
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)/
|
||||
BBF UDP Echo Server
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
|
|
@ -41,4 +47,4 @@ define Package/$(PKG_NAME)/install
|
|||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
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)
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
/* ********** DynamicObj ********** */
|
||||
DM_MAP_OBJ tDynamicObj[] = {
|
||||
/* parentobj, nextobject, parameter */
|
||||
{"Device.IP.Diagnostics.", tDeviceUDPEchoConfigObj, NULL},
|
||||
{0}
|
||||
};
|
||||
|
||||
static int get_IPDiagnosticsUDPEchoConfig_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
*value = dmuci_get_option_value_fallback_def("udpechoserver", "udpechoserver", "enable", "1");
|
||||
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, NULL))
|
||||
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, IPAddress))
|
||||
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)
|
||||
{
|
||||
*value = dmuci_get_option_value_fallback_def("udpechoserver", "udpechoserver", "server_port", "0");
|
||||
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)
|
||||
{
|
||||
*value = dmuci_get_option_value_fallback_def("udpechoserver", "udpechoserver", "plus", "1");
|
||||
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;
|
||||
varstate_get_value_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", "0001-01-01T00:00:00.000000Z");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_IPDiagnosticsUDPEchoConfig_TimeLastPacketReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
*value = udpechoconfig_get("TimeLastPacketReceived", "0001-01-01T00:00:00.000000Z");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *** Device.IP.Diagnostics. *** */
|
||||
DMOBJ tDeviceUDPEchoConfigObj[] = {
|
||||
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
|
||||
{"UDPEchoConfig", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tIPDiagnosticsUDPEchoConfigParams, NULL, BBFDM_BOTH},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* *** Device.IP.Diagnostics.UDPEchoConfig. *** */
|
||||
DMLEAF tIPDiagnosticsUDPEchoConfigParams[] = {
|
||||
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
|
||||
{"Enable", &DMWRITE, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_Enable, set_IPDiagnosticsUDPEchoConfig_Enable, BBFDM_BOTH},
|
||||
{"Interface", &DMWRITE, DMT_STRING, get_IPDiagnosticsUDPEchoConfig_Interface, set_IPDiagnosticsUDPEchoConfig_Interface, BBFDM_BOTH},
|
||||
{"SourceIPAddress", &DMWRITE, DMT_STRING, get_IPDiagnosticsUDPEchoConfig_SourceIPAddress, set_IPDiagnosticsUDPEchoConfig_SourceIPAddress, BBFDM_BOTH},
|
||||
{"UDPPort", &DMWRITE, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_UDPPort, set_IPDiagnosticsUDPEchoConfig_UDPPort, BBFDM_BOTH},
|
||||
{"EchoPlusEnabled", &DMWRITE, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled, set_IPDiagnosticsUDPEchoConfig_EchoPlusEnabled, BBFDM_BOTH},
|
||||
{"EchoPlusSupported", &DMREAD, DMT_BOOL, get_IPDiagnosticsUDPEchoConfig_EchoPlusSupported, NULL, BBFDM_BOTH},
|
||||
{"PacketsReceived", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_PacketsReceived, NULL, BBFDM_BOTH},
|
||||
{"PacketsResponded", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_PacketsResponded, NULL, BBFDM_BOTH},
|
||||
{"BytesReceived", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_BytesReceived, NULL, BBFDM_BOTH},
|
||||
{"BytesResponded", &DMREAD, DMT_UNINT, get_IPDiagnosticsUDPEchoConfig_BytesResponded, NULL, BBFDM_BOTH},
|
||||
{"TimeFirstPacketReceived", &DMREAD, DMT_TIME, get_IPDiagnosticsUDPEchoConfig_TimeFirstPacketReceived, NULL, BBFDM_BOTH},
|
||||
{"TimeLastPacketReceived", &DMREAD, DMT_TIME, get_IPDiagnosticsUDPEchoConfig_TimeLastPacketReceived, NULL, BBFDM_BOTH},
|
||||
{0}
|
||||
};
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* 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_
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
# 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 |
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* 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_ */
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
||||
Loading…
Add table
Reference in a new issue