Merge pull request #54 from freifunk-gluon/minimize
Image size improvements
This commit is contained in:
commit
74b7d01de6
|
@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/package.mk
|
|||
define Package/gluon-announce
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
DEPENDS:=+gluon-core +luci-lib-json +ethtool
|
||||
DEPENDS:=+gluon-core +luci-lib-json +lua-ethtool-stats
|
||||
TITLE:=Lua scripts announcing various information
|
||||
endef
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
local ip = util.exec('ip -o -6 addr show dev br-client')
|
||||
local ip = require 'luci.ip'
|
||||
|
||||
local addresses = {}
|
||||
for _, line in ipairs(util.split(ip)) do
|
||||
table.insert(addresses, line:match('inet6 ([%x:]+)/'))
|
||||
|
||||
for line in io.lines('/proc/net/if_inet6') do
|
||||
local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 4) .. '%s+([^%s]+)$') }
|
||||
if matches[9] == 'br-client' then
|
||||
table.insert(addresses, ip.IPv6(string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches))):string():lower())
|
||||
end
|
||||
end
|
||||
|
||||
return addresses
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
local ethtool = util.exec('ethtool -S bat0')
|
||||
local ethtool = require 'ethtool_stats'
|
||||
|
||||
local fields = {}
|
||||
for k, v in ethtool:gmatch('([%a_]+): ([0-9]+)') do
|
||||
fields[k] = tonumber(v)
|
||||
end
|
||||
local fields = ethtool.interface_stats('bat0')
|
||||
|
||||
local traffic = {}
|
||||
for _, class in ipairs({'rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx'}) do
|
||||
|
|
|
@ -11,7 +11,7 @@ define Package/gluon-next-node
|
|||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Next-node anycast address
|
||||
DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +ip +kmod-macvlan
|
||||
DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +kmod-macvlan
|
||||
endef
|
||||
|
||||
define Package/gluon-next-node/description
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-radvd
|
||||
PKG_VERSION:=2
|
||||
PKG_VERSION:=3
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
|
@ -11,7 +11,7 @@ define Package/gluon-radvd
|
|||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Advertise an IPv6 prefix from the node
|
||||
DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +radvd
|
||||
DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +librt
|
||||
endef
|
||||
|
||||
define Package/gluon-radvd/description
|
||||
|
@ -20,16 +20,20 @@ endef
|
|||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||
endef
|
||||
|
||||
define Package/gluon-radvd/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-radvd $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-radvd))
|
||||
|
|
|
@ -2,24 +2,14 @@
|
|||
|
||||
START=50
|
||||
|
||||
SERVICE_USE_PID=1
|
||||
SERVICE_NAME=gluon-radvd
|
||||
SERVICE_PID_DIR=/var/run/gluon-radvd
|
||||
SERVICE_PID_FILE="$SERVICE_PID_DIR"/gluon-radvd.pid
|
||||
SERVICE_WRITE_PID=1
|
||||
SERVICE_DAEMONIZE=1
|
||||
|
||||
radvd_conf=/var/gluon/radvd/radvd.conf
|
||||
|
||||
start() {
|
||||
mkdir -p "$SERVICE_PID_DIR"
|
||||
chown gluon-radvd "$SERVICE_PID_DIR"
|
||||
|
||||
mkdir -p "$(dirname "$radvd_conf")"
|
||||
/lib/gluon/radvd/generate_config > "$radvd_conf"
|
||||
|
||||
service_start /usr/sbin/radvd -C "$radvd_conf" -m stderr_syslog -u gluon-radvd -p "$SERVICE_PID_FILE"
|
||||
service_start /usr/sbin/gluon-radvd -i br-client -p $(lua -e 'print(require("gluon.site_config").prefix6)')
|
||||
}
|
||||
|
||||
stop() {
|
||||
service_stop /usr/sbin/radvd
|
||||
rm "$radvd_conf"
|
||||
service_stop /usr/sbin/gluon-radvd
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/lua
|
||||
|
||||
local site = require 'gluon.site_config'
|
||||
|
||||
print([[
|
||||
interface br-client
|
||||
{
|
||||
IgnoreIfMissing on;
|
||||
AdvSendAdvert on;
|
||||
AdvDefaultLifetime 0;
|
||||
|
||||
prefix ]] .. site.prefix6 .. [[ {};
|
||||
};
|
||||
]])
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
local users = require 'gluon.users'
|
||||
|
||||
users.add_user('gluon-radvd', 801, 100)
|
||||
users.remove_user('gluon-radvd')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
all: gluon-radvd
|
||||
|
||||
gluon-radvd: gluon-radvd.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lrt
|
|
@ -0,0 +1,647 @@
|
|||
/*
|
||||
Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#define MAX_PREFIXES 8
|
||||
|
||||
/* These are in seconds */
|
||||
#define AdvValidLifetime 86400u
|
||||
#define AdvPreferredLifetime 14400u
|
||||
#define AdvDefaultLifetime 0u
|
||||
#define AdvCurHopLimit 64u
|
||||
|
||||
#define MinRtrAdvInterval 200u
|
||||
#define MaxRtrAdvInterval 600u
|
||||
|
||||
/* And these in milliseconds */
|
||||
#define MAX_RA_DELAY_TIME 500u
|
||||
#define MIN_DELAY_BETWEEN_RAS 3000u
|
||||
|
||||
|
||||
struct icmpv6_opt {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t data[6];
|
||||
};
|
||||
|
||||
|
||||
struct iface {
|
||||
bool ok;
|
||||
unsigned int ifindex;
|
||||
struct in6_addr ifaddr;
|
||||
uint8_t mac[6];
|
||||
};
|
||||
|
||||
static struct global {
|
||||
struct iface iface;
|
||||
|
||||
struct timespec time;
|
||||
struct timespec next_advert;
|
||||
struct timespec next_advert_earliest;
|
||||
|
||||
int icmp_sock;
|
||||
int rtnl_sock;
|
||||
|
||||
const char *ifname;
|
||||
|
||||
size_t n_prefixes;
|
||||
struct in6_addr prefixes[MAX_PREFIXES];
|
||||
} G = {
|
||||
.rtnl_sock = -1,
|
||||
.icmp_sock = -1,
|
||||
};
|
||||
|
||||
|
||||
static inline void exit_errno(const char *message) {
|
||||
error(1, errno, "error: %s", message);
|
||||
}
|
||||
|
||||
static inline void warn_errno(const char *message) {
|
||||
error(0, errno, "warning: %s", message);
|
||||
}
|
||||
|
||||
|
||||
static inline void update_time(void) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &G.time);
|
||||
}
|
||||
|
||||
/* Compares two timespecs and returns true if tp1 is after tp2 */
|
||||
static inline bool timespec_after(const struct timespec *tp1, const struct timespec *tp2) {
|
||||
return (tp1->tv_sec > tp2->tv_sec ||
|
||||
(tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec));
|
||||
}
|
||||
|
||||
/* Returns (tp1 - tp2) in milliseconds */
|
||||
static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) {
|
||||
return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6;
|
||||
}
|
||||
|
||||
static inline void timespec_add(struct timespec *tp, unsigned int ms) {
|
||||
tp->tv_sec += ms/1000;
|
||||
tp->tv_nsec += (ms%1000) * 1e6;
|
||||
|
||||
if (tp->tv_nsec >= 1e9) {
|
||||
tp->tv_nsec -= 1e9;
|
||||
tp->tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline int setsockopt_int(int socket, int level, int option, int value) {
|
||||
return setsockopt(socket, level, option, &value, sizeof(value));
|
||||
}
|
||||
|
||||
|
||||
static void init_random(void) {
|
||||
unsigned int seed;
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
exit_errno("can't open /dev/urandom");
|
||||
|
||||
if (read(fd, &seed, sizeof(seed)) != sizeof(seed))
|
||||
exit_errno("can't read from /dev/urandom");
|
||||
|
||||
close(fd);
|
||||
|
||||
srandom(seed);
|
||||
}
|
||||
|
||||
static inline int rand_range(int min, int max) {
|
||||
unsigned int r = (unsigned int)random();
|
||||
return (r%(max-min) + min);
|
||||
}
|
||||
|
||||
static void init_icmp(void) {
|
||||
G.icmp_sock = socket(AF_INET6, SOCK_RAW|SOCK_NONBLOCK, IPPROTO_ICMPV6);
|
||||
if (G.icmp_sock < 0)
|
||||
exit_errno("can't open ICMP socket");
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_RAW, IPV6_CHECKSUM, 2);
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1);
|
||||
|
||||
setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1);
|
||||
|
||||
struct icmp6_filter filter;
|
||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
|
||||
setsockopt(G.icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
|
||||
}
|
||||
|
||||
static void init_rtnl(void) {
|
||||
G.rtnl_sock = socket(AF_NETLINK, SOCK_DGRAM|SOCK_NONBLOCK, NETLINK_ROUTE);
|
||||
if (G.rtnl_sock < 0)
|
||||
exit_errno("can't open RTNL socket");
|
||||
|
||||
struct sockaddr_nl snl = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR,
|
||||
};
|
||||
if (bind(G.rtnl_sock, (struct sockaddr *)&snl, sizeof(snl)) < 0)
|
||||
exit_errno("can't bind RTNL socket");
|
||||
}
|
||||
|
||||
|
||||
static void schedule_advert(bool nodelay) {
|
||||
struct timespec t = G.time;
|
||||
|
||||
if (nodelay)
|
||||
timespec_add(&t, rand_range(0, MAX_RA_DELAY_TIME));
|
||||
else
|
||||
timespec_add(&t, rand_range(MinRtrAdvInterval*1000, MaxRtrAdvInterval*1000));
|
||||
|
||||
if (timespec_after(&G.next_advert_earliest, &t))
|
||||
t = G.next_advert_earliest;
|
||||
|
||||
if (!nodelay || timespec_after(&G.next_advert, &t))
|
||||
G.next_advert = t;
|
||||
}
|
||||
|
||||
|
||||
static int join_multicast(void) {
|
||||
struct ipv6_mreq mreq = {
|
||||
.ipv6mr_multiaddr = {
|
||||
.s6_addr = {
|
||||
/* all-routers address */
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
}
|
||||
},
|
||||
.ipv6mr_interface = G.iface.ifindex,
|
||||
};
|
||||
|
||||
if (setsockopt(G.icmp_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == 0) {
|
||||
return 2;
|
||||
}
|
||||
else if (errno != EADDRINUSE) {
|
||||
warn_errno("can't join multicast group");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void update_interface(void) {
|
||||
struct iface old;
|
||||
|
||||
memcpy(&old, &G.iface, sizeof(struct iface));
|
||||
memset(&G.iface, 0, sizeof(struct iface));
|
||||
|
||||
/* Update ifindex */
|
||||
G.iface.ifindex = if_nametoindex(G.ifname);
|
||||
if (!G.iface.ifindex)
|
||||
return;
|
||||
|
||||
/* Update MAC address */
|
||||
struct ifreq ifr = {};
|
||||
strncpy(ifr.ifr_name, G.ifname, sizeof(ifr.ifr_name)-1);
|
||||
if (ioctl(G.icmp_sock, SIOCGIFHWADDR, &ifr) < 0)
|
||||
return;
|
||||
|
||||
memcpy(G.iface.mac, ifr.ifr_hwaddr.sa_data, sizeof(G.iface.mac));
|
||||
|
||||
struct ifaddrs *addrs, *addr;
|
||||
if (getifaddrs(&addrs) < 0) {
|
||||
warn_errno("getifaddrs");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&G.iface.ifaddr, 0, sizeof(G.iface.ifaddr));
|
||||
|
||||
for (addr = addrs; addr; addr = addr->ifa_next) {
|
||||
if (addr->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
|
||||
const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)addr->ifa_addr;
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
|
||||
continue;
|
||||
|
||||
if (strncmp(addr->ifa_name, G.ifname, IFNAMSIZ-1) != 0)
|
||||
continue;
|
||||
|
||||
G.iface.ifaddr = in6->sin6_addr;
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&G.iface.ifaddr))
|
||||
return;
|
||||
|
||||
int joined = join_multicast();
|
||||
if (!joined)
|
||||
return;
|
||||
|
||||
setsockopt(G.icmp_sock, SOL_SOCKET, SO_BINDTODEVICE, G.ifname, strnlen(G.ifname, IFNAMSIZ-1));
|
||||
|
||||
G.iface.ok = true;
|
||||
|
||||
if (memcmp(&old, &G.iface, sizeof(struct iface)) != 0 || joined == 2)
|
||||
schedule_advert(true);
|
||||
}
|
||||
|
||||
|
||||
static bool handle_rtnl_link(uint16_t type, const struct ifinfomsg *msg) {
|
||||
switch (type) {
|
||||
case RTM_NEWLINK:
|
||||
if (!G.iface.ok)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_SETLINK:
|
||||
if ((unsigned)msg->ifi_index == G.iface.ifindex)
|
||||
return true;
|
||||
|
||||
if (!G.iface.ok)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
if (G.iface.ok && (unsigned)msg->ifi_index == G.iface.ifindex)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_rtnl_addr(uint16_t type, const struct ifaddrmsg *msg) {
|
||||
switch (type) {
|
||||
case RTM_NEWADDR:
|
||||
if (!G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex)
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELADDR:
|
||||
if (G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_rtnl_msg(uint16_t type, const void *data) {
|
||||
switch (type) {
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
case RTM_SETLINK:
|
||||
return handle_rtnl_link(type, data);
|
||||
|
||||
case RTM_NEWADDR:
|
||||
case RTM_DELADDR:
|
||||
return handle_rtnl_addr(type, data);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_rtnl(void) {
|
||||
char buffer[4096];
|
||||
|
||||
ssize_t len = recv(G.rtnl_sock, buffer, sizeof(buffer), 0);
|
||||
if (len < 0) {
|
||||
warn_errno("recv");
|
||||
return;
|
||||
}
|
||||
|
||||
const struct nlmsghdr *nh;
|
||||
for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
|
||||
switch (nh->nlmsg_type) {
|
||||
case NLMSG_DONE:
|
||||
return;
|
||||
|
||||
case NLMSG_ERROR:
|
||||
error(1, 0, "error: netlink error");
|
||||
|
||||
default:
|
||||
if (handle_rtnl_msg(nh->nlmsg_type, NLMSG_DATA(nh))) {
|
||||
update_interface();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_pktinfo(struct msghdr *msg) {
|
||||
struct cmsghdr *cmsg = (struct cmsghdr*)((char*)msg->msg_control + msg->msg_controllen);
|
||||
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
|
||||
msg->msg_controllen += cmsg->cmsg_len;
|
||||
|
||||
struct in6_pktinfo pktinfo = {
|
||||
.ipi6_addr = G.iface.ifaddr,
|
||||
.ipi6_ifindex = G.iface.ifindex,
|
||||
};
|
||||
|
||||
memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
|
||||
}
|
||||
|
||||
|
||||
static void handle_solicit(void) {
|
||||
struct sockaddr_in6 addr;
|
||||
|
||||
uint8_t buffer[1500] __attribute__((aligned(8)));
|
||||
struct iovec vec = { .iov_base = buffer, .iov_len = sizeof(buffer) };
|
||||
|
||||
uint8_t cbuf[1024] __attribute__((aligned(8)));
|
||||
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &addr,
|
||||
.msg_namelen = sizeof(addr),
|
||||
.msg_iov = &vec,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cbuf,
|
||||
.msg_controllen = sizeof(cbuf),
|
||||
};
|
||||
|
||||
ssize_t len = recvmsg(G.icmp_sock, &msg, 0);
|
||||
if (len < (ssize_t)sizeof(struct nd_router_solicit)) {
|
||||
if (len < 0)
|
||||
warn_errno("recvmsg");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level != IPPROTO_IPV6)
|
||||
continue;
|
||||
|
||||
if (cmsg->cmsg_type != IPV6_HOPLIMIT)
|
||||
continue;
|
||||
|
||||
if (*(int*)CMSG_DATA(cmsg) != 255)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const struct nd_router_solicit *s = (struct nd_router_solicit *)buffer;
|
||||
if (s->nd_rs_hdr.icmp6_type != ND_ROUTER_SOLICIT || s->nd_rs_hdr.icmp6_code != 0)
|
||||
return;
|
||||
|
||||
const struct icmpv6_opt *opt = (struct icmpv6_opt *)(buffer + sizeof(struct nd_router_solicit)), *end = (struct icmpv6_opt *)(buffer+len);
|
||||
|
||||
for (; opt < end; opt += opt->length) {
|
||||
if (opt+1 < end)
|
||||
return;
|
||||
|
||||
if (!opt->length)
|
||||
return;
|
||||
|
||||
if (opt+opt->length < end)
|
||||
return;
|
||||
|
||||
if (opt->type == ND_OPT_SOURCE_LINKADDR && IN6_IS_ADDR_UNSPECIFIED(&addr.sin6_addr))
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt != end)
|
||||
return;
|
||||
|
||||
schedule_advert(true);
|
||||
}
|
||||
|
||||
static void send_advert(void) {
|
||||
if (!G.iface.ok)
|
||||
return;
|
||||
|
||||
struct nd_router_advert advert = {
|
||||
.nd_ra_hdr = {
|
||||
.icmp6_type = ND_ROUTER_ADVERT,
|
||||
.icmp6_dataun.icmp6_un_data8 = {AdvCurHopLimit, 0 /* Flags */, (AdvDefaultLifetime>>8) & 0xff, AdvDefaultLifetime & 0xff },
|
||||
},
|
||||
};
|
||||
|
||||
struct icmpv6_opt lladdr = {ND_OPT_SOURCE_LINKADDR, 1, {}};
|
||||
memcpy(lladdr.data, G.iface.mac, sizeof(G.iface.mac));
|
||||
|
||||
struct nd_opt_prefix_info prefixes[G.n_prefixes];
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < G.n_prefixes; i++) {
|
||||
prefixes[i] = (struct nd_opt_prefix_info){
|
||||
.nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION,
|
||||
.nd_opt_pi_len = 4,
|
||||
.nd_opt_pi_prefix_len = 64,
|
||||
.nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_AUTO|ND_OPT_PI_FLAG_ONLINK,
|
||||
.nd_opt_pi_valid_time = htonl(AdvValidLifetime),
|
||||
.nd_opt_pi_preferred_time = htonl(AdvPreferredLifetime),
|
||||
.nd_opt_pi_prefix = G.prefixes[i],
|
||||
};
|
||||
}
|
||||
|
||||
struct iovec vec[3] = {
|
||||
{ .iov_base = &advert, .iov_len = sizeof(advert) },
|
||||
{ .iov_base = &lladdr, .iov_len = sizeof(lladdr) },
|
||||
{ .iov_base = prefixes, .iov_len = sizeof(prefixes) },
|
||||
};
|
||||
|
||||
struct sockaddr_in6 addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = {
|
||||
.s6_addr = {
|
||||
/* all-nodes address */
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
}
|
||||
},
|
||||
.sin6_scope_id = G.iface.ifindex,
|
||||
};
|
||||
|
||||
uint8_t cbuf[1024] __attribute__((aligned(8))) = {};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_name = &addr,
|
||||
.msg_namelen = sizeof(addr),
|
||||
.msg_iov = vec,
|
||||
.msg_iovlen = 3,
|
||||
.msg_control = cbuf,
|
||||
.msg_controllen = 0,
|
||||
.msg_flags = 0,
|
||||
};
|
||||
|
||||
add_pktinfo(&msg);
|
||||
|
||||
if (sendmsg(G.icmp_sock, &msg, 0) < 0) {
|
||||
G.iface.ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
G.next_advert_earliest = G.time;
|
||||
timespec_add(&G.next_advert_earliest, MIN_DELAY_BETWEEN_RAS);
|
||||
|
||||
schedule_advert(false);
|
||||
}
|
||||
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: gluon-radvd [-h] -i <interface> -p <prefix> [ -p <prefix> ... ]\n");
|
||||
}
|
||||
|
||||
static void add_prefix(const char *prefix) {
|
||||
if (G.n_prefixes == MAX_PREFIXES)
|
||||
error(1, 0, "maximum number of prefixes is %i.", MAX_PREFIXES);
|
||||
|
||||
const size_t len = strlen(prefix)+1;
|
||||
char prefix2[len];
|
||||
memcpy(prefix2, prefix, len);
|
||||
|
||||
char *slash = strchr(prefix2, '/');
|
||||
if (slash) {
|
||||
*slash = 0;
|
||||
if (strcmp(slash+1, "64") != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET6, prefix2, &G.prefixes[G.n_prefixes]) != 1)
|
||||
goto error;
|
||||
|
||||
static const uint8_t zero[8] = {};
|
||||
if (memcmp(G.prefixes[G.n_prefixes].s6_addr + 8, zero, 8) != 0)
|
||||
goto error;
|
||||
|
||||
G.n_prefixes++;
|
||||
return;
|
||||
|
||||
error:
|
||||
error(1, 0, "invalid prefix %s (only prefixes of length 64 are supported).", prefix);
|
||||
}
|
||||
|
||||
static void parse_cmdline(int argc, char *argv[]) {
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "i:p:h")) != -1) {
|
||||
switch(c) {
|
||||
case 'i':
|
||||
if (G.ifname)
|
||||
error(1, 0, "multiple interfaces are not supported.");
|
||||
|
||||
G.ifname = optarg;
|
||||
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
add_prefix(optarg);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
parse_cmdline(argc, argv);
|
||||
|
||||
if (!G.ifname || !G.n_prefixes)
|
||||
error(1, 0, "interface and prefix arguments are required.");
|
||||
|
||||
init_random();
|
||||
init_icmp();
|
||||
init_rtnl();
|
||||
|
||||
update_time();
|
||||
G.next_advert = G.next_advert_earliest = G.time;
|
||||
|
||||
update_interface();
|
||||
|
||||
while (true) {
|
||||
struct pollfd fds[2] = {
|
||||
{ .fd = G.icmp_sock, .events = POLLIN },
|
||||
{ .fd = G.rtnl_sock, .events = POLLIN },
|
||||
};
|
||||
|
||||
int timeout = -1;
|
||||
|
||||
if (G.iface.ok) {
|
||||
timeout = timespec_diff(&G.next_advert, &G.time);
|
||||
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
int ret = poll(fds, 2, timeout);
|
||||
if (ret < 0)
|
||||
exit_errno("poll");
|
||||
|
||||
update_time();
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
handle_solicit();
|
||||
if (fds[1].revents & POLLIN)
|
||||
handle_rtnl();
|
||||
|
||||
if (timespec_after(&G.time, &G.next_advert))
|
||||
send_advert();
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ define Package/gluon-setup-mode
|
|||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Setup mode
|
||||
DEPENDS:=+gluon-core +uhttpd +dnsmasq +ip
|
||||
DEPENDS:=+gluon-core +uhttpd +dnsmasq
|
||||
endef
|
||||
|
||||
define Package/gluon-setup-mode/description
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-simple-tc
|
||||
PKG_VERSION:=3
|
||||
PKG_VERSION:=4
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
|
@ -11,7 +11,7 @@ define Package/gluon-simple-tc
|
|||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Bandwidth limit support
|
||||
DEPENDS:=+gluon-core +tc +kmod-sched
|
||||
DEPENDS:=+gluon-core +kmod-sched +libnl-tiny
|
||||
endef
|
||||
|
||||
define Package/gluon-simple-tc/description
|
||||
|
@ -20,16 +20,23 @@ endef
|
|||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
|
||||
TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
|
||||
|
||||
define Build/Compile
|
||||
CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||
endef
|
||||
|
||||
define Package/gluon-simple-tc/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-simple-tc $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
define Package/gluon-simple-tc/postinst
|
||||
|
|
|
@ -14,17 +14,13 @@ tc_interface() {
|
|||
|
||||
[ "$enabled" -eq 1 ] || return
|
||||
|
||||
config_get limit_egress "$iface" limit_egress
|
||||
config_get limit_ingress "$iface" limit_ingress
|
||||
config_get limit_egress "$iface" limit_egress
|
||||
|
||||
if [ "$limit_egress" ]; then
|
||||
tc qdisc add dev "$INTERFACE" root tbf rate "${limit_egress}kbit" latency 50ms burst 2k
|
||||
fi
|
||||
[ "$limit_ingress" ] || limit_ingress=-
|
||||
[ "$limit_egress" ] || limit_egress=-
|
||||
|
||||
if [ "$limit_ingress" ]; then
|
||||
tc qdisc add dev "$INTERFACE" handle ffff: ingress
|
||||
tc filter add dev "$INTERFACE" parent ffff: u32 match u8 00 00 at 0 police rate "${limit_ingress}kbit" burst 10k drop flowid :1
|
||||
fi
|
||||
gluon-simple-tc "$INTERFACE" "$limit_ingress" "$limit_egress"
|
||||
}
|
||||
|
||||
config_foreach tc_interface 'interface'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
sch_ingress
|
||||
sch_tbf
|
||||
cls_u32
|
||||
cls_basic
|
||||
act_police
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
all: gluon-simple-tc
|
||||
|
||||
gluon-simple-tc: gluon-simple-tc.c
|
||||
$(CC) -Iinclude $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lnl-tiny
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
|
||||
#include <netlink/msg.h>
|
||||
#include <netlink/attr.h>
|
||||
#include <netlink/socket.h>
|
||||
|
||||
|
||||
static struct nl_cb *cb;
|
||||
static struct nl_sock *sock;
|
||||
static double ticks;
|
||||
|
||||
static unsigned ifindex;
|
||||
|
||||
static bool nlexpect;
|
||||
static int nlerror;
|
||||
|
||||
|
||||
static inline void exit_errno(const char *message) {
|
||||
error(1, errno, "error: %s", message);
|
||||
}
|
||||
|
||||
static inline void warn_errno(const char *message) {
|
||||
error(0, errno, "warning: %s", message);
|
||||
}
|
||||
|
||||
|
||||
static void read_psched(void) {
|
||||
uint32_t clock_res;
|
||||
uint32_t t2us;
|
||||
uint32_t us2t;
|
||||
|
||||
FILE *f = fopen("/proc/net/psched", "r");
|
||||
if (!f || fscanf(f, "%08x %08x %08x", &t2us, &us2t, &clock_res) != 3)
|
||||
exit_errno("error reading /proc/net/psched");
|
||||
fclose(f);
|
||||
|
||||
/* compatibility hack from iproute... */
|
||||
if (clock_res == 1000000000)
|
||||
t2us = us2t;
|
||||
|
||||
ticks = (double)t2us / us2t * clock_res;
|
||||
}
|
||||
|
||||
|
||||
static struct nl_msg * prepare_tcmsg(int type, int flags, uint32_t parent, uint32_t handle, uint32_t info) {
|
||||
struct nl_msg *msg = nlmsg_alloc_simple(type, flags);
|
||||
if (!msg)
|
||||
exit_errno("nlmsg_alloc_simple");
|
||||
|
||||
struct tcmsg tcmsg;
|
||||
memset(&tcmsg, 0, sizeof(tcmsg));
|
||||
|
||||
tcmsg.tcm_family = AF_UNSPEC;
|
||||
tcmsg.tcm_ifindex = ifindex;
|
||||
tcmsg.tcm_parent = parent;
|
||||
tcmsg.tcm_handle = handle;
|
||||
tcmsg.tcm_info = info;
|
||||
|
||||
nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
static int error_handler(struct sockaddr_nl *nla __attribute__((unused)), struct nlmsgerr *nlerr, void *arg __attribute__((unused))) {
|
||||
if (!nlexpect || (nlerr->error != -ENOENT && nlerr->error != -EINVAL))
|
||||
nlerror = -nlerr->error;
|
||||
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
static bool do_send(struct nl_msg *msg, bool expect) {
|
||||
nlerror = 0;
|
||||
nlexpect = expect;
|
||||
|
||||
nl_send_auto_complete(sock, msg);
|
||||
nlmsg_free(msg);
|
||||
nl_wait_for_ack(sock);
|
||||
|
||||
if (nlerror) {
|
||||
error(0, nlerror, "netlink");
|
||||
errno = nlerror;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline unsigned get_xmittime(double rate, unsigned size) {
|
||||
return ticks * (size/rate);
|
||||
}
|
||||
|
||||
|
||||
static void complete_rate(struct tc_ratespec *r, uint32_t rtab[256]) {
|
||||
r->linklayer = TC_LINKLAYER_ETHERNET;
|
||||
r->cell_align = -1;
|
||||
r->cell_log = 3;
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < 256; i++)
|
||||
rtab[i] = get_xmittime(r->rate, (i + 1) << 3);
|
||||
}
|
||||
|
||||
|
||||
static void do_ingress(double rate) {
|
||||
if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_INGRESS, 0xffff0000, 0), true))
|
||||
return;
|
||||
|
||||
if (rate < 0)
|
||||
return;
|
||||
|
||||
|
||||
struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_INGRESS, 0xffff0000, 0);
|
||||
nla_put_string(msg, TCA_KIND, "ingress");
|
||||
|
||||
if (!do_send(msg, false))
|
||||
return;
|
||||
|
||||
|
||||
msg = prepare_tcmsg(RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_EXCL, 0xffff0000, 0, TC_H_MAKE(0, htons(ETH_P_ALL)));
|
||||
|
||||
const unsigned buffer = 10240;
|
||||
|
||||
struct tc_police p;
|
||||
memset(&p, 0, sizeof(p));
|
||||
|
||||
/* Range check has been done in main() */
|
||||
p.rate.rate = rate;
|
||||
p.burst = get_xmittime(p.rate.rate, buffer);
|
||||
p.action = TC_POLICE_SHOT;
|
||||
|
||||
uint32_t rtab[256];
|
||||
complete_rate(&p.rate, rtab);
|
||||
|
||||
nla_put_string(msg, TCA_KIND, "basic");
|
||||
|
||||
struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS);
|
||||
struct nlattr *police = nla_nest_start(msg, TCA_BASIC_POLICE);
|
||||
|
||||
nla_put(msg, TCA_POLICE_TBF, sizeof(p), &p);
|
||||
nla_put(msg, TCA_POLICE_RATE, sizeof(rtab), rtab);
|
||||
|
||||
nla_nest_end(msg, police);
|
||||
nla_nest_end(msg, opts);
|
||||
|
||||
do_send(msg, false);
|
||||
}
|
||||
|
||||
static void do_egress(double rate) {
|
||||
if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_ROOT, 0, 0), true))
|
||||
return;
|
||||
|
||||
if (rate < 0)
|
||||
return;
|
||||
|
||||
|
||||
struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_ROOT, 0, 0);
|
||||
const unsigned buffer = 2048;
|
||||
|
||||
struct tc_tbf_qopt opt;
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
|
||||
/* Range check has been done in main() */
|
||||
opt.rate.rate = rate;
|
||||
opt.limit = 0.05*rate + buffer;
|
||||
opt.buffer = get_xmittime(opt.rate.rate, buffer);
|
||||
|
||||
uint32_t rtab[256];
|
||||
complete_rate(&opt.rate, rtab);
|
||||
|
||||
nla_put_string(msg, TCA_KIND, "tbf");
|
||||
|
||||
struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS);
|
||||
nla_put(msg, TCA_TBF_PARMS, sizeof(opt), &opt);
|
||||
nla_put(msg, TCA_TBF_BURST, sizeof(buffer), &buffer);
|
||||
nla_put(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
|
||||
nla_nest_end(msg, opts);
|
||||
|
||||
do_send(msg, false);
|
||||
}
|
||||
|
||||
|
||||
static inline void usage(void) {
|
||||
fprintf(stderr, "Usage: gluon-simple-tc <interface> <ingress Kbit/s>|- <egress Kbit/s>|-\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void maxrate(void) {
|
||||
error(1, 0, "error: maximum allowed rate it about 2^25 Kbit/s");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 4)
|
||||
usage();
|
||||
|
||||
double ingress = -1, egress = -1;
|
||||
char *end;
|
||||
|
||||
ifindex = if_nametoindex(argv[1]);
|
||||
if (!ifindex)
|
||||
error(1, 0, "invalid interface: %s", argv[1]);
|
||||
|
||||
if (strcmp(argv[2], "-") != 0) {
|
||||
ingress = strtod(argv[2], &end);
|
||||
if (*end || ingress < 0)
|
||||
usage();
|
||||
|
||||
ingress *= 125;
|
||||
|
||||
if (ingress >= (1ull << 32))
|
||||
maxrate();
|
||||
}
|
||||
|
||||
if (strcmp(argv[3], "-") != 0) {
|
||||
egress = strtod(argv[3], &end);
|
||||
if (*end || egress < 0)
|
||||
usage();
|
||||
|
||||
egress *= 125;
|
||||
|
||||
if (egress >= (1ull << 32))
|
||||
maxrate();
|
||||
}
|
||||
|
||||
read_psched();
|
||||
|
||||
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
|
||||
|
||||
sock = nl_socket_alloc_cb(cb);
|
||||
if (!sock)
|
||||
exit_errno("nl_socket_alloc");
|
||||
|
||||
if (nl_connect(sock, NETLINK_ROUTE))
|
||||
exit_errno("nl_connect");
|
||||
|
||||
do_ingress(ingress);
|
||||
do_egress(egress);
|
||||
|
||||
nl_socket_free(sock);
|
||||
nl_cb_put(cb);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,483 @@
|
|||
#ifndef __LINUX_PKT_CLS_H
|
||||
#define __LINUX_PKT_CLS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
|
||||
/* I think i could have done better macros ; for now this is stolen from
|
||||
* some arch/mips code - jhs
|
||||
*/
|
||||
#define _TC_MAKE32(x) ((x))
|
||||
|
||||
#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n))
|
||||
#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n))
|
||||
#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n))
|
||||
#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n))
|
||||
|
||||
/* verdict bit breakdown
|
||||
*
|
||||
bit 0: when set -> this packet has been munged already
|
||||
|
||||
bit 1: when set -> It is ok to munge this packet
|
||||
|
||||
bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded
|
||||
assume loop
|
||||
|
||||
bit 6,7: Where this packet was last seen
|
||||
0: Above the transmit example at the socket level
|
||||
1: on the Ingress
|
||||
2: on the Egress
|
||||
|
||||
bit 8: when set --> Request not to classify on ingress.
|
||||
|
||||
bits 9,10,11: redirect counter - redirect TTL. Loop avoidance
|
||||
|
||||
*
|
||||
* */
|
||||
|
||||
#define TC_MUNGED _TC_MAKEMASK1(0)
|
||||
#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED))
|
||||
#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED)
|
||||
|
||||
#define TC_OK2MUNGE _TC_MAKEMASK1(1)
|
||||
#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE))
|
||||
#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE)
|
||||
|
||||
#define S_TC_VERD _TC_MAKE32(2)
|
||||
#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD)
|
||||
#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD)
|
||||
#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD)
|
||||
#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD))
|
||||
|
||||
#define S_TC_FROM _TC_MAKE32(6)
|
||||
#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM)
|
||||
#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM)
|
||||
#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM)
|
||||
#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM))
|
||||
#define AT_STACK 0x0
|
||||
#define AT_INGRESS 0x1
|
||||
#define AT_EGRESS 0x2
|
||||
|
||||
#define TC_NCLS _TC_MAKEMASK1(8)
|
||||
#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS))
|
||||
#define CLR_TC_NCLS(v) ( v & ~TC_NCLS)
|
||||
|
||||
#define S_TC_RTTL _TC_MAKE32(9)
|
||||
#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL)
|
||||
#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL)
|
||||
#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL)
|
||||
#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL))
|
||||
|
||||
#define S_TC_AT _TC_MAKE32(12)
|
||||
#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT)
|
||||
#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT)
|
||||
#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT)
|
||||
#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT))
|
||||
|
||||
/* Action attributes */
|
||||
enum {
|
||||
TCA_ACT_UNSPEC,
|
||||
TCA_ACT_KIND,
|
||||
TCA_ACT_OPTIONS,
|
||||
TCA_ACT_INDEX,
|
||||
TCA_ACT_STATS,
|
||||
__TCA_ACT_MAX
|
||||
};
|
||||
|
||||
#define TCA_ACT_MAX __TCA_ACT_MAX
|
||||
#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
|
||||
#define TCA_ACT_MAX_PRIO 32
|
||||
#define TCA_ACT_BIND 1
|
||||
#define TCA_ACT_NOBIND 0
|
||||
#define TCA_ACT_UNBIND 1
|
||||
#define TCA_ACT_NOUNBIND 0
|
||||
#define TCA_ACT_REPLACE 1
|
||||
#define TCA_ACT_NOREPLACE 0
|
||||
#define MAX_REC_LOOP 4
|
||||
#define MAX_RED_LOOP 4
|
||||
|
||||
#define TC_ACT_UNSPEC (-1)
|
||||
#define TC_ACT_OK 0
|
||||
#define TC_ACT_RECLASSIFY 1
|
||||
#define TC_ACT_SHOT 2
|
||||
#define TC_ACT_PIPE 3
|
||||
#define TC_ACT_STOLEN 4
|
||||
#define TC_ACT_QUEUED 5
|
||||
#define TC_ACT_REPEAT 6
|
||||
#define TC_ACT_JUMP 0x10000000
|
||||
|
||||
/* Action type identifiers*/
|
||||
enum {
|
||||
TCA_ID_UNSPEC=0,
|
||||
TCA_ID_POLICE=1,
|
||||
/* other actions go here */
|
||||
__TCA_ID_MAX=255
|
||||
};
|
||||
|
||||
#define TCA_ID_MAX __TCA_ID_MAX
|
||||
|
||||
struct tc_police {
|
||||
__u32 index;
|
||||
int action;
|
||||
#define TC_POLICE_UNSPEC TC_ACT_UNSPEC
|
||||
#define TC_POLICE_OK TC_ACT_OK
|
||||
#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY
|
||||
#define TC_POLICE_SHOT TC_ACT_SHOT
|
||||
#define TC_POLICE_PIPE TC_ACT_PIPE
|
||||
|
||||
__u32 limit;
|
||||
__u32 burst;
|
||||
__u32 mtu;
|
||||
struct tc_ratespec rate;
|
||||
struct tc_ratespec peakrate;
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
__u32 capab;
|
||||
};
|
||||
|
||||
struct tcf_t {
|
||||
__u64 install;
|
||||
__u64 lastuse;
|
||||
__u64 expires;
|
||||
};
|
||||
|
||||
struct tc_cnt {
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
};
|
||||
|
||||
#define tc_gen \
|
||||
__u32 index; \
|
||||
__u32 capab; \
|
||||
int action; \
|
||||
int refcnt; \
|
||||
int bindcnt
|
||||
|
||||
enum {
|
||||
TCA_POLICE_UNSPEC,
|
||||
TCA_POLICE_TBF,
|
||||
TCA_POLICE_RATE,
|
||||
TCA_POLICE_PEAKRATE,
|
||||
TCA_POLICE_AVRATE,
|
||||
TCA_POLICE_RESULT,
|
||||
__TCA_POLICE_MAX
|
||||
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
|
||||
};
|
||||
|
||||
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
|
||||
|
||||
/* U32 filters */
|
||||
|
||||
#define TC_U32_HTID(h) ((h)&0xFFF00000)
|
||||
#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
|
||||
#define TC_U32_HASH(h) (((h)>>12)&0xFF)
|
||||
#define TC_U32_NODE(h) ((h)&0xFFF)
|
||||
#define TC_U32_KEY(h) ((h)&0xFFFFF)
|
||||
#define TC_U32_UNSPEC 0
|
||||
#define TC_U32_ROOT (0xFFF00000)
|
||||
|
||||
enum {
|
||||
TCA_U32_UNSPEC,
|
||||
TCA_U32_CLASSID,
|
||||
TCA_U32_HASH,
|
||||
TCA_U32_LINK,
|
||||
TCA_U32_DIVISOR,
|
||||
TCA_U32_SEL,
|
||||
TCA_U32_POLICE,
|
||||
TCA_U32_ACT,
|
||||
TCA_U32_INDEV,
|
||||
TCA_U32_PCNT,
|
||||
TCA_U32_MARK,
|
||||
__TCA_U32_MAX
|
||||
};
|
||||
|
||||
#define TCA_U32_MAX (__TCA_U32_MAX - 1)
|
||||
|
||||
struct tc_u32_key {
|
||||
__be32 mask;
|
||||
__be32 val;
|
||||
int off;
|
||||
int offmask;
|
||||
};
|
||||
|
||||
struct tc_u32_sel {
|
||||
unsigned char flags;
|
||||
unsigned char offshift;
|
||||
unsigned char nkeys;
|
||||
|
||||
__be16 offmask;
|
||||
__u16 off;
|
||||
short offoff;
|
||||
|
||||
short hoff;
|
||||
__be32 hmask;
|
||||
struct tc_u32_key keys[0];
|
||||
};
|
||||
|
||||
struct tc_u32_mark {
|
||||
__u32 val;
|
||||
__u32 mask;
|
||||
__u32 success;
|
||||
};
|
||||
|
||||
struct tc_u32_pcnt {
|
||||
__u64 rcnt;
|
||||
__u64 rhit;
|
||||
__u64 kcnts[0];
|
||||
};
|
||||
|
||||
/* Flags */
|
||||
|
||||
#define TC_U32_TERMINAL 1
|
||||
#define TC_U32_OFFSET 2
|
||||
#define TC_U32_VAROFFSET 4
|
||||
#define TC_U32_EAT 8
|
||||
|
||||
#define TC_U32_MAXDEPTH 8
|
||||
|
||||
|
||||
/* RSVP filter */
|
||||
|
||||
enum {
|
||||
TCA_RSVP_UNSPEC,
|
||||
TCA_RSVP_CLASSID,
|
||||
TCA_RSVP_DST,
|
||||
TCA_RSVP_SRC,
|
||||
TCA_RSVP_PINFO,
|
||||
TCA_RSVP_POLICE,
|
||||
TCA_RSVP_ACT,
|
||||
__TCA_RSVP_MAX
|
||||
};
|
||||
|
||||
#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
|
||||
|
||||
struct tc_rsvp_gpi {
|
||||
__u32 key;
|
||||
__u32 mask;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct tc_rsvp_pinfo {
|
||||
struct tc_rsvp_gpi dpi;
|
||||
struct tc_rsvp_gpi spi;
|
||||
__u8 protocol;
|
||||
__u8 tunnelid;
|
||||
__u8 tunnelhdr;
|
||||
__u8 pad;
|
||||
};
|
||||
|
||||
/* ROUTE filter */
|
||||
|
||||
enum {
|
||||
TCA_ROUTE4_UNSPEC,
|
||||
TCA_ROUTE4_CLASSID,
|
||||
TCA_ROUTE4_TO,
|
||||
TCA_ROUTE4_FROM,
|
||||
TCA_ROUTE4_IIF,
|
||||
TCA_ROUTE4_POLICE,
|
||||
TCA_ROUTE4_ACT,
|
||||
__TCA_ROUTE4_MAX
|
||||
};
|
||||
|
||||
#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
|
||||
|
||||
|
||||
/* FW filter */
|
||||
|
||||
enum {
|
||||
TCA_FW_UNSPEC,
|
||||
TCA_FW_CLASSID,
|
||||
TCA_FW_POLICE,
|
||||
TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */
|
||||
TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
|
||||
TCA_FW_MASK,
|
||||
__TCA_FW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FW_MAX (__TCA_FW_MAX - 1)
|
||||
|
||||
/* TC index filter */
|
||||
|
||||
enum {
|
||||
TCA_TCINDEX_UNSPEC,
|
||||
TCA_TCINDEX_HASH,
|
||||
TCA_TCINDEX_MASK,
|
||||
TCA_TCINDEX_SHIFT,
|
||||
TCA_TCINDEX_FALL_THROUGH,
|
||||
TCA_TCINDEX_CLASSID,
|
||||
TCA_TCINDEX_POLICE,
|
||||
TCA_TCINDEX_ACT,
|
||||
__TCA_TCINDEX_MAX
|
||||
};
|
||||
|
||||
#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1)
|
||||
|
||||
/* Flow filter */
|
||||
|
||||
enum {
|
||||
FLOW_KEY_SRC,
|
||||
FLOW_KEY_DST,
|
||||
FLOW_KEY_PROTO,
|
||||
FLOW_KEY_PROTO_SRC,
|
||||
FLOW_KEY_PROTO_DST,
|
||||
FLOW_KEY_IIF,
|
||||
FLOW_KEY_PRIORITY,
|
||||
FLOW_KEY_MARK,
|
||||
FLOW_KEY_NFCT,
|
||||
FLOW_KEY_NFCT_SRC,
|
||||
FLOW_KEY_NFCT_DST,
|
||||
FLOW_KEY_NFCT_PROTO_SRC,
|
||||
FLOW_KEY_NFCT_PROTO_DST,
|
||||
FLOW_KEY_RTCLASSID,
|
||||
FLOW_KEY_SKUID,
|
||||
FLOW_KEY_SKGID,
|
||||
FLOW_KEY_VLAN_TAG,
|
||||
FLOW_KEY_RXHASH,
|
||||
__FLOW_KEY_MAX,
|
||||
};
|
||||
|
||||
#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1)
|
||||
|
||||
enum {
|
||||
FLOW_MODE_MAP,
|
||||
FLOW_MODE_HASH,
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_FLOW_UNSPEC,
|
||||
TCA_FLOW_KEYS,
|
||||
TCA_FLOW_MODE,
|
||||
TCA_FLOW_BASECLASS,
|
||||
TCA_FLOW_RSHIFT,
|
||||
TCA_FLOW_ADDEND,
|
||||
TCA_FLOW_MASK,
|
||||
TCA_FLOW_XOR,
|
||||
TCA_FLOW_DIVISOR,
|
||||
TCA_FLOW_ACT,
|
||||
TCA_FLOW_POLICE,
|
||||
TCA_FLOW_EMATCHES,
|
||||
TCA_FLOW_PERTURB,
|
||||
__TCA_FLOW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1)
|
||||
|
||||
/* Basic filter */
|
||||
|
||||
enum {
|
||||
TCA_BASIC_UNSPEC,
|
||||
TCA_BASIC_CLASSID,
|
||||
TCA_BASIC_EMATCHES,
|
||||
TCA_BASIC_ACT,
|
||||
TCA_BASIC_POLICE,
|
||||
__TCA_BASIC_MAX
|
||||
};
|
||||
|
||||
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
|
||||
|
||||
|
||||
/* Cgroup classifier */
|
||||
|
||||
enum {
|
||||
TCA_CGROUP_UNSPEC,
|
||||
TCA_CGROUP_ACT,
|
||||
TCA_CGROUP_POLICE,
|
||||
TCA_CGROUP_EMATCHES,
|
||||
__TCA_CGROUP_MAX,
|
||||
};
|
||||
|
||||
#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
|
||||
|
||||
/* BPF classifier */
|
||||
|
||||
enum {
|
||||
TCA_BPF_UNSPEC,
|
||||
TCA_BPF_ACT,
|
||||
TCA_BPF_POLICE,
|
||||
TCA_BPF_CLASSID,
|
||||
TCA_BPF_OPS_LEN,
|
||||
TCA_BPF_OPS,
|
||||
__TCA_BPF_MAX,
|
||||
};
|
||||
|
||||
#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
|
||||
|
||||
/* Extended Matches */
|
||||
|
||||
struct tcf_ematch_tree_hdr {
|
||||
__u16 nmatches;
|
||||
__u16 progid;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_EMATCH_TREE_UNSPEC,
|
||||
TCA_EMATCH_TREE_HDR,
|
||||
TCA_EMATCH_TREE_LIST,
|
||||
__TCA_EMATCH_TREE_MAX
|
||||
};
|
||||
#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
|
||||
|
||||
struct tcf_ematch_hdr {
|
||||
__u16 matchid;
|
||||
__u16 kind;
|
||||
__u16 flags;
|
||||
__u16 pad; /* currently unused */
|
||||
};
|
||||
|
||||
/* 0 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +-----------------------+-+-+---+
|
||||
* | Unused |S|I| R |
|
||||
* +-----------------------+-+-+---+
|
||||
*
|
||||
* R(2) ::= relation to next ematch
|
||||
* where: 0 0 END (last ematch)
|
||||
* 0 1 AND
|
||||
* 1 0 OR
|
||||
* 1 1 Unused (invalid)
|
||||
* I(1) ::= invert result
|
||||
* S(1) ::= simple payload
|
||||
*/
|
||||
#define TCF_EM_REL_END 0
|
||||
#define TCF_EM_REL_AND (1<<0)
|
||||
#define TCF_EM_REL_OR (1<<1)
|
||||
#define TCF_EM_INVERT (1<<2)
|
||||
#define TCF_EM_SIMPLE (1<<3)
|
||||
|
||||
#define TCF_EM_REL_MASK 3
|
||||
#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
|
||||
|
||||
enum {
|
||||
TCF_LAYER_LINK,
|
||||
TCF_LAYER_NETWORK,
|
||||
TCF_LAYER_TRANSPORT,
|
||||
__TCF_LAYER_MAX
|
||||
};
|
||||
#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
|
||||
|
||||
/* Ematch type assignments
|
||||
* 1..32767 Reserved for ematches inside kernel tree
|
||||
* 32768..65535 Free to use, not reliable
|
||||
*/
|
||||
#define TCF_EM_CONTAINER 0
|
||||
#define TCF_EM_CMP 1
|
||||
#define TCF_EM_NBYTE 2
|
||||
#define TCF_EM_U32 3
|
||||
#define TCF_EM_META 4
|
||||
#define TCF_EM_TEXT 5
|
||||
#define TCF_EM_VLAN 6
|
||||
#define TCF_EM_CANID 7
|
||||
#define TCF_EM_IPSET 8
|
||||
#define TCF_EM_MAX 8
|
||||
|
||||
enum {
|
||||
TCF_EM_PROG_TC
|
||||
};
|
||||
|
||||
enum {
|
||||
TCF_EM_OPND_EQ,
|
||||
TCF_EM_OPND_GT,
|
||||
TCF_EM_OPND_LT
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,846 @@
|
|||
#ifndef __LINUX_PKT_SCHED_H
|
||||
#define __LINUX_PKT_SCHED_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Logical priority bands not depending on specific packet scheduler.
|
||||
Every scheduler will map them to real traffic classes, if it has
|
||||
no more precise mechanism to classify packets.
|
||||
|
||||
These numbers have no special meaning, though their coincidence
|
||||
with obsolete IPv6 values is not occasional :-). New IPv6 drafts
|
||||
preferred full anarchy inspired by diffserv group.
|
||||
|
||||
Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
|
||||
class, actually, as rule it will be handled with more care than
|
||||
filler or even bulk.
|
||||
*/
|
||||
|
||||
#define TC_PRIO_BESTEFFORT 0
|
||||
#define TC_PRIO_FILLER 1
|
||||
#define TC_PRIO_BULK 2
|
||||
#define TC_PRIO_INTERACTIVE_BULK 4
|
||||
#define TC_PRIO_INTERACTIVE 6
|
||||
#define TC_PRIO_CONTROL 7
|
||||
|
||||
#define TC_PRIO_MAX 15
|
||||
|
||||
/* Generic queue statistics, available for all the elements.
|
||||
Particular schedulers may have also their private records.
|
||||
*/
|
||||
|
||||
struct tc_stats {
|
||||
__u64 bytes; /* Number of enqueued bytes */
|
||||
__u32 packets; /* Number of enqueued packets */
|
||||
__u32 drops; /* Packets dropped because of lack of resources */
|
||||
__u32 overlimits; /* Number of throttle events when this
|
||||
* flow goes out of allocated bandwidth */
|
||||
__u32 bps; /* Current flow byte rate */
|
||||
__u32 pps; /* Current flow packet rate */
|
||||
__u32 qlen;
|
||||
__u32 backlog;
|
||||
};
|
||||
|
||||
struct tc_estimator {
|
||||
signed char interval;
|
||||
unsigned char ewma_log;
|
||||
};
|
||||
|
||||
/* "Handles"
|
||||
---------
|
||||
|
||||
All the traffic control objects have 32bit identifiers, or "handles".
|
||||
|
||||
They can be considered as opaque numbers from user API viewpoint,
|
||||
but actually they always consist of two fields: major and
|
||||
minor numbers, which are interpreted by kernel specially,
|
||||
that may be used by applications, though not recommended.
|
||||
|
||||
F.e. qdisc handles always have minor number equal to zero,
|
||||
classes (or flows) have major equal to parent qdisc major, and
|
||||
minor uniquely identifying class inside qdisc.
|
||||
|
||||
Macros to manipulate handles:
|
||||
*/
|
||||
|
||||
#define TC_H_MAJ_MASK (0xFFFF0000U)
|
||||
#define TC_H_MIN_MASK (0x0000FFFFU)
|
||||
#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
|
||||
#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
|
||||
#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
|
||||
|
||||
#define TC_H_UNSPEC (0U)
|
||||
#define TC_H_ROOT (0xFFFFFFFFU)
|
||||
#define TC_H_INGRESS (0xFFFFFFF1U)
|
||||
|
||||
/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */
|
||||
enum tc_link_layer {
|
||||
TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */
|
||||
TC_LINKLAYER_ETHERNET,
|
||||
TC_LINKLAYER_ATM,
|
||||
};
|
||||
#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */
|
||||
|
||||
struct tc_ratespec {
|
||||
unsigned char cell_log;
|
||||
__u8 linklayer; /* lower 4 bits */
|
||||
unsigned short overhead;
|
||||
short cell_align;
|
||||
unsigned short mpu;
|
||||
__u32 rate;
|
||||
};
|
||||
|
||||
#define TC_RTAB_SIZE 1024
|
||||
|
||||
struct tc_sizespec {
|
||||
unsigned char cell_log;
|
||||
unsigned char size_log;
|
||||
short cell_align;
|
||||
int overhead;
|
||||
unsigned int linklayer;
|
||||
unsigned int mpu;
|
||||
unsigned int mtu;
|
||||
unsigned int tsize;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_STAB_UNSPEC,
|
||||
TCA_STAB_BASE,
|
||||
TCA_STAB_DATA,
|
||||
__TCA_STAB_MAX
|
||||
};
|
||||
|
||||
#define TCA_STAB_MAX (__TCA_STAB_MAX - 1)
|
||||
|
||||
/* FIFO section */
|
||||
|
||||
struct tc_fifo_qopt {
|
||||
__u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
|
||||
};
|
||||
|
||||
/* PRIO section */
|
||||
|
||||
#define TCQ_PRIO_BANDS 16
|
||||
#define TCQ_MIN_PRIO_BANDS 2
|
||||
|
||||
struct tc_prio_qopt {
|
||||
int bands; /* Number of bands */
|
||||
__u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
|
||||
};
|
||||
|
||||
/* MULTIQ section */
|
||||
|
||||
struct tc_multiq_qopt {
|
||||
__u16 bands; /* Number of bands */
|
||||
__u16 max_bands; /* Maximum number of queues */
|
||||
};
|
||||
|
||||
/* PLUG section */
|
||||
|
||||
#define TCQ_PLUG_BUFFER 0
|
||||
#define TCQ_PLUG_RELEASE_ONE 1
|
||||
#define TCQ_PLUG_RELEASE_INDEFINITE 2
|
||||
#define TCQ_PLUG_LIMIT 3
|
||||
|
||||
struct tc_plug_qopt {
|
||||
/* TCQ_PLUG_BUFFER: Inset a plug into the queue and
|
||||
* buffer any incoming packets
|
||||
* TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head
|
||||
* to beginning of the next plug.
|
||||
* TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue.
|
||||
* Stop buffering packets until the next TCQ_PLUG_BUFFER
|
||||
* command is received (just act as a pass-thru queue).
|
||||
* TCQ_PLUG_LIMIT: Increase/decrease queue size
|
||||
*/
|
||||
int action;
|
||||
__u32 limit;
|
||||
};
|
||||
|
||||
/* TBF section */
|
||||
|
||||
struct tc_tbf_qopt {
|
||||
struct tc_ratespec rate;
|
||||
struct tc_ratespec peakrate;
|
||||
__u32 limit;
|
||||
__u32 buffer;
|
||||
__u32 mtu;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_TBF_UNSPEC,
|
||||
TCA_TBF_PARMS,
|
||||
TCA_TBF_RTAB,
|
||||
TCA_TBF_PTAB,
|
||||
TCA_TBF_RATE64,
|
||||
TCA_TBF_PRATE64,
|
||||
TCA_TBF_BURST,
|
||||
TCA_TBF_PBURST,
|
||||
__TCA_TBF_MAX,
|
||||
};
|
||||
|
||||
#define TCA_TBF_MAX (__TCA_TBF_MAX - 1)
|
||||
|
||||
|
||||
/* TEQL section */
|
||||
|
||||
/* TEQL does not require any parameters */
|
||||
|
||||
/* SFQ section */
|
||||
|
||||
struct tc_sfq_qopt {
|
||||
unsigned quantum; /* Bytes per round allocated to flow */
|
||||
int perturb_period; /* Period of hash perturbation */
|
||||
__u32 limit; /* Maximal packets in queue */
|
||||
unsigned divisor; /* Hash divisor */
|
||||
unsigned flows; /* Maximal number of flows */
|
||||
};
|
||||
|
||||
struct tc_sfqred_stats {
|
||||
__u32 prob_drop; /* Early drops, below max threshold */
|
||||
__u32 forced_drop; /* Early drops, after max threshold */
|
||||
__u32 prob_mark; /* Marked packets, below max threshold */
|
||||
__u32 forced_mark; /* Marked packets, after max threshold */
|
||||
__u32 prob_mark_head; /* Marked packets, below max threshold */
|
||||
__u32 forced_mark_head;/* Marked packets, after max threshold */
|
||||
};
|
||||
|
||||
struct tc_sfq_qopt_v1 {
|
||||
struct tc_sfq_qopt v0;
|
||||
unsigned int depth; /* max number of packets per flow */
|
||||
unsigned int headdrop;
|
||||
/* SFQRED parameters */
|
||||
__u32 limit; /* HARD maximal flow queue length (bytes) */
|
||||
__u32 qth_min; /* Min average length threshold (bytes) */
|
||||
__u32 qth_max; /* Max average length threshold (bytes) */
|
||||
unsigned char Wlog; /* log(W) */
|
||||
unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
|
||||
unsigned char Scell_log; /* cell size for idle damping */
|
||||
unsigned char flags;
|
||||
__u32 max_P; /* probability, high resolution */
|
||||
/* SFQRED stats */
|
||||
struct tc_sfqred_stats stats;
|
||||
};
|
||||
|
||||
|
||||
struct tc_sfq_xstats {
|
||||
__s32 allot;
|
||||
};
|
||||
|
||||
/* RED section */
|
||||
|
||||
enum {
|
||||
TCA_RED_UNSPEC,
|
||||
TCA_RED_PARMS,
|
||||
TCA_RED_STAB,
|
||||
TCA_RED_MAX_P,
|
||||
__TCA_RED_MAX,
|
||||
};
|
||||
|
||||
#define TCA_RED_MAX (__TCA_RED_MAX - 1)
|
||||
|
||||
struct tc_red_qopt {
|
||||
__u32 limit; /* HARD maximal queue length (bytes) */
|
||||
__u32 qth_min; /* Min average length threshold (bytes) */
|
||||
__u32 qth_max; /* Max average length threshold (bytes) */
|
||||
unsigned char Wlog; /* log(W) */
|
||||
unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
|
||||
unsigned char Scell_log; /* cell size for idle damping */
|
||||
unsigned char flags;
|
||||
#define TC_RED_ECN 1
|
||||
#define TC_RED_HARDDROP 2
|
||||
#define TC_RED_ADAPTATIVE 4
|
||||
};
|
||||
|
||||
struct tc_red_xstats {
|
||||
__u32 early; /* Early drops */
|
||||
__u32 pdrop; /* Drops due to queue limits */
|
||||
__u32 other; /* Drops due to drop() calls */
|
||||
__u32 marked; /* Marked packets */
|
||||
};
|
||||
|
||||
/* GRED section */
|
||||
|
||||
#define MAX_DPs 16
|
||||
|
||||
enum {
|
||||
TCA_GRED_UNSPEC,
|
||||
TCA_GRED_PARMS,
|
||||
TCA_GRED_STAB,
|
||||
TCA_GRED_DPS,
|
||||
TCA_GRED_MAX_P,
|
||||
__TCA_GRED_MAX,
|
||||
};
|
||||
|
||||
#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
|
||||
|
||||
struct tc_gred_qopt {
|
||||
__u32 limit; /* HARD maximal queue length (bytes) */
|
||||
__u32 qth_min; /* Min average length threshold (bytes) */
|
||||
__u32 qth_max; /* Max average length threshold (bytes) */
|
||||
__u32 DP; /* up to 2^32 DPs */
|
||||
__u32 backlog;
|
||||
__u32 qave;
|
||||
__u32 forced;
|
||||
__u32 early;
|
||||
__u32 other;
|
||||
__u32 pdrop;
|
||||
__u8 Wlog; /* log(W) */
|
||||
__u8 Plog; /* log(P_max/(qth_max-qth_min)) */
|
||||
__u8 Scell_log; /* cell size for idle damping */
|
||||
__u8 prio; /* prio of this VQ */
|
||||
__u32 packets;
|
||||
__u32 bytesin;
|
||||
};
|
||||
|
||||
/* gred setup */
|
||||
struct tc_gred_sopt {
|
||||
__u32 DPs;
|
||||
__u32 def_DP;
|
||||
__u8 grio;
|
||||
__u8 flags;
|
||||
__u16 pad1;
|
||||
};
|
||||
|
||||
/* CHOKe section */
|
||||
|
||||
enum {
|
||||
TCA_CHOKE_UNSPEC,
|
||||
TCA_CHOKE_PARMS,
|
||||
TCA_CHOKE_STAB,
|
||||
TCA_CHOKE_MAX_P,
|
||||
__TCA_CHOKE_MAX,
|
||||
};
|
||||
|
||||
#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
|
||||
|
||||
struct tc_choke_qopt {
|
||||
__u32 limit; /* Hard queue length (packets) */
|
||||
__u32 qth_min; /* Min average threshold (packets) */
|
||||
__u32 qth_max; /* Max average threshold (packets) */
|
||||
unsigned char Wlog; /* log(W) */
|
||||
unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
|
||||
unsigned char Scell_log; /* cell size for idle damping */
|
||||
unsigned char flags; /* see RED flags */
|
||||
};
|
||||
|
||||
struct tc_choke_xstats {
|
||||
__u32 early; /* Early drops */
|
||||
__u32 pdrop; /* Drops due to queue limits */
|
||||
__u32 other; /* Drops due to drop() calls */
|
||||
__u32 marked; /* Marked packets */
|
||||
__u32 matched; /* Drops due to flow match */
|
||||
};
|
||||
|
||||
/* HTB section */
|
||||
#define TC_HTB_NUMPRIO 8
|
||||
#define TC_HTB_MAXDEPTH 8
|
||||
#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */
|
||||
|
||||
struct tc_htb_opt {
|
||||
struct tc_ratespec rate;
|
||||
struct tc_ratespec ceil;
|
||||
__u32 buffer;
|
||||
__u32 cbuffer;
|
||||
__u32 quantum;
|
||||
__u32 level; /* out only */
|
||||
__u32 prio;
|
||||
};
|
||||
struct tc_htb_glob {
|
||||
__u32 version; /* to match HTB/TC */
|
||||
__u32 rate2quantum; /* bps->quantum divisor */
|
||||
__u32 defcls; /* default class number */
|
||||
__u32 debug; /* debug flags */
|
||||
|
||||
/* stats */
|
||||
__u32 direct_pkts; /* count of non shaped packets */
|
||||
};
|
||||
enum {
|
||||
TCA_HTB_UNSPEC,
|
||||
TCA_HTB_PARMS,
|
||||
TCA_HTB_INIT,
|
||||
TCA_HTB_CTAB,
|
||||
TCA_HTB_RTAB,
|
||||
TCA_HTB_DIRECT_QLEN,
|
||||
TCA_HTB_RATE64,
|
||||
TCA_HTB_CEIL64,
|
||||
__TCA_HTB_MAX,
|
||||
};
|
||||
|
||||
#define TCA_HTB_MAX (__TCA_HTB_MAX - 1)
|
||||
|
||||
struct tc_htb_xstats {
|
||||
__u32 lends;
|
||||
__u32 borrows;
|
||||
__u32 giants; /* too big packets (rate will not be accurate) */
|
||||
__u32 tokens;
|
||||
__u32 ctokens;
|
||||
};
|
||||
|
||||
/* HFSC section */
|
||||
|
||||
struct tc_hfsc_qopt {
|
||||
__u16 defcls; /* default class */
|
||||
};
|
||||
|
||||
struct tc_service_curve {
|
||||
__u32 m1; /* slope of the first segment in bps */
|
||||
__u32 d; /* x-projection of the first segment in us */
|
||||
__u32 m2; /* slope of the second segment in bps */
|
||||
};
|
||||
|
||||
struct tc_hfsc_stats {
|
||||
__u64 work; /* total work done */
|
||||
__u64 rtwork; /* work done by real-time criteria */
|
||||
__u32 period; /* current period */
|
||||
__u32 level; /* class level in hierarchy */
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_HFSC_UNSPEC,
|
||||
TCA_HFSC_RSC,
|
||||
TCA_HFSC_FSC,
|
||||
TCA_HFSC_USC,
|
||||
__TCA_HFSC_MAX,
|
||||
};
|
||||
|
||||
#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1)
|
||||
|
||||
|
||||
/* CBQ section */
|
||||
|
||||
#define TC_CBQ_MAXPRIO 8
|
||||
#define TC_CBQ_MAXLEVEL 8
|
||||
#define TC_CBQ_DEF_EWMA 5
|
||||
|
||||
struct tc_cbq_lssopt {
|
||||
unsigned char change;
|
||||
unsigned char flags;
|
||||
#define TCF_CBQ_LSS_BOUNDED 1
|
||||
#define TCF_CBQ_LSS_ISOLATED 2
|
||||
unsigned char ewma_log;
|
||||
unsigned char level;
|
||||
#define TCF_CBQ_LSS_FLAGS 1
|
||||
#define TCF_CBQ_LSS_EWMA 2
|
||||
#define TCF_CBQ_LSS_MAXIDLE 4
|
||||
#define TCF_CBQ_LSS_MINIDLE 8
|
||||
#define TCF_CBQ_LSS_OFFTIME 0x10
|
||||
#define TCF_CBQ_LSS_AVPKT 0x20
|
||||
__u32 maxidle;
|
||||
__u32 minidle;
|
||||
__u32 offtime;
|
||||
__u32 avpkt;
|
||||
};
|
||||
|
||||
struct tc_cbq_wrropt {
|
||||
unsigned char flags;
|
||||
unsigned char priority;
|
||||
unsigned char cpriority;
|
||||
unsigned char __reserved;
|
||||
__u32 allot;
|
||||
__u32 weight;
|
||||
};
|
||||
|
||||
struct tc_cbq_ovl {
|
||||
unsigned char strategy;
|
||||
#define TC_CBQ_OVL_CLASSIC 0
|
||||
#define TC_CBQ_OVL_DELAY 1
|
||||
#define TC_CBQ_OVL_LOWPRIO 2
|
||||
#define TC_CBQ_OVL_DROP 3
|
||||
#define TC_CBQ_OVL_RCLASSIC 4
|
||||
unsigned char priority2;
|
||||
__u16 pad;
|
||||
__u32 penalty;
|
||||
};
|
||||
|
||||
struct tc_cbq_police {
|
||||
unsigned char police;
|
||||
unsigned char __res1;
|
||||
unsigned short __res2;
|
||||
};
|
||||
|
||||
struct tc_cbq_fopt {
|
||||
__u32 split;
|
||||
__u32 defmap;
|
||||
__u32 defchange;
|
||||
};
|
||||
|
||||
struct tc_cbq_xstats {
|
||||
__u32 borrows;
|
||||
__u32 overactions;
|
||||
__s32 avgidle;
|
||||
__s32 undertime;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_CBQ_UNSPEC,
|
||||
TCA_CBQ_LSSOPT,
|
||||
TCA_CBQ_WRROPT,
|
||||
TCA_CBQ_FOPT,
|
||||
TCA_CBQ_OVL_STRATEGY,
|
||||
TCA_CBQ_RATE,
|
||||
TCA_CBQ_RTAB,
|
||||
TCA_CBQ_POLICE,
|
||||
__TCA_CBQ_MAX,
|
||||
};
|
||||
|
||||
#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1)
|
||||
|
||||
/* dsmark section */
|
||||
|
||||
enum {
|
||||
TCA_DSMARK_UNSPEC,
|
||||
TCA_DSMARK_INDICES,
|
||||
TCA_DSMARK_DEFAULT_INDEX,
|
||||
TCA_DSMARK_SET_TC_INDEX,
|
||||
TCA_DSMARK_MASK,
|
||||
TCA_DSMARK_VALUE,
|
||||
__TCA_DSMARK_MAX,
|
||||
};
|
||||
|
||||
#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1)
|
||||
|
||||
/* ATM section */
|
||||
|
||||
enum {
|
||||
TCA_ATM_UNSPEC,
|
||||
TCA_ATM_FD, /* file/socket descriptor */
|
||||
TCA_ATM_PTR, /* pointer to descriptor - later */
|
||||
TCA_ATM_HDR, /* LL header */
|
||||
TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
|
||||
TCA_ATM_ADDR, /* PVC address (for output only) */
|
||||
TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */
|
||||
__TCA_ATM_MAX,
|
||||
};
|
||||
|
||||
#define TCA_ATM_MAX (__TCA_ATM_MAX - 1)
|
||||
|
||||
/* Network emulator */
|
||||
|
||||
enum {
|
||||
TCA_NETEM_UNSPEC,
|
||||
TCA_NETEM_CORR,
|
||||
TCA_NETEM_DELAY_DIST,
|
||||
TCA_NETEM_REORDER,
|
||||
TCA_NETEM_CORRUPT,
|
||||
TCA_NETEM_LOSS,
|
||||
TCA_NETEM_RATE,
|
||||
TCA_NETEM_ECN,
|
||||
TCA_NETEM_RATE64,
|
||||
__TCA_NETEM_MAX,
|
||||
};
|
||||
|
||||
#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1)
|
||||
|
||||
struct tc_netem_qopt {
|
||||
__u32 latency; /* added delay (us) */
|
||||
__u32 limit; /* fifo limit (packets) */
|
||||
__u32 loss; /* random packet loss (0=none ~0=100%) */
|
||||
__u32 gap; /* re-ordering gap (0 for none) */
|
||||
__u32 duplicate; /* random packet dup (0=none ~0=100%) */
|
||||
__u32 jitter; /* random jitter in latency (us) */
|
||||
};
|
||||
|
||||
struct tc_netem_corr {
|
||||
__u32 delay_corr; /* delay correlation */
|
||||
__u32 loss_corr; /* packet loss correlation */
|
||||
__u32 dup_corr; /* duplicate correlation */
|
||||
};
|
||||
|
||||
struct tc_netem_reorder {
|
||||
__u32 probability;
|
||||
__u32 correlation;
|
||||
};
|
||||
|
||||
struct tc_netem_corrupt {
|
||||
__u32 probability;
|
||||
__u32 correlation;
|
||||
};
|
||||
|
||||
struct tc_netem_rate {
|
||||
__u32 rate; /* byte/s */
|
||||
__s32 packet_overhead;
|
||||
__u32 cell_size;
|
||||
__s32 cell_overhead;
|
||||
};
|
||||
|
||||
enum {
|
||||
NETEM_LOSS_UNSPEC,
|
||||
NETEM_LOSS_GI, /* General Intuitive - 4 state model */
|
||||
NETEM_LOSS_GE, /* Gilbert Elliot models */
|
||||
__NETEM_LOSS_MAX
|
||||
};
|
||||
#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1)
|
||||
|
||||
/* State transition probabilities for 4 state model */
|
||||
struct tc_netem_gimodel {
|
||||
__u32 p13;
|
||||
__u32 p31;
|
||||
__u32 p32;
|
||||
__u32 p14;
|
||||
__u32 p23;
|
||||
};
|
||||
|
||||
/* Gilbert-Elliot models */
|
||||
struct tc_netem_gemodel {
|
||||
__u32 p;
|
||||
__u32 r;
|
||||
__u32 h;
|
||||
__u32 k1;
|
||||
};
|
||||
|
||||
#define NETEM_DIST_SCALE 8192
|
||||
#define NETEM_DIST_MAX 16384
|
||||
|
||||
/* DRR */
|
||||
|
||||
enum {
|
||||
TCA_DRR_UNSPEC,
|
||||
TCA_DRR_QUANTUM,
|
||||
__TCA_DRR_MAX
|
||||
};
|
||||
|
||||
#define TCA_DRR_MAX (__TCA_DRR_MAX - 1)
|
||||
|
||||
struct tc_drr_stats {
|
||||
__u32 deficit;
|
||||
};
|
||||
|
||||
/* MQPRIO */
|
||||
#define TC_QOPT_BITMASK 15
|
||||
#define TC_QOPT_MAX_QUEUE 16
|
||||
|
||||
struct tc_mqprio_qopt {
|
||||
__u8 num_tc;
|
||||
__u8 prio_tc_map[TC_QOPT_BITMASK + 1];
|
||||
__u8 hw;
|
||||
__u16 count[TC_QOPT_MAX_QUEUE];
|
||||
__u16 offset[TC_QOPT_MAX_QUEUE];
|
||||
};
|
||||
|
||||
/* SFB */
|
||||
|
||||
enum {
|
||||
TCA_SFB_UNSPEC,
|
||||
TCA_SFB_PARMS,
|
||||
__TCA_SFB_MAX,
|
||||
};
|
||||
|
||||
#define TCA_SFB_MAX (__TCA_SFB_MAX - 1)
|
||||
|
||||
/*
|
||||
* Note: increment, decrement are Q0.16 fixed-point values.
|
||||
*/
|
||||
struct tc_sfb_qopt {
|
||||
__u32 rehash_interval; /* delay between hash move, in ms */
|
||||
__u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */
|
||||
__u32 max; /* max len of qlen_min */
|
||||
__u32 bin_size; /* maximum queue length per bin */
|
||||
__u32 increment; /* probability increment, (d1 in Blue) */
|
||||
__u32 decrement; /* probability decrement, (d2 in Blue) */
|
||||
__u32 limit; /* max SFB queue length */
|
||||
__u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */
|
||||
__u32 penalty_burst;
|
||||
};
|
||||
|
||||
struct tc_sfb_xstats {
|
||||
__u32 earlydrop;
|
||||
__u32 penaltydrop;
|
||||
__u32 bucketdrop;
|
||||
__u32 queuedrop;
|
||||
__u32 childdrop; /* drops in child qdisc */
|
||||
__u32 marked;
|
||||
__u32 maxqlen;
|
||||
__u32 maxprob;
|
||||
__u32 avgprob;
|
||||
};
|
||||
|
||||
#define SFB_MAX_PROB 0xFFFF
|
||||
|
||||
/* QFQ */
|
||||
enum {
|
||||
TCA_QFQ_UNSPEC,
|
||||
TCA_QFQ_WEIGHT,
|
||||
TCA_QFQ_LMAX,
|
||||
__TCA_QFQ_MAX
|
||||
};
|
||||
|
||||
#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1)
|
||||
|
||||
struct tc_qfq_stats {
|
||||
__u32 weight;
|
||||
__u32 lmax;
|
||||
};
|
||||
|
||||
/* CODEL */
|
||||
|
||||
enum {
|
||||
TCA_CODEL_UNSPEC,
|
||||
TCA_CODEL_TARGET,
|
||||
TCA_CODEL_LIMIT,
|
||||
TCA_CODEL_INTERVAL,
|
||||
TCA_CODEL_ECN,
|
||||
__TCA_CODEL_MAX
|
||||
};
|
||||
|
||||
#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1)
|
||||
|
||||
struct tc_codel_xstats {
|
||||
__u32 maxpacket; /* largest packet we've seen so far */
|
||||
__u32 count; /* how many drops we've done since the last time we
|
||||
* entered dropping state
|
||||
*/
|
||||
__u32 lastcount; /* count at entry to dropping state */
|
||||
__u32 ldelay; /* in-queue delay seen by most recently dequeued packet */
|
||||
__s32 drop_next; /* time to drop next packet */
|
||||
__u32 drop_overlimit; /* number of time max qdisc packet limit was hit */
|
||||
__u32 ecn_mark; /* number of packets we ECN marked instead of dropped */
|
||||
__u32 dropping; /* are we in dropping state ? */
|
||||
};
|
||||
|
||||
/* FQ_CODEL */
|
||||
|
||||
enum {
|
||||
TCA_FQ_CODEL_UNSPEC,
|
||||
TCA_FQ_CODEL_TARGET,
|
||||
TCA_FQ_CODEL_LIMIT,
|
||||
TCA_FQ_CODEL_INTERVAL,
|
||||
TCA_FQ_CODEL_ECN,
|
||||
TCA_FQ_CODEL_FLOWS,
|
||||
TCA_FQ_CODEL_QUANTUM,
|
||||
__TCA_FQ_CODEL_MAX
|
||||
};
|
||||
|
||||
#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FQ_CODEL_XSTATS_QDISC,
|
||||
TCA_FQ_CODEL_XSTATS_CLASS,
|
||||
};
|
||||
|
||||
struct tc_fq_codel_qd_stats {
|
||||
__u32 maxpacket; /* largest packet we've seen so far */
|
||||
__u32 drop_overlimit; /* number of time max qdisc
|
||||
* packet limit was hit
|
||||
*/
|
||||
__u32 ecn_mark; /* number of packets we ECN marked
|
||||
* instead of being dropped
|
||||
*/
|
||||
__u32 new_flow_count; /* number of time packets
|
||||
* created a 'new flow'
|
||||
*/
|
||||
__u32 new_flows_len; /* count of flows in new list */
|
||||
__u32 old_flows_len; /* count of flows in old list */
|
||||
};
|
||||
|
||||
struct tc_fq_codel_cl_stats {
|
||||
__s32 deficit;
|
||||
__u32 ldelay; /* in-queue delay seen by most recently
|
||||
* dequeued packet
|
||||
*/
|
||||
__u32 count;
|
||||
__u32 lastcount;
|
||||
__u32 dropping;
|
||||
__s32 drop_next;
|
||||
};
|
||||
|
||||
struct tc_fq_codel_xstats {
|
||||
__u32 type;
|
||||
union {
|
||||
struct tc_fq_codel_qd_stats qdisc_stats;
|
||||
struct tc_fq_codel_cl_stats class_stats;
|
||||
};
|
||||
};
|
||||
|
||||
/* FQ */
|
||||
|
||||
enum {
|
||||
TCA_FQ_UNSPEC,
|
||||
|
||||
TCA_FQ_PLIMIT, /* limit of total number of packets in queue */
|
||||
|
||||
TCA_FQ_FLOW_PLIMIT, /* limit of packets per flow */
|
||||
|
||||
TCA_FQ_QUANTUM, /* RR quantum */
|
||||
|
||||
TCA_FQ_INITIAL_QUANTUM, /* RR quantum for new flow */
|
||||
|
||||
TCA_FQ_RATE_ENABLE, /* enable/disable rate limiting */
|
||||
|
||||
TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */
|
||||
|
||||
TCA_FQ_FLOW_MAX_RATE, /* per flow max rate */
|
||||
|
||||
TCA_FQ_BUCKETS_LOG, /* log2(number of buckets) */
|
||||
|
||||
TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */
|
||||
|
||||
__TCA_FQ_MAX
|
||||
};
|
||||
|
||||
#define TCA_FQ_MAX (__TCA_FQ_MAX - 1)
|
||||
|
||||
struct tc_fq_qd_stats {
|
||||
__u64 gc_flows;
|
||||
__u64 highprio_packets;
|
||||
__u64 tcp_retrans;
|
||||
__u64 throttled;
|
||||
__u64 flows_plimit;
|
||||
__u64 pkts_too_long;
|
||||
__u64 allocation_errors;
|
||||
__s64 time_next_delayed_flow;
|
||||
__u32 flows;
|
||||
__u32 inactive_flows;
|
||||
__u32 throttled_flows;
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
/* Heavy-Hitter Filter */
|
||||
|
||||
enum {
|
||||
TCA_HHF_UNSPEC,
|
||||
TCA_HHF_BACKLOG_LIMIT,
|
||||
TCA_HHF_QUANTUM,
|
||||
TCA_HHF_HH_FLOWS_LIMIT,
|
||||
TCA_HHF_RESET_TIMEOUT,
|
||||
TCA_HHF_ADMIT_BYTES,
|
||||
TCA_HHF_EVICT_TIMEOUT,
|
||||
TCA_HHF_NON_HH_WEIGHT,
|
||||
__TCA_HHF_MAX
|
||||
};
|
||||
|
||||
#define TCA_HHF_MAX (__TCA_HHF_MAX - 1)
|
||||
|
||||
struct tc_hhf_xstats {
|
||||
__u32 drop_overlimit; /* number of times max qdisc packet limit
|
||||
* was hit
|
||||
*/
|
||||
__u32 hh_overlimit; /* number of times max heavy-hitters was hit */
|
||||
__u32 hh_tot_count; /* number of captured heavy-hitters so far */
|
||||
__u32 hh_cur_count; /* number of current heavy-hitters */
|
||||
};
|
||||
|
||||
/* PIE */
|
||||
enum {
|
||||
TCA_PIE_UNSPEC,
|
||||
TCA_PIE_TARGET,
|
||||
TCA_PIE_LIMIT,
|
||||
TCA_PIE_TUPDATE,
|
||||
TCA_PIE_ALPHA,
|
||||
TCA_PIE_BETA,
|
||||
TCA_PIE_ECN,
|
||||
TCA_PIE_BYTEMODE,
|
||||
__TCA_PIE_MAX
|
||||
};
|
||||
#define TCA_PIE_MAX (__TCA_PIE_MAX - 1)
|
||||
|
||||
struct tc_pie_xstats {
|
||||
__u32 prob; /* current probability */
|
||||
__u32 delay; /* current delay in ms */
|
||||
__u32 avg_dq_rate; /* current average dq_rate in bits/pie_time */
|
||||
__u32 packets_in; /* total number of packets enqueued */
|
||||
__u32 dropped; /* packets dropped due to pie_action */
|
||||
__u32 overlimit; /* dropped due to lack of space in queue */
|
||||
__u32 maxq; /* maximum queue size */
|
||||
__u32 ecn_mark; /* packets marked with ecn*/
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,639 @@
|
|||
#ifndef __LINUX_RTNETLINK_H
|
||||
#define __LINUX_RTNETLINK_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/neighbour.h>
|
||||
|
||||
/* rtnetlink families. Values up to 127 are reserved for real address
|
||||
* families, values above 128 may be used arbitrarily.
|
||||
*/
|
||||
#define RTNL_FAMILY_IPMR 128
|
||||
#define RTNL_FAMILY_IP6MR 129
|
||||
#define RTNL_FAMILY_MAX 129
|
||||
|
||||
/****
|
||||
* Routing/neighbour discovery messages.
|
||||
****/
|
||||
|
||||
/* Types of messages */
|
||||
|
||||
enum {
|
||||
RTM_BASE = 16,
|
||||
#define RTM_BASE RTM_BASE
|
||||
|
||||
RTM_NEWLINK = 16,
|
||||
#define RTM_NEWLINK RTM_NEWLINK
|
||||
RTM_DELLINK,
|
||||
#define RTM_DELLINK RTM_DELLINK
|
||||
RTM_GETLINK,
|
||||
#define RTM_GETLINK RTM_GETLINK
|
||||
RTM_SETLINK,
|
||||
#define RTM_SETLINK RTM_SETLINK
|
||||
|
||||
RTM_NEWADDR = 20,
|
||||
#define RTM_NEWADDR RTM_NEWADDR
|
||||
RTM_DELADDR,
|
||||
#define RTM_DELADDR RTM_DELADDR
|
||||
RTM_GETADDR,
|
||||
#define RTM_GETADDR RTM_GETADDR
|
||||
|
||||
RTM_NEWROUTE = 24,
|
||||
#define RTM_NEWROUTE RTM_NEWROUTE
|
||||
RTM_DELROUTE,
|
||||
#define RTM_DELROUTE RTM_DELROUTE
|
||||
RTM_GETROUTE,
|
||||
#define RTM_GETROUTE RTM_GETROUTE
|
||||
|
||||
RTM_NEWNEIGH = 28,
|
||||
#define RTM_NEWNEIGH RTM_NEWNEIGH
|
||||
RTM_DELNEIGH,
|
||||
#define RTM_DELNEIGH RTM_DELNEIGH
|
||||
RTM_GETNEIGH,
|
||||
#define RTM_GETNEIGH RTM_GETNEIGH
|
||||
|
||||
RTM_NEWRULE = 32,
|
||||
#define RTM_NEWRULE RTM_NEWRULE
|
||||
RTM_DELRULE,
|
||||
#define RTM_DELRULE RTM_DELRULE
|
||||
RTM_GETRULE,
|
||||
#define RTM_GETRULE RTM_GETRULE
|
||||
|
||||
RTM_NEWQDISC = 36,
|
||||
#define RTM_NEWQDISC RTM_NEWQDISC
|
||||
RTM_DELQDISC,
|
||||
#define RTM_DELQDISC RTM_DELQDISC
|
||||
RTM_GETQDISC,
|
||||
#define RTM_GETQDISC RTM_GETQDISC
|
||||
|
||||
RTM_NEWTCLASS = 40,
|
||||
#define RTM_NEWTCLASS RTM_NEWTCLASS
|
||||
RTM_DELTCLASS,
|
||||
#define RTM_DELTCLASS RTM_DELTCLASS
|
||||
RTM_GETTCLASS,
|
||||
#define RTM_GETTCLASS RTM_GETTCLASS
|
||||
|
||||
RTM_NEWTFILTER = 44,
|
||||
#define RTM_NEWTFILTER RTM_NEWTFILTER
|
||||
RTM_DELTFILTER,
|
||||
#define RTM_DELTFILTER RTM_DELTFILTER
|
||||
RTM_GETTFILTER,
|
||||
#define RTM_GETTFILTER RTM_GETTFILTER
|
||||
|
||||
RTM_NEWACTION = 48,
|
||||
#define RTM_NEWACTION RTM_NEWACTION
|
||||
RTM_DELACTION,
|
||||
#define RTM_DELACTION RTM_DELACTION
|
||||
RTM_GETACTION,
|
||||
#define RTM_GETACTION RTM_GETACTION
|
||||
|
||||
RTM_NEWPREFIX = 52,
|
||||
#define RTM_NEWPREFIX RTM_NEWPREFIX
|
||||
|
||||
RTM_GETMULTICAST = 58,
|
||||
#define RTM_GETMULTICAST RTM_GETMULTICAST
|
||||
|
||||
RTM_GETANYCAST = 62,
|
||||
#define RTM_GETANYCAST RTM_GETANYCAST
|
||||
|
||||
RTM_NEWNEIGHTBL = 64,
|
||||
#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL
|
||||
RTM_GETNEIGHTBL = 66,
|
||||
#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL
|
||||
RTM_SETNEIGHTBL,
|
||||
#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL
|
||||
|
||||
RTM_NEWNDUSEROPT = 68,
|
||||
#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
|
||||
|
||||
RTM_NEWADDRLABEL = 72,
|
||||
#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
|
||||
RTM_DELADDRLABEL,
|
||||
#define RTM_DELADDRLABEL RTM_DELADDRLABEL
|
||||
RTM_GETADDRLABEL,
|
||||
#define RTM_GETADDRLABEL RTM_GETADDRLABEL
|
||||
|
||||
RTM_GETDCB = 78,
|
||||
#define RTM_GETDCB RTM_GETDCB
|
||||
RTM_SETDCB,
|
||||
#define RTM_SETDCB RTM_SETDCB
|
||||
|
||||
RTM_NEWNETCONF = 80,
|
||||
#define RTM_NEWNETCONF RTM_NEWNETCONF
|
||||
RTM_GETNETCONF = 82,
|
||||
#define RTM_GETNETCONF RTM_GETNETCONF
|
||||
|
||||
RTM_NEWMDB = 84,
|
||||
#define RTM_NEWMDB RTM_NEWMDB
|
||||
RTM_DELMDB = 85,
|
||||
#define RTM_DELMDB RTM_DELMDB
|
||||
RTM_GETMDB = 86,
|
||||
#define RTM_GETMDB RTM_GETMDB
|
||||
|
||||
__RTM_MAX,
|
||||
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
||||
};
|
||||
|
||||
#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE)
|
||||
#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2)
|
||||
#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2)
|
||||
|
||||
/*
|
||||
Generic structure for encapsulation of optional route information.
|
||||
It is reminiscent of sockaddr, but with sa_family replaced
|
||||
with attribute type.
|
||||
*/
|
||||
|
||||
struct rtattr {
|
||||
unsigned short rta_len;
|
||||
unsigned short rta_type;
|
||||
};
|
||||
|
||||
/* Macros to handle rtattributes */
|
||||
|
||||
#define RTA_ALIGNTO 4
|
||||
#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
|
||||
#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
|
||||
(rta)->rta_len >= sizeof(struct rtattr) && \
|
||||
(rta)->rta_len <= (len))
|
||||
#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
|
||||
(struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
|
||||
#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
|
||||
#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
|
||||
#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
|
||||
#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Definitions used in routing table administration.
|
||||
****/
|
||||
|
||||
struct rtmsg {
|
||||
unsigned char rtm_family;
|
||||
unsigned char rtm_dst_len;
|
||||
unsigned char rtm_src_len;
|
||||
unsigned char rtm_tos;
|
||||
|
||||
unsigned char rtm_table; /* Routing table id */
|
||||
unsigned char rtm_protocol; /* Routing protocol; see below */
|
||||
unsigned char rtm_scope; /* See below */
|
||||
unsigned char rtm_type; /* See below */
|
||||
|
||||
unsigned rtm_flags;
|
||||
};
|
||||
|
||||
/* rtm_type */
|
||||
|
||||
enum {
|
||||
RTN_UNSPEC,
|
||||
RTN_UNICAST, /* Gateway or direct route */
|
||||
RTN_LOCAL, /* Accept locally */
|
||||
RTN_BROADCAST, /* Accept locally as broadcast,
|
||||
send as broadcast */
|
||||
RTN_ANYCAST, /* Accept locally as broadcast,
|
||||
but send as unicast */
|
||||
RTN_MULTICAST, /* Multicast route */
|
||||
RTN_BLACKHOLE, /* Drop */
|
||||
RTN_UNREACHABLE, /* Destination is unreachable */
|
||||
RTN_PROHIBIT, /* Administratively prohibited */
|
||||
RTN_THROW, /* Not in this table */
|
||||
RTN_NAT, /* Translate this address */
|
||||
RTN_XRESOLVE, /* Use external resolver */
|
||||
__RTN_MAX
|
||||
};
|
||||
|
||||
#define RTN_MAX (__RTN_MAX - 1)
|
||||
|
||||
|
||||
/* rtm_protocol */
|
||||
|
||||
#define RTPROT_UNSPEC 0
|
||||
#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects;
|
||||
not used by current IPv4 */
|
||||
#define RTPROT_KERNEL 2 /* Route installed by kernel */
|
||||
#define RTPROT_BOOT 3 /* Route installed during boot */
|
||||
#define RTPROT_STATIC 4 /* Route installed by administrator */
|
||||
|
||||
/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
|
||||
they are just passed from user and back as is.
|
||||
It will be used by hypothetical multiple routing daemons.
|
||||
Note that protocol values should be standardized in order to
|
||||
avoid conflicts.
|
||||
*/
|
||||
|
||||
#define RTPROT_GATED 8 /* Apparently, GateD */
|
||||
#define RTPROT_RA 9 /* RDISC/ND router advertisements */
|
||||
#define RTPROT_MRT 10 /* Merit MRT */
|
||||
#define RTPROT_ZEBRA 11 /* Zebra */
|
||||
#define RTPROT_BIRD 12 /* BIRD */
|
||||
#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
|
||||
#define RTPROT_XORP 14 /* XORP */
|
||||
#define RTPROT_NTK 15 /* Netsukuku */
|
||||
#define RTPROT_DHCP 16 /* DHCP client */
|
||||
#define RTPROT_MROUTED 17 /* Multicast daemon */
|
||||
|
||||
/* rtm_scope
|
||||
|
||||
Really it is not scope, but sort of distance to the destination.
|
||||
NOWHERE are reserved for not existing destinations, HOST is our
|
||||
local addresses, LINK are destinations, located on directly attached
|
||||
link and UNIVERSE is everywhere in the Universe.
|
||||
|
||||
Intermediate values are also possible f.e. interior routes
|
||||
could be assigned a value between UNIVERSE and LINK.
|
||||
*/
|
||||
|
||||
enum rt_scope_t {
|
||||
RT_SCOPE_UNIVERSE=0,
|
||||
/* User defined values */
|
||||
RT_SCOPE_SITE=200,
|
||||
RT_SCOPE_LINK=253,
|
||||
RT_SCOPE_HOST=254,
|
||||
RT_SCOPE_NOWHERE=255
|
||||
};
|
||||
|
||||
/* rtm_flags */
|
||||
|
||||
#define RTM_F_NOTIFY 0x100 /* Notify user of route change */
|
||||
#define RTM_F_CLONED 0x200 /* This route is cloned */
|
||||
#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */
|
||||
#define RTM_F_PREFIX 0x800 /* Prefix addresses */
|
||||
|
||||
/* Reserved table identifiers */
|
||||
|
||||
enum rt_class_t {
|
||||
RT_TABLE_UNSPEC=0,
|
||||
/* User defined values */
|
||||
RT_TABLE_COMPAT=252,
|
||||
RT_TABLE_DEFAULT=253,
|
||||
RT_TABLE_MAIN=254,
|
||||
RT_TABLE_LOCAL=255,
|
||||
RT_TABLE_MAX=0xFFFFFFFF
|
||||
};
|
||||
|
||||
|
||||
/* Routing message attributes */
|
||||
|
||||
enum rtattr_type_t {
|
||||
RTA_UNSPEC,
|
||||
RTA_DST,
|
||||
RTA_SRC,
|
||||
RTA_IIF,
|
||||
RTA_OIF,
|
||||
RTA_GATEWAY,
|
||||
RTA_PRIORITY,
|
||||
RTA_PREFSRC,
|
||||
RTA_METRICS,
|
||||
RTA_MULTIPATH,
|
||||
RTA_PROTOINFO, /* no longer used */
|
||||
RTA_FLOW,
|
||||
RTA_CACHEINFO,
|
||||
RTA_SESSION, /* no longer used */
|
||||
RTA_MP_ALGO, /* no longer used */
|
||||
RTA_TABLE,
|
||||
RTA_MARK,
|
||||
RTA_MFC_STATS,
|
||||
__RTA_MAX
|
||||
};
|
||||
|
||||
#define RTA_MAX (__RTA_MAX - 1)
|
||||
|
||||
#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
|
||||
#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
|
||||
|
||||
/* RTM_MULTIPATH --- array of struct rtnexthop.
|
||||
*
|
||||
* "struct rtnexthop" describes all necessary nexthop information,
|
||||
* i.e. parameters of path to a destination via this nexthop.
|
||||
*
|
||||
* At the moment it is impossible to set different prefsrc, mtu, window
|
||||
* and rtt for different paths from multipath.
|
||||
*/
|
||||
|
||||
struct rtnexthop {
|
||||
unsigned short rtnh_len;
|
||||
unsigned char rtnh_flags;
|
||||
unsigned char rtnh_hops;
|
||||
int rtnh_ifindex;
|
||||
};
|
||||
|
||||
/* rtnh_flags */
|
||||
|
||||
#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
|
||||
#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
|
||||
#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
|
||||
|
||||
/* Macros to handle hexthops */
|
||||
|
||||
#define RTNH_ALIGNTO 4
|
||||
#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
|
||||
#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
|
||||
((int)(rtnh)->rtnh_len) <= (len))
|
||||
#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
|
||||
#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
|
||||
#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len))
|
||||
#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
|
||||
|
||||
/* RTM_CACHEINFO */
|
||||
|
||||
struct rta_cacheinfo {
|
||||
__u32 rta_clntref;
|
||||
__u32 rta_lastuse;
|
||||
__s32 rta_expires;
|
||||
__u32 rta_error;
|
||||
__u32 rta_used;
|
||||
|
||||
#define RTNETLINK_HAVE_PEERINFO 1
|
||||
__u32 rta_id;
|
||||
__u32 rta_ts;
|
||||
__u32 rta_tsage;
|
||||
};
|
||||
|
||||
/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
|
||||
|
||||
enum {
|
||||
RTAX_UNSPEC,
|
||||
#define RTAX_UNSPEC RTAX_UNSPEC
|
||||
RTAX_LOCK,
|
||||
#define RTAX_LOCK RTAX_LOCK
|
||||
RTAX_MTU,
|
||||
#define RTAX_MTU RTAX_MTU
|
||||
RTAX_WINDOW,
|
||||
#define RTAX_WINDOW RTAX_WINDOW
|
||||
RTAX_RTT,
|
||||
#define RTAX_RTT RTAX_RTT
|
||||
RTAX_RTTVAR,
|
||||
#define RTAX_RTTVAR RTAX_RTTVAR
|
||||
RTAX_SSTHRESH,
|
||||
#define RTAX_SSTHRESH RTAX_SSTHRESH
|
||||
RTAX_CWND,
|
||||
#define RTAX_CWND RTAX_CWND
|
||||
RTAX_ADVMSS,
|
||||
#define RTAX_ADVMSS RTAX_ADVMSS
|
||||
RTAX_REORDERING,
|
||||
#define RTAX_REORDERING RTAX_REORDERING
|
||||
RTAX_HOPLIMIT,
|
||||
#define RTAX_HOPLIMIT RTAX_HOPLIMIT
|
||||
RTAX_INITCWND,
|
||||
#define RTAX_INITCWND RTAX_INITCWND
|
||||
RTAX_FEATURES,
|
||||
#define RTAX_FEATURES RTAX_FEATURES
|
||||
RTAX_RTO_MIN,
|
||||
#define RTAX_RTO_MIN RTAX_RTO_MIN
|
||||
RTAX_INITRWND,
|
||||
#define RTAX_INITRWND RTAX_INITRWND
|
||||
RTAX_QUICKACK,
|
||||
#define RTAX_QUICKACK RTAX_QUICKACK
|
||||
__RTAX_MAX
|
||||
};
|
||||
|
||||
#define RTAX_MAX (__RTAX_MAX - 1)
|
||||
|
||||
#define RTAX_FEATURE_ECN 0x00000001
|
||||
#define RTAX_FEATURE_SACK 0x00000002
|
||||
#define RTAX_FEATURE_TIMESTAMP 0x00000004
|
||||
#define RTAX_FEATURE_ALLFRAG 0x00000008
|
||||
|
||||
struct rta_session {
|
||||
__u8 proto;
|
||||
__u8 pad1;
|
||||
__u16 pad2;
|
||||
|
||||
union {
|
||||
struct {
|
||||
__u16 sport;
|
||||
__u16 dport;
|
||||
} ports;
|
||||
|
||||
struct {
|
||||
__u8 type;
|
||||
__u8 code;
|
||||
__u16 ident;
|
||||
} icmpt;
|
||||
|
||||
__u32 spi;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct rta_mfc_stats {
|
||||
__u64 mfcs_packets;
|
||||
__u64 mfcs_bytes;
|
||||
__u64 mfcs_wrong_if;
|
||||
};
|
||||
|
||||
/****
|
||||
* General form of address family dependent message.
|
||||
****/
|
||||
|
||||
struct rtgenmsg {
|
||||
unsigned char rtgen_family;
|
||||
};
|
||||
|
||||
/*****************************************************************
|
||||
* Link layer specific messages.
|
||||
****/
|
||||
|
||||
/* struct ifinfomsg
|
||||
* passes link level specific information, not dependent
|
||||
* on network protocol.
|
||||
*/
|
||||
|
||||
struct ifinfomsg {
|
||||
unsigned char ifi_family;
|
||||
unsigned char __ifi_pad;
|
||||
unsigned short ifi_type; /* ARPHRD_* */
|
||||
int ifi_index; /* Link index */
|
||||
unsigned ifi_flags; /* IFF_* flags */
|
||||
unsigned ifi_change; /* IFF_* change mask */
|
||||
};
|
||||
|
||||
/********************************************************************
|
||||
* prefix information
|
||||
****/
|
||||
|
||||
struct prefixmsg {
|
||||
unsigned char prefix_family;
|
||||
unsigned char prefix_pad1;
|
||||
unsigned short prefix_pad2;
|
||||
int prefix_ifindex;
|
||||
unsigned char prefix_type;
|
||||
unsigned char prefix_len;
|
||||
unsigned char prefix_flags;
|
||||
unsigned char prefix_pad3;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PREFIX_UNSPEC,
|
||||
PREFIX_ADDRESS,
|
||||
PREFIX_CACHEINFO,
|
||||
__PREFIX_MAX
|
||||
};
|
||||
|
||||
#define PREFIX_MAX (__PREFIX_MAX - 1)
|
||||
|
||||
struct prefix_cacheinfo {
|
||||
__u32 preferred_time;
|
||||
__u32 valid_time;
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* Traffic control messages.
|
||||
****/
|
||||
|
||||
struct tcmsg {
|
||||
unsigned char tcm_family;
|
||||
unsigned char tcm__pad1;
|
||||
unsigned short tcm__pad2;
|
||||
int tcm_ifindex;
|
||||
__u32 tcm_handle;
|
||||
__u32 tcm_parent;
|
||||
__u32 tcm_info;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_UNSPEC,
|
||||
TCA_KIND,
|
||||
TCA_OPTIONS,
|
||||
TCA_STATS,
|
||||
TCA_XSTATS,
|
||||
TCA_RATE,
|
||||
TCA_FCNT,
|
||||
TCA_STATS2,
|
||||
TCA_STAB,
|
||||
__TCA_MAX
|
||||
};
|
||||
|
||||
#define TCA_MAX (__TCA_MAX - 1)
|
||||
|
||||
#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
|
||||
#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
|
||||
|
||||
/********************************************************************
|
||||
* Neighbor Discovery userland options
|
||||
****/
|
||||
|
||||
struct nduseroptmsg {
|
||||
unsigned char nduseropt_family;
|
||||
unsigned char nduseropt_pad1;
|
||||
unsigned short nduseropt_opts_len; /* Total length of options */
|
||||
int nduseropt_ifindex;
|
||||
__u8 nduseropt_icmp_type;
|
||||
__u8 nduseropt_icmp_code;
|
||||
unsigned short nduseropt_pad2;
|
||||
unsigned int nduseropt_pad3;
|
||||
/* Followed by one or more ND options */
|
||||
};
|
||||
|
||||
enum {
|
||||
NDUSEROPT_UNSPEC,
|
||||
NDUSEROPT_SRCADDR,
|
||||
__NDUSEROPT_MAX
|
||||
};
|
||||
|
||||
#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1)
|
||||
|
||||
/* RTnetlink multicast groups - backwards compatibility for userspace */
|
||||
#define RTMGRP_LINK 1
|
||||
#define RTMGRP_NOTIFY 2
|
||||
#define RTMGRP_NEIGH 4
|
||||
#define RTMGRP_TC 8
|
||||
|
||||
#define RTMGRP_IPV4_IFADDR 0x10
|
||||
#define RTMGRP_IPV4_MROUTE 0x20
|
||||
#define RTMGRP_IPV4_ROUTE 0x40
|
||||
#define RTMGRP_IPV4_RULE 0x80
|
||||
|
||||
#define RTMGRP_IPV6_IFADDR 0x100
|
||||
#define RTMGRP_IPV6_MROUTE 0x200
|
||||
#define RTMGRP_IPV6_ROUTE 0x400
|
||||
#define RTMGRP_IPV6_IFINFO 0x800
|
||||
|
||||
#define RTMGRP_DECnet_IFADDR 0x1000
|
||||
#define RTMGRP_DECnet_ROUTE 0x4000
|
||||
|
||||
#define RTMGRP_IPV6_PREFIX 0x20000
|
||||
|
||||
/* RTnetlink multicast groups */
|
||||
enum rtnetlink_groups {
|
||||
RTNLGRP_NONE,
|
||||
#define RTNLGRP_NONE RTNLGRP_NONE
|
||||
RTNLGRP_LINK,
|
||||
#define RTNLGRP_LINK RTNLGRP_LINK
|
||||
RTNLGRP_NOTIFY,
|
||||
#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
|
||||
RTNLGRP_NEIGH,
|
||||
#define RTNLGRP_NEIGH RTNLGRP_NEIGH
|
||||
RTNLGRP_TC,
|
||||
#define RTNLGRP_TC RTNLGRP_TC
|
||||
RTNLGRP_IPV4_IFADDR,
|
||||
#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
|
||||
RTNLGRP_IPV4_MROUTE,
|
||||
#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE
|
||||
RTNLGRP_IPV4_ROUTE,
|
||||
#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE
|
||||
RTNLGRP_IPV4_RULE,
|
||||
#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE
|
||||
RTNLGRP_IPV6_IFADDR,
|
||||
#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR
|
||||
RTNLGRP_IPV6_MROUTE,
|
||||
#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE
|
||||
RTNLGRP_IPV6_ROUTE,
|
||||
#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE
|
||||
RTNLGRP_IPV6_IFINFO,
|
||||
#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO
|
||||
RTNLGRP_DECnet_IFADDR,
|
||||
#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR
|
||||
RTNLGRP_NOP2,
|
||||
RTNLGRP_DECnet_ROUTE,
|
||||
#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE
|
||||
RTNLGRP_DECnet_RULE,
|
||||
#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE
|
||||
RTNLGRP_NOP4,
|
||||
RTNLGRP_IPV6_PREFIX,
|
||||
#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX
|
||||
RTNLGRP_IPV6_RULE,
|
||||
#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE
|
||||
RTNLGRP_ND_USEROPT,
|
||||
#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT
|
||||
RTNLGRP_PHONET_IFADDR,
|
||||
#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
|
||||
RTNLGRP_PHONET_ROUTE,
|
||||
#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
|
||||
RTNLGRP_DCB,
|
||||
#define RTNLGRP_DCB RTNLGRP_DCB
|
||||
RTNLGRP_IPV4_NETCONF,
|
||||
#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF
|
||||
RTNLGRP_IPV6_NETCONF,
|
||||
#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF
|
||||
RTNLGRP_MDB,
|
||||
#define RTNLGRP_MDB RTNLGRP_MDB
|
||||
__RTNLGRP_MAX
|
||||
};
|
||||
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
|
||||
|
||||
/* TC action piece */
|
||||
struct tcamsg {
|
||||
unsigned char tca_family;
|
||||
unsigned char tca__pad1;
|
||||
unsigned short tca__pad2;
|
||||
};
|
||||
#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg))))
|
||||
#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
|
||||
#define TCA_ACT_TAB 1 /* attr type must be >=1 */
|
||||
#define TCAA_MAX 1
|
||||
|
||||
/* New extended info filters for IFLA_EXT_MASK */
|
||||
#define RTEXT_FILTER_VF (1 << 0)
|
||||
#define RTEXT_FILTER_BRVLAN (1 << 1)
|
||||
|
||||
/* End of information exported to user level */
|
||||
|
||||
|
||||
|
||||
#endif /* __LINUX_RTNETLINK_H */
|
|
@ -0,0 +1,34 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=lua-ethtool-stats
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/lua-ethtool-stats
|
||||
SECTION:=libs
|
||||
CATEGORY:=Libraries
|
||||
TITLE:=Lua libary to obtain interface stats via ethtool
|
||||
DEPENDS:=+lua
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||
endef
|
||||
|
||||
define Package/lua-ethtool-stats/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua
|
||||
$(CP) $(PKG_BUILD_DIR)/ethtool_stats.so $(1)/usr/lib/lua/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,lua-ethtool-stats))
|
|
@ -0,0 +1,7 @@
|
|||
This package provides a Lua module with the name 'ethtool_stats'. The module has
|
||||
a single function, interface_stats().
|
||||
|
||||
interface_stats() expects one parameter, the name of the interface to get the
|
||||
stats from. It returns a table with string keys and numeric values which
|
||||
contains the stats returned by ethtool (the same `ethtool -S interface` would
|
||||
yield).
|
|
@ -0,0 +1,6 @@
|
|||
all: ethtool_stats.so
|
||||
|
||||
CFLAGS += -Wall
|
||||
|
||||
ethtool_stats.so: ethtool_stats.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o $@ $^ $(LDLIBS)
|
|
@ -0,0 +1,170 @@
|
|||
|
||||
/*
|
||||
Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/sockios.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
|
||||
struct stats_context {
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
|
||||
struct ethtool_gstrings *strings;
|
||||
struct ethtool_stats *stats;
|
||||
};
|
||||
|
||||
|
||||
static inline void do_ioctl(lua_State *L, struct stats_context *ctx, void *data) {
|
||||
ctx->ifr.ifr_data = data;
|
||||
if (ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr) < 0)
|
||||
luaL_error(L, "ioctl: %s", strerror(errno));
|
||||
}
|
||||
|
||||
static inline uint32_t get_stats_length(lua_State *L, struct stats_context *ctx) {
|
||||
const size_t sset_info_len = sizeof(struct ethtool_sset_info) + sizeof(uint32_t);
|
||||
struct ethtool_sset_info *sset_info = alloca(sset_info_len);
|
||||
memset(sset_info, 0, sset_info_len);
|
||||
|
||||
sset_info->cmd = ETHTOOL_GSSET_INFO;
|
||||
sset_info->sset_mask = 1ull << ETH_SS_STATS;
|
||||
do_ioctl(L, ctx, sset_info);
|
||||
|
||||
return sset_info->sset_mask ? sset_info->data[0] : 0;
|
||||
}
|
||||
|
||||
static inline void get_stats_strings(lua_State *L, struct stats_context *ctx) {
|
||||
uint32_t n_stats = get_stats_length(L, ctx);
|
||||
|
||||
if (!n_stats)
|
||||
return;
|
||||
|
||||
ctx->strings = calloc(1, sizeof(*ctx->strings) + n_stats * ETH_GSTRING_LEN);
|
||||
if (!ctx->strings) {
|
||||
luaL_error(L, "calloc: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->strings->cmd = ETHTOOL_GSTRINGS;
|
||||
ctx->strings->string_set = ETH_SS_STATS;
|
||||
ctx->strings->len = n_stats;
|
||||
|
||||
do_ioctl(L, ctx, ctx->strings);
|
||||
}
|
||||
|
||||
static inline int get_stats(lua_State *L, struct stats_context *ctx) {
|
||||
get_stats_strings(L, ctx);
|
||||
|
||||
if (!ctx->strings) {
|
||||
lua_newtable(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx->stats = calloc(1, sizeof(struct ethtool_stats) + ctx->strings->len * sizeof(uint64_t));
|
||||
if (!ctx->stats)
|
||||
return luaL_error(L, "calloc: %s", strerror(errno));
|
||||
|
||||
ctx->stats->cmd = ETHTOOL_GSTATS;
|
||||
ctx->stats->n_stats = ctx->strings->len;
|
||||
|
||||
do_ioctl(L, ctx, ctx->stats);
|
||||
|
||||
lua_createtable(L, 0, ctx->strings->len);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < ctx->strings->len; i++) {
|
||||
const char *key = (const char*)&ctx->strings->data[i * ETH_GSTRING_LEN];
|
||||
lua_pushlstring(L, key, strnlen(key, ETH_GSTRING_LEN));
|
||||
lua_pushnumber(L, (lua_Number)ctx->stats->data[i]);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int interface_stats(lua_State *L) {
|
||||
const char *ifname = luaL_checkstring(L, 1);
|
||||
|
||||
struct stats_context *ctx = lua_newuserdata(L, sizeof(*ctx));
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
luaL_getmetatable(L, "ethtool_stats.ctx");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
strncpy(ctx->ifr.ifr_name, ifname, IFNAMSIZ);
|
||||
|
||||
ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (ctx->fd < 0)
|
||||
return luaL_error(L, "socket: %s", strerror(errno));
|
||||
|
||||
return get_stats(L, ctx);
|
||||
}
|
||||
|
||||
static int ctx_gc(lua_State *L) {
|
||||
struct stats_context *ctx = lua_touserdata(L, 1);
|
||||
|
||||
if (ctx->fd >= 0)
|
||||
close(ctx->fd);
|
||||
|
||||
free(ctx->strings);
|
||||
free(ctx->stats);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_reg R[] = {
|
||||
{"interface_stats", interface_stats},
|
||||
{NULL, NULL },
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_ethtool_stats(lua_State *L) {
|
||||
luaL_newmetatable(L, "ethtool_stats.ctx");
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_pushcfunction(L, ctx_gc);
|
||||
lua_settable(L, -3);
|
||||
|
||||
luaL_register(L, "ethtool_stats", R);
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue