diff mbox

[v6,3/3] rtl8139: add vlan tag insertion

Message ID 1299803757-26812-4-git-send-email-benjamin.poirier@gmail.com
State New
Headers show

Commit Message

Benjamin Poirier March 11, 2011, 12:35 a.m. UTC
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 |   57 +++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 41 insertions(+), 16 deletions(-)

Comments

Jason Wang March 18, 2011, 9:33 a.m. UTC | #1
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 |   57 +++++++++++++++++++++++++++++++++++++++++----------------
 >  1 files changed, 41 insertions(+), 16 deletions(-)
 > 
 > diff --git a/hw/rtl8139.c b/hw/rtl8139.c
 > index 312d362..11034fb 100644
 > --- a/hw/rtl8139.c
 > +++ b/hw/rtl8139.c
 > @@ -1821,7 +1821,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)
 >      {
 > @@ -1832,11 +1833,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, NULL);
 > +        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 },

Need to protect againt the malicious guest to send packet whose size is less
than ETHER_ADDR_LEN*2. Other looks good.


 > +            };
 > +
 > +            qemu_sendv_packet(&s->nic->nc, iov, ARRAY_SIZE(iov));
 > +        } else {
 > +            qemu_send_packet(&s->nic->nc, buf, size);
 > +        }
 >      }
 >  }
 >  
 > @@ -1870,7 +1882,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));
 >  
 > @@ -1997,7 +2009,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);
 > @@ -2009,9 +2020,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 */
 > @@ -2035,9 +2043,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 */
 > @@ -2137,13 +2145,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 */
 > @@ -2152,6 +2160,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;
 > @@ -2305,7 +2328,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));
 > @@ -2378,7 +2402,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 mbox

Patch

diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 312d362..11034fb 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -1821,7 +1821,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)
     {
@@ -1832,11 +1833,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, NULL);
+        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);
+        }
     }
 }
 
@@ -1870,7 +1882,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));
 
@@ -1997,7 +2009,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);
@@ -2009,9 +2020,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 */
@@ -2035,9 +2043,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 */
@@ -2137,13 +2145,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 */
@@ -2152,6 +2160,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;
@@ -2305,7 +2328,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));
@@ -2378,7 +2402,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)