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/skbuff.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "rxe.h" 1062306a36Sopenharmony_ci#include "rxe_loc.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* check that QP matches packet opcode type and is in a valid state */ 1362306a36Sopenharmony_cistatic int check_type_state(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, 1462306a36Sopenharmony_ci struct rxe_qp *qp) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci unsigned int pkt_type; 1762306a36Sopenharmony_ci unsigned long flags; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci if (unlikely(!qp->valid)) 2062306a36Sopenharmony_ci return -EINVAL; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci pkt_type = pkt->opcode & 0xe0; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci switch (qp_type(qp)) { 2562306a36Sopenharmony_ci case IB_QPT_RC: 2662306a36Sopenharmony_ci if (unlikely(pkt_type != IB_OPCODE_RC)) 2762306a36Sopenharmony_ci return -EINVAL; 2862306a36Sopenharmony_ci break; 2962306a36Sopenharmony_ci case IB_QPT_UC: 3062306a36Sopenharmony_ci if (unlikely(pkt_type != IB_OPCODE_UC)) 3162306a36Sopenharmony_ci return -EINVAL; 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci case IB_QPT_UD: 3462306a36Sopenharmony_ci case IB_QPT_GSI: 3562306a36Sopenharmony_ci if (unlikely(pkt_type != IB_OPCODE_UD)) 3662306a36Sopenharmony_ci return -EINVAL; 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci default: 3962306a36Sopenharmony_ci return -EINVAL; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci spin_lock_irqsave(&qp->state_lock, flags); 4362306a36Sopenharmony_ci if (pkt->mask & RXE_REQ_MASK) { 4462306a36Sopenharmony_ci if (unlikely(qp_state(qp) < IB_QPS_RTR)) { 4562306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->state_lock, flags); 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci } else { 4962306a36Sopenharmony_ci if (unlikely(qp_state(qp) < IB_QPS_RTS)) { 5062306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->state_lock, flags); 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->state_lock, flags); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void set_bad_pkey_cntr(struct rxe_port *port) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci spin_lock_bh(&port->port_lock); 6262306a36Sopenharmony_ci port->attr.bad_pkey_cntr = min((u32)0xffff, 6362306a36Sopenharmony_ci port->attr.bad_pkey_cntr + 1); 6462306a36Sopenharmony_ci spin_unlock_bh(&port->port_lock); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void set_qkey_viol_cntr(struct rxe_port *port) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci spin_lock_bh(&port->port_lock); 7062306a36Sopenharmony_ci port->attr.qkey_viol_cntr = min((u32)0xffff, 7162306a36Sopenharmony_ci port->attr.qkey_viol_cntr + 1); 7262306a36Sopenharmony_ci spin_unlock_bh(&port->port_lock); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int check_keys(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, 7662306a36Sopenharmony_ci u32 qpn, struct rxe_qp *qp) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct rxe_port *port = &rxe->port; 7962306a36Sopenharmony_ci u16 pkey = bth_pkey(pkt); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pkt->pkey_index = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!pkey_match(pkey, IB_DEFAULT_PKEY_FULL)) { 8462306a36Sopenharmony_ci set_bad_pkey_cntr(port); 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (qp_type(qp) == IB_QPT_UD || qp_type(qp) == IB_QPT_GSI) { 8962306a36Sopenharmony_ci u32 qkey = (qpn == 1) ? GSI_QKEY : qp->attr.qkey; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (unlikely(deth_qkey(pkt) != qkey)) { 9262306a36Sopenharmony_ci set_qkey_viol_cntr(port); 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int check_addr(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, 10162306a36Sopenharmony_ci struct rxe_qp *qp) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct sk_buff *skb = PKT_TO_SKB(pkt); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (qp_type(qp) != IB_QPT_RC && qp_type(qp) != IB_QPT_UC) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (unlikely(pkt->port_num != qp->attr.port_num)) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 11262306a36Sopenharmony_ci struct in_addr *saddr = 11362306a36Sopenharmony_ci &qp->pri_av.sgid_addr._sockaddr_in.sin_addr; 11462306a36Sopenharmony_ci struct in_addr *daddr = 11562306a36Sopenharmony_ci &qp->pri_av.dgid_addr._sockaddr_in.sin_addr; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if ((ip_hdr(skb)->daddr != saddr->s_addr) || 11862306a36Sopenharmony_ci (ip_hdr(skb)->saddr != daddr->s_addr)) 11962306a36Sopenharmony_ci return -EINVAL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci } else if (skb->protocol == htons(ETH_P_IPV6)) { 12262306a36Sopenharmony_ci struct in6_addr *saddr = 12362306a36Sopenharmony_ci &qp->pri_av.sgid_addr._sockaddr_in6.sin6_addr; 12462306a36Sopenharmony_ci struct in6_addr *daddr = 12562306a36Sopenharmony_ci &qp->pri_av.dgid_addr._sockaddr_in6.sin6_addr; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (memcmp(&ipv6_hdr(skb)->daddr, saddr, sizeof(*saddr)) || 12862306a36Sopenharmony_ci memcmp(&ipv6_hdr(skb)->saddr, daddr, sizeof(*daddr))) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int hdr_check(struct rxe_pkt_info *pkt) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct rxe_dev *rxe = pkt->rxe; 13862306a36Sopenharmony_ci struct rxe_port *port = &rxe->port; 13962306a36Sopenharmony_ci struct rxe_qp *qp = NULL; 14062306a36Sopenharmony_ci u32 qpn = bth_qpn(pkt); 14162306a36Sopenharmony_ci int index; 14262306a36Sopenharmony_ci int err; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (unlikely(bth_tver(pkt) != BTH_TVER)) 14562306a36Sopenharmony_ci goto err1; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (unlikely(qpn == 0)) 14862306a36Sopenharmony_ci goto err1; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (qpn != IB_MULTICAST_QPN) { 15162306a36Sopenharmony_ci index = (qpn == 1) ? port->qp_gsi_index : qpn; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci qp = rxe_pool_get_index(&rxe->qp_pool, index); 15462306a36Sopenharmony_ci if (unlikely(!qp)) 15562306a36Sopenharmony_ci goto err1; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci err = check_type_state(rxe, pkt, qp); 15862306a36Sopenharmony_ci if (unlikely(err)) 15962306a36Sopenharmony_ci goto err2; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci err = check_addr(rxe, pkt, qp); 16262306a36Sopenharmony_ci if (unlikely(err)) 16362306a36Sopenharmony_ci goto err2; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci err = check_keys(rxe, pkt, qpn, qp); 16662306a36Sopenharmony_ci if (unlikely(err)) 16762306a36Sopenharmony_ci goto err2; 16862306a36Sopenharmony_ci } else { 16962306a36Sopenharmony_ci if (unlikely((pkt->mask & RXE_GRH_MASK) == 0)) 17062306a36Sopenharmony_ci goto err1; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pkt->qp = qp; 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cierr2: 17762306a36Sopenharmony_ci rxe_put(qp); 17862306a36Sopenharmony_cierr1: 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic inline void rxe_rcv_pkt(struct rxe_pkt_info *pkt, struct sk_buff *skb) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci if (pkt->mask & RXE_REQ_MASK) 18562306a36Sopenharmony_ci rxe_resp_queue_pkt(pkt->qp, skb); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci rxe_comp_queue_pkt(pkt->qp, skb); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct rxe_pkt_info *pkt = SKB_TO_PKT(skb); 19362306a36Sopenharmony_ci struct rxe_mcg *mcg; 19462306a36Sopenharmony_ci struct rxe_mca *mca; 19562306a36Sopenharmony_ci struct rxe_qp *qp; 19662306a36Sopenharmony_ci union ib_gid dgid; 19762306a36Sopenharmony_ci int err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 20062306a36Sopenharmony_ci ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr, 20162306a36Sopenharmony_ci (struct in6_addr *)&dgid); 20262306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IPV6)) 20362306a36Sopenharmony_ci memcpy(&dgid, &ipv6_hdr(skb)->daddr, sizeof(dgid)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* lookup mcast group corresponding to mgid, takes a ref */ 20662306a36Sopenharmony_ci mcg = rxe_lookup_mcg(rxe, &dgid); 20762306a36Sopenharmony_ci if (!mcg) 20862306a36Sopenharmony_ci goto drop; /* mcast group not registered */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* this is unreliable datagram service so we let 21362306a36Sopenharmony_ci * failures to deliver a multicast packet to a 21462306a36Sopenharmony_ci * single QP happen and just move on and try 21562306a36Sopenharmony_ci * the rest of them on the list 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci list_for_each_entry(mca, &mcg->qp_list, qp_list) { 21862306a36Sopenharmony_ci qp = mca->qp; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* validate qp for incoming packet */ 22162306a36Sopenharmony_ci err = check_type_state(rxe, pkt, qp); 22262306a36Sopenharmony_ci if (err) 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci err = check_keys(rxe, pkt, bth_qpn(pkt), qp); 22662306a36Sopenharmony_ci if (err) 22762306a36Sopenharmony_ci continue; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* for all but the last QP create a new clone of the 23062306a36Sopenharmony_ci * skb and pass to the QP. Pass the original skb to 23162306a36Sopenharmony_ci * the last QP in the list. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (mca->qp_list.next != &mcg->qp_list) { 23462306a36Sopenharmony_ci struct sk_buff *cskb; 23562306a36Sopenharmony_ci struct rxe_pkt_info *cpkt; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci cskb = skb_clone(skb, GFP_ATOMIC); 23862306a36Sopenharmony_ci if (unlikely(!cskb)) 23962306a36Sopenharmony_ci continue; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (WARN_ON(!ib_device_try_get(&rxe->ib_dev))) { 24262306a36Sopenharmony_ci kfree_skb(cskb); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci cpkt = SKB_TO_PKT(cskb); 24762306a36Sopenharmony_ci cpkt->qp = qp; 24862306a36Sopenharmony_ci rxe_get(qp); 24962306a36Sopenharmony_ci rxe_rcv_pkt(cpkt, cskb); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci pkt->qp = qp; 25262306a36Sopenharmony_ci rxe_get(qp); 25362306a36Sopenharmony_ci rxe_rcv_pkt(pkt, skb); 25462306a36Sopenharmony_ci skb = NULL; /* mark consumed */ 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci kref_put(&mcg->ref_cnt, rxe_cleanup_mcg); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (likely(!skb)) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* This only occurs if one of the checks fails on the last 26662306a36Sopenharmony_ci * QP in the list above 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cidrop: 27062306a36Sopenharmony_ci kfree_skb(skb); 27162306a36Sopenharmony_ci ib_device_put(&rxe->ib_dev); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * rxe_chk_dgid - validate destination IP address 27662306a36Sopenharmony_ci * @rxe: rxe device that received packet 27762306a36Sopenharmony_ci * @skb: the received packet buffer 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Accept any loopback packets 28062306a36Sopenharmony_ci * Extract IP address from packet and 28162306a36Sopenharmony_ci * Accept if multicast packet 28262306a36Sopenharmony_ci * Accept if matches an SGID table entry 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic int rxe_chk_dgid(struct rxe_dev *rxe, struct sk_buff *skb) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct rxe_pkt_info *pkt = SKB_TO_PKT(skb); 28762306a36Sopenharmony_ci const struct ib_gid_attr *gid_attr; 28862306a36Sopenharmony_ci union ib_gid dgid; 28962306a36Sopenharmony_ci union ib_gid *pdgid; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (pkt->mask & RXE_LOOPBACK_MASK) 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 29562306a36Sopenharmony_ci ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr, 29662306a36Sopenharmony_ci (struct in6_addr *)&dgid); 29762306a36Sopenharmony_ci pdgid = &dgid; 29862306a36Sopenharmony_ci } else { 29962306a36Sopenharmony_ci pdgid = (union ib_gid *)&ipv6_hdr(skb)->daddr; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (rdma_is_multicast_addr((struct in6_addr *)pdgid)) 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci gid_attr = rdma_find_gid_by_port(&rxe->ib_dev, pdgid, 30662306a36Sopenharmony_ci IB_GID_TYPE_ROCE_UDP_ENCAP, 30762306a36Sopenharmony_ci 1, skb->dev); 30862306a36Sopenharmony_ci if (IS_ERR(gid_attr)) 30962306a36Sopenharmony_ci return PTR_ERR(gid_attr); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci rdma_put_gid_attr(gid_attr); 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* rxe_rcv is called from the interface driver */ 31662306a36Sopenharmony_civoid rxe_rcv(struct sk_buff *skb) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci int err; 31962306a36Sopenharmony_ci struct rxe_pkt_info *pkt = SKB_TO_PKT(skb); 32062306a36Sopenharmony_ci struct rxe_dev *rxe = pkt->rxe; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (unlikely(skb->len < RXE_BTH_BYTES)) 32362306a36Sopenharmony_ci goto drop; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (rxe_chk_dgid(rxe, skb) < 0) 32662306a36Sopenharmony_ci goto drop; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci pkt->opcode = bth_opcode(pkt); 32962306a36Sopenharmony_ci pkt->psn = bth_psn(pkt); 33062306a36Sopenharmony_ci pkt->qp = NULL; 33162306a36Sopenharmony_ci pkt->mask |= rxe_opcode[pkt->opcode].mask; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (unlikely(skb->len < header_size(pkt))) 33462306a36Sopenharmony_ci goto drop; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci err = hdr_check(pkt); 33762306a36Sopenharmony_ci if (unlikely(err)) 33862306a36Sopenharmony_ci goto drop; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci err = rxe_icrc_check(skb, pkt); 34162306a36Sopenharmony_ci if (unlikely(err)) 34262306a36Sopenharmony_ci goto drop; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci rxe_counter_inc(rxe, RXE_CNT_RCVD_PKTS); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (unlikely(bth_qpn(pkt) == IB_MULTICAST_QPN)) 34762306a36Sopenharmony_ci rxe_rcv_mcast_pkt(rxe, skb); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci rxe_rcv_pkt(pkt, skb); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cidrop: 35462306a36Sopenharmony_ci if (pkt->qp) 35562306a36Sopenharmony_ci rxe_put(pkt->qp); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci kfree_skb(skb); 35862306a36Sopenharmony_ci ib_device_put(&rxe->ib_dev); 35962306a36Sopenharmony_ci} 360