diff mbox

add a command line option to specify the IP address to send multicast packets from

Message ID 20101118011626.GA2304@zzz.isi.edu
State New
Headers show

Commit Message

Mike Ryan Nov. 18, 2010, 1:16 a.m. UTC
Add an option to specify the host IP to send multicast packets from when
using a multicast socket for networking. The option takes an IP address
and sets the IP_MULTICAST_IF socket option, which causes the packets to
use that IP's interface as an egress.

This is useful if the host machine has several interfaces with several
virtual networks across disparate interfaces.
---
 net.c           |    4 ++++
 net/socket.c    |   46 ++++++++++++++++++++++++++++++++++------------
 qemu-options.hx |   11 +++++++++--
 3 files changed, 47 insertions(+), 14 deletions(-)

Comments

Mike Ryan Nov. 22, 2010, 6:48 p.m. UTC | #1
Michael, this patch implements the feature with a bind address instead
of a bind interface. It should address the cross-platform issues that
were raised.

Others: any comments?

On Wed, Nov 17, 2010 at 05:16:26PM -0800, Mike Ryan wrote:
> Add an option to specify the host IP to send multicast packets from when
> using a multicast socket for networking. The option takes an IP address
> and sets the IP_MULTICAST_IF socket option, which causes the packets to
> use that IP's interface as an egress.
> 
> This is useful if the host machine has several interfaces with several
> virtual networks across disparate interfaces.
> ---
>  net.c           |    4 ++++
>  net/socket.c    |   46 ++++++++++++++++++++++++++++++++++------------
>  qemu-options.hx |   11 +++++++++--
>  3 files changed, 47 insertions(+), 14 deletions(-)
> 
> diff --git a/net.c b/net.c
> index c5e6063..9ba5be2 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 1c4e153..d443f4c 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;
> @@ -201,6 +201,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 +257,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 +477,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 +523,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 +543,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 +559,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 +571,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 +581,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 4d99a58..accd16a 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}
> -- 
> 1.7.0.4
>
Michael S. Tsirkin Nov. 23, 2010, 1:05 p.m. UTC | #2
On Mon, Nov 22, 2010 at 10:48:45AM -0800, Mike Ryan wrote:
> Michael, this patch implements the feature with a bind address instead
> of a bind interface. It should address the cross-platform issues that
> were raised.
> 
> Others: any comments?

Looks ok. This does not handle IPv6 but the rest of the code doesn't,
either.

> On Wed, Nov 17, 2010 at 05:16:26PM -0800, Mike Ryan wrote:
> > Add an option to specify the host IP to send multicast packets from when
> > using a multicast socket for networking. The option takes an IP address
> > and sets the IP_MULTICAST_IF socket option, which causes the packets to
> > use that IP's interface as an egress.
> > 
> > This is useful if the host machine has several interfaces with several
> > virtual networks across disparate interfaces.
> > ---
> >  net.c           |    4 ++++
> >  net/socket.c    |   46 ++++++++++++++++++++++++++++++++++------------
> >  qemu-options.hx |   11 +++++++++--
> >  3 files changed, 47 insertions(+), 14 deletions(-)
> > 
> > diff --git a/net.c b/net.c
> > index c5e6063..9ba5be2 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 1c4e153..d443f4c 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;
> > @@ -201,6 +201,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 +257,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 +477,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 +523,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 +543,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 +559,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 +571,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 +581,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 4d99a58..accd16a 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}
> > -- 
> > 1.7.0.4
> >
Mike Ryan Nov. 23, 2010, 11:49 p.m. UTC | #3
On Tue, Nov 23, 2010 at 03:05:49PM +0200, Michael S. Tsirkin wrote:
> On Mon, Nov 22, 2010 at 10:48:45AM -0800, Mike Ryan wrote:
> > Michael, this patch implements the feature with a bind address instead
> > of a bind interface. It should address the cross-platform issues that
> > were raised.
> > 
> > Others: any comments?
> 
> Looks ok. This does not handle IPv6 but the rest of the code doesn't,
> either.

Great, is there anything I can do to expedite the process of getting it
committed into master?
Michael S. Tsirkin Nov. 24, 2010, 5:32 a.m. UTC | #4
On Wed, Nov 17, 2010 at 05:16:26PM -0800, Mike Ryan wrote:
> Add an option to specify the host IP to send multicast packets from when
> using a multicast socket for networking. The option takes an IP address
> and sets the IP_MULTICAST_IF socket option, which causes the packets to
> use that IP's interface as an egress.
> 
> This is useful if the host machine has several interfaces with several
> virtual networks across disparate interfaces.

Please sign your work:

-------------------

The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
pass it on as a open-source patch.  The rules are pretty simple: if you
can certify the below:

        Developer's Certificate of Origin 1.1

        By making a contribution to this project, I certify that:

        (a) The contribution was created in whole or in part by me and I
            have the right to submit it under the open source license
            indicated in the file; or

        (b) The contribution is based upon previous work that, to the best
            of my knowledge, is covered under an appropriate open source
            license and I have the right under that license to submit that
            work with modifications, whether created in whole or in part
            by me, under the same open source license (unless I am
            permitted to submit under a different license), as indicated
            in the file; or

        (c) The contribution was provided directly to me by some other
            person who certified (a), (b) or (c) and I have not modified
            it.

	(d) I understand and agree that this project and the contribution
	    are public and that a record of the contribution (including all
	    personal information I submit with it, including my sign-off) is
	    maintained indefinitely and may be redistributed consistent with
	    this project or the open source license(s) involved.

then you just add a line saying

	Signed-off-by: Random J Developer <random@developer.example.org>

using your real name (sorry, no pseudonyms or anonymous contributions.)

Some people also put extra tags at the end.  They'll just be ignored for
now, but you can do this to mark internal company procedures or just
point out some special detail about the sign-off. 

-------------------

> ---
>  net.c           |    4 ++++
>  net/socket.c    |   46 ++++++++++++++++++++++++++++++++++------------
>  qemu-options.hx |   11 +++++++++--
>  3 files changed, 47 insertions(+), 14 deletions(-)
> 
> diff --git a/net.c b/net.c
> index c5e6063..9ba5be2 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 1c4e153..d443f4c 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;
> @@ -201,6 +201,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 +257,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 +477,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 +523,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 +543,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 +559,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 +571,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 +581,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 4d99a58..accd16a 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}
> -- 
> 1.7.0.4
>
diff mbox

Patch

diff --git a/net.c b/net.c
index c5e6063..9ba5be2 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 1c4e153..d443f4c 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;
@@ -201,6 +201,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 +257,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 +477,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 +523,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 +543,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 +559,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 +571,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 +581,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 4d99a58..accd16a 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}