xref: /openbmc/qemu/net/dgram.c (revision 284c52eec2d0a1b9c47f06c3eee46762c5fc0915)
15166fe0aSLaurent Vivier /*
25166fe0aSLaurent Vivier  * QEMU System Emulator
35166fe0aSLaurent Vivier  *
45166fe0aSLaurent Vivier  * Copyright (c) 2003-2008 Fabrice Bellard
55166fe0aSLaurent Vivier  * Copyright (c) 2022 Red Hat, Inc.
65166fe0aSLaurent Vivier  *
75166fe0aSLaurent Vivier  * Permission is hereby granted, free of charge, to any person obtaining a copy
85166fe0aSLaurent Vivier  * of this software and associated documentation files (the "Software"), to deal
95166fe0aSLaurent Vivier  * in the Software without restriction, including without limitation the rights
105166fe0aSLaurent Vivier  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
115166fe0aSLaurent Vivier  * copies of the Software, and to permit persons to whom the Software is
125166fe0aSLaurent Vivier  * furnished to do so, subject to the following conditions:
135166fe0aSLaurent Vivier  *
145166fe0aSLaurent Vivier  * The above copyright notice and this permission notice shall be included in
155166fe0aSLaurent Vivier  * all copies or substantial portions of the Software.
165166fe0aSLaurent Vivier  *
175166fe0aSLaurent Vivier  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
185166fe0aSLaurent Vivier  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
195166fe0aSLaurent Vivier  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
205166fe0aSLaurent Vivier  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
215166fe0aSLaurent Vivier  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
225166fe0aSLaurent Vivier  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
235166fe0aSLaurent Vivier  * THE SOFTWARE.
245166fe0aSLaurent Vivier  */
255166fe0aSLaurent Vivier 
265166fe0aSLaurent Vivier #include "qemu/osdep.h"
275166fe0aSLaurent Vivier 
285166fe0aSLaurent Vivier #include "net/net.h"
295166fe0aSLaurent Vivier #include "clients.h"
305166fe0aSLaurent Vivier #include "monitor/monitor.h"
315166fe0aSLaurent Vivier #include "qapi/error.h"
325166fe0aSLaurent Vivier #include "qemu/error-report.h"
335166fe0aSLaurent Vivier #include "qemu/option.h"
345166fe0aSLaurent Vivier #include "qemu/sockets.h"
355166fe0aSLaurent Vivier #include "qemu/iov.h"
365166fe0aSLaurent Vivier #include "qemu/main-loop.h"
375166fe0aSLaurent Vivier #include "qemu/cutils.h"
385166fe0aSLaurent Vivier 
395166fe0aSLaurent Vivier typedef struct NetDgramState {
405166fe0aSLaurent Vivier     NetClientState nc;
415166fe0aSLaurent Vivier     int fd;
425166fe0aSLaurent Vivier     SocketReadState rs;
435166fe0aSLaurent Vivier     bool read_poll;               /* waiting to receive data? */
445166fe0aSLaurent Vivier     bool write_poll;              /* waiting to transmit data? */
457c1f0c33SLaurent Vivier     /* contains destination iff connectionless */
467c1f0c33SLaurent Vivier     struct sockaddr *dest_addr;
477c1f0c33SLaurent Vivier     socklen_t dest_len;
485166fe0aSLaurent Vivier } NetDgramState;
495166fe0aSLaurent Vivier 
505166fe0aSLaurent Vivier static void net_dgram_send(void *opaque);
515166fe0aSLaurent Vivier static void net_dgram_writable(void *opaque);
525166fe0aSLaurent Vivier 
net_dgram_update_fd_handler(NetDgramState * s)535166fe0aSLaurent Vivier static void net_dgram_update_fd_handler(NetDgramState *s)
545166fe0aSLaurent Vivier {
555166fe0aSLaurent Vivier     qemu_set_fd_handler(s->fd,
565166fe0aSLaurent Vivier                         s->read_poll ? net_dgram_send : NULL,
575166fe0aSLaurent Vivier                         s->write_poll ? net_dgram_writable : NULL,
585166fe0aSLaurent Vivier                         s);
595166fe0aSLaurent Vivier }
605166fe0aSLaurent Vivier 
net_dgram_read_poll(NetDgramState * s,bool enable)615166fe0aSLaurent Vivier static void net_dgram_read_poll(NetDgramState *s, bool enable)
625166fe0aSLaurent Vivier {
635166fe0aSLaurent Vivier     s->read_poll = enable;
645166fe0aSLaurent Vivier     net_dgram_update_fd_handler(s);
655166fe0aSLaurent Vivier }
665166fe0aSLaurent Vivier 
net_dgram_write_poll(NetDgramState * s,bool enable)675166fe0aSLaurent Vivier static void net_dgram_write_poll(NetDgramState *s, bool enable)
685166fe0aSLaurent Vivier {
695166fe0aSLaurent Vivier     s->write_poll = enable;
705166fe0aSLaurent Vivier     net_dgram_update_fd_handler(s);
715166fe0aSLaurent Vivier }
725166fe0aSLaurent Vivier 
net_dgram_writable(void * opaque)735166fe0aSLaurent Vivier static void net_dgram_writable(void *opaque)
745166fe0aSLaurent Vivier {
755166fe0aSLaurent Vivier     NetDgramState *s = opaque;
765166fe0aSLaurent Vivier 
775166fe0aSLaurent Vivier     net_dgram_write_poll(s, false);
785166fe0aSLaurent Vivier 
795166fe0aSLaurent Vivier     qemu_flush_queued_packets(&s->nc);
805166fe0aSLaurent Vivier }
815166fe0aSLaurent Vivier 
net_dgram_receive(NetClientState * nc,const uint8_t * buf,size_t size)825166fe0aSLaurent Vivier static ssize_t net_dgram_receive(NetClientState *nc,
835166fe0aSLaurent Vivier                                  const uint8_t *buf, size_t size)
845166fe0aSLaurent Vivier {
855166fe0aSLaurent Vivier     NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc);
865166fe0aSLaurent Vivier     ssize_t ret;
875166fe0aSLaurent Vivier 
885166fe0aSLaurent Vivier     do {
897c1f0c33SLaurent Vivier         if (s->dest_addr) {
907c1f0c33SLaurent Vivier             ret = sendto(s->fd, buf, size, 0, s->dest_addr, s->dest_len);
915166fe0aSLaurent Vivier         } else {
925166fe0aSLaurent Vivier             ret = send(s->fd, buf, size, 0);
935166fe0aSLaurent Vivier         }
945166fe0aSLaurent Vivier     } while (ret == -1 && errno == EINTR);
955166fe0aSLaurent Vivier 
965166fe0aSLaurent Vivier     if (ret == -1 && errno == EAGAIN) {
975166fe0aSLaurent Vivier         net_dgram_write_poll(s, true);
985166fe0aSLaurent Vivier         return 0;
995166fe0aSLaurent Vivier     }
1005166fe0aSLaurent Vivier     return ret;
1015166fe0aSLaurent Vivier }
1025166fe0aSLaurent Vivier 
net_dgram_send_completed(NetClientState * nc,ssize_t len)1035166fe0aSLaurent Vivier static void net_dgram_send_completed(NetClientState *nc, ssize_t len)
1045166fe0aSLaurent Vivier {
1055166fe0aSLaurent Vivier     NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc);
1065166fe0aSLaurent Vivier 
1075166fe0aSLaurent Vivier     if (!s->read_poll) {
1085166fe0aSLaurent Vivier         net_dgram_read_poll(s, true);
1095166fe0aSLaurent Vivier     }
1105166fe0aSLaurent Vivier }
1115166fe0aSLaurent Vivier 
net_dgram_rs_finalize(SocketReadState * rs)1125166fe0aSLaurent Vivier static void net_dgram_rs_finalize(SocketReadState *rs)
1135166fe0aSLaurent Vivier {
1145166fe0aSLaurent Vivier     NetDgramState *s = container_of(rs, NetDgramState, rs);
1155166fe0aSLaurent Vivier 
1165166fe0aSLaurent Vivier     if (qemu_send_packet_async(&s->nc, rs->buf,
1175166fe0aSLaurent Vivier                                rs->packet_len,
1185166fe0aSLaurent Vivier                                net_dgram_send_completed) == 0) {
1195166fe0aSLaurent Vivier         net_dgram_read_poll(s, false);
1205166fe0aSLaurent Vivier     }
1215166fe0aSLaurent Vivier }
1225166fe0aSLaurent Vivier 
net_dgram_send(void * opaque)1235166fe0aSLaurent Vivier static void net_dgram_send(void *opaque)
1245166fe0aSLaurent Vivier {
1255166fe0aSLaurent Vivier     NetDgramState *s = opaque;
1265166fe0aSLaurent Vivier     int size;
1275166fe0aSLaurent Vivier 
1285166fe0aSLaurent Vivier     size = recv(s->fd, s->rs.buf, sizeof(s->rs.buf), 0);
1295166fe0aSLaurent Vivier     if (size < 0) {
1305166fe0aSLaurent Vivier         return;
1315166fe0aSLaurent Vivier     }
1325166fe0aSLaurent Vivier     if (size == 0) {
1335166fe0aSLaurent Vivier         /* end of connection */
1345166fe0aSLaurent Vivier         net_dgram_read_poll(s, false);
1355166fe0aSLaurent Vivier         net_dgram_write_poll(s, false);
1365166fe0aSLaurent Vivier         return;
1375166fe0aSLaurent Vivier     }
1385166fe0aSLaurent Vivier     if (qemu_send_packet_async(&s->nc, s->rs.buf, size,
1395166fe0aSLaurent Vivier                                net_dgram_send_completed) == 0) {
1405166fe0aSLaurent Vivier         net_dgram_read_poll(s, false);
1415166fe0aSLaurent Vivier     }
1425166fe0aSLaurent Vivier }
1435166fe0aSLaurent Vivier 
net_dgram_mcast_create(struct sockaddr_in * mcastaddr,struct in_addr * localaddr,Error ** errp)1445166fe0aSLaurent Vivier static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr,
1455166fe0aSLaurent Vivier                                   struct in_addr *localaddr,
1465166fe0aSLaurent Vivier                                   Error **errp)
1475166fe0aSLaurent Vivier {
1485166fe0aSLaurent Vivier     struct ip_mreq imr;
1495166fe0aSLaurent Vivier     int fd;
1505166fe0aSLaurent Vivier     int val, ret;
1515166fe0aSLaurent Vivier #ifdef __OpenBSD__
1525166fe0aSLaurent Vivier     unsigned char loop;
1535166fe0aSLaurent Vivier #else
1545166fe0aSLaurent Vivier     int loop;
1555166fe0aSLaurent Vivier #endif
1565166fe0aSLaurent Vivier 
1575166fe0aSLaurent Vivier     if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
1585166fe0aSLaurent Vivier         error_setg(errp, "specified mcastaddr %s (0x%08x) "
1595166fe0aSLaurent Vivier                    "does not contain a multicast address",
1605166fe0aSLaurent Vivier                    inet_ntoa(mcastaddr->sin_addr),
1615166fe0aSLaurent Vivier                    (int)ntohl(mcastaddr->sin_addr.s_addr));
1625166fe0aSLaurent Vivier         return -1;
1635166fe0aSLaurent Vivier     }
1645166fe0aSLaurent Vivier 
1655166fe0aSLaurent Vivier     fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
1665166fe0aSLaurent Vivier     if (fd < 0) {
1675166fe0aSLaurent Vivier         error_setg_errno(errp, errno, "can't create datagram socket");
1685166fe0aSLaurent Vivier         return -1;
1695166fe0aSLaurent Vivier     }
1705166fe0aSLaurent Vivier 
1715166fe0aSLaurent Vivier     /*
1725166fe0aSLaurent Vivier      * Allow multiple sockets to bind the same multicast ip and port by setting
1735166fe0aSLaurent Vivier      * SO_REUSEADDR. This is the only situation where SO_REUSEADDR should be set
1745166fe0aSLaurent Vivier      * on windows. Use socket_set_fast_reuse otherwise as it sets SO_REUSEADDR
1755166fe0aSLaurent Vivier      * only on posix systems.
1765166fe0aSLaurent Vivier      */
1775166fe0aSLaurent Vivier     val = 1;
1785166fe0aSLaurent Vivier     ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
1795166fe0aSLaurent Vivier     if (ret < 0) {
1805166fe0aSLaurent Vivier         error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR");
1815166fe0aSLaurent Vivier         goto fail;
1825166fe0aSLaurent Vivier     }
1835166fe0aSLaurent Vivier 
1845166fe0aSLaurent Vivier     ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
1855166fe0aSLaurent Vivier     if (ret < 0) {
1865166fe0aSLaurent Vivier         error_setg_errno(errp, errno, "can't bind ip=%s to socket",
1875166fe0aSLaurent Vivier                          inet_ntoa(mcastaddr->sin_addr));
1885166fe0aSLaurent Vivier         goto fail;
1895166fe0aSLaurent Vivier     }
1905166fe0aSLaurent Vivier 
1915166fe0aSLaurent Vivier     /* Add host to multicast group */
1925166fe0aSLaurent Vivier     imr.imr_multiaddr = mcastaddr->sin_addr;
1935166fe0aSLaurent Vivier     if (localaddr) {
1945166fe0aSLaurent Vivier         imr.imr_interface = *localaddr;
1955166fe0aSLaurent Vivier     } else {
1965166fe0aSLaurent Vivier         imr.imr_interface.s_addr = htonl(INADDR_ANY);
1975166fe0aSLaurent Vivier     }
1985166fe0aSLaurent Vivier 
1995166fe0aSLaurent Vivier     ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
2005166fe0aSLaurent Vivier                      &imr, sizeof(struct ip_mreq));
2015166fe0aSLaurent Vivier     if (ret < 0) {
2025166fe0aSLaurent Vivier         error_setg_errno(errp, errno,
2035166fe0aSLaurent Vivier                          "can't add socket to multicast group %s",
2045166fe0aSLaurent Vivier                          inet_ntoa(imr.imr_multiaddr));
2055166fe0aSLaurent Vivier         goto fail;
2065166fe0aSLaurent Vivier     }
2075166fe0aSLaurent Vivier 
2085166fe0aSLaurent Vivier     /* Force mcast msgs to loopback (eg. several QEMUs in same host */
2095166fe0aSLaurent Vivier     loop = 1;
2105166fe0aSLaurent Vivier     ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
2115166fe0aSLaurent Vivier                      &loop, sizeof(loop));
2125166fe0aSLaurent Vivier     if (ret < 0) {
2135166fe0aSLaurent Vivier         error_setg_errno(errp, errno,
2145166fe0aSLaurent Vivier                          "can't force multicast message to loopback");
2155166fe0aSLaurent Vivier         goto fail;
2165166fe0aSLaurent Vivier     }
2175166fe0aSLaurent Vivier 
2185166fe0aSLaurent Vivier     /* If a bind address is given, only send packets from that address */
2195166fe0aSLaurent Vivier     if (localaddr != NULL) {
2205166fe0aSLaurent Vivier         ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
2215166fe0aSLaurent Vivier                          localaddr, sizeof(*localaddr));
2225166fe0aSLaurent Vivier         if (ret < 0) {
2235166fe0aSLaurent Vivier             error_setg_errno(errp, errno,
2245166fe0aSLaurent Vivier                              "can't set the default network send interface");
2255166fe0aSLaurent Vivier             goto fail;
2265166fe0aSLaurent Vivier         }
2275166fe0aSLaurent Vivier     }
2285166fe0aSLaurent Vivier 
2295166fe0aSLaurent Vivier     qemu_socket_set_nonblock(fd);
2305166fe0aSLaurent Vivier     return fd;
2315166fe0aSLaurent Vivier fail:
2325166fe0aSLaurent Vivier     if (fd >= 0) {
233*25657fc6SMarc-André Lureau         close(fd);
2345166fe0aSLaurent Vivier     }
2355166fe0aSLaurent Vivier     return -1;
2365166fe0aSLaurent Vivier }
2375166fe0aSLaurent Vivier 
net_dgram_cleanup(NetClientState * nc)2385166fe0aSLaurent Vivier static void net_dgram_cleanup(NetClientState *nc)
2395166fe0aSLaurent Vivier {
2405166fe0aSLaurent Vivier     NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc);
2415166fe0aSLaurent Vivier     if (s->fd != -1) {
2425166fe0aSLaurent Vivier         net_dgram_read_poll(s, false);
2435166fe0aSLaurent Vivier         net_dgram_write_poll(s, false);
2445166fe0aSLaurent Vivier         close(s->fd);
2455166fe0aSLaurent Vivier         s->fd = -1;
2465166fe0aSLaurent Vivier     }
2477c1f0c33SLaurent Vivier     g_free(s->dest_addr);
2487c1f0c33SLaurent Vivier     s->dest_addr = NULL;
2497c1f0c33SLaurent Vivier     s->dest_len = 0;
2505166fe0aSLaurent Vivier }
2515166fe0aSLaurent Vivier 
2525166fe0aSLaurent Vivier static NetClientInfo net_dgram_socket_info = {
2535166fe0aSLaurent Vivier     .type = NET_CLIENT_DRIVER_DGRAM,
2545166fe0aSLaurent Vivier     .size = sizeof(NetDgramState),
2555166fe0aSLaurent Vivier     .receive = net_dgram_receive,
2565166fe0aSLaurent Vivier     .cleanup = net_dgram_cleanup,
2575166fe0aSLaurent Vivier };
2585166fe0aSLaurent Vivier 
net_dgram_fd_init(NetClientState * peer,const char * model,const char * name,int fd,Error ** errp)2595166fe0aSLaurent Vivier static NetDgramState *net_dgram_fd_init(NetClientState *peer,
2605166fe0aSLaurent Vivier                                         const char *model,
2615166fe0aSLaurent Vivier                                         const char *name,
2628ecc7f40SLaurent Vivier                                         int fd,
2635166fe0aSLaurent Vivier                                         Error **errp)
2645166fe0aSLaurent Vivier {
2655166fe0aSLaurent Vivier     NetClientState *nc;
2665166fe0aSLaurent Vivier     NetDgramState *s;
2675166fe0aSLaurent Vivier 
2685166fe0aSLaurent Vivier     nc = qemu_new_net_client(&net_dgram_socket_info, peer, model, name);
2695166fe0aSLaurent Vivier 
2705166fe0aSLaurent Vivier     s = DO_UPCAST(NetDgramState, nc, nc);
2715166fe0aSLaurent Vivier 
2725166fe0aSLaurent Vivier     s->fd = fd;
2735166fe0aSLaurent Vivier     net_socket_rs_init(&s->rs, net_dgram_rs_finalize, false);
2745166fe0aSLaurent Vivier     net_dgram_read_poll(s, true);
2755166fe0aSLaurent Vivier 
2765166fe0aSLaurent Vivier     return s;
2775166fe0aSLaurent Vivier }
2785166fe0aSLaurent Vivier 
net_dgram_mcast_init(NetClientState * peer,const char * model,const char * name,SocketAddress * remote,SocketAddress * local,Error ** errp)2795166fe0aSLaurent Vivier static int net_dgram_mcast_init(NetClientState *peer,
2805166fe0aSLaurent Vivier                                 const char *model,
2815166fe0aSLaurent Vivier                                 const char *name,
2825166fe0aSLaurent Vivier                                 SocketAddress *remote,
2835166fe0aSLaurent Vivier                                 SocketAddress *local,
2845166fe0aSLaurent Vivier                                 Error **errp)
2855166fe0aSLaurent Vivier {
2865166fe0aSLaurent Vivier     NetDgramState *s;
2875166fe0aSLaurent Vivier     int fd, ret;
2887c1f0c33SLaurent Vivier     struct sockaddr_in *saddr;
2895166fe0aSLaurent Vivier 
2905166fe0aSLaurent Vivier     if (remote->type != SOCKET_ADDRESS_TYPE_INET) {
2915166fe0aSLaurent Vivier         error_setg(errp, "multicast only support inet type");
2925166fe0aSLaurent Vivier         return -1;
2935166fe0aSLaurent Vivier     }
2945166fe0aSLaurent Vivier 
2957c1f0c33SLaurent Vivier     saddr = g_new(struct sockaddr_in, 1);
2967c1f0c33SLaurent Vivier     if (convert_host_port(saddr, remote->u.inet.host, remote->u.inet.port,
2975166fe0aSLaurent Vivier                           errp) < 0) {
2987c1f0c33SLaurent Vivier         g_free(saddr);
2995166fe0aSLaurent Vivier         return -1;
3005166fe0aSLaurent Vivier     }
3015166fe0aSLaurent Vivier 
3025166fe0aSLaurent Vivier     if (!local) {
3037c1f0c33SLaurent Vivier         fd = net_dgram_mcast_create(saddr, NULL, errp);
3045166fe0aSLaurent Vivier         if (fd < 0) {
3057c1f0c33SLaurent Vivier             g_free(saddr);
3065166fe0aSLaurent Vivier             return -1;
3075166fe0aSLaurent Vivier         }
3085166fe0aSLaurent Vivier     } else {
3095166fe0aSLaurent Vivier         switch (local->type) {
3105166fe0aSLaurent Vivier         case SOCKET_ADDRESS_TYPE_INET: {
3115166fe0aSLaurent Vivier             struct in_addr localaddr;
3125166fe0aSLaurent Vivier 
3135166fe0aSLaurent Vivier             if (inet_aton(local->u.inet.host, &localaddr) == 0) {
3147c1f0c33SLaurent Vivier                 g_free(saddr);
3155166fe0aSLaurent Vivier                 error_setg(errp, "localaddr '%s' is not a valid IPv4 address",
3165166fe0aSLaurent Vivier                            local->u.inet.host);
3175166fe0aSLaurent Vivier                 return -1;
3185166fe0aSLaurent Vivier             }
3195166fe0aSLaurent Vivier 
3207c1f0c33SLaurent Vivier             fd = net_dgram_mcast_create(saddr, &localaddr, errp);
3215166fe0aSLaurent Vivier             if (fd < 0) {
3227c1f0c33SLaurent Vivier                 g_free(saddr);
3235166fe0aSLaurent Vivier                 return -1;
3245166fe0aSLaurent Vivier             }
3255166fe0aSLaurent Vivier             break;
3265166fe0aSLaurent Vivier         }
3278ecc7f40SLaurent Vivier         case SOCKET_ADDRESS_TYPE_FD: {
3288ecc7f40SLaurent Vivier             int newfd;
3298ecc7f40SLaurent Vivier 
3305166fe0aSLaurent Vivier             fd = monitor_fd_param(monitor_cur(), local->u.fd.str, errp);
3315166fe0aSLaurent Vivier             if (fd == -1) {
3327c1f0c33SLaurent Vivier                 g_free(saddr);
3335166fe0aSLaurent Vivier                 return -1;
3345166fe0aSLaurent Vivier             }
3355166fe0aSLaurent Vivier             ret = qemu_socket_try_set_nonblock(fd);
3365166fe0aSLaurent Vivier             if (ret < 0) {
3377c1f0c33SLaurent Vivier                 g_free(saddr);
3385166fe0aSLaurent Vivier                 error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
3395166fe0aSLaurent Vivier                                  name, fd);
3405166fe0aSLaurent Vivier                 return -1;
3415166fe0aSLaurent Vivier             }
3428ecc7f40SLaurent Vivier 
3438ecc7f40SLaurent Vivier             /*
3448ecc7f40SLaurent Vivier              * fd passed: multicast: "learn" dest_addr address from bound
3458ecc7f40SLaurent Vivier              * address and save it. Because this may be "shared" socket from a
3468ecc7f40SLaurent Vivier              * "master" process, datagrams would be recv() by ONLY ONE process:
3478ecc7f40SLaurent Vivier              * we must "clone" this dgram socket --jjo
3488ecc7f40SLaurent Vivier              */
3498ecc7f40SLaurent Vivier 
3508ecc7f40SLaurent Vivier             saddr = g_new(struct sockaddr_in, 1);
3518ecc7f40SLaurent Vivier 
3528ecc7f40SLaurent Vivier             if (convert_host_port(saddr, local->u.inet.host, local->u.inet.port,
3538ecc7f40SLaurent Vivier                                   errp) < 0) {
3548ecc7f40SLaurent Vivier                 g_free(saddr);
355*25657fc6SMarc-André Lureau                 close(fd);
3568ecc7f40SLaurent Vivier                 return -1;
3578ecc7f40SLaurent Vivier             }
3588ecc7f40SLaurent Vivier 
3598ecc7f40SLaurent Vivier             /* must be bound */
3608ecc7f40SLaurent Vivier             if (saddr->sin_addr.s_addr == 0) {
3618ecc7f40SLaurent Vivier                 error_setg(errp, "can't setup multicast destination address");
3628ecc7f40SLaurent Vivier                 g_free(saddr);
363*25657fc6SMarc-André Lureau                 close(fd);
3648ecc7f40SLaurent Vivier                 return -1;
3658ecc7f40SLaurent Vivier             }
3668ecc7f40SLaurent Vivier             /* clone dgram socket */
3678ecc7f40SLaurent Vivier             newfd = net_dgram_mcast_create(saddr, NULL, errp);
3688ecc7f40SLaurent Vivier             if (newfd < 0) {
3698ecc7f40SLaurent Vivier                 g_free(saddr);
370*25657fc6SMarc-André Lureau                 close(fd);
3718ecc7f40SLaurent Vivier                 return -1;
3728ecc7f40SLaurent Vivier             }
3738ecc7f40SLaurent Vivier             /* clone newfd to fd, close newfd */
3748ecc7f40SLaurent Vivier             dup2(newfd, fd);
3758ecc7f40SLaurent Vivier             close(newfd);
3765166fe0aSLaurent Vivier             break;
3778ecc7f40SLaurent Vivier         }
3785166fe0aSLaurent Vivier         default:
3797c1f0c33SLaurent Vivier             g_free(saddr);
3805166fe0aSLaurent Vivier             error_setg(errp, "only support inet or fd type for local");
3815166fe0aSLaurent Vivier             return -1;
3825166fe0aSLaurent Vivier         }
3835166fe0aSLaurent Vivier     }
3845166fe0aSLaurent Vivier 
3858ecc7f40SLaurent Vivier     s = net_dgram_fd_init(peer, model, name, fd, errp);
3865166fe0aSLaurent Vivier     if (!s) {
3877c1f0c33SLaurent Vivier         g_free(saddr);
3885166fe0aSLaurent Vivier         return -1;
3895166fe0aSLaurent Vivier     }
3905166fe0aSLaurent Vivier 
3917c1f0c33SLaurent Vivier     g_assert(s->dest_addr == NULL);
3927c1f0c33SLaurent Vivier     s->dest_addr = (struct sockaddr *)saddr;
3937c1f0c33SLaurent Vivier     s->dest_len = sizeof(*saddr);
3945166fe0aSLaurent Vivier 
3958ecc7f40SLaurent Vivier     if (!local) {
3968ecc7f40SLaurent Vivier         qemu_set_info_str(&s->nc, "mcast=%s:%d",
3978ecc7f40SLaurent Vivier                           inet_ntoa(saddr->sin_addr),
3987c1f0c33SLaurent Vivier                           ntohs(saddr->sin_port));
3998ecc7f40SLaurent Vivier     } else {
4008ecc7f40SLaurent Vivier         switch (local->type) {
4018ecc7f40SLaurent Vivier         case SOCKET_ADDRESS_TYPE_INET:
4028ecc7f40SLaurent Vivier             qemu_set_info_str(&s->nc, "mcast=%s:%d",
4038ecc7f40SLaurent Vivier                               inet_ntoa(saddr->sin_addr),
4048ecc7f40SLaurent Vivier                               ntohs(saddr->sin_port));
4058ecc7f40SLaurent Vivier             break;
4068ecc7f40SLaurent Vivier         case SOCKET_ADDRESS_TYPE_FD:
4078ecc7f40SLaurent Vivier             qemu_set_info_str(&s->nc, "fd=%d (cloned mcast=%s:%d)",
4088ecc7f40SLaurent Vivier                               fd, inet_ntoa(saddr->sin_addr),
4098ecc7f40SLaurent Vivier                               ntohs(saddr->sin_port));
4108ecc7f40SLaurent Vivier             break;
4118ecc7f40SLaurent Vivier         default:
4128ecc7f40SLaurent Vivier             g_assert_not_reached();
4138ecc7f40SLaurent Vivier         }
4148ecc7f40SLaurent Vivier     }
4157c1f0c33SLaurent Vivier 
4165166fe0aSLaurent Vivier     return 0;
4175166fe0aSLaurent Vivier 
4185166fe0aSLaurent Vivier }
4195166fe0aSLaurent Vivier 
4205166fe0aSLaurent Vivier 
net_init_dgram(const Netdev * netdev,const char * name,NetClientState * peer,Error ** errp)4215166fe0aSLaurent Vivier int net_init_dgram(const Netdev *netdev, const char *name,
4225166fe0aSLaurent Vivier                    NetClientState *peer, Error **errp)
4235166fe0aSLaurent Vivier {
4245166fe0aSLaurent Vivier     NetDgramState *s;
4255166fe0aSLaurent Vivier     int fd, ret;
4265166fe0aSLaurent Vivier     SocketAddress *remote, *local;
4277c1f0c33SLaurent Vivier     struct sockaddr *dest_addr;
4287c1f0c33SLaurent Vivier     struct sockaddr_in laddr_in, raddr_in;
429784e7a25SLaurent Vivier     struct sockaddr_un laddr_un, raddr_un;
4307c1f0c33SLaurent Vivier     socklen_t dest_len;
4315166fe0aSLaurent Vivier 
4325166fe0aSLaurent Vivier     assert(netdev->type == NET_CLIENT_DRIVER_DGRAM);
4335166fe0aSLaurent Vivier 
4345166fe0aSLaurent Vivier     remote = netdev->u.dgram.remote;
4355166fe0aSLaurent Vivier     local = netdev->u.dgram.local;
4365166fe0aSLaurent Vivier 
4375166fe0aSLaurent Vivier     /* detect multicast address */
4385166fe0aSLaurent Vivier     if (remote && remote->type == SOCKET_ADDRESS_TYPE_INET) {
4395166fe0aSLaurent Vivier         struct sockaddr_in mcastaddr;
4405166fe0aSLaurent Vivier 
4415166fe0aSLaurent Vivier         if (convert_host_port(&mcastaddr, remote->u.inet.host,
4425166fe0aSLaurent Vivier                               remote->u.inet.port, errp) < 0) {
4435166fe0aSLaurent Vivier             return -1;
4445166fe0aSLaurent Vivier         }
4455166fe0aSLaurent Vivier 
4465166fe0aSLaurent Vivier         if (IN_MULTICAST(ntohl(mcastaddr.sin_addr.s_addr))) {
4475166fe0aSLaurent Vivier             return net_dgram_mcast_init(peer, "dram", name, remote, local,
4485166fe0aSLaurent Vivier                                            errp);
4495166fe0aSLaurent Vivier         }
4505166fe0aSLaurent Vivier     }
4515166fe0aSLaurent Vivier 
4525166fe0aSLaurent Vivier     /* unicast address */
4535166fe0aSLaurent Vivier     if (!local) {
4545166fe0aSLaurent Vivier         error_setg(errp, "dgram requires local= parameter");
4555166fe0aSLaurent Vivier         return -1;
4565166fe0aSLaurent Vivier     }
4575166fe0aSLaurent Vivier 
4585166fe0aSLaurent Vivier     if (remote) {
4595166fe0aSLaurent Vivier         if (local->type == SOCKET_ADDRESS_TYPE_FD) {
4605166fe0aSLaurent Vivier             error_setg(errp, "don't set remote with local.fd");
4615166fe0aSLaurent Vivier             return -1;
4625166fe0aSLaurent Vivier         }
4635166fe0aSLaurent Vivier         if (remote->type != local->type) {
4645166fe0aSLaurent Vivier             error_setg(errp, "remote and local types must be the same");
4655166fe0aSLaurent Vivier             return -1;
4665166fe0aSLaurent Vivier         }
4675166fe0aSLaurent Vivier     } else {
4685166fe0aSLaurent Vivier         if (local->type != SOCKET_ADDRESS_TYPE_FD) {
469784e7a25SLaurent Vivier             error_setg(errp,
470784e7a25SLaurent Vivier                        "type=inet or type=unix requires remote parameter");
4715166fe0aSLaurent Vivier             return -1;
4725166fe0aSLaurent Vivier         }
4735166fe0aSLaurent Vivier     }
4745166fe0aSLaurent Vivier 
4755166fe0aSLaurent Vivier     switch (local->type) {
4765166fe0aSLaurent Vivier     case SOCKET_ADDRESS_TYPE_INET:
4775166fe0aSLaurent Vivier         if (convert_host_port(&laddr_in, local->u.inet.host, local->u.inet.port,
4785166fe0aSLaurent Vivier                               errp) < 0) {
4795166fe0aSLaurent Vivier             return -1;
4805166fe0aSLaurent Vivier         }
4815166fe0aSLaurent Vivier 
4825166fe0aSLaurent Vivier         if (convert_host_port(&raddr_in, remote->u.inet.host,
4835166fe0aSLaurent Vivier                               remote->u.inet.port, errp) < 0) {
4845166fe0aSLaurent Vivier             return -1;
4855166fe0aSLaurent Vivier         }
4865166fe0aSLaurent Vivier 
4875166fe0aSLaurent Vivier         fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
4885166fe0aSLaurent Vivier         if (fd < 0) {
4895166fe0aSLaurent Vivier             error_setg_errno(errp, errno, "can't create datagram socket");
4905166fe0aSLaurent Vivier             return -1;
4915166fe0aSLaurent Vivier         }
4925166fe0aSLaurent Vivier 
4935166fe0aSLaurent Vivier         ret = socket_set_fast_reuse(fd);
4945166fe0aSLaurent Vivier         if (ret < 0) {
4955166fe0aSLaurent Vivier             error_setg_errno(errp, errno,
4965166fe0aSLaurent Vivier                              "can't set socket option SO_REUSEADDR");
497*25657fc6SMarc-André Lureau             close(fd);
4985166fe0aSLaurent Vivier             return -1;
4995166fe0aSLaurent Vivier         }
5005166fe0aSLaurent Vivier         ret = bind(fd, (struct sockaddr *)&laddr_in, sizeof(laddr_in));
5015166fe0aSLaurent Vivier         if (ret < 0) {
5025166fe0aSLaurent Vivier             error_setg_errno(errp, errno, "can't bind ip=%s to socket",
5035166fe0aSLaurent Vivier                              inet_ntoa(laddr_in.sin_addr));
504*25657fc6SMarc-André Lureau             close(fd);
5055166fe0aSLaurent Vivier             return -1;
5065166fe0aSLaurent Vivier         }
5075166fe0aSLaurent Vivier         qemu_socket_set_nonblock(fd);
5087c1f0c33SLaurent Vivier 
5097c1f0c33SLaurent Vivier         dest_len = sizeof(raddr_in);
5107c1f0c33SLaurent Vivier         dest_addr = g_malloc(dest_len);
5117c1f0c33SLaurent Vivier         memcpy(dest_addr, &raddr_in, dest_len);
5125166fe0aSLaurent Vivier         break;
513784e7a25SLaurent Vivier     case SOCKET_ADDRESS_TYPE_UNIX:
514784e7a25SLaurent Vivier         ret = unlink(local->u.q_unix.path);
515784e7a25SLaurent Vivier         if (ret < 0 && errno != ENOENT) {
516784e7a25SLaurent Vivier             error_setg_errno(errp, errno, "failed to unlink socket %s",
517784e7a25SLaurent Vivier                              local->u.q_unix.path);
518784e7a25SLaurent Vivier             return -1;
519784e7a25SLaurent Vivier         }
520784e7a25SLaurent Vivier 
521784e7a25SLaurent Vivier         laddr_un.sun_family = PF_UNIX;
522784e7a25SLaurent Vivier         ret = snprintf(laddr_un.sun_path, sizeof(laddr_un.sun_path), "%s",
523784e7a25SLaurent Vivier                        local->u.q_unix.path);
524784e7a25SLaurent Vivier         if (ret < 0 || ret >= sizeof(laddr_un.sun_path)) {
525784e7a25SLaurent Vivier             error_setg(errp, "UNIX socket path '%s' is too long",
526784e7a25SLaurent Vivier                        local->u.q_unix.path);
527784e7a25SLaurent Vivier             error_append_hint(errp, "Path must be less than %zu bytes\n",
528784e7a25SLaurent Vivier                               sizeof(laddr_un.sun_path));
529784e7a25SLaurent Vivier         }
530784e7a25SLaurent Vivier 
531784e7a25SLaurent Vivier         raddr_un.sun_family = PF_UNIX;
532784e7a25SLaurent Vivier         ret = snprintf(raddr_un.sun_path, sizeof(raddr_un.sun_path), "%s",
533784e7a25SLaurent Vivier                        remote->u.q_unix.path);
534784e7a25SLaurent Vivier         if (ret < 0 || ret >= sizeof(raddr_un.sun_path)) {
535784e7a25SLaurent Vivier             error_setg(errp, "UNIX socket path '%s' is too long",
536784e7a25SLaurent Vivier                        remote->u.q_unix.path);
537784e7a25SLaurent Vivier             error_append_hint(errp, "Path must be less than %zu bytes\n",
538784e7a25SLaurent Vivier                               sizeof(raddr_un.sun_path));
539784e7a25SLaurent Vivier         }
540784e7a25SLaurent Vivier 
541784e7a25SLaurent Vivier         fd = qemu_socket(PF_UNIX, SOCK_DGRAM, 0);
542784e7a25SLaurent Vivier         if (fd < 0) {
543784e7a25SLaurent Vivier             error_setg_errno(errp, errno, "can't create datagram socket");
544784e7a25SLaurent Vivier             return -1;
545784e7a25SLaurent Vivier         }
546784e7a25SLaurent Vivier 
547784e7a25SLaurent Vivier         ret = bind(fd, (struct sockaddr *)&laddr_un, sizeof(laddr_un));
548784e7a25SLaurent Vivier         if (ret < 0) {
549784e7a25SLaurent Vivier             error_setg_errno(errp, errno, "can't bind unix=%s to socket",
550784e7a25SLaurent Vivier                              laddr_un.sun_path);
551*25657fc6SMarc-André Lureau             close(fd);
552784e7a25SLaurent Vivier             return -1;
553784e7a25SLaurent Vivier         }
554784e7a25SLaurent Vivier         qemu_socket_set_nonblock(fd);
555784e7a25SLaurent Vivier 
556784e7a25SLaurent Vivier         dest_len = sizeof(raddr_un);
557784e7a25SLaurent Vivier         dest_addr = g_malloc(dest_len);
558784e7a25SLaurent Vivier         memcpy(dest_addr, &raddr_un, dest_len);
559784e7a25SLaurent Vivier         break;
5605166fe0aSLaurent Vivier     case SOCKET_ADDRESS_TYPE_FD:
5615166fe0aSLaurent Vivier         fd = monitor_fd_param(monitor_cur(), local->u.fd.str, errp);
5625166fe0aSLaurent Vivier         if (fd == -1) {
5635166fe0aSLaurent Vivier             return -1;
5645166fe0aSLaurent Vivier         }
5655166fe0aSLaurent Vivier         ret = qemu_socket_try_set_nonblock(fd);
5665166fe0aSLaurent Vivier         if (ret < 0) {
5675166fe0aSLaurent Vivier             error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
5685166fe0aSLaurent Vivier                              name, fd);
5695166fe0aSLaurent Vivier             return -1;
5705166fe0aSLaurent Vivier         }
5717c1f0c33SLaurent Vivier         dest_addr = NULL;
5727c1f0c33SLaurent Vivier         dest_len = 0;
5735166fe0aSLaurent Vivier         break;
5745166fe0aSLaurent Vivier     default:
5755166fe0aSLaurent Vivier         error_setg(errp, "only support inet or fd type for local");
5765166fe0aSLaurent Vivier         return -1;
5775166fe0aSLaurent Vivier     }
5785166fe0aSLaurent Vivier 
5798ecc7f40SLaurent Vivier     s = net_dgram_fd_init(peer, "dgram", name, fd, errp);
5805166fe0aSLaurent Vivier     if (!s) {
5815166fe0aSLaurent Vivier         return -1;
5825166fe0aSLaurent Vivier     }
5835166fe0aSLaurent Vivier 
5845166fe0aSLaurent Vivier     if (remote) {
5857c1f0c33SLaurent Vivier         g_assert(s->dest_addr == NULL);
5867c1f0c33SLaurent Vivier         s->dest_addr = dest_addr;
5877c1f0c33SLaurent Vivier         s->dest_len = dest_len;
5885166fe0aSLaurent Vivier     }
5895166fe0aSLaurent Vivier 
5905166fe0aSLaurent Vivier     switch (local->type) {
5915166fe0aSLaurent Vivier     case SOCKET_ADDRESS_TYPE_INET:
5925166fe0aSLaurent Vivier         qemu_set_info_str(&s->nc, "udp=%s:%d/%s:%d",
5935166fe0aSLaurent Vivier                           inet_ntoa(laddr_in.sin_addr),
5945166fe0aSLaurent Vivier                           ntohs(laddr_in.sin_port),
5955166fe0aSLaurent Vivier                           inet_ntoa(raddr_in.sin_addr),
5965166fe0aSLaurent Vivier                           ntohs(raddr_in.sin_port));
5975166fe0aSLaurent Vivier         break;
598784e7a25SLaurent Vivier     case SOCKET_ADDRESS_TYPE_UNIX:
599784e7a25SLaurent Vivier         qemu_set_info_str(&s->nc, "udp=%s:%s",
600784e7a25SLaurent Vivier                           laddr_un.sun_path, raddr_un.sun_path);
601784e7a25SLaurent Vivier         break;
6028ecc7f40SLaurent Vivier     case SOCKET_ADDRESS_TYPE_FD: {
6038ecc7f40SLaurent Vivier         SocketAddress *sa;
6048ecc7f40SLaurent Vivier         SocketAddressType sa_type;
6058ecc7f40SLaurent Vivier 
6068ecc7f40SLaurent Vivier         sa = socket_local_address(fd, errp);
6078ecc7f40SLaurent Vivier         if (sa) {
6088ecc7f40SLaurent Vivier             sa_type = sa->type;
6098ecc7f40SLaurent Vivier             qapi_free_SocketAddress(sa);
6108ecc7f40SLaurent Vivier 
6118ecc7f40SLaurent Vivier             qemu_set_info_str(&s->nc, "fd=%d %s", fd,
6128ecc7f40SLaurent Vivier                               SocketAddressType_str(sa_type));
6138ecc7f40SLaurent Vivier         } else {
6145166fe0aSLaurent Vivier             qemu_set_info_str(&s->nc, "fd=%d", fd);
6158ecc7f40SLaurent Vivier         }
6165166fe0aSLaurent Vivier         break;
6178ecc7f40SLaurent Vivier     }
6185166fe0aSLaurent Vivier     default:
6195166fe0aSLaurent Vivier         g_assert_not_reached();
6205166fe0aSLaurent Vivier     }
6215166fe0aSLaurent Vivier 
6225166fe0aSLaurent Vivier     return 0;
6235166fe0aSLaurent Vivier }
624