cregit-Linux how code gets into the kernel

Release 4.15 net/batman-adv/icmp_socket.c

Directory: net/batman-adv
/* Copyright (C) 2007-2017  B.A.T.M.A.N. contributors:
 *
 * Marek Lindner
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "icmp_socket.h"
#include "main.h"

#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/export.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/if_ether.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#include <linux/poll.h>
#include <linux/printk.h>
#include <linux/sched.h> /* for linux/wait.h */
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/wait.h>

#include "hard-interface.h"
#include "log.h"
#include "originator.h"
#include "packet.h"
#include "send.h"


static struct batadv_socket_client *batadv_socket_client_hash[256];

static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
				     struct batadv_icmp_header *icmph,
				     size_t icmp_len);


void batadv_socket_init(void) { memset(batadv_socket_client_hash, 0, sizeof(batadv_socket_client_hash)); }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann19100.00%3100.00%
Total19100.00%3100.00%


static int batadv_socket_open(struct inode *inode, struct file *file) { unsigned int i; struct batadv_socket_client *socket_client; if (!try_module_get(THIS_MODULE)) return -EBUSY; nonseekable_open(inode, file); socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); if (!socket_client) { module_put(THIS_MODULE); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) { if (!batadv_socket_client_hash[i]) { batadv_socket_client_hash[i] = socket_client; break; } } if (i == ARRAY_SIZE(batadv_socket_client_hash)) { pr_err("Error - can't add another packet client: maximum number of clients reached\n"); kfree(socket_client); module_put(THIS_MODULE); return -EXFULL; } INIT_LIST_HEAD(&socket_client->queue_list); socket_client->queue_len = 0; socket_client->index = i; socket_client->bat_priv = inode->i_private; spin_lock_init(&socket_client->lock); init_waitqueue_head(&socket_client->queue_wait); file->private_data = socket_client; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann192100.00%6100.00%
Total192100.00%6100.00%


static int batadv_socket_release(struct inode *inode, struct file *file) { struct batadv_socket_client *client = file->private_data; struct batadv_socket_packet *packet, *tmp; spin_lock_bh(&client->lock); /* for all packets in the queue ... */ list_for_each_entry_safe(packet, tmp, &client->queue_list, list) { list_del(&packet->list); kfree(packet); } batadv_socket_client_hash[client->index] = NULL; spin_unlock_bh(&client->lock); kfree(client); module_put(THIS_MODULE); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann7981.44%480.00%
Geliang Tang1818.56%120.00%
Total97100.00%5100.00%


static ssize_t batadv_socket_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct batadv_socket_client *socket_client = file->private_data; struct batadv_socket_packet *socket_packet; size_t packet_len; int error; if ((file->f_flags & O_NONBLOCK) && socket_client->queue_len == 0) return -EAGAIN; if (!buf || count < sizeof(struct batadv_icmp_packet)) return -EINVAL; if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; error = wait_event_interruptible(socket_client->queue_wait, socket_client->queue_len); if (error) return error; spin_lock_bh(&socket_client->lock); socket_packet = list_first_entry(&socket_client->queue_list, struct batadv_socket_packet, list); list_del(&socket_packet->list); socket_client->queue_len--; spin_unlock_bh(&socket_client->lock); packet_len = min(count, socket_packet->icmp_len); error = copy_to_user(buf, &socket_packet->icmp_packet, packet_len); kfree(socket_packet); if (error) return -EFAULT; return packet_len; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann20099.50%583.33%
Paul Kot10.50%116.67%
Total201100.00%6100.00%


static ssize_t batadv_socket_write(struct file *file, const char __user *buff, size_t len, loff_t *off) { struct batadv_socket_client *socket_client = file->private_data; struct batadv_priv *bat_priv = socket_client->bat_priv; struct batadv_hard_iface *primary_if = NULL; struct sk_buff *skb; struct batadv_icmp_packet_rr *icmp_packet_rr; struct batadv_icmp_header *icmp_header; struct batadv_orig_node *orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL; size_t packet_len = sizeof(struct batadv_icmp_packet); u8 *addr; if (len < sizeof(struct batadv_icmp_header)) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: invalid packet size\n"); return -EINVAL; } primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) { len = -EFAULT; goto out; } if (len >= BATADV_ICMP_MAX_PACKET_SIZE) packet_len = BATADV_ICMP_MAX_PACKET_SIZE; else packet_len = len; skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN); if (!skb) { len = -ENOMEM; goto out; } skb->priority = TC_PRIO_CONTROL; skb_reserve(skb, ETH_HLEN); icmp_header = skb_put(skb, packet_len); if (copy_from_user(icmp_header, buff, packet_len)) { len = -EFAULT; goto free_skb; } if (icmp_header->packet_type != BATADV_ICMP) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); len = -EINVAL; goto free_skb; } switch (icmp_header->msg_type) { case BATADV_ECHO_REQUEST: if (len < sizeof(struct batadv_icmp_packet)) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: invalid packet size\n"); len = -EINVAL; goto free_skb; } if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dst_unreach; orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst); if (!orig_node) goto dst_unreach; neigh_node = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT); if (!neigh_node) goto dst_unreach; if (!neigh_node->if_incoming) goto dst_unreach; if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) goto dst_unreach; icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header; if (packet_len == sizeof(*icmp_packet_rr)) { addr = neigh_node->if_incoming->net_dev->dev_addr; ether_addr_copy(icmp_packet_rr->rr[0], addr); } break; default: batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: got unknown message type\n"); len = -EINVAL; goto free_skb; } icmp_header->uid = socket_client->index; if (icmp_header->version != BATADV_COMPAT_VERSION) { icmp_header->msg_type = BATADV_PARAMETER_PROBLEM; icmp_header->version = BATADV_COMPAT_VERSION; batadv_socket_add_packet(socket_client, icmp_header, packet_len); goto free_skb; } ether_addr_copy(icmp_header->orig, primary_if->net_dev->dev_addr); batadv_send_unicast_skb(skb, neigh_node); goto out; dst_unreach: icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE; batadv_socket_add_packet(socket_client, icmp_header, packet_len); free_skb: kfree_skb(skb); out: if (primary_if) batadv_hardif_put(primary_if); if (neigh_node) batadv_neigh_node_put(neigh_node); if (orig_node) batadv_orig_node_put(orig_node); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann31657.88%1659.26%
Simon Wunderlich12823.44%311.11%
Marek Lindner7413.55%311.11%
Antonio Quartulli244.40%414.81%
Linus Lüssing40.73%13.70%
Total546100.00%27100.00%


static unsigned int batadv_socket_poll(struct file *file, poll_table *wait) { struct batadv_socket_client *socket_client = file->private_data; poll_wait(file, &socket_client->queue_wait, wait); if (socket_client->queue_len > 0) return POLLIN | POLLRDNORM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann53100.00%3100.00%
Total53100.00%3100.00%

static const struct file_operations batadv_fops = { .owner = THIS_MODULE, .open = batadv_socket_open, .release = batadv_socket_release, .read = batadv_socket_read, .write = batadv_socket_write, .poll = batadv_socket_poll, .llseek = no_llseek, };
int batadv_socket_setup(struct batadv_priv *bat_priv) { struct dentry *d; if (!bat_priv->debug_dir) goto err; d = debugfs_create_file(BATADV_ICMP_SOCKET, 0600, bat_priv->debug_dir, bat_priv, &batadv_fops); if (!d) goto err; return 0; err: return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann60100.00%7100.00%
Total60100.00%7100.00%

/** * batadv_socket_add_packet - schedule an icmp packet to be sent to * userspace on an icmp socket. * @socket_client: the socket this packet belongs to * @icmph: pointer to the header of the icmp packet * @icmp_len: total length of the icmp packet */
static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, struct batadv_icmp_header *icmph, size_t icmp_len) { struct batadv_socket_packet *socket_packet; size_t len; socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); if (!socket_packet) return; len = icmp_len; /* check the maximum length before filling the buffer */ if (len > sizeof(socket_packet->icmp_packet)) len = sizeof(socket_packet->icmp_packet); INIT_LIST_HEAD(&socket_packet->list); memcpy(&socket_packet->icmp_packet, icmph, len); socket_packet->icmp_len = len; spin_lock_bh(&socket_client->lock); /* while waiting for the lock the socket_client could have been * deleted */ if (!batadv_socket_client_hash[icmph->uid]) { spin_unlock_bh(&socket_client->lock); kfree(socket_packet); return; } list_add_tail(&socket_packet->list, &socket_client->queue_list); socket_client->queue_len++; if (socket_client->queue_len > 100) { socket_packet = list_first_entry(&socket_client->queue_list, struct batadv_socket_packet, list); list_del(&socket_packet->list); kfree(socket_packet); socket_client->queue_len--; } spin_unlock_bh(&socket_client->lock); wake_up(&socket_client->queue_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann17483.25%571.43%
Simon Wunderlich3416.27%114.29%
Antonio Quartulli10.48%114.29%
Total209100.00%7100.00%

/** * batadv_socket_receive_packet - schedule an icmp packet to be received * locally and sent to userspace. * @icmph: pointer to the header of the icmp packet * @icmp_len: total length of the icmp packet */
void batadv_socket_receive_packet(struct batadv_icmp_header *icmph, size_t icmp_len) { struct batadv_socket_client *hash; hash = batadv_socket_client_hash[icmph->uid]; if (hash) batadv_socket_add_packet(hash, icmph, icmp_len); }

Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann3587.50%466.67%
Simon Wunderlich410.00%116.67%
Antonio Quartulli12.50%116.67%
Total40100.00%6100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Sven Eckelmann129181.50%2764.29%
Simon Wunderlich16910.67%37.14%
Marek Lindner744.67%37.14%
Antonio Quartulli271.70%614.29%
Geliang Tang181.14%12.38%
Linus Lüssing40.25%12.38%
Paul Kot10.06%12.38%
Total1584100.00%42100.00%
Directory: net/batman-adv
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.