xref: /illumos-kvm-cmd/net/socket.c (revision 68396ea9)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "net/socket.h"
25 
26 #include "config-host.h"
27 
28 #include "net.h"
29 #include "qemu-char.h"
30 #include "qemu-common.h"
31 #include "qemu-error.h"
32 #include "qemu-option.h"
33 #include "qemu_socket.h"
34 
35 typedef struct NetSocketState {
36     VLANClientState nc;
37     int fd;
38     int state; /* 0 = getting length, 1 = getting data */
39     unsigned int index;
40     unsigned int packet_len;
41     uint8_t buf[4096];
42     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
43 } NetSocketState;
44 
45 typedef struct NetSocketListenState {
46     VLANState *vlan;
47     char *model;
48     char *name;
49     int fd;
50 } NetSocketListenState;
51 
52 /* XXX: we consider we can send the whole packet without blocking */
net_socket_receive(VLANClientState * nc,const uint8_t * buf,size_t size)53 static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
54 {
55     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
56     uint32_t len;
57     len = htonl(size);
58 
59     send_all(s->fd, (const uint8_t *)&len, sizeof(len));
60     return send_all(s->fd, buf, size);
61 }
62 
net_socket_receive_dgram(VLANClientState * nc,const uint8_t * buf,size_t size)63 static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size)
64 {
65     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
66 
67     return sendto(s->fd, (const void *)buf, size, 0,
68                   (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
69 }
70 
net_socket_send(void * opaque)71 static void net_socket_send(void *opaque)
72 {
73     NetSocketState *s = opaque;
74     int size, err;
75     unsigned l;
76     uint8_t buf1[4096];
77     const uint8_t *buf;
78 
79     size = recv(s->fd, (void *)buf1, sizeof(buf1), 0);
80     if (size < 0) {
81         err = socket_error();
82         if (err != EWOULDBLOCK)
83             goto eoc;
84     } else if (size == 0) {
85         /* end of connection */
86     eoc:
87         qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
88         closesocket(s->fd);
89         return;
90     }
91     buf = buf1;
92     while (size > 0) {
93         /* reassemble a packet from the network */
94         switch(s->state) {
95         case 0:
96             l = 4 - s->index;
97             if (l > size)
98                 l = size;
99             memcpy(s->buf + s->index, buf, l);
100             buf += l;
101             size -= l;
102             s->index += l;
103             if (s->index == 4) {
104                 /* got length */
105                 s->packet_len = ntohl(*(uint32_t *)s->buf);
106                 s->index = 0;
107                 s->state = 1;
108             }
109             break;
110         case 1:
111             l = s->packet_len - s->index;
112             if (l > size)
113                 l = size;
114             if (s->index + l <= sizeof(s->buf)) {
115                 memcpy(s->buf + s->index, buf, l);
116             } else {
117                 fprintf(stderr, "serious error: oversized packet received,"
118                     "connection terminated.\n");
119                 s->state = 0;
120                 goto eoc;
121             }
122 
123             s->index += l;
124             buf += l;
125             size -= l;
126             if (s->index >= s->packet_len) {
127                 qemu_send_packet(&s->nc, s->buf, s->packet_len);
128                 s->index = 0;
129                 s->state = 0;
130             }
131             break;
132         }
133     }
134 }
135 
net_socket_send_dgram(void * opaque)136 static void net_socket_send_dgram(void *opaque)
137 {
138     NetSocketState *s = opaque;
139     int size;
140 
141     size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
142     if (size < 0)
143         return;
144     if (size == 0) {
145         /* end of connection */
146         qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
147         return;
148     }
149     qemu_send_packet(&s->nc, s->buf, size);
150 }
151 
net_socket_mcast_create(struct sockaddr_in * mcastaddr,struct in_addr * localaddr)152 static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
153 {
154     struct ip_mreq imr;
155     int fd;
156     int val, ret;
157     if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
158 	fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
159 		inet_ntoa(mcastaddr->sin_addr),
160                 (int)ntohl(mcastaddr->sin_addr.s_addr));
161 	return -1;
162 
163     }
164     fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
165     if (fd < 0) {
166         perror("socket(PF_INET, SOCK_DGRAM)");
167         return -1;
168     }
169 
170     val = 1;
171     ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
172                    (const char *)&val, sizeof(val));
173     if (ret < 0) {
174 	perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
175 	goto fail;
176     }
177 
178     ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
179     if (ret < 0) {
180         perror("bind");
181         goto fail;
182     }
183 
184     /* Add host to multicast group */
185     imr.imr_multiaddr = mcastaddr->sin_addr;
186     if (localaddr) {
187         imr.imr_interface = *localaddr;
188     } else {
189         imr.imr_interface.s_addr = htonl(INADDR_ANY);
190     }
191 
192     ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
193                      (const char *)&imr, sizeof(struct ip_mreq));
194     if (ret < 0) {
195 	perror("setsockopt(IP_ADD_MEMBERSHIP)");
196 	goto fail;
197     }
198 
199     /* Force mcast msgs to loopback (eg. several QEMUs in same host */
200     val = 1;
201     ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
202                    (const char *)&val, sizeof(val));
203     if (ret < 0) {
204 	perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
205 	goto fail;
206     }
207 
208     /* If a bind address is given, only send packets from that address */
209     if (localaddr != NULL) {
210         ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
211                          (const char *)localaddr, sizeof(*localaddr));
212         if (ret < 0) {
213             perror("setsockopt(IP_MULTICAST_IF)");
214             goto fail;
215         }
216     }
217 
218     socket_set_nonblock(fd);
219     return fd;
220 fail:
221     if (fd >= 0)
222         closesocket(fd);
223     return -1;
224 }
225 
net_socket_cleanup(VLANClientState * nc)226 static void net_socket_cleanup(VLANClientState *nc)
227 {
228     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
229     qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
230     close(s->fd);
231 }
232 
233 static NetClientInfo net_dgram_socket_info = {
234     .type = NET_CLIENT_TYPE_SOCKET,
235     .size = sizeof(NetSocketState),
236     .receive = net_socket_receive_dgram,
237     .cleanup = net_socket_cleanup,
238 };
239 
net_socket_fd_init_dgram(VLANState * vlan,const char * model,const char * name,int fd,int is_connected)240 static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
241                                                 const char *model,
242                                                 const char *name,
243                                                 int fd, int is_connected)
244 {
245     struct sockaddr_in saddr;
246     int newfd;
247     socklen_t saddr_len;
248     VLANClientState *nc;
249     NetSocketState *s;
250 
251     /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
252      * Because this may be "shared" socket from a "master" process, datagrams would be recv()
253      * by ONLY ONE process: we must "clone" this dgram socket --jjo
254      */
255 
256     if (is_connected) {
257 	if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
258 	    /* must be bound */
259 	    if (saddr.sin_addr.s_addr==0) {
260 		fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
261 			fd);
262 		return NULL;
263 	    }
264 	    /* clone dgram socket */
265 	    newfd = net_socket_mcast_create(&saddr, NULL);
266 	    if (newfd < 0) {
267 		/* error already reported by net_socket_mcast_create() */
268 		close(fd);
269 		return NULL;
270 	    }
271 	    /* clone newfd to fd, close newfd */
272 	    dup2(newfd, fd);
273 	    close(newfd);
274 
275 	} else {
276 	    fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
277 		    fd, strerror(errno));
278 	    return NULL;
279 	}
280     }
281 
282     nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name);
283 
284     snprintf(nc->info_str, sizeof(nc->info_str),
285 	    "socket: fd=%d (%s mcast=%s:%d)",
286 	    fd, is_connected ? "cloned" : "",
287 	    inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
288 
289     s = DO_UPCAST(NetSocketState, nc, nc);
290 
291     s->fd = fd;
292 
293     qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
294 
295     /* mcast: save bound address as dst */
296     if (is_connected) s->dgram_dst=saddr;
297 
298     return s;
299 }
300 
net_socket_connect(void * opaque)301 static void net_socket_connect(void *opaque)
302 {
303     NetSocketState *s = opaque;
304     qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
305 }
306 
307 static NetClientInfo net_socket_info = {
308     .type = NET_CLIENT_TYPE_SOCKET,
309     .size = sizeof(NetSocketState),
310     .receive = net_socket_receive,
311     .cleanup = net_socket_cleanup,
312 };
313 
net_socket_fd_init_stream(VLANState * vlan,const char * model,const char * name,int fd,int is_connected)314 static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
315                                                  const char *model,
316                                                  const char *name,
317                                                  int fd, int is_connected)
318 {
319     VLANClientState *nc;
320     NetSocketState *s;
321 
322     nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name);
323 
324     snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
325 
326     s = DO_UPCAST(NetSocketState, nc, nc);
327 
328     s->fd = fd;
329 
330     if (is_connected) {
331         net_socket_connect(s);
332     } else {
333         qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
334     }
335     return s;
336 }
337 
net_socket_fd_init(VLANState * vlan,const char * model,const char * name,int fd,int is_connected)338 static NetSocketState *net_socket_fd_init(VLANState *vlan,
339                                           const char *model, const char *name,
340                                           int fd, int is_connected)
341 {
342     int so_type = -1, optlen=sizeof(so_type);
343 
344     if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
345         (socklen_t *)&optlen)< 0) {
346 	fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd);
347 	return NULL;
348     }
349     switch(so_type) {
350     case SOCK_DGRAM:
351         return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
352     case SOCK_STREAM:
353         return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
354     default:
355         /* who knows ... this could be a eg. a pty, do warn and continue as stream */
356         fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
357         return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
358     }
359     return NULL;
360 }
361 
net_socket_accept(void * opaque)362 static void net_socket_accept(void *opaque)
363 {
364     NetSocketListenState *s = opaque;
365     NetSocketState *s1;
366     struct sockaddr_in saddr;
367     socklen_t len;
368     int fd;
369 
370     for(;;) {
371         len = sizeof(saddr);
372         fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len);
373         if (fd < 0 && errno != EINTR) {
374             return;
375         } else if (fd >= 0) {
376             break;
377         }
378     }
379     s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
380     if (!s1) {
381         closesocket(fd);
382     } else {
383         snprintf(s1->nc.info_str, sizeof(s1->nc.info_str),
384                  "socket: connection from %s:%d",
385                  inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
386     }
387 }
388 
net_socket_listen_init(VLANState * vlan,const char * model,const char * name,const char * host_str)389 static int net_socket_listen_init(VLANState *vlan,
390                                   const char *model,
391                                   const char *name,
392                                   const char *host_str)
393 {
394     NetSocketListenState *s;
395     int fd, val, ret;
396     struct sockaddr_in saddr;
397 
398     if (parse_host_port(&saddr, host_str) < 0)
399         return -1;
400 
401     s = qemu_mallocz(sizeof(NetSocketListenState));
402 
403     fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
404     if (fd < 0) {
405         perror("socket");
406         return -1;
407     }
408     socket_set_nonblock(fd);
409 
410     /* allow fast reuse */
411     val = 1;
412     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
413 
414     ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
415     if (ret < 0) {
416         perror("bind");
417         return -1;
418     }
419     ret = listen(fd, 0);
420     if (ret < 0) {
421         perror("listen");
422         return -1;
423     }
424     s->vlan = vlan;
425     s->model = qemu_strdup(model);
426     s->name = name ? qemu_strdup(name) : NULL;
427     s->fd = fd;
428     qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
429     return 0;
430 }
431 
net_socket_connect_init(VLANState * vlan,const char * model,const char * name,const char * host_str)432 static int net_socket_connect_init(VLANState *vlan,
433                                    const char *model,
434                                    const char *name,
435                                    const char *host_str)
436 {
437     NetSocketState *s;
438     int fd, connected, ret, err;
439     struct sockaddr_in saddr;
440 
441     if (parse_host_port(&saddr, host_str) < 0)
442         return -1;
443 
444     fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
445     if (fd < 0) {
446         perror("socket");
447         return -1;
448     }
449     socket_set_nonblock(fd);
450 
451     connected = 0;
452     for(;;) {
453         ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
454         if (ret < 0) {
455             err = socket_error();
456             if (err == EINTR || err == EWOULDBLOCK) {
457             } else if (err == EINPROGRESS) {
458                 break;
459 #ifdef _WIN32
460             } else if (err == WSAEALREADY) {
461                 break;
462 #endif
463             } else {
464                 perror("connect");
465                 closesocket(fd);
466                 return -1;
467             }
468         } else {
469             connected = 1;
470             break;
471         }
472     }
473     s = net_socket_fd_init(vlan, model, name, fd, connected);
474     if (!s)
475         return -1;
476     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
477              "socket: connect to %s:%d",
478              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
479     return 0;
480 }
481 
net_socket_mcast_init(VLANState * vlan,const char * model,const char * name,const char * host_str,const char * localaddr_str)482 static int net_socket_mcast_init(VLANState *vlan,
483                                  const char *model,
484                                  const char *name,
485                                  const char *host_str,
486                                  const char *localaddr_str)
487 {
488     NetSocketState *s;
489     int fd;
490     struct sockaddr_in saddr;
491     struct in_addr localaddr, *param_localaddr;
492 
493     if (parse_host_port(&saddr, host_str) < 0)
494         return -1;
495 
496     if (localaddr_str != NULL) {
497         if (inet_aton(localaddr_str, &localaddr) == 0)
498             return -1;
499         param_localaddr = &localaddr;
500     } else {
501         param_localaddr = NULL;
502     }
503 
504     fd = net_socket_mcast_create(&saddr, param_localaddr);
505     if (fd < 0)
506 	return -1;
507 
508     s = net_socket_fd_init(vlan, model, name, fd, 0);
509     if (!s)
510         return -1;
511 
512     s->dgram_dst = saddr;
513 
514     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
515              "socket: mcast=%s:%d",
516              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
517     return 0;
518 
519 }
520 
net_init_socket(QemuOpts * opts,Monitor * mon,const char * name,VLANState * vlan)521 int net_init_socket(QemuOpts *opts,
522                     Monitor *mon,
523                     const char *name,
524                     VLANState *vlan)
525 {
526     if (qemu_opt_get(opts, "fd")) {
527         int fd;
528 
529         if (qemu_opt_get(opts, "listen") ||
530             qemu_opt_get(opts, "connect") ||
531             qemu_opt_get(opts, "mcast") ||
532             qemu_opt_get(opts, "localaddr")) {
533             error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n");
534             return -1;
535         }
536 
537         fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
538         if (fd == -1) {
539             return -1;
540         }
541 
542         if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
543             close(fd);
544             return -1;
545         }
546     } else if (qemu_opt_get(opts, "listen")) {
547         const char *listen;
548 
549         if (qemu_opt_get(opts, "fd") ||
550             qemu_opt_get(opts, "connect") ||
551             qemu_opt_get(opts, "mcast") ||
552             qemu_opt_get(opts, "localaddr")) {
553             error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n");
554             return -1;
555         }
556 
557         listen = qemu_opt_get(opts, "listen");
558 
559         if (net_socket_listen_init(vlan, "socket", name, listen) == -1) {
560             return -1;
561         }
562     } else if (qemu_opt_get(opts, "connect")) {
563         const char *connect;
564 
565         if (qemu_opt_get(opts, "fd") ||
566             qemu_opt_get(opts, "listen") ||
567             qemu_opt_get(opts, "mcast") ||
568             qemu_opt_get(opts, "localaddr")) {
569             error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n");
570             return -1;
571         }
572 
573         connect = qemu_opt_get(opts, "connect");
574 
575         if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
576             return -1;
577         }
578     } else if (qemu_opt_get(opts, "mcast")) {
579         const char *mcast, *localaddr;
580 
581         if (qemu_opt_get(opts, "fd") ||
582             qemu_opt_get(opts, "connect") ||
583             qemu_opt_get(opts, "listen")) {
584             error_report("fd=, connect= and listen= is invalid with mcast=");
585             return -1;
586         }
587 
588         mcast = qemu_opt_get(opts, "mcast");
589         localaddr = qemu_opt_get(opts, "localaddr");
590 
591         if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) {
592             return -1;
593         }
594     } else {
595         error_report("-socket requires fd=, listen=, connect= or mcast=");
596         return -1;
597     }
598 
599     return 0;
600 }
601