1 /* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 */ 12 13 #include <linux/netdevice.h> 14 #include "rmnet_config.h" 15 #include "rmnet_map.h" 16 #include "rmnet_private.h" 17 #include "rmnet_vnd.h" 18 19 static u8 rmnet_map_do_flow_control(struct sk_buff *skb, 20 struct rmnet_port *port, 21 int enable) 22 { 23 struct rmnet_map_control_command *cmd; 24 struct rmnet_endpoint *ep; 25 struct net_device *vnd; 26 u16 ip_family; 27 u16 fc_seq; 28 u32 qos_id; 29 u8 mux_id; 30 int r; 31 32 mux_id = RMNET_MAP_GET_MUX_ID(skb); 33 cmd = RMNET_MAP_GET_CMD_START(skb); 34 35 if (mux_id >= RMNET_MAX_LOGICAL_EP) { 36 kfree_skb(skb); 37 return RX_HANDLER_CONSUMED; 38 } 39 40 ep = rmnet_get_endpoint(port, mux_id); 41 if (!ep) { 42 kfree_skb(skb); 43 return RX_HANDLER_CONSUMED; 44 } 45 46 vnd = ep->egress_dev; 47 48 ip_family = cmd->flow_control.ip_family; 49 fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); 50 qos_id = ntohl(cmd->flow_control.qos_id); 51 52 /* Ignore the ip family and pass the sequence number for both v4 and v6 53 * sequence. User space does not support creating dedicated flows for 54 * the 2 protocols 55 */ 56 r = rmnet_vnd_do_flow_control(vnd, enable); 57 if (r) { 58 kfree_skb(skb); 59 return RMNET_MAP_COMMAND_UNSUPPORTED; 60 } else { 61 return RMNET_MAP_COMMAND_ACK; 62 } 63 } 64 65 static void rmnet_map_send_ack(struct sk_buff *skb, 66 unsigned char type, 67 struct rmnet_port *port) 68 { 69 struct rmnet_map_control_command *cmd; 70 int xmit_status; 71 72 if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) { 73 if (skb->len < sizeof(struct rmnet_map_header) + 74 RMNET_MAP_GET_LENGTH(skb) + 75 sizeof(struct rmnet_map_dl_csum_trailer)) { 76 kfree_skb(skb); 77 return; 78 } 79 80 skb_trim(skb, skb->len - 81 sizeof(struct rmnet_map_dl_csum_trailer)); 82 } 83 84 skb->protocol = htons(ETH_P_MAP); 85 86 cmd = RMNET_MAP_GET_CMD_START(skb); 87 cmd->cmd_type = type & 0x03; 88 89 netif_tx_lock(skb->dev); 90 xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); 91 netif_tx_unlock(skb->dev); 92 } 93 94 /* Process MAP command frame and send N/ACK message as appropriate. Message cmd 95 * name is decoded here and appropriate handler is called. 96 */ 97 void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port) 98 { 99 struct rmnet_map_control_command *cmd; 100 unsigned char command_name; 101 unsigned char rc = 0; 102 103 cmd = RMNET_MAP_GET_CMD_START(skb); 104 command_name = cmd->command_name; 105 106 switch (command_name) { 107 case RMNET_MAP_COMMAND_FLOW_ENABLE: 108 rc = rmnet_map_do_flow_control(skb, port, 1); 109 break; 110 111 case RMNET_MAP_COMMAND_FLOW_DISABLE: 112 rc = rmnet_map_do_flow_control(skb, port, 0); 113 break; 114 115 default: 116 rc = RMNET_MAP_COMMAND_UNSUPPORTED; 117 kfree_skb(skb); 118 break; 119 } 120 if (rc == RMNET_MAP_COMMAND_ACK) 121 rmnet_map_send_ack(skb, rc, port); 122 } 123