/* * Socket removal during an interrupt is now safe.
*/ staticvoid ax25_cb_del(ax25_cb *ax25)
{
spin_lock_bh(&ax25_list_lock); if (!hlist_unhashed(&ax25->ax25_node)) {
hlist_del_init(&ax25->ax25_node);
ax25_cb_put(ax25);
}
spin_unlock_bh(&ax25_list_lock);
}
/* * Kill all bound sockets on a dropped device.
*/ staticvoid ax25_kill_by_device(struct net_device *dev)
{
ax25_dev *ax25_dev;
ax25_cb *s; struct sock *sk;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) return;
ax25_dev->device_up = false;
spin_lock_bh(&ax25_list_lock);
again:
ax25_for_each(s, &ax25_list) { if (s->ax25_dev == ax25_dev) {
sk = s->sk; if (!sk) {
spin_unlock_bh(&ax25_list_lock);
ax25_disconnect(s, ENETUNREACH);
s->ax25_dev = NULL;
ax25_cb_del(s);
spin_lock_bh(&ax25_list_lock); goto again;
}
sock_hold(sk);
spin_unlock_bh(&ax25_list_lock);
lock_sock(sk);
ax25_disconnect(s, ENETUNREACH);
s->ax25_dev = NULL; if (sk->sk_socket) {
netdev_put(ax25_dev->dev,
&s->dev_tracker);
ax25_dev_put(ax25_dev);
}
ax25_cb_del(s);
release_sock(sk);
spin_lock_bh(&ax25_list_lock);
sock_put(sk); /* 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);
}
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;
spin_lock(&ax25_list_lock);
ax25_for_each(s, &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;
/* * 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(const ax25_address *src_addr, ax25_address *dest_addr,
ax25_digi *digi, struct net_device *dev)
{
ax25_cb *s;
spin_lock_bh(&ax25_list_lock);
ax25_for_each(s, &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);
/* * 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;
/* * dl1bke 960311: set parameters for existing AX.25 connections, * includes a KILL command to abort any connection. * VERY useful for debugging ;-)
*/ staticint ax25_ctl_ioctl(constunsignedint cmd, void __user *arg)
{ struct ax25_ctl_struct ax25_ctl;
ax25_digi digi;
ax25_dev *ax25_dev;
ax25_cb *ax25; unsignedint k; int ret = 0;
if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) return -EFAULT;
if (ax25_ctl.digi_count > AX25_MAX_DIGIS) return -EINVAL;
/* * 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;
}
/* * XXX: when creating ax25_sock we should update the .obj_size setting * below.
*/ staticstruct proto ax25_proto = {
.name = "AX25",
.owner = THIS_MODULE,
.obj_size = sizeof(struct ax25_sock),
};
staticint ax25_create(struct net *net, struct socket *sock, int protocol, int kern)
{ struct sock *sk;
ax25_cb *ax25;
if (protocol < 0 || protocol > U8_MAX) return -EINVAL;
if (!net_eq(net, &init_net)) return -EAFNOSUPPORT;
switch (sock->type) { case SOCK_DGRAM: if (protocol == 0 || protocol == PF_AX25)
protocol = AX25_P_TEXT; break;
case SOCK_SEQPACKET: switch (protocol) { case 0: case PF_AX25: /* For CLX */
protocol = AX25_P_TEXT; break; case AX25_P_SEGMENT: #ifdef CONFIG_INET case AX25_P_ARP: case AX25_P_IP: #endif #ifdef CONFIG_NETROM case AX25_P_NETROM: #endif #ifdef CONFIG_ROSE case AX25_P_ROSE: #endif return -ESOCKTNOSUPPORT; #ifdef CONFIG_NETROM_MODULE case AX25_P_NETROM: if (ax25_protocol_is_registered(AX25_P_NETROM)) return -ESOCKTNOSUPPORT; break; #endif #ifdef CONFIG_ROSE_MODULE case AX25_P_ROSE: if (ax25_protocol_is_registered(AX25_P_ROSE)) return -ESOCKTNOSUPPORT; break; #endif default: break;
} break;
case SOCK_RAW: if (!capable(CAP_NET_RAW)) return -EPERM; break; default: return -ESOCKTNOSUPPORT;
}
sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto, kern); if (sk == NULL) return -ENOMEM;
/* * We support a funny extension here so you can (as root) give any callsign * digipeated via a local address as source. This hack is obsolete now * that we've implemented support for SO_BINDTODEVICE. It is however small * and trivially backward compatible.
*/ staticint ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{ struct sock *sk = sock->sk; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
ax25_dev *ax25_dev = NULL;
ax25_uid_assoc *user;
ax25_address call;
ax25_cb *ax25; int err = 0;
if (addr_len != sizeof(struct sockaddr_ax25) &&
addr_len != sizeof(struct full_sockaddr_ax25)) /* support for old structure may go away some time * ax25_bind(): uses old (6 digipeater) socket structure.
*/ if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
(addr_len > sizeof(struct full_sockaddr_ax25))) return -EINVAL;
if (addr->fsa_ax25.sax25_family != AF_AX25) return -EINVAL;
user = ax25_findbyuid(current_euid()); if (user) {
call = user->call;
ax25_uid_put(user);
} else { if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) return -EACCES;
/* * FIXME: nonblock behaviour looks like it may have a bug.
*/ staticint __must_check ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{ struct sock *sk = sock->sk;
ax25_cb *ax25 = sk_to_ax25(sk), *ax25t; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
ax25_digi *digi = NULL; int ct = 0, err = 0;
/* * some sanity checks. code further down depends on this
*/
if (addr_len == sizeof(struct sockaddr_ax25)) /* support for this will go away in early 2.5.x * ax25_connect(): uses obsolete socket structure
*/
; elseif (addr_len != sizeof(struct full_sockaddr_ax25)) /* support for old structure may go away some time * ax25_connect(): uses old (6 digipeater) socket structure.
*/ if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
(addr_len > sizeof(struct full_sockaddr_ax25))) return -EINVAL;
if (fsa->fsa_ax25.sax25_family != AF_AX25) return -EINVAL;
lock_sock(sk);
/* deal with restarts */ if (sock->state == SS_CONNECTING) { switch (sk->sk_state) { case TCP_SYN_SENT: /* still trying */
err = -EINPROGRESS; goto out_release;
case TCP_ESTABLISHED: /* connection established */
sock->state = SS_CONNECTED; goto out_release;
/* Must bind first - autobinding does not work. */ if (sock_flag(sk, SOCK_ZAPPED)) {
kfree(digi);
err = -EINVAL; goto out_release;
}
/* Check to see if the device has been filled in, error if it hasn't. */ if (ax25->ax25_dev == NULL) {
kfree(digi);
err = -EHOSTUNREACH; goto out_release;
}
if (sk->sk_type == SOCK_SEQPACKET &&
(ax25t=ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi,
ax25->ax25_dev->dev))) {
kfree(digi);
err = -EADDRINUSE; /* Already such a connection */
ax25_cb_put(ax25t); goto out_release;
}
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX:
ax25_std_establish_data_link(ax25); break;
#ifdef CONFIG_AX25_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE:
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; if (ax25->ax25_dev->dama.slave)
ax25_ds_establish_data_link(ax25); else
ax25_std_establish_data_link(ax25); break; #endif
}
ax25->state = AX25_STATE_1;
ax25_start_heartbeat(ax25);
/* Now the loop */ if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) {
err = -EINPROGRESS; goto out_release;
}
if (sk->sk_state == TCP_SYN_SENT) {
DEFINE_WAIT(wait);
for (;;) {
prepare_to_wait(sk_sleep(sk), &wait,
TASK_INTERRUPTIBLE); if (sk->sk_state != TCP_SYN_SENT) break; if (!signal_pending(current)) {
release_sock(sk);
schedule();
lock_sock(sk); continue;
}
err = -ERESTARTSYS; break;
}
finish_wait(sk_sleep(sk), &wait);
if (err) goto out_release;
}
if (sk->sk_state != TCP_ESTABLISHED) { /* Not in ABM, not in WAIT_UA -> failed */
sock->state = SS_UNCONNECTED;
err = sock_error(sk); /* Always set at this point */ goto out_release;
}
/* * The read queue this time is holding sockets ready to use * hooked into the SABM we saved
*/ for (;;) {
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
skb = skb_dequeue(&sk->sk_receive_queue); if (skb) break;
sax = *usax; if (sk->sk_type == SOCK_SEQPACKET &&
ax25cmp(&ax25->dest_addr, &sax.sax25_call)) {
err = -EISCONN; goto out;
} if (usax->sax25_ndigis == 0)
dp = NULL; else
dp = &dtmp;
} else { /* * FIXME: 1003.1g - if the socket is like this because * it has become closed (not started closed) and is VC * we ought to SIGPIPE, EPIPE
*/ if (sk->sk_state != TCP_ESTABLISHED) {
err = -ENOTCONN; goto out;
}
sax.sax25_family = AF_AX25;
sax.sax25_call = ax25->dest_addr;
dp = ax25->digipeat;
}
/* Build a packet */ /* Assume the worst case */
size = len + ax25->ax25_dev->dev->hard_header_len;
/* User data follows immediately after the AX.25 data */ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
kfree_skb(skb); goto out;
}
skb_reset_network_header(skb);
/* Add the PID if one is not supplied by the user in the skb */ if (!ax25->pidincl)
*(u8 *)skb_push(skb, 1) = sk->sk_protocol;
if (sk->sk_type == SOCK_SEQPACKET) { /* Connected mode sockets go via the LAPB machine */ if (sk->sk_state != TCP_ESTABLISHED) {
kfree_skb(skb);
err = -ENOTCONN; goto out;
}
/* Shove it onto the queue and kick */
ax25_output(ax25, ax25->paclen, skb);
/* Datagram frames go straight out of the door as UI */
ax25_queue_xmit(skb, ax25->ax25_dev->dev);
err = len;
out:
release_sock(sk);
return err;
}
staticint ax25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags)
{ struct sock *sk = sock->sk; struct sk_buff *skb, *last; struct sk_buff_head *sk_queue; int copied; int err = 0; int off = 0; long timeo;
lock_sock(sk); /* * This works for seqpacket too. The receiver has ordered the * queue for us! We do one quick check first though
*/ if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_ESTABLISHED) {
err = -ENOTCONN; goto out;
}
/* We need support for non-blocking reads. */
sk_queue = &sk->sk_receive_queue;
skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, &err, &last); /* If no packet is available, release_sock(sk) and try again. */ if (!skb) { if (err != -EAGAIN) goto out;
release_sock(sk);
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); while (timeo && !__skb_wait_for_more_packets(sk, sk_queue, &err,
&timeo, last)) {
skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off,
&err, &last); if (skb) break;
if (err != -EAGAIN) goto done;
} if (!skb) goto done;
lock_sock(sk);
}
if (!sk_to_ax25(sk)->pidincl)
skb_pull(skb, 1); /* Remove PID */
memset(sax, 0, sizeof(struct full_sockaddr_ax25));
ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL,
&digi, NULL, NULL);
sax->sax25_family = AF_AX25; /* We set this correctly, even though we may not let the application know the digi calls further down (because it
did NOT ask to know them). This could get political... **/
sax->sax25_ndigis = digi.ndigi;
sax->sax25_call = src;
if (sax->sax25_ndigis != 0) { int ct; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
lock_sock(sk); switch (cmd) { case TIOCOUTQ: { long amount;
amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); if (amount < 0)
amount = 0;
res = put_user(amount, (int __user *)argp); break;
}
case TIOCINQ: { struct sk_buff *skb; long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
amount = skb->len;
res = put_user(amount, (int __user *) argp); break;
}
case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ case SIOCAX25GETUID: { struct sockaddr_ax25 sax25; if (copy_from_user(&sax25, argp, sizeof(sax25))) {
res = -EFAULT; break;
}
res = ax25_uid_ioctl(cmd, &sax25); break;
}
case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ long amount; if (!capable(CAP_NET_ADMIN)) {
res = -EPERM; break;
} if (get_user(amount, (long __user *)argp)) {
res = -EFAULT; break;
} if (amount < 0 || amount > AX25_NOUID_BLOCK) {
res = -EINVAL; break;
}
ax25_uid_policy = amount;
res = 0; break;
}
case SIOCADDRT: case SIOCDELRT: case SIOCAX25OPTRT: if (!capable(CAP_NET_ADMIN)) {
res = -EPERM; break;
}
res = ax25_rt_ioctl(cmd, argp); break;
case SIOCAX25CTLCON: if (!capable(CAP_NET_ADMIN)) {
res = -EPERM; break;
}
res = ax25_ctl_ioctl(cmd, argp); break;
case SIOCAX25GETINFO: case SIOCAX25GETINFOOLD: {
ax25_cb *ax25 = sk_to_ax25(sk); struct ax25_info_struct ax25_info;
/* old structure? */ if (cmd == SIOCAX25GETINFOOLD) { staticint warned = 0; if (!warned) {
printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n",
current->comm);
warned=1;
}
if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct_deprecated))) {
res = -EFAULT; break;
}
} else { if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct))) {
res = -EINVAL; break;
}
}
res = 0; break;
}
case SIOCAX25ADDFWD: case SIOCAX25DELFWD: { struct ax25_fwd_struct ax25_fwd; if (!capable(CAP_NET_ADMIN)) {
res = -EPERM; break;
} if (copy_from_user(&ax25_fwd, argp, sizeof(ax25_fwd))) {
res = -EFAULT; break;
}
res = ax25_fwd_ioctl(cmd, &ax25_fwd); break;
}
case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC:
res = -EINVAL; break;
default:
res = -ENOIOCTLCMD; break;
}
release_sock(sk);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.