xref: /qemu/net/socket.c (revision 4e68f7a0)
142281ac9SMark McLoughlin /*
242281ac9SMark McLoughlin  * QEMU System Emulator
342281ac9SMark McLoughlin  *
442281ac9SMark McLoughlin  * Copyright (c) 2003-2008 Fabrice Bellard
542281ac9SMark McLoughlin  *
642281ac9SMark McLoughlin  * Permission is hereby granted, free of charge, to any person obtaining a copy
742281ac9SMark McLoughlin  * of this software and associated documentation files (the "Software"), to deal
842281ac9SMark McLoughlin  * in the Software without restriction, including without limitation the rights
942281ac9SMark McLoughlin  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1042281ac9SMark McLoughlin  * copies of the Software, and to permit persons to whom the Software is
1142281ac9SMark McLoughlin  * furnished to do so, subject to the following conditions:
1242281ac9SMark McLoughlin  *
1342281ac9SMark McLoughlin  * The above copyright notice and this permission notice shall be included in
1442281ac9SMark McLoughlin  * all copies or substantial portions of the Software.
1542281ac9SMark McLoughlin  *
1642281ac9SMark McLoughlin  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1742281ac9SMark McLoughlin  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1842281ac9SMark McLoughlin  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1942281ac9SMark McLoughlin  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2042281ac9SMark McLoughlin  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2142281ac9SMark McLoughlin  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2242281ac9SMark McLoughlin  * THE SOFTWARE.
2342281ac9SMark McLoughlin  */
2442281ac9SMark McLoughlin #include "net/socket.h"
2542281ac9SMark McLoughlin 
2642281ac9SMark McLoughlin #include "config-host.h"
2742281ac9SMark McLoughlin 
2842281ac9SMark McLoughlin #include "net.h"
2942dcc547SLuiz Capitulino #include "monitor.h"
3042281ac9SMark McLoughlin #include "qemu-char.h"
3142281ac9SMark McLoughlin #include "qemu-common.h"
322f792016SMarkus Armbruster #include "qemu-error.h"
3342281ac9SMark McLoughlin #include "qemu-option.h"
3442281ac9SMark McLoughlin #include "qemu_socket.h"
3542281ac9SMark McLoughlin 
3642281ac9SMark McLoughlin typedef struct NetSocketState {
37*4e68f7a0SStefan Hajnoczi     NetClientState nc;
3842281ac9SMark McLoughlin     int fd;
3942281ac9SMark McLoughlin     int state; /* 0 = getting length, 1 = getting data */
4042281ac9SMark McLoughlin     unsigned int index;
4142281ac9SMark McLoughlin     unsigned int packet_len;
4242281ac9SMark McLoughlin     uint8_t buf[4096];
4342281ac9SMark McLoughlin     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
4442281ac9SMark McLoughlin } NetSocketState;
4542281ac9SMark McLoughlin 
4642281ac9SMark McLoughlin typedef struct NetSocketListenState {
47*4e68f7a0SStefan Hajnoczi     NetClientState *peer;
4842281ac9SMark McLoughlin     char *model;
4942281ac9SMark McLoughlin     char *name;
5042281ac9SMark McLoughlin     int fd;
5142281ac9SMark McLoughlin } NetSocketListenState;
5242281ac9SMark McLoughlin 
5342281ac9SMark McLoughlin /* XXX: we consider we can send the whole packet without blocking */
54*4e68f7a0SStefan Hajnoczi static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t size)
5542281ac9SMark McLoughlin {
56564f63e3SMark McLoughlin     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
5742281ac9SMark McLoughlin     uint32_t len;
5842281ac9SMark McLoughlin     len = htonl(size);
5942281ac9SMark McLoughlin 
6042281ac9SMark McLoughlin     send_all(s->fd, (const uint8_t *)&len, sizeof(len));
6142281ac9SMark McLoughlin     return send_all(s->fd, buf, size);
6242281ac9SMark McLoughlin }
6342281ac9SMark McLoughlin 
64*4e68f7a0SStefan Hajnoczi static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
6542281ac9SMark McLoughlin {
66564f63e3SMark McLoughlin     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
6742281ac9SMark McLoughlin 
6842281ac9SMark McLoughlin     return sendto(s->fd, (const void *)buf, size, 0,
6942281ac9SMark McLoughlin                   (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
7042281ac9SMark McLoughlin }
7142281ac9SMark McLoughlin 
7242281ac9SMark McLoughlin static void net_socket_send(void *opaque)
7342281ac9SMark McLoughlin {
7442281ac9SMark McLoughlin     NetSocketState *s = opaque;
7542281ac9SMark McLoughlin     int size, err;
7642281ac9SMark McLoughlin     unsigned l;
7742281ac9SMark McLoughlin     uint8_t buf1[4096];
7842281ac9SMark McLoughlin     const uint8_t *buf;
7942281ac9SMark McLoughlin 
8000aa0040SBlue Swirl     size = qemu_recv(s->fd, buf1, sizeof(buf1), 0);
8142281ac9SMark McLoughlin     if (size < 0) {
8242281ac9SMark McLoughlin         err = socket_error();
8342281ac9SMark McLoughlin         if (err != EWOULDBLOCK)
8442281ac9SMark McLoughlin             goto eoc;
8542281ac9SMark McLoughlin     } else if (size == 0) {
8642281ac9SMark McLoughlin         /* end of connection */
8742281ac9SMark McLoughlin     eoc:
8842281ac9SMark McLoughlin         qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
8942281ac9SMark McLoughlin         closesocket(s->fd);
9042281ac9SMark McLoughlin         return;
9142281ac9SMark McLoughlin     }
9242281ac9SMark McLoughlin     buf = buf1;
9342281ac9SMark McLoughlin     while (size > 0) {
9442281ac9SMark McLoughlin         /* reassemble a packet from the network */
9542281ac9SMark McLoughlin         switch(s->state) {
9642281ac9SMark McLoughlin         case 0:
9742281ac9SMark McLoughlin             l = 4 - s->index;
9842281ac9SMark McLoughlin             if (l > size)
9942281ac9SMark McLoughlin                 l = size;
10042281ac9SMark McLoughlin             memcpy(s->buf + s->index, buf, l);
10142281ac9SMark McLoughlin             buf += l;
10242281ac9SMark McLoughlin             size -= l;
10342281ac9SMark McLoughlin             s->index += l;
10442281ac9SMark McLoughlin             if (s->index == 4) {
10542281ac9SMark McLoughlin                 /* got length */
10642281ac9SMark McLoughlin                 s->packet_len = ntohl(*(uint32_t *)s->buf);
10742281ac9SMark McLoughlin                 s->index = 0;
10842281ac9SMark McLoughlin                 s->state = 1;
10942281ac9SMark McLoughlin             }
11042281ac9SMark McLoughlin             break;
11142281ac9SMark McLoughlin         case 1:
11242281ac9SMark McLoughlin             l = s->packet_len - s->index;
11342281ac9SMark McLoughlin             if (l > size)
11442281ac9SMark McLoughlin                 l = size;
11542281ac9SMark McLoughlin             if (s->index + l <= sizeof(s->buf)) {
11642281ac9SMark McLoughlin                 memcpy(s->buf + s->index, buf, l);
11742281ac9SMark McLoughlin             } else {
11842281ac9SMark McLoughlin                 fprintf(stderr, "serious error: oversized packet received,"
11942281ac9SMark McLoughlin                     "connection terminated.\n");
12042281ac9SMark McLoughlin                 s->state = 0;
12142281ac9SMark McLoughlin                 goto eoc;
12242281ac9SMark McLoughlin             }
12342281ac9SMark McLoughlin 
12442281ac9SMark McLoughlin             s->index += l;
12542281ac9SMark McLoughlin             buf += l;
12642281ac9SMark McLoughlin             size -= l;
12742281ac9SMark McLoughlin             if (s->index >= s->packet_len) {
128564f63e3SMark McLoughlin                 qemu_send_packet(&s->nc, s->buf, s->packet_len);
12942281ac9SMark McLoughlin                 s->index = 0;
13042281ac9SMark McLoughlin                 s->state = 0;
13142281ac9SMark McLoughlin             }
13242281ac9SMark McLoughlin             break;
13342281ac9SMark McLoughlin         }
13442281ac9SMark McLoughlin     }
13542281ac9SMark McLoughlin }
13642281ac9SMark McLoughlin 
13742281ac9SMark McLoughlin static void net_socket_send_dgram(void *opaque)
13842281ac9SMark McLoughlin {
13942281ac9SMark McLoughlin     NetSocketState *s = opaque;
14042281ac9SMark McLoughlin     int size;
14142281ac9SMark McLoughlin 
14200aa0040SBlue Swirl     size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
14342281ac9SMark McLoughlin     if (size < 0)
14442281ac9SMark McLoughlin         return;
14542281ac9SMark McLoughlin     if (size == 0) {
14642281ac9SMark McLoughlin         /* end of connection */
14742281ac9SMark McLoughlin         qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
14842281ac9SMark McLoughlin         return;
14942281ac9SMark McLoughlin     }
150564f63e3SMark McLoughlin     qemu_send_packet(&s->nc, s->buf, size);
15142281ac9SMark McLoughlin }
15242281ac9SMark McLoughlin 
1533a75e74cSMike Ryan static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
15442281ac9SMark McLoughlin {
15542281ac9SMark McLoughlin     struct ip_mreq imr;
15642281ac9SMark McLoughlin     int fd;
15742281ac9SMark McLoughlin     int val, ret;
15823ddf2bbSBrad #ifdef __OpenBSD__
15923ddf2bbSBrad     unsigned char loop;
16023ddf2bbSBrad #else
16123ddf2bbSBrad     int loop;
16223ddf2bbSBrad #endif
16323ddf2bbSBrad 
16442281ac9SMark McLoughlin     if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
165842480d4SStefan Hajnoczi         fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) "
166842480d4SStefan Hajnoczi                 "does not contain a multicast address\n",
16742281ac9SMark McLoughlin                 inet_ntoa(mcastaddr->sin_addr),
16842281ac9SMark McLoughlin                 (int)ntohl(mcastaddr->sin_addr.s_addr));
16942281ac9SMark McLoughlin         return -1;
17042281ac9SMark McLoughlin 
17142281ac9SMark McLoughlin     }
17240ff6d7eSKevin Wolf     fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
17342281ac9SMark McLoughlin     if (fd < 0) {
17442281ac9SMark McLoughlin         perror("socket(PF_INET, SOCK_DGRAM)");
17542281ac9SMark McLoughlin         return -1;
17642281ac9SMark McLoughlin     }
17742281ac9SMark McLoughlin 
17842281ac9SMark McLoughlin     val = 1;
17942281ac9SMark McLoughlin     ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
18042281ac9SMark McLoughlin                    (const char *)&val, sizeof(val));
18142281ac9SMark McLoughlin     if (ret < 0) {
18242281ac9SMark McLoughlin         perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
18342281ac9SMark McLoughlin         goto fail;
18442281ac9SMark McLoughlin     }
18542281ac9SMark McLoughlin 
18642281ac9SMark McLoughlin     ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
18742281ac9SMark McLoughlin     if (ret < 0) {
18842281ac9SMark McLoughlin         perror("bind");
18942281ac9SMark McLoughlin         goto fail;
19042281ac9SMark McLoughlin     }
19142281ac9SMark McLoughlin 
19242281ac9SMark McLoughlin     /* Add host to multicast group */
19342281ac9SMark McLoughlin     imr.imr_multiaddr = mcastaddr->sin_addr;
1943a75e74cSMike Ryan     if (localaddr) {
1953a75e74cSMike Ryan         imr.imr_interface = *localaddr;
1963a75e74cSMike Ryan     } else {
19742281ac9SMark McLoughlin         imr.imr_interface.s_addr = htonl(INADDR_ANY);
1983a75e74cSMike Ryan     }
19942281ac9SMark McLoughlin 
20042281ac9SMark McLoughlin     ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
20142281ac9SMark McLoughlin                      (const char *)&imr, sizeof(struct ip_mreq));
20242281ac9SMark McLoughlin     if (ret < 0) {
20342281ac9SMark McLoughlin         perror("setsockopt(IP_ADD_MEMBERSHIP)");
20442281ac9SMark McLoughlin         goto fail;
20542281ac9SMark McLoughlin     }
20642281ac9SMark McLoughlin 
20742281ac9SMark McLoughlin     /* Force mcast msgs to loopback (eg. several QEMUs in same host */
20823ddf2bbSBrad     loop = 1;
20942281ac9SMark McLoughlin     ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
21023ddf2bbSBrad                    (const char *)&loop, sizeof(loop));
21142281ac9SMark McLoughlin     if (ret < 0) {
21242281ac9SMark McLoughlin         perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
21342281ac9SMark McLoughlin         goto fail;
21442281ac9SMark McLoughlin     }
21542281ac9SMark McLoughlin 
2163a75e74cSMike Ryan     /* If a bind address is given, only send packets from that address */
2173a75e74cSMike Ryan     if (localaddr != NULL) {
2184d22c6c2SBlue Swirl         ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
2194d22c6c2SBlue Swirl                          (const char *)localaddr, sizeof(*localaddr));
2203a75e74cSMike Ryan         if (ret < 0) {
2213a75e74cSMike Ryan             perror("setsockopt(IP_MULTICAST_IF)");
2223a75e74cSMike Ryan             goto fail;
2233a75e74cSMike Ryan         }
2243a75e74cSMike Ryan     }
2253a75e74cSMike Ryan 
22642281ac9SMark McLoughlin     socket_set_nonblock(fd);
22742281ac9SMark McLoughlin     return fd;
22842281ac9SMark McLoughlin fail:
22942281ac9SMark McLoughlin     if (fd >= 0)
23042281ac9SMark McLoughlin         closesocket(fd);
23142281ac9SMark McLoughlin     return -1;
23242281ac9SMark McLoughlin }
23342281ac9SMark McLoughlin 
234*4e68f7a0SStefan Hajnoczi static void net_socket_cleanup(NetClientState *nc)
23542281ac9SMark McLoughlin {
236564f63e3SMark McLoughlin     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
23742281ac9SMark McLoughlin     qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
23842281ac9SMark McLoughlin     close(s->fd);
23942281ac9SMark McLoughlin }
24042281ac9SMark McLoughlin 
241564f63e3SMark McLoughlin static NetClientInfo net_dgram_socket_info = {
2422be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
243564f63e3SMark McLoughlin     .size = sizeof(NetSocketState),
244564f63e3SMark McLoughlin     .receive = net_socket_receive_dgram,
245564f63e3SMark McLoughlin     .cleanup = net_socket_cleanup,
246564f63e3SMark McLoughlin };
247564f63e3SMark McLoughlin 
248*4e68f7a0SStefan Hajnoczi static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
24942281ac9SMark McLoughlin                                                 const char *model,
25042281ac9SMark McLoughlin                                                 const char *name,
25142281ac9SMark McLoughlin                                                 int fd, int is_connected)
25242281ac9SMark McLoughlin {
25342281ac9SMark McLoughlin     struct sockaddr_in saddr;
25442281ac9SMark McLoughlin     int newfd;
25542281ac9SMark McLoughlin     socklen_t saddr_len;
256*4e68f7a0SStefan Hajnoczi     NetClientState *nc;
25742281ac9SMark McLoughlin     NetSocketState *s;
25842281ac9SMark McLoughlin 
25942281ac9SMark McLoughlin     /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
26042281ac9SMark McLoughlin      * Because this may be "shared" socket from a "master" process, datagrams would be recv()
26142281ac9SMark McLoughlin      * by ONLY ONE process: we must "clone" this dgram socket --jjo
26242281ac9SMark McLoughlin      */
26342281ac9SMark McLoughlin 
26442281ac9SMark McLoughlin     if (is_connected) {
26542281ac9SMark McLoughlin         if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
26642281ac9SMark McLoughlin             /* must be bound */
26742281ac9SMark McLoughlin             if (saddr.sin_addr.s_addr == 0) {
268842480d4SStefan Hajnoczi                 fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, "
269842480d4SStefan Hajnoczi                         "cannot setup multicast dst addr\n", fd);
270e5d1fca0SStefan Hajnoczi                 goto err;
27142281ac9SMark McLoughlin             }
27242281ac9SMark McLoughlin             /* clone dgram socket */
2733a75e74cSMike Ryan             newfd = net_socket_mcast_create(&saddr, NULL);
27442281ac9SMark McLoughlin             if (newfd < 0) {
27542281ac9SMark McLoughlin                 /* error already reported by net_socket_mcast_create() */
276e5d1fca0SStefan Hajnoczi                 goto err;
27742281ac9SMark McLoughlin             }
27842281ac9SMark McLoughlin             /* clone newfd to fd, close newfd */
27942281ac9SMark McLoughlin             dup2(newfd, fd);
28042281ac9SMark McLoughlin             close(newfd);
28142281ac9SMark McLoughlin 
28242281ac9SMark McLoughlin         } else {
283842480d4SStefan Hajnoczi             fprintf(stderr,
284842480d4SStefan Hajnoczi                     "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
28542281ac9SMark McLoughlin                     fd, strerror(errno));
286e5d1fca0SStefan Hajnoczi             goto err;
28742281ac9SMark McLoughlin         }
28842281ac9SMark McLoughlin     }
28942281ac9SMark McLoughlin 
290ab5f3f84SStefan Hajnoczi     nc = qemu_new_net_client(&net_dgram_socket_info, peer, model, name);
291564f63e3SMark McLoughlin 
292564f63e3SMark McLoughlin     snprintf(nc->info_str, sizeof(nc->info_str),
293564f63e3SMark McLoughlin             "socket: fd=%d (%s mcast=%s:%d)",
294564f63e3SMark McLoughlin             fd, is_connected ? "cloned" : "",
295564f63e3SMark McLoughlin             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
296564f63e3SMark McLoughlin 
297564f63e3SMark McLoughlin     s = DO_UPCAST(NetSocketState, nc, nc);
298564f63e3SMark McLoughlin 
29942281ac9SMark McLoughlin     s->fd = fd;
30042281ac9SMark McLoughlin 
30142281ac9SMark McLoughlin     qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
30242281ac9SMark McLoughlin 
30342281ac9SMark McLoughlin     /* mcast: save bound address as dst */
30442281ac9SMark McLoughlin     if (is_connected) s->dgram_dst=saddr;
30542281ac9SMark McLoughlin 
30642281ac9SMark McLoughlin     return s;
307e5d1fca0SStefan Hajnoczi 
308e5d1fca0SStefan Hajnoczi err:
309e5d1fca0SStefan Hajnoczi     closesocket(fd);
310e5d1fca0SStefan Hajnoczi     return NULL;
31142281ac9SMark McLoughlin }
31242281ac9SMark McLoughlin 
31342281ac9SMark McLoughlin static void net_socket_connect(void *opaque)
31442281ac9SMark McLoughlin {
31542281ac9SMark McLoughlin     NetSocketState *s = opaque;
31642281ac9SMark McLoughlin     qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
31742281ac9SMark McLoughlin }
31842281ac9SMark McLoughlin 
319564f63e3SMark McLoughlin static NetClientInfo net_socket_info = {
3202be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
321564f63e3SMark McLoughlin     .size = sizeof(NetSocketState),
322564f63e3SMark McLoughlin     .receive = net_socket_receive,
323564f63e3SMark McLoughlin     .cleanup = net_socket_cleanup,
324564f63e3SMark McLoughlin };
325564f63e3SMark McLoughlin 
326*4e68f7a0SStefan Hajnoczi static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
32742281ac9SMark McLoughlin                                                  const char *model,
32842281ac9SMark McLoughlin                                                  const char *name,
32942281ac9SMark McLoughlin                                                  int fd, int is_connected)
33042281ac9SMark McLoughlin {
331*4e68f7a0SStefan Hajnoczi     NetClientState *nc;
33242281ac9SMark McLoughlin     NetSocketState *s;
333564f63e3SMark McLoughlin 
334ab5f3f84SStefan Hajnoczi     nc = qemu_new_net_client(&net_socket_info, peer, model, name);
335564f63e3SMark McLoughlin 
336564f63e3SMark McLoughlin     snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
337564f63e3SMark McLoughlin 
338564f63e3SMark McLoughlin     s = DO_UPCAST(NetSocketState, nc, nc);
339564f63e3SMark McLoughlin 
34042281ac9SMark McLoughlin     s->fd = fd;
341564f63e3SMark McLoughlin 
34242281ac9SMark McLoughlin     if (is_connected) {
34342281ac9SMark McLoughlin         net_socket_connect(s);
34442281ac9SMark McLoughlin     } else {
34542281ac9SMark McLoughlin         qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
34642281ac9SMark McLoughlin     }
34742281ac9SMark McLoughlin     return s;
34842281ac9SMark McLoughlin }
34942281ac9SMark McLoughlin 
350*4e68f7a0SStefan Hajnoczi static NetSocketState *net_socket_fd_init(NetClientState *peer,
35142281ac9SMark McLoughlin                                           const char *model, const char *name,
35242281ac9SMark McLoughlin                                           int fd, int is_connected)
35342281ac9SMark McLoughlin {
35442281ac9SMark McLoughlin     int so_type = -1, optlen=sizeof(so_type);
35542281ac9SMark McLoughlin 
35642281ac9SMark McLoughlin     if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
35742281ac9SMark McLoughlin         (socklen_t *)&optlen)< 0) {
358842480d4SStefan Hajnoczi         fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n",
359842480d4SStefan Hajnoczi                 fd);
360e5d1fca0SStefan Hajnoczi         closesocket(fd);
36142281ac9SMark McLoughlin         return NULL;
36242281ac9SMark McLoughlin     }
36342281ac9SMark McLoughlin     switch(so_type) {
36442281ac9SMark McLoughlin     case SOCK_DGRAM:
365d33d93b2SStefan Hajnoczi         return net_socket_fd_init_dgram(peer, model, name, fd, is_connected);
36642281ac9SMark McLoughlin     case SOCK_STREAM:
367d33d93b2SStefan Hajnoczi         return net_socket_fd_init_stream(peer, model, name, fd, is_connected);
36842281ac9SMark McLoughlin     default:
36942281ac9SMark McLoughlin         /* who knows ... this could be a eg. a pty, do warn and continue as stream */
37042281ac9SMark McLoughlin         fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
371d33d93b2SStefan Hajnoczi         return net_socket_fd_init_stream(peer, model, name, fd, is_connected);
37242281ac9SMark McLoughlin     }
37342281ac9SMark McLoughlin     return NULL;
37442281ac9SMark McLoughlin }
37542281ac9SMark McLoughlin 
37642281ac9SMark McLoughlin static void net_socket_accept(void *opaque)
37742281ac9SMark McLoughlin {
37842281ac9SMark McLoughlin     NetSocketListenState *s = opaque;
37942281ac9SMark McLoughlin     NetSocketState *s1;
38042281ac9SMark McLoughlin     struct sockaddr_in saddr;
38142281ac9SMark McLoughlin     socklen_t len;
38242281ac9SMark McLoughlin     int fd;
38342281ac9SMark McLoughlin 
38442281ac9SMark McLoughlin     for(;;) {
38542281ac9SMark McLoughlin         len = sizeof(saddr);
38640ff6d7eSKevin Wolf         fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len);
38742281ac9SMark McLoughlin         if (fd < 0 && errno != EINTR) {
38842281ac9SMark McLoughlin             return;
38942281ac9SMark McLoughlin         } else if (fd >= 0) {
39042281ac9SMark McLoughlin             break;
39142281ac9SMark McLoughlin         }
39242281ac9SMark McLoughlin     }
393d33d93b2SStefan Hajnoczi     s1 = net_socket_fd_init(s->peer, s->model, s->name, fd, 1);
394e5d1fca0SStefan Hajnoczi     if (s1) {
395564f63e3SMark McLoughlin         snprintf(s1->nc.info_str, sizeof(s1->nc.info_str),
39642281ac9SMark McLoughlin                  "socket: connection from %s:%d",
39742281ac9SMark McLoughlin                  inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
39842281ac9SMark McLoughlin     }
39942281ac9SMark McLoughlin }
40042281ac9SMark McLoughlin 
401*4e68f7a0SStefan Hajnoczi static int net_socket_listen_init(NetClientState *peer,
40242281ac9SMark McLoughlin                                   const char *model,
40342281ac9SMark McLoughlin                                   const char *name,
40442281ac9SMark McLoughlin                                   const char *host_str)
40542281ac9SMark McLoughlin {
40642281ac9SMark McLoughlin     NetSocketListenState *s;
40742281ac9SMark McLoughlin     int fd, val, ret;
40842281ac9SMark McLoughlin     struct sockaddr_in saddr;
40942281ac9SMark McLoughlin 
41042281ac9SMark McLoughlin     if (parse_host_port(&saddr, host_str) < 0)
41142281ac9SMark McLoughlin         return -1;
41242281ac9SMark McLoughlin 
4137267c094SAnthony Liguori     s = g_malloc0(sizeof(NetSocketListenState));
41442281ac9SMark McLoughlin 
41540ff6d7eSKevin Wolf     fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
41642281ac9SMark McLoughlin     if (fd < 0) {
41742281ac9SMark McLoughlin         perror("socket");
418c7ee8f68SZhi Hui Li         g_free(s);
41942281ac9SMark McLoughlin         return -1;
42042281ac9SMark McLoughlin     }
42142281ac9SMark McLoughlin     socket_set_nonblock(fd);
42242281ac9SMark McLoughlin 
42342281ac9SMark McLoughlin     /* allow fast reuse */
42442281ac9SMark McLoughlin     val = 1;
42542281ac9SMark McLoughlin     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
42642281ac9SMark McLoughlin 
42742281ac9SMark McLoughlin     ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
42842281ac9SMark McLoughlin     if (ret < 0) {
42942281ac9SMark McLoughlin         perror("bind");
430c7ee8f68SZhi Hui Li         g_free(s);
431a46667eaSPeter Maydell         closesocket(fd);
43242281ac9SMark McLoughlin         return -1;
43342281ac9SMark McLoughlin     }
43442281ac9SMark McLoughlin     ret = listen(fd, 0);
43542281ac9SMark McLoughlin     if (ret < 0) {
43642281ac9SMark McLoughlin         perror("listen");
437c7ee8f68SZhi Hui Li         g_free(s);
438a46667eaSPeter Maydell         closesocket(fd);
43942281ac9SMark McLoughlin         return -1;
44042281ac9SMark McLoughlin     }
441d33d93b2SStefan Hajnoczi     s->peer = peer;
4427267c094SAnthony Liguori     s->model = g_strdup(model);
4437267c094SAnthony Liguori     s->name = name ? g_strdup(name) : NULL;
44442281ac9SMark McLoughlin     s->fd = fd;
44542281ac9SMark McLoughlin     qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
44642281ac9SMark McLoughlin     return 0;
44742281ac9SMark McLoughlin }
44842281ac9SMark McLoughlin 
449*4e68f7a0SStefan Hajnoczi static int net_socket_connect_init(NetClientState *peer,
45042281ac9SMark McLoughlin                                    const char *model,
45142281ac9SMark McLoughlin                                    const char *name,
45242281ac9SMark McLoughlin                                    const char *host_str)
45342281ac9SMark McLoughlin {
45442281ac9SMark McLoughlin     NetSocketState *s;
45542281ac9SMark McLoughlin     int fd, connected, ret, err;
45642281ac9SMark McLoughlin     struct sockaddr_in saddr;
45742281ac9SMark McLoughlin 
45842281ac9SMark McLoughlin     if (parse_host_port(&saddr, host_str) < 0)
45942281ac9SMark McLoughlin         return -1;
46042281ac9SMark McLoughlin 
46140ff6d7eSKevin Wolf     fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
46242281ac9SMark McLoughlin     if (fd < 0) {
46342281ac9SMark McLoughlin         perror("socket");
46442281ac9SMark McLoughlin         return -1;
46542281ac9SMark McLoughlin     }
46642281ac9SMark McLoughlin     socket_set_nonblock(fd);
46742281ac9SMark McLoughlin 
46842281ac9SMark McLoughlin     connected = 0;
46942281ac9SMark McLoughlin     for(;;) {
47042281ac9SMark McLoughlin         ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
47142281ac9SMark McLoughlin         if (ret < 0) {
47242281ac9SMark McLoughlin             err = socket_error();
47342281ac9SMark McLoughlin             if (err == EINTR || err == EWOULDBLOCK) {
47442281ac9SMark McLoughlin             } else if (err == EINPROGRESS) {
47542281ac9SMark McLoughlin                 break;
47642281ac9SMark McLoughlin #ifdef _WIN32
477c7eb1f02SPavel Dovgaluk             } else if (err == WSAEALREADY || err == WSAEINVAL) {
47842281ac9SMark McLoughlin                 break;
47942281ac9SMark McLoughlin #endif
48042281ac9SMark McLoughlin             } else {
48142281ac9SMark McLoughlin                 perror("connect");
48242281ac9SMark McLoughlin                 closesocket(fd);
48342281ac9SMark McLoughlin                 return -1;
48442281ac9SMark McLoughlin             }
48542281ac9SMark McLoughlin         } else {
48642281ac9SMark McLoughlin             connected = 1;
48742281ac9SMark McLoughlin             break;
48842281ac9SMark McLoughlin         }
48942281ac9SMark McLoughlin     }
490d33d93b2SStefan Hajnoczi     s = net_socket_fd_init(peer, model, name, fd, connected);
49142281ac9SMark McLoughlin     if (!s)
49242281ac9SMark McLoughlin         return -1;
493564f63e3SMark McLoughlin     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
49442281ac9SMark McLoughlin              "socket: connect to %s:%d",
49542281ac9SMark McLoughlin              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
49642281ac9SMark McLoughlin     return 0;
49742281ac9SMark McLoughlin }
49842281ac9SMark McLoughlin 
499*4e68f7a0SStefan Hajnoczi static int net_socket_mcast_init(NetClientState *peer,
50042281ac9SMark McLoughlin                                  const char *model,
50142281ac9SMark McLoughlin                                  const char *name,
5023a75e74cSMike Ryan                                  const char *host_str,
5033a75e74cSMike Ryan                                  const char *localaddr_str)
50442281ac9SMark McLoughlin {
50542281ac9SMark McLoughlin     NetSocketState *s;
50642281ac9SMark McLoughlin     int fd;
50742281ac9SMark McLoughlin     struct sockaddr_in saddr;
5083a75e74cSMike Ryan     struct in_addr localaddr, *param_localaddr;
50942281ac9SMark McLoughlin 
51042281ac9SMark McLoughlin     if (parse_host_port(&saddr, host_str) < 0)
51142281ac9SMark McLoughlin         return -1;
51242281ac9SMark McLoughlin 
5133a75e74cSMike Ryan     if (localaddr_str != NULL) {
5143a75e74cSMike Ryan         if (inet_aton(localaddr_str, &localaddr) == 0)
5153a75e74cSMike Ryan             return -1;
5163a75e74cSMike Ryan         param_localaddr = &localaddr;
5173a75e74cSMike Ryan     } else {
5183a75e74cSMike Ryan         param_localaddr = NULL;
5193a75e74cSMike Ryan     }
52042281ac9SMark McLoughlin 
5213a75e74cSMike Ryan     fd = net_socket_mcast_create(&saddr, param_localaddr);
52242281ac9SMark McLoughlin     if (fd < 0)
52342281ac9SMark McLoughlin         return -1;
52442281ac9SMark McLoughlin 
525d33d93b2SStefan Hajnoczi     s = net_socket_fd_init(peer, model, name, fd, 0);
52642281ac9SMark McLoughlin     if (!s)
52742281ac9SMark McLoughlin         return -1;
52842281ac9SMark McLoughlin 
52942281ac9SMark McLoughlin     s->dgram_dst = saddr;
53042281ac9SMark McLoughlin 
531564f63e3SMark McLoughlin     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
53242281ac9SMark McLoughlin              "socket: mcast=%s:%d",
53342281ac9SMark McLoughlin              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
53442281ac9SMark McLoughlin     return 0;
53542281ac9SMark McLoughlin 
53642281ac9SMark McLoughlin }
53742281ac9SMark McLoughlin 
538*4e68f7a0SStefan Hajnoczi static int net_socket_udp_init(NetClientState *peer,
5390e0e7facSBenjamin                                  const char *model,
5400e0e7facSBenjamin                                  const char *name,
5410e0e7facSBenjamin                                  const char *rhost,
5420e0e7facSBenjamin                                  const char *lhost)
5430e0e7facSBenjamin {
5440e0e7facSBenjamin     NetSocketState *s;
5450e0e7facSBenjamin     int fd, val, ret;
5460e0e7facSBenjamin     struct sockaddr_in laddr, raddr;
5470e0e7facSBenjamin 
5480e0e7facSBenjamin     if (parse_host_port(&laddr, lhost) < 0) {
5490e0e7facSBenjamin         return -1;
5500e0e7facSBenjamin     }
5510e0e7facSBenjamin 
5520e0e7facSBenjamin     if (parse_host_port(&raddr, rhost) < 0) {
5530e0e7facSBenjamin         return -1;
5540e0e7facSBenjamin     }
5550e0e7facSBenjamin 
5560e0e7facSBenjamin     fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
5570e0e7facSBenjamin     if (fd < 0) {
5580e0e7facSBenjamin         perror("socket(PF_INET, SOCK_DGRAM)");
5590e0e7facSBenjamin         return -1;
5600e0e7facSBenjamin     }
5610e0e7facSBenjamin     val = 1;
5620e0e7facSBenjamin     ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
5630e0e7facSBenjamin                    (const char *)&val, sizeof(val));
5640e0e7facSBenjamin     if (ret < 0) {
5650e0e7facSBenjamin         perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
5660e0e7facSBenjamin         closesocket(fd);
5670e0e7facSBenjamin         return -1;
5680e0e7facSBenjamin     }
5690e0e7facSBenjamin     ret = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
5700e0e7facSBenjamin     if (ret < 0) {
5710e0e7facSBenjamin         perror("bind");
5720e0e7facSBenjamin         closesocket(fd);
5730e0e7facSBenjamin         return -1;
5740e0e7facSBenjamin     }
5750e0e7facSBenjamin 
576d33d93b2SStefan Hajnoczi     s = net_socket_fd_init(peer, model, name, fd, 0);
5770e0e7facSBenjamin     if (!s) {
5780e0e7facSBenjamin         return -1;
5790e0e7facSBenjamin     }
5800e0e7facSBenjamin 
5810e0e7facSBenjamin     s->dgram_dst = raddr;
5820e0e7facSBenjamin 
5830e0e7facSBenjamin     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
5840e0e7facSBenjamin              "socket: udp=%s:%d",
5850e0e7facSBenjamin              inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port));
5860e0e7facSBenjamin     return 0;
5870e0e7facSBenjamin }
5880e0e7facSBenjamin 
5891a0c0958SLaszlo Ersek int net_init_socket(const NetClientOptions *opts, const char *name,
590*4e68f7a0SStefan Hajnoczi                     NetClientState *peer)
59142281ac9SMark McLoughlin {
592bef8e8feSLaszlo Ersek     const NetdevSocketOptions *sock;
593bef8e8feSLaszlo Ersek 
594bef8e8feSLaszlo Ersek     assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
595bef8e8feSLaszlo Ersek     sock = opts->socket;
596bef8e8feSLaszlo Ersek 
597bef8e8feSLaszlo Ersek     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
598bef8e8feSLaszlo Ersek         sock->has_udp != 1) {
599bef8e8feSLaszlo Ersek         error_report("exactly one of fd=, listen=, connect=, mcast= or udp="
600bef8e8feSLaszlo Ersek                      " is required");
601bef8e8feSLaszlo Ersek         return -1;
602bef8e8feSLaszlo Ersek     }
603bef8e8feSLaszlo Ersek 
604bef8e8feSLaszlo Ersek     if (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) {
605bef8e8feSLaszlo Ersek         error_report("localaddr= is only valid with mcast= or udp=");
606bef8e8feSLaszlo Ersek         return -1;
607bef8e8feSLaszlo Ersek     }
608bef8e8feSLaszlo Ersek 
609bef8e8feSLaszlo Ersek     if (sock->has_fd) {
61042281ac9SMark McLoughlin         int fd;
61142281ac9SMark McLoughlin 
612bef8e8feSLaszlo Ersek         fd = net_handle_fd_param(cur_mon, sock->fd);
613d33d93b2SStefan Hajnoczi         if (fd == -1 || !net_socket_fd_init(peer, "socket", name, fd, 1)) {
61442281ac9SMark McLoughlin             return -1;
61542281ac9SMark McLoughlin         }
616bef8e8feSLaszlo Ersek         return 0;
61742281ac9SMark McLoughlin     }
61842281ac9SMark McLoughlin 
619bef8e8feSLaszlo Ersek     if (sock->has_listen) {
620d33d93b2SStefan Hajnoczi         if (net_socket_listen_init(peer, "socket", name, sock->listen) == -1) {
62142281ac9SMark McLoughlin             return -1;
62242281ac9SMark McLoughlin         }
623bef8e8feSLaszlo Ersek         return 0;
62442281ac9SMark McLoughlin     }
62542281ac9SMark McLoughlin 
626bef8e8feSLaszlo Ersek     if (sock->has_connect) {
627d33d93b2SStefan Hajnoczi         if (net_socket_connect_init(peer, "socket", name, sock->connect) ==
628bef8e8feSLaszlo Ersek             -1) {
62942281ac9SMark McLoughlin             return -1;
63042281ac9SMark McLoughlin         }
631bef8e8feSLaszlo Ersek         return 0;
63242281ac9SMark McLoughlin     }
63342281ac9SMark McLoughlin 
634bef8e8feSLaszlo Ersek     if (sock->has_mcast) {
635bef8e8feSLaszlo Ersek         /* if sock->localaddr is missing, it has been initialized to "all bits
636bef8e8feSLaszlo Ersek          * zero" */
637d33d93b2SStefan Hajnoczi         if (net_socket_mcast_init(peer, "socket", name, sock->mcast,
638bef8e8feSLaszlo Ersek             sock->localaddr) == -1) {
63942281ac9SMark McLoughlin             return -1;
64042281ac9SMark McLoughlin         }
641bef8e8feSLaszlo Ersek         return 0;
64242281ac9SMark McLoughlin     }
64342281ac9SMark McLoughlin 
644bef8e8feSLaszlo Ersek     assert(sock->has_udp);
645bef8e8feSLaszlo Ersek     if (!sock->has_localaddr) {
6460e0e7facSBenjamin         error_report("localaddr= is mandatory with udp=");
6470e0e7facSBenjamin         return -1;
6480e0e7facSBenjamin     }
649d33d93b2SStefan Hajnoczi     if (net_socket_udp_init(peer, "udp", name, sock->udp, sock->localaddr) ==
650bef8e8feSLaszlo Ersek         -1) {
6510e0e7facSBenjamin         return -1;
6520e0e7facSBenjamin     }
65342281ac9SMark McLoughlin     return 0;
65442281ac9SMark McLoughlin }
655