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(ns->udp_ports.sleep); 23 24 if (!ret) { 25 if (ns->udp_ports.ports[table][entry]) 26 ret = -EBUSY; 27 else 28 ns->udp_ports.ports[table][entry] = 29 be16_to_cpu(ti->port) << 16 | ti->type; 30 } 31 32 netdev_info(dev, "set [%d, %d] type %d family %d port %d - %d\n", 33 table, entry, ti->type, ti->sa_family, ntohs(ti->port), 34 ret); 35 return ret; 36 } 37 38 static int 39 nsim_udp_tunnel_unset_port(struct net_device *dev, unsigned int table, 40 unsigned int entry, struct udp_tunnel_info *ti) 41 { 42 struct netdevsim *ns = netdev_priv(dev); 43 int ret; 44 45 ret = -ns->udp_ports.inject_error; 46 ns->udp_ports.inject_error = 0; 47 48 if (ns->udp_ports.sleep) 49 msleep(ns->udp_ports.sleep); 50 if (!ret) { 51 u32 val = be16_to_cpu(ti->port) << 16 | ti->type; 52 53 if (val == ns->udp_ports.ports[table][entry]) 54 ns->udp_ports.ports[table][entry] = 0; 55 else 56 ret = -ENOENT; 57 } 58 59 netdev_info(dev, "unset [%d, %d] type %d family %d port %d - %d\n", 60 table, entry, ti->type, ti->sa_family, ntohs(ti->port), 61 ret); 62 return ret; 63 } 64 65 static int 66 nsim_udp_tunnel_sync_table(struct net_device *dev, unsigned int table) 67 { 68 struct netdevsim *ns = netdev_priv(dev); 69 struct udp_tunnel_info ti; 70 unsigned int i; 71 int ret; 72 73 ret = -ns->udp_ports.inject_error; 74 ns->udp_ports.inject_error = 0; 75 76 for (i = 0; i < NSIM_UDP_TUNNEL_N_PORTS; i++) { 77 udp_tunnel_nic_get_port(dev, table, i, &ti); 78 ns->udp_ports.ports[table][i] = 79 be16_to_cpu(ti.port) << 16 | ti.type; 80 } 81 82 return ret; 83 } 84 85 static const struct udp_tunnel_nic_info nsim_udp_tunnel_info = { 86 .set_port = nsim_udp_tunnel_set_port, 87 .unset_port = nsim_udp_tunnel_unset_port, 88 .sync_table = nsim_udp_tunnel_sync_table, 89 90 .tables = { 91 { 92 .n_entries = NSIM_UDP_TUNNEL_N_PORTS, 93 .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, 94 }, 95 { 96 .n_entries = NSIM_UDP_TUNNEL_N_PORTS, 97 .tunnel_types = UDP_TUNNEL_TYPE_GENEVE | 98 UDP_TUNNEL_TYPE_VXLAN_GPE, 99 }, 100 }, 101 }; 102 103 static ssize_t 104 nsim_udp_tunnels_info_reset_write(struct file *file, const char __user *data, 105 size_t count, loff_t *ppos) 106 { 107 struct net_device *dev = file->private_data; 108 struct netdevsim *ns = netdev_priv(dev); 109 110 memset(&ns->udp_ports.ports, 0, sizeof(ns->udp_ports.ports)); 111 rtnl_lock(); 112 udp_tunnel_nic_reset_ntf(dev); 113 rtnl_unlock(); 114 115 return count; 116 } 117 118 static const struct file_operations nsim_udp_tunnels_info_reset_fops = { 119 .open = simple_open, 120 .write = nsim_udp_tunnels_info_reset_write, 121 .llseek = generic_file_llseek, 122 }; 123 124 int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev, 125 struct net_device *dev) 126 { 127 struct netdevsim *ns = netdev_priv(dev); 128 struct udp_tunnel_nic_info *info; 129 130 debugfs_create_u32("udp_ports_inject_error", 0600, 131 ns->nsim_dev_port->ddir, 132 &ns->udp_ports.inject_error); 133 134 ns->udp_ports.dfs_ports[0].array = ns->udp_ports.ports[0]; 135 ns->udp_ports.dfs_ports[0].n_elements = NSIM_UDP_TUNNEL_N_PORTS; 136 debugfs_create_u32_array("udp_ports_table0", 0400, 137 ns->nsim_dev_port->ddir, 138 &ns->udp_ports.dfs_ports[0]); 139 140 ns->udp_ports.dfs_ports[1].array = ns->udp_ports.ports[1]; 141 ns->udp_ports.dfs_ports[1].n_elements = NSIM_UDP_TUNNEL_N_PORTS; 142 debugfs_create_u32_array("udp_ports_table1", 0400, 143 ns->nsim_dev_port->ddir, 144 &ns->udp_ports.dfs_ports[1]); 145 146 debugfs_create_file("udp_ports_reset", 0200, ns->nsim_dev_port->ddir, 147 dev, &nsim_udp_tunnels_info_reset_fops); 148 149 /* Note: it's not normal to allocate the info struct like this! 150 * Drivers are expected to use a static const one, here we're testing. 151 */ 152 info = kmemdup(&nsim_udp_tunnel_info, sizeof(nsim_udp_tunnel_info), 153 GFP_KERNEL); 154 if (!info) 155 return -ENOMEM; 156 ns->udp_ports.sleep = nsim_dev->udp_ports.sleep; 157 158 if (nsim_dev->udp_ports.sync_all) { 159 info->set_port = NULL; 160 info->unset_port = NULL; 161 } else { 162 info->sync_table = NULL; 163 } 164 165 if (ns->udp_ports.sleep) 166 info->flags |= UDP_TUNNEL_NIC_INFO_MAY_SLEEP; 167 if (nsim_dev->udp_ports.open_only) 168 info->flags |= UDP_TUNNEL_NIC_INFO_OPEN_ONLY; 169 if (nsim_dev->udp_ports.ipv4_only) 170 info->flags |= UDP_TUNNEL_NIC_INFO_IPV4_ONLY; 171 172 dev->udp_tunnel_nic_info = info; 173 return 0; 174 } 175 176 void nsim_udp_tunnels_info_destroy(struct net_device *dev) 177 { 178 kfree(dev->udp_tunnel_nic_info); 179 dev->udp_tunnel_nic_info = NULL; 180 } 181 182 void nsim_udp_tunnels_debugfs_create(struct nsim_dev *nsim_dev) 183 { 184 debugfs_create_bool("udp_ports_sync_all", 0600, nsim_dev->ddir, 185 &nsim_dev->udp_ports.sync_all); 186 debugfs_create_bool("udp_ports_open_only", 0600, nsim_dev->ddir, 187 &nsim_dev->udp_ports.open_only); 188 debugfs_create_bool("udp_ports_ipv4_only", 0600, nsim_dev->ddir, 189 &nsim_dev->udp_ports.ipv4_only); 190 debugfs_create_u32("udp_ports_sleep", 0600, nsim_dev->ddir, 191 &nsim_dev->udp_ports.sleep); 192 } 193