1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun // Copyright (c) 2020 Facebook Inc.
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun #include <linux/debugfs.h>
5*4882a593Smuzhiyun #include <linux/netdevice.h>
6*4882a593Smuzhiyun #include <linux/slab.h>
7*4882a593Smuzhiyun #include <net/udp_tunnel.h>
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include "netdevsim.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun static int
nsim_udp_tunnel_set_port(struct net_device * dev,unsigned int table,unsigned int entry,struct udp_tunnel_info * ti)12*4882a593Smuzhiyun nsim_udp_tunnel_set_port(struct net_device *dev, unsigned int table,
13*4882a593Smuzhiyun unsigned int entry, struct udp_tunnel_info *ti)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun struct netdevsim *ns = netdev_priv(dev);
16*4882a593Smuzhiyun int ret;
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun ret = -ns->udp_ports.inject_error;
19*4882a593Smuzhiyun ns->udp_ports.inject_error = 0;
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun if (ns->udp_ports.sleep)
22*4882a593Smuzhiyun msleep(ns->udp_ports.sleep);
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun if (!ret) {
25*4882a593Smuzhiyun if (ns->udp_ports.ports[table][entry]) {
26*4882a593Smuzhiyun WARN(1, "entry already in use\n");
27*4882a593Smuzhiyun ret = -EBUSY;
28*4882a593Smuzhiyun } else {
29*4882a593Smuzhiyun ns->udp_ports.ports[table][entry] =
30*4882a593Smuzhiyun be16_to_cpu(ti->port) << 16 | ti->type;
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun netdev_info(dev, "set [%d, %d] type %d family %d port %d - %d\n",
35*4882a593Smuzhiyun table, entry, ti->type, ti->sa_family, ntohs(ti->port),
36*4882a593Smuzhiyun ret);
37*4882a593Smuzhiyun return ret;
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static int
nsim_udp_tunnel_unset_port(struct net_device * dev,unsigned int table,unsigned int entry,struct udp_tunnel_info * ti)41*4882a593Smuzhiyun nsim_udp_tunnel_unset_port(struct net_device *dev, unsigned int table,
42*4882a593Smuzhiyun unsigned int entry, struct udp_tunnel_info *ti)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct netdevsim *ns = netdev_priv(dev);
45*4882a593Smuzhiyun int ret;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun ret = -ns->udp_ports.inject_error;
48*4882a593Smuzhiyun ns->udp_ports.inject_error = 0;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun if (ns->udp_ports.sleep)
51*4882a593Smuzhiyun msleep(ns->udp_ports.sleep);
52*4882a593Smuzhiyun if (!ret) {
53*4882a593Smuzhiyun u32 val = be16_to_cpu(ti->port) << 16 | ti->type;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun if (val == ns->udp_ports.ports[table][entry]) {
56*4882a593Smuzhiyun ns->udp_ports.ports[table][entry] = 0;
57*4882a593Smuzhiyun } else {
58*4882a593Smuzhiyun WARN(1, "entry not installed %x vs %x\n",
59*4882a593Smuzhiyun val, ns->udp_ports.ports[table][entry]);
60*4882a593Smuzhiyun ret = -ENOENT;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun netdev_info(dev, "unset [%d, %d] type %d family %d port %d - %d\n",
65*4882a593Smuzhiyun table, entry, ti->type, ti->sa_family, ntohs(ti->port),
66*4882a593Smuzhiyun ret);
67*4882a593Smuzhiyun return ret;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun static int
nsim_udp_tunnel_sync_table(struct net_device * dev,unsigned int table)71*4882a593Smuzhiyun nsim_udp_tunnel_sync_table(struct net_device *dev, unsigned int table)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun struct netdevsim *ns = netdev_priv(dev);
74*4882a593Smuzhiyun struct udp_tunnel_info ti;
75*4882a593Smuzhiyun unsigned int i;
76*4882a593Smuzhiyun int ret;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun ret = -ns->udp_ports.inject_error;
79*4882a593Smuzhiyun ns->udp_ports.inject_error = 0;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun for (i = 0; i < NSIM_UDP_TUNNEL_N_PORTS; i++) {
82*4882a593Smuzhiyun udp_tunnel_nic_get_port(dev, table, i, &ti);
83*4882a593Smuzhiyun ns->udp_ports.ports[table][i] =
84*4882a593Smuzhiyun be16_to_cpu(ti.port) << 16 | ti.type;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun return ret;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun static const struct udp_tunnel_nic_info nsim_udp_tunnel_info = {
91*4882a593Smuzhiyun .set_port = nsim_udp_tunnel_set_port,
92*4882a593Smuzhiyun .unset_port = nsim_udp_tunnel_unset_port,
93*4882a593Smuzhiyun .sync_table = nsim_udp_tunnel_sync_table,
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun .tables = {
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun .n_entries = NSIM_UDP_TUNNEL_N_PORTS,
98*4882a593Smuzhiyun .tunnel_types = UDP_TUNNEL_TYPE_VXLAN,
99*4882a593Smuzhiyun },
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun .n_entries = NSIM_UDP_TUNNEL_N_PORTS,
102*4882a593Smuzhiyun .tunnel_types = UDP_TUNNEL_TYPE_GENEVE |
103*4882a593Smuzhiyun UDP_TUNNEL_TYPE_VXLAN_GPE,
104*4882a593Smuzhiyun },
105*4882a593Smuzhiyun },
106*4882a593Smuzhiyun };
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun static ssize_t
nsim_udp_tunnels_info_reset_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)109*4882a593Smuzhiyun nsim_udp_tunnels_info_reset_write(struct file *file, const char __user *data,
110*4882a593Smuzhiyun size_t count, loff_t *ppos)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun struct net_device *dev = file->private_data;
113*4882a593Smuzhiyun struct netdevsim *ns = netdev_priv(dev);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun memset(ns->udp_ports.ports, 0, sizeof(ns->udp_ports.__ports));
116*4882a593Smuzhiyun rtnl_lock();
117*4882a593Smuzhiyun udp_tunnel_nic_reset_ntf(dev);
118*4882a593Smuzhiyun rtnl_unlock();
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return count;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun static const struct file_operations nsim_udp_tunnels_info_reset_fops = {
124*4882a593Smuzhiyun .open = simple_open,
125*4882a593Smuzhiyun .write = nsim_udp_tunnels_info_reset_write,
126*4882a593Smuzhiyun .llseek = generic_file_llseek,
127*4882a593Smuzhiyun .owner = THIS_MODULE,
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun
nsim_udp_tunnels_info_create(struct nsim_dev * nsim_dev,struct net_device * dev)130*4882a593Smuzhiyun int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev,
131*4882a593Smuzhiyun struct net_device *dev)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun struct netdevsim *ns = netdev_priv(dev);
134*4882a593Smuzhiyun struct udp_tunnel_nic_info *info;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (nsim_dev->udp_ports.shared && nsim_dev->udp_ports.open_only) {
137*4882a593Smuzhiyun dev_err(&nsim_dev->nsim_bus_dev->dev,
138*4882a593Smuzhiyun "shared can't be used in conjunction with open_only\n");
139*4882a593Smuzhiyun return -EINVAL;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (!nsim_dev->udp_ports.shared)
143*4882a593Smuzhiyun ns->udp_ports.ports = ns->udp_ports.__ports;
144*4882a593Smuzhiyun else
145*4882a593Smuzhiyun ns->udp_ports.ports = nsim_dev->udp_ports.__ports;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun debugfs_create_u32("udp_ports_inject_error", 0600,
148*4882a593Smuzhiyun ns->nsim_dev_port->ddir,
149*4882a593Smuzhiyun &ns->udp_ports.inject_error);
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun ns->udp_ports.dfs_ports[0].array = ns->udp_ports.ports[0];
152*4882a593Smuzhiyun ns->udp_ports.dfs_ports[0].n_elements = NSIM_UDP_TUNNEL_N_PORTS;
153*4882a593Smuzhiyun debugfs_create_u32_array("udp_ports_table0", 0400,
154*4882a593Smuzhiyun ns->nsim_dev_port->ddir,
155*4882a593Smuzhiyun &ns->udp_ports.dfs_ports[0]);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun ns->udp_ports.dfs_ports[1].array = ns->udp_ports.ports[1];
158*4882a593Smuzhiyun ns->udp_ports.dfs_ports[1].n_elements = NSIM_UDP_TUNNEL_N_PORTS;
159*4882a593Smuzhiyun debugfs_create_u32_array("udp_ports_table1", 0400,
160*4882a593Smuzhiyun ns->nsim_dev_port->ddir,
161*4882a593Smuzhiyun &ns->udp_ports.dfs_ports[1]);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun debugfs_create_file("udp_ports_reset", 0200, ns->nsim_dev_port->ddir,
164*4882a593Smuzhiyun dev, &nsim_udp_tunnels_info_reset_fops);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun /* Note: it's not normal to allocate the info struct like this!
167*4882a593Smuzhiyun * Drivers are expected to use a static const one, here we're testing.
168*4882a593Smuzhiyun */
169*4882a593Smuzhiyun info = kmemdup(&nsim_udp_tunnel_info, sizeof(nsim_udp_tunnel_info),
170*4882a593Smuzhiyun GFP_KERNEL);
171*4882a593Smuzhiyun if (!info)
172*4882a593Smuzhiyun return -ENOMEM;
173*4882a593Smuzhiyun ns->udp_ports.sleep = nsim_dev->udp_ports.sleep;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (nsim_dev->udp_ports.sync_all) {
176*4882a593Smuzhiyun info->set_port = NULL;
177*4882a593Smuzhiyun info->unset_port = NULL;
178*4882a593Smuzhiyun } else {
179*4882a593Smuzhiyun info->sync_table = NULL;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (ns->udp_ports.sleep)
183*4882a593Smuzhiyun info->flags |= UDP_TUNNEL_NIC_INFO_MAY_SLEEP;
184*4882a593Smuzhiyun if (nsim_dev->udp_ports.open_only)
185*4882a593Smuzhiyun info->flags |= UDP_TUNNEL_NIC_INFO_OPEN_ONLY;
186*4882a593Smuzhiyun if (nsim_dev->udp_ports.ipv4_only)
187*4882a593Smuzhiyun info->flags |= UDP_TUNNEL_NIC_INFO_IPV4_ONLY;
188*4882a593Smuzhiyun if (nsim_dev->udp_ports.shared)
189*4882a593Smuzhiyun info->shared = &nsim_dev->udp_ports.utn_shared;
190*4882a593Smuzhiyun if (nsim_dev->udp_ports.static_iana_vxlan)
191*4882a593Smuzhiyun info->flags |= UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun dev->udp_tunnel_nic_info = info;
194*4882a593Smuzhiyun return 0;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
nsim_udp_tunnels_info_destroy(struct net_device * dev)197*4882a593Smuzhiyun void nsim_udp_tunnels_info_destroy(struct net_device *dev)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun kfree(dev->udp_tunnel_nic_info);
200*4882a593Smuzhiyun dev->udp_tunnel_nic_info = NULL;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
nsim_udp_tunnels_debugfs_create(struct nsim_dev * nsim_dev)203*4882a593Smuzhiyun void nsim_udp_tunnels_debugfs_create(struct nsim_dev *nsim_dev)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun debugfs_create_bool("udp_ports_sync_all", 0600, nsim_dev->ddir,
206*4882a593Smuzhiyun &nsim_dev->udp_ports.sync_all);
207*4882a593Smuzhiyun debugfs_create_bool("udp_ports_open_only", 0600, nsim_dev->ddir,
208*4882a593Smuzhiyun &nsim_dev->udp_ports.open_only);
209*4882a593Smuzhiyun debugfs_create_bool("udp_ports_ipv4_only", 0600, nsim_dev->ddir,
210*4882a593Smuzhiyun &nsim_dev->udp_ports.ipv4_only);
211*4882a593Smuzhiyun debugfs_create_bool("udp_ports_shared", 0600, nsim_dev->ddir,
212*4882a593Smuzhiyun &nsim_dev->udp_ports.shared);
213*4882a593Smuzhiyun debugfs_create_bool("udp_ports_static_iana_vxlan", 0600, nsim_dev->ddir,
214*4882a593Smuzhiyun &nsim_dev->udp_ports.static_iana_vxlan);
215*4882a593Smuzhiyun debugfs_create_u32("udp_ports_sleep", 0600, nsim_dev->ddir,
216*4882a593Smuzhiyun &nsim_dev->udp_ports.sleep);
217*4882a593Smuzhiyun }
218