[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC PATCH 3/5] net_pkt: Extend packet abstraction as requi
From: |
Leonid Bloch |
Subject: |
[Qemu-devel] [RFC PATCH 3/5] net_pkt: Extend packet abstraction as requied by e1000e functionality |
Date: |
Sun, 25 Oct 2015 19:00:06 +0200 |
From: Dmitry Fleytman <address@hidden>
This patch extends TX/RX packet abstractions with features that will
be used by e1000e device implementation.
Changes are:
1. Support iovec lists for RX buffers
2. Deeper RX packets parsing
3. Loopback option for TX packets
4. Extended VLAN headers handling
Signed-off-by: Dmitry Fleytman <address@hidden>
Signed-off-by: Leonid Bloch <address@hidden>
---
hw/net/net_rx_pkt.c | 138 ++++++++++++++++++++++++++++++++++++++---------
hw/net/net_rx_pkt.h | 71 +++++++++++++++++++++++-
hw/net/net_tx_pkt.c | 77 +++++++++++++++++++-------
hw/net/net_tx_pkt.h | 59 +++++++++++++++++---
include/net/eth.h | 90 ++++++++++++++++---------------
net/eth.c | 152 +++++++++++++++++++++++++++++++++++++++++++---------
6 files changed, 463 insertions(+), 124 deletions(-)
diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c
index f4c929f..b349c19 100644
--- a/hw/net/net_rx_pkt.c
+++ b/hw/net/net_rx_pkt.c
@@ -22,17 +22,11 @@
#include "net/checksum.h"
#include "net/tap.h"
-/*
- * RX packet may contain up to 2 fragments - rebuilt eth header
- * in case of VLAN tag stripping
- * and payload received from QEMU - in any case
- */
-#define NET_MAX_RX_PACKET_FRAGMENTS (2)
-
struct NetRxPkt {
struct virtio_net_hdr virt_hdr;
uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN];
- struct iovec vec[NET_MAX_RX_PACKET_FRAGMENTS];
+ struct iovec *vec;
+ uint16_t vec_len_total;
uint16_t vec_len;
uint32_t tot_len;
uint16_t tci;
@@ -45,17 +39,26 @@ struct NetRxPkt {
bool isip6;
bool isudp;
bool istcp;
+
+ size_t l3hdr_off;
+ size_t l4hdr_off;
};
void net_rx_pkt_init(struct NetRxPkt **pkt, bool has_virt_hdr)
{
struct NetRxPkt *p = g_malloc0(sizeof *p);
p->has_virt_hdr = has_virt_hdr;
+ p->vec = NULL;
+ p->vec_len_total = 0;
*pkt = p;
}
void net_rx_pkt_uninit(struct NetRxPkt *pkt)
{
+ if (pkt->vec_len_total != 0) {
+ g_free(pkt->vec);
+ }
+
g_free(pkt);
}
@@ -65,36 +68,83 @@ struct virtio_net_hdr *net_rx_pkt_get_vhdr(struct NetRxPkt
*pkt)
return &pkt->virt_hdr;
}
-void net_rx_pkt_attach_data(struct NetRxPkt *pkt, const void *data,
- size_t len, bool strip_vlan)
+static void
+net_rx_pkt_iovec_realloc(struct NetRxPkt *pkt,
+ int new_iov_len)
+{
+ if (pkt->vec_len_total < new_iov_len) {
+ g_free(pkt->vec);
+ pkt->vec = g_malloc(sizeof(*pkt->vec) * new_iov_len);
+ pkt->vec_len_total = new_iov_len;
+ }
+}
+
+static void
+net_rx_pkt_pull_data(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt,
+ size_t ploff)
+{
+ if (pkt->vlan_stripped) {
+ net_rx_pkt_iovec_realloc(pkt, iovcnt + 1);
+
+ pkt->vec[0].iov_base = pkt->ehdr_buf;
+ pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header);
+
+ pkt->tot_len =
+ iov_size(iov, iovcnt) - ploff + sizeof(struct eth_header);
+
+ pkt->vec_len = iov_copy(pkt->vec + 1, pkt->vec_len_total - 1,
+ iov, iovcnt, ploff, pkt->tot_len);
+ } else {
+ net_rx_pkt_iovec_realloc(pkt, iovcnt);
+
+ pkt->tot_len = iov_size(iov, iovcnt) - ploff;
+ pkt->vec_len = iov_copy(pkt->vec, pkt->vec_len_total,
+ iov, iovcnt, ploff, pkt->tot_len);
+ }
+
+ eth_get_protocols(pkt->vec, pkt->vec_len, &pkt->isip4, &pkt->isip6,
+ &pkt->isudp, &pkt->istcp, &pkt->l3hdr_off, &pkt->l4hdr_off);
+}
+
+void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt,
+ size_t iovoff, bool strip_vlan)
{
uint16_t tci = 0;
- uint16_t ploff;
+ uint16_t ploff = iovoff;
assert(pkt);
pkt->vlan_stripped = false;
if (strip_vlan) {
- pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci);
+ pkt->vlan_stripped = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf,
+ &ploff, &tci);
}
- if (pkt->vlan_stripped) {
- pkt->vec[0].iov_base = pkt->ehdr_buf;
- pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header);
- pkt->vec[1].iov_base = (uint8_t *) data + ploff;
- pkt->vec[1].iov_len = len - ploff;
- pkt->vec_len = 2;
- pkt->tot_len = len - ploff + sizeof(struct eth_header);
- } else {
- pkt->vec[0].iov_base = (void *)data;
- pkt->vec[0].iov_len = len;
- pkt->vec_len = 1;
- pkt->tot_len = len;
+ pkt->tci = tci;
+
+ net_rx_pkt_pull_data(pkt, iov, iovcnt, ploff);
+}
+
+void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt,
+ size_t iovoff, bool strip_vlan,
+ uint16_t vet)
+{
+ uint16_t tci = 0;
+ uint16_t ploff = iovoff;
+ assert(pkt);
+ pkt->vlan_stripped = false;
+
+ if (strip_vlan) {
+ pkt->vlan_stripped = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet,
+ pkt->ehdr_buf,
+ &ploff, &tci);
}
pkt->tci = tci;
- eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
- &pkt->isudp, &pkt->istcp);
+ net_rx_pkt_pull_data(pkt, iov, iovcnt, ploff);
}
void net_rx_pkt_dump(struct NetRxPkt *pkt)
@@ -143,6 +193,34 @@ void net_rx_pkt_get_protocols(struct NetRxPkt *pkt,
*istcp = pkt->istcp;
}
+uint16_t net_rx_pkt_get_ip_id(struct NetRxPkt *pkt)
+{
+ assert(pkt);
+
+ if (pkt->isip4) {
+ struct ip_header iphdr = { 0 };
+ iov_to_buf(pkt->vec, pkt->vec_len, pkt->l3hdr_off,
+ &iphdr, sizeof(iphdr));
+ return be16_to_cpu(iphdr.ip_id);
+ }
+
+ return 0;
+}
+
+bool net_rx_pkt_is_tcp_ack(struct NetRxPkt *pkt)
+{
+ assert(pkt);
+
+ if (pkt->istcp) {
+ struct tcp_header tcphdr = { 0 };
+ iov_to_buf(pkt->vec, pkt->vec_len, pkt->l4hdr_off,
+ &tcphdr, sizeof(tcphdr));
+ return TCP_HEADER_FLAGS(&tcphdr) & TCP_FLAG_ACK;
+ }
+
+ return false;
+}
+
struct iovec *net_rx_pkt_get_iovec(struct NetRxPkt *pkt)
{
assert(pkt);
@@ -158,6 +236,14 @@ void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt,
memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr);
}
+void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt)
+{
+ assert(pkt);
+
+ iov_to_buf(iov, iovcnt, 0, &pkt->virt_hdr, sizeof pkt->virt_hdr);
+}
+
bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt)
{
assert(pkt);
diff --git a/hw/net/net_rx_pkt.h b/hw/net/net_rx_pkt.h
index 1e4accf..27c045e 100644
--- a/hw/net/net_rx_pkt.h
+++ b/hw/net/net_rx_pkt.h
@@ -69,6 +69,22 @@ void net_rx_pkt_get_protocols(struct NetRxPkt *pkt,
bool *isudp, bool *istcp);
/**
+* fetches IP identification for the packet
+*
+* @pkt: packet
+*
+*/
+uint16_t net_rx_pkt_get_ip_id(struct NetRxPkt *pkt);
+
+/**
+* check if given packet is a TCP ACK packet
+*
+* @pkt: packet
+*
+*/
+bool net_rx_pkt_is_tcp_ack(struct NetRxPkt *pkt);
+
+/**
* returns virtio header stored in rx context
*
* @pkt: packet
@@ -123,6 +139,37 @@ bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt);
uint16_t net_rx_pkt_get_num_frags(struct NetRxPkt *pkt);
/**
+* attach scatter-gather data to rx packet
+*
+* @pkt: packet
+* @iov: received data scatter-gather list
+* @iovcnt number of elements in iov
+* @iovoff data start offset in the iov
+* @strip_vlan: should the module strip vlan from data
+*
+*/
+void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt,
+ const struct iovec *iov,
+ int iovcnt, size_t iovoff,
+ bool strip_vlan);
+
+/**
+* attach scatter-gather data to rx packet
+*
+* @pkt: packet
+* @iov: received data scatter-gather list
+* @iovcnt number of elements in iov
+* @iovoff data start offset in the iov
+* @strip_vlan: should the module strip vlan from data
+* @vet: VLAN tag Ethernet type
+*
+*/
+void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt,
+ size_t iovoff, bool strip_vlan,
+ uint16_t vet);
+
+/**
* attach data to rx packet
*
* @pkt: packet
@@ -131,8 +178,17 @@ uint16_t net_rx_pkt_get_num_frags(struct NetRxPkt *pkt);
* @strip_vlan: should the module strip vlan from data
*
*/
-void net_rx_pkt_attach_data(struct NetRxPkt *pkt, const void *data,
- size_t len, bool strip_vlan);
+static inline void
+net_rx_pkt_attach_data(struct NetRxPkt *pkt, const void *data,
+ size_t len, bool strip_vlan)
+{
+ const struct iovec iov = {
+ .iov_base = (void *) data,
+ .iov_len = len
+ };
+
+ net_rx_pkt_attach_iovec(pkt, &iov, 1, 0, strip_vlan);
+}
/**
* returns io vector that holds the attached data
@@ -162,6 +218,17 @@ void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt,
struct virtio_net_hdr *vhdr);
/**
+* copy passed vhdr data to packet context
+*
+* @pkt: packet
+* @iov: VHDR iov
+* @iovcnt: VHDR iov array size
+*
+*/
+void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt,
+ const struct iovec *iov, int iovcnt);
+
+/**
* save packet type in packet context
*
* @pkt: packet
diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
index a2c1a76..aa1e2ef 100644
--- a/hw/net/net_tx_pkt.c
+++ b/hw/net/net_tx_pkt.c
@@ -52,6 +52,8 @@ struct NetTxPkt {
uint16_t hdr_len;
eth_pkt_types_e packet_type;
uint8_t l4proto;
+
+ bool is_loopback;
};
void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags,
@@ -86,6 +88,22 @@ void net_tx_pkt_uninit(struct NetTxPkt *pkt)
}
}
+void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt)
+{
+ uint16_t csum;
+ assert(pkt);
+ struct ip_header *ip_hdr;
+ ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base;
+
+ ip_hdr->ip_len = cpu_to_be16(pkt->payload_len +
+ pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
+
+ ip_hdr->ip_sum = 0;
+ csum = net_raw_checksum((uint8_t *)ip_hdr,
+ pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
+ ip_hdr->ip_sum = cpu_to_be16(csum);
+}
+
void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt)
{
uint16_t csum;
@@ -93,29 +111,22 @@ void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt)
assert(pkt);
uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
struct ip_header *ip_hdr;
+ ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base;
if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type &&
VIRTIO_NET_HDR_GSO_UDP != gso_type) {
return;
}
- ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base;
-
if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len >
ETH_MAX_IP_DGRAM_LEN) {
return;
}
- ip_hdr->ip_len = cpu_to_be16(pkt->payload_len +
- pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
-
- /* Calculate IP header checksum */
- ip_hdr->ip_sum = 0;
- csum = net_raw_checksum((uint8_t *)ip_hdr,
- pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
- ip_hdr->ip_sum = cpu_to_be16(csum);
+ /* Calculate IP header checksum */
+ net_tx_pkt_update_ip_hdr_checksum(pkt);
- /* Calculate IP pseudo header checksum */
+ /* Calculate IP pseudo header checksum */
ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len);
csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum));
iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags,
@@ -146,10 +157,11 @@ static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt)
l2_hdr->iov_len = 0;
return false;
} else {
- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
+ l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN;
+ l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr, 1);
}
- l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
+ l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len);
switch (l3_proto) {
case ETH_P_IP:
@@ -242,7 +254,7 @@ static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt,
uint8_t rc = VIRTIO_NET_HDR_GSO_NONE;
uint16_t l3_proto;
- l3_proto = eth_get_l3_proto(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base,
+ l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1,
pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len);
if (!tso_enable) {
@@ -308,13 +320,14 @@ void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool
tso_enable,
}
}
-void net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan)
+void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt,
+ uint16_t vlan, uint16_t vlan_ethtype)
{
bool is_new;
assert(pkt);
- eth_setup_vlan_headers(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base,
- vlan, &is_new);
+ eth_setup_vlan_headers_ex(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base,
+ vlan, vlan_ethtype, &is_new);
/* update l2hdrlen */
if (is_new) {
@@ -350,6 +363,11 @@ bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt,
hwaddr pa,
return true;
}
+bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt)
+{
+ return pkt->raw_frags > 0;
+}
+
eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt)
{
assert(pkt);
@@ -488,6 +506,16 @@ static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt
*pkt,
return fetched;
}
+static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt,
+ NetClientState *nc, const struct iovec *iov, int iov_cnt)
+{
+ if (pkt->is_loopback) {
+ nc->info->receive_iov(nc, iov, iov_cnt);
+ } else {
+ qemu_sendv_packet(nc, iov, iov_cnt);
+ }
+}
+
static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt,
NetClientState *nc)
{
@@ -526,7 +554,7 @@ static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt
*pkt,
eth_fix_ip4_checksum(l3_iov_base, l3_iov_len);
- qemu_sendv_packet(nc, fragment, dst_idx);
+ net_tx_pkt_sendv(pkt, nc, fragment, dst_idx);
fragment_offset += fragment_len;
@@ -558,10 +586,21 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState
*nc)
if (pkt->has_virt_hdr ||
pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) {
- qemu_sendv_packet(nc, pkt->vec,
+ net_tx_pkt_sendv(pkt, nc, pkt->vec,
pkt->payload_frags + NET_TX_PKT_PL_START_FRAG);
return true;
}
return net_tx_pkt_do_sw_fragmentation(pkt, nc);
}
+
+bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc)
+{
+ bool res;
+
+ pkt->is_loopback = true;
+ res = net_tx_pkt_send(pkt, nc);
+ pkt->is_loopback = false;
+
+ return res;
+}
diff --git a/hw/net/net_tx_pkt.h b/hw/net/net_tx_pkt.h
index 73a67f8..cb5983e 100644
--- a/hw/net/net_tx_pkt.h
+++ b/hw/net/net_tx_pkt.h
@@ -66,13 +66,29 @@ void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool
tso_enable,
bool csum_enable, uint32_t gso_size);
/**
- * updates vlan tag, and adds vlan header in case it is missing
- *
- * @pkt: packet
- * @vlan: VLAN tag
- *
- */
-void net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan);
+* updates vlan tag, and adds vlan header with custom ethernet type
+* in case it is missing.
+*
+* @pkt: packet
+* @vlan: VLAN tag
+* @vlan_ethtype: VLAN header Ethernet type
+*
+*/
+void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt,
+ uint16_t vlan, uint16_t vlan_ethtype);
+
+/**
+* updates vlan tag, and adds vlan header in case it is missing
+*
+* @pkt: packet
+* @vlan: VLAN tag
+*
+*/
+static inline void
+net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan)
+{
+ net_tx_pkt_setup_vlan_header_ex(pkt, vlan, ETH_P_VLAN);
+}
/**
* populate data fragment into pkt context.
@@ -86,7 +102,7 @@ bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt,
hwaddr pa,
size_t len);
/**
- * fix ip header fields and calculate checksums needed.
+ * Fix ip header fields and calculate IP header and pseudo header checksums.
*
* @pkt: packet
*
@@ -94,6 +110,14 @@ bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt,
hwaddr pa,
void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt);
/**
+ * Calculate the IP header checksum.
+ *
+ * @pkt: packet
+ *
+ */
+void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt);
+
+/**
* get length of all populated data.
*
* @pkt: packet
@@ -138,6 +162,17 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt);
bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc);
/**
+* Redirect packet directly to receive path (emulate loopback phy).
+* Handles sw offloads if vhdr is not supported.
+*
+* @pkt: packet
+* @nc: NetClientState
+* @ret: operation result
+*
+*/
+bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc);
+
+/**
* parse raw packet data and analyze offload requirements.
*
* @pkt: packet
@@ -145,4 +180,12 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState
*nc);
*/
bool net_tx_pkt_parse(struct NetTxPkt *pkt);
+/**
+* indicates if there are data fragments held by this packet object.
+*
+* @pkt: packet
+*
+*/
+bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt);
+
#endif
diff --git a/include/net/eth.h b/include/net/eth.h
index b3273b8..85d7b1e 100644
--- a/include/net/eth.h
+++ b/include/net/eth.h
@@ -68,6 +68,13 @@ typedef struct tcp_header {
uint16_t th_urp; /* urgent pointer */
} tcp_header;
+#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
+
+#define TCP_HEADER_FLAGS(tcp) \
+ TCP_FLAGS_ONLY(be16_to_cpu((tcp)->th_offset_flags))
+
+#define TCP_FLAG_ACK 0x10
+
typedef struct udp_header {
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
@@ -162,18 +169,19 @@ struct tcp_hdr {
#define PKT_GET_IP_HDR(p) \
((struct ip_header *)(((uint8_t *)(p)) + eth_get_l2_hdr_length(p)))
#define IP_HDR_GET_LEN(p) \
- ((((struct ip_header *)p)->ip_ver_len & 0x0F) << 2)
+ ((((struct ip_header *)(p))->ip_ver_len & 0x0F) << 2)
#define PKT_GET_IP_HDR_LEN(p) \
(IP_HDR_GET_LEN(PKT_GET_IP_HDR(p)))
#define PKT_GET_IP6_HDR(p) \
((struct ip6_header *) (((uint8_t *)(p)) + eth_get_l2_hdr_length(p)))
#define IP_HEADER_VERSION(ip) \
- ((ip->ip_ver_len >> 4)&0xf)
+ (((ip)->ip_ver_len >> 4)&0xf)
#define ETH_P_IP (0x0800)
#define ETH_P_IPV6 (0x86dd)
#define ETH_P_VLAN (0x8100)
#define ETH_P_DVLAN (0x88a8)
+#define ETH_P_UNKNOWN (0xffff)
#define VLAN_VID_MASK 0x0fff
#define IP_HEADER_VERSION_4 (4)
#define IP_HEADER_VERSION_6 (6)
@@ -250,15 +258,25 @@ get_eth_packet_type(const struct eth_header *ehdr)
}
static inline uint32_t
-eth_get_l2_hdr_length(const void *p)
+eth_get_l2_hdr_length(const struct iovec *iov, int iovcnt)
{
- uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto);
- struct vlan_header *hvlan = PKT_GET_VLAN_HDR(p);
+ uint8_t p[sizeof(struct eth_header) + sizeof(struct vlan_header)];
+ size_t copied = iov_to_buf(iov, iovcnt, 0, p, ARRAY_SIZE(p));
+ uint16_t proto;
+ struct vlan_header *hvlan;
+
+ if (copied < ARRAY_SIZE(p)) {
+ return copied;
+ }
+
+ proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto);
+ hvlan = PKT_GET_VLAN_HDR(p);
+
switch (proto) {
case ETH_P_VLAN:
return sizeof(struct eth_header) + sizeof(struct vlan_header);
case ETH_P_DVLAN:
- if (hvlan->h_proto == ETH_P_VLAN) {
+ if (be16_to_cpu(hvlan->h_proto) == ETH_P_VLAN) {
return sizeof(struct eth_header) + 2 * sizeof(struct vlan_header);
} else {
return sizeof(struct eth_header) + sizeof(struct vlan_header);
@@ -282,51 +300,37 @@ eth_get_pkt_tci(const void *p)
}
}
-static inline bool
-eth_strip_vlan(const void *p, uint8_t *new_ehdr_buf,
- uint16_t *payload_offset, uint16_t *tci)
-{
- uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto);
- struct vlan_header *hvlan = PKT_GET_VLAN_HDR(p);
- struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
+bool
+eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
+ uint8_t *new_ehdr_buf,
+ uint16_t *payload_offset, uint16_t *tci);
- switch (proto) {
- case ETH_P_VLAN:
- case ETH_P_DVLAN:
- memcpy(new_ehdr->h_source, PKT_GET_ETH_HDR(p)->h_source, ETH_ALEN);
- memcpy(new_ehdr->h_dest, PKT_GET_ETH_HDR(p)->h_dest, ETH_ALEN);
- new_ehdr->h_proto = hvlan->h_proto;
- *tci = be16_to_cpu(hvlan->h_tci);
- *payload_offset =
- sizeof(struct eth_header) + sizeof(struct vlan_header);
- if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
- memcpy(PKT_GET_VLAN_HDR(new_ehdr),
- PKT_GET_DVLAN_HDR(p),
- sizeof(struct vlan_header));
- *payload_offset += sizeof(struct vlan_header);
- }
- return true;
- default:
- return false;
- }
-}
+bool
+eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff,
+ uint16_t vet, uint8_t *new_ehdr_buf,
+ uint16_t *payload_offset, uint16_t *tci);
-static inline uint16_t
-eth_get_l3_proto(const void *l2hdr, size_t l2hdr_len)
+uint16_t
+eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len);
+
+void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag,
+ uint16_t vlan_ethtype, bool *is_new);
+
+static inline void
+eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
+ bool *is_new)
{
- uint8_t *proto_ptr = (uint8_t *) l2hdr + l2hdr_len - sizeof(uint16_t);
- return be16_to_cpup((uint16_t *)proto_ptr);
+ eth_setup_vlan_headers_ex(ehdr, vlan_tag, ETH_P_VLAN, is_new);
}
-void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
- bool *is_new);
uint8_t eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto);
-void eth_get_protocols(const uint8_t *headers,
- uint32_t hdr_length,
+void eth_get_protocols(const struct iovec *iov, int iovcnt,
bool *isip4, bool *isip6,
- bool *isudp, bool *istcp);
+ bool *isudp, bool *istcp,
+ size_t *l3hdr_off,
+ size_t *l4hdr_off);
void eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
void *l3hdr, size_t l3hdr_len,
@@ -340,7 +344,7 @@ uint32_t
eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl);
bool
-eth_parse_ipv6_hdr(struct iovec *pkt, int pkt_frags,
+eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
size_t ip6hdr_off, uint8_t *l4proto,
size_t *full_hdr_len);
diff --git a/net/eth.c b/net/eth.c
index 7c61132..87f30f6 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -20,8 +20,8 @@
#include "qemu-common.h"
#include "net/tap.h"
-void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
- bool *is_new)
+void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag,
+ uint16_t vlan_ethtype, bool *is_new)
{
struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr);
@@ -35,7 +35,7 @@ void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t
vlan_tag,
default:
/* No VLAN header, put a new one */
vhdr->h_proto = ehdr->h_proto;
- ehdr->h_proto = cpu_to_be16(ETH_P_VLAN);
+ ehdr->h_proto = cpu_to_be16(vlan_ethtype);
*is_new = true;
break;
}
@@ -78,61 +78,161 @@ eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr,
uint8_t l4proto)
return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
}
-void eth_get_protocols(const uint8_t *headers,
- uint32_t hdr_length,
+uint16_t
+eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len)
+{
+ uint16_t proto;
+ size_t copied = iov_to_buf(l2hdr_iov, iovcnt, l2hdr_len - sizeof(proto),
+ &proto, sizeof(proto));
+
+ return (copied == sizeof(proto)) ? be16_to_cpu(proto) : ETH_P_UNKNOWN;
+}
+
+void eth_get_protocols(const struct iovec *iov, int iovcnt,
bool *isip4, bool *isip6,
- bool *isudp, bool *istcp)
+ bool *isudp, bool *istcp,
+ size_t *l3hdr_off,
+ size_t *l4hdr_off)
{
int proto;
- size_t l2hdr_len = eth_get_l2_hdr_length(headers);
- assert(hdr_length >= eth_get_l2_hdr_length(headers));
+ size_t l2hdr_len = eth_get_l2_hdr_length(iov, iovcnt);
+
*isip4 = *isip6 = *isudp = *istcp = false;
- proto = eth_get_l3_proto(headers, l2hdr_len);
- if (proto == ETH_P_IP) {
- *isip4 = true;
+ proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len);
- struct ip_header *iphdr;
+ *l3hdr_off = l2hdr_len;
- assert(hdr_length >=
- eth_get_l2_hdr_length(headers) + sizeof(struct ip_header));
+ if (proto == ETH_P_IP) {
+ struct ip_header iphdr;
+ size_t copied = iov_to_buf(iov, iovcnt, l2hdr_len,
+ &iphdr, sizeof(iphdr));
- iphdr = PKT_GET_IP_HDR(headers);
+ *isip4 = true;
- if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
- if (iphdr->ip_p == IP_PROTO_TCP) {
+ if (copied < sizeof(iphdr)) {
+ return;
+ }
+
+ if (IP_HEADER_VERSION(&iphdr) == IP_HEADER_VERSION_4) {
+ if (iphdr.ip_p == IP_PROTO_TCP) {
*istcp = true;
- } else if (iphdr->ip_p == IP_PROTO_UDP) {
+ } else if (iphdr.ip_p == IP_PROTO_UDP) {
*isudp = true;
}
}
+
+ *l4hdr_off = l2hdr_len + IP_HDR_GET_LEN(&iphdr);
} else if (proto == ETH_P_IPV6) {
uint8_t l4proto;
size_t full_ip6hdr_len;
- struct iovec hdr_vec;
- hdr_vec.iov_base = (void *) headers;
- hdr_vec.iov_len = hdr_length;
-
*isip6 = true;
- if (eth_parse_ipv6_hdr(&hdr_vec, 1, l2hdr_len,
- &l4proto, &full_ip6hdr_len)) {
+ if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len,
+ &l4proto, &full_ip6hdr_len)) {
if (l4proto == IP_PROTO_TCP) {
*istcp = true;
} else if (l4proto == IP_PROTO_UDP) {
*isudp = true;
}
}
+
+ *l4hdr_off = l2hdr_len + full_ip6hdr_len;
}
}
+bool
+eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
+ uint8_t *new_ehdr_buf,
+ uint16_t *payload_offset, uint16_t *tci)
+{
+ struct vlan_header vlan_hdr;
+ struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
+
+ size_t copied = iov_to_buf(iov, iovcnt, iovoff,
+ new_ehdr, sizeof(*new_ehdr));
+
+ if (copied < sizeof(*new_ehdr)) {
+ return false;
+ }
+
+ switch (be16_to_cpu(new_ehdr->h_proto)) {
+ case ETH_P_VLAN:
+ case ETH_P_DVLAN:
+ copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
+ &vlan_hdr, sizeof(vlan_hdr));
+
+ if (copied < sizeof(vlan_hdr)) {
+ return false;
+ }
+
+ new_ehdr->h_proto = vlan_hdr.h_proto;
+
+ *tci = be16_to_cpu(vlan_hdr.h_tci);
+ *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
+
+ if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
+
+ copied = iov_to_buf(iov, iovcnt, *payload_offset,
+ PKT_GET_VLAN_HDR(new_ehdr), sizeof(vlan_hdr));
+
+ if (copied < sizeof(vlan_hdr)) {
+ return false;
+ }
+
+ *payload_offset += sizeof(vlan_hdr);
+ }
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff,
+ uint16_t vet, uint8_t *new_ehdr_buf,
+ uint16_t *payload_offset, uint16_t *tci)
+{
+ struct vlan_header vlan_hdr;
+ struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
+
+ size_t copied = iov_to_buf(iov, iovcnt, iovoff,
+ new_ehdr, sizeof(*new_ehdr));
+
+ if (copied < sizeof(*new_ehdr)) {
+ return false;
+ }
+
+ if (be16_to_cpu(new_ehdr->h_proto) == vet) {
+ copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
+ &vlan_hdr, sizeof(vlan_hdr));
+
+ if (copied < sizeof(vlan_hdr)) {
+ return false;
+ }
+
+ new_ehdr->h_proto = vlan_hdr.h_proto;
+
+ *tci = be16_to_cpu(vlan_hdr.h_tci);
+ *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
+ return true;
+ }
+
+ return false;
+}
+
void
eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
void *l3hdr, size_t l3hdr_len,
size_t l3payload_len,
size_t frag_offset, bool more_frags)
{
- if (eth_get_l3_proto(l2hdr, l2hdr_len) == ETH_P_IP) {
+ const struct iovec l2vec = {
+ .iov_base = (void *) l2hdr,
+ .iov_len = l2hdr_len
+ };
+
+ if (eth_get_l3_proto(&l2vec, 1, l2hdr_len) == ETH_P_IP) {
uint16_t orig_flags;
struct ip_header *iphdr = (struct ip_header *) l3hdr;
uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE;
@@ -185,7 +285,7 @@ eth_is_ip6_extension_header_type(uint8_t hdr_type)
}
}
-bool eth_parse_ipv6_hdr(struct iovec *pkt, int pkt_frags,
+bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
size_t ip6hdr_off, uint8_t *l4proto,
size_t *full_hdr_len)
{
--
2.4.3
- [Qemu-devel] [RFC PATCH 0/5] Introduce Intel 82574 GbE Controller Emulation (e1000e), Leonid Bloch, 2015/10/25
- [Qemu-devel] [RFC PATCH 1/5] net: Add macros for ETH address tracing, Leonid Bloch, 2015/10/25
- [Qemu-devel] [RFC PATCH 4/5] e1000_regs: Add definitions for Intel 82574-specific bits, Leonid Bloch, 2015/10/25
- [Qemu-devel] [RFC PATCH 3/5] net_pkt: Extend packet abstraction as requied by e1000e functionality,
Leonid Bloch <=
- [Qemu-devel] [RFC PATCH 2/5] net_pkt: Name vmxnet3 packet abstractions more generic, Leonid Bloch, 2015/10/25
- [Qemu-devel] [RFC PATCH 5/5] net: Introduce e1000e device emulation, Leonid Bloch, 2015/10/25
- Re: [Qemu-devel] [RFC PATCH 0/5] Introduce Intel 82574 GbE Controller Emulation (e1000e), Jason Wang, 2015/10/28