move bulkdata, twamp and udpechoserver to their individual repos

This commit is contained in:
Sukru Senli 2021-04-20 19:19:26 +02:00
parent 88892e5f7c
commit 7d991d89be
44 changed files with 35 additions and 6768 deletions

View file

@ -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)))

View file

@ -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)

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 |

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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_

View file

@ -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;
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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

View file

@ -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, "&#10;") == 0)
rowseparator = '\n';
else if(strcmp(profile->csv_encoding_row_separator, "&#13;") == 0)
rowseparator = '\r';
if(profile->csv_encoding_field_separator)
separator = profile->csv_encoding_field_separator[0];
get_time_stamp(profile->csv_encoding_row_time_stamp, &timestamp);
/*
* Create header ReportTimestamp,ParameterName,ParameterValue,ParameterType in case of ParameterPerRow
*/
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
if(timestamp == NULL)
asprintf(&str, "ParameterName%cParameterValue%cParameterType%c", separator, separator, rowseparator);
else
asprintf(&str, "ReportTimestamp%cParameterName%cParameterValue%cParameterType%c", separator, separator, separator, rowseparator);
append_string_to_string(str, report);
FREE(str);
if(profile->nbre_of_retained_failed_reports != 0) {
if(profile->nbre_failed_reports >= profile->nbre_of_retained_failed_reports && profile->nbre_of_retained_failed_reports > 0)
add_failed_reports_to_report_csv(profile, report, 1);
else
add_failed_reports_to_report_csv(profile, report, 0);
}
}
if(strcmp(profile->csv_encoding_report_format, "column") == 0 && timestamp != NULL) {
if(profile->nbre_of_retained_failed_reports != 0) {
if(profile->nbre_failed_reports >= profile->nbre_of_retained_failed_reports && profile->nbre_of_retained_failed_reports > 0)
add_failed_reports_to_report_csv(profile, report, 1);
else
add_failed_reports_to_report_csv(profile, report, 0);
}
append_string_to_string("ReportTimestamp", &str1);
append_string_to_string(timestamp, &str2);
}
/*
* Add New reports
*/
profile->new_report = NULL;
for(i = 0; i < profile->profile_parameter_number; i++) {
LIST_HEAD(results_list);
bulkdata_get_value_results(profile->profile_parameter[i].reference, &results_list);
list_for_each_entry(p, &results_list, list) {
paramprofilename = get_bulkdata_profile_parameter_name(profile->profile_parameter[i].reference, profile->profile_parameter[i].name, p->name);
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
type = strstr(p->type, ":");
if(timestamp == NULL)
asprintf(&str, "%s%c%s%c%s%c", paramprofilename, separator, p->data, separator, type+1, rowseparator);
else
asprintf(&str, "%s%c%s%c%s%c%s%c", timestamp, separator, paramprofilename, separator, p->data, separator, type+1, rowseparator);
append_string_to_string(str, report);
append_string_to_string(str, &profile->new_report);
FREE(str);
} else if(strcmp(profile->csv_encoding_report_format, "column") == 0) {
if(str1 == NULL || strlen(str1) <= 0)
asprintf(&str, "%s", paramprofilename);
else
asprintf(&str, "%c%s", separator, paramprofilename);
append_string_to_string(str, &str1);
FREE(str);
if(str2 == NULL || strlen(str2) <= 0)
asprintf(&str, "%s", p->data);
else
asprintf(&str, "%c%s", separator, p->data);
append_string_to_string(str, &str2);
FREE(str);
}
FREE(paramprofilename);
}
bulkdata_free_data_from_list(&results_list);
}
if(strcmp(profile->csv_encoding_report_format, "column") == 0) {
asprintf(&str, "%c", rowseparator);
append_string_to_string(str, &str1);
append_string_to_string(str, &str2);
append_string_to_string(str1, report);
append_string_to_string(str2, report);
append_string_to_string(str1, &profile->new_report);
append_string_to_string(str2, &profile->new_report);
}
FREE(str);
FREE(str1);
FREE(str2);
FREE(timestamp);
return 0;
}
static void create_json_failed_report(struct profile *profile, char **report)
{
add_failed_reports_to_report_json(profile, NULL, report, 0);
}
static void create_csv_failed_report(struct profile *profile, char **report)
{
char rowseparator = '\0', separator = '\0', *timestamp = NULL;
if(strcmp(profile->csv_encoding_row_separator, "&#10;") == 0) {
rowseparator = '\n';
} else if(strcmp(profile->csv_encoding_row_separator, "&#13;") == 0) {
rowseparator = '\r';
}
if(profile->csv_encoding_field_separator)
separator = profile->csv_encoding_field_separator[0];
get_time_stamp(profile->csv_encoding_row_time_stamp, &timestamp);
if(strcmp(profile->csv_encoding_report_format, "row") == 0) {
if(timestamp == NULL)
asprintf(report, "ParameterName%cParameterValue%cParameterType%c", separator, separator, rowseparator);
else
asprintf(report, "ReportTimestamp%cParameterName%cParameterValue%cParameterType%c", separator, separator, separator, rowseparator);
}
add_failed_reports_to_report_csv(profile, report, 0);
}
void create_encoding_bulkdata_report(struct profile *profile, char **report)
{
if(strcasecmp(profile->encoding_type, "json") == 0) {
create_json_bulkdata_report(profile, report);
} else if(strcasecmp(profile->encoding_type, "csv") == 0) {
create_csv_bulkdata_report(profile, report);
}
}
void create_failed_report(struct profile *profile, char **report)
{
if(strcasecmp(profile->encoding_type, "json") == 0) {
create_json_failed_report(profile, report);
} else if(strcasecmp(profile->encoding_type, "csv") == 0) {
create_csv_failed_report(profile, report);
}
}

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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 */

View file

@ -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)))

View file

@ -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)

View file

@ -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}
};

View file

@ -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_

View file

@ -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(&current, NULL);
if (clients[i].status == kTesting) {
uint8_t has_active_test_sessions = 0;
for (j = 0; j < clients[i].sess_no; j++) {
rv = get_actual_shutdown(&current, &clients[i].shutdown_time, &clients[i].sessions[j].req.Timeout);
if (rv > 0) {
has_active_test_sessions = 1;
if (FD_ISSET(clients[i].sessions[j].socket, &tmp_fds)) {
rv = receive_test_message(&clients[i], j);
}
} else {
FD_CLR(clients[i].sessions[j].socket, &read_fds);
close(clients[i].sessions[j].socket);
used_sockets--;
clients[i].sessions[j].socket = -1;
/* print loss result */
twamp_log(SINFO, "Session: %u, FW Lost packets: %u/%u, FW Loss Ratio: %3.2f%%", j, clients[i].sessions[j].fw_lst_msg, clients[i].sessions[j].fw_msg, (float)100 * clients[i].sessions[j].fw_lst_msg / clients[i].sessions[j].fw_msg);
}
}
if (!has_active_test_sessions) {
memset(&clients[i].shutdown_time, 0, sizeof(clients[i].shutdown_time));
clients[i].sess_no = 0;
clients[i].status = kConfigured;
}
}
}
}
return 0;
}
char *get_twamp_reflector_option(char *instance, char *option)
{
struct uci_section *s;
char *v, *twamp_id;
dmuci_foreach_section("twamp", "twamp_reflector", s) {
twamp_id = dmuci_get_value_bysection(s, "id");
if(strcmp(twamp_id, instance) == 0)
{
v = dmuci_get_value_bysection(s, option);
return v;
}
}
v = "";
return v;
}
int twamp_init(void)
{
char *id, *value = NULL;
int a;
value = dmuci_get_value("twamp", "twamp", "log_level");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_twamp_conf.loglevel = a;
}
else
cur_twamp_conf.loglevel = DEFAULT_LOGLEVEL;
twamp_log(SDEBUG,"TWAMP Reflector Log Level:%d", cur_twamp_conf.loglevel);
id = dmuci_get_value("twamp", "twamp", "id");
cur_twamp_conf.enable = atoi(get_twamp_reflector_option(id, "enable"));
cur_twamp_conf.interface = strdup(get_twamp_reflector_option(id, "interface"));
cur_twamp_conf.device = strdup(get_twamp_reflector_option(id, "device"));
cur_twamp_conf.ip_version = atoi(get_twamp_reflector_option(id, "ip_version"));
cur_twamp_conf.port = atoi(get_twamp_reflector_option(id, "port"));
cur_twamp_conf.max_ttl = atoi(get_twamp_reflector_option(id, "max_ttl"));
cur_twamp_conf.ip_list = strdup(get_twamp_reflector_option(id, "ip_list"));
cur_twamp_conf.port_list = strdup(get_twamp_reflector_option(id, "port_list"));
twamp_log(SDEBUG,"TWAMP Reflector Enable: %d", cur_twamp_conf.enable);
twamp_log(SDEBUG,"TWAMP Reflector Interface: %s", cur_twamp_conf.interface);
twamp_log(SDEBUG,"TWAMP Reflector Device: %s", cur_twamp_conf.device);
twamp_log(SDEBUG,"TWAMP Reflector Port: %d", cur_twamp_conf.port);
twamp_log(SDEBUG,"TWAMP Reflector MaximumTTL: %d", cur_twamp_conf.max_ttl);
twamp_log(SDEBUG,"TWAMP Reflector IPAllowedList: %s", cur_twamp_conf.ip_list);
twamp_log(SDEBUG,"TWAMP Reflector PortAllowedList: %s", cur_twamp_conf.port_list);
return 0;
}
void twamp_exit(void)
{
free(cur_twamp_conf.interface);
free(cur_twamp_conf.device);
free(cur_twamp_conf.ip_list);
free(cur_twamp_conf.port_list);
}
int main(int argc, char *argv[])
{
dmuci_init();
twamp_init();
dmuci_fini();
twamp_log(SINFO,"START TWAMP Reflector");
twamp_connect();
twamp_exit();
twamp_log(SINFO,"EXIT TWAMP Reflector");
return 0;
}

View file

@ -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__ */

View file

@ -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 |

View file

@ -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();
}
}

View file

@ -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_ */

View file

@ -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
}

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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)))

View file

@ -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)

View file

@ -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}
};

View file

@ -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_

View file

@ -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), &lt);
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &lt);
sprintf(cur_udpechoserver_result.TimeFirstPacket,"%s.%06ld", s_now, (long) cur_udpechoserver_result.TimeFirstPacketReceived.tv_usec);
(void) localtime_r(&(cur_udpechoserver_result.TimeLastPacketReceived.tv_sec), &lt);
strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &lt);
sprintf(cur_udpechoserver_result.TimeLastPacket,"%s.%06ld", s_now, (long) cur_udpechoserver_result.TimeLastPacketReceived.tv_usec);
if(cur_udpechoserver_conf.plus) {
if(n >= sizeof(struct udpechoserver_plus)) {
plus = (struct udpechoserver_plus *)buf;
plus->TestRespSN = htonl(cur_udpechoserver_result.TestRespSN);
plus->TestRespRecvTimeStamp = htonl(tv_recv.tv_sec*1000000+tv_recv.tv_usec);
plus->TestRespReplyFailureCount = htonl(cur_udpechoserver_result.TestRespReplyFailureCount);
gettimeofday(&tv_reply, NULL);
plus->TestRespReplyTimeStamp = htonl(tv_reply.tv_sec*1000000+tv_reply.tv_usec);
}
}
ret = sendto(sock, buf, n, 0, (struct sockaddr *)&from, fromlen);
if (n != ret) {
cur_udpechoserver_result.TestRespReplyFailureCount++;
udpechoserver_log(SCRIT,"Error in send massage");
}
else {
cur_udpechoserver_result.TestRespSN++;
cur_udpechoserver_result.PacketsResponded++;
cur_udpechoserver_result.BytesResponded+=ret;
}
udpechoserver_log(SINFO,"UDPECHOSERVER sent massage to %s with data size is %d Bytes", str, ret);
set_udpechoserver_stats();
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : PacketsReceived = %d", cur_udpechoserver_result.PacketsReceived);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : PacketsResponded = %d", cur_udpechoserver_result.PacketsResponded);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : BytesReceived = %d", cur_udpechoserver_result.BytesReceived);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : BytesResponded = %d", cur_udpechoserver_result.BytesResponded);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TestRespReplyFailureCount = %d", cur_udpechoserver_result.TestRespReplyFailureCount);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TimeFirstPacketReceived = %s", cur_udpechoserver_result.TimeFirstPacket);
udpechoserver_log(SDEBUG,"UDPECHOSERVER Stats : TimeLastPacketReceived = %s", cur_udpechoserver_result.TimeLastPacket);
}
}
int udpechoserver_init(void)
{
char *value = NULL;
int a;
value = dmuci_get_value("udpechoserver", "udpechoserver", "log_level");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_udpechoserver_conf.loglevel = a;
}
else
cur_udpechoserver_conf.loglevel = DEFAULT_LOGLEVEL;
udpechoserver_log(SDEBUG,"Log Level of UDP ECHO SERVER is :%d", cur_udpechoserver_conf.loglevel);
value = dmuci_get_value("udpechoserver", "udpechoserver", "enable");
if(value != NULL && *value != '\0') {
if ((strcasecmp(value,"true")==0) || (strcmp(value,"1")==0)) {
cur_udpechoserver_conf.enable = true;
udpechoserver_log(SDEBUG,"UDP echo server is Enabled");
}
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "interface");
if(value != NULL && *value != '\0') {
if (cur_udpechoserver_conf.interface != NULL)
free(cur_udpechoserver_conf.interface);
cur_udpechoserver_conf.interface = strdup(value);
udpechoserver_log(SDEBUG,"UDP echo server interface is :%s", cur_udpechoserver_conf.interface);
}
else {
cur_udpechoserver_conf.interface = strdup("");
udpechoserver_log(SDEBUG,"UDP echo server interface is empty");
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "address");
if(value != NULL && *value != '\0') {
if (cur_udpechoserver_conf.address != NULL)
free(cur_udpechoserver_conf.address);
cur_udpechoserver_conf.address = strdup(value);
udpechoserver_log(SDEBUG,"UDP echo server address is :%s", cur_udpechoserver_conf.address);
}
else {
cur_udpechoserver_conf.address = strdup("");
udpechoserver_log(SDEBUG,"UDP echo server address is empty");
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "server_port");
if(value != NULL && *value != '\0') {
a = atoi(value);
cur_udpechoserver_conf.server_port = a;
udpechoserver_log(SDEBUG,"UDP echo server port is :%d", cur_udpechoserver_conf.server_port);
}
value = dmuci_get_value("udpechoserver", "udpechoserver", "plus");
if(value != NULL && *value != '\0') {
if ((strcasecmp(value,"true")==0) || (strcmp(value,"1")==0)) {
cur_udpechoserver_conf.plus = true;
udpechoserver_log(SDEBUG,"UDP echo server plus is Enabled");
}
else {
cur_udpechoserver_conf.plus = false;
udpechoserver_log(SDEBUG,"UDP echo server plus is Disabled");
}
}
return 0;
}
void udpechoserver_exit(void)
{
free(cur_udpechoserver_conf.address);
free(cur_udpechoserver_conf.interface);
}
int main(void)
{
dmuci_init();
udpechoserver_init();
dmuci_fini();
udpechoserver_log(SINFO,"START UDPECHOSERVER");
udpechoserver_connect();
udpechoserver_exit();
udpechoserver_log(SINFO,"EXIT UDPECHOSERVER");
return 0;
}

View file

@ -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__ */

View file

@ -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 |

View file

@ -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();
}
}

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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__ */