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