Message ID | 1299538414-22208-4-git-send-email-benjamin.poirier@gmail.com |
---|---|
State | New |
Headers | show |
Benjamin Poirier writes: > Add support to the emulated hardware to insert vlan tags in packets > going from the guest to the network. > > Signed-off-by: Benjamin Poirier <benjamin.poirier@gmail.com> > Cc: Igor V. Kovalenko <igor.v.kovalenko@gmail.com> > Cc: Jason Wang <jasowang@redhat.com> > Cc: Michael S. Tsirkin <mst@redhat.com> > Cc: Blue Swirl <blauwirbel@gmail.com> > --- > hw/rtl8139.c | 107 ++++++++++++++++++++++++++++++++++++++++++--------------- > 1 files changed, 79 insertions(+), 28 deletions(-) Style issues were found through scripts/checkpatch.pl please have a look, and better to meger all rx changes into previous patch. Other looks good. > > diff --git a/hw/rtl8139.c b/hw/rtl8139.c > index 55a386e..265918a 100644 > --- a/hw/rtl8139.c > +++ b/hw/rtl8139.c > @@ -819,11 +819,14 @@ static int rtl8139_can_receive(VLANClientState *nc) > } > } > > -static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) > +static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, > + size_t buf_size, int do_interrupt, const uint8_t *dot1q_buf) > { > RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; > + /* size_ is the total length of argument buffers */ > + int size_ = buf_size + (dot1q_buf ? VLAN_HLEN : 0); > + /* size is the length of the buffer passed to the driver */ > int size = size_; > - const uint8_t *dot1q_buf; > const uint8_t *next_part; > size_t next_part_size; > > @@ -1005,10 +1008,13 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ > /* next_part starts right after the vlan header (if any), at the > * ethertype for the payload */ > next_part = &buf[ETHER_ADDR_LEN * 2]; > - if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) next_part) == > - ETH_P_8021Q) { > - dot1q_buf = next_part; > - next_part += VLAN_HLEN; > + if (s->CpCmd & CPlusRxVLAN && (dot1q_buf || be16_to_cpup((uint16_t *) > + next_part) == ETH_P_8021Q)) { > + if (!dot1q_buf) { > + /* the tag is in the buffer */ > + dot1q_buf = next_part; > + next_part += VLAN_HLEN; > + } > size -= VLAN_HLEN; > > rxdw1 &= ~CP_RX_VLAN_TAG_MASK; > @@ -1020,9 +1026,8 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ > } else { > /* reset VLAN tag flag */ > rxdw1 &= ~CP_RX_TAVA; > - dot1q_buf = NULL; > } > - next_part_size = buf + size_ - next_part; > + next_part_size = buf + buf_size - next_part; > > /* if too small buffer, then expand it */ > if (size < MIN_BUF_SIZE) { > @@ -1147,10 +1152,18 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ > > /* if too small buffer, then expand it */ > if (size < MIN_BUF_SIZE) { > - memcpy(buf1, buf, size); > + if (dot1q_buf) { > + memcpy(buf1, buf, 2 * ETHER_ADDR_LEN); > + memcpy(buf1 + 2 * ETHER_ADDR_LEN, dot1q_buf, VLAN_HLEN); > + memcpy(buf1 + 2 * ETHER_ADDR_LEN + VLAN_HLEN, buf + 2 * > + ETHER_ADDR_LEN, buf_size - 2 * ETHER_ADDR_LEN); > + } else { > + memcpy(buf1, buf, size); > + } > memset(buf1 + size, 0, MIN_BUF_SIZE - size); > buf = buf1; > size = MIN_BUF_SIZE; > + dot1q_buf = NULL; > } > > /* begin ring receiver mode */ > @@ -1178,10 +1191,23 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ > > rtl8139_write_buffer(s, (uint8_t *)&val, 4); > > - rtl8139_write_buffer(s, buf, size); > + /* receive/copy to target memory */ > + if (dot1q_buf) { > + rtl8139_write_buffer(s, buf, 2 * ETHER_ADDR_LEN); > + val = crc32(0, buf, 2 * ETHER_ADDR_LEN); > + rtl8139_write_buffer(s, dot1q_buf, VLAN_HLEN); > + val = crc32(val, dot1q_buf, VLAN_HLEN); > + rtl8139_write_buffer(s, buf + 2 * ETHER_ADDR_LEN, buf_size - 2 * > + ETHER_ADDR_LEN); > + val = crc32(val, buf + 2 * ETHER_ADDR_LEN, buf_size - 2 * > + ETHER_ADDR_LEN); > + } else { > + rtl8139_write_buffer(s, buf, size); > + val = crc32(0, buf, size); > + } > > /* write checksum */ > - val = cpu_to_le32(crc32(0, buf, size)); > + val = cpu_to_le32(val); > rtl8139_write_buffer(s, (uint8_t *)&val, 4); > > /* correct buffer write pointer */ > @@ -1205,7 +1231,7 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ > > static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t size) > { > - return rtl8139_do_receive(nc, buf, size, 1); > + return rtl8139_do_receive(nc, buf, size, 1, NULL); > } > > static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) > @@ -1780,7 +1806,8 @@ static uint32_t rtl8139_RxConfig_read(RTL8139State *s) > return ret; > } > > -static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt) > +static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, > + int do_interrupt, const uint8_t *dot1q_buf) > { > if (!size) > { > @@ -1791,11 +1818,22 @@ static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size > if (TxLoopBack == (s->TxConfig & TxLoopBack)) > { > DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); > - rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); > + rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt, dot1q_buf); > } > else > { > - qemu_send_packet(&s->nic->nc, buf, size); > + if (dot1q_buf) { > + struct iovec iov[] = { > + { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, > + { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, > + { .iov_base = buf + ETHER_ADDR_LEN * 2, > + .iov_len = size - ETHER_ADDR_LEN * 2 }, > + }; > + > + qemu_sendv_packet(&s->nic->nc, iov, ARRAY_SIZE(iov)); > + } else { > + qemu_send_packet(&s->nic->nc, buf, size); > + } > } > } > > @@ -1829,7 +1867,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) > s->TxStatus[descriptor] |= TxHostOwns; > s->TxStatus[descriptor] |= TxStatOK; > > - rtl8139_transfer_frame(s, txbuffer, txsize, 0); > + rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL); > > DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor)); > > @@ -1956,7 +1994,6 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > > cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); > txdw0 = le32_to_cpu(val); > - /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ > cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); > txdw1 = le32_to_cpu(val); > cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); > @@ -1968,9 +2005,6 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > descriptor, > txdw0, txdw1, txbufLO, txbufHI)); > > - /* TODO: the following discard cast should clean clang analyzer output */ > - (void)txdw1; > - > /* w0 ownership flag */ > #define CP_TX_OWN (1<<31) > /* w0 end of ring flag */ > @@ -1994,9 +2028,9 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > /* w0 bits 0...15 : buffer size */ > #define CP_TX_BUFFER_SIZE (1<<16) > #define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) > -/* w1 tag available flag */ > -#define CP_RX_TAGC (1<<17) > -/* w1 bits 0...15 : VLAN tag */ > +/* w1 add tag flag */ > +#define CP_TX_TAGC (1<<17) > +/* w1 bits 0...15 : VLAN tag (big endian) */ > #define CP_TX_VLAN_TAG_MASK ((1<<16) - 1) > /* w2 low 32bit of Rx buffer ptr */ > /* w3 high 32bit of Rx buffer ptr */ > @@ -2096,13 +2130,13 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > /* update ring data */ > val = cpu_to_le32(txdw0); > cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4); > - /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ > -// val = cpu_to_le32(txdw1); > -// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4); > > /* Now decide if descriptor being processed is holding the last segment of packet */ > if (txdw0 & CP_TX_LS) > { > + uint8_t dot1q_buffer_space[VLAN_HLEN]; > + uint16_t *dot1q_buffer; > + > DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor)); > > /* can transfer fully assembled packet */ > @@ -2111,6 +2145,21 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > int saved_size = s->cplus_txbuffer_offset; > int saved_buffer_len = s->cplus_txbuffer_len; > > + /* create vlan tag */ > + if (txdw1 & CP_TX_TAGC) { > + /* the vlan tag is in BE byte order in the descriptor > + * BE + le_to_cpu() + ~swap()~ = cpu */ > + DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : inserting vlan tag with " > + "tci: %u\n", bswap16(txdw1 & CP_TX_VLAN_TAG_MASK))); > + > + dot1q_buffer = (uint16_t*) dot1q_buffer_space; > + dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); > + /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ > + dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); > + } else { > + dot1q_buffer = NULL; > + } > + > /* reset the card space to protect from recursive call */ > s->cplus_txbuffer = NULL; > s->cplus_txbuffer_offset = 0; > @@ -2264,7 +2313,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > > int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; > DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size)); > - rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0); > + rtl8139_transfer_frame(s, saved_buffer, tso_send_size, > + 0, (uint8_t *) dot1q_buffer); > > /* add transferred count to TCP sequence number */ > p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); > @@ -2337,7 +2387,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) > > DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size)); > > - rtl8139_transfer_frame(s, saved_buffer, saved_size, 1); > + rtl8139_transfer_frame(s, saved_buffer, saved_size, 1, > + (uint8_t *) dot1q_buffer); > > /* restore card space if there was no recursion and reset offset */ > if (!s->cplus_txbuffer) > -- > 1.7.2.3 > >
diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 55a386e..265918a 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -819,11 +819,14 @@ static int rtl8139_can_receive(VLANClientState *nc) } } -static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) +static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, + size_t buf_size, int do_interrupt, const uint8_t *dot1q_buf) { RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + /* size_ is the total length of argument buffers */ + int size_ = buf_size + (dot1q_buf ? VLAN_HLEN : 0); + /* size is the length of the buffer passed to the driver */ int size = size_; - const uint8_t *dot1q_buf; const uint8_t *next_part; size_t next_part_size; @@ -1005,10 +1008,13 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ /* next_part starts right after the vlan header (if any), at the * ethertype for the payload */ next_part = &buf[ETHER_ADDR_LEN * 2]; - if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) next_part) == - ETH_P_8021Q) { - dot1q_buf = next_part; - next_part += VLAN_HLEN; + if (s->CpCmd & CPlusRxVLAN && (dot1q_buf || be16_to_cpup((uint16_t *) + next_part) == ETH_P_8021Q)) { + if (!dot1q_buf) { + /* the tag is in the buffer */ + dot1q_buf = next_part; + next_part += VLAN_HLEN; + } size -= VLAN_HLEN; rxdw1 &= ~CP_RX_VLAN_TAG_MASK; @@ -1020,9 +1026,8 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ } else { /* reset VLAN tag flag */ rxdw1 &= ~CP_RX_TAVA; - dot1q_buf = NULL; } - next_part_size = buf + size_ - next_part; + next_part_size = buf + buf_size - next_part; /* if too small buffer, then expand it */ if (size < MIN_BUF_SIZE) { @@ -1147,10 +1152,18 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ /* if too small buffer, then expand it */ if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); + if (dot1q_buf) { + memcpy(buf1, buf, 2 * ETHER_ADDR_LEN); + memcpy(buf1 + 2 * ETHER_ADDR_LEN, dot1q_buf, VLAN_HLEN); + memcpy(buf1 + 2 * ETHER_ADDR_LEN + VLAN_HLEN, buf + 2 * + ETHER_ADDR_LEN, buf_size - 2 * ETHER_ADDR_LEN); + } else { + memcpy(buf1, buf, size); + } memset(buf1 + size, 0, MIN_BUF_SIZE - size); buf = buf1; size = MIN_BUF_SIZE; + dot1q_buf = NULL; } /* begin ring receiver mode */ @@ -1178,10 +1191,23 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ rtl8139_write_buffer(s, (uint8_t *)&val, 4); - rtl8139_write_buffer(s, buf, size); + /* receive/copy to target memory */ + if (dot1q_buf) { + rtl8139_write_buffer(s, buf, 2 * ETHER_ADDR_LEN); + val = crc32(0, buf, 2 * ETHER_ADDR_LEN); + rtl8139_write_buffer(s, dot1q_buf, VLAN_HLEN); + val = crc32(val, dot1q_buf, VLAN_HLEN); + rtl8139_write_buffer(s, buf + 2 * ETHER_ADDR_LEN, buf_size - 2 * + ETHER_ADDR_LEN); + val = crc32(val, buf + 2 * ETHER_ADDR_LEN, buf_size - 2 * + ETHER_ADDR_LEN); + } else { + rtl8139_write_buffer(s, buf, size); + val = crc32(0, buf, size); + } /* write checksum */ - val = cpu_to_le32(crc32(0, buf, size)); + val = cpu_to_le32(val); rtl8139_write_buffer(s, (uint8_t *)&val, 4); /* correct buffer write pointer */ @@ -1205,7 +1231,7 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - return rtl8139_do_receive(nc, buf, size, 1); + return rtl8139_do_receive(nc, buf, size, 1, NULL); } static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) @@ -1780,7 +1806,8 @@ static uint32_t rtl8139_RxConfig_read(RTL8139State *s) return ret; } -static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt) +static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, + int do_interrupt, const uint8_t *dot1q_buf) { if (!size) { @@ -1791,11 +1818,22 @@ static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size if (TxLoopBack == (s->TxConfig & TxLoopBack)) { DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); - rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); + rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt, dot1q_buf); } else { - qemu_send_packet(&s->nic->nc, buf, size); + if (dot1q_buf) { + struct iovec iov[] = { + { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, + { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, + { .iov_base = buf + ETHER_ADDR_LEN * 2, + .iov_len = size - ETHER_ADDR_LEN * 2 }, + }; + + qemu_sendv_packet(&s->nic->nc, iov, ARRAY_SIZE(iov)); + } else { + qemu_send_packet(&s->nic->nc, buf, size); + } } } @@ -1829,7 +1867,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) s->TxStatus[descriptor] |= TxHostOwns; s->TxStatus[descriptor] |= TxStatOK; - rtl8139_transfer_frame(s, txbuffer, txsize, 0); + rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL); DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor)); @@ -1956,7 +1994,6 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); txdw0 = le32_to_cpu(val); - /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); txdw1 = le32_to_cpu(val); cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); @@ -1968,9 +2005,6 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) descriptor, txdw0, txdw1, txbufLO, txbufHI)); - /* TODO: the following discard cast should clean clang analyzer output */ - (void)txdw1; - /* w0 ownership flag */ #define CP_TX_OWN (1<<31) /* w0 end of ring flag */ @@ -1994,9 +2028,9 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* w0 bits 0...15 : buffer size */ #define CP_TX_BUFFER_SIZE (1<<16) #define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) -/* w1 tag available flag */ -#define CP_RX_TAGC (1<<17) -/* w1 bits 0...15 : VLAN tag */ +/* w1 add tag flag */ +#define CP_TX_TAGC (1<<17) +/* w1 bits 0...15 : VLAN tag (big endian) */ #define CP_TX_VLAN_TAG_MASK ((1<<16) - 1) /* w2 low 32bit of Rx buffer ptr */ /* w3 high 32bit of Rx buffer ptr */ @@ -2096,13 +2130,13 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* update ring data */ val = cpu_to_le32(txdw0); cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4); - /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ -// val = cpu_to_le32(txdw1); -// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4); /* Now decide if descriptor being processed is holding the last segment of packet */ if (txdw0 & CP_TX_LS) { + uint8_t dot1q_buffer_space[VLAN_HLEN]; + uint16_t *dot1q_buffer; + DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor)); /* can transfer fully assembled packet */ @@ -2111,6 +2145,21 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) int saved_size = s->cplus_txbuffer_offset; int saved_buffer_len = s->cplus_txbuffer_len; + /* create vlan tag */ + if (txdw1 & CP_TX_TAGC) { + /* the vlan tag is in BE byte order in the descriptor + * BE + le_to_cpu() + ~swap()~ = cpu */ + DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : inserting vlan tag with " + "tci: %u\n", bswap16(txdw1 & CP_TX_VLAN_TAG_MASK))); + + dot1q_buffer = (uint16_t*) dot1q_buffer_space; + dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); + /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ + dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); + } else { + dot1q_buffer = NULL; + } + /* reset the card space to protect from recursive call */ s->cplus_txbuffer = NULL; s->cplus_txbuffer_offset = 0; @@ -2264,7 +2313,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size)); - rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0); + rtl8139_transfer_frame(s, saved_buffer, tso_send_size, + 0, (uint8_t *) dot1q_buffer); /* add transferred count to TCP sequence number */ p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); @@ -2337,7 +2387,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size)); - rtl8139_transfer_frame(s, saved_buffer, saved_size, 1); + rtl8139_transfer_frame(s, saved_buffer, saved_size, 1, + (uint8_t *) dot1q_buffer); /* restore card space if there was no recursion and reset offset */ if (!s->cplus_txbuffer)
Add support to the emulated hardware to insert vlan tags in packets going from the guest to the network. Signed-off-by: Benjamin Poirier <benjamin.poirier@gmail.com> Cc: Igor V. Kovalenko <igor.v.kovalenko@gmail.com> Cc: Jason Wang <jasowang@redhat.com> Cc: Michael S. Tsirkin <mst@redhat.com> Cc: Blue Swirl <blauwirbel@gmail.com> --- hw/rtl8139.c | 107 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 79 insertions(+), 28 deletions(-)