Release 4.11 net/batman-adv/icmp_socket.c
/* 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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 19 | 100.00% | 3 | 100.00% |
Total | 19 | 100.00% | 3 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 192 | 100.00% | 6 | 100.00% |
Total | 192 | 100.00% | 6 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 79 | 81.44% | 4 | 80.00% |
Geliang Tang | 18 | 18.56% | 1 | 20.00% |
Total | 97 | 100.00% | 5 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 206 | 99.52% | 5 | 83.33% |
Paul Kot | 1 | 0.48% | 1 | 16.67% |
Total | 207 | 100.00% | 6 | 100.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 = (struct batadv_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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 320 | 58.08% | 16 | 59.26% |
Simon Wunderlich | 129 | 23.41% | 3 | 11.11% |
Marek Lindner | 74 | 13.43% | 3 | 11.11% |
Antonio Quartulli | 24 | 4.36% | 4 | 14.81% |
Linus Lüssing | 4 | 0.73% | 1 | 3.70% |
Total | 551 | 100.00% | 27 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 53 | 100.00% | 3 | 100.00% |
Total | 53 | 100.00% | 3 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 60 | 100.00% | 7 | 100.00% |
Total | 60 | 100.00% | 7 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 174 | 83.25% | 5 | 71.43% |
Simon Wunderlich | 34 | 16.27% | 1 | 14.29% |
Antonio Quartulli | 1 | 0.48% | 1 | 14.29% |
Total | 209 | 100.00% | 7 | 100.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
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 35 | 87.50% | 4 | 66.67% |
Simon Wunderlich | 4 | 10.00% | 1 | 16.67% |
Antonio Quartulli | 1 | 2.50% | 1 | 16.67% |
Total | 40 | 100.00% | 6 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sven Eckelmann | 1301 | 81.57% | 27 | 64.29% |
Simon Wunderlich | 170 | 10.66% | 3 | 7.14% |
Marek Lindner | 74 | 4.64% | 3 | 7.14% |
Antonio Quartulli | 27 | 1.69% | 6 | 14.29% |
Geliang Tang | 18 | 1.13% | 1 | 2.38% |
Linus Lüssing | 4 | 0.25% | 1 | 2.38% |
Paul Kot | 1 | 0.06% | 1 | 2.38% |
Total | 1595 | 100.00% | 42 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.