| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. |
| 3 | */ |
| 4 | |
| 5 | #include <linux/netdevice.h> |
| 6 | #include "rmnet_config.h" |
| 7 | #include "rmnet_map.h" |
| 8 | #include "rmnet_private.h" |
| 9 | #include "rmnet_vnd.h" |
| 10 | |
| 11 | static u8 rmnet_map_do_flow_control(struct sk_buff *skb, |
| 12 | struct rmnet_port *port, |
| 13 | int enable) |
| 14 | { |
| 15 | struct rmnet_map_header * = (void *)skb->data; |
| 16 | struct rmnet_endpoint *ep; |
| 17 | struct net_device *vnd; |
| 18 | u8 mux_id; |
| 19 | int r; |
| 20 | |
| 21 | mux_id = map_header->mux_id; |
| 22 | |
| 23 | if (mux_id >= RMNET_MAX_LOGICAL_EP) { |
| 24 | kfree_skb(skb); |
| 25 | return RX_HANDLER_CONSUMED; |
| 26 | } |
| 27 | |
| 28 | ep = rmnet_get_endpoint(port, mux_id); |
| 29 | if (!ep) { |
| 30 | kfree_skb(skb); |
| 31 | return RX_HANDLER_CONSUMED; |
| 32 | } |
| 33 | |
| 34 | vnd = ep->egress_dev; |
| 35 | |
| 36 | /* Ignore the ip family and pass the sequence number for both v4 and v6 |
| 37 | * sequence. User space does not support creating dedicated flows for |
| 38 | * the 2 protocols |
| 39 | */ |
| 40 | r = rmnet_vnd_do_flow_control(dev: vnd, enable); |
| 41 | if (r) { |
| 42 | kfree_skb(skb); |
| 43 | return RMNET_MAP_COMMAND_UNSUPPORTED; |
| 44 | } else { |
| 45 | return RMNET_MAP_COMMAND_ACK; |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | static void rmnet_map_send_ack(struct sk_buff *skb, |
| 50 | unsigned char type, |
| 51 | struct rmnet_port *port) |
| 52 | { |
| 53 | struct rmnet_map_header * = (void *)skb->data; |
| 54 | struct rmnet_map_control_command *cmd; |
| 55 | struct net_device *dev = skb->dev; |
| 56 | |
| 57 | if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) |
| 58 | skb_trim(skb, |
| 59 | len: skb->len - sizeof(struct rmnet_map_dl_csum_trailer)); |
| 60 | |
| 61 | skb->protocol = htons(ETH_P_MAP); |
| 62 | |
| 63 | /* Command data immediately follows the MAP header */ |
| 64 | cmd = (struct rmnet_map_control_command *)(map_header + 1); |
| 65 | cmd->cmd_type = type & 0x03; |
| 66 | |
| 67 | netif_tx_lock(dev); |
| 68 | dev->netdev_ops->ndo_start_xmit(skb, dev); |
| 69 | netif_tx_unlock(dev); |
| 70 | } |
| 71 | |
| 72 | /* Process MAP command frame and send N/ACK message as appropriate. Message cmd |
| 73 | * name is decoded here and appropriate handler is called. |
| 74 | */ |
| 75 | void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port) |
| 76 | { |
| 77 | struct rmnet_map_header * = (void *)skb->data; |
| 78 | struct rmnet_map_control_command *cmd; |
| 79 | unsigned char command_name; |
| 80 | unsigned char rc = 0; |
| 81 | |
| 82 | /* Command data immediately follows the MAP header */ |
| 83 | cmd = (struct rmnet_map_control_command *)(map_header + 1); |
| 84 | command_name = cmd->command_name; |
| 85 | |
| 86 | switch (command_name) { |
| 87 | case RMNET_MAP_COMMAND_FLOW_ENABLE: |
| 88 | rc = rmnet_map_do_flow_control(skb, port, enable: 1); |
| 89 | break; |
| 90 | |
| 91 | case RMNET_MAP_COMMAND_FLOW_DISABLE: |
| 92 | rc = rmnet_map_do_flow_control(skb, port, enable: 0); |
| 93 | break; |
| 94 | |
| 95 | default: |
| 96 | rc = RMNET_MAP_COMMAND_UNSUPPORTED; |
| 97 | kfree_skb(skb); |
| 98 | break; |
| 99 | } |
| 100 | if (rc == RMNET_MAP_COMMAND_ACK) |
| 101 | rmnet_map_send_ack(skb, type: rc, port); |
| 102 | } |
| 103 | |