1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/lib/kernel/if.cpp

254 lines
6.3 KiB
C++
Raw Normal View History

/** Interface related functions.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
2019-01-13 00:42:39 +01:00
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
2017-04-27 12:56:43 +02:00
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* 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 3 of the License, or
* any later version.
*
2017-04-27 12:56:43 +02:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
2017-04-27 12:56:43 +02:00
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2015-06-02 21:53:04 +02:00
*********************************************************************************/
#include <cstdio>
#include <cstdlib>
#include <dirent.h>
2014-12-05 12:39:52 +01:00
#include <linux/if_packet.h>
#include <netlink/route/link.h>
#include <villas/node/config.h>
#include <villas/utils.hpp>
#include <villas/kernel/if.h>
#include <villas/kernel/tc.h>
#include <villas/kernel/tc_netem.h>
#include <villas/kernel/nl.h>
#include <villas/kernel/kernel.h>
#include <villas/nodes/socket.hpp>
2019-06-04 16:55:38 +02:00
using namespace villas::utils;
2017-03-12 17:08:04 -03:00
int if_init(struct interface *i, struct rtnl_link *link)
{
i->nl_link = link;
2019-01-21 15:49:14 +01:00
debug(LOG_IF | 3, "Created interface '%s'", if_name(i));
int n = if_get_irqs(i);
if (n > 0)
2019-01-21 15:49:14 +01:00
debug(6, "Found %u IRQs for interface '%s'", n, if_name(i));
else
2019-01-21 15:49:14 +01:00
warning("Did not found any interrupts for interface '%s'", if_name(i));
2014-12-05 12:39:52 +01:00
vlist_init(&i->nodes);
2014-12-05 12:39:52 +01:00
2017-03-12 17:08:04 -03:00
return 0;
2014-12-05 12:39:52 +01:00
}
2017-02-18 10:45:05 -05:00
int if_destroy(struct interface *i)
{
/* List members are freed by the nodes they belong to. */
2019-04-07 15:13:40 +02:00
vlist_destroy(&i->nodes, nullptr, false);
rtnl_qdisc_put(i->tc_qdisc);
2017-02-18 10:45:05 -05:00
return 0;
}
int if_start(struct interface *i)
{
info("Starting interface '%s' which is used by %zu nodes", if_name(i), vlist_length(&i->nodes));
2015-08-07 01:11:43 +02:00
/* Set affinity for network interfaces (skip _loopback_ dev) */
//if_set_affinity(i, i->affinity);
/* Assign fwmark's to nodes which have netem options */
2019-02-15 09:42:33 +01:00
int ret, fwmark = 0;
for (size_t j = 0; j < vlist_length(&i->nodes); j++) {
struct node *n = (struct node *) vlist_at(&i->nodes, j);
2019-02-15 09:42:33 +01:00
if (n->tc_qdisc && n->fwmark < 0)
n->fwmark = 1 + fwmark++;
}
2015-08-07 01:11:43 +02:00
/* Abort if no node is using netem */
2019-02-15 11:18:32 +01:00
if (fwmark == 0)
return 0;
if (getuid() != 0)
error("Network emulation requires super-user privileges!");
/* Replace root qdisc */
2019-02-15 11:18:32 +01:00
ret = tc_prio(i, &i->tc_qdisc, TC_HANDLE(1, 0), TC_H_ROOT, fwmark);
if (ret)
error("Failed to setup priority queuing discipline: %s", nl_geterror(ret));
2015-08-07 01:11:43 +02:00
/* Create netem qdisks and appropriate filter per netem node */
for (size_t j = 0; j < vlist_length(&i->nodes); j++) {
struct node *n = (struct node *) vlist_at(&i->nodes, j);
if (n->tc_qdisc) {
2019-02-15 09:42:33 +01:00
ret = tc_mark(i, &n->tc_classifier, TC_HANDLE(1, n->fwmark), n->fwmark);
if (ret)
error("Failed to setup FW mark classifier: %s", nl_geterror(ret));
char *buf = tc_netem_print(n->tc_qdisc);
debug(LOG_IF | 5, "Starting network emulation on interface '%s' for FW mark %u: %s",
2019-02-15 09:42:33 +01:00
if_name(i), n->fwmark, buf);
free(buf);
2019-02-15 09:42:33 +01:00
ret = tc_netem(i, &n->tc_qdisc, TC_HANDLE(0x1000+n->fwmark, 0), TC_HANDLE(1, n->fwmark));
if (ret)
error("Failed to setup netem qdisc: %s", nl_geterror(ret));
}
}
2014-12-05 12:39:52 +01:00
return 0;
}
int if_stop(struct interface *i)
{
2019-01-21 15:49:14 +01:00
info("Stopping interface '%s'", if_name(i));
2014-12-05 12:39:52 +01:00
2019-03-15 17:19:08 +01:00
//if_set_affinity(i, -1L);
2015-08-07 01:11:43 +02:00
if (i->tc_qdisc)
tc_reset(i);
2014-12-05 12:39:52 +01:00
return 0;
}
2019-01-21 15:49:14 +01:00
const char * if_name(struct interface *i)
{
return rtnl_link_get_name(i->nl_link);
}
struct interface * if_get_egress(struct sockaddr *sa, struct vlist *interfaces)
{
int ret;
struct rtnl_link *link;
/* Determine outgoing interface */
link = if_get_egress_link(sa);
if (!link) {
char *buf = socket_print_addr(sa);
error("Failed to get interface for socket address '%s'", buf);
free(buf);
2019-04-07 15:13:40 +02:00
return nullptr;
}
/* Search of existing interface with correct ifindex */
struct interface *i;
for (size_t k = 0; k < vlist_length(interfaces); k++) {
i = (struct interface *) vlist_at(interfaces, k);
if (rtnl_link_get_ifindex(i->nl_link) == rtnl_link_get_ifindex(link))
return i;
}
/* If not found, create a new interface */
2019-04-07 15:13:40 +02:00
i = (struct interface *) alloc(sizeof(struct interface));
2019-01-21 18:54:48 +01:00
if (!i)
2019-04-07 15:13:40 +02:00
return nullptr;
ret = if_init(i, link);
if (ret)
2019-04-07 15:13:40 +02:00
return nullptr;
2019-01-21 23:00:55 +01:00
vlist_push(interfaces, i);
return i;
}
struct rtnl_link * if_get_egress_link(struct sockaddr *sa)
2014-12-05 12:39:52 +01:00
{
int ifindex = -1;
2014-12-05 12:39:52 +01:00
switch (sa->sa_family) {
case AF_INET:
case AF_INET6: {
2014-12-05 12:39:52 +01:00
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
struct nl_addr *addr = (sa->sa_family == AF_INET)
? nl_addr_build(sin->sin_family, &sin->sin_addr.s_addr, sizeof(sin->sin_addr.s_addr))
: nl_addr_build(sin6->sin6_family, sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr));
ifindex = nl_get_egress(addr); nl_addr_put(addr);
if (ifindex < 0)
error("Netlink error: %s", nl_geterror(ifindex));
break;
2014-12-05 12:39:52 +01:00
}
2014-12-05 12:39:52 +01:00
case AF_PACKET: {
struct sockaddr_ll *sll = (struct sockaddr_ll *) sa;
ifindex = sll->sll_ifindex;
break;
2014-12-05 12:39:52 +01:00
}
}
struct nl_cache *cache = nl_cache_mngt_require("route/link");
return rtnl_link_get(cache, ifindex);
}
int if_get_irqs(struct interface *i)
{
char dirname[NAME_MAX];
2014-12-05 12:39:52 +01:00
int irq, n = 0;
2019-01-21 15:49:14 +01:00
snprintf(dirname, sizeof(dirname), "/sys/class/net/%s/device/msi_irqs/", if_name(i));
DIR *dir = opendir(dirname);
2014-12-05 12:39:52 +01:00
if (dir) {
memset(&i->irqs, 0, sizeof(char) * IF_IRQ_MAX);
2014-12-05 12:39:52 +01:00
struct dirent *entry;
while ((entry = readdir(dir)) && n < IF_IRQ_MAX) {
2015-09-19 15:28:28 +02:00
irq = atoi(entry->d_name);
if (irq)
2014-12-05 12:39:52 +01:00
i->irqs[n++] = irq;
}
2014-12-05 12:39:52 +01:00
closedir(dir);
}
return 0;
}
int if_set_affinity(struct interface *i, int affinity)
{
char filename[NAME_MAX];
FILE *file;
for (int n = 0; n < IF_IRQ_MAX && i->irqs[n]; n++) {
snprintf(filename, sizeof(filename), "/proc/irq/%u/smp_affinity", i->irqs[n]);
file = fopen(filename, "w");
if (file) {
if (fprintf(file, "%8x", affinity) < 0)
error("Failed to set affinity for IRQ %u", i->irqs[n]);
fclose(file);
2019-01-21 15:49:14 +01:00
debug(LOG_IF | 5, "Set affinity of IRQ %u for interface '%s' to %#x", i->irqs[n], if_name(i), affinity);
}
else
2019-01-21 15:49:14 +01:00
error("Failed to set affinity for interface '%s'", if_name(i));
}
return 0;
}