Files
rtbrick-bngblaster/code/bngblaster/src/bbl_interface.c
T

454 lines
14 KiB
C

/*
* BNG Blaster (BBL) - Interfaces
*
* Christian Giese, October 2020
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bbl.h"
#include "bbl_io.h"
#include <sys/stat.h>
/**
* bbl_interface_lock
*
* @brief This functions locks the interface
* creating the file "/run/lock/bngblaster_<interface>.lock".
*
* @param ctx global context
* @param interface interface
* @return false if failed to lock (e.g. in use)
*/
static bool
bbl_interface_lock(bbl_ctx_s *ctx, char *interface_name)
{
FILE *lock_file;
char lock_path[FILE_PATH_LEN];
int lock_pid;
char proc_pid_path[FILE_PATH_LEN];
struct stat sts;
pid_t pid = getpid();
snprintf(lock_path, sizeof(lock_path), "/run/lock/bngblaster_%s.lock", interface_name);
lock_file = fopen(lock_path, "r");
if(lock_file) {
/* lock file exists */
if(fscanf(lock_file,"%d", &lock_pid) == 1 && lock_pid > 1) {
snprintf(proc_pid_path, sizeof(proc_pid_path), "/proc/%d", lock_pid);
if (!(stat(proc_pid_path, &sts) == -1 && errno == ENOENT)) {
LOG(ERROR, "Interface %s in use by process %d (%s)\n", interface_name, lock_pid, lock_path);
if(!ctx->config.interface_lock_force) return false;
}
} else {
LOG(ERROR, "Invalid interface lock file %s\n", lock_path);
if(!ctx->config.interface_lock_force) return false;
}
fclose(lock_file);
}
/* crate lock file */
lock_pid = pid;
lock_file = fopen(lock_path, "w");
if(!lock_file) {
LOG(ERROR, "Failed to open interface lock file %s\n", lock_path);
return false;
}
fprintf(lock_file, "%d", lock_pid);
fclose(lock_file);
return true;
}
/**
* bbl_interface_unlock_all
*
* @brief This functions unlocks all interfaces.
*
* @param ctx global context
*/
void
bbl_interface_unlock_all(bbl_ctx_s *ctx)
{
char lock_path[FILE_PATH_LEN];
for(int i = 0; i < ctx->interfaces.count; i++) {
snprintf(lock_path, sizeof(lock_path), "/run/lock/bngblaster_%s.lock", ctx->interfaces.names[i]);
remove(lock_path);
}
}
/**
* bbl_add_interface
*
* @param ctx global context
* @param interface interface name
* @return interface
*/
static bbl_interface_s *
bbl_add_interface(bbl_ctx_s *ctx, char *interface_name)
{
bbl_interface_s *interface;
struct ifreq ifr;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if(!bbl_interface_lock(ctx, interface_name)) {
return NULL;
}
interface = calloc(1, sizeof(bbl_interface_s));
if (!interface) {
LOG(ERROR, "No memory for interface %s\n", interface_name);
return NULL;
}
interface->name = strdup(interface_name);
interface->ctx = ctx;
CIRCLEQ_INSERT_TAIL(&ctx->interface_qhead, interface, interface_qnode);
interface->pcap_index = ctx->pcap.index;
ctx->pcap.index++;
ctx->interfaces.names[ctx->interfaces.count++] = interface->name;
/*
* TX list init.
*/
CIRCLEQ_INIT(&interface->session_tx_qhead);
CIRCLEQ_INIT(&interface->l2tp_tx_qhead);
/*
* Obtain the interface MAC address.
*/
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name);
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
LOG(ERROR, "Getting MAC address error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return NULL;
}
memcpy(&interface->mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
/*
* Obtain the interface index.
*/
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name);
if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
LOG(ERROR, "Get interface index error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return NULL;
}
interface->ifindex = ifr.ifr_ifindex;
/* The BNG Blaster supports multiple IO modes where packet_mmap is
* selected per default. */
if(!bbl_io_add_interface(ctx, interface)) {
LOG(ERROR, "Failed to add interface %s\n", interface->name);
return NULL;
}
/*
* Timer to compute periodic rates.
*/
timer_add_periodic(&ctx->timer_root, &interface->rate_job, "Rate Computation", 1, 0, interface,
&bbl_compute_interface_rate_job);
return interface;
}
/**
* bbl_interface_present
*
* @param ctx global context
* @param interface_name interface name
* @return true if interface is already added
*/
static bool
bbl_interface_present(bbl_ctx_s *ctx, char *interface_name)
{
for(int i = 0; i < ctx->interfaces.count; i++) {
if (strcmp(ctx->interfaces.names[i], interface_name) == 0) {
return true;
}
}
return false;
}
/**
* bbl_add_access_interfaces
*
* @param ctx global context
*/
static bool
bbl_add_access_interfaces(bbl_ctx_s *ctx)
{
bbl_access_config_s *access_config = ctx->config.access_config;
struct bbl_interface_ *access_if;
int i;
while(access_config) {
for(i = 0; i < ctx->interfaces.access_if_count; i++) {
if(ctx->interfaces.access_if[i]->name) {
if (strcmp(ctx->interfaces.access_if[i]->name, access_config->interface) == 0) {
/* Interface already added! */
access_config->access_if = ctx->interfaces.access_if[i];
goto NEXT;
}
}
}
access_if = bbl_add_interface(ctx, access_config->interface);
if (!access_if) {
LOG(ERROR, "Failed to add access interface %s\n", access_config->interface);
return false;
}
access_if->type = INTERFACE_TYPE_ACCESS;
access_config->access_if = access_if;
if(ctx->interfaces.access_if_count < BBL_MAX_INTERFACES) {
ctx->interfaces.access_if[ctx->interfaces.access_if_count++] = access_if;
} else {
LOG(ERROR, "Failed to add access interface %s (limit reached)\n", access_config->interface);
return false;
}
bbl_send_init_interface(access_if, BBL_SEND_DEFAULT_SIZE);
NEXT:
access_config = access_config->next;
}
return true;
}
/**
* bbl_add_network_interfaces
*
* @param ctx global context
*/
static bool
bbl_add_network_interfaces(bbl_ctx_s *ctx)
{
bbl_network_config_s *network_config = ctx->config.network_config;
struct bbl_interface_ *network_if;
isis_instance_t *isis;
bool isis_result;
while(network_config) {
if(bbl_interface_present(ctx, network_config->interface)) {
LOG(ERROR, "Failed to add network interface %s (already added)\n", network_config->interface);
return false;
}
network_if = bbl_add_interface(ctx, network_config->interface);
if (!network_if) {
LOG(ERROR, "Failed to add network interface %s\n", network_config->interface);
return false;
}
network_if->type = INTERFACE_TYPE_NETWORK;
network_config->network_if = network_if;
if(ctx->interfaces.network_if_count < BBL_MAX_INTERFACES) {
ctx->interfaces.network_if[ctx->interfaces.network_if_count++] = network_if;
} else {
LOG(ERROR, "Failed to add network interface %s (limit reached)\n", network_config->interface);
return false;
}
bbl_send_init_interface(network_if, BBL_SEND_DEFAULT_SIZE);
/* Init ethernet */
network_if->vlan = network_config->vlan;
/* Copy gateway MAC from config (default 00:00:00:00:00:00) */
memcpy(network_if->gateway_mac, network_config->gateway_mac, ETH_ADDR_LEN);
/* Init IPv4 */
if(network_config->ip.address && network_config->gateway) {
network_if->ip.address = network_config->ip.address;
network_if->ip.len = network_config->ip.len;
network_if->gateway = network_config->gateway;
/* Send initial ARP request */
network_if->send_requests |= BBL_IF_SEND_ARP_REQUEST;
}
/* Init link-local IPv6 address */
network_if->ip6_ll[0] = 0xfe;
network_if->ip6_ll[1] = 0x80;
network_if->ip6_ll[8] = network_if->mac[0];
network_if->ip6_ll[9] = network_if->mac[1];
network_if->ip6_ll[10] = network_if->mac[2];
network_if->ip6_ll[11] = 0xff;
network_if->ip6_ll[12] = 0xfe;
network_if->ip6_ll[13] = network_if->mac[3];
network_if->ip6_ll[14] = network_if->mac[4];
network_if->ip6_ll[15] = network_if->mac[5];
/* Init IPv6 */
if(ipv6_prefix_not_zero(&network_config->ip6) &&
ipv6_addr_not_zero(&network_config->gateway6)) {
/* Init global IPv6 address */
memcpy(&network_if->ip6, &network_config->ip6, sizeof(ipv6_prefix));
memcpy(&network_if->gateway6, &network_config->gateway6, sizeof(ipv6addr_t));
memcpy(&network_if->gateway6_solicited_node_multicast, &ipv6_solicited_node_multicast, sizeof(ipv6addr_t));
memcpy(((uint8_t*)&network_if->gateway6_solicited_node_multicast)+13,
((uint8_t*)&network_if->gateway6)+13, 3);
/* Send initial ICMPv6 NS */
network_if->send_requests |= BBL_IF_SEND_ICMPV6_NS;
}
network_if->gateway_resolve_wait = network_config->gateway_resolve_wait;
/* Init TCP */
if(!bbl_tcp_interface_init(network_if, network_config)) {
LOG(ERROR, "Failed to init TCP for network interface %s\n",
network_config->interface);
return false;
}
/* Init routing protocols */
if(network_config->isis_instance_id) {
isis_result = false;
isis = ctx->isis_instances;
while (isis) {
if(isis->config->id == network_config->isis_instance_id) {
isis_result = isis_adjacency_init(network_config, network_if, isis);
if(!isis_result) {
LOG(ERROR, "Failed to enable IS-IS for network interface %s (adjacency init failed)\n",
network_config->interface);
return false;
}
break;
}
isis = isis->next;
}
if(!isis_result) {
LOG(ERROR, "Failed to enable IS-IS for network interface %s (instance not found)\n",
network_config->interface);
return false;
}
}
/* Next ... */
network_config = network_config->next;
}
return true;
}
/**
* bbl_add_a10nsp_interfaces
*
* @param ctx global context
*/
static bool
bbl_add_a10nsp_interfaces(bbl_ctx_s *ctx)
{
bbl_a10nsp_config_s *a10nsp_config = ctx->config.a10nsp_config;
struct bbl_interface_ *a10nsp_if;
while(a10nsp_config) {
if(bbl_interface_present(ctx, a10nsp_config->interface)) {
LOG(ERROR, "Failed to add a10nsp interface %s (already added)\n", a10nsp_config->interface);
return false;
}
a10nsp_if = bbl_add_interface(ctx, a10nsp_config->interface);
if (!a10nsp_if) {
LOG(ERROR, "Failed to add a10nsp interface %s\n", a10nsp_config->interface);
return false;
}
a10nsp_if->type = INTERFACE_TYPE_A10NSP;
a10nsp_config->a10nsp_if = a10nsp_if;
a10nsp_if->qinq = a10nsp_config->qinq;
if(*(uint32_t*)a10nsp_config->mac) {
memcpy(a10nsp_if->mac, a10nsp_config->mac, ETH_ADDR_LEN);
}
if(ctx->interfaces.a10nsp_if_count < BBL_MAX_INTERFACES) {
ctx->interfaces.a10nsp_if[ctx->interfaces.a10nsp_if_count++] = a10nsp_if;
} else {
LOG(ERROR, "Failed to add a10nsp interface %s (limit reached)\n", a10nsp_config->interface);
return false;
}
bbl_send_init_interface(a10nsp_if, BBL_SEND_DEFAULT_SIZE);
a10nsp_config = a10nsp_config->next;
}
return true;
}
/**
* bbl_add_interfaces
*
* @brief This function will add and initialize
* all interfaces defined in the configuration.
*
* @param ctx global context
* @return true if all interfaces are
* added and initialised successfully
*/
bool
bbl_add_interfaces(bbl_ctx_s *ctx)
{
/* Add network interfaces */
if(!bbl_add_access_interfaces(ctx)) {
return false;
}
/* Add access interfaces */
if(!bbl_add_network_interfaces(ctx)) {
return false;
}
/* Add a10nsp interfaces */
if(!bbl_add_a10nsp_interfaces(ctx)) {
return false;
}
return true;
}
/**
* bbl_get_network_interface
*
* @brief This function returns the network interface
* with the given name or the first interface
* if name is NULL.
*
* @param ctx global context
* @param interface interface name
* @return interface
*/
bbl_interface_s *
bbl_get_network_interface(bbl_ctx_s *ctx, char *interface_name)
{
for(int i = 0; i < ctx->interfaces.network_if_count; i++) {
if(ctx->interfaces.network_if[i]) {
if(interface_name) {
if (strcmp(ctx->interfaces.network_if[i]->name, interface_name) == 0) {
return ctx->interfaces.network_if[i];
}
} else {
return ctx->interfaces.network_if[i];
}
}
}
return NULL;
}
/**
* bbl_get_a10nsp_interface
*
* @brief This function returns the network interface
* with the given name.
*
* @param ctx global context
* @param interface interface name
* @return interface
*/
bbl_interface_s *
bbl_get_a10nsp_interface(bbl_ctx_s *ctx, char *interface_name)
{
if(!interface_name) {
return NULL;
}
for(int i = 0; i < ctx->interfaces.a10nsp_if_count; i++) {
if(ctx->interfaces.a10nsp_if[i]) {
if (strcmp(ctx->interfaces.a10nsp_if[i]->name, interface_name) == 0) {
return ctx->interfaces.a10nsp_if[i];
}
}
}
return NULL;
}