// SPDX-License-Identifier: GPL-2.0 #include "hwpa_ppe_internal.h" #include #define PPE_DRV_CC_TO_EXP(cc) ((cc) - 1) static enum hwpa_backend_rv hwpa_ppe_exception_check_and_set_ipv4_flushed(struct flow_keys *keys) { struct hwpa_ppe_context *ppe_ctx = hwpa_ppe_get_context(); struct ppe_drv_v4_5tuple v4_5tuple = {0}; struct hwpa_ppe_accelerator *accelerator; struct hwpa_ppe_session *hws; uint32_t hash; v4_5tuple.flow_ip = ntohl(keys->addrs.v4addrs.src); v4_5tuple.return_ip = ntohl(keys->addrs.v4addrs.dst); v4_5tuple.protocol = keys->basic.ip_proto; /* * PPE support only TCP, UDP or UDP_LITE for 5-tuple */ if ((keys->basic.ip_proto == IPPROTO_TCP) || (keys->basic.ip_proto == IPPROTO_UDP) || (keys->basic.ip_proto == IPPROTO_UDPLITE)) { v4_5tuple.flow_ident = ntohs(keys->ports.src); v4_5tuple.return_ident = ntohs(keys->ports.dst); } hash = hwpa_ipv4_gen_session_hash_raw(v4_5tuple.flow_ip, v4_5tuple.flow_ident, v4_5tuple.return_ip, v4_5tuple.return_ident, v4_5tuple.protocol); rcu_read_lock(); hash_for_each_possible_rcu(ppe_ctx->used_hws_hlist, hws, node, hash) { accelerator = hwpa_ppe_get_accelerator(hws); if ((hws->on_free_list) || (hws->accel_type != HWPA_PPE_ACCELERATOR_PPE_IPV4) || (!accelerator || !accelerator->set_flushed_session)) { continue; } if (accelerator->set_flushed_session(hws, &v4_5tuple) == HWPA_BACKEND_SUCCESS) { rcu_read_unlock(); return HWPA_BACKEND_SUCCESS; } } rcu_read_unlock(); PR_DEVEL("No matching ppe connetion found for\nhash: 0x%x protocol: %d\n from ip: %pI4h:%d\n to ip: %pI4h:%d\n", hash, v4_5tuple.protocol, &v4_5tuple.flow_ip, v4_5tuple.flow_ident, &v4_5tuple.return_ip, v4_5tuple.return_ident); return HWPA_BACKEND_ERR_NO_MATCH; } static enum hwpa_backend_rv hwpa_ppe_exception_check_and_set_ipv6_flushed(struct flow_keys *keys) { struct hwpa_ppe_context *ppe_ctx = hwpa_ppe_get_context(); struct ppe_drv_v6_5tuple tuple = {0}; struct hwpa_ppe_accelerator *accelerator; struct hwpa_ppe_session *hws; uint32_t hash; PPE_DRV_IN6_TO_IPV6(tuple.flow_ip, keys->addrs.v6addrs.src); PPE_DRV_IN6_TO_IPV6(tuple.return_ip, keys->addrs.v6addrs.dst); tuple.protocol = keys->basic.ip_proto; /* * The PPE supports only TCP, UDP or UDP_LITE for 5-tuple */ if ((keys->basic.ip_proto == IPPROTO_TCP) || (keys->basic.ip_proto == IPPROTO_UDP) || (keys->basic.ip_proto == IPPROTO_UDPLITE)) { tuple.flow_ident = ntohs(keys->ports.src); tuple.return_ident = ntohs(keys->ports.dst); } hash = hwpa_ipv6_gen_session_hash_raw(tuple.flow_ip, tuple.flow_ident, tuple.return_ip, tuple.return_ident, tuple.protocol); rcu_read_lock(); hash_for_each_possible_rcu(ppe_ctx->used_hws_hlist, hws, node, hash) { accelerator = hwpa_ppe_get_accelerator(hws); if ((hws->on_free_list) || (hws->accel_type != HWPA_PPE_ACCELERATOR_PPE_IPV6) || (!accelerator || !accelerator->set_flushed_session)) { continue; } if (accelerator->set_flushed_session(hws, &tuple) == HWPA_BACKEND_SUCCESS) { rcu_read_unlock(); return HWPA_BACKEND_SUCCESS; } } rcu_read_unlock(); PR_DEVEL("No matching PPE connection found for\nhash: 0x%x protocol: %d\n from IP: %pI6h:%d\n to IP: %pI6h:%d\n", hash, tuple.protocol, tuple.flow_ip, tuple.flow_ident, tuple.return_ip, tuple.return_ident); return HWPA_BACKEND_ERR_NO_MATCH; } bool hwpa_ppe_callback(void *app_data, struct sk_buff *skb, void *cc_info) { ppe_drv_cc_t cc = PPE_DRV_CC_TO_EXP(*(ppe_drv_cc_t *)cc_info); struct flow_keys keys = {0}; switch (cc) { case PPE_DRV_CC_EXP_TCP_FLAGS_0: case PPE_DRV_CC_EXP_TCP_FLAGS_1: case PPE_DRV_CC_EXP_TCP_FLAGS_2: PR_DEVEL("TCP Flag exception received: %d\n", cc); break; case PPE_DRV_CC_L3_FLOW_DE_ACCELEARTE: case PPE_DRV_CC_L3_NO_ROUTE_ACTION: /* session already de-accelerated */ return false; case PPE_DRV_CC_EXP_IPV6_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_IPV6_BAD_PAYLOAD_LEN: case PPE_DRV_CC_EXP_IPV6_DATA_INCOMPLETE: case PPE_DRV_CC_EXP_IPV6_SMALL_HOP_LIMIT: case PPE_DRV_CC_EXP_IPV6_FRAG: case PPE_DRV_CC_EXP_IPV6_ESP_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_UNKNOWN_L2_PROT: case PPE_DRV_CC_EXP_PPPOE_WRONG_VER_TYPE: case PPE_DRV_CC_EXP_PPPOE_WRONG_CODE: case PPE_DRV_CC_EXP_PPPOE_UNSUPPORTED_PPP_PROT: case PPE_DRV_CC_EXP_IPV4_SMALL_IHL: case PPE_DRV_CC_EXP_IPV4_WITH_OPTION: case PPE_DRV_CC_EXP_IPV4_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_IPV4_BAD_TOTAL_LEN: case PPE_DRV_CC_EXP_IPV4_DATA_INCOMPLETE: case PPE_DRV_CC_EXP_IPV4_FRAG: case PPE_DRV_CC_EXP_IPV4_SMALL_TTL: case PPE_DRV_CC_EXP_IPV4_CHECKSUM_ERR: case PPE_DRV_CC_EXP_IPV4_ESP_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_TCP_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_TCP_SMALL_DATA_OFFSET: case PPE_DRV_CC_EXP_UDP_HDR_INCOMPLETE: case PPE_DRV_CC_EXP_UDP_LITE_HDR_INCOMPLETE: default: PR_DEVEL("Unhandled ppe drv exception: %d\n", cc); } if (!skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_PARSE_1ST_FRAG | FLOW_DISSECTOR_F_STOP_AT_ENCAP)) { PR_DEVEL("dissection failed for skb:%p", skb); return false; } switch (keys.control.addr_type) { case FLOW_DISSECTOR_KEY_IPV4_ADDRS: hwpa_ppe_exception_check_and_set_ipv4_flushed(&keys); break; case FLOW_DISSECTOR_KEY_IPV6_ADDRS: hwpa_ppe_exception_check_and_set_ipv6_flushed(&keys); break; default: PR_DEVEL("dissection failed for skb:%p", skb); } return false; } void hwpa_ppe_exception_init(void) { uint32_t i; for (i = 0; i < PPE_DRV_CC_MAX; ++i) ppe_drv_cc_register_cb(i, hwpa_ppe_callback, NULL); } void hwpa_ppe_exception_exit(void) { uint32_t i; for (i = 0; i < PPE_DRV_CC_MAX; ++i) ppe_drv_cc_unregister_cb(i); }