mirror of
https://github.com/rtbrick/bngblaster.git
synced 2024-05-06 15:54:57 +00:00
1693 lines
63 KiB
C
1693 lines
63 KiB
C
/*
|
|
* BNG Blaster (BBL) - Control Socket
|
|
*
|
|
* Christian Giese, January 2021
|
|
*
|
|
* Copyright (C) 2020-2022, RtBrick, Inc.
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "bbl.h"
|
|
#include "bbl_ctrl.h"
|
|
#include "bbl_session.h"
|
|
#include "bbl_stream.h"
|
|
#include "bbl_dhcp.h"
|
|
#include "bbl_dhcpv6.h"
|
|
|
|
#define BACKLOG 4
|
|
|
|
extern volatile bool g_teardown;
|
|
extern volatile bool g_teardown_request;
|
|
|
|
typedef ssize_t callback_function(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments);
|
|
|
|
static char *
|
|
string_or_na(char *string) {
|
|
if(string) {
|
|
return string;
|
|
} else {
|
|
return "N/A";
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_status(int fd, const char *status, uint32_t code, const char *message) {
|
|
ssize_t result = 0;
|
|
json_t *root = json_pack("{sssiss*}", "status", status, "code", code, "message", message);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_multicast_traffic_start(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ctx->multicast_traffic = true;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_multicast_traffic_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ctx->multicast_traffic = false;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_traffic_stats(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root = json_pack("{ss si s{si si}}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"session-traffic",
|
|
"total-flows", ctx->stats.session_traffic_flows,
|
|
"verified-flows", ctx->stats.session_traffic_flows_verified);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_traffic(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
if(session_id) {
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
session->session_traffic = status;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(session) {
|
|
session->session_traffic = status;
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_traffic_start(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_traffic(fd, ctx, session_id, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_traffic_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_traffic(fd, ctx, session_id, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_igmp_join(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments) {
|
|
bbl_session_s *session;
|
|
const char *s;
|
|
uint32_t group_address = 0;
|
|
uint32_t source1 = 0;
|
|
uint32_t source2 = 0;
|
|
uint32_t source3 = 0;
|
|
bbl_igmp_group_s *group = NULL;
|
|
int i;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:s}", "group", &s) == 0) {
|
|
if(!inet_pton(AF_INET, s, &group_address)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid group address");
|
|
}
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing group address");
|
|
}
|
|
if (json_unpack(arguments, "{s:s}", "source1", &s) == 0) {
|
|
if(!inet_pton(AF_INET, s, &source1)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid source1 address");
|
|
}
|
|
}
|
|
if (json_unpack(arguments, "{s:s}", "source2", &s) == 0) {
|
|
if(!inet_pton(AF_INET, s, &source2)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid source2 address");
|
|
}
|
|
}
|
|
if (json_unpack(arguments, "{s:s}", "source3", &s) == 0) {
|
|
if(!inet_pton(AF_INET, s, &source3)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid source3 address");
|
|
}
|
|
}
|
|
|
|
/* Search session */
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
/* Search for free slot ... */
|
|
for(i=0; i < IGMP_MAX_GROUPS; i++) {
|
|
if(!session->igmp_groups[i].zapping) {
|
|
if (session->igmp_groups[i].group == group_address) {
|
|
group = &session->igmp_groups[i];
|
|
if(group->state == IGMP_GROUP_IDLE) {
|
|
break;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 409, "group already exists");
|
|
}
|
|
} else if(session->igmp_groups[i].state == IGMP_GROUP_IDLE) {
|
|
group = &session->igmp_groups[i];
|
|
}
|
|
}
|
|
}
|
|
if(!group) {
|
|
return bbl_ctrl_status(fd, "error", 409, "no igmp group slot available");
|
|
}
|
|
|
|
memset(group, 0x0, sizeof(bbl_igmp_group_s));
|
|
group->group = group_address;
|
|
if(source1) group->source[0] = source1;
|
|
if(source2) group->source[1] = source2;
|
|
if(source3) group->source[2] = source3;
|
|
group->state = IGMP_GROUP_JOINING;
|
|
group->robustness_count = session->igmp_robustness;
|
|
group->send = true;
|
|
session->send_requests |= BBL_SEND_IGMP;
|
|
bbl_session_tx_qnode_insert(session);
|
|
LOG(IGMP, "IGMP (ID: %u) join %s\n",
|
|
session->session_id, format_ipv4_address(&group->group));
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_igmp_leave(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments) {
|
|
|
|
bbl_session_s *session;
|
|
const char *s;
|
|
uint32_t group_address = 0;
|
|
bbl_igmp_group_s *group = NULL;
|
|
int i;
|
|
|
|
if(session_id == 0) {
|
|
/* session-id is mandatory */
|
|
return bbl_ctrl_status(fd, "error", 400, "missing session-id");
|
|
}
|
|
if (json_unpack(arguments, "{s:s}", "group", &s) == 0) {
|
|
if(!inet_pton(AF_INET, s, &group_address)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid group address");
|
|
}
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing group address");
|
|
}
|
|
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
/* Search for group ... */
|
|
for(i=0; i < IGMP_MAX_GROUPS; i++) {
|
|
if (session->igmp_groups[i].group == group_address) {
|
|
group = &session->igmp_groups[i];
|
|
break;
|
|
}
|
|
}
|
|
if(!group) {
|
|
return bbl_ctrl_status(fd, "warning", 404, "group not found");
|
|
}
|
|
if(group->zapping) {
|
|
return bbl_ctrl_status(fd, "error", 408, "group used by zapping test");
|
|
}
|
|
if(group->state <= IGMP_GROUP_LEAVING) {
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
group->state = IGMP_GROUP_LEAVING;
|
|
group->robustness_count = session->igmp_robustness;
|
|
group->send = true;
|
|
group->leave_tx_time.tv_sec = 0;
|
|
group->leave_tx_time.tv_nsec = 0;
|
|
group->last_mc_rx_time.tv_sec = 0;
|
|
group->last_mc_rx_time.tv_nsec = 0;
|
|
session->send_requests |= BBL_SEND_IGMP;
|
|
bbl_session_tx_qnode_insert(session);
|
|
LOG(IGMP, "IGMP (ID: %u) leave %s\n",
|
|
session->session_id, format_ipv4_address(&group->group));
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_igmp_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root, *groups, *record, *sources;
|
|
bbl_session_s *session = NULL;
|
|
bbl_igmp_group_s *group = NULL;
|
|
uint32_t delay = 0;
|
|
uint32_t ms;
|
|
|
|
struct timespec time_diff;
|
|
int i, i2;
|
|
|
|
if(session_id == 0) {
|
|
/* session-id is mandatory */
|
|
return bbl_ctrl_status(fd, "error", 400, "missing session-id");
|
|
}
|
|
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
groups = json_array();
|
|
/* Add group informations */
|
|
for(i=0; i < IGMP_MAX_GROUPS; i++) {
|
|
group = &session->igmp_groups[i];
|
|
if(group->group) {
|
|
sources = json_array();
|
|
for(i2=0; i2 < IGMP_MAX_SOURCES; i2++) {
|
|
if(group->source[i2]) {
|
|
json_array_append(sources, json_string(format_ipv4_address(&group->source[i2])));
|
|
}
|
|
}
|
|
record = json_pack("{ss so si si}",
|
|
"group", format_ipv4_address(&group->group),
|
|
"sources", sources,
|
|
"packets", group->packets,
|
|
"loss", group->loss);
|
|
|
|
switch (group->state) {
|
|
case IGMP_GROUP_IDLE:
|
|
json_object_set(record, "state", json_string("idle"));
|
|
if(group->last_mc_rx_time.tv_sec && group->leave_tx_time.tv_sec) {
|
|
timespec_sub(&time_diff, &group->last_mc_rx_time, &group->leave_tx_time);
|
|
ms = time_diff.tv_nsec / 1000000; /* convert nanoseconds to milliseconds */
|
|
if(time_diff.tv_nsec % 1000000) ms++; /* simple roundup function */
|
|
delay = (time_diff.tv_sec * 1000) + ms;
|
|
json_object_set(record, "leave-delay-ms", json_integer(delay));
|
|
}
|
|
break;
|
|
case IGMP_GROUP_LEAVING:
|
|
json_object_set(record, "state", json_string("leaving"));
|
|
break;
|
|
case IGMP_GROUP_ACTIVE:
|
|
json_object_set(record, "state", json_string("active"));
|
|
if(group->first_mc_rx_time.tv_sec) {
|
|
timespec_sub(&time_diff, &group->first_mc_rx_time, &group->join_tx_time);
|
|
ms = time_diff.tv_nsec / 1000000; /* convert nanoseconds to milliseconds */
|
|
if(time_diff.tv_nsec % 1000000) ms++; /* simple roundup function */
|
|
delay = (time_diff.tv_sec * 1000) + ms;
|
|
json_object_set(record, "join-delay-ms", json_integer(delay));
|
|
}
|
|
break;
|
|
case IGMP_GROUP_JOINING:
|
|
json_object_set(record, "state", json_string("joining"));
|
|
if(group->first_mc_rx_time.tv_sec) {
|
|
timespec_sub(&time_diff, &group->first_mc_rx_time, &group->join_tx_time);
|
|
ms = time_diff.tv_nsec / 1000000; /* convert nanoseconds to milliseconds */
|
|
if(time_diff.tv_nsec % 1000000) ms++; /* simple roundup function */
|
|
delay = (time_diff.tv_sec * 1000) + ms;
|
|
json_object_set(record, "join-delay-ms", json_integer(delay));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
json_array_append(groups, record);
|
|
}
|
|
}
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"igmp-groups", groups);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(groups);
|
|
}
|
|
return result;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_counters(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root = json_pack("{ss si s{si si si si}}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"session-counters",
|
|
"sessions", ctx->config.sessions,
|
|
"sessions-established", ctx->sessions_established_max,
|
|
"sessions-flapped", ctx->sessions_flapped,
|
|
"dhcpv6-sessions-established", ctx->dhcpv6_established_max);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root;
|
|
json_t *session_json;
|
|
bbl_session_s *session;
|
|
|
|
if(session_id == 0) {
|
|
/* session-id is mandatory */
|
|
return bbl_ctrl_status(fd, "error", 400, "missing session-id");
|
|
}
|
|
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
session_json = bbl_session_json(session);
|
|
if(!session_json) {
|
|
bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
}
|
|
|
|
root = json_pack("{ss si so*}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"session-info", session_json);
|
|
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(session_json);
|
|
}
|
|
return result;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
static json_t *
|
|
bbl_ctrl_interfaces_json(bbl_interface_s *interace, const char *type) {
|
|
return json_pack("{ss si ss si si si si si si si si}",
|
|
"name", interace->name,
|
|
"ifindex", interace->ifindex,
|
|
"type", type,
|
|
"tx-packets", interace->stats.packets_tx,
|
|
"tx-bytes", interace->stats.bytes_tx,
|
|
"tx-pps", interace->stats.rate_packets_tx.avg,
|
|
"tx-kbps", interace->stats.rate_bytes_tx.avg * 8 / 1000,
|
|
"rx-packets", interace->stats.packets_rx,
|
|
"rx-bytes", interace->stats.bytes_rx,
|
|
"rx-pps", interace->stats.rate_packets_rx.avg,
|
|
"rx-kbps", interace->stats.rate_bytes_rx.avg * 8 / 1000);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_interfaces(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root, *interfaces, *interface;
|
|
int i;
|
|
|
|
interfaces = json_array();
|
|
for(i=0; i < ctx->interfaces.access_if_count; i++) {
|
|
interface = bbl_ctrl_interfaces_json(ctx->interfaces.access_if[i], "access");
|
|
json_array_append(interfaces, interface);
|
|
}
|
|
for(i=0; i < ctx->interfaces.network_if_count; i++) {
|
|
interface = bbl_ctrl_interfaces_json(ctx->interfaces.network_if[i], "network");
|
|
json_array_append(interfaces, interface);
|
|
}
|
|
for(i=0; i < ctx->interfaces.a10nsp_if_count; i++) {
|
|
interface = bbl_ctrl_interfaces_json(ctx->interfaces.a10nsp_if[i], "a10nsp");
|
|
json_array_append(interfaces, interface);
|
|
}
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"interfaces", interfaces);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(interfaces);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_terminate(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
bbl_session_s *session;
|
|
if(session_id) {
|
|
/* Terminate single matching session ... */
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
bbl_session_clear(ctx, session);
|
|
return bbl_ctrl_status(fd, "ok", 200, "terminate session");
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Terminate all sessions ... */
|
|
g_teardown = true;
|
|
g_teardown_request = true;
|
|
LOG_NOARG(INFO, "Teardown request\n");
|
|
return bbl_ctrl_status(fd, "ok", 200, "terminate all sessions");
|
|
}
|
|
}
|
|
|
|
static void
|
|
bbl_ctrl_session_ncp_open(bbl_session_s *session, bool ipcp) {
|
|
if(session->session_state == BBL_ESTABLISHED ||
|
|
session->session_state == BBL_PPP_NETWORK) {
|
|
if(ipcp) {
|
|
if(session->ipcp_state == BBL_PPP_CLOSED) {
|
|
session->ipcp_state = BBL_PPP_INIT;
|
|
session->ipcp_request_code = PPP_CODE_CONF_REQUEST;
|
|
session->send_requests |= BBL_SEND_IPCP_REQUEST;
|
|
bbl_session_tx_qnode_insert(session);
|
|
}
|
|
} else {
|
|
/* ip6cp */
|
|
if(session->ip6cp_state == BBL_PPP_CLOSED) {
|
|
session->ip6cp_state = BBL_PPP_INIT;
|
|
session->ip6cp_request_code = PPP_CODE_CONF_REQUEST;
|
|
session->send_requests |= BBL_SEND_IP6CP_REQUEST;
|
|
bbl_session_tx_qnode_insert(session);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
bbl_ctrl_session_ncp_close(bbl_session_s *session, bool ipcp) {
|
|
if(session->session_state == BBL_ESTABLISHED ||
|
|
session->session_state == BBL_PPP_NETWORK) {
|
|
if(ipcp) {
|
|
if(session->ipcp_state == BBL_PPP_OPENED) {
|
|
session->ipcp_state = BBL_PPP_TERMINATE;
|
|
session->ipcp_request_code = PPP_CODE_TERM_REQUEST;
|
|
session->send_requests |= BBL_SEND_IPCP_REQUEST;
|
|
session->ip_address = 0;
|
|
session->peer_ip_address = 0;
|
|
session->dns1 = 0;
|
|
session->dns2 = 0;
|
|
bbl_session_tx_qnode_insert(session);
|
|
}
|
|
} else { /* ip6cp */
|
|
if(session->ip6cp_state == BBL_PPP_OPENED) {
|
|
session->ip6cp_state = BBL_PPP_TERMINATE;
|
|
session->ip6cp_request_code = PPP_CODE_TERM_REQUEST;
|
|
session->send_requests |= BBL_SEND_IP6CP_REQUEST;
|
|
/* Stop IPv6 */
|
|
session->ipv6_prefix.len = 0;
|
|
session->icmpv6_ra_received = false;
|
|
memset(session->ipv6_dns1, 0x0, IPV6_ADDR_LEN);
|
|
memset(session->ipv6_dns2, 0x0, IPV6_ADDR_LEN);
|
|
/* Stop DHCPv6 */
|
|
bbl_dhcpv6_stop(session);
|
|
bbl_session_tx_qnode_insert(session);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_ncp_open_close(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool open, bool ipcp) {
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
if(session_id) {
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
if(session->access_type == ACCESS_TYPE_PPPOE) {
|
|
if(open) {
|
|
bbl_ctrl_session_ncp_open(session, ipcp);
|
|
} else {
|
|
bbl_ctrl_session_ncp_close(session, ipcp);
|
|
}
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 400, "matching session is not of type pppoe");
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(session) {
|
|
if(session->access_type == ACCESS_TYPE_PPPOE) {
|
|
if(open) {
|
|
bbl_ctrl_session_ncp_open(session, ipcp);
|
|
} else {
|
|
bbl_ctrl_session_ncp_close(session, ipcp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_ipcp_open(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_ncp_open_close(fd, ctx, session_id, true, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_ipcp_close(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_ncp_open_close(fd, ctx, session_id, false, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_ip6cp_open(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_ncp_open_close(fd, ctx, session_id, true, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_ip6cp_close(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_session_ncp_open_close(fd, ctx, session_id, false, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root, *flows, *flow;
|
|
bbl_li_flow_t *li_flow;
|
|
struct dict_itor *itor;
|
|
|
|
flows = json_array();
|
|
itor = dict_itor_new(ctx->li_flow_dict);
|
|
dict_itor_first(itor);
|
|
for (; dict_itor_valid(itor); dict_itor_next(itor)) {
|
|
li_flow = (bbl_li_flow_t*)*dict_itor_datum(itor);
|
|
if(li_flow) {
|
|
flow = json_pack("{ss si ss si ss ss ss si si si si si si si si si si si si}",
|
|
"source-address", format_ipv4_address(&li_flow->src_ipv4),
|
|
"source-port", li_flow->src_port,
|
|
"destination-address", format_ipv4_address(&li_flow->dst_ipv4),
|
|
"destination-port", li_flow->dst_port,
|
|
"direction", bbl_li_direction_string(li_flow->direction),
|
|
"packet-type", bbl_li_packet_type_string(li_flow->packet_type),
|
|
"sub-packet-type", bbl_li_sub_packet_type_string(li_flow->sub_packet_type),
|
|
"liid", li_flow->liid,
|
|
"bytes-rx", li_flow->bytes_rx,
|
|
"packets-rx", li_flow->packets_rx,
|
|
"packets-rx-ipv4", li_flow->packets_rx_ipv4,
|
|
"packets-rx-ipv4-tcp", li_flow->packets_rx_ipv4_tcp,
|
|
"packets-rx-ipv4-udp", li_flow->packets_rx_ipv4_udp,
|
|
"packets-rx-ipv4-host-internal", li_flow->packets_rx_ipv4_internal,
|
|
"packets-rx-ipv6", li_flow->packets_rx_ipv6,
|
|
"packets-rx-ipv6-tcp", li_flow->packets_rx_ipv6_tcp,
|
|
"packets-rx-ipv6-udp", li_flow->packets_rx_ipv6_udp,
|
|
"packets-rx-ipv6-host-internal", li_flow->packets_rx_ipv6_internal,
|
|
"packets-rx-ipv6-no-next-header", li_flow->packets_rx_ipv6_no_next_header);
|
|
json_array_append(flows, flow);
|
|
}
|
|
}
|
|
dict_itor_free(itor);
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"li-flows", flows);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(flows);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_l2tp_tunnels(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root, *tunnels, *tunnel;
|
|
|
|
bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server;
|
|
bbl_l2tp_tunnel_t *l2tp_tunnel;
|
|
|
|
tunnels = json_array();
|
|
|
|
while(l2tp_server) {
|
|
CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) {
|
|
|
|
tunnel = json_pack("{ss ss ss si si ss ss ss ss si si si si si si si}",
|
|
"state", l2tp_tunnel_state_string(l2tp_tunnel->state),
|
|
"server-name", l2tp_server->host_name,
|
|
"server-address", format_ipv4_address(&l2tp_server->ip),
|
|
"tunnel-id", l2tp_tunnel->tunnel_id,
|
|
"peer-tunnel-id", l2tp_tunnel->peer_tunnel_id,
|
|
"peer-name", string_or_na(l2tp_tunnel->peer_name),
|
|
"peer-address", format_ipv4_address(&l2tp_tunnel->peer_ip),
|
|
"peer-vendor", string_or_na(l2tp_tunnel->peer_vendor),
|
|
"secret", string_or_na(l2tp_server->secret),
|
|
"control-packets-rx", l2tp_tunnel->stats.control_rx,
|
|
"control-packets-rx-dup", l2tp_tunnel->stats.control_rx_dup,
|
|
"control-packets-rx-out-of-order", l2tp_tunnel->stats.control_rx_ooo,
|
|
"control-packets-tx", l2tp_tunnel->stats.control_tx,
|
|
"control-packets-tx-retry", l2tp_tunnel->stats.control_retry,
|
|
"data-packets-rx", l2tp_tunnel->stats.data_rx,
|
|
"data-packets-tx", l2tp_tunnel->stats.data_tx);
|
|
json_array_append(tunnels, tunnel);
|
|
}
|
|
l2tp_server = l2tp_server->next;
|
|
}
|
|
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"l2tp-tunnels", tunnels);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(tunnels);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
json_t *
|
|
l2tp_session_json(bbl_l2tp_session_t *l2tp_session) {
|
|
char *proxy_auth_response = NULL;
|
|
|
|
if(l2tp_session->proxy_auth_response) {
|
|
if(l2tp_session->proxy_auth_type == L2TP_PROXY_AUTH_TYPE_PAP) {
|
|
proxy_auth_response = (char*)l2tp_session->proxy_auth_response;
|
|
} else {
|
|
proxy_auth_response = "0x...";
|
|
}
|
|
}
|
|
|
|
return json_pack("{ss si si si si si ss ss ss ss ss si si ss ss si si si si}",
|
|
"state", l2tp_session_state_string(l2tp_session->state),
|
|
"tunnel-id", l2tp_session->key.tunnel_id,
|
|
"session-id", l2tp_session->key.session_id,
|
|
"peer-tunnel-id", l2tp_session->tunnel->peer_tunnel_id,
|
|
"peer-session-id", l2tp_session->peer_session_id,
|
|
"peer-proxy-auth-type", l2tp_session->proxy_auth_type,
|
|
"peer-proxy-auth-name", string_or_na(l2tp_session->proxy_auth_name),
|
|
"peer-proxy-auth-response", string_or_na(proxy_auth_response),
|
|
"peer-called-number", string_or_na(l2tp_session->peer_called_number),
|
|
"peer-calling-number", string_or_na(l2tp_session->peer_calling_number),
|
|
"peer-sub-address", string_or_na(l2tp_session->peer_sub_address),
|
|
"peer-tx-bps", l2tp_session->peer_tx_bps,
|
|
"peer-rx-bps", l2tp_session->peer_rx_bps,
|
|
"peer-ari", string_or_na(l2tp_session->peer_ari),
|
|
"peer-aci", string_or_na(l2tp_session->peer_aci),
|
|
"data-packets-rx", l2tp_session->stats.data_rx,
|
|
"data-packets-tx", l2tp_session->stats.data_tx,
|
|
"data-ipv4-packets-rx", l2tp_session->stats.data_ipv4_rx,
|
|
"data-ipv4-packets-tx", l2tp_session->stats.data_ipv4_tx);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_l2tp_sessions(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
ssize_t result = 0;
|
|
json_t *root, *sessions;
|
|
|
|
bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server;
|
|
bbl_l2tp_tunnel_t *l2tp_tunnel;
|
|
bbl_l2tp_session_t *l2tp_session;
|
|
l2tp_key_t l2tp_key = {0};
|
|
void **search = NULL;
|
|
|
|
int l2tp_tunnel_id = 0;
|
|
int l2tp_session_id = 0;
|
|
|
|
json_unpack(arguments, "{s:i}", "tunnel-id", &l2tp_tunnel_id);
|
|
json_unpack(arguments, "{s:i}", "session-id", &l2tp_session_id);
|
|
|
|
sessions = json_array();
|
|
|
|
if(l2tp_tunnel_id && l2tp_session_id) {
|
|
l2tp_key.tunnel_id = l2tp_tunnel_id;
|
|
l2tp_key.session_id = l2tp_session_id;
|
|
search = dict_search(ctx->l2tp_session_dict, &l2tp_key);
|
|
if(search) {
|
|
l2tp_session = *search;
|
|
json_array_append(sessions, l2tp_session_json(l2tp_session));
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
json_decref(sessions);
|
|
return result;
|
|
}
|
|
} else if (l2tp_tunnel_id) {
|
|
l2tp_key.tunnel_id = l2tp_tunnel_id;
|
|
search = dict_search(ctx->l2tp_session_dict, &l2tp_key);
|
|
if(search) {
|
|
l2tp_session = *search;
|
|
l2tp_tunnel = l2tp_session->tunnel;
|
|
CIRCLEQ_FOREACH(l2tp_session, &l2tp_tunnel->session_qhead, session_qnode) {
|
|
if(!l2tp_session->key.session_id) continue; /* skip tunnel session */
|
|
json_array_append(sessions, l2tp_session_json(l2tp_session));
|
|
}
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "warning", 404, "tunnel not found");
|
|
json_decref(sessions);
|
|
return result;
|
|
}
|
|
} else {
|
|
while(l2tp_server) {
|
|
CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) {
|
|
CIRCLEQ_FOREACH(l2tp_session, &l2tp_tunnel->session_qhead, session_qnode) {
|
|
if(!l2tp_session->key.session_id) continue; /* skip tunnel session */
|
|
json_array_append(sessions, l2tp_session_json(l2tp_session));
|
|
}
|
|
}
|
|
l2tp_server = l2tp_server->next;
|
|
}
|
|
}
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"l2tp-sessions", sessions);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(sessions);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_l2tp_csurq(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
json_t *sessions, *number;
|
|
|
|
bbl_l2tp_tunnel_t *l2tp_tunnel;
|
|
bbl_l2tp_session_t *l2tp_session;
|
|
l2tp_key_t l2tp_key = {0};
|
|
void **search = NULL;
|
|
|
|
uint16_t l2tp_session_id = 0;
|
|
int l2tp_tunnel_id = 0;
|
|
int size, i;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:i}", "tunnel-id", &l2tp_tunnel_id) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing tunnel-id");
|
|
}
|
|
l2tp_key.tunnel_id = l2tp_tunnel_id;
|
|
search = dict_search(ctx->l2tp_session_dict, &l2tp_key);
|
|
if(search) {
|
|
l2tp_session = *search;
|
|
l2tp_tunnel = l2tp_session->tunnel;
|
|
if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) {
|
|
return bbl_ctrl_status(fd, "warning", 400, "tunnel not established");
|
|
}
|
|
sessions = json_object_get(arguments, "sessions");
|
|
if (json_is_array(sessions)) {
|
|
size = json_array_size(sessions);
|
|
l2tp_tunnel->csurq_requests_len = size;
|
|
l2tp_tunnel->csurq_requests = malloc(size * sizeof(uint16_t));
|
|
for (i = 0; i < size; i++) {
|
|
number = json_array_get(sessions, i);
|
|
if(json_is_number(number)) {
|
|
l2tp_session_id = json_number_value(number);
|
|
l2tp_tunnel->csurq_requests[i] = l2tp_session_id;
|
|
}
|
|
}
|
|
bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_CSURQ);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid request");
|
|
}
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "tunnel not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_l2tp_tunnel_terminate(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
bbl_l2tp_tunnel_t *l2tp_tunnel;
|
|
bbl_l2tp_session_t *l2tp_session;
|
|
l2tp_key_t l2tp_key = {0};
|
|
void **search = NULL;
|
|
|
|
int l2tp_tunnel_id = 0;
|
|
int result_code;
|
|
int error_code;
|
|
char *error_message;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:i}", "tunnel-id", &l2tp_tunnel_id) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing tunnel-id");
|
|
}
|
|
l2tp_key.tunnel_id = l2tp_tunnel_id;
|
|
search = dict_search(ctx->l2tp_session_dict, &l2tp_key);
|
|
if(search) {
|
|
l2tp_session = *search;
|
|
l2tp_tunnel = l2tp_session->tunnel;
|
|
if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) {
|
|
return bbl_ctrl_status(fd, "warning", 400, "tunnel not established");
|
|
}
|
|
bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN);
|
|
if (json_unpack(arguments, "{s:i}", "result-code", &result_code) != 0) {
|
|
result_code = 1;
|
|
}
|
|
l2tp_tunnel->result_code = result_code;
|
|
if (json_unpack(arguments, "{s:i}", "error-code", &error_code) != 0) {
|
|
error_code = 0;
|
|
}
|
|
l2tp_tunnel->error_code = error_code;
|
|
if (json_unpack(arguments, "{s:s}", "error-message", &error_message) != 0) {
|
|
error_message = NULL;
|
|
}
|
|
l2tp_tunnel->error_message = error_message;
|
|
bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "tunnel not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_l2tp_session_terminate(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments) {
|
|
bbl_session_s *session;
|
|
bbl_l2tp_tunnel_t *l2tp_tunnel;
|
|
bbl_l2tp_session_t *l2tp_session;
|
|
|
|
int result_code;
|
|
int error_code;
|
|
char *error_message;
|
|
int disconnect_code;
|
|
int disconnect_protocol;
|
|
int disconnect_direction;
|
|
char* disconnect_message;
|
|
|
|
if(session_id == 0) {
|
|
/* session-id is mandatory */
|
|
return bbl_ctrl_status(fd, "error", 400, "missing session-id");
|
|
}
|
|
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
l2tp_session = session->l2tp_session;
|
|
if(!l2tp_session) {
|
|
return bbl_ctrl_status(fd, "error", 400, "no L2TP session");
|
|
}
|
|
l2tp_tunnel = l2tp_session->tunnel;
|
|
if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) {
|
|
return bbl_ctrl_status(fd, "warning", 400, "tunnel not established");
|
|
}
|
|
if(l2tp_session->state != BBL_L2TP_SESSION_ESTABLISHED) {
|
|
return bbl_ctrl_status(fd, "warning", 400, "session not established");
|
|
}
|
|
if (json_unpack(arguments, "{s:i}", "result-code", &result_code) != 0) {
|
|
result_code = 2;
|
|
}
|
|
l2tp_session->result_code = result_code;
|
|
if (json_unpack(arguments, "{s:i}", "error-code", &error_code) != 0) {
|
|
error_code = 0;
|
|
}
|
|
l2tp_session->error_code = error_code;
|
|
if (json_unpack(arguments, "{s:s}", "error-message", &error_message) != 0) {
|
|
error_message = NULL;
|
|
}
|
|
l2tp_session->error_message = error_message;
|
|
if (json_unpack(arguments, "{s:i}", "disconnect-code", &disconnect_code) != 0) {
|
|
disconnect_code = 0;
|
|
}
|
|
l2tp_session->disconnect_code = disconnect_code;
|
|
if (json_unpack(arguments, "{s:i}", "disconnect-protocol", &disconnect_protocol) != 0) {
|
|
disconnect_protocol = 0;
|
|
}
|
|
l2tp_session->disconnect_protocol = disconnect_protocol;
|
|
if (json_unpack(arguments, "{s:i}", "disconnect-direction", &disconnect_direction) != 0) {
|
|
disconnect_direction = 0;
|
|
}
|
|
l2tp_session->disconnect_direction = disconnect_direction;
|
|
if (json_unpack(arguments, "{s:s}", "disconnect-message", &disconnect_message) != 0) {
|
|
disconnect_message = NULL;
|
|
}
|
|
l2tp_session->disconnect_message = disconnect_message;
|
|
bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_CDN);
|
|
bbl_l2tp_session_delete(l2tp_session);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_session_streams(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root;
|
|
json_t *json_streams = NULL;
|
|
json_t *json_stream = NULL;
|
|
|
|
bbl_session_s *session;
|
|
bbl_stream *stream;
|
|
|
|
if(session_id == 0) {
|
|
/* session-id is mandatory */
|
|
return bbl_ctrl_status(fd, "error", 400, "missing session-id");
|
|
}
|
|
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
stream = session->stream;
|
|
|
|
json_streams = json_array();
|
|
while(stream) {
|
|
json_stream = bbl_stream_json(stream);
|
|
json_array_append(json_streams, json_stream);
|
|
stream = stream->next;
|
|
}
|
|
root = json_pack("{ss si s{si si si si si si si si si sf sf so*}}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"session-streams",
|
|
"session-id", session->session_id,
|
|
"rx-packets", session->stats.packets_rx,
|
|
"tx-packets", session->stats.packets_tx,
|
|
"rx-accounting-packets", session->stats.accounting_packets_rx,
|
|
"tx-accounting-packets", session->stats.accounting_packets_tx,
|
|
"rx-pps", session->stats.rate_packets_rx.avg,
|
|
"tx-pps", session->stats.rate_packets_tx.avg,
|
|
"rx-bps-l2", session->stats.rate_bytes_rx.avg * 8,
|
|
"tx-bps-l2", session->stats.rate_bytes_tx.avg * 8,
|
|
"rx-mbps-l2", (double)(session->stats.rate_bytes_rx.avg * 8) / 1000000.0,
|
|
"tx-mbps-l2", (double)(session->stats.rate_bytes_tx.avg * 8) / 1000000.0,
|
|
"streams", json_streams);
|
|
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(json_streams);
|
|
}
|
|
return result;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
}
|
|
|
|
static ssize_t
|
|
bbl_ctrl_stream_traffic_start_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
|
|
if(session_id) {
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
session->stream_traffic = status;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(session) {
|
|
session->stream_traffic = status;
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_stream_traffic_start(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_stream_traffic_start_stop(fd, ctx, session_id, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_stream_traffic_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_stream_traffic_start_stop(fd, ctx, session_id, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_stream_reset(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
bbl_stream *stream;
|
|
struct dict_itor *itor;
|
|
|
|
ctx->stats.stream_traffic_flows_verified = 0;
|
|
|
|
/* Iterate over all traffic streams */
|
|
itor = dict_itor_new(ctx->stream_flow_dict);
|
|
dict_itor_first(itor);
|
|
for (; dict_itor_valid(itor); dict_itor_next(itor)) {
|
|
stream = (bbl_stream*)*dict_itor_datum(itor);
|
|
if(!stream) {
|
|
continue;
|
|
}
|
|
if(stream->thread.thread) {
|
|
pthread_mutex_lock(&stream->thread.mutex);
|
|
stream->thread.thread->packets_tx = 0;
|
|
stream->thread.thread->packets_tx_last_sync = 0;
|
|
stream->thread.thread->bytes_tx = 0;
|
|
stream->thread.thread->bytes_tx_last_sync = 0;
|
|
}
|
|
|
|
stream->flow_seq = 0;
|
|
stream->rx_first_seq = 0;
|
|
stream->rx_last_seq = 0;
|
|
stream->stop = false;
|
|
|
|
stream->packets_tx = 0;
|
|
stream->packets_rx = 0;
|
|
stream->packets_tx_last_sync = 0;
|
|
stream->packets_rx_last_sync = 0;
|
|
|
|
stream->min_delay_ns = 0;
|
|
stream->max_delay_ns = 0;
|
|
|
|
stream->rx_mpls1 = false;
|
|
stream->rx_mpls1_label = 0;
|
|
stream->rx_mpls1_exp = 0;
|
|
stream->rx_mpls1_ttl = 0;
|
|
|
|
stream->rx_mpls2 = false;
|
|
stream->rx_mpls2_label = 0;
|
|
stream->rx_mpls2_exp = 0;
|
|
stream->rx_mpls2_ttl = 0;
|
|
|
|
stream->packets_rx_last_sync = 0;
|
|
stream->packets_rx_last_sync = 0;
|
|
|
|
if(stream->thread.thread) {
|
|
pthread_mutex_unlock(&stream->thread.mutex);
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_sessions_pending(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
|
|
ssize_t result = 0;
|
|
json_t *root, *json_session, *json_sessions;
|
|
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
|
|
json_sessions = json_array();
|
|
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(!session) continue;
|
|
|
|
if(session->session_state != BBL_ESTABLISHED ||
|
|
session->session_traffic_flows != session->session_traffic_flows_verified) {
|
|
json_session = json_pack("{si ss si si}",
|
|
"session-id", session->session_id,
|
|
"session-state", session_state_string(session->session_state),
|
|
"session-traffic-flows", session->session_traffic_flows,
|
|
"session-traffic-flows-verified", session->session_traffic_flows_verified);
|
|
json_array_append(json_sessions, json_session);
|
|
}
|
|
}
|
|
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"sessions-pending", json_sessions);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(json_sessions);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_start_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
if(session_id) {
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
session->cfm_cc = status;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(session) {
|
|
session->cfm_cc = status;
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_start(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_cfm_cc_start_stop(fd, ctx, session_id, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_cfm_cc_start_stop(fd, ctx, session_id, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_rdi(int fd, bbl_ctx_s *ctx, uint32_t session_id, bool status) {
|
|
bbl_session_s *session;
|
|
uint32_t i;
|
|
if(session_id) {
|
|
session = bbl_session_get(ctx, session_id);
|
|
if(session) {
|
|
session->cfm_rdi = status;
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
}
|
|
} else {
|
|
/* Iterate over all sessions */
|
|
for(i = 0; i < ctx->sessions; i++) {
|
|
session = ctx->session_list[i];
|
|
if(session) {
|
|
session->cfm_rdi = status;
|
|
}
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_rdi_on(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_cfm_cc_rdi(fd, ctx, session_id, true);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_cfm_cc_rdi_off(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* arguments __attribute__((unused))) {
|
|
return bbl_ctrl_cfm_cc_rdi(fd, ctx, session_id, false);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_stream_stats(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root = json_pack("{ss si s{si si}}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"stream-stats",
|
|
"total-flows", ctx->stats.stream_traffic_flows,
|
|
"verified-flows", ctx->stats.stream_traffic_flows_verified);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_stream_info(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
ssize_t result = 0;
|
|
|
|
json_t *root;
|
|
json_t *json_stream = NULL;
|
|
|
|
bbl_stream *stream;
|
|
void **search = NULL;
|
|
|
|
int number = 0;
|
|
uint64_t flow_id;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:i}", "flow-id", &number) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing flow-id");
|
|
}
|
|
|
|
flow_id = number;
|
|
search = dict_search(ctx->stream_flow_dict, &flow_id);
|
|
if(search) {
|
|
stream = *search;
|
|
if(stream->thread.thread) {
|
|
pthread_mutex_lock(&stream->thread.mutex);
|
|
}
|
|
json_stream = bbl_stream_json(stream);
|
|
root = json_pack("{ss si so*}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"stream-info", json_stream);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(json_stream);
|
|
}
|
|
if(stream->thread.thread) {
|
|
pthread_mutex_unlock(&stream->thread.mutex);
|
|
}
|
|
return result;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "warning", 404, "stream not found");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_traffic_start(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
enable_disable_traffic(ctx, true);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_traffic_stop(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
enable_disable_traffic(ctx, false);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_isis_adjacencies(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
ssize_t result = 0;
|
|
json_t *root, *adjacencies, *adjacency;
|
|
|
|
adjacencies = json_array();
|
|
for(int i=0; i < ctx->interfaces.network_if_count; i++) {
|
|
if(ctx->interfaces.network_if[i]->isis_adjacency_p2p) {
|
|
adjacency = isis_ctrl_adjacency_p2p(ctx->interfaces.network_if[i]->isis_adjacency_p2p);
|
|
if(adjacency) {
|
|
json_array_append(adjacencies, adjacency);
|
|
}
|
|
} else {
|
|
for(int level=0; level<ISIS_LEVELS; level++) {
|
|
adjacency = isis_ctrl_adjacency(ctx->interfaces.network_if[i]->isis_adjacency[level]);
|
|
if(adjacency) {
|
|
json_array_append(adjacencies, adjacency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"isis-adjacencies", adjacencies);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(adjacencies);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_isis_database(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
|
|
ssize_t result = 0;
|
|
json_t *root = NULL;
|
|
json_t *database = NULL;
|
|
isis_instance_t *instance = NULL;
|
|
|
|
int instance_id = 0;
|
|
int level = 0;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:i}", "instance", &instance_id) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing ISIS instance");
|
|
}
|
|
if (json_unpack(arguments, "{s:i}", "level", &level) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing ISIS level");
|
|
}
|
|
if (!(level == ISIS_LEVEL_1 || level == ISIS_LEVEL_2)) {
|
|
return bbl_ctrl_status(fd, "error", 400, "invalid ISIS level");
|
|
}
|
|
|
|
/* Search for matching instance */
|
|
instance = ctx->isis_instances;
|
|
while(instance) {
|
|
if(instance->config->id == instance_id) {
|
|
break;
|
|
}
|
|
instance = instance->next;
|
|
}
|
|
|
|
if (!instance) {
|
|
return bbl_ctrl_status(fd, "error", 400, "ISIS instance not found");
|
|
}
|
|
|
|
if(!instance->level[level-1].lsdb) {
|
|
return bbl_ctrl_status(fd, "error", 400, "ISIS database not found");
|
|
}
|
|
|
|
database = isis_ctrl_database(instance->level[level-1].lsdb);
|
|
if(database) {
|
|
root = json_pack("{ss si so}",
|
|
"status", "ok",
|
|
"code", 200,
|
|
"isis-database", database);
|
|
if(root) {
|
|
result = json_dumpfd(root, fd, 0);
|
|
json_decref(root);
|
|
} else {
|
|
result = bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
json_decref(database);
|
|
}
|
|
return result;
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 500, "internal error");
|
|
}
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_isis_load_mrt(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
char *file_path;
|
|
int instance_id = 0;
|
|
|
|
isis_instance_t *instance = NULL;
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:s}", "file", &file_path) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing MRT file");
|
|
}
|
|
if (json_unpack(arguments, "{s:i}", "instance", &instance_id) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing ISIS instance");
|
|
}
|
|
|
|
/* Search for matching instance */
|
|
instance = ctx->isis_instances;
|
|
while(instance) {
|
|
if(instance->config->id == instance_id) {
|
|
break;
|
|
}
|
|
instance = instance->next;
|
|
}
|
|
|
|
if (!instance) {
|
|
return bbl_ctrl_status(fd, "error", 404, "ISIS instance not found");
|
|
}
|
|
|
|
if(!isis_mrt_load(instance, file_path)) {
|
|
return bbl_ctrl_status(fd, "error", 500, "failed to load ISIS MRT file");
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_isis_lsp_update(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments) {
|
|
|
|
protocol_error_t result;
|
|
|
|
json_t *value;
|
|
size_t pdu_count;
|
|
|
|
int instance_id = 0;
|
|
isis_instance_t *instance = NULL;
|
|
|
|
isis_pdu_t pdu = {0};
|
|
|
|
const char *pdu_string;
|
|
uint16_t pdu_string_len;
|
|
|
|
uint8_t buf[ISIS_MAX_PDU_LEN];
|
|
uint16_t len;
|
|
|
|
|
|
/* Unpack further arguments */
|
|
if (json_unpack(arguments, "{s:i}", "instance", &instance_id) != 0) {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing ISIS instance");
|
|
}
|
|
|
|
/* Search for matching instance */
|
|
instance = ctx->isis_instances;
|
|
while(instance) {
|
|
if(instance->config->id == instance_id) {
|
|
break;
|
|
}
|
|
instance = instance->next;
|
|
}
|
|
|
|
if (!instance) {
|
|
return bbl_ctrl_status(fd, "error", 404, "ISIS instance not found");
|
|
}
|
|
|
|
/* Process PDU array */
|
|
value = json_object_get(arguments, "pdu");
|
|
if (json_is_array(value)) {
|
|
pdu_count = json_array_size(value);
|
|
for (size_t i = 0; i < pdu_count; i++) {
|
|
pdu_string = json_string_value(json_array_get(value, i));
|
|
if(!pdu_string) {
|
|
return bbl_ctrl_status(fd, "error", 500, "failed to read ISIS PDU");
|
|
}
|
|
pdu_string_len = strlen(pdu_string);
|
|
/* Load PDU from hexstring */
|
|
for (len = 0; len < (pdu_string_len/2); len++) {
|
|
sscanf(pdu_string + len*2, "%02hhx", &buf[len]);
|
|
}
|
|
result = isis_pdu_load(&pdu, (uint8_t*)buf, len);
|
|
if(result != PROTOCOL_SUCCESS) {
|
|
return bbl_ctrl_status(fd, "error", 500, "failed to decode ISIS PDU");
|
|
}
|
|
/* Update external LSP */
|
|
if(!isis_lsp_update_external(instance, &pdu)) {
|
|
return bbl_ctrl_status(fd, "error", 500, "failed to update ISIS LSP");
|
|
}
|
|
}
|
|
} else {
|
|
return bbl_ctrl_status(fd, "error", 400, "missing PDU list");
|
|
}
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
ssize_t
|
|
bbl_ctrl_isis_teardown(int fd, bbl_ctx_s *ctx, uint32_t session_id __attribute__((unused)), json_t* arguments __attribute__((unused))) {
|
|
isis_teardown(ctx);
|
|
return bbl_ctrl_status(fd, "ok", 200, NULL);
|
|
}
|
|
|
|
struct action {
|
|
char *name;
|
|
callback_function *fn;
|
|
};
|
|
|
|
struct action actions[] = {
|
|
{"interfaces", bbl_ctrl_interfaces},
|
|
{"terminate", bbl_ctrl_session_terminate},
|
|
{"ipcp-open", bbl_ctrl_session_ipcp_open},
|
|
{"ipcp-close", bbl_ctrl_session_ipcp_close},
|
|
{"ip6cp-open", bbl_ctrl_session_ip6cp_open},
|
|
{"ip6cp-close", bbl_ctrl_session_ip6cp_close},
|
|
{"session-counters", bbl_ctrl_session_counters},
|
|
{"session-info", bbl_ctrl_session_info},
|
|
{"session-traffic", bbl_ctrl_session_traffic_stats},
|
|
{"session-traffic-enabled", bbl_ctrl_session_traffic_start},
|
|
{"session-traffic-start", bbl_ctrl_session_traffic_start},
|
|
{"session-traffic-disabled", bbl_ctrl_session_traffic_stop},
|
|
{"session-traffic-stop", bbl_ctrl_session_traffic_stop},
|
|
{"multicast-traffic-start", bbl_ctrl_multicast_traffic_start},
|
|
{"multicast-traffic-stop", bbl_ctrl_multicast_traffic_stop},
|
|
{"igmp-join", bbl_ctrl_igmp_join},
|
|
{"igmp-leave", bbl_ctrl_igmp_leave},
|
|
{"igmp-info", bbl_ctrl_igmp_info},
|
|
{"li-flows", bbl_ctrl_li_flows},
|
|
{"l2tp-tunnels", bbl_ctrl_l2tp_tunnels},
|
|
{"l2tp-sessions", bbl_ctrl_l2tp_sessions},
|
|
{"l2tp-csurq", bbl_ctrl_l2tp_csurq},
|
|
{"l2tp-tunnel-terminate", bbl_ctrl_l2tp_tunnel_terminate},
|
|
{"l2tp-session-terminate", bbl_ctrl_l2tp_session_terminate},
|
|
{"session-streams", bbl_ctrl_session_streams},
|
|
{"stream-traffic-enabled", bbl_ctrl_stream_traffic_start},
|
|
{"stream-traffic-start", bbl_ctrl_stream_traffic_start},
|
|
{"stream-traffic-disabled", bbl_ctrl_stream_traffic_stop},
|
|
{"stream-traffic-stop", bbl_ctrl_stream_traffic_stop},
|
|
{"stream-info", bbl_ctrl_stream_info},
|
|
{"stream-stats", bbl_ctrl_stream_stats},
|
|
{"stream-reset", bbl_ctrl_stream_reset},
|
|
{"sessions-pending", bbl_ctrl_sessions_pending},
|
|
{"cfm-cc-start", bbl_ctrl_cfm_cc_start},
|
|
{"cfm-cc-stop", bbl_ctrl_cfm_cc_stop},
|
|
{"cfm-cc-rdi-on", bbl_ctrl_cfm_cc_rdi_on},
|
|
{"cfm-cc-rdi-off", bbl_ctrl_cfm_cc_rdi_off},
|
|
{"traffic-start", bbl_ctrl_traffic_start},
|
|
{"traffic-stop", bbl_ctrl_traffic_stop},
|
|
{"isis-adjacencies", bbl_ctrl_isis_adjacencies},
|
|
{"isis-database", bbl_ctrl_isis_database},
|
|
{"isis-load-mrt", bbl_ctrl_isis_load_mrt},
|
|
{"isis-lsp-update", bbl_ctrl_isis_lsp_update},
|
|
{"isis-teardown", bbl_ctrl_isis_teardown},
|
|
{"bgp-sessions", bgp_ctrl_sessions},
|
|
{"bgp-teardown", bgp_ctrl_teardown},
|
|
{"bgp-raw-update", bgp_ctrl_raw_update},
|
|
{"bgp-raw-update-list", bgp_ctrl_raw_update_list},
|
|
{"bgp-disconnect", bgp_ctrl_disconnect},
|
|
{NULL, NULL},
|
|
};
|
|
|
|
void
|
|
bbl_ctrl_socket_job(timer_s *timer) {
|
|
bbl_ctx_s *ctx = timer->data;
|
|
int fd;
|
|
size_t i;
|
|
size_t flags = JSON_DISABLE_EOF_CHECK;
|
|
json_error_t error;
|
|
json_t *root = NULL;
|
|
json_t* arguments = NULL;
|
|
json_t* value = NULL;
|
|
const char *command = NULL;
|
|
uint32_t session_id = 0;
|
|
|
|
vlan_session_key_t key = {0};
|
|
bbl_session_s *session;
|
|
void **search;
|
|
|
|
while(true) {
|
|
fd = accept(ctx->ctrl_socket, 0, 0);
|
|
if(fd < 0) {
|
|
/* The accept function fails with error EAGAIN or EWOULDBLOCK if
|
|
* there are no pending connections present on the queue.*/
|
|
if(errno == EAGAIN) {
|
|
return;
|
|
}
|
|
} else {
|
|
/* New connection */
|
|
root = json_loadfd(fd, flags, &error);
|
|
if (!root) {
|
|
LOG(DEBUG, "Invalid json via ctrl socket: line %d: %s\n", error.line, error.text);
|
|
bbl_ctrl_status(fd, "error", 400, "invalid json");
|
|
} else {
|
|
/* Each command request should be formatted as shown in the example below
|
|
* with a mandatory command element and optional arguments.
|
|
* {
|
|
* "command": "session-info",
|
|
* "arguments": {
|
|
* "outer-vlan": 1,
|
|
* "inner-vlan": 2
|
|
* }
|
|
* }
|
|
*/
|
|
if(json_unpack(root, "{s:s, s?o}", "command", &command, "arguments", &arguments) != 0) {
|
|
LOG_NOARG(DEBUG, "Invalid command via ctrl socket\n");
|
|
bbl_ctrl_status(fd, "error", 400, "invalid request");
|
|
} else {
|
|
if(arguments) {
|
|
value = json_object_get(arguments, "session-id");
|
|
if (value) {
|
|
if(json_is_number(value)) {
|
|
session_id = json_number_value(value);
|
|
} else {
|
|
bbl_ctrl_status(fd, "error", 400, "invalid session-id");
|
|
goto CLOSE;
|
|
}
|
|
} else {
|
|
/* Deprecated!
|
|
* For backward compatibility with version 0.4.X, we still
|
|
* support per session commands using VLAN index instead of
|
|
* new session-id. */
|
|
value = json_object_get(arguments, "ifindex");
|
|
if (value) {
|
|
if(json_is_number(value)) {
|
|
key.ifindex = json_number_value(value);
|
|
} else {
|
|
bbl_ctrl_status(fd, "error", 400, "invalid ifindex");
|
|
goto CLOSE;
|
|
}
|
|
} else {
|
|
/* Use first interface as default. */
|
|
if(ctx->interfaces.access_if[0]) {
|
|
key.ifindex = ctx->interfaces.access_if[0]->ifindex;
|
|
}
|
|
}
|
|
value = json_object_get(arguments, "outer-vlan");
|
|
if (value) {
|
|
if(json_is_number(value)) {
|
|
key.outer_vlan_id = json_number_value(value);
|
|
} else {
|
|
bbl_ctrl_status(fd, "error", 400, "invalid outer-vlan");
|
|
goto CLOSE;
|
|
}
|
|
}
|
|
value = json_object_get(arguments, "inner-vlan");
|
|
if (value) {
|
|
if(json_is_number(value)) {
|
|
key.inner_vlan_id = json_number_value(value);
|
|
} else {
|
|
bbl_ctrl_status(fd, "error", 400, "invalid inner-vlan");
|
|
goto CLOSE;
|
|
}
|
|
}
|
|
if(key.outer_vlan_id) {
|
|
search = dict_search(ctx->vlan_session_dict, &key);
|
|
if(search) {
|
|
session = *search;
|
|
session_id = session->session_id;
|
|
} else {
|
|
bbl_ctrl_status(fd, "warning", 404, "session not found");
|
|
goto CLOSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(i = 0; true; i++) {
|
|
if(actions[i].name == NULL) {
|
|
bbl_ctrl_status(fd, "error", 400, "unknown command");
|
|
break;
|
|
} else if(strcmp(actions[i].name, command) == 0) {
|
|
actions[i].fn(fd, ctx, session_id, arguments);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CLOSE:
|
|
if(root) json_decref(root);
|
|
close(fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
bbl_ctrl_socket_open(bbl_ctx_s *ctx) {
|
|
struct sockaddr_un addr = {0};
|
|
ctx->ctrl_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if(ctx->ctrl_socket < 0) {
|
|
fprintf(stderr, "Error: Failed to create ctrl socket\n");
|
|
return false;
|
|
}
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, ctx->ctrl_socket_path, sizeof(addr.sun_path)-1);
|
|
|
|
unlink(ctx->ctrl_socket_path);
|
|
if (bind(ctx->ctrl_socket, (struct sockaddr *)&addr, SUN_LEN(&addr)) != 0) {
|
|
fprintf(stderr, "Error: Failed to bind ctrl socket %s (error %d)\n", ctx->ctrl_socket_path, errno);
|
|
return false;
|
|
}
|
|
|
|
if (listen(ctx->ctrl_socket, BACKLOG) != 0) {
|
|
fprintf(stderr, "Error: Failed to listen on ctrl socket %s (error %d)\n", ctx->ctrl_socket_path, errno);
|
|
return false;
|
|
}
|
|
|
|
/* Change socket to non-blocking */
|
|
fcntl(ctx->ctrl_socket, F_SETFL, O_NONBLOCK);
|
|
|
|
timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_timer, "CTRL Socket Timer", 0, 100 * MSEC, ctx, &bbl_ctrl_socket_job);
|
|
|
|
LOG(INFO, "Opened control socket %s\n", ctx->ctrl_socket_path);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
bbl_ctrl_socket_close(bbl_ctx_s *ctx) {
|
|
if(ctx->ctrl_socket) {
|
|
close(ctx->ctrl_socket);
|
|
ctx->ctrl_socket = 0;
|
|
unlink(ctx->ctrl_socket_path);
|
|
}
|
|
return true;
|
|
} |