1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /*
3 * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
4 * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
5 */
6
7 #include <linux/crc32.h>
8
9 #include "rxe.h"
10 #include "rxe_loc.h"
11
12 /**
13 * rxe_crc32() - Compute cumulative crc32 for a contiguous segment
14 * @rxe: rdma_rxe device object
15 * @crc: starting crc32 value from previous segments
16 * @next: starting address of current segment
17 * @len: length of current segment
18 *
19 * Return: the cumulative crc32 checksum
20 */
rxe_crc32(struct rxe_dev * rxe,__be32 crc,void * next,size_t len)21 static __be32 rxe_crc32(struct rxe_dev *rxe, __be32 crc, void *next, size_t len)
22 {
23 return (__force __be32)crc32_le((__force u32)crc, next, len);
24 }
25
26 /**
27 * rxe_icrc_hdr() - Compute the partial ICRC for the network and transport
28 * headers of a packet.
29 * @skb: packet buffer
30 * @pkt: packet information
31 *
32 * Return: the partial ICRC
33 */
rxe_icrc_hdr(struct sk_buff * skb,struct rxe_pkt_info * pkt)34 static __be32 rxe_icrc_hdr(struct sk_buff *skb, struct rxe_pkt_info *pkt)
35 {
36 unsigned int bth_offset = 0;
37 struct iphdr *ip4h = NULL;
38 struct ipv6hdr *ip6h = NULL;
39 struct udphdr *udph;
40 struct rxe_bth *bth;
41 __be32 crc;
42 int length;
43 int hdr_size = sizeof(struct udphdr) +
44 (skb->protocol == htons(ETH_P_IP) ?
45 sizeof(struct iphdr) : sizeof(struct ipv6hdr));
46 /* pseudo header buffer size is calculate using ipv6 header size since
47 * it is bigger than ipv4
48 */
49 u8 pshdr[sizeof(struct udphdr) +
50 sizeof(struct ipv6hdr) +
51 RXE_BTH_BYTES];
52
53 /* This seed is the result of computing a CRC with a seed of
54 * 0xfffffff and 8 bytes of 0xff representing a masked LRH.
55 */
56 crc = (__force __be32)0xdebb20e3;
57
58 if (skb->protocol == htons(ETH_P_IP)) { /* IPv4 */
59 memcpy(pshdr, ip_hdr(skb), hdr_size);
60 ip4h = (struct iphdr *)pshdr;
61 udph = (struct udphdr *)(ip4h + 1);
62
63 ip4h->ttl = 0xff;
64 ip4h->check = CSUM_MANGLED_0;
65 ip4h->tos = 0xff;
66 } else { /* IPv6 */
67 memcpy(pshdr, ipv6_hdr(skb), hdr_size);
68 ip6h = (struct ipv6hdr *)pshdr;
69 udph = (struct udphdr *)(ip6h + 1);
70
71 memset(ip6h->flow_lbl, 0xff, sizeof(ip6h->flow_lbl));
72 ip6h->priority = 0xf;
73 ip6h->hop_limit = 0xff;
74 }
75 udph->check = CSUM_MANGLED_0;
76
77 bth_offset += hdr_size;
78
79 memcpy(&pshdr[bth_offset], pkt->hdr, RXE_BTH_BYTES);
80 bth = (struct rxe_bth *)&pshdr[bth_offset];
81
82 /* exclude bth.resv8a */
83 bth->qpn |= cpu_to_be32(~BTH_QPN_MASK);
84
85 length = hdr_size + RXE_BTH_BYTES;
86 crc = rxe_crc32(pkt->rxe, crc, pshdr, length);
87
88 /* And finish to compute the CRC on the remainder of the headers. */
89 crc = rxe_crc32(pkt->rxe, crc, pkt->hdr + RXE_BTH_BYTES,
90 rxe_opcode[pkt->opcode].length - RXE_BTH_BYTES);
91 return crc;
92 }
93
94 /**
95 * rxe_icrc_check() - Compute ICRC for a packet and compare to the ICRC
96 * delivered in the packet.
97 * @skb: packet buffer
98 * @pkt: packet information
99 *
100 * Return: 0 if the values match else an error
101 */
rxe_icrc_check(struct sk_buff * skb,struct rxe_pkt_info * pkt)102 int rxe_icrc_check(struct sk_buff *skb, struct rxe_pkt_info *pkt)
103 {
104 __be32 *icrcp;
105 __be32 pkt_icrc;
106 __be32 icrc;
107
108 icrcp = (__be32 *)(pkt->hdr + pkt->paylen - RXE_ICRC_SIZE);
109 pkt_icrc = *icrcp;
110
111 icrc = rxe_icrc_hdr(skb, pkt);
112 icrc = rxe_crc32(pkt->rxe, icrc, (u8 *)payload_addr(pkt),
113 payload_size(pkt) + bth_pad(pkt));
114 icrc = ~icrc;
115
116 if (unlikely(icrc != pkt_icrc))
117 return -EINVAL;
118
119 return 0;
120 }
121
122 /**
123 * rxe_icrc_generate() - compute ICRC for a packet.
124 * @skb: packet buffer
125 * @pkt: packet information
126 */
rxe_icrc_generate(struct sk_buff * skb,struct rxe_pkt_info * pkt)127 void rxe_icrc_generate(struct sk_buff *skb, struct rxe_pkt_info *pkt)
128 {
129 __be32 *icrcp;
130 __be32 icrc;
131
132 icrcp = (__be32 *)(pkt->hdr + pkt->paylen - RXE_ICRC_SIZE);
133 icrc = rxe_icrc_hdr(skb, pkt);
134 icrc = rxe_crc32(pkt->rxe, icrc, (u8 *)payload_addr(pkt),
135 payload_size(pkt) + bth_pad(pkt));
136 *icrcp = ~icrc;
137 }
138