Commit 6c2dd397 authored by Joanne Hugé's avatar Joanne Hugé

Add option to send packets to two servers

parent 7a42d58b
......@@ -81,16 +81,27 @@ static uint64_t rtt_hist[MAX_RTT];
static uint64_t nb_cycles;
static main_param_t main_params;
static thread_param_t thread_params;
static egress_param_t egress_params;
static ingress_param_t ingress_params;
static rtt_stat_t rtt_stats = {.min_rtt = INT_MAX};
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX,
static egress_param_t egress_params;
static egress_param_t egress_params2;
static egress_stat_t egress_stats = {.packets_sent = 0,
.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX};
static egress_stat_t egress_stats2 = {.packets_sent = 0,
.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX};
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX};
static egress_info_t egress_info;
static egress_info_t egress_info2;
static int enable_histograms;
static int enable_etf;
static int enable_timestamps;
static int two_servers = 0;
static enum TSNTask tsn_task;
static struct timespec measures_start;
static struct timespec measures_end;
......@@ -99,7 +110,7 @@ static char send_data[MAX_BUFFER_SIZE];
static void help(char *argv[]) {
printf(
"Usage: %s [-a CPU -p PRIO -i USEC -r USEC -l N] [-d BUF_LEN | -c "
"DELAY -s NS] [-b -e OFFSET -q PK_PRIO -gtvT] IF IP\n\n"
"DELAY -s NS] [-b -e OFFSET -q PK_PRIO -gtvT] IF IP IF2 IP2\n\n"
" -h Show help\n"
" -f IF Set the network interface to be used\n"
" -a CPU Pin the real time thread to CPU\n"
......@@ -173,10 +184,12 @@ static void *packet_sending_thread(void *p) {
if (thread_params.start_ts) {
if (thread_params.start_ts < ts_to_uint(next)) {
fprintf(stderr, "start timestamp is in the past, aborting...\n");
fflush(stdout);
exit(EXIT_FAILURE);
}
if (thread_params.start_ts > (ts_to_uint(next) + UINT64_C(3600000000000))) {
fprintf(stderr, "start timestamp is too high, aborting...\n");
fflush(stdout);
exit(EXIT_FAILURE);
}
next = uint_to_ts(thread_params.start_ts);
......@@ -353,6 +366,8 @@ int main(int argc, char *argv[]) {
main_params.interval_input = 0;
egress_params.packet_priority = 3;
egress_params.tx_buffer_len = 1024;
egress_params2.packet_priority = 3;
egress_params2.tx_buffer_len = 1024;
enable_etf = 0;
enable_timestamps = 0;
enable_histograms = 0;
......@@ -368,10 +383,20 @@ int main(int argc, char *argv[]) {
// Process bash options
process_options(argc, argv);
main_params.target_interval = thread_params.interval / 1000;
set_latency_target();
egress_params.use_etf = enable_etf;
egress_params.use_timestamps = enable_timestamps;
egress_params2.use_etf = enable_etf;
egress_params2.use_timestamps = enable_timestamps;
egress_info.params = &egress_params;
egress_info.stats = &egress_stats;
egress_info.stats->kernel_latency_hist = kernel_latency_hist;
egress_info2.params = &egress_params2;
egress_info2.stats = &egress_stats2;
if (enable_histograms) {
// Init histograms
......@@ -388,8 +413,9 @@ int main(int argc, char *argv[]) {
init_signals(sighand);
// Initialize the UDP packet sending socket
init_udp_send(&egress_params, &egress_stats, enable_histograms,
thread_params.enable_etf_tracing, kernel_latency_hist);
init_udp_send(&egress_info, enable_histograms, thread_params.enable_etf_tracing);
if(two_servers)
init_udp_send(&egress_info2, enable_histograms, thread_params.enable_etf_tracing);
// Initialize the UDP packet receiving socket if RTT is measured
if (tsn_task == RTT_TASK)
......@@ -489,12 +515,14 @@ static void do_tsn_task(char *data, uint64_t next_txtime) {
// One way packet sending
if (tsn_task == SEND_PACKET_TASK) {
send_udp_packet(data, next_txtime);
send_udp_packet(data, next_txtime, &egress_info);
if(two_servers)
send_udp_packet(data, next_txtime, &egress_info2);
// Round Trip Time measurement
} else if (tsn_task == RTT_TASK) {
clock_gettime(CLOCK_MONOTONIC, &t1);
send_udp_packet(data, next_txtime);
send_udp_packet(data, next_txtime, &egress_info);
recv_udp_packet();
clock_gettime(CLOCK_MONOTONIC, &t2);
......@@ -634,15 +662,21 @@ static void process_options(int argc, char *argv[]) {
}
}
if (argc != optind + 2) {
if (argc != optind + 2 && argc != optind + 4) {
if (argc < optind + 2)
fprintf(stderr, "You need to specifiy an interface and IP address\n");
else
fprintf(stderr, "Too many arguments\n");
fprintf(stderr, "Wrong number of arguments");
help(argv);
exit(EXIT_FAILURE);
}
strcpy(egress_params.network_if, argv[optind]);
strcpy(egress_params.server_ip, argv[optind + 1]);
if (argc == optind + 4) {
two_servers = 1;
strcpy(egress_params2.network_if, argv[optind + 2]);
strcpy(egress_params2.server_ip, argv[optind + 3]);
}
}
......@@ -40,76 +40,67 @@
#include "tracer.h"
static void *poll_thread(void *p);
static void process_error_queue();
static void process_error_queue(egress_info_t * info);
static void init_tx_buffer();
static int set_if();
static void init_tx_buffer(egress_info_t * info);
static int set_if(egress_info_t * info);
static int so_timestamping_flags =
SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
static egress_param_t *params;
static egress_stat_t *stats;
static uint64_t *kernel_latency_hist;
static int use_histogram;
static int stop_tracing;
static uint64_t packets_sent = 0;
static struct sock_txtime sk_txtime;
static char *tx_buffer;
static int sock_fd;
static int64_t timestamps_buffer[TIMESTAMP_BUFFER_SIZE];
static int ts_buf_read_index = 0;
static int ts_buf_write_index = 0;
/*
* Init UDP socket
*/
void init_udp_send(egress_param_t *_params, egress_stat_t *_stats,
int _use_histogram, int _stop_tracing, uint64_t *_kernel_latency_hist) {
void init_udp_send(egress_info_t *info, int _use_histogram, int _stop_tracing) {
int set_if_err;
pthread_t thread;
params = _params;
stats = _stats;
kernel_latency_hist = _kernel_latency_hist;
egress_param_t *params = info->params;
egress_stat_t *stats = info->stats;
stats->packets_sent = 0;
stats->ts_buf_read_index = 0;
stats->ts_buf_write_index = 0;
use_histogram = _use_histogram;
stop_tracing = _stop_tracing;
init_tx_buffer();
init_tx_buffer(info);
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock_fd < 0) error(EXIT_FAILURE, errno, "Socket creation failed\n");
info->sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (info->sock_fd < 0) error(EXIT_FAILURE, errno, "Socket creation failed\n");
set_if_err = set_if();
set_if_err = set_if(info);
if (set_if_err < 0) error(EXIT_FAILURE, errno, "Couldn't set interface\n");
if (setsockopt(sock_fd, SOL_SOCKET, SO_PRIORITY, &params->packet_priority,
if (setsockopt(info->sock_fd, SOL_SOCKET, SO_PRIORITY, &params->packet_priority,
sizeof(params->packet_priority)))
error(EXIT_FAILURE, errno, "Couldn't set socket priority\n");
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, params->network_if,
if (setsockopt(info->sock_fd, SOL_SOCKET, SO_BINDTODEVICE, params->network_if,
strlen(params->network_if)))
error(EXIT_FAILURE, errno, "setsockopt SO_BINDTODEVICE failed\n");
if (params->use_etf) {
sk_txtime.clockid = CLOCK_REALTIME;
sk_txtime.flags = SOF_TXTIME_REPORT_ERRORS;
info->sk_txtime.clockid = CLOCK_REALTIME;
info->sk_txtime.flags = SOF_TXTIME_REPORT_ERRORS;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TXTIME, &sk_txtime,
sizeof(sk_txtime)))
if (setsockopt(info->sock_fd, SOL_SOCKET, SO_TXTIME, &info->sk_txtime,
sizeof(info->sk_txtime)))
error(EXIT_FAILURE, errno, "setsockopt SO_TXTIME failed\n");
}
if (params->use_timestamps) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags,
if (setsockopt(info->sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags,
sizeof(so_timestamping_flags)))
error(EXIT_FAILURE, errno, "setsockopt SO_TIMESTAMPING failed\n");
}
// Create poll thread
if (pthread_create(&thread, NULL, poll_thread, NULL))
if (pthread_create(&thread, NULL, poll_thread, info))
error(EXIT_FAILURE, errno, "Couldn't create poll thread");
}
......@@ -117,7 +108,7 @@ void init_udp_send(egress_param_t *_params, egress_stat_t *_stats,
/*
* Sends udp packets
*/
void send_udp_packet(char *data, uint64_t txtime) {
void send_udp_packet(char *data, uint64_t txtime, egress_info_t * info) {
struct msghdr msg; // Message hardware, sent to the socket
struct cmsghdr *cmsg; // Control message hardware, for txtime
char control[CMSG_SPACE(sizeof(txtime))] = {}; // Stores txtime
......@@ -126,23 +117,26 @@ void send_udp_packet(char *data, uint64_t txtime) {
struct sockaddr_in sin; // Server address
struct timespec ts; // timestamp for userspace timestamping
egress_param_t *params = info->params;
egress_stat_t *stats = info->stats;
if (params->use_timestamps) {
clock_gettime(CLOCK_REALTIME, &ts);
timestamps_buffer[ts_buf_write_index] = ts_to_uint(ts);
ts_buf_write_index = (ts_buf_write_index + 1) % TIMESTAMP_BUFFER_SIZE;
stats->timestamps_buffer[stats->ts_buf_write_index] = ts_to_uint(ts);
stats->ts_buf_write_index = (stats->ts_buf_write_index + 1) % TIMESTAMP_BUFFER_SIZE;
}
packets_sent++;
stats->packets_sent++;
for(int i = 0; i < (int)params->tx_buffer_len; i++)
tx_buffer[i] = data[i];
info->tx_buffer[i] = data[i];
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(params->server_ip);
sin.sin_port = htons(SERVER_PORT_INT);
iov.iov_base = tx_buffer;
iov.iov_base = info->tx_buffer;
iov.iov_len = params->tx_buffer_len;
memset(&msg, 0, sizeof(msg));
......@@ -164,29 +158,31 @@ void send_udp_packet(char *data, uint64_t txtime) {
msg.msg_controllen = cmsg->cmsg_len;
}
sendmsgerr = sendmsg(sock_fd, &msg, 0);
sendmsgerr = sendmsg(info->sock_fd, &msg, 0);
if (sendmsgerr < 0)
error(EXIT_FAILURE, errno, "sendmsg failed, ret value: %d\n", sendmsgerr);
}
static void *poll_thread(void *p) {
(void)p;
egress_info_t * info = (egress_info_t *) p;
// Poll file descriptor
struct pollfd poll_fd = {.fd = sock_fd};
struct pollfd poll_fd = {.fd = info->sock_fd};
while (1) {
int ret;
ret = poll(&poll_fd, 1, -1);
if (ret == 1 && poll_fd.revents & POLLERR) {
process_error_queue();
process_error_queue(info);
}
}
return NULL;
}
static void process_error_queue() {
static void process_error_queue(egress_info_t * info) {
int recv_ret;
// IO vector
......@@ -206,8 +202,11 @@ static void process_error_queue() {
struct cmsghdr *cmsg;
egress_stat_t *stats = info->stats;
// Timestamps and errors are received in the error queue
recv_ret = recvmsg(sock_fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
recv_ret = recvmsg(info->sock_fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
if (recv_ret == -1) {
fprintf(stderr, "recvmsg() failed\n");
return;
......@@ -219,22 +218,22 @@ static void process_error_queue() {
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
int kernel_latency =
(ts_to_uint(*stamp) - timestamps_buffer[ts_buf_read_index]) / 1000;
ts_buf_read_index = (ts_buf_read_index + 1) % TIMESTAMP_BUFFER_SIZE;
(ts_to_uint(*stamp) - stats->timestamps_buffer[stats->ts_buf_read_index]) / 1000;
stats->ts_buf_read_index = (stats->ts_buf_read_index + 1) % TIMESTAMP_BUFFER_SIZE;
stats->min_kernel_latency =
_min_(kernel_latency, stats->min_kernel_latency);
stats->max_kernel_latency =
_max_(kernel_latency, stats->max_kernel_latency);
stats->avg_kernel_latency =
(stats->max_kernel_latency * packets_sent + kernel_latency) /
(packets_sent + 1);
(stats->max_kernel_latency * stats->packets_sent + kernel_latency) /
(stats->packets_sent + 1);
if (use_histogram) {
if (kernel_latency > MAX_KERNEL_LATENCY)
stats->high_kernel_latency++;
else
kernel_latency_hist[kernel_latency]++;
stats->kernel_latency_hist[kernel_latency]++;
}
}
......@@ -264,24 +263,29 @@ static void process_error_queue() {
}
// Sets the interface
static int set_if() {
static int set_if(egress_info_t * info) {
struct ifreq ifreq;
egress_param_t * params = info->params;
memset(&ifreq, 0, sizeof(ifreq));
strncpy(ifreq.ifr_name, params->network_if, sizeof(ifreq.ifr_name) - 1);
if (ioctl(sock_fd, SIOCGIFINDEX, &ifreq))
if (ioctl(info->sock_fd, SIOCGIFINDEX, &ifreq))
error(EXIT_FAILURE, errno, "ioctl SIOCGIFINDEX failed\n");
return ifreq.ifr_ifindex;
}
static void init_tx_buffer() {
static void init_tx_buffer(egress_info_t * info) {
egress_param_t * params = info->params;
if (params->tx_buffer_len < 1) {
fprintf(stderr, "tx buffer length should be greater than 1\n");
exit(EXIT_FAILURE);
}
tx_buffer = malloc(params->tx_buffer_len);
info->tx_buffer = malloc(params->tx_buffer_len);
}
#ifndef SEND_PACKET_H
#define SEND_PACKET_H
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
typedef struct egress_param {
......@@ -16,6 +43,8 @@ typedef struct egress_param {
typedef struct egress_stat {
uint64_t packets_sent;
uint64_t high_kernel_latency;
uint64_t invalid_parameter;
uint64_t missed_deadline;
......@@ -28,13 +57,27 @@ typedef struct egress_stat {
int avg_interval;
int max_interval;
int ts_buf_read_index;
int ts_buf_write_index;
uint64_t *kernel_latency_hist;
int64_t timestamps_buffer[TIMESTAMP_BUFFER_SIZE];
} egress_stat_t;
void init_udp_send(egress_param_t *_params,
egress_stat_t *_stats,
int _use_histogram, int _stop_tracing,
uint64_t *_kernel_latency_hist);
typedef struct egress_info {
int sock_fd;
struct sock_txtime sk_txtime;
char *tx_buffer;
egress_param_t *params;
egress_stat_t *stats;
} egress_info_t;
void send_udp_packet(char *data, uint64_t txtime);
void init_udp_send(egress_info_t *info, int _use_histogram, int _stop_tracing);
void send_udp_packet(char *data, uint64_t txtime, egress_info_t * info);
#endif
......@@ -71,6 +71,7 @@ static thread_param_t thread_params;
static ingress_param_t ingress_params;
static egress_param_t egress_params;
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX};
static egress_info_t egress_info;
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX};
static int enable_histograms;
......@@ -151,24 +152,24 @@ static void *emit_signal_thread(void *p) {
clock_gettime(CLOCK_REALTIME, &current);
// Check if something went wrong
if(i > 0) {
emit_diff = calcdiff_ns_signed(current, previous_emit);
ts_diff = calcdiff_ns_signed(emit_signal_next, previous_ts);
if((emit_diff < ((int64_t)thread_params.interval) - ERROR_MARGIN_NS) ||
(emit_diff > ((int64_t)thread_params.interval) + ERROR_MARGIN_NS)) {
fprintf(stderr, "Signal emission interval reached error threshold: %" PRIi64 "\n", emit_diff);
latency_spike = 1;
}
if((ts_diff < ((int64_t)thread_params.interval) - ERROR_MARGIN_NS) ||
(ts_diff > ((int64_t)thread_params.interval) + ERROR_MARGIN_NS)) {
fprintf(stderr, "Timestamp interval reached error threshold: %" PRIi64 "\n", ts_diff);
latency_spike = 1;
}
if(latency_spike) {
fprintf(stderr, "Exiting... Current interval: %d\n", interval_us);
exit(EXIT_FAILURE);
}
}
//if(i > 0) {
// emit_diff = calcdiff_ns_signed(current, previous_emit);
// ts_diff = calcdiff_ns_signed(emit_signal_next, previous_ts);
// if((emit_diff < ((int64_t)thread_params.interval) - ERROR_MARGIN_NS) ||
// (emit_diff > ((int64_t)thread_params.interval) + ERROR_MARGIN_NS)) {
// fprintf(stderr, "Signal emission interval reached error threshold: %" PRIi64 "\n", emit_diff);
// latency_spike = 1;
// }
// if((ts_diff < ((int64_t)thread_params.interval) - ERROR_MARGIN_NS) ||
// (ts_diff > ((int64_t)thread_params.interval) + ERROR_MARGIN_NS)) {
// fprintf(stderr, "Timestamp interval reached error threshold: %" PRIi64 "\n", ts_diff);
// latency_spike = 1;
// }
// if(latency_spike) {
// fprintf(stderr, "Exiting... Current interval: %d\n", interval_us);
// exit(EXIT_FAILURE);
// }
//}
previous_emit = current;
previous_ts = emit_signal_next;
}
......@@ -221,7 +222,7 @@ static void *tsn_thread(void *p) {
// RTT
if (tsn_task == RTT_TASK) {
recv_udp_packet();
send_udp_packet("", 0);
send_udp_packet("", 0, &egress_info);
// Receive packet
} else if (tsn_task == RECV_PACKET_TASK || tsn_task == XDP_TASK) {
......@@ -413,6 +414,9 @@ int main(int argc, char *argv[]) {
ingress_params.use_timestamps = enable_timestamps;
ingress_params.interval = thread_params.interval;
egress_info.params = &egress_params;
egress_info.stats = &egress_stats;
// Init histograms
if (enable_histograms) {
memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist));
......@@ -435,7 +439,7 @@ int main(int argc, char *argv[]) {
// Initialize the UDP packet sending socket if RTT is measured
if (tsn_task == RTT_TASK)
init_udp_send(&egress_params, &egress_stats, 0, 0, NULL);
init_udp_send(&egress_info, 0, 0);
if (thread_params.emit_signal) {
pthread_mutex_init(&emit_signal_mutex, NULL);
......
......@@ -4,7 +4,7 @@ script_dir=$(dirname $(realpath $0))
usage() {
cat << ENDUSAGE
Usage: $0 [-h] QDISC_OPT [CLIENT_OPTS] BOARD_HOSTNAME
Usage: $0 [-h] QDISC_OPT [CLIENT_OPTS] IF BOARD_HOSTNAME [IF2 BOARD_HOSTNAME2]
-h Show help
QDISC_OPTS: [-e DELTA [-o USEC] -H] [-q]
Which qdisc to use (will call create-qdisc script)
......@@ -24,8 +24,8 @@ Usage: $0 [-h] QDISC_OPT [CLIENT_OPTS] BOARD_HOSTNAME
(to be used with PTP)
-d TX_BUF_LEN Length of the data sent in the packets
-i USEC Interval at which the RT thread should send packets
-I IF Network interface to use to send the packets
-a CPU CPU on which to pin the program
-U Interactive interval change
TRACE_OPTS: -T [-P TRACER -E EVENTS -B SIZE]
Options to trace with trace-cmd until ETF deadline is missed
(see trace-cmd man page and ftrace documentation)
......@@ -56,7 +56,7 @@ etf_offset=500
tracecmd_events="-e irq -e sched -e net_dev_start_xmit -e net_dev_xmit -e net_dev_xmit_timeout"
tracecmd_opts=""
while getopts "a:bc:d:e:o:ghi:qs:tB:E:I:HP:TS:" opt; do
while getopts "a:bc:d:e:o:ghi:qs:tB:E:I:HP:TS:U:" opt; do
case "${opt}" in
h )
usage
......@@ -106,9 +106,6 @@ while getopts "a:bc:d:e:o:ghi:qs:tB:E:I:HP:TS:" opt; do
E )
tracecmd_events=${OPTARG}
;;
I )
interface="${OPTARG}"
;;
H )
qdisc_options+=" -H"
;;
......@@ -123,20 +120,37 @@ while getopts "a:bc:d:e:o:ghi:qs:tB:E:I:HP:TS:" opt; do
use_tracer=1
client_options+=" -S ${OPTARG}"
;;
U )
client_options+=" -U ${OPTARG}"
;;
* )
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "$1" ]; then
usage
fi
qdisc_options+=" -I $interface"
client_options+=" -a $cpu"
board_name=$1
if [ -z "$1" ] || [ -z "$2" ]; then
usage
fi
board_name=$2
board_ip=$(cat /etc/hosts | grep $board_name | awk '{print $1}')
interface=$1
board2_ip=""
interface2=""
if [ -n "$3" ]; then
if [ -z "$4" ]; then
usage
fi
interface2=$3
board2_name=$4
board2_ip=$(cat /etc/hosts | grep $board2_name | awk '{print $1}')
fi
if [ -z "${use_histogram}" ]; then
client_options+=" -v"
......@@ -174,18 +188,18 @@ cd $script_dir;
if [ -n "${use_histogram}" ]; then
echo "client $client_options $interface $board_ip > $output;mv $output ~/";
$script_dir/../packet-exchange/build/client $client_options $interface $board_ip > $output;
echo "client $client_options $interface $board_ip $interface2 $board2_ip > $output;mv $output ~/";
$script_dir/../packet-exchange/build/client $client_options $interface $board_ip $interface2 $board2_ip > $output;
mv $output ~/;
elif [ -n "${use_tracer}" ]; then
echo "trace-cmd record $tracecmd_opts $tracecmd_events ./client $client_options $interface $board_ip";
trace-cmd record $tracecmd_opts $tracecmd_events $script_dir/../packet-exchange/build/client $client_options $interface $board_ip;
echo "trace-cmd record $tracecmd_opts $tracecmd_events ./client $client_options $interface $board_ip $interface2 $board2_ip";
trace-cmd record $tracecmd_opts $tracecmd_events $script_dir/../packet-exchange/build/client $client_options $interface $board_ip $interface2 $board2_ip;
else
echo "client $client_options $interface $board_ip";
$script_dir/../packet-exchange/build/client $client_options $interface $board_ip;
echo "client $client_options $interface $board_ip $interface2 $board2_ip";
$script_dir/../packet-exchange/build/client $client_options $interface $board_ip $interface2 $board2_ip;
fi
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment