xref: /qemu/net/eth.c (revision 1728593a)
175020a70SDmitry Fleytman /*
275020a70SDmitry Fleytman  * QEMU network structures definitions and helper functions
375020a70SDmitry Fleytman  *
475020a70SDmitry Fleytman  * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
575020a70SDmitry Fleytman  *
675020a70SDmitry Fleytman  * Developed by Daynix Computing LTD (http://www.daynix.com)
775020a70SDmitry Fleytman  *
875020a70SDmitry Fleytman  * Authors:
975020a70SDmitry Fleytman  * Dmitry Fleytman <dmitry@daynix.com>
1075020a70SDmitry Fleytman  * Tamir Shomer <tamirs@daynix.com>
1175020a70SDmitry Fleytman  * Yan Vugenfirer <yan@daynix.com>
1275020a70SDmitry Fleytman  *
1375020a70SDmitry Fleytman  * This work is licensed under the terms of the GNU GPL, version 2 or later.
1475020a70SDmitry Fleytman  * See the COPYING file in the top-level directory.
1575020a70SDmitry Fleytman  *
1675020a70SDmitry Fleytman  */
1775020a70SDmitry Fleytman 
182744d920SPeter Maydell #include "qemu/osdep.h"
197564bf77SPrasad J Pandit #include "qemu/log.h"
2075020a70SDmitry Fleytman #include "net/eth.h"
2175020a70SDmitry Fleytman #include "net/checksum.h"
2275020a70SDmitry Fleytman #include "net/tap.h"
2375020a70SDmitry Fleytman 
eth_setup_vlan_headers(struct eth_header * ehdr,size_t * ehdr_size,uint16_t vlan_tag,uint16_t vlan_ethtype)24aaa8a15cSAkihiko Odaki void eth_setup_vlan_headers(struct eth_header *ehdr, size_t *ehdr_size,
25aaa8a15cSAkihiko Odaki                             uint16_t vlan_tag, uint16_t vlan_ethtype)
2675020a70SDmitry Fleytman {
2775020a70SDmitry Fleytman     struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr);
2875020a70SDmitry Fleytman 
29aaa8a15cSAkihiko Odaki     memmove(vhdr + 1, vhdr, *ehdr_size - ETH_HLEN);
30aaa8a15cSAkihiko Odaki     vhdr->h_tci = cpu_to_be16(vlan_tag);
3175020a70SDmitry Fleytman     vhdr->h_proto = ehdr->h_proto;
32eb700029SDmitry Fleytman     ehdr->h_proto = cpu_to_be16(vlan_ethtype);
33aaa8a15cSAkihiko Odaki     *ehdr_size += sizeof(*vhdr);
3475020a70SDmitry Fleytman }
3575020a70SDmitry Fleytman 
3675020a70SDmitry Fleytman uint8_t
eth_get_gso_type(uint16_t l3_proto,uint8_t * l3_hdr,uint8_t l4proto)3775020a70SDmitry Fleytman eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto)
3875020a70SDmitry Fleytman {
3975020a70SDmitry Fleytman     uint8_t ecn_state = 0;
4075020a70SDmitry Fleytman 
4175020a70SDmitry Fleytman     if (l3_proto == ETH_P_IP) {
4275020a70SDmitry Fleytman         struct ip_header *iphdr = (struct ip_header *) l3_hdr;
4375020a70SDmitry Fleytman 
4475020a70SDmitry Fleytman         if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
4575020a70SDmitry Fleytman             if (IPTOS_ECN(iphdr->ip_tos) == IPTOS_ECN_CE) {
4675020a70SDmitry Fleytman                 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
4775020a70SDmitry Fleytman             }
4875020a70SDmitry Fleytman             if (l4proto == IP_PROTO_TCP) {
4975020a70SDmitry Fleytman                 return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state;
5075020a70SDmitry Fleytman             } else if (l4proto == IP_PROTO_UDP) {
5175020a70SDmitry Fleytman                 return VIRTIO_NET_HDR_GSO_UDP | ecn_state;
5275020a70SDmitry Fleytman             }
5375020a70SDmitry Fleytman         }
5475020a70SDmitry Fleytman     } else if (l3_proto == ETH_P_IPV6) {
5575020a70SDmitry Fleytman         struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr;
5675020a70SDmitry Fleytman 
5775020a70SDmitry Fleytman         if (IP6_ECN(ip6hdr->ip6_ecn_acc) == IP6_ECN_CE) {
5875020a70SDmitry Fleytman             ecn_state = VIRTIO_NET_HDR_GSO_ECN;
5975020a70SDmitry Fleytman         }
6075020a70SDmitry Fleytman 
6175020a70SDmitry Fleytman         if (l4proto == IP_PROTO_TCP) {
6275020a70SDmitry Fleytman             return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state;
6375020a70SDmitry Fleytman         }
6475020a70SDmitry Fleytman     }
657564bf77SPrasad J Pandit     qemu_log_mask(LOG_UNIMP, "%s: probably not GSO frame, "
667564bf77SPrasad J Pandit         "unknown L3 protocol: 0x%04"PRIx16"\n", __func__, l3_proto);
6775020a70SDmitry Fleytman 
6875020a70SDmitry Fleytman     return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
6975020a70SDmitry Fleytman }
7075020a70SDmitry Fleytman 
71eb700029SDmitry Fleytman uint16_t
eth_get_l3_proto(const struct iovec * l2hdr_iov,int iovcnt,size_t l2hdr_len)72eb700029SDmitry Fleytman eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len)
73eb700029SDmitry Fleytman {
74eb700029SDmitry Fleytman     uint16_t proto;
75eb700029SDmitry Fleytman     size_t copied;
76eb700029SDmitry Fleytman     size_t size = iov_size(l2hdr_iov, iovcnt);
77eb700029SDmitry Fleytman     size_t proto_offset = l2hdr_len - sizeof(proto);
78eb700029SDmitry Fleytman 
79eb700029SDmitry Fleytman     if (size < proto_offset) {
80eb700029SDmitry Fleytman         return ETH_P_UNKNOWN;
81eb700029SDmitry Fleytman     }
82eb700029SDmitry Fleytman 
83eb700029SDmitry Fleytman     copied = iov_to_buf(l2hdr_iov, iovcnt, proto_offset,
84eb700029SDmitry Fleytman                         &proto, sizeof(proto));
85eb700029SDmitry Fleytman 
86eb700029SDmitry Fleytman     return (copied == sizeof(proto)) ? be16_to_cpu(proto) : ETH_P_UNKNOWN;
87eb700029SDmitry Fleytman }
88eb700029SDmitry Fleytman 
89eb700029SDmitry Fleytman static bool
_eth_copy_chunk(size_t input_size,const struct iovec * iov,int iovcnt,size_t offset,size_t length,void * buffer)90eb700029SDmitry Fleytman _eth_copy_chunk(size_t input_size,
91eb700029SDmitry Fleytman                 const struct iovec *iov, int iovcnt,
92eb700029SDmitry Fleytman                 size_t offset, size_t length,
93eb700029SDmitry Fleytman                 void *buffer)
94eb700029SDmitry Fleytman {
95eb700029SDmitry Fleytman     size_t copied;
96eb700029SDmitry Fleytman 
97eb700029SDmitry Fleytman     if (input_size < offset) {
98eb700029SDmitry Fleytman         return false;
99eb700029SDmitry Fleytman     }
100eb700029SDmitry Fleytman 
101eb700029SDmitry Fleytman     copied = iov_to_buf(iov, iovcnt, offset, buffer, length);
102eb700029SDmitry Fleytman 
103eb700029SDmitry Fleytman     if (copied < length) {
104eb700029SDmitry Fleytman         return false;
105eb700029SDmitry Fleytman     }
106eb700029SDmitry Fleytman 
107eb700029SDmitry Fleytman     return true;
108eb700029SDmitry Fleytman }
109eb700029SDmitry Fleytman 
110eb700029SDmitry Fleytman static bool
_eth_tcp_has_data(bool is_ip4,const struct ip_header * ip4_hdr,const struct ip6_header * ip6_hdr,size_t full_ip6hdr_len,const struct tcp_header * tcp)111eb700029SDmitry Fleytman _eth_tcp_has_data(bool is_ip4,
112eb700029SDmitry Fleytman                   const struct ip_header  *ip4_hdr,
113eb700029SDmitry Fleytman                   const struct ip6_header *ip6_hdr,
114eb700029SDmitry Fleytman                   size_t full_ip6hdr_len,
115eb700029SDmitry Fleytman                   const struct tcp_header *tcp)
116eb700029SDmitry Fleytman {
117eb700029SDmitry Fleytman     uint32_t l4len;
118eb700029SDmitry Fleytman 
119eb700029SDmitry Fleytman     if (is_ip4) {
120eb700029SDmitry Fleytman         l4len = be16_to_cpu(ip4_hdr->ip_len) - IP_HDR_GET_LEN(ip4_hdr);
121eb700029SDmitry Fleytman     } else {
122eb700029SDmitry Fleytman         size_t opts_len = full_ip6hdr_len - sizeof(struct ip6_header);
123eb700029SDmitry Fleytman         l4len = be16_to_cpu(ip6_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen) - opts_len;
124eb700029SDmitry Fleytman     }
125eb700029SDmitry Fleytman 
126eb700029SDmitry Fleytman     return l4len > TCP_HEADER_DATA_OFFSET(tcp);
127eb700029SDmitry Fleytman }
128eb700029SDmitry Fleytman 
eth_get_protocols(const struct iovec * iov,size_t iovcnt,size_t iovoff,bool * hasip4,bool * hasip6,size_t * l3hdr_off,size_t * l4hdr_off,size_t * l5hdr_off,eth_ip6_hdr_info * ip6hdr_info,eth_ip4_hdr_info * ip4hdr_info,eth_l4_hdr_info * l4hdr_info)1292f0fa232SAkihiko Odaki void eth_get_protocols(const struct iovec *iov, size_t iovcnt, size_t iovoff,
13069ff5ef8SAkihiko Odaki                        bool *hasip4, bool *hasip6,
131eb700029SDmitry Fleytman                        size_t *l3hdr_off,
132eb700029SDmitry Fleytman                        size_t *l4hdr_off,
133eb700029SDmitry Fleytman                        size_t *l5hdr_off,
134eb700029SDmitry Fleytman                        eth_ip6_hdr_info *ip6hdr_info,
135eb700029SDmitry Fleytman                        eth_ip4_hdr_info *ip4hdr_info,
136eb700029SDmitry Fleytman                        eth_l4_hdr_info  *l4hdr_info)
13775020a70SDmitry Fleytman {
13875020a70SDmitry Fleytman     int proto;
139eb700029SDmitry Fleytman     bool fragment = false;
140eb700029SDmitry Fleytman     size_t input_size = iov_size(iov, iovcnt);
141eb700029SDmitry Fleytman     size_t copied;
14269ff5ef8SAkihiko Odaki     uint8_t ip_p;
143eb700029SDmitry Fleytman 
14465f474bbSAkihiko Odaki     *hasip4 = *hasip6 = false;
1452f0fa232SAkihiko Odaki     *l3hdr_off = iovoff + eth_get_l2_hdr_length_iov(iov, iovcnt, iovoff);
14665f474bbSAkihiko Odaki     l4hdr_info->proto = ETH_L4_HDR_PROTO_INVALID;
14775020a70SDmitry Fleytman 
1482f0fa232SAkihiko Odaki     proto = eth_get_l3_proto(iov, iovcnt, *l3hdr_off);
149eb700029SDmitry Fleytman 
15075020a70SDmitry Fleytman     if (proto == ETH_P_IP) {
151eb700029SDmitry Fleytman         struct ip_header *iphdr = &ip4hdr_info->ip4_hdr;
152eb700029SDmitry Fleytman 
1532f0fa232SAkihiko Odaki         if (input_size < *l3hdr_off) {
154eb700029SDmitry Fleytman             return;
155eb700029SDmitry Fleytman         }
156eb700029SDmitry Fleytman 
1572f0fa232SAkihiko Odaki         copied = iov_to_buf(iov, iovcnt, *l3hdr_off, iphdr, sizeof(*iphdr));
15869ff5ef8SAkihiko Odaki         if (copied < sizeof(*iphdr) ||
15969ff5ef8SAkihiko Odaki             IP_HEADER_VERSION(iphdr) != IP_HEADER_VERSION_4) {
160eb700029SDmitry Fleytman             return;
161eb700029SDmitry Fleytman         }
16275020a70SDmitry Fleytman 
16369ff5ef8SAkihiko Odaki         *hasip4 = true;
16469ff5ef8SAkihiko Odaki         ip_p = iphdr->ip_p;
165eb700029SDmitry Fleytman         ip4hdr_info->fragment = IP4_IS_FRAGMENT(iphdr);
1662f0fa232SAkihiko Odaki         *l4hdr_off = *l3hdr_off + IP_HDR_GET_LEN(iphdr);
167eb700029SDmitry Fleytman 
168eb700029SDmitry Fleytman         fragment = ip4hdr_info->fragment;
169eb700029SDmitry Fleytman     } else if (proto == ETH_P_IPV6) {
1702f0fa232SAkihiko Odaki         if (!eth_parse_ipv6_hdr(iov, iovcnt, *l3hdr_off, ip6hdr_info)) {
17169ff5ef8SAkihiko Odaki             return;
17275020a70SDmitry Fleytman         }
17369ff5ef8SAkihiko Odaki 
17469ff5ef8SAkihiko Odaki         *hasip6 = true;
17569ff5ef8SAkihiko Odaki         ip_p = ip6hdr_info->l4proto;
1762f0fa232SAkihiko Odaki         *l4hdr_off = *l3hdr_off + ip6hdr_info->full_hdr_len;
17769ff5ef8SAkihiko Odaki         fragment = ip6hdr_info->fragment;
178eb700029SDmitry Fleytman     } else {
179eb700029SDmitry Fleytman         return;
180eb700029SDmitry Fleytman     }
181eb700029SDmitry Fleytman 
18269ff5ef8SAkihiko Odaki     if (fragment) {
18369ff5ef8SAkihiko Odaki         return;
184eb700029SDmitry Fleytman     }
185eb700029SDmitry Fleytman 
18669ff5ef8SAkihiko Odaki     switch (ip_p) {
18769ff5ef8SAkihiko Odaki     case IP_PROTO_TCP:
18865f474bbSAkihiko Odaki         if (_eth_copy_chunk(input_size,
189eb700029SDmitry Fleytman                             iov, iovcnt,
190eb700029SDmitry Fleytman                             *l4hdr_off, sizeof(l4hdr_info->hdr.tcp),
19165f474bbSAkihiko Odaki                             &l4hdr_info->hdr.tcp)) {
19265f474bbSAkihiko Odaki             l4hdr_info->proto = ETH_L4_HDR_PROTO_TCP;
193eb700029SDmitry Fleytman             *l5hdr_off = *l4hdr_off +
194eb700029SDmitry Fleytman                 TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp);
195eb700029SDmitry Fleytman 
196eb700029SDmitry Fleytman             l4hdr_info->has_tcp_data =
197eb700029SDmitry Fleytman                 _eth_tcp_has_data(proto == ETH_P_IP,
198eb700029SDmitry Fleytman                                   &ip4hdr_info->ip4_hdr,
199eb700029SDmitry Fleytman                                   &ip6hdr_info->ip6_hdr,
200eb700029SDmitry Fleytman                                   *l4hdr_off - *l3hdr_off,
201eb700029SDmitry Fleytman                                   &l4hdr_info->hdr.tcp);
202eb700029SDmitry Fleytman         }
20369ff5ef8SAkihiko Odaki         break;
20469ff5ef8SAkihiko Odaki 
20569ff5ef8SAkihiko Odaki     case IP_PROTO_UDP:
20665f474bbSAkihiko Odaki         if (_eth_copy_chunk(input_size,
207eb700029SDmitry Fleytman                             iov, iovcnt,
208eb700029SDmitry Fleytman                             *l4hdr_off, sizeof(l4hdr_info->hdr.udp),
20965f474bbSAkihiko Odaki                             &l4hdr_info->hdr.udp)) {
21065f474bbSAkihiko Odaki             l4hdr_info->proto = ETH_L4_HDR_PROTO_UDP;
211eb700029SDmitry Fleytman             *l5hdr_off = *l4hdr_off + sizeof(l4hdr_info->hdr.udp);
21265f474bbSAkihiko Odaki         }
21369ff5ef8SAkihiko Odaki         break;
214907209e3SAkihiko Odaki 
215907209e3SAkihiko Odaki     case IP_PROTO_SCTP:
216907209e3SAkihiko Odaki         l4hdr_info->proto = ETH_L4_HDR_PROTO_SCTP;
217907209e3SAkihiko Odaki         break;
21875020a70SDmitry Fleytman     }
21975020a70SDmitry Fleytman }
22075020a70SDmitry Fleytman 
221566342c3SDmitry Fleytman size_t
eth_strip_vlan(const struct iovec * iov,int iovcnt,size_t iovoff,void * new_ehdr_buf,uint16_t * payload_offset,uint16_t * tci)222eb700029SDmitry Fleytman eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
22385427bf3SAkihiko Odaki                void *new_ehdr_buf,
224eb700029SDmitry Fleytman                uint16_t *payload_offset, uint16_t *tci)
225eb700029SDmitry Fleytman {
226eb700029SDmitry Fleytman     struct vlan_header vlan_hdr;
22785427bf3SAkihiko Odaki     struct eth_header *new_ehdr = new_ehdr_buf;
228eb700029SDmitry Fleytman 
229eb700029SDmitry Fleytman     size_t copied = iov_to_buf(iov, iovcnt, iovoff,
230eb700029SDmitry Fleytman                                new_ehdr, sizeof(*new_ehdr));
231eb700029SDmitry Fleytman 
232eb700029SDmitry Fleytman     if (copied < sizeof(*new_ehdr)) {
233566342c3SDmitry Fleytman         return 0;
234eb700029SDmitry Fleytman     }
235eb700029SDmitry Fleytman 
236eb700029SDmitry Fleytman     switch (be16_to_cpu(new_ehdr->h_proto)) {
237eb700029SDmitry Fleytman     case ETH_P_VLAN:
238eb700029SDmitry Fleytman     case ETH_P_DVLAN:
239eb700029SDmitry Fleytman         copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
240eb700029SDmitry Fleytman                             &vlan_hdr, sizeof(vlan_hdr));
241eb700029SDmitry Fleytman 
242eb700029SDmitry Fleytman         if (copied < sizeof(vlan_hdr)) {
243566342c3SDmitry Fleytman             return 0;
244eb700029SDmitry Fleytman         }
245eb700029SDmitry Fleytman 
246eb700029SDmitry Fleytman         new_ehdr->h_proto = vlan_hdr.h_proto;
247eb700029SDmitry Fleytman 
248eb700029SDmitry Fleytman         *tci = be16_to_cpu(vlan_hdr.h_tci);
249eb700029SDmitry Fleytman         *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
250eb700029SDmitry Fleytman 
251eb700029SDmitry Fleytman         if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
252eb700029SDmitry Fleytman 
253eb700029SDmitry Fleytman             copied = iov_to_buf(iov, iovcnt, *payload_offset,
254eb700029SDmitry Fleytman                                 PKT_GET_VLAN_HDR(new_ehdr), sizeof(vlan_hdr));
255eb700029SDmitry Fleytman 
256eb700029SDmitry Fleytman             if (copied < sizeof(vlan_hdr)) {
257566342c3SDmitry Fleytman                 return 0;
258eb700029SDmitry Fleytman             }
259eb700029SDmitry Fleytman 
260eb700029SDmitry Fleytman             *payload_offset += sizeof(vlan_hdr);
261566342c3SDmitry Fleytman 
262566342c3SDmitry Fleytman             return sizeof(struct eth_header) + sizeof(struct vlan_header);
263566342c3SDmitry Fleytman         } else {
264566342c3SDmitry Fleytman             return sizeof(struct eth_header);
265eb700029SDmitry Fleytman         }
266eb700029SDmitry Fleytman     default:
267566342c3SDmitry Fleytman         return 0;
268eb700029SDmitry Fleytman     }
269eb700029SDmitry Fleytman }
270eb700029SDmitry Fleytman 
271566342c3SDmitry Fleytman size_t
eth_strip_vlan_ex(const struct iovec * iov,int iovcnt,size_t iovoff,int index,uint16_t vet,uint16_t vet_ext,void * new_ehdr_buf,uint16_t * payload_offset,uint16_t * tci)272*7e64a9caSAkihiko Odaki eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff, int index,
273*7e64a9caSAkihiko Odaki                   uint16_t vet, uint16_t vet_ext, void *new_ehdr_buf,
274eb700029SDmitry Fleytman                   uint16_t *payload_offset, uint16_t *tci)
275eb700029SDmitry Fleytman {
276eb700029SDmitry Fleytman     struct vlan_header vlan_hdr;
277*7e64a9caSAkihiko Odaki     uint16_t *new_ehdr_proto;
278*7e64a9caSAkihiko Odaki     size_t new_ehdr_size;
279*7e64a9caSAkihiko Odaki     size_t copied;
280eb700029SDmitry Fleytman 
281*7e64a9caSAkihiko Odaki     switch (index) {
282*7e64a9caSAkihiko Odaki     case 0:
283*7e64a9caSAkihiko Odaki         new_ehdr_proto = &PKT_GET_ETH_HDR(new_ehdr_buf)->h_proto;
284*7e64a9caSAkihiko Odaki         new_ehdr_size = sizeof(struct eth_header);
285*7e64a9caSAkihiko Odaki         copied = iov_to_buf(iov, iovcnt, iovoff, new_ehdr_buf, new_ehdr_size);
286*7e64a9caSAkihiko Odaki         break;
287eb700029SDmitry Fleytman 
288*7e64a9caSAkihiko Odaki     case 1:
289*7e64a9caSAkihiko Odaki         new_ehdr_proto = &PKT_GET_VLAN_HDR(new_ehdr_buf)->h_proto;
290*7e64a9caSAkihiko Odaki         new_ehdr_size = sizeof(struct eth_header) + sizeof(struct vlan_header);
291*7e64a9caSAkihiko Odaki         copied = iov_to_buf(iov, iovcnt, iovoff, new_ehdr_buf, new_ehdr_size);
292*7e64a9caSAkihiko Odaki         if (be16_to_cpu(PKT_GET_ETH_HDR(new_ehdr_buf)->h_proto) != vet_ext) {
293*7e64a9caSAkihiko Odaki             return 0;
294*7e64a9caSAkihiko Odaki         }
295*7e64a9caSAkihiko Odaki         break;
296*7e64a9caSAkihiko Odaki 
297*7e64a9caSAkihiko Odaki     default:
298566342c3SDmitry Fleytman         return 0;
299eb700029SDmitry Fleytman     }
300eb700029SDmitry Fleytman 
301*7e64a9caSAkihiko Odaki     if (copied < new_ehdr_size || be16_to_cpu(*new_ehdr_proto) != vet) {
302*7e64a9caSAkihiko Odaki         return 0;
303*7e64a9caSAkihiko Odaki     }
304eb700029SDmitry Fleytman 
305*7e64a9caSAkihiko Odaki     copied = iov_to_buf(iov, iovcnt, iovoff + new_ehdr_size,
306*7e64a9caSAkihiko Odaki                         &vlan_hdr, sizeof(vlan_hdr));
307eb700029SDmitry Fleytman     if (copied < sizeof(vlan_hdr)) {
308566342c3SDmitry Fleytman         return 0;
309eb700029SDmitry Fleytman     }
310eb700029SDmitry Fleytman 
311*7e64a9caSAkihiko Odaki     *new_ehdr_proto = vlan_hdr.h_proto;
312*7e64a9caSAkihiko Odaki     *payload_offset = iovoff + new_ehdr_size + sizeof(vlan_hdr);
313eb700029SDmitry Fleytman     *tci = be16_to_cpu(vlan_hdr.h_tci);
314eb700029SDmitry Fleytman 
315*7e64a9caSAkihiko Odaki     return new_ehdr_size;
316eb700029SDmitry Fleytman }
317eb700029SDmitry Fleytman 
31875020a70SDmitry Fleytman void
eth_fix_ip4_checksum(void * l3hdr,size_t l3hdr_len)31975020a70SDmitry Fleytman eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len)
32075020a70SDmitry Fleytman {
32175020a70SDmitry Fleytman     struct ip_header *iphdr = (struct ip_header *) l3hdr;
32275020a70SDmitry Fleytman     iphdr->ip_sum = 0;
32375020a70SDmitry Fleytman     iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len));
32475020a70SDmitry Fleytman }
32575020a70SDmitry Fleytman 
32675020a70SDmitry Fleytman uint32_t
eth_calc_ip4_pseudo_hdr_csum(struct ip_header * iphdr,uint16_t csl,uint32_t * cso)327eb700029SDmitry Fleytman eth_calc_ip4_pseudo_hdr_csum(struct ip_header *iphdr,
328eb700029SDmitry Fleytman                              uint16_t csl,
329eb700029SDmitry Fleytman                              uint32_t *cso)
33075020a70SDmitry Fleytman {
33175020a70SDmitry Fleytman     struct ip_pseudo_header ipph;
33275020a70SDmitry Fleytman     ipph.ip_src = iphdr->ip_src;
33375020a70SDmitry Fleytman     ipph.ip_dst = iphdr->ip_dst;
33475020a70SDmitry Fleytman     ipph.ip_payload = cpu_to_be16(csl);
33575020a70SDmitry Fleytman     ipph.ip_proto = iphdr->ip_p;
33675020a70SDmitry Fleytman     ipph.zeros = 0;
337eb700029SDmitry Fleytman     *cso = sizeof(ipph);
338eb700029SDmitry Fleytman     return net_checksum_add(*cso, (uint8_t *) &ipph);
339eb700029SDmitry Fleytman }
340eb700029SDmitry Fleytman 
341eb700029SDmitry Fleytman uint32_t
eth_calc_ip6_pseudo_hdr_csum(struct ip6_header * iphdr,uint16_t csl,uint8_t l4_proto,uint32_t * cso)342eb700029SDmitry Fleytman eth_calc_ip6_pseudo_hdr_csum(struct ip6_header *iphdr,
343eb700029SDmitry Fleytman                              uint16_t csl,
344eb700029SDmitry Fleytman                              uint8_t l4_proto,
345eb700029SDmitry Fleytman                              uint32_t *cso)
346eb700029SDmitry Fleytman {
347eb700029SDmitry Fleytman     struct ip6_pseudo_header ipph;
348eb700029SDmitry Fleytman     ipph.ip6_src = iphdr->ip6_src;
349eb700029SDmitry Fleytman     ipph.ip6_dst = iphdr->ip6_dst;
350eb700029SDmitry Fleytman     ipph.len = cpu_to_be16(csl);
351eb700029SDmitry Fleytman     ipph.zero[0] = 0;
352eb700029SDmitry Fleytman     ipph.zero[1] = 0;
353eb700029SDmitry Fleytman     ipph.zero[2] = 0;
354eb700029SDmitry Fleytman     ipph.next_hdr = l4_proto;
355eb700029SDmitry Fleytman     *cso = sizeof(ipph);
356eb700029SDmitry Fleytman     return net_checksum_add(*cso, (uint8_t *)&ipph);
35775020a70SDmitry Fleytman }
35875020a70SDmitry Fleytman 
35975020a70SDmitry Fleytman static bool
eth_is_ip6_extension_header_type(uint8_t hdr_type)36075020a70SDmitry Fleytman eth_is_ip6_extension_header_type(uint8_t hdr_type)
36175020a70SDmitry Fleytman {
36275020a70SDmitry Fleytman     switch (hdr_type) {
36375020a70SDmitry Fleytman     case IP6_HOP_BY_HOP:
36475020a70SDmitry Fleytman     case IP6_ROUTING:
36575020a70SDmitry Fleytman     case IP6_FRAGMENT:
36675020a70SDmitry Fleytman     case IP6_AUTHENTICATION:
36775020a70SDmitry Fleytman     case IP6_DESTINATON:
36875020a70SDmitry Fleytman     case IP6_MOBILITY:
36975020a70SDmitry Fleytman         return true;
37075020a70SDmitry Fleytman     default:
37175020a70SDmitry Fleytman         return false;
37275020a70SDmitry Fleytman     }
37375020a70SDmitry Fleytman }
37475020a70SDmitry Fleytman 
375eb700029SDmitry Fleytman static bool
_eth_get_rss_ex_dst_addr(const struct iovec * pkt,int pkt_frags,size_t ext_hdr_offset,struct ip6_ext_hdr * ext_hdr,struct in6_address * dst_addr)376eb700029SDmitry Fleytman _eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags,
377dbd8d3f9SPhilippe Mathieu-Daudé                         size_t ext_hdr_offset,
378eb700029SDmitry Fleytman                         struct ip6_ext_hdr *ext_hdr,
379eb700029SDmitry Fleytman                         struct in6_address *dst_addr)
38075020a70SDmitry Fleytman {
3817d6a4f12SPhilippe Mathieu-Daudé     struct ip6_ext_hdr_routing rt_hdr;
382eb700029SDmitry Fleytman     size_t input_size = iov_size(pkt, pkt_frags);
38375020a70SDmitry Fleytman     size_t bytes_read;
38475020a70SDmitry Fleytman 
3857d6a4f12SPhilippe Mathieu-Daudé     if (input_size < ext_hdr_offset + sizeof(rt_hdr) + sizeof(*dst_addr)) {
38675020a70SDmitry Fleytman         return false;
38775020a70SDmitry Fleytman     }
38875020a70SDmitry Fleytman 
3897d6a4f12SPhilippe Mathieu-Daudé     bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset,
3907d6a4f12SPhilippe Mathieu-Daudé                             &rt_hdr, sizeof(rt_hdr));
3917d6a4f12SPhilippe Mathieu-Daudé     assert(bytes_read == sizeof(rt_hdr));
392c7274b5eSPhilippe Mathieu-Daudé     if ((rt_hdr.rtype != 2) || (rt_hdr.segleft != 1)) {
393c7274b5eSPhilippe Mathieu-Daudé         return false;
394c7274b5eSPhilippe Mathieu-Daudé     }
3957d6a4f12SPhilippe Mathieu-Daudé     bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset + sizeof(rt_hdr),
3964555ca68SPaolo Bonzini                             dst_addr, sizeof(*dst_addr));
397c7274b5eSPhilippe Mathieu-Daudé     assert(bytes_read == sizeof(*dst_addr));
39875020a70SDmitry Fleytman 
399c7274b5eSPhilippe Mathieu-Daudé     return true;
400eb700029SDmitry Fleytman }
401eb700029SDmitry Fleytman 
402eb700029SDmitry Fleytman static bool
_eth_get_rss_ex_src_addr(const struct iovec * pkt,int pkt_frags,size_t dsthdr_offset,struct ip6_ext_hdr * ext_hdr,struct in6_address * src_addr)403eb700029SDmitry Fleytman _eth_get_rss_ex_src_addr(const struct iovec *pkt, int pkt_frags,
404eb700029SDmitry Fleytman                         size_t dsthdr_offset,
405eb700029SDmitry Fleytman                         struct ip6_ext_hdr *ext_hdr,
406eb700029SDmitry Fleytman                         struct in6_address *src_addr)
407eb700029SDmitry Fleytman {
408eb700029SDmitry Fleytman     size_t bytes_left = (ext_hdr->ip6r_len + 1) * 8 - sizeof(*ext_hdr);
409eb700029SDmitry Fleytman     struct ip6_option_hdr opthdr;
410eb700029SDmitry Fleytman     size_t opt_offset = dsthdr_offset + sizeof(*ext_hdr);
411eb700029SDmitry Fleytman 
412eb700029SDmitry Fleytman     while (bytes_left > sizeof(opthdr)) {
413eb700029SDmitry Fleytman         size_t input_size = iov_size(pkt, pkt_frags);
414eb700029SDmitry Fleytman         size_t bytes_read, optlen;
415eb700029SDmitry Fleytman 
416eb700029SDmitry Fleytman         if (input_size < opt_offset) {
417eb700029SDmitry Fleytman             return false;
418eb700029SDmitry Fleytman         }
419eb700029SDmitry Fleytman 
420eb700029SDmitry Fleytman         bytes_read = iov_to_buf(pkt, pkt_frags, opt_offset,
421eb700029SDmitry Fleytman                                 &opthdr, sizeof(opthdr));
422eb700029SDmitry Fleytman 
423eb700029SDmitry Fleytman         if (bytes_read != sizeof(opthdr)) {
424eb700029SDmitry Fleytman             return false;
425eb700029SDmitry Fleytman         }
426eb700029SDmitry Fleytman 
427eb700029SDmitry Fleytman         optlen = (opthdr.type == IP6_OPT_PAD1) ? 1
428eb700029SDmitry Fleytman                                                : (opthdr.len + sizeof(opthdr));
429eb700029SDmitry Fleytman 
430eb700029SDmitry Fleytman         if (optlen > bytes_left) {
431eb700029SDmitry Fleytman             return false;
432eb700029SDmitry Fleytman         }
433eb700029SDmitry Fleytman 
434eb700029SDmitry Fleytman         if (opthdr.type == IP6_OPT_HOME) {
435eb700029SDmitry Fleytman             if (input_size < opt_offset + sizeof(opthdr)) {
436eb700029SDmitry Fleytman                 return false;
437eb700029SDmitry Fleytman             }
438eb700029SDmitry Fleytman 
439eb700029SDmitry Fleytman             bytes_read = iov_to_buf(pkt, pkt_frags,
440eb700029SDmitry Fleytman                                     opt_offset + sizeof(opthdr),
4414555ca68SPaolo Bonzini                                     src_addr, sizeof(*src_addr));
442eb700029SDmitry Fleytman 
443b2caa3b8SPhilippe Mathieu-Daudé             return bytes_read == sizeof(*src_addr);
444eb700029SDmitry Fleytman         }
445eb700029SDmitry Fleytman 
446eb700029SDmitry Fleytman         opt_offset += optlen;
447eb700029SDmitry Fleytman         bytes_left -= optlen;
448eb700029SDmitry Fleytman     }
449eb700029SDmitry Fleytman 
450eb700029SDmitry Fleytman     return false;
451eb700029SDmitry Fleytman }
452eb700029SDmitry Fleytman 
eth_parse_ipv6_hdr(const struct iovec * pkt,int pkt_frags,size_t ip6hdr_off,eth_ip6_hdr_info * info)453eb700029SDmitry Fleytman bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
454eb700029SDmitry Fleytman                         size_t ip6hdr_off, eth_ip6_hdr_info *info)
455eb700029SDmitry Fleytman {
456eb700029SDmitry Fleytman     struct ip6_ext_hdr ext_hdr;
457eb700029SDmitry Fleytman     size_t bytes_read;
458eb700029SDmitry Fleytman     uint8_t curr_ext_hdr_type;
459eb700029SDmitry Fleytman     size_t input_size = iov_size(pkt, pkt_frags);
460eb700029SDmitry Fleytman 
461eb700029SDmitry Fleytman     info->rss_ex_dst_valid = false;
462eb700029SDmitry Fleytman     info->rss_ex_src_valid = false;
463eb700029SDmitry Fleytman     info->fragment = false;
464eb700029SDmitry Fleytman 
465eb700029SDmitry Fleytman     if (input_size < ip6hdr_off) {
466eb700029SDmitry Fleytman         return false;
467eb700029SDmitry Fleytman     }
468eb700029SDmitry Fleytman 
469eb700029SDmitry Fleytman     bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off,
470eb700029SDmitry Fleytman                             &info->ip6_hdr, sizeof(info->ip6_hdr));
471eb700029SDmitry Fleytman     if (bytes_read < sizeof(info->ip6_hdr)) {
472eb700029SDmitry Fleytman         return false;
473eb700029SDmitry Fleytman     }
474eb700029SDmitry Fleytman 
475eb700029SDmitry Fleytman     info->full_hdr_len = sizeof(struct ip6_header);
476eb700029SDmitry Fleytman 
477eb700029SDmitry Fleytman     curr_ext_hdr_type = info->ip6_hdr.ip6_nxt;
478eb700029SDmitry Fleytman 
479eb700029SDmitry Fleytman     if (!eth_is_ip6_extension_header_type(curr_ext_hdr_type)) {
480eb700029SDmitry Fleytman         info->l4proto = info->ip6_hdr.ip6_nxt;
481eb700029SDmitry Fleytman         info->has_ext_hdrs = false;
48275020a70SDmitry Fleytman         return true;
48375020a70SDmitry Fleytman     }
48475020a70SDmitry Fleytman 
485eb700029SDmitry Fleytman     info->has_ext_hdrs = true;
48675020a70SDmitry Fleytman 
487eb700029SDmitry Fleytman     do {
488eb700029SDmitry Fleytman         if (input_size < ip6hdr_off + info->full_hdr_len) {
489eb700029SDmitry Fleytman             return false;
490eb700029SDmitry Fleytman         }
491eb700029SDmitry Fleytman 
492eb700029SDmitry Fleytman         bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + info->full_hdr_len,
493eb700029SDmitry Fleytman                                 &ext_hdr, sizeof(ext_hdr));
494eb700029SDmitry Fleytman 
495eb700029SDmitry Fleytman         if (bytes_read < sizeof(ext_hdr)) {
496eb700029SDmitry Fleytman             return false;
497eb700029SDmitry Fleytman         }
498eb700029SDmitry Fleytman 
499eb700029SDmitry Fleytman         if (curr_ext_hdr_type == IP6_ROUTING) {
50038462440SPhilippe Mathieu-Daudé             if (ext_hdr.ip6r_len == sizeof(struct in6_address) / 8) {
501eb700029SDmitry Fleytman                 info->rss_ex_dst_valid =
502eb700029SDmitry Fleytman                     _eth_get_rss_ex_dst_addr(pkt, pkt_frags,
503eb700029SDmitry Fleytman                                              ip6hdr_off + info->full_hdr_len,
504eb700029SDmitry Fleytman                                              &ext_hdr, &info->rss_ex_dst);
50538462440SPhilippe Mathieu-Daudé             }
506eb700029SDmitry Fleytman         } else if (curr_ext_hdr_type == IP6_DESTINATON) {
507eb700029SDmitry Fleytman             info->rss_ex_src_valid =
508eb700029SDmitry Fleytman                 _eth_get_rss_ex_src_addr(pkt, pkt_frags,
509eb700029SDmitry Fleytman                                          ip6hdr_off + info->full_hdr_len,
510eb700029SDmitry Fleytman                                          &ext_hdr, &info->rss_ex_src);
511eb700029SDmitry Fleytman         } else if (curr_ext_hdr_type == IP6_FRAGMENT) {
512eb700029SDmitry Fleytman             info->fragment = true;
513eb700029SDmitry Fleytman         }
514eb700029SDmitry Fleytman 
515eb700029SDmitry Fleytman         info->full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY;
516eb700029SDmitry Fleytman         curr_ext_hdr_type = ext_hdr.ip6r_nxt;
517eb700029SDmitry Fleytman     } while (eth_is_ip6_extension_header_type(curr_ext_hdr_type));
518eb700029SDmitry Fleytman 
519eb700029SDmitry Fleytman     info->l4proto = ext_hdr.ip6r_nxt;
52075020a70SDmitry Fleytman     return true;
52175020a70SDmitry Fleytman }
522af774513SBin Meng 
eth_pad_short_frame(uint8_t * padded_pkt,size_t * padded_buflen,const void * pkt,size_t pkt_size)523af774513SBin Meng bool eth_pad_short_frame(uint8_t *padded_pkt, size_t *padded_buflen,
524af774513SBin Meng                          const void *pkt, size_t pkt_size)
525af774513SBin Meng {
526af774513SBin Meng     assert(padded_buflen && *padded_buflen >= ETH_ZLEN);
527af774513SBin Meng 
528af774513SBin Meng     if (pkt_size >= ETH_ZLEN) {
529af774513SBin Meng         return false;
530af774513SBin Meng     }
531af774513SBin Meng 
532af774513SBin Meng     /* pad to minimum Ethernet frame length */
533af774513SBin Meng     memcpy(padded_pkt, pkt, pkt_size);
534af774513SBin Meng     memset(&padded_pkt[pkt_size], 0, ETH_ZLEN - pkt_size);
535af774513SBin Meng     *padded_buflen = ETH_ZLEN;
536af774513SBin Meng 
537af774513SBin Meng     return true;
538af774513SBin Meng }
539