blob: c4ca5b94b54f8f686b1b996fd1a0b3f266c14bf5 [file] [log] [blame]
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include "aos/common/logging/logging_impl.h"
#include "aos/common/util/inet_addr.h"
#include "aos/linux_code/configuration.h"
#include "aos/common/network_port.h"
#include "aos/linux_code/init.h"
#include "aos/common/byteorder.h"
namespace aos {
namespace {
struct FDsToCopy {
const int input;
const int output;
const struct sockaddr_in *const interface_address;
const struct sockaddr_in *const source_address;
};
void *FDCopyThread(void *to_copy_in) {
FDsToCopy *to_copy = static_cast<FDsToCopy *>(to_copy_in);
char buffer[32768];
ssize_t position = 0;
while (true) {
CHECK_GE(position, 0);
CHECK_LE(position, static_cast<ssize_t>(sizeof(buffer)));
if (position != sizeof(buffer)) {
ssize_t read_bytes;
bool good_data = true;
if (to_copy->interface_address != nullptr ||
to_copy->source_address != nullptr) {
char control_buffer[0x100];
struct msghdr header;
memset(static_cast<void *>(&header), 0, sizeof(header));
header.msg_control = control_buffer;
header.msg_controllen = sizeof(control_buffer);
struct iovec iovecs[1];
iovecs[0].iov_base = buffer + position;
iovecs[0].iov_len = sizeof(buffer) - position;
header.msg_iov = iovecs;
header.msg_iovlen = sizeof(iovecs) / sizeof(iovecs[0]);
struct sockaddr_in sender_address;
header.msg_name = &sender_address;
header.msg_namelen = sizeof(sender_address);
read_bytes = recvmsg(to_copy->input, &header, 0);
if (read_bytes != -1) {
if (to_copy->interface_address != nullptr) {
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&header);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&header, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO) {
unsigned char *data = CMSG_DATA(cmsg);
struct in_pktinfo *pktinfo;
memcpy(&pktinfo, &data, sizeof(void *));
if (pktinfo->ipi_spec_dst.s_addr !=
to_copy->interface_address->sin_addr.s_addr) {
good_data = false;
}
}
}
}
if (to_copy->source_address != nullptr) {
CHECK_GE(header.msg_namelen, sizeof(struct sockaddr_in));
if (to_copy->source_address->sin_port != hton<uint16_t>(0)) {
if (sender_address.sin_port !=
to_copy->source_address->sin_port) {
good_data = false;
}
}
if (sender_address.sin_addr.s_addr !=
to_copy->source_address->sin_addr.s_addr) {
good_data = false;
}
}
}
} else {
read_bytes = read(to_copy->input,
buffer + position, sizeof(buffer) - position);
}
if (read_bytes == -1) {
if (errno != EINTR) {
PLOG(FATAL, "read(%d, %p, %zd) failed",
to_copy->input, buffer + position, position - sizeof(buffer));
}
} else if (read_bytes == 0 && to_copy->interface_address == NULL) {
// read(2) says that this means EOF
return NULL;
}
if (good_data) {
position += read_bytes;
}
}
CHECK_GE(position, 0);
CHECK_LE(position, static_cast<ssize_t>(sizeof(buffer)));
if (position > 0) {
ssize_t sent_bytes = write(to_copy->output, buffer, position);
if (sent_bytes == -1) {
if (errno != EINTR) {
PLOG(FATAL, "write(%d, %p, %zd) failed",
to_copy->output, buffer, position);
}
} else if (sent_bytes != 0) {
if (sent_bytes == position) {
position = 0;
} else {
memmove(buffer, buffer + sent_bytes, position - sent_bytes);
position -= sent_bytes;
}
}
}
}
}
int NetconsoleMain(int argc, char **argv) {
WriteCoreDumps();
logging::Init();
logging::AddImplementation(new logging::StreamLogImplementation(stdout));
int input, output;
if (argc > 1) {
output = open(argv[1], O_APPEND | O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (output == -1) {
if (errno == EACCES || errno == ELOOP || errno == ENOSPC ||
errno == ENOTDIR || errno == EROFS || errno == ETXTBSY) {
PLOG(FATAL, "opening output file '%s' failed", argv[1]);
}
PLOG(FATAL, "open('%s', stuff, 0644) failed", argv[1]);
}
fprintf(stderr, "Writing output to '%s'.\n", argv[1]);
input = -1;
fprintf(stderr, "Not taking any input.\n");
} else {
output = STDOUT_FILENO;
fprintf(stderr, "Writing output to stdout.\n");
input = STDIN_FILENO;
fprintf(stderr, "Reading stdin.\n");
}
int on = 1;
int from_crio = socket(AF_INET, SOCK_DGRAM, 0);
if (from_crio == -1) {
PLOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed");
}
if (setsockopt(from_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
PLOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed", on, from_crio);
}
if (setsockopt(from_crio, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
PLOG(FATAL, "SOL_SOCKET::SO_BROADCAST=%d(%d) failed", on, from_crio);
}
if (setsockopt(from_crio, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) == -1) {
PLOG(FATAL, "IPROTO_IP::IP_PKTINFO=%d(%d) failed", on, from_crio);
}
union {
struct sockaddr_in in;
struct sockaddr addr;
} address, crio_address;
address.in.sin_family = AF_INET;
address.in.sin_port = hton<uint16_t>(6666);
address.in.sin_addr.s_addr = INADDR_ANY;
crio_address.in.sin_family = AF_INET;
crio_address.in.sin_port = hton<uint16_t>(0);
crio_address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
::aos::util::SetLastSegment(&crio_address.in.sin_addr,
::aos::NetworkAddress::kCRIO);
if (bind(from_crio, &address.addr, sizeof(address)) == -1) {
PLOG(FATAL, "bind(%d, %p, %zu) failed",
from_crio, &address.addr, sizeof(address));
}
pthread_t input_thread, output_thread;
address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
::aos::util::SetLastSegment(&address.in.sin_addr, NetworkAddress::kCRIO);
fprintf(stderr, "Using cRIO IP %s.\n",
inet_ntoa(address.in.sin_addr));
if (input != -1) {
int to_crio = socket(AF_INET, SOCK_DGRAM, 0);
if (to_crio == -1) {
PLOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed");
}
if (setsockopt(to_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
PLOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed", on, to_crio);
}
address.in.sin_port = hton<uint16_t>(6668);
if (connect(to_crio, &address.addr, sizeof(address)) == -1) {
PLOG(FATAL, "connect(%d, %p, %zu) failed",
to_crio, &address.addr, sizeof(address));
}
FDsToCopy input_fds{input, to_crio, nullptr, nullptr};
if (pthread_create(&input_thread, NULL, FDCopyThread, &input_fds) == -1) {
PLOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed",
&input_thread, FDCopyThread, &input_fds);
}
}
address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
FDsToCopy output_fds{from_crio, output, &address.in, &crio_address.in};
if (pthread_create(&output_thread, NULL, FDCopyThread, &output_fds) == -1) {
PLOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed",
&output_thread, FDCopyThread, &output_fds);
}
// input_thread will finish when stdin gets an EOF
if (pthread_join((input == -1) ? output_thread : input_thread, NULL) == -1) {
PLOG(FATAL, "pthread_join(a_thread, NULL) failed");
}
exit(EXIT_SUCCESS);
}
} // namespace
} // namespace aos
int main(int argc, char **argv) {
return ::aos::NetconsoleMain(argc, argv);
}