18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * RMNET Data ingress/egress handler 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/netdev_features.h> 98c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 108c2ecf20Sopenharmony_ci#include <net/sock.h> 118c2ecf20Sopenharmony_ci#include "rmnet_private.h" 128c2ecf20Sopenharmony_ci#include "rmnet_config.h" 138c2ecf20Sopenharmony_ci#include "rmnet_vnd.h" 148c2ecf20Sopenharmony_ci#include "rmnet_map.h" 158c2ecf20Sopenharmony_ci#include "rmnet_handlers.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define RMNET_IP_VERSION_4 0x40 188c2ecf20Sopenharmony_ci#define RMNET_IP_VERSION_6 0x60 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Helper Functions */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void rmnet_set_skb_proto(struct sk_buff *skb) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci switch (skb->data[0] & 0xF0) { 258c2ecf20Sopenharmony_ci case RMNET_IP_VERSION_4: 268c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 278c2ecf20Sopenharmony_ci break; 288c2ecf20Sopenharmony_ci case RMNET_IP_VERSION_6: 298c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 308c2ecf20Sopenharmony_ci break; 318c2ecf20Sopenharmony_ci default: 328c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_MAP); 338c2ecf20Sopenharmony_ci break; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Generic handler */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void 408c2ecf20Sopenharmony_cirmnet_deliver_skb(struct sk_buff *skb) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(skb->dev); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 458c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 468c2ecf20Sopenharmony_ci rmnet_vnd_rx_fixup(skb, skb->dev); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_HOST; 498c2ecf20Sopenharmony_ci skb_set_mac_header(skb, 0); 508c2ecf20Sopenharmony_ci gro_cells_receive(&priv->gro_cells, skb); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* MAP handler */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void 568c2ecf20Sopenharmony_ci__rmnet_map_ingress_handler(struct sk_buff *skb, 578c2ecf20Sopenharmony_ci struct rmnet_port *port) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct rmnet_endpoint *ep; 608c2ecf20Sopenharmony_ci u16 len, pad; 618c2ecf20Sopenharmony_ci u8 mux_id; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (RMNET_MAP_GET_CD_BIT(skb)) { 648c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) 658c2ecf20Sopenharmony_ci return rmnet_map_command(skb, port); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci goto free_skb; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci mux_id = RMNET_MAP_GET_MUX_ID(skb); 718c2ecf20Sopenharmony_ci pad = RMNET_MAP_GET_PAD(skb); 728c2ecf20Sopenharmony_ci len = RMNET_MAP_GET_LENGTH(skb) - pad; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (mux_id >= RMNET_MAX_LOGICAL_EP) 758c2ecf20Sopenharmony_ci goto free_skb; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ep = rmnet_get_endpoint(port, mux_id); 788c2ecf20Sopenharmony_ci if (!ep) 798c2ecf20Sopenharmony_ci goto free_skb; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci skb->dev = ep->egress_dev; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Subtract MAP header */ 848c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct rmnet_map_header)); 858c2ecf20Sopenharmony_ci rmnet_set_skb_proto(skb); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { 888c2ecf20Sopenharmony_ci if (!rmnet_map_checksum_downlink_packet(skb, len + pad)) 898c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci skb_trim(skb, len); 938c2ecf20Sopenharmony_ci rmnet_deliver_skb(skb); 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cifree_skb: 978c2ecf20Sopenharmony_ci kfree_skb(skb); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void 1018c2ecf20Sopenharmony_cirmnet_map_ingress_handler(struct sk_buff *skb, 1028c2ecf20Sopenharmony_ci struct rmnet_port *port) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct sk_buff *skbn; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (skb->dev->type == ARPHRD_ETHER) { 1078c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, ETH_HLEN, 0, GFP_ATOMIC)) { 1088c2ecf20Sopenharmony_ci kfree_skb(skb); 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci skb_push(skb, ETH_HLEN); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) { 1168c2ecf20Sopenharmony_ci while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) 1178c2ecf20Sopenharmony_ci __rmnet_map_ingress_handler(skbn, port); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci consume_skb(skb); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci __rmnet_map_ingress_handler(skb, port); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int rmnet_map_egress_handler(struct sk_buff *skb, 1268c2ecf20Sopenharmony_ci struct rmnet_port *port, u8 mux_id, 1278c2ecf20Sopenharmony_ci struct net_device *orig_dev) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int required_headroom, additional_header_len; 1308c2ecf20Sopenharmony_ci struct rmnet_map_header *map_header; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci additional_header_len = 0; 1338c2ecf20Sopenharmony_ci required_headroom = sizeof(struct rmnet_map_header); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) { 1368c2ecf20Sopenharmony_ci additional_header_len = sizeof(struct rmnet_map_ul_csum_header); 1378c2ecf20Sopenharmony_ci required_headroom += additional_header_len; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (skb_headroom(skb) < required_headroom) { 1418c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, required_headroom, 0, GFP_ATOMIC)) 1428c2ecf20Sopenharmony_ci return -ENOMEM; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) 1468c2ecf20Sopenharmony_ci rmnet_map_checksum_uplink_packet(skb, orig_dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); 1498c2ecf20Sopenharmony_ci if (!map_header) 1508c2ecf20Sopenharmony_ci return -ENOMEM; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci map_header->mux_id = mux_id; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_MAP); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void 1608c2ecf20Sopenharmony_cirmnet_bridge_handler(struct sk_buff *skb, struct net_device *bridge_dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci if (skb_mac_header_was_set(skb)) 1638c2ecf20Sopenharmony_ci skb_push(skb, skb->mac_len); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (bridge_dev) { 1668c2ecf20Sopenharmony_ci skb->dev = bridge_dev; 1678c2ecf20Sopenharmony_ci dev_queue_xmit(skb); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Ingress / Egress Entry Points */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* Processes packet as per ingress data format for receiving device. Logical 1748c2ecf20Sopenharmony_ci * endpoint is determined from packet inspection. Packet is then sent to the 1758c2ecf20Sopenharmony_ci * egress device listed in the logical endpoint configuration. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cirx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct sk_buff *skb = *pskb; 1808c2ecf20Sopenharmony_ci struct rmnet_port *port; 1818c2ecf20Sopenharmony_ci struct net_device *dev; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (!skb) 1848c2ecf20Sopenharmony_ci goto done; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_LOOPBACK) 1878c2ecf20Sopenharmony_ci return RX_HANDLER_PASS; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci dev = skb->dev; 1908c2ecf20Sopenharmony_ci port = rmnet_get_port_rcu(dev); 1918c2ecf20Sopenharmony_ci if (unlikely(!port)) { 1928c2ecf20Sopenharmony_ci atomic_long_inc(&skb->dev->rx_nohandler); 1938c2ecf20Sopenharmony_ci kfree_skb(skb); 1948c2ecf20Sopenharmony_ci goto done; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch (port->rmnet_mode) { 1988c2ecf20Sopenharmony_ci case RMNET_EPMODE_VND: 1998c2ecf20Sopenharmony_ci rmnet_map_ingress_handler(skb, port); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case RMNET_EPMODE_BRIDGE: 2028c2ecf20Sopenharmony_ci rmnet_bridge_handler(skb, port->bridge_ep); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cidone: 2078c2ecf20Sopenharmony_ci return RX_HANDLER_CONSUMED; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* Modifies packet as per logical endpoint configuration and egress data format 2118c2ecf20Sopenharmony_ci * for egress device configured in logical endpoint. Packet is then transmitted 2128c2ecf20Sopenharmony_ci * on the egress device. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_civoid rmnet_egress_handler(struct sk_buff *skb) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct net_device *orig_dev; 2178c2ecf20Sopenharmony_ci struct rmnet_port *port; 2188c2ecf20Sopenharmony_ci struct rmnet_priv *priv; 2198c2ecf20Sopenharmony_ci u8 mux_id; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci sk_pacing_shift_update(skb->sk, 8); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci orig_dev = skb->dev; 2248c2ecf20Sopenharmony_ci priv = netdev_priv(orig_dev); 2258c2ecf20Sopenharmony_ci skb->dev = priv->real_dev; 2268c2ecf20Sopenharmony_ci mux_id = priv->mux_id; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci port = rmnet_get_port_rcu(skb->dev); 2298c2ecf20Sopenharmony_ci if (!port) 2308c2ecf20Sopenharmony_ci goto drop; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) 2338c2ecf20Sopenharmony_ci goto drop; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci rmnet_vnd_tx_fixup(skb, orig_dev); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci dev_queue_xmit(skb); 2388c2ecf20Sopenharmony_ci return; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cidrop: 2418c2ecf20Sopenharmony_ci this_cpu_inc(priv->pcpu_stats->stats.tx_drops); 2428c2ecf20Sopenharmony_ci kfree_skb(skb); 2438c2ecf20Sopenharmony_ci} 244