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