162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/crc32.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "rxe.h"
1062306a36Sopenharmony_ci#include "rxe_loc.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * rxe_icrc_init() - Initialize crypto function for computing crc32
1462306a36Sopenharmony_ci * @rxe: rdma_rxe device object
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Return: 0 on success else an error
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ciint rxe_icrc_init(struct rxe_dev *rxe)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct crypto_shash *tfm;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	tfm = crypto_alloc_shash("crc32", 0, 0);
2362306a36Sopenharmony_ci	if (IS_ERR(tfm)) {
2462306a36Sopenharmony_ci		rxe_dbg_dev(rxe, "failed to init crc32 algorithm err: %ld\n",
2562306a36Sopenharmony_ci			       PTR_ERR(tfm));
2662306a36Sopenharmony_ci		return PTR_ERR(tfm);
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	rxe->tfm = tfm;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return 0;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/**
3562306a36Sopenharmony_ci * rxe_crc32() - Compute cumulative crc32 for a contiguous segment
3662306a36Sopenharmony_ci * @rxe: rdma_rxe device object
3762306a36Sopenharmony_ci * @crc: starting crc32 value from previous segments
3862306a36Sopenharmony_ci * @next: starting address of current segment
3962306a36Sopenharmony_ci * @len: length of current segment
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * Return: the cumulative crc32 checksum
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic __be32 rxe_crc32(struct rxe_dev *rxe, __be32 crc, void *next, size_t len)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	__be32 icrc;
4662306a36Sopenharmony_ci	int err;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	SHASH_DESC_ON_STACK(shash, rxe->tfm);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	shash->tfm = rxe->tfm;
5162306a36Sopenharmony_ci	*(__be32 *)shash_desc_ctx(shash) = crc;
5262306a36Sopenharmony_ci	err = crypto_shash_update(shash, next, len);
5362306a36Sopenharmony_ci	if (unlikely(err)) {
5462306a36Sopenharmony_ci		rxe_dbg_dev(rxe, "failed crc calculation, err: %d\n", err);
5562306a36Sopenharmony_ci		return (__force __be32)crc32_le((__force u32)crc, next, len);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	icrc = *(__be32 *)shash_desc_ctx(shash);
5962306a36Sopenharmony_ci	barrier_data(shash_desc_ctx(shash));
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return icrc;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/**
6562306a36Sopenharmony_ci * rxe_icrc_hdr() - Compute the partial ICRC for the network and transport
6662306a36Sopenharmony_ci *		  headers of a packet.
6762306a36Sopenharmony_ci * @skb: packet buffer
6862306a36Sopenharmony_ci * @pkt: packet information
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * Return: the partial ICRC
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic __be32 rxe_icrc_hdr(struct sk_buff *skb, struct rxe_pkt_info *pkt)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	unsigned int bth_offset = 0;
7562306a36Sopenharmony_ci	struct iphdr *ip4h = NULL;
7662306a36Sopenharmony_ci	struct ipv6hdr *ip6h = NULL;
7762306a36Sopenharmony_ci	struct udphdr *udph;
7862306a36Sopenharmony_ci	struct rxe_bth *bth;
7962306a36Sopenharmony_ci	__be32 crc;
8062306a36Sopenharmony_ci	int length;
8162306a36Sopenharmony_ci	int hdr_size = sizeof(struct udphdr) +
8262306a36Sopenharmony_ci		(skb->protocol == htons(ETH_P_IP) ?
8362306a36Sopenharmony_ci		sizeof(struct iphdr) : sizeof(struct ipv6hdr));
8462306a36Sopenharmony_ci	/* pseudo header buffer size is calculate using ipv6 header size since
8562306a36Sopenharmony_ci	 * it is bigger than ipv4
8662306a36Sopenharmony_ci	 */
8762306a36Sopenharmony_ci	u8 pshdr[sizeof(struct udphdr) +
8862306a36Sopenharmony_ci		sizeof(struct ipv6hdr) +
8962306a36Sopenharmony_ci		RXE_BTH_BYTES];
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* This seed is the result of computing a CRC with a seed of
9262306a36Sopenharmony_ci	 * 0xfffffff and 8 bytes of 0xff representing a masked LRH.
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci	crc = (__force __be32)0xdebb20e3;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP)) { /* IPv4 */
9762306a36Sopenharmony_ci		memcpy(pshdr, ip_hdr(skb), hdr_size);
9862306a36Sopenharmony_ci		ip4h = (struct iphdr *)pshdr;
9962306a36Sopenharmony_ci		udph = (struct udphdr *)(ip4h + 1);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		ip4h->ttl = 0xff;
10262306a36Sopenharmony_ci		ip4h->check = CSUM_MANGLED_0;
10362306a36Sopenharmony_ci		ip4h->tos = 0xff;
10462306a36Sopenharmony_ci	} else {				/* IPv6 */
10562306a36Sopenharmony_ci		memcpy(pshdr, ipv6_hdr(skb), hdr_size);
10662306a36Sopenharmony_ci		ip6h = (struct ipv6hdr *)pshdr;
10762306a36Sopenharmony_ci		udph = (struct udphdr *)(ip6h + 1);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		memset(ip6h->flow_lbl, 0xff, sizeof(ip6h->flow_lbl));
11062306a36Sopenharmony_ci		ip6h->priority = 0xf;
11162306a36Sopenharmony_ci		ip6h->hop_limit = 0xff;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	udph->check = CSUM_MANGLED_0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	bth_offset += hdr_size;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	memcpy(&pshdr[bth_offset], pkt->hdr, RXE_BTH_BYTES);
11862306a36Sopenharmony_ci	bth = (struct rxe_bth *)&pshdr[bth_offset];
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* exclude bth.resv8a */
12162306a36Sopenharmony_ci	bth->qpn |= cpu_to_be32(~BTH_QPN_MASK);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	length = hdr_size + RXE_BTH_BYTES;
12462306a36Sopenharmony_ci	crc = rxe_crc32(pkt->rxe, crc, pshdr, length);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* And finish to compute the CRC on the remainder of the headers. */
12762306a36Sopenharmony_ci	crc = rxe_crc32(pkt->rxe, crc, pkt->hdr + RXE_BTH_BYTES,
12862306a36Sopenharmony_ci			rxe_opcode[pkt->opcode].length - RXE_BTH_BYTES);
12962306a36Sopenharmony_ci	return crc;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * rxe_icrc_check() - Compute ICRC for a packet and compare to the ICRC
13462306a36Sopenharmony_ci *		      delivered in the packet.
13562306a36Sopenharmony_ci * @skb: packet buffer
13662306a36Sopenharmony_ci * @pkt: packet information
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * Return: 0 if the values match else an error
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ciint rxe_icrc_check(struct sk_buff *skb, struct rxe_pkt_info *pkt)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	__be32 *icrcp;
14362306a36Sopenharmony_ci	__be32 pkt_icrc;
14462306a36Sopenharmony_ci	__be32 icrc;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	icrcp = (__be32 *)(pkt->hdr + pkt->paylen - RXE_ICRC_SIZE);
14762306a36Sopenharmony_ci	pkt_icrc = *icrcp;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	icrc = rxe_icrc_hdr(skb, pkt);
15062306a36Sopenharmony_ci	icrc = rxe_crc32(pkt->rxe, icrc, (u8 *)payload_addr(pkt),
15162306a36Sopenharmony_ci				payload_size(pkt) + bth_pad(pkt));
15262306a36Sopenharmony_ci	icrc = ~icrc;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (unlikely(icrc != pkt_icrc))
15562306a36Sopenharmony_ci		return -EINVAL;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/**
16162306a36Sopenharmony_ci * rxe_icrc_generate() - compute ICRC for a packet.
16262306a36Sopenharmony_ci * @skb: packet buffer
16362306a36Sopenharmony_ci * @pkt: packet information
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_civoid rxe_icrc_generate(struct sk_buff *skb, struct rxe_pkt_info *pkt)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	__be32 *icrcp;
16862306a36Sopenharmony_ci	__be32 icrc;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	icrcp = (__be32 *)(pkt->hdr + pkt->paylen - RXE_ICRC_SIZE);
17162306a36Sopenharmony_ci	icrc = rxe_icrc_hdr(skb, pkt);
17262306a36Sopenharmony_ci	icrc = rxe_crc32(pkt->rxe, icrc, (u8 *)payload_addr(pkt),
17362306a36Sopenharmony_ci				payload_size(pkt) + bth_pad(pkt));
17462306a36Sopenharmony_ci	*icrcp = ~icrc;
17562306a36Sopenharmony_ci}
176