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
;
320 sae_connid_t cid1
, cid2
;
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
= SAE_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 bzero(&sa
, sizeof(sa
));
513 sa
.sae_dstaddr
= ares
->ai_addr
;
514 sa
.sae_dstaddrlen
= ares
->ai_addrlen
;
515 sa
.sae_srcif
= ifscope
;
517 error
= connectx(sockfd
, &sa
, SAE_ASSOCID_ANY
, 0, NULL
, 0, NULL
, &cid1
);
518 if ((error
!= 0) && (errno
!= EPROTO
)) {
519 err(EX_OSERR
, "ERROR connecting");
520 } else if ((error
!= 0) && (errno
== EPROTO
)) {
521 ps
= peeloff(sockfd
, SAE_ASSOCID_ANY
);
529 printf("%s: peeled off\n", __func__
);
536 if ((iter
== 0) && (peeled_off
== 0)) {
537 /* Add alternate path if available */
539 if (alt_addr_arg
&& alt_addr_arg
[0] != 0) {
540 retval
= getaddrinfo(alt_addr_arg
, alt_port_arg
, &ahints
, &altres
);
543 printf("client: alternate address resolution failed. \n");
545 printf("client: connecting to alternate address (ifscope %d)\n", ifscope
);
548 char str
[2 * INET6_ADDRSTRLEN
];
552 sprint_sockaddr(str
, sizeof(str
), altres
->ai_addr
);
553 printf("connectx(%s, %d, %d)\n", str
, ifscope
, cid1
);
556 bzero(&sa
, sizeof(sa
));
557 sa
.sae_srcif
= ifscope
;
558 sa
.sae_srcaddr
= altres
->ai_addr
;
559 sa
.sae_srcaddrlen
= altres
->ai_addrlen
;
560 sa
.sae_dstaddr
= ares
->ai_addr
;
561 sa
.sae_dstaddrlen
= ares
->ai_addrlen
;
563 error
= connectx(sockfd
, &sa
, SAE_ASSOCID_ANY
, 0, NULL
, 0, NULL
, &cid2
);
565 err(EX_OSERR
, "ERROR setting up alternate path");
572 if ((iter
== 10) && (connordrtest
== 1)) {
575 socorder
.sco_cid
= cid2
;
576 socorder
.sco_rank
= 1;
577 retval
= ioctl(sockfd
, SIOCSCONNORDER
, &socorder
);
579 warn("Error in changing priority");
581 bzero(&socorder
, sizeof(socorder
));
582 socorder
.sco_cid
= cid2
;
583 retval
= ioctl(sockfd
, SIOCGCONNORDER
, &socorder
);
584 printf("cid %d rank %d", socorder
.sco_cid
, socorder
.sco_rank
);
586 socorder
.sco_cid
= cid1
;
587 socorder
.sco_rank
= 0;
588 retval
= ioctl(sockfd
, SIOCSCONNORDER
, &socorder
);
590 warn("Error in changing priority");
592 bzero(&socorder
, sizeof(socorder
));
593 socorder
.sco_cid
= cid1
;
594 retval
= ioctl(sockfd
, SIOCGCONNORDER
, &socorder
);
595 printf("cid %d rank %d \n", socorder
.sco_cid
, socorder
.sco_rank
);
598 if (which_buf
== 0) {
606 while (bytes_to_rdwr
) {
609 printf("writing %d bytes\n", bytes_to_rdwr
);
611 n
= write(sockfd
, buffer
, bytes_to_rdwr
);
613 err(EX_OSERR
, "ERROR writing to socket");
615 if (n
<= bytes_to_rdwr
)
618 errx(EX_DATAERR
, "ERROR extra data write %zd %d\n", n
, bytes_to_rdwr
);
621 bytes_to_rdwr
= rsplen
;
622 while (bytes_to_rdwr
) {
625 printf("reading %d bytes\n", rsplen
);
627 n
= read(sockfd
, buffer3
, rsplen
);
630 err(EX_OSERR
, "ERROR reading from socket");
632 if (n
<= bytes_to_rdwr
)
635 errx(EX_DATAERR
, "ERROR extra bytes read n:%zd expected:%d\n", n
, bytes_to_rdwr
);
638 bytes_to_rdwr
= reqlen
;
643 printf("client: Req size %d Rsp size %d Read/Write %d times \n", reqlen
, rsplen
, iter
);
647 if ((!longlived
) || (peeled_off
== 1)) {
650 printf("close(%d)\n", sockfd
);
654 printf("Longlived countdown # %d. \n", longlived
);
657 printf("disconnectx(%d, %d)\n", sockfd
, cid1
);
659 disconnectx(sockfd
, SAE_ASSOCID_ANY
, cid1
);
660 if (cid2
!= SAE_CONNID_ANY
) {
663 printf("disconnectx(%d, %d)\n", sockfd
, cid2
);
665 disconnectx(sockfd
, SAE_ASSOCID_ANY
, cid2
);
667 if (!nowaitforjoin
) {
670 printf("sleep(10)\n");
676 ntimes
= atoi(ntimes_arg
);
678 /* If fastjoin must be tested, write some data before doing the next connectx() */
679 bytes_to_rdwr
= reqlen
/ 2;
682 printf("fastjoin writing %d bytes\n", bytes_to_rdwr
);
684 n
= write(sockfd
, buffer
, bytes_to_rdwr
);
686 warnx("Fastjoin: Error writing to socket. \n");
688 bytes_to_rdwr
= reqlen
- (int)n
;
689 printf("FastJoin: Wrote %zd bytes, remaining %d of %d \n", n
, bytes_to_rdwr
, reqlen
);
697 freeaddrinfo(altres
);
702 "\020\1CONNECTING\2CONNECTED\3DISCONNECTING\4DISCONNECTED\5BOUND_IF"\
703 "\6BOUND_IP\7BOUND_PORT\10PREFERRED\11MP_CAPABLE\12MP_READY" \
707 * Print a value a la the %b format of the kernel's printf
710 printb(const char *s
, unsigned v
, const char *bits
)
715 if (bits
&& *bits
== 8)
716 printf("%s=%o", s
, v
);
718 printf("%s=%x", s
, v
);
722 while ((i
= *bits
++) != '\0') {
723 if (v
& (1 << (i
-1))) {
727 for (; (c
= *bits
) > 32; bits
++)
730 for (; *bits
> 32; bits
++)
739 showconninfo(int s
, sae_connid_t cid
)
741 char buf
[INET6_ADDRSTRLEN
];
742 conninfo_t
*cfo
= NULL
;
745 err
= copyconninfo(s
, cid
, &cfo
);
747 printf("getconninfo failed for cid %d\n", cid
);
751 printf("%6d:\t", cid
);
752 printb("flags", cfo
->ci_flags
, CIF_BITS
);
754 //printf("\toutif %s\n", if_indextoname(cfo->ci_ifindex, buf));
756 if (cfo
->ci_src
!= NULL
) {
757 printf("\tsrc %s port %d\n", inet_ntop(cfo
->ci_src
->sa_family
,
758 (cfo
->ci_src
->sa_family
== AF_INET
) ?
759 (void *)&((struct sockaddr_in
*)cfo
->ci_src
)->
761 (void *)&((struct sockaddr_in6
*)cfo
->ci_src
)->sin6_addr
,
763 (cfo
->ci_src
->sa_family
== AF_INET
) ?
764 ntohs(((struct sockaddr_in
*)cfo
->ci_src
)->sin_port
) :
765 ntohs(((struct sockaddr_in6
*)cfo
->ci_src
)->sin6_port
));
767 if (cfo
->ci_dst
!= NULL
) {
768 printf("\tdst %s port %d\n", inet_ntop(cfo
->ci_dst
->sa_family
,
769 (cfo
->ci_dst
->sa_family
== AF_INET
) ?
770 (void *)&((struct sockaddr_in
*)cfo
->ci_dst
)->
772 (void *)&((struct sockaddr_in6
*)cfo
->ci_dst
)->sin6_addr
,
774 (cfo
->ci_dst
->sa_family
== AF_INET
) ?
775 ntohs(((struct sockaddr_in
*)cfo
->ci_dst
)->sin_port
) :
776 ntohs(((struct sockaddr_in6
*)cfo
->ci_dst
)->sin6_port
));
778 if (cfo
->ci_aux_data
!= NULL
) {
779 switch (cfo
->ci_aux_type
) {
781 printf("\tTCP aux info available\n");
784 printf("\tUnknown aux type %d\n", cfo
->ci_aux_type
);
799 uint32_t aid_cnt
= 0, cid_cnt
= 0;
800 sae_associd_t
*aid
= NULL
;
801 sae_connid_t
*cid
= NULL
;
804 error
= copyassocids(s
, &aid
, &aid_cnt
);
806 printf("copyassocids failed\n");
809 printf("found %d associations", aid_cnt
);
811 printf(" with IDs:");
812 for (i
= 0; i
< aid_cnt
; i
++)
813 printf(" %d\n", aid
[i
]);
818 /* just do an association for now */
819 error
= copyconnids(s
, SAE_ASSOCID_ANY
, &cid
, &cid_cnt
);
821 warn("getconnids failed\n");
824 printf("found %d connections", cid_cnt
);
827 for (i
= 0; i
< cid_cnt
; i
++) {
828 if (showconninfo(s
, cid
[i
]) != 0)