Commit b6503ed9b8815ecfb82fe9faba28936365321248

Authored by aliguori
1 parent 3831ab20

qemu:virtio-net: Add additional MACs via a filter table (Alex Williamson)

Create a filter table and allow the guest to populate it with the
MAC class control commands.  We manage the size and usage of the
filter table including enabling promiscuous and all-multi modes
as necessary.  The guest should therefore assume the table is
infinite.  Eventually this might allow us to bind directly to a
hardware NIC and manipulate a physical MAC filter.

The specifics of the TABLE_SET command are documented in
virtio-net.h.  Separate buffers in the same command are used
for unicaste and multicast addresses for priority and
sychronization.  With this we can export the VIRTIO_NET_F_CTRL_RX
feature bit.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6539 c046a42c-6fe2-441c-8c8c-71466251a162
hw/virtio-net.c
... ... @@ -16,7 +16,9 @@
16 16 #include "qemu-timer.h"
17 17 #include "virtio-net.h"
18 18  
19   -#define VIRTIO_NET_VM_VERSION 4
  19 +#define VIRTIO_NET_VM_VERSION 5
  20 +
  21 +#define MAC_TABLE_ENTRIES 32
20 22  
21 23 typedef struct VirtIONet
22 24 {
... ... @@ -32,6 +34,10 @@ typedef struct VirtIONet
32 34 int mergeable_rx_bufs;
33 35 int promisc;
34 36 int allmulti;
  37 + struct {
  38 + int in_use;
  39 + uint8_t *macs;
  40 + } mac_table;
35 41 } VirtIONet;
36 42  
37 43 /* TODO
... ... @@ -87,13 +93,18 @@ static void virtio_net_reset(VirtIODevice *vdev)
87 93 /* Reset back to compatibility mode */
88 94 n->promisc = 1;
89 95 n->allmulti = 0;
  96 +
  97 + /* Flush any MAC filter table state */
  98 + n->mac_table.in_use = 0;
  99 + memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
90 100 }
91 101  
92 102 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
93 103 {
94 104 uint32_t features = (1 << VIRTIO_NET_F_MAC) |
95 105 (1 << VIRTIO_NET_F_STATUS) |
96   - (1 << VIRTIO_NET_F_CTRL_VQ);
  106 + (1 << VIRTIO_NET_F_CTRL_VQ) |
  107 + (1 << VIRTIO_NET_F_CTRL_RX);
97 108  
98 109 return features;
99 110 }
... ... @@ -127,6 +138,53 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
127 138 return VIRTIO_NET_OK;
128 139 }
129 140  
  141 +static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
  142 + VirtQueueElement *elem)
  143 +{
  144 + struct virtio_net_ctrl_mac mac_data;
  145 +
  146 + if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 ||
  147 + elem->out_sg[1].iov_len < sizeof(mac_data) ||
  148 + elem->out_sg[2].iov_len < sizeof(mac_data))
  149 + return VIRTIO_NET_ERR;
  150 +
  151 + n->mac_table.in_use = 0;
  152 + memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
  153 +
  154 + mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base);
  155 +
  156 + if (sizeof(mac_data.entries) +
  157 + (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len)
  158 + return VIRTIO_NET_ERR;
  159 +
  160 + if (mac_data.entries <= MAC_TABLE_ENTRIES) {
  161 + memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data),
  162 + mac_data.entries * ETH_ALEN);
  163 + n->mac_table.in_use += mac_data.entries;
  164 + } else {
  165 + n->promisc = 1;
  166 + return VIRTIO_NET_OK;
  167 + }
  168 +
  169 + mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
  170 +
  171 + if (sizeof(mac_data.entries) +
  172 + (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len)
  173 + return VIRTIO_NET_ERR;
  174 +
  175 + if (mac_data.entries) {
  176 + if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
  177 + memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN),
  178 + elem->out_sg[2].iov_base + sizeof(mac_data),
  179 + mac_data.entries * ETH_ALEN);
  180 + n->mac_table.in_use += mac_data.entries;
  181 + } else
  182 + n->allmulti = 1;
  183 + }
  184 +
  185 + return VIRTIO_NET_OK;
  186 +}
  187 +
130 188 static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
131 189 {
132 190 VirtIONet *n = to_virtio_net(vdev);
... ... @@ -151,6 +209,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
151 209  
152 210 if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE)
153 211 status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem);
  212 + else if (ctrl.class == VIRTIO_NET_CTRL_MAC)
  213 + status = virtio_net_handle_mac(n, ctrl.cmd, &elem);
154 214  
155 215 stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
156 216  
... ... @@ -226,6 +286,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
226 286 {
227 287 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
228 288 uint8_t *ptr = (uint8_t *)buf;
  289 + int i;
229 290  
230 291 if (n->promisc)
231 292 return 1;
... ... @@ -244,6 +305,11 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
244 305 if (!memcmp(ptr, n->mac, ETH_ALEN))
245 306 return 1;
246 307  
  308 + for (i = 0; i < n->mac_table.in_use; i++) {
  309 + if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
  310 + return 1;
  311 + }
  312 +
247 313 return 0;
248 314 }
249 315  
... ... @@ -406,6 +472,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
406 472 qemu_put_be16(f, n->status);
407 473 qemu_put_be32(f, n->promisc);
408 474 qemu_put_be32(f, n->allmulti);
  475 + qemu_put_be32(f, n->mac_table.in_use);
  476 + qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
409 477 }
410 478  
411 479 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
... ... @@ -429,6 +497,19 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
429 497 n->allmulti = qemu_get_be32(f);
430 498 }
431 499  
  500 + if (version_id >= 5) {
  501 + n->mac_table.in_use = qemu_get_be32(f);
  502 + /* MAC_TABLE_ENTRIES may be different from the saved image */
  503 + if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
  504 + qemu_get_buffer(f, n->mac_table.macs,
  505 + n->mac_table.in_use * ETH_ALEN);
  506 + } else if (n->mac_table.in_use) {
  507 + qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR);
  508 + n->promisc = 1;
  509 + n->mac_table.in_use = 0;
  510 + }
  511 + }
  512 +
432 513 if (n->tx_timer_active) {
433 514 qemu_mod_timer(n->tx_timer,
434 515 qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
... ... @@ -474,6 +555,10 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
474 555 n->mergeable_rx_bufs = 0;
475 556 n->promisc = 1; /* for compatibility */
476 557  
  558 + n->mac_table.macs = qemu_mallocz(MAC_TABLE_ENTRIES * ETH_ALEN);
  559 + if (!n->mac_table.macs)
  560 + return;
  561 +
477 562 register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
478 563 virtio_net_save, virtio_net_load, n);
479 564 }
... ...
hw/virtio-net.h
... ... @@ -113,4 +113,26 @@ typedef uint8_t virtio_net_ctrl_ack;
113 113 #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0
114 114 #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1
115 115  
  116 +/*
  117 + * Control the MAC filter table.
  118 + *
  119 + * The MAC filter table is managed by the hypervisor, the guest should
  120 + * assume the size is infinite. Filtering should be considered
  121 + * non-perfect, ie. based on hypervisor resources, the guest may
  122 + * received packets from sources not specified in the filter list.
  123 + *
  124 + * In addition to the class/cmd header, the TABLE_SET command requires
  125 + * two out scatterlists. Each contains a 4 byte count of entries followed
  126 + * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
  127 + * first sg list contains unicast addresses, the second is for multicast.
  128 + * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
  129 + * is available.
  130 + */
  131 +struct virtio_net_ctrl_mac {
  132 + uint32_t entries;
  133 + uint8_t macs[][ETH_ALEN];
  134 +};
  135 +#define VIRTIO_NET_CTRL_MAC 1
  136 + #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
  137 +
116 138 #endif
... ...