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