at v3.7 348 lines 9.2 kB view raw
1/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: 2 * 3 * Marek Lindner 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of version 2 of the GNU General Public 7 * License as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 17 * 02110-1301, USA 18 */ 19 20#include "main.h" 21#include <linux/debugfs.h> 22#include <linux/slab.h> 23#include "icmp_socket.h" 24#include "send.h" 25#include "hash.h" 26#include "originator.h" 27#include "hard-interface.h" 28 29static struct batadv_socket_client *batadv_socket_client_hash[256]; 30 31static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, 32 struct batadv_icmp_packet_rr *icmp_packet, 33 size_t icmp_len); 34 35void batadv_socket_init(void) 36{ 37 memset(batadv_socket_client_hash, 0, sizeof(batadv_socket_client_hash)); 38} 39 40static int batadv_socket_open(struct inode *inode, struct file *file) 41{ 42 unsigned int i; 43 struct batadv_socket_client *socket_client; 44 45 nonseekable_open(inode, file); 46 47 socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); 48 49 if (!socket_client) 50 return -ENOMEM; 51 52 for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) { 53 if (!batadv_socket_client_hash[i]) { 54 batadv_socket_client_hash[i] = socket_client; 55 break; 56 } 57 } 58 59 if (i == ARRAY_SIZE(batadv_socket_client_hash)) { 60 pr_err("Error - can't add another packet client: maximum number of clients reached\n"); 61 kfree(socket_client); 62 return -EXFULL; 63 } 64 65 INIT_LIST_HEAD(&socket_client->queue_list); 66 socket_client->queue_len = 0; 67 socket_client->index = i; 68 socket_client->bat_priv = inode->i_private; 69 spin_lock_init(&socket_client->lock); 70 init_waitqueue_head(&socket_client->queue_wait); 71 72 file->private_data = socket_client; 73 74 batadv_inc_module_count(); 75 return 0; 76} 77 78static int batadv_socket_release(struct inode *inode, struct file *file) 79{ 80 struct batadv_socket_client *socket_client = file->private_data; 81 struct batadv_socket_packet *socket_packet; 82 struct list_head *list_pos, *list_pos_tmp; 83 84 spin_lock_bh(&socket_client->lock); 85 86 /* for all packets in the queue ... */ 87 list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) { 88 socket_packet = list_entry(list_pos, 89 struct batadv_socket_packet, list); 90 91 list_del(list_pos); 92 kfree(socket_packet); 93 } 94 95 batadv_socket_client_hash[socket_client->index] = NULL; 96 spin_unlock_bh(&socket_client->lock); 97 98 kfree(socket_client); 99 batadv_dec_module_count(); 100 101 return 0; 102} 103 104static ssize_t batadv_socket_read(struct file *file, char __user *buf, 105 size_t count, loff_t *ppos) 106{ 107 struct batadv_socket_client *socket_client = file->private_data; 108 struct batadv_socket_packet *socket_packet; 109 size_t packet_len; 110 int error; 111 112 if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0)) 113 return -EAGAIN; 114 115 if ((!buf) || (count < sizeof(struct batadv_icmp_packet))) 116 return -EINVAL; 117 118 if (!access_ok(VERIFY_WRITE, buf, count)) 119 return -EFAULT; 120 121 error = wait_event_interruptible(socket_client->queue_wait, 122 socket_client->queue_len); 123 124 if (error) 125 return error; 126 127 spin_lock_bh(&socket_client->lock); 128 129 socket_packet = list_first_entry(&socket_client->queue_list, 130 struct batadv_socket_packet, list); 131 list_del(&socket_packet->list); 132 socket_client->queue_len--; 133 134 spin_unlock_bh(&socket_client->lock); 135 136 packet_len = min(count, socket_packet->icmp_len); 137 error = copy_to_user(buf, &socket_packet->icmp_packet, packet_len); 138 139 kfree(socket_packet); 140 141 if (error) 142 return -EFAULT; 143 144 return packet_len; 145} 146 147static ssize_t batadv_socket_write(struct file *file, const char __user *buff, 148 size_t len, loff_t *off) 149{ 150 struct batadv_socket_client *socket_client = file->private_data; 151 struct batadv_priv *bat_priv = socket_client->bat_priv; 152 struct batadv_hard_iface *primary_if = NULL; 153 struct sk_buff *skb; 154 struct batadv_icmp_packet_rr *icmp_packet; 155 156 struct batadv_orig_node *orig_node = NULL; 157 struct batadv_neigh_node *neigh_node = NULL; 158 size_t packet_len = sizeof(struct batadv_icmp_packet); 159 160 if (len < sizeof(struct batadv_icmp_packet)) { 161 batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 162 "Error - can't send packet from char device: invalid packet size\n"); 163 return -EINVAL; 164 } 165 166 primary_if = batadv_primary_if_get_selected(bat_priv); 167 168 if (!primary_if) { 169 len = -EFAULT; 170 goto out; 171 } 172 173 if (len >= sizeof(struct batadv_icmp_packet_rr)) 174 packet_len = sizeof(struct batadv_icmp_packet_rr); 175 176 skb = dev_alloc_skb(packet_len + ETH_HLEN); 177 if (!skb) { 178 len = -ENOMEM; 179 goto out; 180 } 181 182 skb_reserve(skb, ETH_HLEN); 183 icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len); 184 185 if (copy_from_user(icmp_packet, buff, packet_len)) { 186 len = -EFAULT; 187 goto free_skb; 188 } 189 190 if (icmp_packet->header.packet_type != BATADV_ICMP) { 191 batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 192 "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); 193 len = -EINVAL; 194 goto free_skb; 195 } 196 197 if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { 198 batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 199 "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n"); 200 len = -EINVAL; 201 goto free_skb; 202 } 203 204 icmp_packet->uid = socket_client->index; 205 206 if (icmp_packet->header.version != BATADV_COMPAT_VERSION) { 207 icmp_packet->msg_type = BATADV_PARAMETER_PROBLEM; 208 icmp_packet->header.version = BATADV_COMPAT_VERSION; 209 batadv_socket_add_packet(socket_client, icmp_packet, 210 packet_len); 211 goto free_skb; 212 } 213 214 if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) 215 goto dst_unreach; 216 217 orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst); 218 if (!orig_node) 219 goto dst_unreach; 220 221 neigh_node = batadv_orig_node_get_router(orig_node); 222 if (!neigh_node) 223 goto dst_unreach; 224 225 if (!neigh_node->if_incoming) 226 goto dst_unreach; 227 228 if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) 229 goto dst_unreach; 230 231 memcpy(icmp_packet->orig, 232 primary_if->net_dev->dev_addr, ETH_ALEN); 233 234 if (packet_len == sizeof(struct batadv_icmp_packet_rr)) 235 memcpy(icmp_packet->rr, 236 neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN); 237 238 batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); 239 goto out; 240 241dst_unreach: 242 icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE; 243 batadv_socket_add_packet(socket_client, icmp_packet, packet_len); 244free_skb: 245 kfree_skb(skb); 246out: 247 if (primary_if) 248 batadv_hardif_free_ref(primary_if); 249 if (neigh_node) 250 batadv_neigh_node_free_ref(neigh_node); 251 if (orig_node) 252 batadv_orig_node_free_ref(orig_node); 253 return len; 254} 255 256static unsigned int batadv_socket_poll(struct file *file, poll_table *wait) 257{ 258 struct batadv_socket_client *socket_client = file->private_data; 259 260 poll_wait(file, &socket_client->queue_wait, wait); 261 262 if (socket_client->queue_len > 0) 263 return POLLIN | POLLRDNORM; 264 265 return 0; 266} 267 268static const struct file_operations batadv_fops = { 269 .owner = THIS_MODULE, 270 .open = batadv_socket_open, 271 .release = batadv_socket_release, 272 .read = batadv_socket_read, 273 .write = batadv_socket_write, 274 .poll = batadv_socket_poll, 275 .llseek = no_llseek, 276}; 277 278int batadv_socket_setup(struct batadv_priv *bat_priv) 279{ 280 struct dentry *d; 281 282 if (!bat_priv->debug_dir) 283 goto err; 284 285 d = debugfs_create_file(BATADV_ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR, 286 bat_priv->debug_dir, bat_priv, &batadv_fops); 287 if (!d) 288 goto err; 289 290 return 0; 291 292err: 293 return -ENOMEM; 294} 295 296static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, 297 struct batadv_icmp_packet_rr *icmp_packet, 298 size_t icmp_len) 299{ 300 struct batadv_socket_packet *socket_packet; 301 302 socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); 303 304 if (!socket_packet) 305 return; 306 307 INIT_LIST_HEAD(&socket_packet->list); 308 memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len); 309 socket_packet->icmp_len = icmp_len; 310 311 spin_lock_bh(&socket_client->lock); 312 313 /* while waiting for the lock the socket_client could have been 314 * deleted 315 */ 316 if (!batadv_socket_client_hash[icmp_packet->uid]) { 317 spin_unlock_bh(&socket_client->lock); 318 kfree(socket_packet); 319 return; 320 } 321 322 list_add_tail(&socket_packet->list, &socket_client->queue_list); 323 socket_client->queue_len++; 324 325 if (socket_client->queue_len > 100) { 326 socket_packet = list_first_entry(&socket_client->queue_list, 327 struct batadv_socket_packet, 328 list); 329 330 list_del(&socket_packet->list); 331 kfree(socket_packet); 332 socket_client->queue_len--; 333 } 334 335 spin_unlock_bh(&socket_client->lock); 336 337 wake_up(&socket_client->queue_wait); 338} 339 340void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet, 341 size_t icmp_len) 342{ 343 struct batadv_socket_client *hash; 344 345 hash = batadv_socket_client_hash[icmp_packet->uid]; 346 if (hash) 347 batadv_socket_add_packet(hash, icmp_packet, icmp_len); 348}