qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] net: add raw backend


From: Michael S. Tsirkin
Subject: Re: [Qemu-devel] [PATCH] net: add raw backend
Date: Thu, 2 Jul 2009 18:43:54 +0300
User-agent: Mutt/1.5.19 (2009-01-05)

On Wed, Jul 01, 2009 at 06:46:43PM +0300, Or Gerlitz wrote:
> Add raw network backend option which uses a packet socket to provide
> raw networking access. Once the socket is opened its bouned to a
> provided host interface, such that packets received on the interface
> are delivered to the VM and packets sent by the VM are sent to the
> interface.
> 
> Signed-off-by: Or Gerlitz<address@hidden>

Looks good to me overall. A couple of comments:

> diff --git a/net.c b/net.c
> index 55f70f2..f7ff381 100644
> --- a/net.c
> +++ b/net.c
> @@ -93,6 +93,9 @@
>  #endif
>  #endif
> 
> +#include <netpacket/packet.h>
> +#include <net/ethernet.h>
> +
>  #if defined(__OpenBSD__)
>  #include <util.h>
>  #endif
> @@ -1476,6 +1479,155 @@ static TAPState *net_tap_init(VLANState *vlan, const 
> char *model,
> 
>  #endif /* !_WIN32 */
> 
> +typedef struct RAWState {
> +    VLANClientState *vc;
> +    int fd;
> +    uint8_t buf[4096];
> +    int promisc;
> +} RAWState;
> +
> +static int net_raw_fd_init(Monitor *mon, const char *ifname, int promisc)
> +{
> +     int fd, ret;
> +     struct ifreq req;
> +     struct sockaddr_ll lladdr;
> +
> +     fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
> +     if (fd < 0)
> +             config_error(mon, "packet socket failed\n");
> +
> +     memset(&req, 0, sizeof(req));
> +     strncpy(req.ifr_name, ifname, IFNAMSIZ-1);
> +     ret = ioctl(fd, SIOCGIFINDEX, &req);
> +     if (ret < 0)
> +             config_error(mon, "SIOCGIFINDEX failed\n");
> +
> +     memset(&lladdr, 0, sizeof(lladdr));
> +     lladdr.sll_family   = AF_PACKET;
> +     lladdr.sll_protocol = htons(ETH_P_ALL);
> +     lladdr.sll_ifindex  = req.ifr_ifindex;
> +     ret = bind(fd, (const struct sockaddr *)&lladdr, sizeof(lladdr));
> +     if (ret < 0)
> +             config_error(mon, "bind failed\n");
> +
> +     /* set iface to promiscuous mode (packets sent to the VM MAC) */
> +     if (promisc) {
> +             ret = ioctl(fd, SIOCGIFFLAGS, &req);
> +             if (ret < 0)
> +                     perror("SIOCGIFFLAGS failed\n");
> +             req.ifr_flags |= IFF_PROMISC;
> +             ret = ioctl(fd, SIOCSIFFLAGS, &req);
> +             if (ret < 0)
> +                     config_error(mon, "SIOCSIFFLAGS to promiscous 
> failed\n");
> +     }
> +
> +     ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
> +     if (ret < 0)
> +             config_error(mon, "O_NONBLOCK set failed\n");
> +
> +     return fd;
> +}
> +
> +static void raw_cleanup(VLANClientState *vc)
> +{
> +     struct ifreq req;
> +     RAWState *s = vc->opaque;
> +
> +     qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
> +     if (s->promisc) {
> +             ioctl(s->fd, SIOCGIFFLAGS, &req);
> +             req.ifr_flags &= ~IFF_PROMISC;
> +             ioctl(s->fd, SIOCSIFFLAGS, &req);
> +     }
> +     close(s->fd);
> +     qemu_free(s);
> +}
> +
> +static void raw_send(void *opaque);
> +
> +static int raw_can_send(void *opaque)
> +{
> +     RAWState *s = opaque;
> +
> +     return qemu_can_send_packet(s->vc);
> +}
> +
> +static void raw_send_completed(VLANClientState *vc, ssize_t len)
> +{
> +     RAWState *s = vc->opaque;
> +
> +     qemu_set_fd_handler2(s->fd, raw_can_send, raw_send, NULL, s);
> +}
> +
> +static void raw_send(void *opaque)
> +{
> +     RAWState *s = opaque;
> +     int size;
> +
> +     do {
> +             size = recv(s->fd, s->buf, sizeof(s->buf), MSG_TRUNC);
> +             if (size <= 0)
> +                     break;

A couple of improvement suggestions here:
- You might get size > sizeof(s->buf).
  Should not happen, but you might want to check for this condition and
  report it + discard the packet.

- It might be a good idea to request aux data and verify that checksum
  is set, calculate it if not. this will make it possible to bind to
  a local device as well.


> +
> +             size = qemu_send_packet_async(s->vc, s->buf, size,
> +                                             raw_send_completed);
> +             if (size == 0)
> +                     qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
> +
> +     } while (size > 0);
> +}
> +
> +static ssize_t raw_receive_iov(VLANClientState *vc, const struct iovec *iov,
> +                             int iovcnt)
> +{
> +     ssize_t len;
> +     RAWState *s = vc->opaque;
> +
> +     do {
> +             len = writev(s->fd, iov, iovcnt);
> +     } while (len == -1 && (errno == EINTR || errno == EAGAIN));
> +
> +     return len;
> +}
> +
> +static ssize_t raw_receive(VLANClientState *vc, const uint8_t *buf, size_t 
> size)
> +{
> +     struct iovec iov[1];
> +
> +     iov[0].iov_base = (char *)buf;
> +     iov[0].iov_len  = size;
> +
> +     return raw_receive_iov(vc, iov, 1);
> +}
> +
> +static int net_raw_init(Monitor *mon, VLANState *vlan, const char *model,
> +                     const char *name, const char *ifname,
> +                     int promisc, int fd)
> +{
> +     RAWState *s;
> +
> +     s = qemu_mallocz(sizeof(RAWState));
> +
> +     if (fd == -1) {
> +             s->fd = net_raw_fd_init(mon, ifname, promisc);
> +             s->promisc = promisc;
> +     } else
> +             s->fd = fd;
> +
> +     s->vc = qemu_new_vlan_client(vlan, model, name, NULL, raw_receive,
> +                                     raw_receive_iov, raw_cleanup, s);
> +     qemu_set_fd_handler2(s->fd, raw_can_send, raw_send, NULL, s);
> +
> +     if (fd == -1)
> +             snprintf(s->vc->info_str, sizeof(s->vc->info_str),
> +                     "raw: ifname=%s, promisc=%d", ifname, promisc);
> +     else
> +             snprintf(s->vc->info_str, sizeof(s->vc->info_str),
> +                     "raw: fd=%d", fd);
> +
> +     return 0;
> +}
> +
>  #if defined(CONFIG_VDE)
>  typedef struct VDEState {
>      VLANClientState *vc;
> @@ -2348,6 +2500,41 @@ int net_client_init(Monitor *mon, const char *device, 
> const char *p)
>          }
>      } else
>  #endif
> +    if (!strcmp(device, "raw")) {
> +     char chkbuf[64], ifname[64];
> +        int raw_fd = -1;
> +        int promisc = 1;

promisc = 0 might be a safer default.

> +        if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
> +            static const char * const fd_params[] = {
> +                "vlan", "name", "fd", NULL
> +            };
> +            if (check_params(chkbuf, sizeof(chkbuf), fd_params, p) < 0) {
> +                config_error(mon, "invalid parameter '%s' in '%s'\n", 
> chkbuf, p);
> +                ret = -1;
> +                goto out;
> +            }
> +         raw_fd = strtol(buf, NULL, 0);
> +         fcntl(raw_fd, F_SETFL, fcntl(raw_fd, F_GETFL | O_NONBLOCK));
> +        } else {
> +            static const char * const tap_params[] = {
> +                "vlan", "name", "ifname", "promisc", NULL
> +            };
> +            if (check_params(chkbuf, sizeof(chkbuf), tap_params, p) < 0) {
> +                config_error(mon, "invalid parameter '%s' in '%s'\n", 
> chkbuf, p);
> +                ret = -1;
> +                goto out;
> +            }
> +            if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
> +             config_error(mon, "raw: no interface name\n");
> +             ret = -1;
> +             goto out;
> +            }
> +            if (get_param_value(buf, sizeof(buf), "promisc", p))
> +                promisc = atoi(buf);
> +     }
> +     vlan->nb_host_devs++;
> +     ret = net_raw_init(mon, vlan, device, name, ifname, promisc, raw_fd);
> +    } else
>      if (!strcmp(device, "socket")) {
>          char chkbuf[64];
>          if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 503da33..0a3c807 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -761,6 +761,10 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
>      "                use 'sndbuf=nbytes' to limit the size of the send 
> buffer\n"
>  #endif
>  #endif
> +    "-net raw[,vlan=n][,name=str],ifname=name[,promisc=m]\n"
> +    "                bound the host network interface to VLAN 'n' in a raw 
> manner:\n"

in a raw manner -> using a raw packet socket

> +    "                packets received on the interface are delivered to the 
> vlan and\n"
> +    "                packets delivered on the vlan are sent to the 
> interface\n"

document promisc option?

>      "-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"
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]