diff --git a/net.c b/net.c index c5e6063fcf..9ba5be22dd 100644 --- a/net.c +++ b/net.c @@ -1050,6 +1050,10 @@ static const struct { .name = "mcast", .type = QEMU_OPT_STRING, .help = "UDP multicast address and port number", + }, { + .name = "localaddr", + .type = QEMU_OPT_STRING, + .help = "source address for multicast packets", }, { /* end of list */ } }, diff --git a/net/socket.c b/net/socket.c index 1c4e153e3f..916c2a8e10 100644 --- a/net/socket.c +++ b/net/socket.c @@ -149,7 +149,7 @@ static void net_socket_send_dgram(void *opaque) qemu_send_packet(&s->nc, s->buf, size); } -static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) { struct ip_mreq imr; int fd; @@ -183,7 +183,11 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) /* Add host to multicast group */ imr.imr_multiaddr = mcastaddr->sin_addr; - imr.imr_interface.s_addr = htonl(INADDR_ANY); + if (localaddr) { + imr.imr_interface = *localaddr; + } else { + imr.imr_interface.s_addr = htonl(INADDR_ANY); + } ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(struct ip_mreq)); @@ -201,6 +205,15 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) goto fail; } + /* If a bind address is given, only send packets from that address */ + if (localaddr != NULL) { + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, localaddr, sizeof(*localaddr)); + if (ret < 0) { + perror("setsockopt(IP_MULTICAST_IF)"); + goto fail; + } + } + socket_set_nonblock(fd); return fd; fail: @@ -248,7 +261,7 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, return NULL; } /* clone dgram socket */ - newfd = net_socket_mcast_create(&saddr); + newfd = net_socket_mcast_create(&saddr, NULL); if (newfd < 0) { /* error already reported by net_socket_mcast_create() */ close(fd); @@ -468,17 +481,26 @@ static int net_socket_connect_init(VLANState *vlan, static int net_socket_mcast_init(VLANState *vlan, const char *model, const char *name, - const char *host_str) + const char *host_str, + const char *localaddr_str) { NetSocketState *s; int fd; struct sockaddr_in saddr; + struct in_addr localaddr, *param_localaddr; if (parse_host_port(&saddr, host_str) < 0) return -1; + if (localaddr_str != NULL) { + if (inet_aton(localaddr_str, &localaddr) == 0) + return -1; + param_localaddr = &localaddr; + } else { + param_localaddr = NULL; + } - fd = net_socket_mcast_create(&saddr); + fd = net_socket_mcast_create(&saddr, param_localaddr); if (fd < 0) return -1; @@ -505,8 +527,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "listen") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - error_report("listen=, connect= and mcast= is invalid with fd="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n"); return -1; } @@ -524,8 +547,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - error_report("fd=, connect= and mcast= is invalid with listen="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n"); return -1; } @@ -539,8 +563,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "listen") || - qemu_opt_get(opts, "mcast")) { - error_report("fd=, listen= and mcast= is invalid with connect="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n"); return -1; } @@ -550,7 +575,7 @@ int net_init_socket(QemuOpts *opts, return -1; } } else if (qemu_opt_get(opts, "mcast")) { - const char *mcast; + const char *mcast, *localaddr; if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || @@ -560,8 +585,9 @@ int net_init_socket(QemuOpts *opts, } mcast = qemu_opt_get(opts, "mcast"); + localaddr = qemu_opt_get(opts, "localaddr"); - if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) { + if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) { return -1; } } else { diff --git a/qemu-options.hx b/qemu-options.hx index 4d99a58fc5..accd16a55c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1061,8 +1061,9 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, #endif "-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n" " connect the vlan 'n' to another VLAN using a socket connection\n" - "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port]\n" + "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port[,localaddr=addr]]\n" " connect the vlan 'n' to multicast maddr and port\n" + " use 'localaddr=addr' to specify the host address to send packets from\n" #ifdef CONFIG_VDE "-net vde[,vlan=n][,name=str][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n" " connect the vlan 'n' to port 'n' of a vde switch running\n" @@ -1256,7 +1257,7 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \ -net socket,connect=127.0.0.1:1234 @end example -@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,mcast=@var{maddr}:@var{port}] +@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] Create a VLAN @var{n} shared with another QEMU virtual machines using a UDP multicast socket, effectively making a bus for @@ -1296,6 +1297,12 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ /path/to/linux ubd0=/path/to/root_fs eth0=mcast @end example +Example (send packets from host's 1.2.3.4): +@example +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4 +@end example + @item -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname}