#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h>
#include <err.h>
#include <sysexits.h>
#include <getopt.h>
#include "conn_lib.h"
struct so_cordreq socorder;
static void showmpinfo(int s);
#define MSG_HDR "Message Header"
#define RESPONSE "I got your message"
static int verbose = 0;
static int32_t thiszone = 0;
char* setup_buffer1(int bufsz)
{
int i = 0, j = 1;
char *buf = malloc(bufsz);
if (buf) {
bzero(buf, bufsz);
strlcpy(buf, MSG_HDR, sizeof(MSG_HDR));
}
for (i = sizeof(MSG_HDR); i < bufsz; i++) {
buf[i] = j;
j++;
if (j >= 255)
j = 1;
}
return buf;
}
char* setup_buffer2(int bufsz)
{
int i = 0;
char j = 'A';
char *buf = malloc(bufsz);
if (buf) {
bzero(buf, bufsz);
strlcpy(buf, MSG_HDR, sizeof(MSG_HDR));
}
for (i = sizeof(MSG_HDR); i < bufsz; i++) {
buf[i] = j;
j++;
if (j >= 'z')
j = 'A';
}
return buf;
}
char *setup_buffer3(int bufsz)
{
char *buf = malloc(bufsz);
if (buf) {
bzero(buf, bufsz);
}
return buf;
}
static int32_t
gmt2local(time_t t)
{
int dt, dir;
struct tm *gmt, *loc;
struct tm sgmt;
if (t == 0)
t = time(NULL);
gmt = &sgmt;
*gmt = *gmtime(&t);
loc = localtime(&t);
dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
(loc->tm_min - gmt->tm_min) * 60;
dir = loc->tm_year - gmt->tm_year;
if (dir == 0)
dir = loc->tm_yday - gmt->tm_yday;
dt += dir * 24 * 60 * 60;
return (dt);
}
static void
ts_print(void)
{
int s;
struct timeval tv;
gettimeofday(&tv, NULL);
s = (tv.tv_sec + thiszone) % 86400;
printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60,
(u_int32_t)tv.tv_usec);
}
static const char *
basename(const char * str)
{
const char *last_slash = strrchr(str, '/');
if (last_slash == NULL)
return (str);
else
return (last_slash + 1);
}
struct option_desc {
const char *option;
const char *description;
int required;
};
struct option_desc option_desc_list[] = {
{ "--host addr", "address of server to connect to", 1 },
{ "--port n", "port of server to connect to", 1 },
{ "--reqlen n", "length of request (256 by default)", 0 },
{ "--rsplen n", "length of response (256 by default)", 0 },
{ "--ntimes n", "number of time to send request (1 by default)", 0 },
{ "--alt_addr addr", "alternate server to connect to", 0 },
{ "--connorder add", "alternate server to connect to", 0 },
{ "--longlived n", "number of reconnection for long lived (default 0)", 0 },
{ "--fastjoin (0|1)", "use fast join (default 0)", 0 },
{ "--nowaitforjoin (0|1)", "do not wait for join (default 0 -- i.e. wait)", 0 },
{ "--verbose", "increase verbosity", 0 },
{ "--help", "display this help", 0 },
{ NULL, NULL, 0 }
};
static void
usage(const char *cmd)
{
struct option_desc *option_desc;
char *usage_str = malloc(LINE_MAX);
size_t usage_len;
if (usage_str == NULL)
err(1, "%s: malloc(%d)", __func__, LINE_MAX);
usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd));
for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
int len;
if (option_desc->required)
len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option);
else
len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option);
if (len < 0)
err(1, "%s: snprintf(", __func__);
usage_len += len;
if (usage_len > LINE_MAX)
break;
}
printf("%s\n", usage_str);
printf("options:\n");
for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
printf(" %-24s # %s\n", option_desc->option, option_desc->description);
}
printf("\n");
printf("# legacy usage: ");
printf("%s hostname port reqlen rsplen ntimes alt_addr 0 connorder longlived fastjoin nowaitforjoin\n",
basename(cmd));
}
static struct option longopts[] = {
{ "host", required_argument, NULL, 'c' },
{ "port", required_argument, NULL, 'p' },
{ "reqlen", required_argument, NULL, 'r' },
{ "rsplen", required_argument, NULL, 'R' },
{ "ntimes", required_argument, NULL, 'n' },
{ "alt_addr", required_argument, NULL, 'a' },
{ "connorder", required_argument, NULL, 'o' },
{ "longlived", required_argument, NULL, 'l' },
{ "fastjoin", required_argument, NULL, 'f' },
{ "nowaitforjoin", required_argument, NULL, 'w' },
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, 'v' },
{ "quiet", no_argument, NULL, 'q' },
{ NULL, 0, NULL, 0 }
};
static int
sprint_sockaddr(char *str, socklen_t strlen, struct sockaddr *sa)
{
int retval = 0;
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)sa;
char str4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, str4, sizeof(str4));
retval = snprintf(str, strlen, "%s:%u", str4, ntohs(sin->sin_port));
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
char str6[INET6_ADDRSTRLEN];
char ifname[IF_NAMESIZE];
char scopestr[2 + IF_NAMESIZE];
inet_ntop(AF_INET6, &sin6->sin6_addr, str6, sizeof(str6));
if (sin6->sin6_scope_id == 0)
*scopestr = '\0';
else {
if_indextoname(sin6->sin6_scope_id, ifname);
snprintf(scopestr, sizeof(scopestr), "%%%s", ifname);
}
retval = snprintf(str, strlen, "%s%s:%u",
str6,
scopestr,
ntohs(sin6->sin6_port));
}
return (retval);
}
int main(int argc, char * const *argv)
{
int sockfd, ps, portno;
ssize_t n;
int reqlen = 256;
int rsplen = 256;
int ntimes = 1;
int connordrtest = 0;
int longlived = 0;
int fastjoin = 0;
char *buffer = NULL;
char *buffer1;
char *buffer2;
char *buffer3;
struct addrinfo *ares = NULL, ahints;
struct addrinfo *altres = NULL;
int retval = 0;
int which_buf = 0;
sae_connid_t cid1, cid2;
int iter;
int bytes_to_rdwr;
int peeled_off = 0;
int nowaitforjoin = 0;
int ch;
const char *host_arg = NULL;
const char *port_arg = NULL;
const char *reqlen_arg = "256";
const char *rsplen_arg = "256";
const char *ntimes_arg = "1";
const char *alt_addr_arg = NULL;
const char *alt_port_arg = "0";
const char *connorder_arg = NULL;
const char *longlived_arg = NULL;
const char *fastjoin_arg = NULL;
const char *nowaitforjoin_arg = NULL;
int gotopt = 0;
thiszone = gmt2local(0);
while ((ch = getopt_long(argc, argv, "a:c:f:hl:n:o:p:qr:R:vw:", longopts, NULL)) != -1) {
gotopt = 1;
switch (ch) {
case 'a':
alt_addr_arg = optarg;
break;
case 'c':
host_arg = optarg;
break;
case 'f':
fastjoin_arg = optarg;
break;
case 'l':
longlived_arg = optarg;
break;
case 'n':
ntimes_arg = optarg;
break;
case 'o':
connorder_arg = optarg;
break;
case 'p':
port_arg = optarg;
break;
case 'q':
verbose--;
break;
case 'r':
reqlen_arg = optarg;
break;
case 'R':
rsplen_arg = optarg;
break;
case 'v':
verbose++;
break;
case 'w':
nowaitforjoin_arg = optarg;
break;
default:
usage(argv[0]);
exit(EX_USAGE);
}
}
if (gotopt == 0) {
if (argc == 12) {
host_arg = argv[1];
port_arg = argv[2];
reqlen_arg = argv[3];
rsplen_arg = argv[4];
ntimes_arg = argv[5];
alt_addr_arg = argv[6];
connorder_arg = argv[8];
longlived_arg = argv[9];
fastjoin_arg = argv[10];
nowaitforjoin_arg = argv[11];
} else {
usage(argv[0]);
exit(EX_USAGE);
}
}
if (host_arg == NULL)
errx(EX_USAGE, "missing required host option\n");
if (port_arg == NULL)
errx(EX_USAGE, "missing required port option\n");
portno = atoi(port_arg);
if (portno < 0 || portno > 65535)
errx(EX_USAGE, "invalid port %s\n", port_arg);
if (reqlen_arg != NULL) {
reqlen = atoi(reqlen_arg);
if (reqlen < 0)
errx(EX_USAGE, "invalid request length %s\n", reqlen_arg);
}
if (rsplen_arg != NULL) {
rsplen = atoi(rsplen_arg);
if (rsplen < 0)
errx(EX_USAGE, "invalid response length %s\n", rsplen_arg);
}
if (ntimes_arg != NULL) {
ntimes = atoi(ntimes_arg);
if (ntimes < 1)
errx(EX_USAGE, "invalid ntimes option %s\n", ntimes_arg);
}
if (connorder_arg != NULL) {
connordrtest = atoi(connorder_arg);
if (connordrtest != 0 && connordrtest != 1)
errx(EX_USAGE, "invalid connorder count %s\n", connorder_arg);
}
if (longlived_arg != NULL) {
longlived = atoi(longlived_arg);
if (longlived < 0)
errx(EX_USAGE, "invalid longlived count %s\n", longlived_arg);
}
if (fastjoin_arg != NULL) {
fastjoin = atoi(fastjoin_arg);
if (fastjoin != 0 && fastjoin != 1)
errx(EX_USAGE, "invalid fastjoin option %s\n", fastjoin_arg);
}
if (nowaitforjoin_arg != NULL) {
nowaitforjoin = atoi(nowaitforjoin_arg);
if (nowaitforjoin != 0 && nowaitforjoin != 1)
errx(EX_USAGE, "invalid nowaitforjoin option %s\n", nowaitforjoin_arg);
}
buffer1 = setup_buffer1(reqlen);
if (!buffer1) {
printf("client: failed to alloc buffer space \n");
return -1;
}
buffer2 = setup_buffer2(reqlen);
if (!buffer2) {
printf("client: failed to alloc buffer space \n");
return -1;
}
buffer3 = setup_buffer3(rsplen);
if (!buffer3) {
printf("client: failed to alloc buffer space \n");
return -1;
}
if (verbose > 0)
printf("host: %s port: %s reqlen: %d rsplen: %d ntimes: %d alt_addr: %s connorder: %d longlived: %d fasjoin: %d nowaitforjoin: %d\n",
host_arg, port_arg, reqlen, rsplen, ntimes, alt_addr_arg, connordrtest, longlived, fastjoin, nowaitforjoin);
sockfd = socket(AF_MULTIPATH, SOCK_STREAM, 0);
if (sockfd < 0)
err(EX_OSERR, "ERROR opening socket");
#define SO_MPTCP_FASTJOIN 0x1111
opterr = setsockopt(sockfd, SOL_SOCKET, SO_MPTCP_FASTJOIN, &fastjoin, sizeof(fastjoin));
if (opterr != 0)
warn("setsockopt(SO_MPTCP_FASTJOIN, %d)", fastjoin);
memset(&ahints, 0, sizeof(struct addrinfo));
ahints.ai_family = AF_INET;
ahints.ai_socktype = SOCK_STREAM;
ahints.ai_protocol = IPPROTO_TCP;
retval = getaddrinfo(host_arg, port_arg, &ahints, &ares);
if (retval != 0)
printf("getaddrinfo(%s, %s) failed %d\n", host_arg, port_arg, retval);
bytes_to_rdwr = reqlen;
connect_again:
cid1 = cid2 = SAE_CONNID_ANY;
int ifscope = 0;
int error = 0;
if (verbose > 0) {
char str[2 * INET6_ADDRSTRLEN];
ts_print();
sprint_sockaddr(str, sizeof(str), ares->ai_addr);
printf("connectx(%s, %d, %d)\n", str, ifscope, cid1);
}
sa_endpoints_t sa;
bzero(&sa, sizeof(sa));
sa.sae_dstaddr = ares->ai_addr;
sa.sae_dstaddrlen = ares->ai_addrlen;
sa.sae_srcif = ifscope;
error = connectx(sockfd, &sa, SAE_ASSOCID_ANY, 0, NULL, 0, NULL, &cid1);
if ((error != 0) && (errno != EPROTO)) {
err(EX_OSERR, "ERROR connecting");
} else if ((error != 0) && (errno == EPROTO)) {
ps = peeloff(sockfd, SAE_ASSOCID_ANY);
if (ps != -1) {
close(sockfd);
sockfd = ps;
}
peeled_off = 1;
ts_print();
printf("%s: peeled off\n", __func__);
}
iter = 0;
while (ntimes) {
if ((iter == 0) && (peeled_off == 0)) {
if (alt_addr_arg && alt_addr_arg[0] != 0) {
retval = getaddrinfo(alt_addr_arg, alt_port_arg, &ahints, &altres);
if (retval != 0)
printf("client: alternate address resolution failed. \n");
else {
printf("client: connecting to alternate address (ifscope %d)\n", ifscope);
if (verbose > 0) {
char str[2 * INET6_ADDRSTRLEN];
ts_print();
sprint_sockaddr(str, sizeof(str), altres->ai_addr);
printf("connectx(%s, %d, %d)\n", str, ifscope, cid1);
}
sa_endpoints_t sa;
bzero(&sa, sizeof(sa));
sa.sae_srcif = ifscope;
sa.sae_srcaddr = altres->ai_addr;
sa.sae_srcaddrlen = altres->ai_addrlen;
sa.sae_dstaddr = ares->ai_addr;
sa.sae_dstaddrlen = ares->ai_addrlen;
error = connectx(sockfd, &sa, SAE_ASSOCID_ANY, 0, NULL, 0, NULL, &cid2);
if (error < 0) {
err(EX_OSERR, "ERROR setting up alternate path");
}
}
}
}
if ((iter == 10) && (connordrtest == 1)) {
int retval = 0;
socorder.sco_cid = cid2;
socorder.sco_rank = 1;
retval = ioctl(sockfd, SIOCSCONNORDER, &socorder);
if (retval != 0)
warn("Error in changing priority");
bzero(&socorder, sizeof(socorder));
socorder.sco_cid = cid2;
retval = ioctl(sockfd, SIOCGCONNORDER, &socorder);
printf("cid %d rank %d", socorder.sco_cid, socorder.sco_rank);
socorder.sco_cid = cid1;
socorder.sco_rank = 0;
retval = ioctl(sockfd, SIOCSCONNORDER, &socorder);
if (retval != 0)
warn("Error in changing priority");
bzero(&socorder, sizeof(socorder));
socorder.sco_cid = cid1;
retval = ioctl(sockfd, SIOCGCONNORDER, &socorder);
printf("cid %d rank %d \n", socorder.sco_cid, socorder.sco_rank);
}
if (which_buf == 0) {
buffer = buffer1;
which_buf = 1;
} else {
buffer = buffer2;
which_buf = 0;
}
while (bytes_to_rdwr) {
if (verbose) {
ts_print();
printf("writing %d bytes\n", bytes_to_rdwr);
}
n = write(sockfd, buffer, bytes_to_rdwr);
if (n <= 0) {
err(EX_OSERR, "ERROR writing to socket");
}
if (n <= bytes_to_rdwr)
bytes_to_rdwr -= n;
else {
errx(EX_DATAERR, "ERROR extra data write %zd %d\n", n, bytes_to_rdwr);
}
}
bytes_to_rdwr = rsplen;
while (bytes_to_rdwr) {
if (verbose) {
ts_print();
printf("reading %d bytes\n", rsplen);
}
n = read(sockfd, buffer3, rsplen);
if (n <= 0) {
err(EX_OSERR, "ERROR reading from socket");
}
if (n <= bytes_to_rdwr)
bytes_to_rdwr -= n;
else {
errx(EX_DATAERR, "ERROR extra bytes read n:%zd expected:%d\n", n, bytes_to_rdwr);
}
}
bytes_to_rdwr = reqlen;
ntimes--;
iter++;
}
printf("client: Req size %d Rsp size %d Read/Write %d times \n", reqlen, rsplen, iter);
showmpinfo(sockfd);
if ((!longlived) || (peeled_off == 1)) {
if (verbose) {
ts_print();
printf("close(%d)\n", sockfd);
}
close(sockfd);
} else {
printf("Longlived countdown # %d. \n", longlived);
if (verbose) {
ts_print();
printf("disconnectx(%d, %d)\n", sockfd, cid1);
}
disconnectx(sockfd, SAE_ASSOCID_ANY, cid1);
if (cid2 != SAE_CONNID_ANY) {
if (verbose) {
ts_print();
printf("disconnectx(%d, %d)\n", sockfd, cid2);
}
disconnectx(sockfd, SAE_ASSOCID_ANY, cid2);
}
if (!nowaitforjoin) {
if (verbose) {
ts_print();
printf("sleep(10)\n");
}
sleep(10);
}
longlived--;
ntimes = atoi(ntimes_arg);
bytes_to_rdwr = reqlen / 2;
if (verbose) {
ts_print();
printf("fastjoin writing %d bytes\n", bytes_to_rdwr);
}
n = write(sockfd, buffer, bytes_to_rdwr);
if (n <= 0) {
warnx("Fastjoin: Error writing to socket. \n");
} else {
bytes_to_rdwr = reqlen - (int)n;
printf("FastJoin: Wrote %zd bytes, remaining %d of %d \n", n, bytes_to_rdwr, reqlen);
}
goto connect_again;
}
if (ares)
freeaddrinfo(ares);
if (altres)
freeaddrinfo(altres);
return 0;
}
#define CIF_BITS \
"\020\1CONNECTING\2CONNECTED\3DISCONNECTING\4DISCONNECTED\5BOUND_IF"\
"\6BOUND_IP\7BOUND_PORT\10PREFERRED\11MP_CAPABLE\12MP_READY" \
"\13MP_DEGRADED"
static void
printb(const char *s, unsigned v, const char *bits)
{
int i, any = 0;
char c;
if (bits && *bits == 8)
printf("%s=%o", s, v);
else
printf("%s=%x", s, v);
bits++;
if (bits) {
putchar('<');
while ((i = *bits++) != '\0') {
if (v & (1 << (i-1))) {
if (any)
putchar(',');
any = 1;
for (; (c = *bits) > 32; bits++)
putchar(c);
} else {
for (; *bits > 32; bits++)
;
}
}
putchar('>');
}
}
static int
showconninfo(int s, sae_connid_t cid)
{
char buf[INET6_ADDRSTRLEN];
conninfo_t *cfo = NULL;
int err;
err = copyconninfo(s, cid, &cfo);
if (err != 0) {
printf("getconninfo failed for cid %d\n", cid);
goto out;
}
printf("%6d:\t", cid);
printb("flags", cfo->ci_flags, CIF_BITS);
printf("\n");
#if 1
if (cfo->ci_src != NULL) {
printf("\tsrc %s port %d\n", inet_ntop(cfo->ci_src->sa_family,
(cfo->ci_src->sa_family == AF_INET) ?
(void *)&((struct sockaddr_in *)cfo->ci_src)->
sin_addr.s_addr :
(void *)&((struct sockaddr_in6 *)cfo->ci_src)->sin6_addr,
buf, sizeof (buf)),
(cfo->ci_src->sa_family == AF_INET) ?
ntohs(((struct sockaddr_in *)cfo->ci_src)->sin_port) :
ntohs(((struct sockaddr_in6 *)cfo->ci_src)->sin6_port));
}
if (cfo->ci_dst != NULL) {
printf("\tdst %s port %d\n", inet_ntop(cfo->ci_dst->sa_family,
(cfo->ci_dst->sa_family == AF_INET) ?
(void *)&((struct sockaddr_in *)cfo->ci_dst)->
sin_addr.s_addr :
(void *)&((struct sockaddr_in6 *)cfo->ci_dst)->sin6_addr,
buf, sizeof (buf)),
(cfo->ci_dst->sa_family == AF_INET) ?
ntohs(((struct sockaddr_in *)cfo->ci_dst)->sin_port) :
ntohs(((struct sockaddr_in6 *)cfo->ci_dst)->sin6_port));
}
if (cfo->ci_aux_data != NULL) {
switch (cfo->ci_aux_type) {
case CIAUX_TCP:
printf("\tTCP aux info available\n");
break;
default:
printf("\tUnknown aux type %d\n", cfo->ci_aux_type);
break;
}
}
#endif
out:
if (cfo != NULL)
freeconninfo(cfo);
return (err);
}
static void
showmpinfo(int s)
{
uint32_t aid_cnt = 0, cid_cnt = 0;
sae_associd_t *aid = NULL;
sae_connid_t *cid = NULL;
int i, error = 0;
error = copyassocids(s, &aid, &aid_cnt);
if (error != 0) {
printf("copyassocids failed\n");
goto done;
} else {
printf("found %d associations", aid_cnt);
if (aid_cnt > 0) {
printf(" with IDs:");
for (i = 0; i < aid_cnt; i++)
printf(" %d\n", aid[i]);
}
printf("\n");
}
error = copyconnids(s, SAE_ASSOCID_ANY, &cid, &cid_cnt);
if (error != 0) {
warn("getconnids failed\n");
goto done;
} else {
printf("found %d connections", cid_cnt);
if (cid_cnt > 0) {
printf(":\n");
for (i = 0; i < cid_cnt; i++) {
if (showconninfo(s, cid[i]) != 0)
break;
}
}
printf("\n");
}
done:
if (aid != NULL)
freeassocids(aid);
if (cid != NULL)
freeconnids(cid);
}