blob: 64b677045d6e59079668297d7e669192a5722677 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Compile with:
* gcc -static -O2 mambo-socket-proxy.c -o mambo-socket-proxy -pthread
* Run inside the simulator:
* - to forward host ssh connections to sim ssh server
* ./mambo-socket-proxy -h 10022 -s 22
* Then connect to port 10022 on your host
* ssh -p 10022 localhost
* - to allow http proxy access from inside the sim to local http proxy
* ./mambo-socket-proxy -b proxy.mynetwork -h 3128 -s 3128
*
* Copyright (C) 2017 Michael Neuling <mikey@neuling.org>, IBM
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <getopt.h>
#define CALL_TCL 86
#define BOGUS_SOCKET_CONN_PROBE_CODE 224
#define BOGUS_SOCKET_CONN_SEND_CODE 225
#define BOGUS_SOCKET_CONN_RECV_CODE 226
static inline int callthru2(int command, unsigned long arg1, unsigned long arg2)
{
register int c asm("r3") = command;
register unsigned long a1 asm("r4") = arg1;
register unsigned long a2 asm("r5") = arg2;
asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2));
return (c);
}
static inline int callthru3(int command, unsigned long arg1, unsigned long arg2,
unsigned long arg3)
{
register int c asm("r3") = command;
register unsigned long a1 asm("r4") = arg1;
register unsigned long a2 asm("r5") = arg2;
register unsigned long a3 asm("r6") = arg3;
asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
"r"(a3));
return (c);
}
static inline int callthru4(int command, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4)
{
register int c asm("r3") = command;
register unsigned long a1 asm("r4") = arg1;
register unsigned long a2 asm("r5") = arg2;
register unsigned long a3 asm("r6") = arg3;
register unsigned long a4 asm("r7") = arg4;
asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
"r"(a3), "r"(a4));
return (c);
}
static inline int callthru5(int command, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
register int c asm("r3") = command;
register unsigned long a1 asm("r4") = arg1;
register unsigned long a2 asm("r5") = arg2;
register unsigned long a3 asm("r6") = arg3;
register unsigned long a4 asm("r7") = arg4;
register unsigned long a5 asm("r8") = arg5;
asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
"r"(a3), "r"(a4), "r"(a5));
return (c);
}
unsigned long callthru_tcl(const char *str, int strlen)
{
return callthru2(CALL_TCL, (unsigned long)str,
(unsigned long)strlen);
}
unsigned long bogus_socket_conn_probe(int dev, void *addr, int conn)
{
return callthru3(BOGUS_SOCKET_CONN_PROBE_CODE,
(unsigned long)dev,
(unsigned long)addr,
(unsigned long)conn);
}
unsigned long bogus_socket_conn_recv(int dev, void *addr, int maxlen, int conn)
{
return callthru4(BOGUS_SOCKET_CONN_RECV_CODE,
(unsigned long)dev,
(unsigned long)addr,
(unsigned long)maxlen,
(unsigned long)conn);
}
unsigned long bogus_socket_conn_send(int dev, void *addr, int maxlen, int conn)
{
return callthru5(BOGUS_SOCKET_CONN_SEND_CODE,
(unsigned long)dev,
(unsigned long)addr,
(unsigned long)maxlen,
0,
(unsigned long)conn);
}
#define BUF_MAX 1024
struct sock_info {
char *host;
int sock;
int dev;
int open;
int conn;
};
void *recv_thread(void *ptr)
{
struct timeval timeout;
struct sock_info *si = ptr;
char buf[BUF_MAX];
int len;
fd_set set;
/* 1 sec */
while(1) {
FD_ZERO(&set);
FD_SET(si->sock, &set);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
/* Set timeout to 1 second */
len = select(si->sock+1, &set, NULL, NULL, &timeout);
if (len <= 0) /* timeout */
len = -1;
else /* Receive from mambo tcp server */
len = recv(si->sock, &buf, BUF_MAX, 0);
if (len == 0) {
si->open = 0;
return NULL; /* closed */
}
if (len != -1) {
bogus_socket_conn_send(si->dev, &buf, len, si->conn);
}
if (!si->open)
return NULL;
}
}
#define POLL_MAX_NS 10000000
void *send_thread(void *ptr)
{
struct sock_info *si = ptr;
char buf[BUF_MAX];
int len;
struct timespec t;
int fault_retry = 16;
t.tv_sec = 0;
t.tv_nsec = POLL_MAX_NS;
while(1) {
/* Send to mambo tcp server */
len = bogus_socket_conn_recv(si->dev, &buf, BUF_MAX, si->conn);
if (len == -3 && fault_retry--) {
/* Page fault. Touch the buf and try again */
memset(buf, 0, BUF_MAX);
continue;
}
fault_retry = 16;
if (len == -1) /* EAGAIN */
nanosleep(&t , NULL);
else if (len > 0)
send(si->sock, &buf, len, 0);
else {
si->open = 0;
return NULL; /* closed */
}
if (!si->open)
return NULL;
}
}
void *connect_sockets(void *ptr)
{
struct sock_info *si = ptr;
pthread_t recv, send;
unsigned long rc = 0;
if (pthread_create(&recv, NULL, recv_thread, si) ||
pthread_create(&send, NULL, send_thread, si)) {
rc = -1;
goto out;
}
if (pthread_join(recv, NULL) || pthread_join(send, NULL)) {
rc = -1;
goto out;
}
out:
/* FIXME: Do shutdown better */
shutdown(si->sock, SHUT_WR);
si->open = 0;
free(si);
return (void *)rc;
}
void print_usage() {
printf("Usage:\n");
printf(" mambo-socket-proxy [-b <host>] -h <host port> -s <sim port>\n");
printf("\n");
printf(" -h <host port> : Port on the host to forward\n");
printf(" -s <host port> : Port in the sim to forward\n");
printf(" -b <host machine> : Connect sim port to host network\n");
printf("\n");
}
int main (int argc, char *argv[])
{
char cmd[128];
struct sockaddr_in ser, client;
pthread_t sockets_thread;
struct sock_info *si;
int sock, conn, rc = 0, option = 0, one_shot = 0, c, sock_desc = 0;
char *host = NULL;
int host_port = -1, sim_port = -1;
int dev = 1; /* backwards starts at 1 so forwards can use 0 */
while ((option = getopt(argc, argv,"rb:h:s:")) != -1) {
switch (option) {
case 'b' :
host = optarg;
break;
case 'h' :
host_port = atoi(optarg);
break;
case 's' :
sim_port = atoi(optarg);
break;
default:
print_usage();
exit(1);
}
}
if (host_port == -1 || sim_port ==-1) {
print_usage();
exit(EXIT_FAILURE);
}
/*
* A host/backwards connection will use dev=0 and conn >= 0.
* The forwards connection will use dev >= 1 and conn=0
*/
if (host) {
sock_desc = socket(PF_INET, SOCK_STREAM, 0);
ser.sin_family = AF_INET;
ser.sin_addr.s_addr = INADDR_ANY;
ser.sin_port = htons(sim_port);
if (bind(sock_desc, (struct sockaddr *) &ser, sizeof(ser)) < 0) {
perror("Can't connect to sim port");
rc = -1;
goto out;
}
listen(sock_desc, 3);
} else {
/*
* Cleaning up old bogus socket.
*/
sprintf(cmd, "mysim bogus socket cleanup");
callthru_tcl(cmd, strlen(cmd));
sleep(1); /* Cleanup takes a while */
sprintf(cmd, "mysim bogus socket init 0 server "
"127.0.0.1 %i poll 0 nonblock", host_port);
callthru_tcl(cmd, strlen(cmd));
}
while (1) {
if (host) {
sock = accept(sock_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (sock < 0) {
perror("accept failed");
rc = -1;
goto out;
}
sprintf(cmd, "mysim bogus socket init %i client %s %i poll 0",
dev, host, host_port);
callthru_tcl(cmd, strlen(cmd));
while (bogus_socket_conn_probe(dev, NULL, 0) == -1)
sleep(1);
} else {
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 10000000;
do {
conn = bogus_socket_conn_probe(0, NULL, -1);
nanosleep(&t , NULL);
} while (conn == -1);
sock = socket(PF_INET, SOCK_STREAM, 0);
ser.sin_family = AF_INET;
ser.sin_port = htons(sim_port);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(ser.sin_zero, '\0', sizeof ser.sin_zero);
if (connect(sock, (struct sockaddr *) &ser, sizeof(ser))) {
perror("Can't connect to sim port");
rc = -1;
goto out;
}
}
si = malloc(sizeof(struct sock_info));
si->host = host;
si->sock = sock;
si->dev = host?dev:0;
si->open = 1;
si->conn = host?0:conn;
if (pthread_create(&sockets_thread, NULL, connect_sockets, si)) {
rc = -1;
goto out;
}
if (one_shot)
break;
++dev; // FIXME: do a real allocator
}
out:
exit(rc);
}