Line data Source code
1 : /*
2 : build/native/gcc/bin/fdctl spy --config src/app/fdctl/config/testnet.toml
3 : */
4 :
5 : #define _GNU_SOURCE /* See feature_test_macros(7) */
6 :
7 : #include "fdctl.h"
8 : #include "../../flamenco/gossip/fd_gossip.h"
9 : #include "../../util/fd_util.h"
10 : #include "../../ballet/base58/fd_base58.h"
11 : #include "../../flamenco/types/fd_types_yaml.h"
12 : #include "../../util/net/fd_eth.h"
13 : #include <stdio.h>
14 : #include <unistd.h>
15 : #include <signal.h>
16 : #include <sys/socket.h>
17 : #include <netinet/in.h>
18 : #include <arpa/inet.h>
19 : #include <sys/random.h>
20 : #include <errno.h>
21 : #include <netdb.h>
22 : #include <stdlib.h>
23 :
24 0 : static void print_data(fd_crds_data_t* data, void* arg) {
25 0 : fd_flamenco_yaml_t * yamldump = (fd_flamenco_yaml_t *)arg;
26 0 : FILE * dumpfile = (FILE *)fd_flamenco_yaml_file(yamldump);
27 0 : fd_crds_data_walk(yamldump, data, fd_flamenco_yaml_walk, NULL, 1U);
28 0 : fflush(dumpfile);
29 0 : }
30 :
31 : // SIGINT signal handler
32 : volatile int stopflag = 0;
33 0 : static void stop(int sig) { (void)sig; stopflag = 1; }
34 :
35 : static int sockfd = -1;
36 :
37 : /* Convert my style of address to UNIX style */
38 : static int
39 0 : gossip_to_sockaddr( uchar * dst, fd_gossip_peer_addr_t const * src ) {
40 0 : fd_memset(dst, 0, sizeof(struct sockaddr_in));
41 0 : struct sockaddr_in * t = (struct sockaddr_in *)dst;
42 0 : t->sin_family = AF_INET;
43 0 : t->sin_addr.s_addr = src->addr;
44 0 : t->sin_port = src->port;
45 0 : return sizeof(struct sockaddr_in);
46 0 : }
47 :
48 : /* Convert my style of address from UNIX style */
49 : static int
50 0 : gossip_from_sockaddr( fd_gossip_peer_addr_t * dst, uchar const * src ) {
51 0 : FD_STATIC_ASSERT(sizeof(fd_gossip_peer_addr_t) == sizeof(ulong),"messed up size");
52 0 : dst->l = 0;
53 0 : const struct sockaddr_in * sa = (const struct sockaddr_in *)src;
54 0 : dst->addr = sa->sin_addr.s_addr;
55 0 : dst->port = sa->sin_port;
56 0 : return 0;
57 0 : }
58 :
59 : static void
60 0 : send_packet( uchar const * data, size_t sz, fd_gossip_peer_addr_t const * addr, void * arg ) {
61 0 : (void)arg;
62 0 : uchar saddr[sizeof(struct sockaddr_in)];
63 0 : int saddrlen = gossip_to_sockaddr(saddr, addr);
64 0 : if ( sendto(sockfd, data, sz, MSG_DONTWAIT,
65 0 : (const struct sockaddr *)saddr, (socklen_t)saddrlen) < 0 ) {
66 0 : FD_LOG_WARNING(("sendto failed: %s", strerror(errno)));
67 0 : }
68 0 : }
69 :
70 : static int
71 0 : main_loop( fd_gossip_t * glob, fd_gossip_config_t * config, volatile int * stopflag ) {
72 0 : int fd;
73 0 : if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
74 0 : FD_LOG_ERR(("socket failed: %s", strerror(errno)));
75 0 : return -1;
76 0 : }
77 0 : sockfd = fd;
78 0 : int optval = 1<<20;
79 0 : if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&optval, sizeof(int)) < 0) {
80 0 : FD_LOG_ERR(("setsocketopt failed: %s", strerror(errno)));
81 0 : return -1;
82 0 : }
83 0 : if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&optval, sizeof(int)) < 0) {
84 0 : FD_LOG_ERR(("setsocketopt failed: %s", strerror(errno)));
85 0 : return -1;
86 0 : }
87 0 : uchar saddr[sizeof(struct sockaddr_in6)];
88 0 : int saddrlen = gossip_to_sockaddr(saddr, &config->my_addr);
89 0 : if (saddrlen < 0 || bind(fd, (struct sockaddr*)saddr, (uint)saddrlen) < 0) {
90 0 : FD_LOG_ERR(("bind failed: %s", strerror(errno)));
91 0 : return -1;
92 0 : }
93 :
94 0 : fd_gossip_settime(glob, fd_log_wallclock());
95 0 : fd_gossip_start(glob);
96 :
97 0 : #define VLEN 32U
98 0 : struct mmsghdr msgs[VLEN];
99 0 : struct iovec iovecs[VLEN];
100 0 : uchar bufs[VLEN][FD_ETH_PAYLOAD_MAX];
101 0 : uchar sockaddrs[VLEN][sizeof(struct sockaddr_in6)]; /* sockaddr is smaller than sockaddr_in6 */
102 :
103 0 : while ( !*stopflag ) {
104 0 : fd_gossip_settime(glob, fd_log_wallclock());
105 0 : fd_gossip_continue(glob);
106 :
107 0 : fd_memset(msgs, 0, sizeof(msgs));
108 0 : for (uint i = 0; i < VLEN; i++) {
109 0 : iovecs[i].iov_base = bufs[i];
110 0 : iovecs[i].iov_len = FD_ETH_PAYLOAD_MAX;
111 0 : msgs[i].msg_hdr.msg_iov = &iovecs[i];
112 0 : msgs[i].msg_hdr.msg_iovlen = 1;
113 0 : msgs[i].msg_hdr.msg_name = sockaddrs[i];
114 0 : msgs[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
115 0 : }
116 :
117 : /* Read more packets */
118 0 : int retval = recvmmsg(fd, msgs, VLEN, MSG_DONTWAIT, NULL);
119 0 : if (retval < 0) {
120 0 : if (errno == EINTR || errno == EWOULDBLOCK)
121 0 : continue;
122 0 : FD_LOG_ERR(("recvmmsg failed: %s", strerror(errno)));
123 0 : return -1;
124 0 : }
125 0 : if (retval == 0)
126 0 : continue;
127 :
128 0 : for (uint i = 0; i < (uint)retval; ++i) {
129 0 : fd_gossip_peer_addr_t from;
130 0 : gossip_from_sockaddr( &from, msgs[i].msg_hdr.msg_name );
131 0 : fd_gossip_recv_packet(glob, bufs[i], msgs[i].msg_len, &from);
132 0 : }
133 0 : }
134 :
135 0 : close(fd);
136 0 : return 0;
137 0 : }
138 :
139 : /* Convert a host:port string to a gossip network address. If host is
140 : * missing, it assumes the local hostname. */
141 : static fd_gossip_peer_addr_t *
142 0 : resolve_hostport(const char* str /* host:port */, fd_gossip_peer_addr_t * res) {
143 0 : fd_memset(res, 0, sizeof(fd_gossip_peer_addr_t));
144 :
145 : /* Find the : and copy out the host */
146 0 : char buf[128];
147 0 : uint i;
148 0 : for (i = 0; ; ++i) {
149 0 : if (str[i] == '\0' || i > sizeof(buf)-1U) {
150 0 : FD_LOG_ERR(("missing colon"));
151 0 : return NULL;
152 0 : }
153 0 : if (str[i] == ':') {
154 0 : buf[i] = '\0';
155 0 : break;
156 0 : }
157 0 : buf[i] = str[i];
158 0 : }
159 0 : if (i == 0)
160 : /* :port means $HOST:port */
161 0 : gethostname(buf, sizeof(buf));
162 :
163 0 : struct hostent * host = gethostbyname( buf );
164 0 : if (host == NULL) {
165 0 : FD_LOG_WARNING(("unable to resolve host %s", buf));
166 0 : return NULL;
167 0 : }
168 : /* Convert result to gossip address */
169 0 : res->l = 0;
170 0 : res->addr = ((struct in_addr *)host->h_addr)->s_addr;
171 0 : int port = atoi(str + i + 1);
172 0 : if (port < 1024 || port > (int)USHORT_MAX) {
173 0 : FD_LOG_ERR(("invalid port number"));
174 0 : return NULL;
175 0 : }
176 0 : res->port = htons((ushort)port);
177 :
178 0 : return res;
179 0 : }
180 :
181 : void
182 : spy_cmd_fn( args_t * args,
183 0 : config_t * const config ) {
184 0 : (void)args;
185 :
186 0 : fd_valloc_t valloc = fd_libc_alloc_virtual();
187 :
188 0 : fd_gossip_config_t gconfig;
189 0 : fd_memset(&gconfig, 0, sizeof(gconfig));
190 :
191 0 : uchar private_key[32];
192 0 : FD_TEST( 32UL==getrandom( private_key, 32UL, 0 ) );
193 0 : fd_sha512_t sha[1];
194 0 : fd_pubkey_t public_key;
195 0 : FD_TEST( fd_ed25519_public_from_private( public_key.uc, private_key, sha ) );
196 :
197 0 : gconfig.private_key = private_key;
198 0 : gconfig.public_key = &public_key;
199 :
200 0 : char gossiphost[256];
201 0 : if ( config->gossip.host[0] == '\0' )
202 0 : gethostname(gossiphost, sizeof(gossiphost));
203 0 : else
204 0 : strncpy(gossiphost, config->gossip.host, sizeof(gossiphost));
205 0 : ulong seed = fd_hash(0, gossiphost, strnlen(gossiphost, sizeof(gossiphost)));
206 :
207 : /* Compute my address */
208 0 : struct hostent * hostres = gethostbyname( gossiphost );
209 0 : if (hostres == NULL) {
210 0 : FD_LOG_ERR(("unable to resolve host %s", gossiphost));
211 0 : return;
212 0 : }
213 0 : gconfig.my_addr.l = 0;
214 0 : gconfig.my_addr.addr = ((struct in_addr *)hostres->h_addr)->s_addr;
215 0 : gconfig.my_addr.port = htons((ushort)config->gossip.port);
216 :
217 0 : gconfig.shred_version = config->consensus.expected_shred_version;
218 0 : if (0 == gconfig.shred_version)
219 : /* TODO: This is a placeholder until we can do something smarter */
220 0 : gconfig.shred_version = 61807;
221 :
222 0 : fd_flamenco_yaml_t * yamldump =
223 0 : fd_flamenco_yaml_init( fd_flamenco_yaml_new(
224 0 : fd_valloc_malloc( valloc, fd_flamenco_yaml_align(), fd_flamenco_yaml_footprint() ) ),
225 0 : stdout );
226 0 : gconfig.deliver_fun = print_data;
227 0 : gconfig.deliver_arg = yamldump;
228 0 : gconfig.send_fun = send_packet;
229 0 : gconfig.send_arg = NULL;
230 :
231 0 : void * shm = fd_valloc_malloc(valloc, fd_gossip_align(), fd_gossip_footprint());
232 0 : fd_gossip_t * glob = fd_gossip_join(fd_gossip_new(shm, seed));
233 :
234 0 : if ( fd_gossip_set_config(glob, &gconfig) )
235 0 : return;
236 :
237 0 : for ( ulong i = 0; i < config->gossip.entrypoints_cnt; ++i ) {
238 0 : fd_gossip_peer_addr_t peeraddr;
239 0 : if ( fd_gossip_add_active_peer(glob, resolve_hostport(config->gossip.entrypoints[i], &peeraddr)) )
240 0 : return;
241 0 : }
242 :
243 0 : signal(SIGINT, stop);
244 0 : signal(SIGPIPE, SIG_IGN);
245 :
246 0 : if ( main_loop(glob, &gconfig, &stopflag) )
247 0 : return;
248 :
249 0 : fd_valloc_free(valloc, fd_flamenco_yaml_delete(yamldump));
250 :
251 0 : fd_valloc_free(valloc, fd_gossip_delete(fd_gossip_leave(glob)));
252 0 : }
|