2014-07-14 11:49:44 +00:00
|
|
|
/** Interface related functions.
|
2014-06-05 09:35:40 +00:00
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2017-03-03 20:20:13 -04:00
|
|
|
* @copyright 2017, 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-05-05 19:24:16 +00:00
|
|
|
*
|
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-05-05 19:24:16 +00:00
|
|
|
*
|
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
|
|
|
*********************************************************************************/
|
2014-06-05 09:35:40 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <dirent.h>
|
2014-12-05 12:39:52 +01:00
|
|
|
#include <linux/if_packet.h>
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
#include <netlink/route/link.h>
|
|
|
|
|
2018-02-06 21:26:12 +01:00
|
|
|
#include <villas/config.h>
|
|
|
|
#include <villas/utils.h>
|
2016-06-14 01:17:58 +02:00
|
|
|
|
2018-02-06 21:26:12 +01:00
|
|
|
#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>
|
2016-06-14 01:17:58 +02:00
|
|
|
|
2018-02-06 21:26:12 +01:00
|
|
|
#include <villas/nodes/socket.h>
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2017-03-12 17:08:04 -03:00
|
|
|
int if_init(struct interface *i, struct rtnl_link *link)
|
2015-08-22 17:42:02 +02:00
|
|
|
{
|
|
|
|
i->nl_link = link;
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2017-03-12 17:06:00 -03:00
|
|
|
debug(LOG_IF | 3, "Created interface '%s'", rtnl_link_get_name(i->nl_link));
|
2014-07-04 09:47:29 +00:00
|
|
|
|
2015-09-30 11:12:16 +02:00
|
|
|
int n = if_get_irqs(i);
|
|
|
|
if (n > 0)
|
|
|
|
debug(6, "Found %u IRQs for interface '%s'", n, rtnl_link_get_name(i->nl_link));
|
|
|
|
else
|
|
|
|
warn("Did not found any interrupts for interface '%s'", rtnl_link_get_name(i->nl_link));
|
2014-12-05 12:39:52 +01:00
|
|
|
|
2016-04-16 19:54:26 +02:00
|
|
|
list_init(&i->sockets);
|
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
|
|
|
}
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2017-02-18 10:45:05 -05:00
|
|
|
int if_destroy(struct interface *i)
|
2015-03-21 15:23:57 +01:00
|
|
|
{
|
2015-08-22 17:42:02 +02:00
|
|
|
/* List members are freed by the nodes they belong to. */
|
2016-04-16 19:54:26 +02:00
|
|
|
list_destroy(&i->sockets, NULL, false);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
rtnl_qdisc_put(i->tc_qdisc);
|
2015-03-21 15:23:57 +01:00
|
|
|
|
|
|
|
free(i);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-02-18 10:45:05 -05:00
|
|
|
return 0;
|
2015-03-21 15:23:57 +01:00
|
|
|
}
|
|
|
|
|
2017-03-29 04:25:30 +02:00
|
|
|
int if_start(struct interface *i)
|
2015-05-06 11:49:13 +02:00
|
|
|
{
|
2015-10-09 12:50:35 +02:00
|
|
|
info("Starting interface '%s' which is used by %zu sockets", rtnl_link_get_name(i->nl_link), list_length(&i->sockets));
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2018-07-16 14:42:11 +02:00
|
|
|
{
|
2015-09-14 18:05:03 +02:00
|
|
|
/* Set affinity for network interfaces (skip _loopback_ dev) */
|
2017-03-29 04:25:30 +02:00
|
|
|
//if_set_affinity(i, i->affinity);
|
2017-05-05 19:03:49 +00:00
|
|
|
|
2015-06-02 22:07:58 +02:00
|
|
|
/* Assign fwmark's to socket nodes which have netem options */
|
2015-09-14 18:05:03 +02:00
|
|
|
int ret, mark = 0;
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t j = 0; j < list_length(&i->sockets); j++) {
|
2017-10-18 15:39:53 +02:00
|
|
|
struct socket *s = (struct socket *) list_at(&i->sockets, j);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
if (s->tc_qdisc)
|
2014-12-05 12:39:52 +01:00
|
|
|
s->mark = 1 + mark++;
|
2015-06-02 22:07:58 +02:00
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-06-02 22:07:58 +02:00
|
|
|
/* Abort if no node is using netem */
|
|
|
|
if (mark == 0)
|
|
|
|
return 0;
|
|
|
|
|
2017-05-24 14:47:24 +00:00
|
|
|
if (getuid() != 0)
|
|
|
|
error("Network emulation requires super-user privileges!");
|
|
|
|
|
2015-06-02 22:07:58 +02:00
|
|
|
/* Replace root qdisc */
|
2015-09-19 15:28:28 +02:00
|
|
|
ret = tc_prio(i, &i->tc_qdisc, TC_HANDLE(1, 0), TC_H_ROOT, mark);
|
|
|
|
if (ret)
|
|
|
|
error("Failed to setup priority queuing discipline: %s", nl_geterror(ret));
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-06-02 22:07:58 +02:00
|
|
|
/* Create netem qdisks and appropriate filter per netem node */
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t j = 0; j < list_length(&i->sockets); j++) {
|
2017-10-18 15:39:53 +02:00
|
|
|
struct socket *s = (struct socket *) list_at(&i->sockets, j);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
if (s->tc_qdisc) {
|
2015-09-19 15:28:28 +02:00
|
|
|
ret = tc_mark(i, &s->tc_classifier, TC_HANDLE(1, s->mark), s->mark);
|
|
|
|
if (ret)
|
2015-09-14 18:05:03 +02:00
|
|
|
error("Failed to setup FW mark classifier: %s", nl_geterror(ret));
|
2017-05-05 19:03:49 +00:00
|
|
|
|
2017-12-20 11:23:55 +01:00
|
|
|
char *buf = tc_netem_print(s->tc_qdisc);
|
2017-03-12 17:06:00 -03:00
|
|
|
debug(LOG_IF | 5, "Starting network emulation on interface '%s' for FW mark %u: %s",
|
2015-08-22 17:42:02 +02:00
|
|
|
rtnl_link_get_name(i->nl_link), s->mark, buf);
|
2015-09-22 12:58:37 +02:00
|
|
|
free(buf);
|
2015-09-14 18:05:03 +02:00
|
|
|
|
2015-09-19 15:28:28 +02:00
|
|
|
ret = tc_netem(i, &s->tc_qdisc, TC_HANDLE(0x1000+s->mark, 0), TC_HANDLE(1, s->mark));
|
|
|
|
if (ret)
|
2015-09-14 18:05:03 +02:00
|
|
|
error("Failed to setup netem qdisc: %s", nl_geterror(ret));
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2014-06-05 09:35:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 12:39:52 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int if_stop(struct interface *i)
|
2015-05-06 11:49:13 +02:00
|
|
|
{
|
2015-08-22 17:42:02 +02:00
|
|
|
info("Stopping interface '%s'", rtnl_link_get_name(i->nl_link));
|
2014-12-05 12:39:52 +01:00
|
|
|
|
2018-07-16 14:42:11 +02:00
|
|
|
if_set_affinity(i, -1L);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2018-07-16 14:42:11 +02:00
|
|
|
if (i->tc_qdisc)
|
|
|
|
tc_reset(i);
|
2014-12-05 12:39:52 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
int if_get_egress(struct sockaddr *sa, struct rtnl_link **link)
|
2014-12-05 12:39:52 +01:00
|
|
|
{
|
2015-09-14 18:05:03 +02:00
|
|
|
int ifindex = -1;
|
|
|
|
|
2014-12-05 12:39:52 +01:00
|
|
|
switch (sa->sa_family) {
|
2015-08-22 17:42:02 +02:00
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6: {
|
2014-12-05 12:39:52 +01:00
|
|
|
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
|
2015-08-22 17:42:02 +02:00
|
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
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));
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-13 10:12:35 +02:00
|
|
|
ifindex = nl_get_egress(addr); nl_addr_put(addr);
|
2015-09-14 18:05:03 +02:00
|
|
|
if (ifindex < 0)
|
|
|
|
error("Netlink error: %s", nl_geterror(ifindex));
|
|
|
|
break;
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2014-12-05 12:39:52 +01:00
|
|
|
case AF_PACKET: {
|
|
|
|
struct sockaddr_ll *sll = (struct sockaddr_ll *) sa;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-14 18:05:03 +02:00
|
|
|
ifindex = sll->sll_ifindex;
|
|
|
|
break;
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-14 18:05:03 +02:00
|
|
|
struct nl_cache *cache = nl_cache_mngt_require("route/link");
|
2015-09-19 15:28:28 +02:00
|
|
|
*link = rtnl_link_get(cache, ifindex);
|
|
|
|
if (!*link)
|
2015-09-14 18:05:03 +02:00
|
|
|
return -1;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
return 0;
|
2014-06-05 09:35:40 +00:00
|
|
|
}
|
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
int if_get_irqs(struct interface *i)
|
2014-06-05 09:35:40 +00:00
|
|
|
{
|
2014-06-25 01:53:46 +00:00
|
|
|
char dirname[NAME_MAX];
|
2014-12-05 12:39:52 +01:00
|
|
|
int irq, n = 0;
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
snprintf(dirname, sizeof(dirname), "/sys/class/net/%s/device/msi_irqs/", rtnl_link_get_name(i->nl_link));
|
2014-09-04 13:30:36 +00:00
|
|
|
DIR *dir = opendir(dirname);
|
2014-12-05 12:39:52 +01:00
|
|
|
if (dir) {
|
|
|
|
memset(&i->irqs, 0, sizeof(char) * IF_IRQ_MAX);
|
2014-06-05 09:35:40 +00:00
|
|
|
|
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-06-25 01:53:46 +00:00
|
|
|
|
2014-12-05 12:39:52 +01:00
|
|
|
closedir(dir);
|
2014-06-25 01:53:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-22 17:42:02 +02:00
|
|
|
int if_set_affinity(struct interface *i, int affinity)
|
2014-06-25 01:53:46 +00:00
|
|
|
{
|
|
|
|
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]);
|
|
|
|
|
2014-06-05 09:35:40 +00:00
|
|
|
file = fopen(filename, "w");
|
2014-07-04 09:47:29 +00:00
|
|
|
if (file) {
|
|
|
|
if (fprintf(file, "%8x", affinity) < 0)
|
|
|
|
error("Failed to set affinity for IRQ %u", i->irqs[n]);
|
2014-06-05 09:35:40 +00:00
|
|
|
|
2014-07-04 09:47:29 +00:00
|
|
|
fclose(file);
|
2017-03-12 17:06:00 -03:00
|
|
|
debug(LOG_IF | 5, "Set affinity of IRQ %u for interface '%s' to %#x", i->irqs[n], rtnl_link_get_name(i->nl_link), affinity);
|
2014-07-04 09:47:29 +00:00
|
|
|
}
|
2014-09-04 13:30:36 +00:00
|
|
|
else
|
2015-08-22 17:42:02 +02:00
|
|
|
error("Failed to set affinity for interface '%s'", rtnl_link_get_name(i->nl_link));
|
2014-06-05 09:35:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|