eth.h 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
/*
 * QEMU network structures definitions and helper functions
 *
 * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
 *
 * Developed by Daynix Computing LTD (http://www.daynix.com)
 *
 * Portions developed by Free Software Foundation, Inc
 * Copyright (C) 1991-1997, 2001, 2003, 2006 Free Software Foundation, Inc.
 * See netinet/ip6.h and netinet/in.h (GNU C Library)
 *
 * Portions developed by Igor Kovalenko
 * Copyright (c) 2006 Igor Kovalenko
 * See hw/rtl8139.c (QEMU)
 *
 * Authors:
 * Dmitry Fleytman <dmitry@daynix.com>
 * Tamir Shomer <tamirs@daynix.com>
 * Yan Vugenfirer <yan@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_ETH_H
#define QEMU_ETH_H

#include <sys/types.h>
#include <string.h>
#include "qemu/bswap.h"
#include "qemu/iov.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 destination 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;            /* acknowledgment 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 ip6_ext_hdr {
    uint8_t        ip6r_nxt;   /* next header */
    uint8_t        ip6r_len;   /* length in units of 8 octets */
};

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;     /* acknowledgment number */
#ifdef HOST_WORDS_BIGENDIAN
    u_char  th_off : 4,     /* data offset */
        th_x2:4;            /* (unused) */
#else
    u_char  th_x2 : 4,      /* (unused) */
        th_off:4;           /* data offset */
#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_DVLAN_HDR(p)       \
    (PKT_GET_VLAN_HDR(p) + 1)
#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)     \
    (((x) == 0) || ((x) == 0xFFF))

#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_IP_DGRAM_LEN  (0xFFFF)

#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 */

#define IP6_EXT_GRANULARITY   (8)  /* Size granularity for
                                      IPv6 extension headers */

/* IP6 extension header types */
#define IP6_HOP_BY_HOP        (0)
#define IP6_ROUTING           (43)
#define IP6_FRAGMENT          (44)
#define IP6_ESP               (50)
#define IP6_AUTHENTICATION    (51)
#define IP6_NONE              (59)
#define IP6_DESTINATON        (60)
#define IP6_MOBILITY          (135)

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 (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);
        }
    default:
        return sizeof(struct eth_header);
    }
}

static inline uint16_t
eth_get_pkt_tci(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_tci);
    default:
        return 0;
    }
}

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;

    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;
    }
}

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, uint8_t l4proto);

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);

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,
                   size_t ip6hdr_off, uint8_t *l4proto,
                   size_t *full_hdr_len);

#endif