Commit b6503ed9b8815ecfb82fe9faba28936365321248
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
Showing
2 changed files
with
109 additions
and
2 deletions
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 | ... | ... |