mirror of
https://github.com/svpcom/wfb-ng.git
synced 2025-03-14 10:33:58 -03:00
Add traffic shaper support
This commit is contained in:
parent
9f85c94253
commit
dae57752c2
@ -1,3 +1,6 @@
|
||||
#WFB_NICS="wlx00c0caa578a9"
|
||||
#WFB_NICS="wlan1 wlan2"
|
||||
# For multi-link setup you can specify path to custom config
|
||||
# and/or list of cards in /etc/default/wifibroadcast.<profile_name>
|
||||
# WIFIBROADCAST_CFG=/etc/wifibroadcast_linkXXX.cfg
|
||||
WFB_NICS="wlan0"
|
||||
|
58
scripts/tc.sh
Executable file
58
scripts/tc.sh
Executable file
@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
wlan=$1
|
||||
|
||||
### This is sample script for traffic shaping
|
||||
|
||||
# Assumptions: MCS1 for all links
|
||||
# For different MCS you need to recalculate max total bandwidth and max bandwidth for each stream
|
||||
|
||||
#### Set in the wifibroadcast.cfg:
|
||||
|
||||
## [base]
|
||||
## use_qdisc=True
|
||||
|
||||
## [drone_video]
|
||||
## fwmark=1
|
||||
|
||||
## [drone_mavlink]
|
||||
## fwmark=10
|
||||
|
||||
## [drone_tunnel]
|
||||
## fwmark=20
|
||||
|
||||
## [gs_mavlink]
|
||||
## fwmark=10
|
||||
|
||||
## [gs_tunnel]
|
||||
## fwmark=20
|
||||
|
||||
# cleanup
|
||||
tc qdisc del dev $wlan root
|
||||
|
||||
# root qdisc
|
||||
tc qdisc add dev $wlan handle 1 root htb default 100
|
||||
tc class add dev $wlan parent 1: classid 1:99 htb rate 8mbit
|
||||
|
||||
#default class for unclassified traffic (should empty in normal conditions)
|
||||
tc class add dev $wlan parent 1:99 classid 1:100 htb rate 1kbit ceil 1mbit prio 100 quantum 1000
|
||||
tc qdisc add dev $wlan handle 100: parent 1:100 pfifo
|
||||
|
||||
# video
|
||||
tc class add dev $wlan parent 1:99 classid 1:1 htb rate 6.5mbit ceil 7mbit prio 2
|
||||
tc filter add dev $wlan parent 1: handle 1 fw classid 1:1 # data
|
||||
tc filter add dev $wlan parent 1: handle 2 fw classid 1:1 # fec
|
||||
|
||||
# mavlink
|
||||
tc class add dev $wlan parent 1:99 classid 1:10 htb rate 500kbit ceil 1mbit prio 1
|
||||
tc filter add dev $wlan parent 1: handle 10 fw classid 1:10 # data
|
||||
tc filter add dev $wlan parent 1: handle 11 fw classid 1:10 # fec
|
||||
|
||||
# tunnel
|
||||
tc class add dev $wlan parent 1:99 classid 1:20 htb rate 500kbit ceil 1mbit prio 3
|
||||
tc filter add dev $wlan parent 1: handle 20 fw classid 1:20 # data
|
||||
tc filter add dev $wlan parent 1: handle 21 fw classid 1:20 # fec
|
||||
|
||||
# qdisc
|
||||
tc qdisc add dev $wlan handle 101: parent 1:1 pfifo # video
|
||||
tc qdisc add dev $wlan handle 102: parent 1:10 pfifo # mavlink
|
||||
tc qdisc add dev $wlan handle 103: parent 1:20 pfifo # tunnel
|
@ -5,9 +5,10 @@ ReloadPropagatedFrom=wifibroadcast.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# common environment
|
||||
EnvironmentFile=/etc/default/wifibroadcast
|
||||
# Use for multi-link setup
|
||||
# Environment="WIFIBROADCAST_CFG=/etc/wifibroadcast_linkXXX.cfg"
|
||||
# per-profile environment
|
||||
EnvironmentFile=-/etc/default/wifibroadcast.%i
|
||||
ExecStart=/usr/bin/wfb-server %i ${WFB_NICS}
|
||||
TimeoutStopSec=5s
|
||||
Restart=on-failure
|
||||
|
58
src/tx.cpp
58
src/tx.cpp
@ -126,9 +126,26 @@ void Transmitter::make_session_key(void)
|
||||
}
|
||||
}
|
||||
|
||||
void RawSocketTransmitter::set_mark(uint32_t idx)
|
||||
{
|
||||
if (!use_qdisc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = sockfds[current_output];
|
||||
uint32_t sockopt = this->fwmark + idx;
|
||||
|
||||
if(setsockopt(fd, SOL_SOCKET, SO_MARK, (const void *)&sockopt , sizeof(sockopt)) !=0)
|
||||
{
|
||||
throw runtime_error(string_format("Unable to set SO_MARK fd(%d)=%u: %s", fd, sockopt, strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay,
|
||||
const vector<string> &wlans, shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len,
|
||||
uint8_t frame_type, bool use_qdisc, uint32_t priority) : \
|
||||
uint8_t frame_type, bool use_qdisc, uint32_t fwmark) : \
|
||||
Transmitter(k, n, keypair, epoch, channel_id, fec_delay),
|
||||
channel_id(channel_id),
|
||||
current_output(0),
|
||||
@ -137,7 +154,7 @@ RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair,
|
||||
radiotap_header_len(radiotap_header_len),
|
||||
frame_type(frame_type),
|
||||
use_qdisc(use_qdisc),
|
||||
priority(priority)
|
||||
fwmark(fwmark)
|
||||
{
|
||||
for(auto it=wlans.begin(); it!=wlans.end(); it++)
|
||||
{
|
||||
@ -147,15 +164,7 @@ RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair,
|
||||
throw runtime_error(string_format("Unable to open PF_PACKET socket: %s", strerror(errno)));
|
||||
}
|
||||
|
||||
if(use_qdisc)
|
||||
{
|
||||
if(setsockopt(fd, SOL_PACKET, SO_PRIORITY, (const void *)&priority , sizeof(priority)) !=0)
|
||||
{
|
||||
close(fd);
|
||||
throw runtime_error(string_format("Unable to set SO_PRIORITY: %s", strerror(errno)));
|
||||
}
|
||||
}
|
||||
else
|
||||
if(!use_qdisc)
|
||||
{
|
||||
const int optval = 1;
|
||||
if(setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, (const void *)&optval , sizeof(optval)) !=0)
|
||||
@ -334,7 +343,7 @@ bool Transmitter::send_packet(const uint8_t *buf, size_t size, uint8_t flags)
|
||||
assert(size <= MAX_PAYLOAD_SIZE);
|
||||
|
||||
// FEC-only packets are only for closing already opened blocks
|
||||
if (fragment_idx == 0 && flags & WFB_PACKET_FEC_ONLY)
|
||||
if (fragment_idx == 0 && (flags & WFB_PACKET_FEC_ONLY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -347,6 +356,12 @@ bool Transmitter::send_packet(const uint8_t *buf, size_t size, uint8_t flags)
|
||||
memcpy(block[fragment_idx] + sizeof(wpacket_hdr_t), buf, size);
|
||||
memset(block[fragment_idx] + sizeof(wpacket_hdr_t) + size, '\0', MAX_FEC_PAYLOAD - (sizeof(wpacket_hdr_t) + size));
|
||||
|
||||
// mark data packets with fwmark
|
||||
if(fragment_idx == 0)
|
||||
{
|
||||
set_mark(0);
|
||||
}
|
||||
|
||||
send_block_fragment(sizeof(wpacket_hdr_t) + size);
|
||||
max_packet_size = max(max_packet_size, sizeof(wpacket_hdr_t) + size);
|
||||
fragment_idx += 1;
|
||||
@ -354,6 +369,10 @@ bool Transmitter::send_packet(const uint8_t *buf, size_t size, uint8_t flags)
|
||||
if (fragment_idx < fec_k) return true;
|
||||
|
||||
fec_encode(fec_p, (const uint8_t**)block, block + fec_k, max_packet_size);
|
||||
|
||||
// mark fec packets with fwmark + 1
|
||||
set_mark(1);
|
||||
|
||||
while (fragment_idx < fec_n)
|
||||
{
|
||||
if(fec_delay > 0)
|
||||
@ -604,7 +623,7 @@ int main(int argc, char * const *argv)
|
||||
size_t radiotap_header_len = 0;
|
||||
uint8_t frame_type = FRAME_TYPE_DATA;
|
||||
bool use_qdisc = false;
|
||||
uint32_t priority = 0;
|
||||
uint32_t fwmark = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "K:k:n:u:p:F:l:B:G:S:L:M:N:D:T:i:e:R:f:mVQP:")) != -1) {
|
||||
switch (opt) {
|
||||
@ -693,14 +712,14 @@ int main(int argc, char * const *argv)
|
||||
use_qdisc = true;
|
||||
break;
|
||||
case 'P':
|
||||
priority = (uint32_t)atoi(optarg);
|
||||
fwmark = (uint32_t)atoi(optarg);
|
||||
break;
|
||||
default: /* '?' */
|
||||
show_usage:
|
||||
fprintf(stderr, "Usage: %s [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-F fec_delay] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-N VHT_NSS] [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [-m] [-V] [-Q] [-P priority] interface1 [interface2] ...\n",
|
||||
fprintf(stderr, "Usage: %s [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-F fec_delay] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-N VHT_NSS] [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [-m] [-V] [-Q] [-P fwmark] interface1 [interface2] ...\n",
|
||||
argv[0]);
|
||||
fprintf(stderr, "Default: K='%s', k=%d, n=%d, fec_delay=%u [us], udp_port=%d, link_id=0x%06x, radio_port=%u, epoch=%" PRIu64 ", bandwidth=%d guard_interval=%s stbc=%d ldpc=%d mcs_index=%d vht_nss=%d, vht_mode=%d, fec_timeout=%d, log_interval=%d, rcv_buf=system_default, frame_type=data, mirror=false, use_qdisc=false, priority=%u\n",
|
||||
keypair.c_str(), k, n, fec_delay, udp_port, link_id, radio_port, epoch, bandwidth, short_gi ? "short" : "long", stbc, ldpc, mcs_index, vht_nss, vht_mode, fec_timeout, log_interval, priority);
|
||||
fprintf(stderr, "Default: K='%s', k=%d, n=%d, fec_delay=%u [us], udp_port=%d, link_id=0x%06x, radio_port=%u, epoch=%" PRIu64 ", bandwidth=%d guard_interval=%s stbc=%d ldpc=%d mcs_index=%d vht_nss=%d, vht_mode=%d, fec_timeout=%d, log_interval=%d, rcv_buf=system_default, frame_type=data, mirror=false, use_qdisc=false, fwmark=%u\n",
|
||||
keypair.c_str(), k, n, fec_delay, udp_port, link_id, radio_port, epoch, bandwidth, short_gi ? "short" : "long", stbc, ldpc, mcs_index, vht_nss, vht_mode, fec_timeout, log_interval, fwmark);
|
||||
fprintf(stderr, "Radio MTU: %lu\n", (unsigned long)MAX_PAYLOAD_SIZE);
|
||||
fprintf(stderr, "WFB-ng version " WFB_VERSION "\n");
|
||||
fprintf(stderr, "WFB-ng home page: <http://wfb-ng.org>\n");
|
||||
@ -878,11 +897,12 @@ int main(int argc, char * const *argv)
|
||||
if (debug_port)
|
||||
{
|
||||
fprintf(stderr, "Using %zu ports from %d for wlan emulation\n", wlans.size(), debug_port);
|
||||
t = shared_ptr<UdpTransmitter>(new UdpTransmitter(k, n, keypair, "127.0.0.1", debug_port, epoch, channel_id, fec_delay));
|
||||
t = shared_ptr<UdpTransmitter>(new UdpTransmitter(k, n, keypair, "127.0.0.1", debug_port, epoch, channel_id,
|
||||
fec_delay, use_qdisc, fwmark));
|
||||
} else {
|
||||
t = shared_ptr<RawSocketTransmitter>(new RawSocketTransmitter(k, n, keypair, epoch, channel_id, fec_delay,
|
||||
wlans, radiotap_header, radiotap_header_len,
|
||||
frame_type, use_qdisc, priority));
|
||||
frame_type, use_qdisc, fwmark));
|
||||
}
|
||||
|
||||
data_source(t, rx_fd, fec_timeout, mirror, log_interval);
|
||||
|
39
src/tx.hpp
39
src/tx.hpp
@ -41,6 +41,7 @@ public:
|
||||
virtual void dump_stats(FILE *fp, uint64_t ts, uint32_t &injected_packets, uint32_t &dropped_packets, uint32_t &injected_bytes) = 0;
|
||||
protected:
|
||||
virtual void inject_packet(const uint8_t *buf, size_t size) = 0;
|
||||
virtual void set_mark(uint32_t idx) = 0;
|
||||
|
||||
private:
|
||||
void send_block_fragment(size_t packet_size);
|
||||
@ -108,12 +109,23 @@ class RawSocketTransmitter : public Transmitter
|
||||
{
|
||||
public:
|
||||
RawSocketTransmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, const std::vector<std::string> &wlans,
|
||||
shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len, uint8_t frame_type, bool use_qdisc, uint32_t priority);
|
||||
shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len, uint8_t frame_type, bool use_qdisc, uint32_t fwmark);
|
||||
virtual ~RawSocketTransmitter();
|
||||
virtual void select_output(int idx) { current_output = idx; }
|
||||
virtual void select_output(int idx)
|
||||
{
|
||||
bool sw = current_output != idx;
|
||||
current_output = idx;
|
||||
if (sw)
|
||||
{
|
||||
// select_output call should happend only between data packets
|
||||
// All FEC packets issued after last data packet in block and will have set_mark(1)
|
||||
set_mark(0);
|
||||
}
|
||||
}
|
||||
virtual void dump_stats(FILE *fp, uint64_t ts, uint32_t &injected_packets, uint32_t &dropped_packets, uint32_t &injected_bytes);
|
||||
private:
|
||||
virtual void inject_packet(const uint8_t *buf, size_t size);
|
||||
virtual void set_mark(uint32_t mark);
|
||||
const uint32_t channel_id;
|
||||
int current_output;
|
||||
uint16_t ieee80211_seq;
|
||||
@ -123,15 +135,16 @@ private:
|
||||
const size_t radiotap_header_len;
|
||||
const uint8_t frame_type;
|
||||
const bool use_qdisc;
|
||||
const uint32_t priority;
|
||||
const uint32_t fwmark;
|
||||
};
|
||||
|
||||
|
||||
class UdpTransmitter : public Transmitter
|
||||
{
|
||||
public:
|
||||
UdpTransmitter(int k, int m, const std::string &keypair, const std::string &client_addr, int base_port, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay): \
|
||||
Transmitter(k, m, keypair, epoch, channel_id, fec_delay), base_port(base_port)
|
||||
UdpTransmitter(int k, int m, const std::string &keypair, const std::string &client_addr, int base_port, uint64_t epoch, uint32_t channel_id,
|
||||
uint32_t fec_delay, bool use_qdisc, uint32_t fwmark): \
|
||||
Transmitter(k, m, keypair, epoch, channel_id, fec_delay), base_port(base_port), use_qdisc(use_qdisc), fwmark(fwmark)
|
||||
{
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) throw std::runtime_error(string_format("Error opening socket: %s", strerror(errno)));
|
||||
@ -155,6 +168,20 @@ public:
|
||||
saddr.sin_port = htons((unsigned short)(base_port + idx));
|
||||
}
|
||||
|
||||
virtual void set_mark(uint32_t idx)
|
||||
{
|
||||
if (!use_qdisc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sockopt = this->fwmark + idx;
|
||||
if(setsockopt(sockfd, SOL_SOCKET, SO_MARK, (const void *)&sockopt , sizeof(sockopt)) !=0)
|
||||
{
|
||||
throw runtime_error(string_format("Unable to set SO_MARK fd(%d)=%u: %s", sockfd, sockopt, strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void inject_packet(const uint8_t *buf, size_t size)
|
||||
{
|
||||
@ -186,4 +213,6 @@ private:
|
||||
int sockfd;
|
||||
int base_port;
|
||||
struct sockaddr_in saddr;
|
||||
const bool use_qdisc;
|
||||
const uint32_t fwmark;
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ mcs_index = 1 # mcs index
|
||||
|
||||
# Packet queueing control
|
||||
use_qdisc = False # Use or bypass kernel qdisc
|
||||
priority = 0 # If use qdisc then set skb->priority to this value
|
||||
fwmark = 0 # If use qdisc then set fwmark to this value for data packets and to fwmark + 1 for FEC packets
|
||||
|
||||
|
||||
[drone_base]
|
||||
|
@ -656,7 +656,7 @@ def init_udp_direct_tx(service_name, cfg, wlans, link_id, ant_sel_f):
|
||||
key=os.path.join(settings.path.conf_dir, cfg.keypair),
|
||||
bw=cfg.bandwidth,
|
||||
force_vht=' -V' if cfg.force_vht else '',
|
||||
qdisc=' -Q -P %d' % (cfg.priority,) if cfg.use_qdisc else '',
|
||||
qdisc=' -Q -P %d' % (cfg.fwmark,) if cfg.use_qdisc else '',
|
||||
gi="short" if cfg.short_gi else "long",
|
||||
stbc=cfg.stbc,
|
||||
ldpc=cfg.ldpc,
|
||||
@ -784,7 +784,7 @@ def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f):
|
||||
key=os.path.join(settings.path.conf_dir, cfg.keypair),
|
||||
bw=cfg.bandwidth,
|
||||
force_vht=' -V' if cfg.force_vht else '',
|
||||
qdisc=' -Q -P %d' % (cfg.priority,) if cfg.use_qdisc else '',
|
||||
qdisc=' -Q -P %d' % (cfg.fwmark,) if cfg.use_qdisc else '',
|
||||
gi="short" if cfg.short_gi else "long",
|
||||
stbc=cfg.stbc,
|
||||
ldpc=cfg.ldpc,
|
||||
@ -878,7 +878,7 @@ def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f):
|
||||
key=os.path.join(settings.path.conf_dir, cfg.keypair),
|
||||
bw=cfg.bandwidth,
|
||||
force_vht=' -V' if cfg.force_vht else '',
|
||||
qdisc=' -Q -P %d' % (cfg.priority,) if cfg.use_qdisc else '',
|
||||
qdisc=' -Q -P %d' % (cfg.fwmark,) if cfg.use_qdisc else '',
|
||||
gi="short" if cfg.short_gi else "long",
|
||||
stbc=cfg.stbc,
|
||||
ldpc=cfg.ldpc,
|
||||
@ -982,7 +982,7 @@ def init_udp_proxy(service_name, cfg, wlans, link_id, ant_sel_f):
|
||||
key=os.path.join(settings.path.conf_dir, cfg.keypair),
|
||||
bw=cfg.bandwidth,
|
||||
force_vht=' -V' if cfg.force_vht else '',
|
||||
qdisc=' -Q -P %d' % (cfg.priority,) if cfg.use_qdisc else '',
|
||||
qdisc=' -Q -P %d' % (cfg.fwmark,) if cfg.use_qdisc else '',
|
||||
gi="short" if cfg.short_gi else "long",
|
||||
stbc=cfg.stbc,
|
||||
ldpc=cfg.ldpc,
|
||||
|
@ -42,6 +42,7 @@ class TXRXTestCase(unittest.TestCase):
|
||||
cmd_rx = [os.path.join(bindir, 'wfb_rx'), '-K', 'drone.key', '-a', '10001', '-u', '10002',
|
||||
'-i', str(link_id), '-e', str(epoch), '-R', str(512 * 1024), 'wlan0']
|
||||
cmd_tx = [os.path.join(bindir, 'wfb_tx'), '-K', 'gs.key', '-u', '10003', '-D', '10004', '-T', '30', '-F', '3000',
|
||||
# '-Q', '-P 1', ## requires root priv
|
||||
'-i', str(link_id), '-e', str(epoch), '-R', str(512 * 1024), 'wlan0']
|
||||
|
||||
self.rx_pp = RXProtocol(None, cmd_rx, 'debug rx')
|
||||
|
Loading…
Reference in New Issue
Block a user