/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
* Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk)
* Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
* Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
* Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
* Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl)
* Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
*/
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/slab.h>
#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/netfilter.h>
#include <linux/sysctl.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <net/net_namespace.h>
#include <net/tcp_states.h>
#include <net/ip.h>
#include <net/arp.h>
HLIST_HEAD(ax25_list);
DEFINE_SPINLOCK(ax25_list_lock);
static const struct proto_ops ax25_proto_ops;
static void ax25_free_sock(struct sock *sk)
{
ax25_cb_put(ax25_sk(sk));
}
/*
* Socket removal during an interrupt is now safe.
*/
static void ax25_cb_del(ax25_cb *ax25)
{
if (!hlist_unhashed(&ax25->ax25_node)) {
spin_lock_bh(&ax25_list_lock);
hlist_del_init(&ax25->ax25_node);
spin_unlock_bh(&ax25_list_lock);
ax25_cb_put(ax25);
}
}
/*
* Kill all bound sockets on a dropped device.
*/
static void ax25_kill_by_device(struct net_device *dev)
{
ax25_dev *ax25_dev;
ax25_cb *s;
struct hlist_node *node;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return;
spin_lock_bh(&ax25_list_lock);
again:
ax25_for_each(s, node, &ax25_list) {
if (s->ax25_dev == ax25_dev) {
s->ax25_dev = NULL;
spin_unlock_bh(&ax25_list_lock);
ax25_disconnect(s, ENETUNREACH);
spin_lock_bh(&ax25_list_lock);
/* The entry could have been deleted from the
* list meanwhile and thus the next pointer is
* no longer valid. Play it safe and restart
* the scan. Forward progress is ensured
* because we set s->ax25_dev to NULL and we
* are never passed a NULL 'dev' argument.
*/
goto again;
}
}
spin_unlock_bh(&ax25_list_lock);
}
/*
* Handle device status changes.
*/
static int ax25_device_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = (struct net_device *)ptr;
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
/* Reject non AX.25 devices */
if (dev->type != ARPHRD_AX25)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UP:
ax25_dev_device_up(dev);
break;
case NETDEV_DOWN:
ax25_kill_by_device(dev);
ax25_rt_device_down(dev);
ax25_dev_device_down(dev);
break;
default:
break;
}
return NOTIFY_DONE;
}
/*
* Add a socket to the bound sockets list.
*/
void ax25_cb_add(ax25_cb *ax25)
{
spin_lock_bh(&ax25_list_lock);
ax25_cb_hold(ax25);
hlist_add_head(&ax25->ax25_node, &ax25_list);
spin_unlock_bh(&ax25_list_lock);
}
/*
* Find a socket that wants to accept the SABM we have just
* received.
*/
struct sock *ax25_find_listener(ax25_address *addr, int digi,
struct net_device *dev, int type)
{
ax25_cb *s;
struct hlist_node *node;
spin_lock(&ax25_list_lock);
ax25_for_each(s, node, &ax25_list) {
if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
continue;
if (s->sk && !ax25cmp(&s->source_addr, addr) &&
s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) {
/* If device is null we match any device */
if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
sock_hold(s->sk);
spin_unlock(&ax25_list_lock);
return s->sk;
}
}
}
spin_unlock(&ax25_list_lock);
return NULL;
}
/*
* Find an AX.25 socket given both ends.
*/
struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr,
int type)
{
struct sock *sk = NULL;
ax25_cb *s;
struct hlist_node *node;
spin_lock(&ax25_list_lock);
ax25_for_each(s, node, &ax25_list) {
if (s->sk && !ax25cmp(&s->source_addr, my_addr) &&
!ax25cmp(&s->dest_addr, dest_addr) &&
s->sk->sk_type == type) {
sk = s->sk;
sock_hold(sk);
break;
}
}
spin_unlock(&ax25_list_lock);
return sk;
}
/*
* Find an AX.25 control block given both ends. It will only pick up
* floating AX.25 control blocks or non Raw socket bound control blocks.
*/
ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr,
ax25_digi *digi, struct net_device *dev)
{
ax25_cb *s;
struct hlist_node *node;
spin_lock_bh(&ax25_list_lock);
ax25_for_each(s, node, &ax25_list) {
if (s->sk && s->sk->sk_type != SOCK_SEQPACKET)
continue;
if (s->ax25_dev == NULL)
continue;
if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
if (digi != NULL && digi->ndigi != 0) {
if (s->digipeat == NULL)
continue;
if (ax25digicmp(s->digipeat, digi) != 0)
continue;
} else {
if (s->digipeat != NULL && s->digipeat->ndigi != 0)
continue;
}
ax25_cb_hold(s);
spin_unlock_bh(&ax25_list_lock);
return s;
}
}
spin_unlock_bh(&ax25_list_lock);
return NULL;
}
EXPORT_SYMBOL(ax25_find_cb);
void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto)
{
ax25_cb *s;
struct sk_buff *copy;
struct hlist_node *node;
spin_lock(&ax25_list_lock);
ax25_for_each(s, node, &ax25_list) {
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 &&
s->sk->sk_type == SOCK_RAW &&
s->sk->sk_protocol == proto &&
s->ax25_dev->dev == skb->dev &&
atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) {
if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
continue;
if (sock_queue_rcv_skb(s->sk, copy) != 0)
kfree_skb(copy);
}
}
spin_unlock(&ax25_list_lock);
}
/*
* Deferred destroy.
*/
void ax25_destroy_socket(ax25_cb *);
/*
* Handler for deferred kills.
*/
static void ax25_destroy_timer(unsigned long data)
{
ax25_cb *ax25=(ax25_cb *)data;
struct sock *sk;
sk=ax25->sk;
bh_lock_sock(sk);
sock_hold(sk);
ax25_destroy_socket(ax25);
bh_unlock_sock(sk);
sock_put(sk);
}
/*
* This is called from user mode and the timers. Thus it protects itself
* against interrupt users but doesn't worry about being called during
* work. Once it is removed from the queue no interrupt or bottom half
* will touch it and we are (fairly 8-) ) safe.
*/
void ax25_destroy_socket(ax25_cb *ax25)
{
struct sk_buff *skb;
ax25_cb_del(ax25);
ax25_stop_heartbeat(ax25);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
ax25_stop_t3timer(ax25);
ax25_stop_idletimer(ax25);
ax25_clear_queues(ax25); /* Flush the queues */
if (ax25->sk != NULL) {
while ((skb = skb_dequeue(&ax25->sk->sk_receive_queue)) != NULL) {
if (skb->sk != ax25->sk) {
/* A pending connection */
ax25_cb *sax25 = ax25_sk(skb->sk);
/* Queue the unaccepted socket for death */
sock_orphan(skb->sk);
/* 9A4GL: hack to release unaccepted sockets */
skb->sk->sk_state = TCP_LISTEN;
ax25_start_heartbeat(sax25);
sax25->state = AX25_STATE_0;
}
kfree_skb(skb);
}
skb_queue_purge(&ax25->sk->sk_write_queue);
}
if (ax25->sk != NULL) {
if (sk_has_allocations(ax25->sk)) {
/* Defer: outstanding buffers */
setup_timer(&ax25->dtimer, ax25_destroy_timer,
(unsigned long)ax25);
ax25->dtimer.expires = jiffies + 2 * HZ;
add_timer(&ax25->dtimer);
} else {
struct sock *sk=ax25->sk;
ax25->sk=NULL;
sock_put(sk);
}
} else {
ax25_cb_put(ax25);
}
}
/*
* dl1bke 960311: set parameters for existing AX.25 connections,
* includes a KILL command to abort any connection.
* VERY useful for debugging ;-)
*/
static int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg)
{
struct ax25_ctl_struct ax25_ctl;
ax25_digi digi;
ax25_dev *ax25_dev;
ax25_cb *ax25;
unsigned int k;
int ret = 0;
if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
return -ENODEV;
if (ax25_ctl.digi_count > AX25_MAX_DIGIS)
return -EINVAL;
if (ax25_ctl.arg > ULONG_MAX / HZ && ax25_ctl.cmd != AX25_KILL)
return -EINVAL;
digi.ndigi = ax25_ctl.digi_count;
for (k = 0; k < digi.ndigi; k++)
digi.calls[k] = ax25_ctl.digi_addr[k];
if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL)
return -ENOTCONN;
switch (ax25_ctl.cmd) {
case AX25_KILL:
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
#ifdef CONFIG_AX25_DAMA_SLAVE
if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
ax25_dama_off(ax25);
#endif
ax25_disconnect(ax25, ENETRESET);
break;
case AX25_WINDOW:
if (ax25->modulus == AX25_MODULUS) {
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
goto einval_put;
} else {
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
goto einval_put;
}
ax25->window = ax25_ctl.arg;
break;
case AX25_T1:
if (ax25_ctl.arg < 1)
goto einval_put;
ax25->rtt = (ax25_ctl.arg * HZ) / 2;
ax25->t1 = ax25_ctl.arg * HZ;
break;
case AX25_T2:
if (ax25_ctl.arg < 1)
goto einval_put;
ax25->t2 = ax25_ctl.arg * HZ;
break;
case AX25_N2:
if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
goto einval_put;
ax25->n2count = 0;
ax25->n2 = ax25_ctl.arg;
break;
case AX25_T3:
ax25->t3 = ax25_ctl.arg * HZ;
break;
case AX25_IDLE:
ax25->idle = ax25_ctl.arg * 60 * HZ;
break;
case AX25_PACLEN:
if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
goto einval_put;
ax25->paclen = ax25_ctl.arg;
break;
default:
goto einval_put;
}
out_put:
ax25_cb_put(ax25);
return ret;
einval_put:
ret = -EINVAL;
goto out_put;
}
static void ax25_fillin_cb_from_dev(ax25_cb *ax25, ax25_dev *ax25_dev)
{
ax25->rtt = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2;
ax25->t1 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]);
ax25->t2 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]);
ax25->t3 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]);
ax25->n2 = ax25_dev->values[AX25_VALUES_N2];
ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN];
ax25->idle = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]);
ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
ax25->modulus = AX25_EMODULUS;
ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
}
}
/*
* Fill in a created AX.25 created control block with the default
* values for a particular device.
*/
void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
{
ax25->ax25_dev = ax25_dev;
if (ax25->ax25_dev != NULL) {
ax25_fillin_cb_from_dev(ax25, ax25_dev);
return;
}
/*
* No device, use kernel / AX.25 spec default values
*/
ax25->rtt = msecs_to_jiffies(AX25_DEF_T1) / 2;
ax25->t1 = msecs_to_jiffies(AX25_DEF_T1);
ax25->t2 = msecs_to_jiffies(AX25_DEF_T2);
ax25->t3 = msecs_to_jiffies(AX25_DEF_T3);
ax25->n2 = AX25_DEF_N2;
ax25->paclen = AX25_DEF_PACLEN;
ax25->idle = msecs_to_jiffies(AX25_DEF_IDLE);
ax25->backoff = AX25_DEF_BACKOFF;
if (AX25_DEF_AXDEFMODE) {
ax25->modulus = AX25_EMODULUS;
ax25->window = AX25_DEF_EWINDOW;
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = AX25_DEF_WINDOW;
}
}
/*
* Create an empty AX.25 control block.
*/
ax25_cb *ax25_create_cb(void)
{
ax25_cb *ax25;
if ((ax25 = kzalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
return NULL;
atomic_set(&ax25->refcount, 1);
skb_queue_head_init(&ax25->write_queue);
skb_queue_head_init(&ax25->frag_queue);
skb_queue_head_init(&ax25->ack_queue);
skb_queue_head_init(&ax25->reseq_queue);
ax25_setup_timers(ax25);
ax25_fillin_cb(ax25, NULL);
ax25->state = AX25_STATE_0;
return ax25;
}
/*
* Handling for system calls applied via the various interfaces to an
* AX25 socket object
*/
static int ax25_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
ax25_cb *ax25;
struct net_device *dev;
char devname[IFNAMSIZ];
int opt, res = 0;
if (level != SOL_AX25)
return -ENOPROTOOPT;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(opt, (int __user *)optval))
return -EFAULT;
lock_sock(sk);
ax25 = ax25_sk(sk);
switch (optname) {
case AX25_WINDOW:
if (ax25->modulus == AX25_MODULUS) {
if (opt < 1 || opt > 7) {
res = -EINVAL;
break;
}
} else {
if (opt < 1 || opt > 63) {
res = -EINVAL;
break;
}
}
ax25->window = opt;
break;
case AX25_T1:
if (opt < 1) {
res = -EINVAL;
break;
}
ax25->rtt = (opt * HZ) >> 1;
ax25->t1 = opt * HZ;
break;
case AX25_T2:
if (opt < 1) {
res = -EINVAL;
break;
}
ax25->t2 = opt * HZ;
break;
case AX25_N2:
if (opt < 1 || opt > 31) {
res = -EINVAL;
break;
}
ax25->n2 = opt;
break;
case AX25_T3:
if (opt < 1) {
res = -EINVAL;
break;
}
ax25->t3 = opt * HZ;
break;
case AX25_IDLE:
if (opt < 0) {
res = -EINVAL;
break;
}
ax25->idle = opt * 60 * HZ;
break;
case AX25_BACKOFF:
if (opt < 0 || opt > 2) {
res = -EINVAL;
break;
}
ax25->backoff = opt;
break;
case AX25_EXTSEQ:
ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
break;
case AX25_PIDINCL:
ax25->pidincl = opt ? 1 : 0;
break;
case AX25_IAMDIGI:
ax25->iamdigi = opt ? 1 : 0;
break;
case AX25_PACLEN:
if (opt < 16 || opt > 65535) {
res = -EINVAL;
break;
}
ax25->paclen = opt;
break;
case SO_BINDTODEVICE:
if (optlen > IFNAMSIZ)
optlen = IFNAMSIZ;
if (copy_from_user(devname, optval, optlen)) {
res = -EFAULT;
break;
}
if (sk->sk_type == SOCK_SEQPACKET &&
(sock->state != SS_UNCONNECTED ||
sk->sk_state == TCP_LISTEN)) {
res = -EADDRNOTAVAIL;
break;
}
dev = dev_get_by_name(&init_net, devname);
if (!dev) {
res = -ENODEV;
break;
}
ax25->ax25_dev = ax25_dev_ax25dev(dev);
ax25_fillin_cb(ax25, ax25->ax25_dev);
dev_put(dev);
break;
default:
res = -ENOPROTOOPT;
}
release_sock(sk);
return res;
}
static int ax25_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{