]>
Commit | Line | Data |
---|---|---|
75020a70 DF |
1 | /* |
2 | * QEMU network structures definitions and helper functions | |
3 | * | |
4 | * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) | |
5 | * | |
6 | * Developed by Daynix Computing LTD (http://www.daynix.com) | |
7 | * | |
8 | * Authors: | |
9 | * Dmitry Fleytman <[email protected]> | |
10 | * Tamir Shomer <[email protected]> | |
11 | * Yan Vugenfirer <[email protected]> | |
12 | * | |
13 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
14 | * See the COPYING file in the top-level directory. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include "net/eth.h" | |
19 | #include "net/checksum.h" | |
20 | #include "qemu-common.h" | |
21 | #include "net/tap.h" | |
22 | ||
23 | void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag, | |
24 | bool *is_new) | |
25 | { | |
26 | struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr); | |
27 | ||
28 | switch (be16_to_cpu(ehdr->h_proto)) { | |
29 | case ETH_P_VLAN: | |
30 | case ETH_P_DVLAN: | |
31 | /* vlan hdr exists */ | |
32 | *is_new = false; | |
33 | break; | |
34 | ||
35 | default: | |
36 | /* No VLAN header, put a new one */ | |
37 | vhdr->h_proto = ehdr->h_proto; | |
38 | ehdr->h_proto = cpu_to_be16(ETH_P_VLAN); | |
39 | *is_new = true; | |
40 | break; | |
41 | } | |
42 | vhdr->h_tci = cpu_to_be16(vlan_tag); | |
43 | } | |
44 | ||
45 | uint8_t | |
46 | eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto) | |
47 | { | |
48 | uint8_t ecn_state = 0; | |
49 | ||
50 | if (l3_proto == ETH_P_IP) { | |
51 | struct ip_header *iphdr = (struct ip_header *) l3_hdr; | |
52 | ||
53 | if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) { | |
54 | if (IPTOS_ECN(iphdr->ip_tos) == IPTOS_ECN_CE) { | |
55 | ecn_state = VIRTIO_NET_HDR_GSO_ECN; | |
56 | } | |
57 | if (l4proto == IP_PROTO_TCP) { | |
58 | return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state; | |
59 | } else if (l4proto == IP_PROTO_UDP) { | |
60 | return VIRTIO_NET_HDR_GSO_UDP | ecn_state; | |
61 | } | |
62 | } | |
63 | } else if (l3_proto == ETH_P_IPV6) { | |
64 | struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr; | |
65 | ||
66 | if (IP6_ECN(ip6hdr->ip6_ecn_acc) == IP6_ECN_CE) { | |
67 | ecn_state = VIRTIO_NET_HDR_GSO_ECN; | |
68 | } | |
69 | ||
70 | if (l4proto == IP_PROTO_TCP) { | |
71 | return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state; | |
72 | } | |
73 | } | |
74 | ||
75 | /* Unsupported offload */ | |
dfc6f865 | 76 | g_assert_not_reached(); |
75020a70 DF |
77 | |
78 | return VIRTIO_NET_HDR_GSO_NONE | ecn_state; | |
79 | } | |
80 | ||
81 | void eth_get_protocols(const uint8_t *headers, | |
82 | uint32_t hdr_length, | |
83 | bool *isip4, bool *isip6, | |
84 | bool *isudp, bool *istcp) | |
85 | { | |
86 | int proto; | |
87 | size_t l2hdr_len = eth_get_l2_hdr_length(headers); | |
88 | assert(hdr_length >= eth_get_l2_hdr_length(headers)); | |
89 | *isip4 = *isip6 = *isudp = *istcp = false; | |
90 | ||
91 | proto = eth_get_l3_proto(headers, l2hdr_len); | |
92 | if (proto == ETH_P_IP) { | |
93 | *isip4 = true; | |
94 | ||
95 | struct ip_header *iphdr; | |
96 | ||
97 | assert(hdr_length >= | |
98 | eth_get_l2_hdr_length(headers) + sizeof(struct ip_header)); | |
99 | ||
100 | iphdr = PKT_GET_IP_HDR(headers); | |
101 | ||
102 | if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) { | |
103 | if (iphdr->ip_p == IP_PROTO_TCP) { | |
104 | *istcp = true; | |
105 | } else if (iphdr->ip_p == IP_PROTO_UDP) { | |
106 | *isudp = true; | |
107 | } | |
108 | } | |
109 | } else if (proto == ETH_P_IPV6) { | |
110 | uint8_t l4proto; | |
111 | size_t full_ip6hdr_len; | |
112 | ||
113 | struct iovec hdr_vec; | |
114 | hdr_vec.iov_base = (void *) headers; | |
115 | hdr_vec.iov_len = hdr_length; | |
116 | ||
117 | *isip6 = true; | |
118 | if (eth_parse_ipv6_hdr(&hdr_vec, 1, l2hdr_len, | |
119 | &l4proto, &full_ip6hdr_len)) { | |
120 | if (l4proto == IP_PROTO_TCP) { | |
121 | *istcp = true; | |
122 | } else if (l4proto == IP_PROTO_UDP) { | |
123 | *isudp = true; | |
124 | } | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | void | |
130 | eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len, | |
131 | void *l3hdr, size_t l3hdr_len, | |
132 | size_t l3payload_len, | |
133 | size_t frag_offset, bool more_frags) | |
134 | { | |
135 | if (eth_get_l3_proto(l2hdr, l2hdr_len) == ETH_P_IP) { | |
136 | uint16_t orig_flags; | |
137 | struct ip_header *iphdr = (struct ip_header *) l3hdr; | |
138 | uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE; | |
139 | uint16_t new_ip_off; | |
140 | ||
141 | assert(frag_offset % IP_FRAG_UNIT_SIZE == 0); | |
142 | assert((frag_off_units & ~IP_OFFMASK) == 0); | |
143 | ||
144 | orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF); | |
145 | new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); | |
146 | iphdr->ip_off = cpu_to_be16(new_ip_off); | |
147 | iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len); | |
148 | } | |
149 | } | |
150 | ||
151 | void | |
152 | eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len) | |
153 | { | |
154 | struct ip_header *iphdr = (struct ip_header *) l3hdr; | |
155 | iphdr->ip_sum = 0; | |
156 | iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len)); | |
157 | } | |
158 | ||
159 | uint32_t | |
160 | eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl) | |
161 | { | |
162 | struct ip_pseudo_header ipph; | |
163 | ipph.ip_src = iphdr->ip_src; | |
164 | ipph.ip_dst = iphdr->ip_dst; | |
165 | ipph.ip_payload = cpu_to_be16(csl); | |
166 | ipph.ip_proto = iphdr->ip_p; | |
167 | ipph.zeros = 0; | |
168 | return net_checksum_add(sizeof(ipph), (uint8_t *) &ipph); | |
169 | } | |
170 | ||
171 | static bool | |
172 | eth_is_ip6_extension_header_type(uint8_t hdr_type) | |
173 | { | |
174 | switch (hdr_type) { | |
175 | case IP6_HOP_BY_HOP: | |
176 | case IP6_ROUTING: | |
177 | case IP6_FRAGMENT: | |
178 | case IP6_ESP: | |
179 | case IP6_AUTHENTICATION: | |
180 | case IP6_DESTINATON: | |
181 | case IP6_MOBILITY: | |
182 | return true; | |
183 | default: | |
184 | return false; | |
185 | } | |
186 | } | |
187 | ||
188 | bool eth_parse_ipv6_hdr(struct iovec *pkt, int pkt_frags, | |
189 | size_t ip6hdr_off, uint8_t *l4proto, | |
190 | size_t *full_hdr_len) | |
191 | { | |
192 | struct ip6_header ip6_hdr; | |
193 | struct ip6_ext_hdr ext_hdr; | |
194 | size_t bytes_read; | |
195 | ||
196 | bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off, | |
197 | &ip6_hdr, sizeof(ip6_hdr)); | |
198 | if (bytes_read < sizeof(ip6_hdr)) { | |
199 | return false; | |
200 | } | |
201 | ||
202 | *full_hdr_len = sizeof(struct ip6_header); | |
203 | ||
204 | if (!eth_is_ip6_extension_header_type(ip6_hdr.ip6_nxt)) { | |
205 | *l4proto = ip6_hdr.ip6_nxt; | |
206 | return true; | |
207 | } | |
208 | ||
209 | do { | |
210 | bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + *full_hdr_len, | |
211 | &ext_hdr, sizeof(ext_hdr)); | |
212 | *full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY; | |
213 | } while (eth_is_ip6_extension_header_type(ext_hdr.ip6r_nxt)); | |
214 | ||
215 | *l4proto = ext_hdr.ip6r_nxt; | |
216 | return true; | |
217 | } |