summaryrefslogtreecommitdiff
path: root/include/net/eth.h
blob: 18d0be3b16eba8cede1175ff58bfcdc1f7ffff5e (plain)
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 "qemu/bswap.h"
#include "qemu/iov.h"

#define ETH_ALEN 6
#define ETH_HLEN 14

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_address {
    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_address ip6_src;    /* source address */
    struct in6_address 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)      /* Internet Protocol packet  */
#define ETH_P_ARP                 (0x0806)      /* Address Resolution packet */
#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