Signed-off-by: Dmitry Fleytman <dmi...@daynix.com> Signed-off-by: Yan Vugenfirer <y...@daynix.com> --- hw/vmxnet_debug.h | 121 ++++++++++++++++++++ hw/vmxnet_utils.c | 165 +++++++++++++++++++++++++++ hw/vmxnet_utils.h | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 606 insertions(+), 0 deletions(-) create mode 100644 hw/vmxnet_debug.h create mode 100644 hw/vmxnet_utils.c create mode 100644 hw/vmxnet_utils.h
diff --git a/hw/vmxnet_debug.h b/hw/vmxnet_debug.h new file mode 100644 index 0000000..cc3471f --- /dev/null +++ b/hw/vmxnet_debug.h @@ -0,0 +1,121 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - debugging facilities + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmi...@daynix.com> + * Tamir Shomer <tam...@daynix.com> + * Yan Vugenfirer <y...@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VMXNET_DEBUG_H +#define _QEMU_VMXNET_DEBUG_H + +#ifdef VMXNET_VERSION_2 + #define VMXNET_DEVICE_NAME "vmxnet" +#elif defined VMXNET_VERSION_3 + #define VMXNET_DEVICE_NAME "vmxnet3" +#else + #error "VMXNET version is not defined" +#endif + +/* #define DEBUG_VMXNET_CB */ +#define DEBUG_VMXNET_WARNINGS +#define DEBUG_VMXNET_ERRORS +/* #define DEBUG_VMXNET_INTERRUPTS */ +/* #define DEBUG_VMXNET_CONFIG */ +/* #define DEBUG_VMXNET_RINGS */ +/* #define DEBUG_VMXNET_PACKETS */ +/* #define DEBUG_VMXNET_SHMEM_ACCESS */ + +#ifdef DEBUG_VMXNET_SHMEM_ACCESS +#define DSHPRINTF(fmt, ...) \ + do { \ + printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DSHPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_CB +#define DCBPRINTF(fmt, ...) \ + do { \ + printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DCBPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_PACKETS +#define DPKPRINTF(fmt, ...) \ + do { \ + printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DPKPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_WARNINGS +#define DWRPRINTF(fmt, ...) \ + do { \ + printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DWRPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_ERRORS +#define DERPRINTF(fmt, ...) \ + do { \ + printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DERPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_INTERRUPTS +#define DIRPRINTF(fmt, ...) \ + do { \ + printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DIRPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_CONFIG +#define DCFPRINTF(fmt, ...) \ + do { \ + printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DCFPRINTF(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_VMXNET_RINGS +#define DRIPRINTF(fmt, ...) \ + do { \ + printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ + ## __VA_ARGS__); \ + } while (0) +#else +#define DRIPRINTF(fmt, ...) do {} while (0) +#endif + +#define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X" +#define MAC_ARG(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] + +#endif /* _QEMU_VMXNET3_DEBUG_H */ diff --git a/hw/vmxnet_utils.c b/hw/vmxnet_utils.c new file mode 100644 index 0000000..2f21023 --- /dev/null +++ b/hw/vmxnet_utils.c @@ -0,0 +1,165 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - network auxiliary code + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmi...@daynix.com> + * Tamir Shomer <tam...@daynix.com> + * Yan Vugenfirer <y...@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "hw.h" +#include "virtio-net.h" +#include "vmxnet_utils.h" +#include "net/checksum.h" + +void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag, + bool *is_new) +{ + struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr); + + switch (be16_to_cpu(ehdr->h_proto)) { + case ETH_P_VLAN: + case ETH_P_DVLAN: + /* vlan hdr exists */ + *is_new = false; + break; + + default: + /* No VLAN header, put a new one */ + vhdr->h_proto = ehdr->h_proto; + ehdr->h_proto = cpu_to_be16(ETH_P_VLAN); + *is_new = true; + break; + } + vhdr->h_tci = cpu_to_be16(vlan_tag); +} + +uint8_t +eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr) +{ + uint8_t ecn_state = 0; + + if (ETH_P_IP == l3_proto) { + struct ip_header *iphdr = (struct ip_header *) l3_hdr; + + if (IP_HEADER_VERSION_4 == IP_HEADER_VERSION(iphdr)) { + if (IPTOS_ECN_CE == IPTOS_ECN(iphdr->ip_tos)) { + ecn_state = VIRTIO_NET_HDR_GSO_ECN; + } + if (IP_PROTO_TCP == iphdr->ip_p) { + return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state; + } else if (IP_PROTO_UDP == iphdr->ip_p) { + return VIRTIO_NET_HDR_GSO_UDP | ecn_state; + } + } + } else if (ETH_P_IPV6 == l3_proto) { + struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr; + + if (IP6_ECN_CE == IP6_ECN(ip6hdr->ip6_ecn_acc)) { + ecn_state = VIRTIO_NET_HDR_GSO_ECN; + } + + if (IP_PROTO_TCP == ip6hdr->ip6_nxt) { + return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state; + } + } + + /* Unsupported offload */ + assert(false); + + return VIRTIO_NET_HDR_GSO_NONE | ecn_state; +} + +void eth_get_protocols(const uint8_t *headers, + uint32_t hdr_length, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp) +{ + int proto; + size_t l2hdr_len = eth_get_l2_hdr_length(headers); + assert(hdr_length >= eth_get_l2_hdr_length(headers)); + *isip4 = *isip6 = *isudp = *istcp = false; + + proto = eth_get_l3_proto(headers, l2hdr_len); + if (ETH_P_IP == proto) { + *isip4 = true; + + struct ip_header *iphdr; + + assert(hdr_length >= + eth_get_l2_hdr_length(headers) + sizeof(struct ip_header)); + + iphdr = PKT_GET_IP_HDR(headers); + + if (IP_HEADER_VERSION_4 == IP_HEADER_VERSION(iphdr)) { + if (IP_PROTO_TCP == iphdr->ip_p) { + *istcp = true; + } else if (IP_PROTO_UDP == iphdr->ip_p) { + *isudp = true; + } + } + } else if (ETH_P_IPV6 == proto) { + *isip6 = true; + + struct ip6_header *ip6hdr; + assert(hdr_length >= + eth_get_l2_hdr_length(headers) + sizeof(struct ip6_header)); + ip6hdr = PKT_GET_IP6_HDR(headers); + + if (IP_PROTO_TCP == ip6hdr->ip6_nxt) { + *istcp = true; + } else if (IP_PROTO_UDP == ip6hdr->ip6_nxt) { + *isudp = true; + } + } +} + +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_P_IP == eth_get_l3_proto(l2hdr, l2hdr_len)) { + uint16_t orig_flags; + struct ip_header *iphdr = (struct ip_header *) l3hdr; + uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE; + uint16_t new_ip_off; + + assert(0 == frag_offset % IP_FRAG_UNIT_SIZE); + assert(0 == (frag_off_units & ~IP_OFFMASK)); + + orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF); + new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); + iphdr->ip_off = cpu_to_be16(new_ip_off); + iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len); + } +} + +void +eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len) +{ + struct ip_header *iphdr = (struct ip_header *) l3hdr; + iphdr->ip_sum = 0; + iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len)); +} + +uint16_t +eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl) +{ + struct ip_pseudo_header ipph; + ipph.ip_src = iphdr->ip_src; + ipph.ip_dst = iphdr->ip_dst; + ipph.ip_payload = cpu_to_be16(csl); + ipph.ip_proto = iphdr->ip_p; + ipph.zeros = 0; + return net_checksum_add(sizeof(ipph), (uint8_t *) &ipph); +} diff --git a/hw/vmxnet_utils.h b/hw/vmxnet_utils.h new file mode 100644 index 0000000..7040ab9 --- /dev/null +++ b/hw/vmxnet_utils.h @@ -0,0 +1,320 @@ +/* + * QEMU VMWARE VMXNET* paravirtual NICs - network auxiliary code + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmi...@daynix.com> + * Tamir Shomer <tam...@daynix.com> + * Yan Vugenfirer <y...@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef _VMXNET_UTILS_H_ +#define _VMXNET_UTILS_H_ + +#include <sys/types.h> +#include <bswap.h> +#include <string.h> +#include <targphys.h> +#include <hw/virtio-net.h> + +#define ETH_ALEN 6 + +struct eth_header { + uint8_t h_dest[ETH_ALEN]; /* destination eth addr */ + uint8_t h_source[ETH_ALEN]; /* source ether addr */ + uint16_t h_proto; /* packet type ID field */ +}; + +struct vlan_header { + uint16_t h_tci; /* priority and VLAN ID */ + uint16_t h_proto; /* encapsulated protocol */ +}; + +struct ip_header { + uint8_t ip_ver_len; /* version and header length */ + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + uint32_t ip_src, ip_dst; /* source and dest address */ +}; + +typedef struct tcp_header { + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + uint32_t th_seq; /* sequence number */ + uint32_t th_ack; /* acknowledgement number */ + uint16_t th_offset_flags; /* data offset, reserved 6 bits, */ + /* TCP protocol flags */ + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +} tcp_header; + +typedef struct udp_header { + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +} udp_header; + +typedef struct ip_pseudo_header { + uint32_t ip_src; + uint32_t ip_dst; + uint8_t zeros; + uint8_t ip_proto; + uint16_t ip_payload; +} ip_pseudo_header; + +/* IPv6 address */ +struct in6_addr { + union { + uint8_t __u6_addr8[16]; + } __in6_u; +}; + +struct ip6_header { + union { + struct ip6_hdrctl { + uint32_t ip6_un1_flow; /* 4 bits version, 8 bits TC, + 20 bits flow-ID */ + uint16_t ip6_un1_plen; /* payload length */ + uint8_t ip6_un1_nxt; /* next header */ + uint8_t ip6_un1_hlim; /* hop limit */ + } ip6_un1; + uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits tclass */ + struct ip6_ecn_access { + uint8_t ip6_un3_vfc; /* 4 bits version, top 4 bits tclass */ + uint8_t ip6_un3_ecn; /* 2 bits ECN, top 6 bits payload length */ + } ip6_un3; + } ip6_ctlun; + struct in6_addr ip6_src; /* source address */ + struct in6_addr ip6_dst; /* destination address */ +}; + +struct udp_hdr { + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +}; + +struct tcp_hdr { + u_short th_sport; /* source port */ + u_short th_dport; /* destination port */ + uint32_t th_seq; /* sequence number */ + uint32_t th_ack; /* acknowledgement number */ +#if BYTE_ORDER == LITTLE_ENDIAN + u_char th_x2 : 4, /* (unused) */ + th_off:4; /* data offset */ +#endif +#if BYTE_ORDER == BIG_ENDIAN + u_char th_off : 4, /* data offset */ + th_x2:4; /* (unused) */ +#endif + +#define TH_ELN 0x1 /* explicit loss notification */ +#define TH_ECN 0x2 /* explicit congestion notification */ +#define TH_FS 0x4 /* fast start */ + + u_char th_flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + u_short th_win; /* window */ + u_short th_sum; /* checksum */ + u_short th_urp; /* urgent pointer */ +}; + +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_ecn_acc ip6_ctlun.ip6_un3.ip6_un3_ecn + +#define PKT_GET_ETH_HDR(p) \ + ((struct eth_header *)(p)) +#define PKT_GET_VLAN_HDR(p) \ + ((struct vlan_header *) (((uint8_t *)(p)) + sizeof(struct eth_header))) +#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) +#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) + +#define ETH_P_IP (0x0800) +#define ETH_P_IPV6 (0x86dd) +#define ETH_P_VLAN (0x8100) +#define ETH_P_DVLAN (0x88a8) +#define VLAN_VID_MASK 0x0fff +#define IP_HEADER_VERSION_4 (4) +#define IP_HEADER_VERSION_6 (6) +#define IP_PROTO_TCP (6) +#define IP_PROTO_UDP (17) +#define IPTOS_ECN_MASK 0x03 +#define IPTOS_ECN(x) ((x) & IPTOS_ECN_MASK) +#define IPTOS_ECN_CE 0x03 +#define IP6_ECN_MASK 0xC0 +#define IP6_ECN(x) ((x) & IP6_ECN_MASK) +#define IP6_ECN_CE 0xC0 +#define IP4_DONT_FRAGMENT_FLAG (1 << 14) + +#define IS_SPECIAL_VLAN_ID(x) \ + ((0 == (x)) || (0xFFF == (x))) + +#define ETH_MAX_L2_HDR_LEN \ + (sizeof(struct eth_header) + 2*sizeof(struct vlan_header)) + +#define ETH_MAX_IP4_HDR_LEN (60) +#define ETH_MAX_IP6_HDR_LEN \ + (sizeof(struct ip6_header)) +#define ETH_MAX_L3_HDR_LEN \ + (MAX(ETH_MAX_IP4_HDR_LEN, ETH_MAX_IP6_HDR_LEN)) +#define ETH_MAX_IP_DGRAM_LEN (0xFFFF) +#define ETH_MAX_IP_PLOAD_LEN \ + (ETH_MAX_IP_DGRAM_LEN - ETH_MAX_IP4_HDR_LEN - ETH_MAX_L2_HDR_LEN) + +#define IP_FRAG_UNIT_SIZE (8) +#define IP_FRAG_ALIGN_SIZE(x) ((x) & ~0x7) +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + + +/* Bitfields */ +#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag)) + +static inline int is_multicast_ether_addr(const uint8_t *addr) +{ + return 0x01 & addr[0]; +} + +static inline int is_broadcast_ether_addr(const uint8_t *addr) +{ + return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff; +} + +static inline int is_unicast_ether_addr(const uint8_t *addr) +{ + return !is_multicast_ether_addr(addr); +} + +typedef enum { + ETH_PKT_UCAST = 0xAABBCC00, + ETH_PKT_BCAST, + ETH_PKT_MCAST +} eth_pkt_types_e; + +static inline eth_pkt_types_e +get_eth_packet_type(const struct eth_header *ehdr) +{ + if (is_broadcast_ether_addr(ehdr->h_dest)) { + return ETH_PKT_BCAST; + } else if (is_multicast_ether_addr(ehdr->h_dest)) { + return ETH_PKT_MCAST; + } else { /* unicast */ + return ETH_PKT_UCAST; + } +} + +static inline uint32_t +eth_get_l2_hdr_length(const void *p) +{ + uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto); + struct vlan_header *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 (ETH_P_VLAN == hvlan->h_proto) { + return sizeof(struct eth_header) + 2*sizeof(struct vlan_header); + } else { + return sizeof(struct eth_header) + sizeof(struct vlan_header); + } + default: + return sizeof(struct eth_header); + } +} + +static inline uint16_t +eth_get_pkt_vlan_tag(const void *p) +{ + uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto); + struct vlan_header *hvlan = PKT_GET_VLAN_HDR(p); + switch (proto) { + case ETH_P_VLAN: + case ETH_P_DVLAN: + return be16_to_cpu(hvlan->h_proto); + default: + return 0; + } +} + +static inline bool +eth_strip_vlan(const void *p, struct eth_header *new_ehdr, + uint16_t *payload_offset, uint16_t *vlan_tag) +{ + uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto); + struct vlan_header *hvlan = PKT_GET_VLAN_HDR(p); + + 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; + *vlan_tag = be16_to_cpu(hvlan->h_tci); + *payload_offset = + sizeof(struct eth_header) + sizeof(struct vlan_header); + return true; + default: + return false; + } +} + +static inline uint16_t +eth_get_l3_proto(const void *l2hdr, size_t l2hdr_len) +{ + uint8_t *proto_ptr = (uint8_t *) l2hdr + l2hdr_len - sizeof(uint16_t); + return be16_to_cpup((uint16_t *)proto_ptr); +} + +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); + +void eth_get_protocols(const uint8_t *headers, + uint32_t hdr_length, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp); + +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); + +void +eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len); + +uint16_t +eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl); + +#endif + -- 1.7.7.6