2 * Copyright (c) 2012-2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * The Regents of the University of California. All rights reserved.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that: (1) source code distributions
34 * retain the above copyright notice and this paragraph in its entirety, (2)
35 * distributions including binary code include the above copyright notice and
36 * this paragraph in its entirety in the documentation or other materials
37 * provided with the distribution, and (3) all advertising materials mentioning
38 * features or use of this software display the following acknowledgement:
39 * ``This product includes software developed by the University of California,
40 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
41 * the University nor the names of its contributors may be used to endorse
42 * or promote products derived from this software without specific prior
44 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
45 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
46 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50 // Created by Anumita Biswas on 7/17/12.
58 #include <sys/types.h>
59 #include <sys/socket.h>
60 #include <sys/ioctl.h>
62 #include <netinet/in.h>
65 #include <arpa/inet.h>
72 struct so_cordreq socorder
;
73 static void showmpinfo(int s
);
75 #define MSG_HDR "Message Header"
76 #define RESPONSE "I got your message"
78 static int verbose
= 0;
80 static int32_t thiszone
= 0; /* time difference with gmt */
82 char* setup_buffer1(int bufsz
)
85 char *buf
= malloc(bufsz
);
88 strlcpy(buf
, MSG_HDR
, sizeof(MSG_HDR
));
90 for (i
= sizeof(MSG_HDR
); i
< bufsz
; i
++) {
99 char* setup_buffer2(int bufsz
)
103 char *buf
= malloc(bufsz
);
106 strlcpy(buf
, MSG_HDR
, sizeof(MSG_HDR
));
108 for (i
= sizeof(MSG_HDR
); i
< bufsz
; i
++) {
117 char *setup_buffer3(int bufsz
)
119 char *buf
= malloc(bufsz
);
127 * Returns the difference between gmt and local time in seconds.
128 * Use gmtime() and localtime() to keep things simple.
129 * from tcpdump/gmt2local.c
135 struct tm
*gmt
, *loc
;
143 dt
= (loc
->tm_hour
- gmt
->tm_hour
) * 60 * 60 +
144 (loc
->tm_min
- gmt
->tm_min
) * 60;
147 * If the year or julian day is different, we span 00:00 GMT
148 * and must add or subtract a day. Check the year first to
149 * avoid problems when the julian day wraps.
151 dir
= loc
->tm_year
- gmt
->tm_year
;
153 dir
= loc
->tm_yday
- gmt
->tm_yday
;
154 dt
+= dir
* 24 * 60 * 60;
160 * Print the timestamp
161 * from tcpdump/util.c
169 gettimeofday(&tv
, NULL
);
172 s
= (tv
.tv_sec
+ thiszone
) % 86400;
173 printf("%02d:%02d:%02d.%06u ", s
/ 3600, (s
% 3600) / 60, s
% 60,
174 (u_int32_t
)tv
.tv_usec
);
178 basename(const char * str
)
180 const char *last_slash
= strrchr(str
, '/');
182 if (last_slash
== NULL
)
185 return (last_slash
+ 1);
190 const char *description
;
194 struct option_desc option_desc_list
[] = {
195 { "--host addr", "address of server to connect to", 1 },
196 { "--port n", "port of server to connect to", 1 },
197 { "--reqlen n", "length of request (256 by default)", 0 },
198 { "--rsplen n", "length of response (256 by default)", 0 },
199 { "--ntimes n", "number of time to send request (1 by default)", 0 },
200 { "--alt_addr addr", "alternate server to connect to", 0 },
201 { "--connorder add", "alternate server to connect to", 0 },
202 { "--longlived n", "number of reconnection for long lived (default 0)", 0 },
203 { "--fastjoin (0|1)", "use fast join (default 0)", 0 },
204 { "--nowaitforjoin (0|1)", "do not wait for join (default 0 -- i.e. wait)", 0 },
205 { "--verbose", "increase verbosity", 0 },
206 { "--help", "display this help", 0 },
208 { NULL
, NULL
, 0 } /* Mark end of list */
212 usage(const char *cmd
)
214 struct option_desc
*option_desc
;
215 char *usage_str
= malloc(LINE_MAX
);
218 if (usage_str
== NULL
)
219 err(1, "%s: malloc(%d)", __func__
, LINE_MAX
);
221 usage_len
= snprintf(usage_str
, LINE_MAX
, "# usage: %s ", basename(cmd
));
223 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
226 if (option_desc
->required
)
227 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "%s ", option_desc
->option
);
229 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "[%s] ", option_desc
->option
);
231 err(1, "%s: snprintf(", __func__
);
234 if (usage_len
> LINE_MAX
)
237 printf("%s\n", usage_str
);
238 printf("options:\n");
240 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
241 printf(" %-24s # %s\n", option_desc
->option
, option_desc
->description
);
244 printf("# legacy usage: ");
245 printf("%s hostname port reqlen rsplen ntimes alt_addr 0 connorder longlived fastjoin nowaitforjoin\n",
249 static struct option longopts
[] = {
250 { "host", required_argument
, NULL
, 'c' },
251 { "port", required_argument
, NULL
, 'p' },
252 { "reqlen", required_argument
, NULL
, 'r' },
253 { "rsplen", required_argument
, NULL
, 'R' },
254 { "ntimes", required_argument
, NULL
, 'n' },
255 { "alt_addr", required_argument
, NULL
, 'a' },
256 { "connorder", required_argument
, NULL
, 'o' },
257 { "longlived", required_argument
, NULL
, 'l' },
258 { "fastjoin", required_argument
, NULL
, 'f' },
259 { "nowaitforjoin", required_argument
, NULL
, 'w' },
260 { "help", no_argument
, NULL
, 'h' },
261 { "verbose", no_argument
, NULL
, 'v' },
262 { "quiet", no_argument
, NULL
, 'q' },
268 sprint_sockaddr(char *str
, socklen_t strlen
, struct sockaddr
*sa
)
272 if (sa
->sa_family
== AF_INET
) {
273 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
274 char str4
[INET_ADDRSTRLEN
];
276 inet_ntop(AF_INET
, &sin
->sin_addr
, str4
, sizeof(str4
));
278 retval
= snprintf(str
, strlen
, "%s:%u", str4
, ntohs(sin
->sin_port
));
279 } else if (sa
->sa_family
== AF_INET6
) {
280 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
281 char str6
[INET6_ADDRSTRLEN
];
282 char ifname
[IF_NAMESIZE
];
283 char scopestr
[2 + IF_NAMESIZE
];
285 inet_ntop(AF_INET6
, &sin6
->sin6_addr
, str6
, sizeof(str6
));
287 if (sin6
->sin6_scope_id
== 0)
290 if_indextoname(sin6
->sin6_scope_id
, ifname
);
291 snprintf(scopestr
, sizeof(scopestr
), "%%%s", ifname
);
294 retval
= snprintf(str
, strlen
, "%s%s:%u",
297 ntohs(sin6
->sin6_port
));
302 int main(int argc
, char * const *argv
)
304 int sockfd
, ps
, portno
;
309 int connordrtest
= 0;
316 struct addrinfo
*ares
= NULL
, ahints
;
317 struct addrinfo
*altres
= NULL
;
324 int nowaitforjoin
= 0;
326 const char *host_arg
= NULL
;
327 const char *port_arg
= NULL
;
328 const char *reqlen_arg
= "256";
329 const char *rsplen_arg
= "256";
330 const char *ntimes_arg
= "1";
331 const char *alt_addr_arg
= NULL
;
332 const char *alt_port_arg
= "0";
333 const char *connorder_arg
= NULL
;
334 const char *longlived_arg
= NULL
;
335 const char *fastjoin_arg
= NULL
;
336 const char *nowaitforjoin_arg
= NULL
;
339 thiszone
= gmt2local(0);
341 while ((ch
= getopt_long(argc
, argv
, "a:c:f:hl:n:o:p:qr:R:vw:", longopts
, NULL
)) != -1) {
345 alt_addr_arg
= optarg
;
351 fastjoin_arg
= optarg
;
354 longlived_arg
= optarg
;
360 connorder_arg
= optarg
;
378 nowaitforjoin_arg
= optarg
;
392 reqlen_arg
= argv
[3];
393 rsplen_arg
= argv
[4];
394 ntimes_arg
= argv
[5];
395 alt_addr_arg
= argv
[6];
396 connorder_arg
= argv
[8];
397 longlived_arg
= argv
[9];
398 fastjoin_arg
= argv
[10];
399 nowaitforjoin_arg
= argv
[11];
406 if (host_arg
== NULL
)
407 errx(EX_USAGE
, "missing required host option\n");
409 if (port_arg
== NULL
)
410 errx(EX_USAGE
, "missing required port option\n");
411 portno
= atoi(port_arg
);
412 if (portno
< 0 || portno
> 65535)
413 errx(EX_USAGE
, "invalid port %s\n", port_arg
);
415 if (reqlen_arg
!= NULL
) {
416 reqlen
= atoi(reqlen_arg
);
418 errx(EX_USAGE
, "invalid request length %s\n", reqlen_arg
);
421 if (rsplen_arg
!= NULL
) {
422 rsplen
= atoi(rsplen_arg
);
424 errx(EX_USAGE
, "invalid response length %s\n", rsplen_arg
);
427 if (ntimes_arg
!= NULL
) {
428 ntimes
= atoi(ntimes_arg
);
430 errx(EX_USAGE
, "invalid ntimes option %s\n", ntimes_arg
);
433 if (connorder_arg
!= NULL
) {
434 connordrtest
= atoi(connorder_arg
);
435 if (connordrtest
!= 0 && connordrtest
!= 1)
436 errx(EX_USAGE
, "invalid connorder count %s\n", connorder_arg
);
439 if (longlived_arg
!= NULL
) {
440 longlived
= atoi(longlived_arg
);
442 errx(EX_USAGE
, "invalid longlived count %s\n", longlived_arg
);
445 if (fastjoin_arg
!= NULL
) {
446 fastjoin
= atoi(fastjoin_arg
);
447 if (fastjoin
!= 0 && fastjoin
!= 1)
448 errx(EX_USAGE
, "invalid fastjoin option %s\n", fastjoin_arg
);
451 if (nowaitforjoin_arg
!= NULL
) {
452 nowaitforjoin
= atoi(nowaitforjoin_arg
);
453 if (nowaitforjoin
!= 0 && nowaitforjoin
!= 1)
454 errx(EX_USAGE
, "invalid nowaitforjoin option %s\n", nowaitforjoin_arg
);
457 buffer1
= setup_buffer1(reqlen
);
459 printf("client: failed to alloc buffer space \n");
463 buffer2
= setup_buffer2(reqlen
);
465 printf("client: failed to alloc buffer space \n");
469 buffer3
= setup_buffer3(rsplen
);
471 printf("client: failed to alloc buffer space \n");
476 printf("host: %s port: %s reqlen: %d rsplen: %d ntimes: %d alt_addr: %s connorder: %d longlived: %d fasjoin: %d nowaitforjoin: %d\n",
477 host_arg
, port_arg
, reqlen
, rsplen
, ntimes
, alt_addr_arg
, connordrtest
, longlived
, fastjoin
, nowaitforjoin
);
479 sockfd
= socket(AF_MULTIPATH
, SOCK_STREAM
, 0);
481 err(EX_OSERR
, "ERROR opening socket");
482 #define SO_MPTCP_FASTJOIN 0x1111
483 opterr
= setsockopt(sockfd
, SOL_SOCKET
, SO_MPTCP_FASTJOIN
, &fastjoin
, sizeof(fastjoin
));
485 warn("setsockopt(SO_MPTCP_FASTJOIN, %d)", fastjoin
);
487 memset(&ahints
, 0, sizeof(struct addrinfo
));
488 ahints
.ai_family
= AF_INET
;
489 ahints
.ai_socktype
= SOCK_STREAM
;
490 ahints
.ai_protocol
= IPPROTO_TCP
;
492 retval
= getaddrinfo(host_arg
, port_arg
, &ahints
, &ares
);
494 printf("getaddrinfo(%s, %s) failed %d\n", host_arg
, port_arg
, retval
);
496 bytes_to_rdwr
= reqlen
;
499 cid1
= cid2
= CONNID_ANY
;
504 char str
[2 * INET6_ADDRSTRLEN
];
508 sprint_sockaddr(str
, sizeof(str
), ares
->ai_addr
);
509 printf("connectx(%s, %d, %d)\n", str
, ifscope
, cid1
);
512 error
= connectx(sockfd
, NULL
, 0, ares
->ai_addr
,
513 ares
->ai_addrlen
, ifscope
, ASSOCID_ANY
, &cid1
);
514 if ((error
!= 0) && (errno
!= EPROTO
)) {
515 err(EX_OSERR
, "ERROR connecting");
516 } else if ((error
!= 0) && (errno
== EPROTO
)) {
517 ps
= peeloff(sockfd
, ASSOCID_ANY
);
525 printf("%s: peeled off\n", __func__
);
533 if ((iter
== 0) && (peeled_off
== 0)) {
534 /* Add alternate path if available */
536 if (alt_addr_arg
&& alt_addr_arg
[0] != 0) {
537 retval
= getaddrinfo(alt_addr_arg
, alt_port_arg
, &ahints
, &altres
);
540 printf("client: alternate address resolution failed. \n");
542 printf("client: connecting to alternate address (ifscope %d)\n", ifscope
);
545 char str
[2 * INET6_ADDRSTRLEN
];
549 sprint_sockaddr(str
, sizeof(str
), altres
->ai_addr
);
550 printf("connectx(%s, %d, %d)\n", str
, ifscope
, cid1
);
553 error
= connectx(sockfd
, altres
->ai_addr
, altres
->ai_addrlen
,
554 ares
->ai_addr
, ares
->ai_addrlen
,
555 ifscope
, ASSOCID_ANY
, &cid2
);
557 err(EX_OSERR
, "ERROR setting up alternate path");
564 if ((iter
== 10) && (connordrtest
== 1)) {
567 socorder
.sco_cid
= cid2
;
568 socorder
.sco_rank
= 1;
569 retval
= ioctl(sockfd
, SIOCSCONNORDER
, &socorder
);
571 warn("Error in changing priority");
573 bzero(&socorder
, sizeof(socorder
));
574 socorder
.sco_cid
= cid2
;
575 retval
= ioctl(sockfd
, SIOCGCONNORDER
, &socorder
);
576 printf("cid %d rank %d", socorder
.sco_cid
, socorder
.sco_rank
);
578 socorder
.sco_cid
= cid1
;
579 socorder
.sco_rank
= 0;
580 retval
= ioctl(sockfd
, SIOCSCONNORDER
, &socorder
);
582 warn("Error in changing priority");
584 bzero(&socorder
, sizeof(socorder
));
585 socorder
.sco_cid
= cid1
;
586 retval
= ioctl(sockfd
, SIOCGCONNORDER
, &socorder
);
587 printf("cid %d rank %d \n", socorder
.sco_cid
, socorder
.sco_rank
);
590 if (which_buf
== 0) {
598 while (bytes_to_rdwr
) {
601 printf("writing %d bytes\n", bytes_to_rdwr
);
603 n
= write(sockfd
, buffer
, bytes_to_rdwr
);
605 err(EX_OSERR
, "ERROR writing to socket");
607 if (n
<= bytes_to_rdwr
)
610 errx(EX_DATAERR
, "ERROR extra data write %zd %d\n", n
, bytes_to_rdwr
);
613 bytes_to_rdwr
= rsplen
;
614 while (bytes_to_rdwr
) {
617 printf("reading %d bytes\n", rsplen
);
619 n
= read(sockfd
, buffer3
, rsplen
);
622 err(EX_OSERR
, "ERROR reading from socket");
624 if (n
<= bytes_to_rdwr
)
627 errx(EX_DATAERR
, "ERROR extra bytes read n:%zd expected:%d\n", n
, bytes_to_rdwr
);
630 bytes_to_rdwr
= reqlen
;
635 printf("client: Req size %d Rsp size %d Read/Write %d times \n", reqlen
, rsplen
, iter
);
639 if ((!longlived
) || (peeled_off
== 1)) {
642 printf("close(%d)\n", sockfd
);
646 printf("Longlived countdown # %d. \n", longlived
);
649 printf("disconnectx(%d, %d)\n", sockfd
, cid1
);
651 disconnectx(sockfd
, ASSOCID_ANY
, cid1
);
652 if (cid2
!= CONNID_ANY
) {
655 printf("disconnectx(%d, %d)\n", sockfd
, cid2
);
657 disconnectx(sockfd
, ASSOCID_ANY
, cid2
);
659 if (!nowaitforjoin
) {
662 printf("sleep(10)\n");
668 ntimes
= atoi(ntimes_arg
);
670 /* If fastjoin must be tested, write some data before doing the next connectx() */
671 bytes_to_rdwr
= reqlen
/ 2;
674 printf("fastjoin writing %d bytes\n", bytes_to_rdwr
);
676 n
= write(sockfd
, buffer
, bytes_to_rdwr
);
678 warnx("Fastjoin: Error writing to socket. \n");
680 bytes_to_rdwr
= reqlen
- (int)n
;
681 printf("FastJoin: Wrote %zd bytes, remaining %d of %d \n", n
, bytes_to_rdwr
, reqlen
);
689 freeaddrinfo(altres
);
694 "\020\1CONNECTING\2CONNECTED\3DISCONNECTING\4DISCONNECTED\5BOUND_IF"\
695 "\6BOUND_IP\7BOUND_PORT\10PREFERRED\11MP_CAPABLE\12MP_READY" \
699 * Print a value a la the %b format of the kernel's printf
702 printb(const char *s
, unsigned v
, const char *bits
)
707 if (bits
&& *bits
== 8)
708 printf("%s=%o", s
, v
);
710 printf("%s=%x", s
, v
);
714 while ((i
= *bits
++) != '\0') {
715 if (v
& (1 << (i
-1))) {
719 for (; (c
= *bits
) > 32; bits
++)
722 for (; *bits
> 32; bits
++)
731 showconninfo(int s
, connid_t cid
)
733 char buf
[INET6_ADDRSTRLEN
];
734 conninfo_t
*cfo
= NULL
;
737 err
= copyconninfo(s
, cid
, &cfo
);
739 printf("getconninfo failed for cid %d\n", cid
);
743 printf("%6d:\t", cid
);
744 printb("flags", cfo
->ci_flags
, CIF_BITS
);
746 //printf("\toutif %s\n", if_indextoname(cfo->ci_ifindex, buf));
748 if (cfo
->ci_src
!= NULL
) {
749 printf("\tsrc %s port %d\n", inet_ntop(cfo
->ci_src
->sa_family
,
750 (cfo
->ci_src
->sa_family
== AF_INET
) ?
751 (void *)&((struct sockaddr_in
*)cfo
->ci_src
)->
753 (void *)&((struct sockaddr_in6
*)cfo
->ci_src
)->sin6_addr
,
755 (cfo
->ci_src
->sa_family
== AF_INET
) ?
756 ntohs(((struct sockaddr_in
*)cfo
->ci_src
)->sin_port
) :
757 ntohs(((struct sockaddr_in6
*)cfo
->ci_src
)->sin6_port
));
759 if (cfo
->ci_dst
!= NULL
) {
760 printf("\tdst %s port %d\n", inet_ntop(cfo
->ci_dst
->sa_family
,
761 (cfo
->ci_dst
->sa_family
== AF_INET
) ?
762 (void *)&((struct sockaddr_in
*)cfo
->ci_dst
)->
764 (void *)&((struct sockaddr_in6
*)cfo
->ci_dst
)->sin6_addr
,
766 (cfo
->ci_dst
->sa_family
== AF_INET
) ?
767 ntohs(((struct sockaddr_in
*)cfo
->ci_dst
)->sin_port
) :
768 ntohs(((struct sockaddr_in6
*)cfo
->ci_dst
)->sin6_port
));
770 if (cfo
->ci_aux_data
!= NULL
) {
771 switch (cfo
->ci_aux_type
) {
773 printf("\tTCP aux info available\n");
776 printf("\tUnknown aux type %d\n", cfo
->ci_aux_type
);
791 uint32_t aid_cnt
, cid_cnt
;
792 associd_t
*aid
= NULL
;
793 connid_t
*cid
= NULL
;
796 error
= copyassocids(s
, &aid
, &aid_cnt
);
798 printf("copyassocids failed\n");
801 printf("found %d associations", aid_cnt
);
803 printf(" with IDs:");
804 for (i
= 0; i
< aid_cnt
; i
++)
805 printf(" %d\n", aid
[i
]);
810 /* just do an association for now */
811 error
= copyconnids(s
, ASSOCID_ANY
, &cid
, &cid_cnt
);
813 warn("getconnids failed\n");
816 printf("found %d connections", cid_cnt
);
819 for (i
= 0; i
< cid_cnt
; i
++) {
820 if (showconninfo(s
, cid
[i
]) != 0)