qemu-devel
[Top][All Lists]
Advanced

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

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


From: Or Gerlitz
Subject: [Qemu-devel] [PATCH] net: add raw backend
Date: Wed, 1 Jul 2009 18:46:43 +0300 (IDT)

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>

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;
+
+               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;
+        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"
+    "                packets received on the interface are delivered to the 
vlan and\n"
+    "                packets delivered on the vlan are sent to the interface\n"
     "-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]