162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2013-2018, 2021, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * RMNET Data ingress/egress handler 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/netdev_features.h> 962306a36Sopenharmony_ci#include <linux/if_arp.h> 1062306a36Sopenharmony_ci#include <net/sock.h> 1162306a36Sopenharmony_ci#include "rmnet_private.h" 1262306a36Sopenharmony_ci#include "rmnet_config.h" 1362306a36Sopenharmony_ci#include "rmnet_vnd.h" 1462306a36Sopenharmony_ci#include "rmnet_map.h" 1562306a36Sopenharmony_ci#include "rmnet_handlers.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define RMNET_IP_VERSION_4 0x40 1862306a36Sopenharmony_ci#define RMNET_IP_VERSION_6 0x60 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Helper Functions */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void rmnet_set_skb_proto(struct sk_buff *skb) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci switch (skb->data[0] & 0xF0) { 2562306a36Sopenharmony_ci case RMNET_IP_VERSION_4: 2662306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 2762306a36Sopenharmony_ci break; 2862306a36Sopenharmony_ci case RMNET_IP_VERSION_6: 2962306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 3062306a36Sopenharmony_ci break; 3162306a36Sopenharmony_ci default: 3262306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MAP); 3362306a36Sopenharmony_ci break; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Generic handler */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void 4062306a36Sopenharmony_cirmnet_deliver_skb(struct sk_buff *skb) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct rmnet_priv *priv = netdev_priv(skb->dev); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci skb_reset_transport_header(skb); 4562306a36Sopenharmony_ci skb_reset_network_header(skb); 4662306a36Sopenharmony_ci rmnet_vnd_rx_fixup(skb, skb->dev); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 4962306a36Sopenharmony_ci skb_set_mac_header(skb, 0); 5062306a36Sopenharmony_ci gro_cells_receive(&priv->gro_cells, skb); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* MAP handler */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void 5662306a36Sopenharmony_ci__rmnet_map_ingress_handler(struct sk_buff *skb, 5762306a36Sopenharmony_ci struct rmnet_port *port) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct rmnet_map_header *map_header = (void *)skb->data; 6062306a36Sopenharmony_ci struct rmnet_endpoint *ep; 6162306a36Sopenharmony_ci u16 len, pad; 6262306a36Sopenharmony_ci u8 mux_id; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (map_header->flags & MAP_CMD_FLAG) { 6562306a36Sopenharmony_ci /* Packet contains a MAP command (not data) */ 6662306a36Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) 6762306a36Sopenharmony_ci return rmnet_map_command(skb, port); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci goto free_skb; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci mux_id = map_header->mux_id; 7362306a36Sopenharmony_ci pad = map_header->flags & MAP_PAD_LEN_MASK; 7462306a36Sopenharmony_ci len = ntohs(map_header->pkt_len) - pad; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (mux_id >= RMNET_MAX_LOGICAL_EP) 7762306a36Sopenharmony_ci goto free_skb; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ep = rmnet_get_endpoint(port, mux_id); 8062306a36Sopenharmony_ci if (!ep) 8162306a36Sopenharmony_ci goto free_skb; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci skb->dev = ep->egress_dev; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) && 8662306a36Sopenharmony_ci (map_header->flags & MAP_NEXT_HEADER_FLAG)) { 8762306a36Sopenharmony_ci if (rmnet_map_process_next_hdr_packet(skb, len)) 8862306a36Sopenharmony_ci goto free_skb; 8962306a36Sopenharmony_ci skb_pull(skb, sizeof(*map_header)); 9062306a36Sopenharmony_ci rmnet_set_skb_proto(skb); 9162306a36Sopenharmony_ci } else { 9262306a36Sopenharmony_ci /* Subtract MAP header */ 9362306a36Sopenharmony_ci skb_pull(skb, sizeof(*map_header)); 9462306a36Sopenharmony_ci rmnet_set_skb_proto(skb); 9562306a36Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4 && 9662306a36Sopenharmony_ci !rmnet_map_checksum_downlink_packet(skb, len + pad)) 9762306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci skb_trim(skb, len); 10162306a36Sopenharmony_ci rmnet_deliver_skb(skb); 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cifree_skb: 10562306a36Sopenharmony_ci kfree_skb(skb); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void 10962306a36Sopenharmony_cirmnet_map_ingress_handler(struct sk_buff *skb, 11062306a36Sopenharmony_ci struct rmnet_port *port) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct sk_buff *skbn; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (skb->dev->type == ARPHRD_ETHER) { 11562306a36Sopenharmony_ci if (pskb_expand_head(skb, ETH_HLEN, 0, GFP_ATOMIC)) { 11662306a36Sopenharmony_ci kfree_skb(skb); 11762306a36Sopenharmony_ci return; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) { 12462306a36Sopenharmony_ci while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) 12562306a36Sopenharmony_ci __rmnet_map_ingress_handler(skbn, port); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci consume_skb(skb); 12862306a36Sopenharmony_ci } else { 12962306a36Sopenharmony_ci __rmnet_map_ingress_handler(skb, port); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int rmnet_map_egress_handler(struct sk_buff *skb, 13462306a36Sopenharmony_ci struct rmnet_port *port, u8 mux_id, 13562306a36Sopenharmony_ci struct net_device *orig_dev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int required_headroom, additional_header_len, csum_type = 0; 13862306a36Sopenharmony_ci struct rmnet_map_header *map_header; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci additional_header_len = 0; 14162306a36Sopenharmony_ci required_headroom = sizeof(struct rmnet_map_header); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) { 14462306a36Sopenharmony_ci additional_header_len = sizeof(struct rmnet_map_ul_csum_header); 14562306a36Sopenharmony_ci csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV4; 14662306a36Sopenharmony_ci } else if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5) { 14762306a36Sopenharmony_ci additional_header_len = sizeof(struct rmnet_map_v5_csum_header); 14862306a36Sopenharmony_ci csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV5; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci required_headroom += additional_header_len; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (skb_cow_head(skb, required_headroom) < 0) 15462306a36Sopenharmony_ci return -ENOMEM; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (csum_type) 15762306a36Sopenharmony_ci rmnet_map_checksum_uplink_packet(skb, port, orig_dev, 15862306a36Sopenharmony_ci csum_type); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci map_header = rmnet_map_add_map_header(skb, additional_header_len, 16162306a36Sopenharmony_ci port, 0); 16262306a36Sopenharmony_ci if (!map_header) 16362306a36Sopenharmony_ci return -ENOMEM; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci map_header->mux_id = mux_id; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (READ_ONCE(port->egress_agg_params.count) > 1) { 16862306a36Sopenharmony_ci unsigned int len; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci len = rmnet_map_tx_aggregate(skb, port, orig_dev); 17162306a36Sopenharmony_ci if (likely(len)) { 17262306a36Sopenharmony_ci rmnet_vnd_tx_fixup_len(len, orig_dev); 17362306a36Sopenharmony_ci return -EINPROGRESS; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci return -ENOMEM; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MAP); 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void 18362306a36Sopenharmony_cirmnet_bridge_handler(struct sk_buff *skb, struct net_device *bridge_dev) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci if (skb_mac_header_was_set(skb)) 18662306a36Sopenharmony_ci skb_push(skb, skb->mac_len); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (bridge_dev) { 18962306a36Sopenharmony_ci skb->dev = bridge_dev; 19062306a36Sopenharmony_ci dev_queue_xmit(skb); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* Ingress / Egress Entry Points */ 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* Processes packet as per ingress data format for receiving device. Logical 19762306a36Sopenharmony_ci * endpoint is determined from packet inspection. Packet is then sent to the 19862306a36Sopenharmony_ci * egress device listed in the logical endpoint configuration. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cirx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct sk_buff *skb = *pskb; 20362306a36Sopenharmony_ci struct rmnet_port *port; 20462306a36Sopenharmony_ci struct net_device *dev; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!skb) 20762306a36Sopenharmony_ci goto done; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (skb_linearize(skb)) { 21062306a36Sopenharmony_ci kfree_skb(skb); 21162306a36Sopenharmony_ci goto done; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (skb->pkt_type == PACKET_LOOPBACK) 21562306a36Sopenharmony_ci return RX_HANDLER_PASS; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci dev = skb->dev; 21862306a36Sopenharmony_ci port = rmnet_get_port_rcu(dev); 21962306a36Sopenharmony_ci if (unlikely(!port)) { 22062306a36Sopenharmony_ci dev_core_stats_rx_nohandler_inc(skb->dev); 22162306a36Sopenharmony_ci kfree_skb(skb); 22262306a36Sopenharmony_ci goto done; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci switch (port->rmnet_mode) { 22662306a36Sopenharmony_ci case RMNET_EPMODE_VND: 22762306a36Sopenharmony_ci rmnet_map_ingress_handler(skb, port); 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci case RMNET_EPMODE_BRIDGE: 23062306a36Sopenharmony_ci rmnet_bridge_handler(skb, port->bridge_ep); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cidone: 23562306a36Sopenharmony_ci return RX_HANDLER_CONSUMED; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* Modifies packet as per logical endpoint configuration and egress data format 23962306a36Sopenharmony_ci * for egress device configured in logical endpoint. Packet is then transmitted 24062306a36Sopenharmony_ci * on the egress device. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_civoid rmnet_egress_handler(struct sk_buff *skb) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct net_device *orig_dev; 24562306a36Sopenharmony_ci struct rmnet_port *port; 24662306a36Sopenharmony_ci struct rmnet_priv *priv; 24762306a36Sopenharmony_ci u8 mux_id; 24862306a36Sopenharmony_ci int err; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci sk_pacing_shift_update(skb->sk, 8); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci orig_dev = skb->dev; 25362306a36Sopenharmony_ci priv = netdev_priv(orig_dev); 25462306a36Sopenharmony_ci skb->dev = priv->real_dev; 25562306a36Sopenharmony_ci mux_id = priv->mux_id; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci port = rmnet_get_port_rcu(skb->dev); 25862306a36Sopenharmony_ci if (!port) 25962306a36Sopenharmony_ci goto drop; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev); 26262306a36Sopenharmony_ci if (err == -ENOMEM) 26362306a36Sopenharmony_ci goto drop; 26462306a36Sopenharmony_ci else if (err == -EINPROGRESS) 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci rmnet_vnd_tx_fixup(skb, orig_dev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci dev_queue_xmit(skb); 27062306a36Sopenharmony_ci return; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cidrop: 27362306a36Sopenharmony_ci this_cpu_inc(priv->pcpu_stats->stats.tx_drops); 27462306a36Sopenharmony_ci kfree_skb(skb); 27562306a36Sopenharmony_ci} 276