blob: f6246739ffd7b3dbdc77735ee283dc7c01b8b3d7 [file] [log] [blame]
Brian Silverman771b4b82013-04-12 16:36:56 -07001#include <stdio.h>
2#include <sys/types.h>
3#include <sys/socket.h>
4#include <netinet/in.h>
5#include <arpa/inet.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <pthread.h>
9#include <sys/stat.h>
10#include <assert.h>
11
12#include "aos/common/logging/logging_impl.h"
Brianc1dc7d22014-04-02 12:21:08 -070013#include "aos/common/util/inet_addr.h"
Brian Silverman14fd0fb2014-01-14 21:42:01 -080014#include "aos/linux_code/configuration.h"
Brian Silvermanfe1ef172014-04-12 17:12:45 -070015#include "aos/common/network_port.h"
16#include "aos/linux_code/init.h"
Brian Silvermanf02c3982014-04-21 22:04:41 -070017#include "aos/common/byteorder.h"
Brian Silverman771b4b82013-04-12 16:36:56 -070018
19namespace aos {
20namespace {
21
22struct FDsToCopy {
23 const int input;
24 const int output;
Brian Silvermane1514fc2013-04-13 14:57:35 -070025
26 const struct sockaddr_in *const interface_address;
Brian Silvermanfe1ef172014-04-12 17:12:45 -070027 const struct sockaddr_in *const source_address;
Brian Silverman771b4b82013-04-12 16:36:56 -070028};
29
30void *FDCopyThread(void *to_copy_in) {
31 FDsToCopy *to_copy = static_cast<FDsToCopy *>(to_copy_in);
32
33 char buffer[32768];
34 ssize_t position = 0;
35 while (true) {
36 assert(position >= 0);
37 assert(position <= static_cast<ssize_t>(sizeof(buffer)));
38 if (position != sizeof(buffer)) {
Brian Silvermane1514fc2013-04-13 14:57:35 -070039 ssize_t read_bytes;
40 bool good_data = true;
Brian Silvermanfe1ef172014-04-12 17:12:45 -070041 if (to_copy->interface_address != nullptr ||
42 to_copy->source_address != nullptr) {
Brian Silvermane1514fc2013-04-13 14:57:35 -070043 char control_buffer[0x100];
44 struct msghdr header;
45 memset(static_cast<void *>(&header), 0, sizeof(header));
46 header.msg_control = control_buffer;
47 header.msg_controllen = sizeof(control_buffer);
48 struct iovec iovecs[1];
49 iovecs[0].iov_base = buffer + position;
Brian Silvermanfe1ef172014-04-12 17:12:45 -070050 iovecs[0].iov_len = sizeof(buffer) - position;
Brian Silvermane1514fc2013-04-13 14:57:35 -070051 header.msg_iov = iovecs;
52 header.msg_iovlen = sizeof(iovecs) / sizeof(iovecs[0]);
Brian Silvermanfe1ef172014-04-12 17:12:45 -070053 struct sockaddr_in sender_address;
54 header.msg_name = &sender_address;
55 header.msg_namelen = sizeof(sender_address);
56
Brian Silvermane1514fc2013-04-13 14:57:35 -070057 read_bytes = recvmsg(to_copy->input, &header, 0);
58 if (read_bytes != -1) {
Brian Silvermanfe1ef172014-04-12 17:12:45 -070059 if (to_copy->interface_address != nullptr) {
60 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&header);
61 cmsg != NULL;
62 cmsg = CMSG_NXTHDR(&header, cmsg)) {
63 if (cmsg->cmsg_level == IPPROTO_IP &&
64 cmsg->cmsg_type == IP_PKTINFO) {
65 unsigned char *data = CMSG_DATA(cmsg);
66 struct in_pktinfo *pktinfo;
67 memcpy(&pktinfo, &data, sizeof(void *));
68 if (pktinfo->ipi_spec_dst.s_addr !=
69 to_copy->interface_address->sin_addr.s_addr) {
70 good_data = false;
71 }
72 }
73 }
74 }
75 if (to_copy->source_address != nullptr) {
76 assert(header.msg_namelen >= sizeof(struct sockaddr_in));
Brian Silvermanf02c3982014-04-21 22:04:41 -070077 if (to_copy->source_address->sin_port != hton<uint16_t>(0)) {
Brian Silvermanfe1ef172014-04-12 17:12:45 -070078 if (sender_address.sin_port !=
79 to_copy->source_address->sin_port) {
80 good_data = false;
81 }
82 }
83 if (sender_address.sin_addr.s_addr !=
84 to_copy->source_address->sin_addr.s_addr) {
85 good_data = false;
Brian Silvermane1514fc2013-04-13 14:57:35 -070086 }
87 }
88 }
89 } else {
90 read_bytes = read(to_copy->input,
Brian Silvermanfe1ef172014-04-12 17:12:45 -070091 buffer + position, sizeof(buffer) - position);
Brian Silvermane1514fc2013-04-13 14:57:35 -070092 }
Brian Silverman771b4b82013-04-12 16:36:56 -070093 if (read_bytes == -1) {
94 if (errno != EINTR) {
95 LOG(FATAL, "read(%d, %p, %zd) failed with %d: %s\n",
96 to_copy->input, buffer + position, position - sizeof(buffer),
97 errno, strerror(errno));
98 }
Brian Silvermane1514fc2013-04-13 14:57:35 -070099 } else if (read_bytes == 0 && to_copy->interface_address == NULL) {
Brian Silverman771b4b82013-04-12 16:36:56 -0700100 // read(2) says that this means EOF
101 return NULL;
102 }
Brian Silvermane1514fc2013-04-13 14:57:35 -0700103 if (good_data) {
104 position += read_bytes;
105 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700106 }
107
108 assert(position >= 0);
109 assert(position <= static_cast<ssize_t>(sizeof(buffer)));
110 if (position > 0) {
111 ssize_t sent_bytes = write(to_copy->output, buffer, position);
112 if (sent_bytes == -1) {
113 if (errno != EINTR) {
114 LOG(FATAL, "write(%d, %p, %zd) failed with %d: %s\n",
115 to_copy->output, buffer, position, errno, strerror(errno));
116 }
117 } else if (sent_bytes != 0) {
Brian Silverman1c8670d2013-04-13 18:59:13 -0700118 if (sent_bytes == position) {
119 position = 0;
120 } else {
121 memmove(buffer, buffer + sent_bytes, position - sent_bytes);
122 position -= sent_bytes;
123 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700124 }
125 }
126 }
127}
128
129int NetconsoleMain(int argc, char **argv) {
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700130 WriteCoreDumps();
Brian Silverman771b4b82013-04-12 16:36:56 -0700131 logging::Init();
132
Brian Silvermancb9637c2013-04-13 22:19:43 -0700133 int input, output;
Brian Silverman771b4b82013-04-12 16:36:56 -0700134 if (argc > 1) {
Brian Silvermancb9637c2013-04-13 22:19:43 -0700135 output = open(argv[1], O_APPEND | O_CREAT | O_WRONLY | O_TRUNC, 0666);
Brian Silverman771b4b82013-04-12 16:36:56 -0700136 if (output == -1) {
137 if (errno == EACCES || errno == ELOOP || errno == ENOSPC ||
138 errno == ENOTDIR || errno == EROFS || errno == ETXTBSY) {
139 fprintf(stderr, "Opening output file '%s' failed because of %s.\n",
140 argv[1], strerror(errno));
141 exit(EXIT_FAILURE);
142 }
143 LOG(FATAL, "open('%s', stuff, 0644) failed with %d: %s\n", argv[1],
144 errno, strerror(errno));
145 }
146 fprintf(stderr, "Writing output to '%s'.\n", argv[1]);
Brian Silvermancb9637c2013-04-13 22:19:43 -0700147 input = -1;
148 fprintf(stderr, "Not taking any input.\n");
Brian Silverman771b4b82013-04-12 16:36:56 -0700149 } else {
150 output = STDOUT_FILENO;
151 fprintf(stderr, "Writing output to stdout.\n");
Brian Silvermancb9637c2013-04-13 22:19:43 -0700152 input = STDIN_FILENO;
153 fprintf(stderr, "Reading stdin.\n");
Brian Silverman771b4b82013-04-12 16:36:56 -0700154 }
155
Brian Silverman771b4b82013-04-12 16:36:56 -0700156 int on = 1;
157
158 int from_crio = socket(AF_INET, SOCK_DGRAM, 0);
159 if (from_crio == -1) {
160 LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
161 errno, strerror(errno));
162 }
163 if (setsockopt(from_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
164 LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
165 on, from_crio, errno, strerror(errno));
166 }
167 if (setsockopt(from_crio, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
168 LOG(FATAL, "SOL_SOCKET::SO_BROADCAST=%d(%d) failed with %d: %s\n",
169 on, from_crio, errno, strerror(errno));
170 }
Brian Silvermane1514fc2013-04-13 14:57:35 -0700171 if (setsockopt(from_crio, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) == -1) {
172 LOG(FATAL, "IPROTO_IP::IP_PKTINFO=%d(%d) failed with %d: %s\n",
173 on, from_crio, errno, strerror(errno));
174 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700175 union {
176 struct sockaddr_in in;
177 struct sockaddr addr;
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700178 } address, crio_address;
179
Brian Silverman771b4b82013-04-12 16:36:56 -0700180 address.in.sin_family = AF_INET;
Brian Silvermanf02c3982014-04-21 22:04:41 -0700181 address.in.sin_port = hton<uint16_t>(6666);
Brian Silverman771b4b82013-04-12 16:36:56 -0700182 address.in.sin_addr.s_addr = INADDR_ANY;
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700183
184 crio_address.in.sin_family = AF_INET;
Brian Silvermanf02c3982014-04-21 22:04:41 -0700185 crio_address.in.sin_port = hton<uint16_t>(0);
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700186 crio_address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
187 ::aos::util::SetLastSegment(&crio_address.in.sin_addr,
188 ::aos::NetworkAddress::kCRIO);
189
Brian Silverman771b4b82013-04-12 16:36:56 -0700190 if (bind(from_crio, &address.addr, sizeof(address)) == -1) {
191 LOG(FATAL, "bind(%d, %p, %zu) failed with %d: %s\n",
192 from_crio, &address.addr, sizeof(address), errno, strerror(errno));
193 }
194
Brian Silvermancb9637c2013-04-13 22:19:43 -0700195 pthread_t input_thread, output_thread;
196
Brian Silverman66f079a2013-08-26 16:24:30 -0700197 address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
198 ::aos::util::SetLastSegment(&address.in.sin_addr, NetworkAddress::kCRIO);
199 fprintf(stderr, "Using cRIO IP %s.\n",
200 inet_ntoa(address.in.sin_addr));
201
Brian Silvermancb9637c2013-04-13 22:19:43 -0700202 if (input != -1) {
203 int to_crio = socket(AF_INET, SOCK_DGRAM, 0);
204 if (to_crio == -1) {
205 LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
206 errno, strerror(errno));
207 }
208 if (setsockopt(to_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
209 LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
210 on, to_crio, errno, strerror(errno));
211 }
Brian Silvermanf02c3982014-04-21 22:04:41 -0700212 address.in.sin_port = hton<uint16_t>(6668);
Brian Silvermancb9637c2013-04-13 22:19:43 -0700213 if (connect(to_crio, &address.addr, sizeof(address)) == -1) {
214 LOG(FATAL, "connect(%d, %p, %zu) failed with %d: %s\n",
215 to_crio, &address.addr, sizeof(address), errno, strerror(errno));
216 }
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700217 FDsToCopy input_fds{input, to_crio, nullptr, nullptr};
Brian Silvermancb9637c2013-04-13 22:19:43 -0700218 if (pthread_create(&input_thread, NULL, FDCopyThread, &input_fds) == -1) {
219 LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
220 &input_thread, FDCopyThread, &input_fds, errno, strerror(errno));
221 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700222 }
223
Brian Silverman66f079a2013-08-26 16:24:30 -0700224 address.in.sin_addr = ::aos::configuration::GetOwnIPAddress();
Brian Silvermanfe1ef172014-04-12 17:12:45 -0700225 FDsToCopy output_fds{from_crio, output, &address.in, &crio_address.in};
Brian Silverman771b4b82013-04-12 16:36:56 -0700226 if (pthread_create(&output_thread, NULL, FDCopyThread, &output_fds) == -1) {
227 LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
228 &output_thread, FDCopyThread, &output_fds, errno, strerror(errno));
229 }
Brian Silverman771b4b82013-04-12 16:36:56 -0700230
231 // input_thread will finish when stdin gets an EOF
Brian Silvermancb9637c2013-04-13 22:19:43 -0700232 if (pthread_join((input == -1) ? output_thread : input_thread, NULL) == -1) {
233 LOG(FATAL, "pthread_join(a_thread, NULL) failed with %d: %s\n",
Brian Silverman771b4b82013-04-12 16:36:56 -0700234 errno, strerror(errno));
235 }
236 exit(EXIT_SUCCESS);
237}
238
239} // namespace
240} // namespace aos
241
242int main(int argc, char **argv) {
243 return ::aos::NetconsoleMain(argc, argv);
244}