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