/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* sock_test.c. Implementing a CLI for inetboot testing.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include "socket_impl.h"
#include "socket_inet.h"
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <netinet/in_systm.h>
#include <sys/promif.h>
#include <sys/salib.h>
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include "tcp_inet.h"
#include "ipv4.h"
#include <netinet/tcp.h>
static int atoi(const char *);
static int st_accept(void);
static int st_bind(void);
static int st_connect(void);
static int st_echo(void);
static int st_getsockname(void);
static int st_getsockopt(void);
static int st_get_addr_and_port(in_addr_t *, unsigned short *);
static int st_get_buf_and_cnt(char **, int *);
static int st_listen(void);
static int st_match_option(char *, int *, int *);
static int st_send(void);
static int st_sendto(void);
static int st_recv(void);
static int st_recvfrom(void);
static int st_set_addr(void);
static int st_set_netmask(void);
static int st_set_router(void);
static int st_setsockopt(void);
static int st_socket(void);
static int st_sock_close(void);
static int st_tcp_tw_report(void);
static int st_toggle_promiscuous(void);
static int st_use_obp(void);
/* Wrapper for socket calls. */
static int st_local_accept(int, struct sockaddr *, socklen_t *);
static int st_local_bind(int, const struct sockaddr *, socklen_t);
static int st_local_connect(int, const struct sockaddr *, socklen_t);
static int st_local_getsockname(int, struct sockaddr *, socklen_t *);
static int st_local_getsockopt(int, int, int, void *, socklen_t *);
static int st_local_listen(int, int);
static int st_local_recv(int, void *, size_t, int);
static int st_local_recvfrom(int, void *, size_t, int, struct sockaddr *,
socklen_t *);
static int st_local_send(int, const void *, size_t, int);
static int st_local_sendto(int, const void *, size_t, int,
const struct sockaddr *, socklen_t);
static int st_local_setsockopt(int, int, int, const void *, socklen_t);
static int st_local_socket(int, int, int);
static int st_local_socket_close(int);
struct sock_test_cmd_s {
char *st_cmd;
int (*st_fn)(void);
};
static struct sock_test_cmd_s st_cmds[] = {
{ "set_addr", st_set_addr},
{ "set_netmask", st_set_netmask},
{ "set_router", st_set_router},
{ "socket", st_socket },
{ "bind", st_bind },
{ "accept", st_accept },
{ "connect", st_connect },
{ "listen", st_listen },
{ "send", st_send },
{ "sendto", st_sendto },
{ "recv", st_recv },
{ "recvfrom", st_recvfrom },
{ "setsockopt", st_setsockopt },
{ "getsockopt", st_getsockopt },
{ "getsockname", st_getsockname },
{ "close", st_sock_close },
{ "echo", st_echo },
{ "toggle_promiscous", st_toggle_promiscuous},
{ "use_obp", st_use_obp},
{ "tcp_tw_report", st_tcp_tw_report},
{ NULL, NULL }
};
struct so_option_string_s {
char *so_name;
int so_opt;
int so_opt_level;
} so_option_array[] = {
{ "rcvtimeo", SO_RCVTIMEO, SOL_SOCKET },
{ "dontroute", SO_DONTROUTE, SOL_SOCKET },
{ "reuseaddr", SO_REUSEADDR, SOL_SOCKET },
{ "rcvbuf", SO_RCVBUF, SOL_SOCKET },
{ "sndbuf", SO_SNDBUF, SOL_SOCKET },
{ NULL, 0 }
};
#define NO_OPENED_SOCKET -1
/* Right now, we only allow one socket at one time. */
static int g_sock_fd = NO_OPENED_SOCKET;
static int save_g_sock_fd = NO_OPENED_SOCKET;
/* Boolean to decide if OBP network routines should be used. */
static boolean_t use_obp = B_FALSE;
/*
* The following routines are wrappers for the real socket routines. The
* boolean use_obp is used to decide whether the real socket routines is
* called or the "equivalent" OBP provided routines should be called.
*/
static int
st_local_socket(int domain, int type, int protocol)
{
if (!use_obp) {
return (socket(domain, type, protocol));
} else {
return (0);
}
}
static int
st_local_socket_close(int sd)
{
if (!use_obp) {
return (socket_close(sd));
} else {
return (0);
}
}
static int
st_local_accept(int sd, struct sockaddr *addr, socklen_t *addr_len)
{
if (!use_obp) {
return (accept(sd, addr, addr_len));
} else {
return (0);
}
}
static int
st_local_bind(int sd, const struct sockaddr *name, socklen_t namelen)
{
if (!use_obp) {
return (bind(sd, name, namelen));
} else {
return (0);
}
}
static int
st_local_connect(int sd, const struct sockaddr *addr, socklen_t addr_len)
{
if (!use_obp) {
return (connect(sd, addr, addr_len));
} else {
return (0);
}
}
static int
st_local_listen(int sd, int backlog)
{
if (!use_obp) {
return (listen(sd, backlog));
} else {
return (0);
}
}
static int
st_local_send(int sd, const void *msg, size_t len, int flags)
{
if (!use_obp) {
return (send(sd, msg, len, flags));
} else {
return (0);
}
}
static int
st_local_sendto(int sd, const void *msg, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen)
{
if (!use_obp) {
return (sendto(sd, msg, len, flags, to, tolen));
} else {
return (0);
}
}
static int
st_local_recv(int sd, void *buf, size_t len, int flags)
{
if (!use_obp) {
return (recv(sd, buf, len, flags));
} else {
return (0);
}
}
static int
st_local_recvfrom(int sd, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
if (!use_obp) {
return (recvfrom(sd, buf, len, flags, from, fromlen));
} else {
return (0);
}
}
static int
st_local_getsockname(int sd, struct sockaddr *name, socklen_t *namelen)
{
if (!use_obp) {
return (getsockname(sd, name, namelen));
} else {
return (0);
}
}
static int
st_local_getsockopt(int sd, int level, int option, void *optval,
socklen_t *optlen)
{
if (!use_obp) {
return (getsockopt(sd, level, option, optval, optlen));
} else {
return (0);
}
}
static int
st_local_setsockopt(int sd, int level, int option, const void *optval,
socklen_t optlen)
{
if (!use_obp) {
return (setsockopt(sd, level, option, optval, optlen));
} else {
return (0);
}
}
static int
atoi(const char *p)
{
int n;
int c = *p++, neg = 0;
while (isspace(c)) {
c = *p++;
}
if (!isdigit(c)) {
switch (c) {
case '-':
neg++;
/* FALLTHROUGH */
case '+':
c = *p++;
}
}
for (n = 0; isdigit(c); c = *p++) {
n *= 10; /* two steps to avoid unnecessary overflow */
n += '0' - c; /* accum neg to avoid surprises at MAX */
}
return (neg ? n : -n);
}
int
st_interpret(char *buf)
{
char *cmd;
int i;
if ((cmd = strtok(buf, " ")) == NULL)
return (-1);
for (i = 0; st_cmds[i].st_cmd != NULL; i++) {
if (strcmp(cmd, st_cmds[i].st_cmd) == 0) {
return (st_cmds[i].st_fn());
}
}
printf("! Unknown command: %s\n", cmd);
return (-1);
}
static int
st_socket(void)
{
char *type;
if ((type = strtok(NULL, " ")) == NULL) {
printf("! usage: socket type\n");
return (-1);
}
if (g_sock_fd != NO_OPENED_SOCKET) {
printf("! Cannot open more than 1 socket\n");
return (-1);
}
if (strcmp(type, "stream") == 0) {
if ((g_sock_fd = st_local_socket(AF_INET, SOCK_STREAM,
0)) < 0) {
printf("! Error in opening TCP socket: %d\n", errno);
return (-1);
} else {
printf("@ TCP socket opened\n");
}
} else if (strcmp(type, "dgram") == 0) {
if ((g_sock_fd = st_local_socket(AF_INET, SOCK_DGRAM,
0)) < 0) {
printf("! Error in opening UDP socket: %d\n", errno);
return (-1);
} else {
printf("@ UDP socket opened\n");
}
} else if (strcmp(type, "raw") == 0) {
if ((g_sock_fd = st_local_socket(AF_INET, SOCK_RAW, 0)) < 0) {
printf("! Error in opening RAW socket: %d\n", errno);
return (-1);
} else {
printf("@ RAW socket opened\n");
}
} else {
printf("! Unknown socket type: %s\n", type);
return (-1);
}
return (0);
}
static int
st_set_addr(void)
{
char *tmp;
struct in_addr addr;
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No address given\n");
return (-1);
}
if ((addr.s_addr = inet_addr(tmp)) == (uint32_t)-1) {
printf("! Malformed address\n");
return (-1);
}
ipv4_setipaddr(&addr);
printf("@ IP address %s set\n", inet_ntoa(addr));
return (0);
}
static int
st_set_netmask(void)
{
char *tmp;
struct in_addr addr;
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No netmask given\n");
return (-1);
}
if ((addr.s_addr = inet_addr(tmp)) == (uint32_t)-1) {
printf("! Malformed netmask\n");
return (-1);
}
ipv4_setnetmask(&addr);
printf("@ Netmask %s set\n", inet_ntoa(addr));
return (0);
}
static int
st_set_router(void)
{
char *tmp;
struct in_addr addr;
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No router address given\n");
return (-1);
}
if ((addr.s_addr = inet_addr(tmp)) == (uint32_t)-1) {
printf("! Malformed router address\n");
return (-1);
}
ipv4_setdefaultrouter(&addr);
if (ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &addr) < 0) {
printf("! Cannot add default route\n");
} else {
printf("@ Default router %s set\n", inet_ntoa(addr));
}
return (0);
}
static int
st_get_addr_and_port(in_addr_t *addr, unsigned short *port)
{
char *tmp;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
tmp = strtok(NULL, "/");
if (tmp == NULL) {
printf("! No address given\n");
return (-1);
}
if ((*addr = inet_addr(tmp)) == (uint32_t)-1) {
printf("! Malformed address\n");
return (-1);
}
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No port given\n");
return (-1);
}
*port = htons(atoi(tmp));
return (0);
}
static int
st_bind(void)
{
struct sockaddr_in local_addr;
if (st_get_addr_and_port(&(local_addr.sin_addr.s_addr),
&(local_addr.sin_port)) < 0) {
return (-1);
}
local_addr.sin_family = AF_INET;
if (st_local_bind(g_sock_fd, (struct sockaddr *)&local_addr,
sizeof (local_addr)) < 0) {
printf("! Bind failed: %d\n", errno);
return (-1);
}
printf("@ Socket bound to %s/%d\n", inet_ntoa(local_addr.sin_addr),
ntohs(local_addr.sin_port));
return (0);
}
static int
st_listen(void)
{
char *tmp;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No backlog given\n");
return (-1);
}
if (st_local_listen(g_sock_fd, atoi(tmp)) < 0) {
printf("! Listen failed: %d\n", errno);
return (-1);
}
printf("@ Listen succeeded\n");
return (0);
}
static int
st_accept(void)
{
struct sockaddr_in addr;
socklen_t addr_len;
int sd;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
addr_len = sizeof (struct sockaddr_in);
if ((sd = st_local_accept(g_sock_fd, (struct sockaddr *)&addr,
&addr_len)) < 0) {
printf("! Accept failed: %d\n", errno);
return (-1);
}
printf("@ Accept succeeded from %s:%d. Socket descriptor saved\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
save_g_sock_fd = g_sock_fd;
g_sock_fd = sd;
return (0);
}
static int
st_connect(void)
{
struct sockaddr_in peer_addr;
if (st_get_addr_and_port(&(peer_addr.sin_addr.s_addr),
&(peer_addr.sin_port)) < 0) {
return (-1);
}
peer_addr.sin_family = AF_INET;
if (st_local_connect(g_sock_fd, (struct sockaddr *)&peer_addr,
sizeof (peer_addr)) < 0) {
printf("! Connect failed: %d\n", errno);
return (-1);
}
printf("@ Socket connected to %s/%d\n", inet_ntoa(peer_addr.sin_addr),
ntohs(peer_addr.sin_port));
return (0);
}
static int
st_get_buf_and_cnt(char **buf, int *send_cnt)
{
char *cnt;
if ((*buf = strtok(NULL, " ")) == NULL) {
printf("! No send buffer\n");
return (-1);
}
if ((cnt = strtok(NULL, " ")) == NULL) {
printf("! Missing send length\n");
return (-1);
}
if ((*send_cnt = atoi(cnt)) < 0) {
printf("! Invalid send count\n");
return (-1);
}
return (0);
}
static int
st_send(void)
{
char *buf;
int send_cnt;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if (st_get_buf_and_cnt(&buf, &send_cnt) < 0)
return (-1);
if ((send_cnt = st_local_send(g_sock_fd, buf, send_cnt, 0)) < 0) {
printf("! Send failed: %d\n", errno);
return (-1);
}
printf("@ Send %d bytes\n", send_cnt);
return (0);
}
static int
st_sendto(void)
{
struct sockaddr_in peer_addr;
char *buf;
int send_cnt;
if (st_get_addr_and_port(&(peer_addr.sin_addr.s_addr),
&(peer_addr.sin_port)) < 0) {
return (-1);
}
peer_addr.sin_family = AF_INET;
if (st_get_buf_and_cnt(&buf, &send_cnt) < 0)
return (-1);
if ((send_cnt = st_local_sendto(g_sock_fd, buf, send_cnt, 0,
(struct sockaddr *)&peer_addr, sizeof (peer_addr))) < 0) {
printf("! Sendto failed: %d\n", errno);
return (-1);
}
printf("@ Send %d bytes\n", send_cnt);
return (0);
}
static int
st_recv(void)
{
char *tmp;
char *buf;
int buf_len, ret;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No buffer len given\n");
return (-1);
}
buf_len = atoi(tmp);
if ((buf = bkmem_zalloc(buf_len)) == NULL) {
printf("! Cannot allocate buffer: %d\n", errno);
return (-1);
}
if ((ret = st_local_recv(g_sock_fd, buf, buf_len, 0)) <= 0) {
if (ret == 0) {
printf("@ EOF received: %d\n", errno);
return (0);
}
printf("! Cannot recv: %d\n", errno);
return (-1);
}
printf("@ Bytes received: %d\n", ret);
hexdump(buf, ret);
bkmem_free(buf, buf_len);
return (0);
}
static int
st_recvfrom(void)
{
char *tmp;
char *buf;
int buf_len, ret;
struct sockaddr_in from;
socklen_t fromlen;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No buffer len given\n");
return (-1);
}
buf_len = atoi(tmp);
if ((buf = bkmem_zalloc(buf_len)) == NULL) {
printf("! Cannot allocate buffer: %d\n", errno);
return (-1);
}
fromlen = sizeof (from);
if ((ret = st_local_recvfrom(g_sock_fd, buf, buf_len, 0,
(struct sockaddr *)&from, &fromlen)) <= 0) {
if (ret == 0) {
printf("@ EOF received: %d\n", errno);
return (0);
}
printf("! Cannot recv: %d\n", errno);
return (-1);
}
printf("@ Bytes received from %s/%d: %d\n",
inet_ntoa(from.sin_addr), ntohs(from.sin_port), ret);
hexdump(buf, ret);
bkmem_free(buf, buf_len);
return (0);
}
/*
* To act as an echo server. Note that it assumes the address and
* netmask have been set.
*/
static int
st_echo(void)
{
char *tmp;
int listen_fd, newfd;
int echo_port;
struct sockaddr_in addr;
socklen_t addr_size;
int backlog = 20;
char *buf;
int buf_len, ret, snd_cnt;
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No echo port given\n");
return (-1);
}
echo_port = atoi(tmp);
tmp = strtok(NULL, " ");
if (tmp == NULL) {
printf("! No buffer size given\n");
return (-1);
}
buf_len = atoi(tmp);
/* Create local socket for echo server */
if ((listen_fd = st_local_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("! Error in opening TCP socket: %d\n", errno);
return (-1);
} else {
printf("@ Local TCP socket opened\n");
}
/* Bind local socket */
addr.sin_family = AF_INET;
addr.sin_port = htons(echo_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (st_local_bind(listen_fd, (struct sockaddr *)&addr,
sizeof (addr)) < 0) {
printf("! Bind failed: %d\n", errno);
return (-1);
}
if (st_local_listen(listen_fd, backlog) < 0) {
printf("! Listen failed: %d\n", errno);
return (-1);
}
addr_size = sizeof (addr);
if ((newfd = st_local_accept(listen_fd, (struct sockaddr *)&addr,
&addr_size)) < 0) {
printf("! Accept failed: %d\n", errno);
(void) st_local_socket_close(listen_fd);
return (-1);
}
printf("@ Accepted connection: %s/%d\n", inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
(void) st_local_socket_close(listen_fd);
if ((buf = bkmem_zalloc(buf_len)) == NULL) {
printf("! Cannot allocate buffer: %d\n", errno);
(void) st_local_socket_close(newfd);
return (-1);
}
while ((ret = st_local_recv(newfd, buf, buf_len, 0)) > 0) {
printf("@ Bytes received: %d\n", ret);
hexdump(buf, ret);
if ((snd_cnt = st_local_send(newfd, buf, ret, 0)) < ret) {
printf("! Send failed: %d\n", errno);
bkmem_free(buf, buf_len);
return (-1);
}
printf("@ Sent %d bytes\n", snd_cnt);
}
(void) st_local_socket_close(newfd);
if (ret < 0) {
printf("! Cannot recv: %d\n", errno);
bkmem_free(buf, buf_len);
return (-1);
} else {
return (0);
}
}
static int
st_match_option(char *opt_s, int *opt, int *opt_level)
{
int i;
for (i = 0; so_option_array[i].so_name != NULL; i++) {
if (strcmp(so_option_array[i].so_name, opt_s) == 0) {
*opt = so_option_array[i].so_opt;
*opt_level = so_option_array[i].so_opt_level;
return (0);
}
}
printf("! Unknown option\n");
return (-1);
}
static int
st_setsockopt(void)
{
char *tmp;
int opt, opt_level, opt_val;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No option given\n");
return (-1);
}
if (st_match_option(tmp, &opt, &opt_level) < 0) {
return (-1);
}
/* We only support integer option for the moment. */
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No option value given\n");
return (-1);
}
opt_val = atoi(tmp);
if (st_local_setsockopt(g_sock_fd, opt_level, opt, &opt_val,
sizeof (int)) < 0) {
printf("! Cannot set option: %d\n", errno);
return (-1);
}
printf("@ Option set successfully\n");
return (0);
}
static int
st_getsockname(void)
{
struct sockaddr_in addr;
socklen_t len;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
len = sizeof (addr);
if (st_local_getsockname(g_sock_fd, (struct sockaddr *)&addr,
&len) < 0) {
printf("! getsockname failed: %d\n", errno);
return (-1);
}
printf("@ Local socket name: %s/%d\n", inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
return (0);
}
static int
st_getsockopt(void)
{
char *tmp;
int opt, opt_level, opt_val;
socklen_t opt_len;
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if ((tmp = strtok(NULL, " ")) == NULL) {
printf("! No option given\n");
return (-1);
}
if (st_match_option(tmp, &opt, &opt_level) < 0) {
return (-1);
}
opt_len = sizeof (opt_val);
if (st_local_getsockopt(g_sock_fd, opt_level, opt, &opt_val,
&opt_len) < 0) {
printf("! Cannot get option: %d\n", errno);
return (-1);
}
printf("@ Option value is %d\n", opt_val);
return (-1);
}
static int
st_sock_close(void)
{
if (g_sock_fd == NO_OPENED_SOCKET) {
printf("! No socket opened\n");
return (-1);
}
if (st_local_socket_close(g_sock_fd) < 0) {
printf("! Error in closing socket: %d\n", errno);
return (-1);
}
printf("@ Socket closed");
if (save_g_sock_fd != NO_OPENED_SOCKET) {
g_sock_fd = save_g_sock_fd;
save_g_sock_fd = NO_OPENED_SOCKET;
printf(", switching to saved socket descriptor\n");
} else {
g_sock_fd = NO_OPENED_SOCKET;
printf("\n");
}
return (0);
}
static int
st_toggle_promiscuous(void)
{
/* We always start with non-promiscuous mode. */
static boolean_t promiscuous = B_FALSE;
promiscuous = !promiscuous;
(void) ipv4_setpromiscuous(promiscuous);
printf("@ Setting promiscuous to %d\n", promiscuous);
return (0);
}
static int
st_use_obp(void)
{
if ((use_obp = !use_obp) == B_TRUE) {
printf("@ Now using OBP routines\n");
} else {
printf("@ Now using socket routines\n");
}
return (0);
}
static int
st_tcp_tw_report(void)
{
printf("@ TCP Time Wait report\n");
tcp_time_wait_report();
return (0);
}