darling-network_cmds/mptcp_client/mptcp_client.c
2020-08-18 15:24:54 -04:00

702 lines
17 KiB
C

/*
* Copyright (c) 2012-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
//
// Created by Anumita Biswas on 7/17/12.
//
#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; /* time difference with gmt */
char *setup_buffer1(int bufsz)
{
int i = 0, j = 1;
char *buf;
buf = malloc(bufsz);
if (!buf)
return NULL;
bzero(buf, bufsz);
strlcpy(buf, MSG_HDR, bufsz);
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;
buf = malloc(bufsz);
if (!buf)
return NULL;
bzero(buf, bufsz);
strlcpy(buf, MSG_HDR, bufsz);
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;
buf = malloc(bufsz);
if (!buf)
return NULL;
bzero(buf, bufsz);
return buf;
}
/*
* Returns the difference between gmt and local time in seconds.
* Use gmtime() and localtime() to keep things simple.
* from tcpdump/gmt2local.c
*/
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;
/*
* If the year or julian day is different, we span 00:00 GMT
* and must add or subtract a day. Check the year first to
* avoid problems when the julian day wraps.
*/
dir = loc->tm_year - gmt->tm_year;
if (dir == 0)
dir = loc->tm_yday - gmt->tm_yday;
dt += dir * 24 * 60 * 60;
return (dt);
}
/*
* Print the timestamp
* from tcpdump/util.c
*/
static void
ts_print(void)
{
int s;
struct timeval tv;
gettimeofday(&tv, NULL);
/* Default */
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 },
{ "--verbose", "increase verbosity", 0 },
{ "--help", "display this help", 0 },
{ NULL, NULL, 0 } /* Mark end of list */
};
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: ");
}
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' },
{ "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, portno;
ssize_t n;
int reqlen = 256;
int rsplen = 256;
int ntimes = 1;
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 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";
int gotopt = 0;
thiszone = gmt2local(0);
while ((ch = getopt_long(argc, argv, "a:c:hn:p:qr:R:v", longopts, NULL)) != -1) {
gotopt = 1;
switch (ch) {
case 'a':
alt_addr_arg = optarg;
break;
case 'c':
host_arg = optarg;
break;
case 'n':
ntimes_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;
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];
} 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 || reqlen > 1024 * 1024)
errx(EX_USAGE, "invalid request length %s\n", reqlen_arg);
}
if (rsplen_arg != NULL) {
rsplen = atoi(rsplen_arg);
if (rsplen < 0 || rsplen > 1024 * 1024)
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);
}
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\n",
host_arg, port_arg, reqlen, rsplen, ntimes, alt_addr_arg);
sockfd = socket(AF_MULTIPATH, SOCK_STREAM, 0);
if (sockfd < 0)
err(EX_OSERR, "ERROR opening socket");
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;
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)
err(EX_OSERR, "ERROR connecting");
iter = 0;
while (ntimes) {
if (iter == 0) {
/* Add alternate path if available */
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 (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 (verbose) {
ts_print();
printf("close(%d)\n", sockfd);
}
close(sockfd);
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"
/*
* Print a value a la the %b format of the kernel's printf
*/
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 (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;
}
}
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");
}
/* just do an association for now */
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);
}