1 | /* |
2 | * Copyright (c) 2004 Topspin Corporation. All rights reserved. |
3 | * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. |
4 | * |
5 | * This software is available to you under a choice of one of two |
6 | * licenses. You may choose to be licensed under the terms of the GNU |
7 | * General Public License (GPL) Version 2, available from the file |
8 | * COPYING in the main directory of this source tree, or the |
9 | * OpenIB.org BSD license below: |
10 | * |
11 | * Redistribution and use in source and binary forms, with or |
12 | * without modification, are permitted provided that the following |
13 | * conditions are met: |
14 | * |
15 | * - Redistributions of source code must retain the above |
16 | * copyright notice, this list of conditions and the following |
17 | * disclaimer. |
18 | * |
19 | * - Redistributions in binary form must reproduce the above |
20 | * copyright notice, this list of conditions and the following |
21 | * disclaimer in the documentation and/or other materials |
22 | * provided with the distribution. |
23 | * |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
31 | * SOFTWARE. |
32 | */ |
33 | |
34 | #include <linux/errno.h> |
35 | #include <linux/string.h> |
36 | #include <linux/export.h> |
37 | #include <linux/if_ether.h> |
38 | #include <linux/ip.h> |
39 | |
40 | #include <rdma/ib_pack.h> |
41 | |
42 | #define STRUCT_FIELD(header, field) \ |
43 | .struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field), \ |
44 | .struct_size_bytes = sizeof_field(struct ib_unpacked_ ## header, field), \ |
45 | .field_name = #header ":" #field |
46 | |
47 | static const struct ib_field lrh_table[] = { |
48 | { STRUCT_FIELD(lrh, virtual_lane), |
49 | .offset_words = 0, |
50 | .offset_bits = 0, |
51 | .size_bits = 4 }, |
52 | { STRUCT_FIELD(lrh, link_version), |
53 | .offset_words = 0, |
54 | .offset_bits = 4, |
55 | .size_bits = 4 }, |
56 | { STRUCT_FIELD(lrh, service_level), |
57 | .offset_words = 0, |
58 | .offset_bits = 8, |
59 | .size_bits = 4 }, |
60 | { RESERVED, |
61 | .offset_words = 0, |
62 | .offset_bits = 12, |
63 | .size_bits = 2 }, |
64 | { STRUCT_FIELD(lrh, link_next_header), |
65 | .offset_words = 0, |
66 | .offset_bits = 14, |
67 | .size_bits = 2 }, |
68 | { STRUCT_FIELD(lrh, destination_lid), |
69 | .offset_words = 0, |
70 | .offset_bits = 16, |
71 | .size_bits = 16 }, |
72 | { RESERVED, |
73 | .offset_words = 1, |
74 | .offset_bits = 0, |
75 | .size_bits = 5 }, |
76 | { STRUCT_FIELD(lrh, packet_length), |
77 | .offset_words = 1, |
78 | .offset_bits = 5, |
79 | .size_bits = 11 }, |
80 | { STRUCT_FIELD(lrh, source_lid), |
81 | .offset_words = 1, |
82 | .offset_bits = 16, |
83 | .size_bits = 16 } |
84 | }; |
85 | |
86 | static const struct ib_field eth_table[] = { |
87 | { STRUCT_FIELD(eth, dmac_h), |
88 | .offset_words = 0, |
89 | .offset_bits = 0, |
90 | .size_bits = 32 }, |
91 | { STRUCT_FIELD(eth, dmac_l), |
92 | .offset_words = 1, |
93 | .offset_bits = 0, |
94 | .size_bits = 16 }, |
95 | { STRUCT_FIELD(eth, smac_h), |
96 | .offset_words = 1, |
97 | .offset_bits = 16, |
98 | .size_bits = 16 }, |
99 | { STRUCT_FIELD(eth, smac_l), |
100 | .offset_words = 2, |
101 | .offset_bits = 0, |
102 | .size_bits = 32 }, |
103 | { STRUCT_FIELD(eth, type), |
104 | .offset_words = 3, |
105 | .offset_bits = 0, |
106 | .size_bits = 16 } |
107 | }; |
108 | |
109 | static const struct ib_field vlan_table[] = { |
110 | { STRUCT_FIELD(vlan, tag), |
111 | .offset_words = 0, |
112 | .offset_bits = 0, |
113 | .size_bits = 16 }, |
114 | { STRUCT_FIELD(vlan, type), |
115 | .offset_words = 0, |
116 | .offset_bits = 16, |
117 | .size_bits = 16 } |
118 | }; |
119 | |
120 | static const struct ib_field ip4_table[] = { |
121 | { STRUCT_FIELD(ip4, ver), |
122 | .offset_words = 0, |
123 | .offset_bits = 0, |
124 | .size_bits = 4 }, |
125 | { STRUCT_FIELD(ip4, hdr_len), |
126 | .offset_words = 0, |
127 | .offset_bits = 4, |
128 | .size_bits = 4 }, |
129 | { STRUCT_FIELD(ip4, tos), |
130 | .offset_words = 0, |
131 | .offset_bits = 8, |
132 | .size_bits = 8 }, |
133 | { STRUCT_FIELD(ip4, tot_len), |
134 | .offset_words = 0, |
135 | .offset_bits = 16, |
136 | .size_bits = 16 }, |
137 | { STRUCT_FIELD(ip4, id), |
138 | .offset_words = 1, |
139 | .offset_bits = 0, |
140 | .size_bits = 16 }, |
141 | { STRUCT_FIELD(ip4, frag_off), |
142 | .offset_words = 1, |
143 | .offset_bits = 16, |
144 | .size_bits = 16 }, |
145 | { STRUCT_FIELD(ip4, ttl), |
146 | .offset_words = 2, |
147 | .offset_bits = 0, |
148 | .size_bits = 8 }, |
149 | { STRUCT_FIELD(ip4, protocol), |
150 | .offset_words = 2, |
151 | .offset_bits = 8, |
152 | .size_bits = 8 }, |
153 | { STRUCT_FIELD(ip4, check), |
154 | .offset_words = 2, |
155 | .offset_bits = 16, |
156 | .size_bits = 16 }, |
157 | { STRUCT_FIELD(ip4, saddr), |
158 | .offset_words = 3, |
159 | .offset_bits = 0, |
160 | .size_bits = 32 }, |
161 | { STRUCT_FIELD(ip4, daddr), |
162 | .offset_words = 4, |
163 | .offset_bits = 0, |
164 | .size_bits = 32 } |
165 | }; |
166 | |
167 | static const struct ib_field udp_table[] = { |
168 | { STRUCT_FIELD(udp, sport), |
169 | .offset_words = 0, |
170 | .offset_bits = 0, |
171 | .size_bits = 16 }, |
172 | { STRUCT_FIELD(udp, dport), |
173 | .offset_words = 0, |
174 | .offset_bits = 16, |
175 | .size_bits = 16 }, |
176 | { STRUCT_FIELD(udp, length), |
177 | .offset_words = 1, |
178 | .offset_bits = 0, |
179 | .size_bits = 16 }, |
180 | { STRUCT_FIELD(udp, csum), |
181 | .offset_words = 1, |
182 | .offset_bits = 16, |
183 | .size_bits = 16 } |
184 | }; |
185 | |
186 | static const struct ib_field grh_table[] = { |
187 | { STRUCT_FIELD(grh, ip_version), |
188 | .offset_words = 0, |
189 | .offset_bits = 0, |
190 | .size_bits = 4 }, |
191 | { STRUCT_FIELD(grh, traffic_class), |
192 | .offset_words = 0, |
193 | .offset_bits = 4, |
194 | .size_bits = 8 }, |
195 | { STRUCT_FIELD(grh, flow_label), |
196 | .offset_words = 0, |
197 | .offset_bits = 12, |
198 | .size_bits = 20 }, |
199 | { STRUCT_FIELD(grh, payload_length), |
200 | .offset_words = 1, |
201 | .offset_bits = 0, |
202 | .size_bits = 16 }, |
203 | { STRUCT_FIELD(grh, next_header), |
204 | .offset_words = 1, |
205 | .offset_bits = 16, |
206 | .size_bits = 8 }, |
207 | { STRUCT_FIELD(grh, hop_limit), |
208 | .offset_words = 1, |
209 | .offset_bits = 24, |
210 | .size_bits = 8 }, |
211 | { STRUCT_FIELD(grh, source_gid), |
212 | .offset_words = 2, |
213 | .offset_bits = 0, |
214 | .size_bits = 128 }, |
215 | { STRUCT_FIELD(grh, destination_gid), |
216 | .offset_words = 6, |
217 | .offset_bits = 0, |
218 | .size_bits = 128 } |
219 | }; |
220 | |
221 | static const struct ib_field bth_table[] = { |
222 | { STRUCT_FIELD(bth, opcode), |
223 | .offset_words = 0, |
224 | .offset_bits = 0, |
225 | .size_bits = 8 }, |
226 | { STRUCT_FIELD(bth, solicited_event), |
227 | .offset_words = 0, |
228 | .offset_bits = 8, |
229 | .size_bits = 1 }, |
230 | { STRUCT_FIELD(bth, mig_req), |
231 | .offset_words = 0, |
232 | .offset_bits = 9, |
233 | .size_bits = 1 }, |
234 | { STRUCT_FIELD(bth, pad_count), |
235 | .offset_words = 0, |
236 | .offset_bits = 10, |
237 | .size_bits = 2 }, |
238 | { STRUCT_FIELD(bth, transport_header_version), |
239 | .offset_words = 0, |
240 | .offset_bits = 12, |
241 | .size_bits = 4 }, |
242 | { STRUCT_FIELD(bth, pkey), |
243 | .offset_words = 0, |
244 | .offset_bits = 16, |
245 | .size_bits = 16 }, |
246 | { RESERVED, |
247 | .offset_words = 1, |
248 | .offset_bits = 0, |
249 | .size_bits = 8 }, |
250 | { STRUCT_FIELD(bth, destination_qpn), |
251 | .offset_words = 1, |
252 | .offset_bits = 8, |
253 | .size_bits = 24 }, |
254 | { STRUCT_FIELD(bth, ack_req), |
255 | .offset_words = 2, |
256 | .offset_bits = 0, |
257 | .size_bits = 1 }, |
258 | { RESERVED, |
259 | .offset_words = 2, |
260 | .offset_bits = 1, |
261 | .size_bits = 7 }, |
262 | { STRUCT_FIELD(bth, psn), |
263 | .offset_words = 2, |
264 | .offset_bits = 8, |
265 | .size_bits = 24 } |
266 | }; |
267 | |
268 | static const struct ib_field deth_table[] = { |
269 | { STRUCT_FIELD(deth, qkey), |
270 | .offset_words = 0, |
271 | .offset_bits = 0, |
272 | .size_bits = 32 }, |
273 | { RESERVED, |
274 | .offset_words = 1, |
275 | .offset_bits = 0, |
276 | .size_bits = 8 }, |
277 | { STRUCT_FIELD(deth, source_qpn), |
278 | .offset_words = 1, |
279 | .offset_bits = 8, |
280 | .size_bits = 24 } |
281 | }; |
282 | |
283 | __sum16 ib_ud_ip4_csum(struct ib_ud_header *) |
284 | { |
285 | struct iphdr iph; |
286 | |
287 | iph.ihl = 5; |
288 | iph.version = 4; |
289 | iph.tos = header->ip4.tos; |
290 | iph.tot_len = header->ip4.tot_len; |
291 | iph.id = header->ip4.id; |
292 | iph.frag_off = header->ip4.frag_off; |
293 | iph.ttl = header->ip4.ttl; |
294 | iph.protocol = header->ip4.protocol; |
295 | iph.check = 0; |
296 | iph.saddr = header->ip4.saddr; |
297 | iph.daddr = header->ip4.daddr; |
298 | |
299 | return ip_fast_csum(iph: (u8 *)&iph, ihl: iph.ihl); |
300 | } |
301 | EXPORT_SYMBOL(ib_ud_ip4_csum); |
302 | |
303 | /** |
304 | * ib_ud_header_init - Initialize UD header structure |
305 | * @payload_bytes:Length of packet payload |
306 | * @lrh_present: specify if LRH is present |
307 | * @eth_present: specify if Eth header is present |
308 | * @vlan_present: packet is tagged vlan |
309 | * @grh_present: GRH flag (if non-zero, GRH will be included) |
310 | * @ip_version: if non-zero, IP header, V4 or V6, will be included |
311 | * @udp_present :if non-zero, UDP header will be included |
312 | * @immediate_present: specify if immediate data is present |
313 | * @header:Structure to initialize |
314 | */ |
315 | int (int payload_bytes, |
316 | int lrh_present, |
317 | int eth_present, |
318 | int vlan_present, |
319 | int grh_present, |
320 | int ip_version, |
321 | int udp_present, |
322 | int immediate_present, |
323 | struct ib_ud_header *) |
324 | { |
325 | size_t udp_bytes = udp_present ? IB_UDP_BYTES : 0; |
326 | |
327 | grh_present = grh_present && !ip_version; |
328 | memset(header, 0, sizeof *header); |
329 | |
330 | /* |
331 | * UDP header without IP header doesn't make sense |
332 | */ |
333 | if (udp_present && ip_version != 4 && ip_version != 6) |
334 | return -EINVAL; |
335 | |
336 | if (lrh_present) { |
337 | u16 packet_length; |
338 | |
339 | header->lrh.link_version = 0; |
340 | header->lrh.link_next_header = |
341 | grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL; |
342 | packet_length = (IB_LRH_BYTES + |
343 | IB_BTH_BYTES + |
344 | IB_DETH_BYTES + |
345 | (grh_present ? IB_GRH_BYTES : 0) + |
346 | payload_bytes + |
347 | 4 + /* ICRC */ |
348 | 3) / 4; /* round up */ |
349 | header->lrh.packet_length = cpu_to_be16(packet_length); |
350 | } |
351 | |
352 | if (vlan_present) |
353 | header->eth.type = cpu_to_be16(ETH_P_8021Q); |
354 | |
355 | if (ip_version == 6 || grh_present) { |
356 | header->grh.ip_version = 6; |
357 | header->grh.payload_length = |
358 | cpu_to_be16((udp_bytes + |
359 | IB_BTH_BYTES + |
360 | IB_DETH_BYTES + |
361 | payload_bytes + |
362 | 4 + /* ICRC */ |
363 | 3) & ~3); /* round up */ |
364 | header->grh.next_header = udp_present ? IPPROTO_UDP : 0x1b; |
365 | } |
366 | |
367 | if (ip_version == 4) { |
368 | header->ip4.ver = 4; /* version 4 */ |
369 | header->ip4.hdr_len = 5; /* 5 words */ |
370 | header->ip4.tot_len = |
371 | cpu_to_be16(IB_IP4_BYTES + |
372 | udp_bytes + |
373 | IB_BTH_BYTES + |
374 | IB_DETH_BYTES + |
375 | payload_bytes + |
376 | 4); /* ICRC */ |
377 | header->ip4.protocol = IPPROTO_UDP; |
378 | } |
379 | if (udp_present && ip_version) |
380 | header->udp.length = |
381 | cpu_to_be16(IB_UDP_BYTES + |
382 | IB_BTH_BYTES + |
383 | IB_DETH_BYTES + |
384 | payload_bytes + |
385 | 4); /* ICRC */ |
386 | |
387 | if (immediate_present) |
388 | header->bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE; |
389 | else |
390 | header->bth.opcode = IB_OPCODE_UD_SEND_ONLY; |
391 | header->bth.pad_count = (4 - payload_bytes) & 3; |
392 | header->bth.transport_header_version = 0; |
393 | |
394 | header->lrh_present = lrh_present; |
395 | header->eth_present = eth_present; |
396 | header->vlan_present = vlan_present; |
397 | header->grh_present = grh_present || (ip_version == 6); |
398 | header->ipv4_present = ip_version == 4; |
399 | header->udp_present = udp_present; |
400 | header->immediate_present = immediate_present; |
401 | return 0; |
402 | } |
403 | EXPORT_SYMBOL(ib_ud_header_init); |
404 | |
405 | /** |
406 | * ib_ud_header_pack - Pack UD header struct into wire format |
407 | * @header:UD header struct |
408 | * @buf:Buffer to pack into |
409 | * |
410 | * ib_ud_header_pack() packs the UD header structure @header into wire |
411 | * format in the buffer @buf. |
412 | */ |
413 | int (struct ib_ud_header *, |
414 | void *buf) |
415 | { |
416 | int len = 0; |
417 | |
418 | if (header->lrh_present) { |
419 | ib_pack(desc: lrh_table, ARRAY_SIZE(lrh_table), |
420 | structure: &header->lrh, buf: buf + len); |
421 | len += IB_LRH_BYTES; |
422 | } |
423 | if (header->eth_present) { |
424 | ib_pack(desc: eth_table, ARRAY_SIZE(eth_table), |
425 | structure: &header->eth, buf: buf + len); |
426 | len += IB_ETH_BYTES; |
427 | } |
428 | if (header->vlan_present) { |
429 | ib_pack(desc: vlan_table, ARRAY_SIZE(vlan_table), |
430 | structure: &header->vlan, buf: buf + len); |
431 | len += IB_VLAN_BYTES; |
432 | } |
433 | if (header->grh_present) { |
434 | ib_pack(desc: grh_table, ARRAY_SIZE(grh_table), |
435 | structure: &header->grh, buf: buf + len); |
436 | len += IB_GRH_BYTES; |
437 | } |
438 | if (header->ipv4_present) { |
439 | ib_pack(desc: ip4_table, ARRAY_SIZE(ip4_table), |
440 | structure: &header->ip4, buf: buf + len); |
441 | len += IB_IP4_BYTES; |
442 | } |
443 | if (header->udp_present) { |
444 | ib_pack(desc: udp_table, ARRAY_SIZE(udp_table), |
445 | structure: &header->udp, buf: buf + len); |
446 | len += IB_UDP_BYTES; |
447 | } |
448 | |
449 | ib_pack(desc: bth_table, ARRAY_SIZE(bth_table), |
450 | structure: &header->bth, buf: buf + len); |
451 | len += IB_BTH_BYTES; |
452 | |
453 | ib_pack(desc: deth_table, ARRAY_SIZE(deth_table), |
454 | structure: &header->deth, buf: buf + len); |
455 | len += IB_DETH_BYTES; |
456 | |
457 | if (header->immediate_present) { |
458 | memcpy(buf + len, &header->immediate_data, sizeof header->immediate_data); |
459 | len += sizeof header->immediate_data; |
460 | } |
461 | |
462 | return len; |
463 | } |
464 | EXPORT_SYMBOL(ib_ud_header_pack); |
465 | |
466 | /** |
467 | * ib_ud_header_unpack - Unpack UD header struct from wire format |
468 | * @header:UD header struct |
469 | * @buf:Buffer to pack into |
470 | * |
471 | * ib_ud_header_pack() unpacks the UD header structure @header from wire |
472 | * format in the buffer @buf. |
473 | */ |
474 | int (void *buf, |
475 | struct ib_ud_header *) |
476 | { |
477 | ib_unpack(desc: lrh_table, ARRAY_SIZE(lrh_table), |
478 | buf, structure: &header->lrh); |
479 | buf += IB_LRH_BYTES; |
480 | |
481 | if (header->lrh.link_version != 0) { |
482 | pr_warn("Invalid LRH.link_version %u\n" , |
483 | header->lrh.link_version); |
484 | return -EINVAL; |
485 | } |
486 | |
487 | switch (header->lrh.link_next_header) { |
488 | case IB_LNH_IBA_LOCAL: |
489 | header->grh_present = 0; |
490 | break; |
491 | |
492 | case IB_LNH_IBA_GLOBAL: |
493 | header->grh_present = 1; |
494 | ib_unpack(desc: grh_table, ARRAY_SIZE(grh_table), |
495 | buf, structure: &header->grh); |
496 | buf += IB_GRH_BYTES; |
497 | |
498 | if (header->grh.ip_version != 6) { |
499 | pr_warn("Invalid GRH.ip_version %u\n" , |
500 | header->grh.ip_version); |
501 | return -EINVAL; |
502 | } |
503 | if (header->grh.next_header != 0x1b) { |
504 | pr_warn("Invalid GRH.next_header 0x%02x\n" , |
505 | header->grh.next_header); |
506 | return -EINVAL; |
507 | } |
508 | break; |
509 | |
510 | default: |
511 | pr_warn("Invalid LRH.link_next_header %u\n" , |
512 | header->lrh.link_next_header); |
513 | return -EINVAL; |
514 | } |
515 | |
516 | ib_unpack(desc: bth_table, ARRAY_SIZE(bth_table), |
517 | buf, structure: &header->bth); |
518 | buf += IB_BTH_BYTES; |
519 | |
520 | switch (header->bth.opcode) { |
521 | case IB_OPCODE_UD_SEND_ONLY: |
522 | header->immediate_present = 0; |
523 | break; |
524 | case IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE: |
525 | header->immediate_present = 1; |
526 | break; |
527 | default: |
528 | pr_warn("Invalid BTH.opcode 0x%02x\n" , header->bth.opcode); |
529 | return -EINVAL; |
530 | } |
531 | |
532 | if (header->bth.transport_header_version != 0) { |
533 | pr_warn("Invalid BTH.transport_header_version %u\n" , |
534 | header->bth.transport_header_version); |
535 | return -EINVAL; |
536 | } |
537 | |
538 | ib_unpack(desc: deth_table, ARRAY_SIZE(deth_table), |
539 | buf, structure: &header->deth); |
540 | buf += IB_DETH_BYTES; |
541 | |
542 | if (header->immediate_present) |
543 | memcpy(&header->immediate_data, buf, sizeof header->immediate_data); |
544 | |
545 | return 0; |
546 | } |
547 | EXPORT_SYMBOL(ib_ud_header_unpack); |
548 | |