blob: 8eb2251b1c070bec3a327c473fb57dae1dacd40c [file] [log] [blame]
Subash Abhinov Kasiviswanathancf303c02016-10-26 17:38:48 -06001/* Copyright (c) 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/module.h>
14#include <linux/kernel.h>
15#include <linux/netdevice.h>
16#include <linux/skbuff.h>
17#include <linux/if_arp.h>
18#include <linux/rtnetlink.h>
19#include <linux/ip.h>
20#include <linux/icmp.h>
21#include <linux/msm_rmnet.h>
22
23unsigned int multiplication_factor = 1;
24module_param(multiplication_factor, uint, 0644);
25
26struct net_device *mydevice;
27
28static void iplo_do_ip_loopback(struct sk_buff *skb, int ip_offset)
29{
30 struct iphdr *hdr;
31 struct icmphdr *icmp;
32 __be32 ipaddr;
33 int i;
34
35 hdr = (struct iphdr *)(skb->data + ip_offset);
36 ipaddr = hdr->saddr;
37 hdr->saddr = hdr->daddr;
38 hdr->daddr = ipaddr;
39 switch (hdr->protocol) {
40 case 1: /* ICMP */
41 icmp = (struct icmphdr *)(skb->data + ip_offset + hdr->ihl * 4);
42 if (icmp->type == ICMP_ECHO)
43 icmp->type = ICMP_ECHOREPLY;
44 break;
45 case 11: /* UDP */
46 break;
47 case 6: /* TCP */
48 break;
49 default:
50 break;
51 }
52 if (multiplication_factor < 2) {
53 netif_rx(skb);
54 skb->dev->stats.tx_packets++;
55 } else {
56 for (i = 0; i < (multiplication_factor - 1); i++) {
57 netif_rx(skb_copy(skb, GFP_ATOMIC));
58 skb->dev->stats.tx_packets++;
59 }
60 netif_rx(skb);
61 skb->dev->stats.tx_packets++;
62 }
63}
64
65static netdev_tx_t iplo_vnd_start_xmit(struct sk_buff *skb,
66 struct net_device *dev)
67{
68 int ip_offset = 0;
69
70 switch (ntohs(skb->protocol)) {
71 case ETH_P_MAP:
72 ip_offset = 4;
73 case ETH_P_IP:
74 iplo_do_ip_loopback(skb, ip_offset);
75 dev->stats.rx_packets++;
76 break;
77 default:
78 dev->stats.tx_dropped++;
79 kfree_skb(skb);
80 break;
81 }
82 return NETDEV_TX_OK;
83}
84
85static int iplo_vnd_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
86{
87 struct rmnet_ioctl_extended_s ext_cmd;
88 int rc = 0;
89
90 rc = copy_from_user(&ext_cmd, ifr->ifr_ifru.ifru_data,
91 sizeof(struct rmnet_ioctl_extended_s));
92
93 if (rc) {
94 pr_err("%s() copy_from_user failed, error %d\n", __func__, rc);
95 return rc;
96 }
97
98 switch (ext_cmd.extended_ioctl) {
99 case RMNET_IOCTL_SET_MRU:
100 break;
101 case RMNET_IOCTL_GET_EPID:
102 ext_cmd.u.data = 100;
103 break;
104 case RMNET_IOCTL_GET_SUPPORTED_FEATURES:
105 ext_cmd.u.data = 0;
106 break;
107 case RMNET_IOCTL_GET_DRIVER_NAME:
108 strlcpy(ext_cmd.u.if_name, "rmnet_mhi",
109 sizeof(ext_cmd.u.if_name));
110 break;
111 default:
112 rc = -EINVAL;
113 break;
114 }
115
116 rc = copy_to_user(ifr->ifr_ifru.ifru_data, &ext_cmd,
117 sizeof(struct rmnet_ioctl_extended_s));
118
119 if (rc)
120 pr_err("%s() copy_to_user failed, error %d\n", __func__, rc);
121
122 return rc;
123}
124
125static int iplo_vnd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
126{
127 int rc = 0;
128
129 struct rmnet_ioctl_data_s ioctl_data;
130
131 switch (cmd) {
132 case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
133 break;
134 case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
135 ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP;
136 if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data,
137 sizeof(struct rmnet_ioctl_data_s)))
138 rc = -EFAULT;
139 break;
140 case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
141 ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP;
142 if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data,
143 sizeof(struct rmnet_ioctl_data_s)))
144 rc = -EFAULT;
145 break;
146 case RMNET_IOCTL_SET_QOS_ENABLE:
147 rc = -EINVAL;
148 break;
149 case RMNET_IOCTL_SET_QOS_DISABLE:
150 rc = 0;
151 break;
152 case RMNET_IOCTL_OPEN:
153 case RMNET_IOCTL_CLOSE:
154 /* We just ignore them and return success */
155 rc = 0;
156 break;
157 case RMNET_IOCTL_EXTENDED:
158 rc = iplo_vnd_ioctl_extended(dev, ifr);
159 break;
160 default:
161 /* Don't fail any IOCTL right now */
162 rc = 0;
163 break;
164 }
165
166 return rc;
167}
168
169static int iplo_vnd_change_mtu(struct net_device *dev, int new_mtu)
170{
171 if (0 > new_mtu || 16384 < new_mtu)
172 return -EINVAL;
173
174 dev->mtu = new_mtu;
175 return 0;
176}
177
178static const struct net_device_ops iplo_device_ops = {
179 .ndo_init = 0,
180 .ndo_do_ioctl = iplo_vnd_ioctl,
181 .ndo_start_xmit = iplo_vnd_start_xmit,
182 .ndo_change_mtu = iplo_vnd_change_mtu,
183};
184
185static void iplo_device_setup(struct net_device *dev)
186{
187 dev->flags |= IFF_NOARP;
188 dev->netdev_ops = &iplo_device_ops;
189 dev->mtu = 1500;
190 dev->needed_headroom = 0;
191 dev->watchdog_timeo = 100;
192 dev->header_ops = 0;
193 dev->type = ARPHRD_RAWIP;
194 dev->hard_header_len = 0;
195 dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
196 dev->tx_queue_len = 1000;
197}
198
199int __init rmnet_iplo_init(void)
200{
201 int rc;
202
203 pr_err("iplo: Module is coming up\n");
204 mydevice = alloc_netdev(100, "rmnet_mhi0", NET_NAME_ENUM,
205 iplo_device_setup);
206 if (!mydevice) {
207 pr_err("iplo: Failed to to allocate netdev for iplo\n");
208 return -EINVAL;
209 }
210 rtnl_lock();
211 rc = register_netdevice(mydevice);
212 rtnl_unlock();
213 if (rc != 0) {
214 pr_err("iplo: Failed to to register netdev [%s]\n",
215 mydevice->name);
216 free_netdev(mydevice);
217 return -EINVAL;
218 }
219 return 0;
220}
221
222void __exit rmnet_iplo_exit(void)
223{
224 unregister_netdev(mydevice);
225 free_netdev(mydevice);
226 pr_err("iplo: Module is going away\n");
227}
228
229module_init(rmnet_iplo_init)
230module_exit(rmnet_iplo_exit)
231MODULE_LICENSE("GPL v2");
232MODULE_DESCRIPTION("RmNet IP Loop Back Driver");