/* * af_ax25.c: Network subsystem interface and NEW-AX.25 main functions * * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), * Jonathan (G4KLX), Alan Cox (GW4PTS) * * Comment: SOCK_DGRAM support is missing and should be implemented ASAP. * There is currently no clean way for unproto operation. Most * application use AF_PACKET, SOCK_RAW which is seriously broken * because it skips the DDI arbiter etc. etc. * * Changelog: * 2001-02-06 Joerg Reuter DL1BKE * port to kernel 2.4.1 * * License: This module 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For TIOCINQ/OUTQ */ #include #include #include #include #include #include #include #include #include #include #include #include "af_ax25.h" #include "ax25_vj.h" #include "ax25_ddi.h" #include "ax25_route.h" #include "ax25_core.h" #include "ax25_ctl.h" #include "ax25_in.h" #include "ax25_subr.h" #include "ax25_netlink.h" #include "ax25_timer.h" /* * ------------------------------------------------------------------------ * declaration of static functions * ------------------------------------------------------------------------ */ static int ax25_device_event(struct notifier_block* ,unsigned long, void*); static int ax25_setsockopt(struct socket*, int, int, char*, int); static int ax25_getsockopt(struct socket*, int, int, char*, int*); static int ax25_listen(struct socket*, int); static int ax25_shutdown(struct socket*, int); static int ax25_create(struct socket*, int); static int ax25_release(struct socket *); static int ax25_bind(struct socket*, struct sockaddr*, int); static int ax25_connect(struct socket*, struct sockaddr*, int, int); static int ax25_accept(struct socket*, struct socket*, int); static int ax25_getname(struct socket*, struct sockaddr*, int*, int); static int ax25_sendmsg(struct socket*, struct msghdr*, int, struct scm_cookie*); static int ax25_recvmsg(struct socket*, struct msghdr*, int, int, struct scm_cookie*); static int ax25_ioctl(struct socket*, unsigned int, unsigned long); static int ax25_print_list(char*, off_t*, off_t, int, off_t*, ax25_cb*, char*); static int ax25_get_info(char*, char **, off_t, int); static int ax25_gifconf(struct net_device *dev, char *buf, int len); /* in ax25_ipax.c */ int ipax_init(void); int ipax_cleanup(void); /* * ------------------------------------------------------------------------ * static variables and structures * ------------------------------------------------------------------------ */ /* * table of exportes symbols */ EXPORT_SYMBOL(ax25_find_match_for_uid); EXPORT_SYMBOL(ax25_find_cb); EXPORT_SYMBOL(ax25_linkfail_register); EXPORT_SYMBOL(ax25_linkfail_release); EXPORT_SYMBOL(ax25_listen_register); EXPORT_SYMBOL(ax25_listen_release); EXPORT_SYMBOL(ax25_protocol_register); EXPORT_SYMBOL(ax25_protocol_release); EXPORT_SYMBOL(ax25_send_frame); EXPORT_SYMBOL(ax25_uid_policy); EXPORT_SYMBOL(ax25cmp); EXPORT_SYMBOL(ax2asc); EXPORT_SYMBOL(asc2ax); EXPORT_SYMBOL(null_ax25_address); /* for debugging */ EXPORT_SYMBOL(ax25_kill_by_device); /* * This list contains all sockets that are not bound to * a specific device. */ ax25_cb *ax25_list = NULL; /* * Protocol family registration data */ static struct net_proto_family ax25_family_ops = { family: PF_AX25, create: ax25_create, }; /* * Protocol operations for AF_AX25 */ static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { family: PF_AX25, release: ax25_release, bind: ax25_bind, connect: ax25_connect, socketpair: sock_no_socketpair, accept: ax25_accept, getname: ax25_getname, poll: datagram_poll, ioctl: ax25_ioctl, listen: ax25_listen, shutdown: ax25_shutdown, setsockopt: ax25_setsockopt, getsockopt: ax25_getsockopt, sendmsg: ax25_sendmsg, recvmsg: ax25_recvmsg, mmap: sock_no_mmap }; SOCKOPS_WRAP(ax25_proto, PF_AX25); /* * Device up/down notifier block */ static struct notifier_block ax25_dev_notifier = { ax25_device_event, 0 }; /* * ------------------------------------------------------------------------ * Interface implementation * All public functions of this module are defined here * ------------------------------------------------------------------------ */ /* * ------------------------------------------------------------------------ * Init functions. called by the kernel on startup * ------------------------------------------------------------------------ */ void __init ax25_proto_init(struct net_proto *pro) { struct net_device *dev; sock_register(&ax25_family_ops); ax25_packet_type.type = htons(ETH_P_AX25); dev_add_pack(&ax25_packet_type); register_gifconf(PF_AX25, ax25_gifconf); register_netdevice_notifier(&ax25_dev_notifier); ax25_register_sysctl(); ax25_ddi_init(); ax25_netlink_init(); proc_net_create("ax25_route", 0, ax25_rt_get_info); proc_net_create("ax25", 0, ax25_get_info); proc_net_create("ax25_calls", 0, ax25_cs_get_info); proc_net_create("ax25_ports", 0, ax25_dev_get_info); printk(KERN_INFO "NET4: AX.25 for Linux 2.4-NET4 by DG2FEF\n"); #ifdef CONFIG_INET ipax_init(); #endif for (dev=dev_base; dev!=NULL; dev=dev->next) { if (dev->type == ARPHRD_AX25 && AX25_PTR(dev)) { register_ax25device(dev); if (netif_running(dev)) ax25_dev_device_up(dev); } } } void __exit ax25_proto_remove(void) { int i; struct net_device *dev; proc_net_remove("ax25_route"); proc_net_remove("ax25"); proc_net_remove("ax25_calls"); proc_net_remove("ax25_ports"); for (i=0; idev_addr)) break; } read_unlock(&ax25_dev_lock); return dev; } /* ---------------------------------------------------------------------*/ /* * Kill all bound sockets on a dropped device. */ void ax25_kill_by_device(struct net_device *dev) { ax25_cb *s; unsigned long flags; printk(KERN_WARNING "ax25_kill_by_device(%s)\n", dev->name); save_flags(flags); cli(); for (s = ax25_dev_list(dev); s != NULL; s = ax25_dev_list(dev)) { /* * list structure is being modified by ax25_remove_cb, * so we can not walk along ax25->next path */ if (s->peer && s->peer->device != s->device) ax25_destroy_cb(s->peer); if (s->sk) { ax25_remove_cb(s); ax25_disconnect(s, ENETUNREACH); ax25_close_socket(s->sk, ENETUNREACH); } else ax25_destroy_cb(s); } restore_flags(flags); } /* * ------------------------------------------------------------------------ * End of public area, all private functions of this module are defined * here. * ------------------------------------------------------------------------ */ /* ---------------------------------------------------------------------*/ /* * 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; /* Reject non AX.25 devices */ if (dev->type != ARPHRD_AX25 || !AX25_PTR(dev)) return NOTIFY_DONE; switch (event) { case NETDEV_UP: ax25_dev_device_up(dev); break; case NETDEV_DOWN: ax25_dev_device_down(dev); break; case NETDEV_REGISTER: register_ax25device(dev); break; case NETDEV_UNREGISTER: unregister_ax25device(dev); break; default: break; } return NOTIFY_DONE; } /* ---------------------------------------------------------------------*/ /* * 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 *optval, int optlen) { struct sock *sk = sock->sk; struct net_device *dev; char devname[IFNAMSIZ]; int opt; if (level != SOL_AX25) return -ENOPROTOOPT; if (optlen < sizeof(int)) return -EINVAL; if (get_user(opt, (int *)optval)) return -EFAULT; switch (optname) { case AX25_WINDOW: if (sk->protinfo.ax25->seqmask == AX25_SEQMASK) { if (opt < 1 || opt > 7) return -EINVAL; } else { if (opt < 1 || opt > 63) return -EINVAL; } sk->protinfo.ax25->window = opt; return 0; case AX25_T1: if (opt < 1) return -EINVAL; sk->protinfo.ax25->t1 = opt; sk->protinfo.ax25->rtt = (opt * AX25_TICS) / 4; return 0; case AX25_T2: if (opt < 0) return -EINVAL; sk->protinfo.ax25->t2 = opt; return 0; case AX25_N2: if (opt < 1 || opt > 31) return -EINVAL; sk->protinfo.ax25->n2 = opt; return 0; case AX25_T3: if (opt < 1) return -EINVAL; sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; sk->protinfo.ax25->idle = opt * AX25_SLOWHZ; return 0; case AX25_BACKOFF: if (opt < 0 || opt > 2) return -EINVAL; sk->protinfo.ax25->backoff = opt; return 0; case AX25_EXTSEQ: sk->protinfo.ax25->seqmask = opt ? AX25_ESEQMASK : AX25_SEQMASK; return 0; case AX25_PIDINCL: sk->protinfo.ax25->pidincl = opt ? 1 : 0; return 0; case AX25_IAMDIGI: sk->protinfo.ax25->iamdigi = opt ? 1 : 0; return 0; case AX25_PACLEN: if (opt < 16 || opt > 65535) return -EINVAL; sk->protinfo.ax25->paclen = opt; return 0; case SO_BINDTODEVICE: if (optlen > IFNAMSIZ) optlen = IFNAMSIZ; if (copy_from_user(devname, optval, optlen)) return -EFAULT; dev = dev_get_by_name(devname); if (dev == NULL) return -ENODEV; if (sk->type == SOCK_SEQPACKET && (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) return -EADDRNOTAVAIL; ax25_fillin_cb(sk->protinfo.ax25, dev); return 0; default: return -ENOPROTOOPT; } } /* ---------------------------------------------------------------------*/ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; struct net_device *dev; char devname[IFNAMSIZ]; void *valptr; int val = 0; int maxlen, length; if (level != SOL_AX25) return -ENOPROTOOPT; if (get_user(maxlen, optlen)) return -EFAULT; if (maxlen < 1) return -EFAULT; valptr = (void *) &val; length = min(maxlen, sizeof(int)); switch (optname) { case AX25_WINDOW: val = sk->protinfo.ax25->window; break; case AX25_T1: val = sk->protinfo.ax25->t1; break; case AX25_T2: val = sk->protinfo.ax25->t2; break; case AX25_N2: val = sk->protinfo.ax25->n2; break; case AX25_T3: val = sk->protinfo.ax25->t3 / AX25_SLOWHZ; break; case AX25_IDLE: val = sk->protinfo.ax25->idle / AX25_SLOWHZ; break; case AX25_BACKOFF: val = sk->protinfo.ax25->backoff; break; case AX25_EXTSEQ: val = (sk->protinfo.ax25->seqmask == AX25_ESEQMASK); break; case AX25_PIDINCL: val = sk->protinfo.ax25->pidincl; break; case AX25_IAMDIGI: val = sk->protinfo.ax25->iamdigi; break; case AX25_PACLEN: val = sk->protinfo.ax25->paclen; break; case SO_BINDTODEVICE: dev = sk->protinfo.ax25->device; if (dev != NULL) { strncpy(devname, dev->name, IFNAMSIZ); length = min(strlen(dev->name)+1, maxlen); devname[length-1] = '\0'; } else { *devname = '\0'; length = 1; } valptr = (void *) devname; break; default: return -ENOPROTOOPT; } if (put_user(length, optlen)) return -EFAULT; if (copy_to_user(optval, valptr, length)) return -EFAULT; return 0; } /* ---------------------------------------------------------------------*/ static int ax25_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; if (sk->type != SOCK_SEQPACKET) return -EOPNOTSUPP; if (sk->state != TCP_LISTEN) { /* * POSIX VIOLATION: according to listen(2) the call can * never return EADDRINUSE. bind(2) should have done this. * However, things are different with AX.25. You are * _required_ to bind() before connect() to set the * source callsign of the outgoing connection. But as you * may open multiple connections at one time with the * same source callsign, you cannot perform this check * within bind(). And as I like to have descriptive errors, * EADDRINUSE is perfect to be returned here. */ if (ax25_find_listener(&sk->protinfo.ax25->addr.src, sk->protinfo.ax25->iamdigi, sk->protinfo.ax25->device)) return -EADDRINUSE; ax25_insert_cb(sk->protinfo.ax25); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; } return -EINVAL; } /* ---------------------------------------------------------------------*/ static int ax25_create(struct socket *sock, int protocol) { struct sock *sk; ax25_cb *ax25; 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: #endif #ifdef CONFIG_NETROM case AX25_P_NETROM: #endif #ifdef CONFIG_ROSE case AX25_P_ROSE: #endif return -ESOCKTNOSUPPORT; default: if (ax25_protocol_is_registered(protocol)) return -ESOCKTNOSUPPORT; } break; case SOCK_RAW: break; default: return -ESOCKTNOSUPPORT; } if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) return -ENOMEM; if ((ax25 = ax25_create_cb()) == NULL) { sk_free(sk); return -ENOMEM; } sock_init_data(sock, sk); sk->destruct = ax25_free_sock; sock->ops = &ax25_proto_ops; sk->protocol = protocol; ax25->sk = sk; sk->protinfo.ax25 = ax25; return 0; } static int ax25_release(struct socket *sock) { struct sock *sk = sock->sk; ax25_cb *ax25; if (sk == NULL) return 0; ax25 = sk->protinfo.ax25; sk->state = TCP_CLOSE; sk->shutdown = SHUTDOWN_MASK; /* * don't wake me, I'm dying: this one has cost me nerves a bit. * Seems that we should not attempt to wake an application * that is currently exiting, which is exactly what state_change() * does. It results in calling __wake_up() with invalid arguments */ if (!(current->flags & PF_EXITING)) sk->state_change(sk); sk->dead = 1; if (sk->type == SOCK_STREAM || sk->type == SOCK_SEQPACKET) { switch (ax25->state) { default: ax25_remove_cb(ax25); break; case AX25_STATE_3: /* connected */ case AX25_STATE_4: /* timer recovery */ ax25_set_cond(ax25, AX25_COND_RELEASE); break; } } sk->protinfo.ax25 = NULL; if (ax25->inserted && ax25->device != NULL) { ax25->killtimer = 0; ax25->sk = NULL; } else { ax25_destroy_cb(ax25); } ax25_destroy_socket(sk); return 0; } /* ---------------------------------------------------------------------*/ /* * Former semantics: * * - struct sockaddr_ax25 contains the interface callsign, outgoing * user connects either get the interface callsign or the one * provided by the uid/callsign translation table for the source * address * * - struct full_sockaddr_ax25 provides the interface callsign as * the first digipeater, fsa.fsa_ax25call provides the source * address for the connection. * * New semantics: * * We now have SO_BINDTODEVICE, ax25_bind (should) only set the * source address. Thus we'll allow the "bind to device callsign * provided with the digipeater field" hack only for backward * compatibility. * * NB: I don't follow Matthias' and Jens' patch here as I do * plan to allow multiple callsigns for one uid (including * multiple SSIDs) and assigning user callsigns per interface. */ static int 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; struct net_device *dev; ax25_address *call = NULL; /* already bound */ if (sk->zapped == 0) return -EINVAL; if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; /* wrong family */ if (addr->fsax25_family != AF_AX25) return -EINVAL; /* * User did not set interface with SO_BINDTODEVICE * thus we'll use the compatibility code */ dev = sk->protinfo.ax25->device; if (dev == NULL) { /* Try to find the interface... */ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) { /* device callsign provided... */ if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && (dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) return -EADDRNOTAVAIL; } else { /* addr->fsax25_call is device callsign */ if ((dev = ax25rtr_get_dev(&addr->fsax25_call)) == NULL) return -EADDRNOTAVAIL; } ax25_fillin_cb(sk->protinfo.ax25, dev); } /* root can do whatever (s)he likes, but anyone else... */ if (!capable(CAP_NET_BIND_SERVICE)) { call = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name); if (call == NULL && ax25_uid_policy) return -EACCES; } if (call == NULL) call = &addr->fsax25_call; sk->protinfo.ax25->addr.src = *call; sk->protinfo.ax25->addr.dcount = 0; sk->protinfo.ax25->addr.lastrepeat = -1; // ax25_insert_socket(sk->protinfo.ax25); /* FIXME: gone with Matthias' patch, intentionally? */ sk->zapped = 0; return 0; } /* ---------------------------------------------------------------------*/ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; ax25_addr_t *addr; ax25_address *dest; int ct = 0, err; /* deal with restarts */ if (sock->state == SS_CONNECTING) { switch (sk->state) { case TCP_SYN_SENT: /* still trying */ if (!(flags & O_NONBLOCK)) goto wait_for_con; return -EALREADY; case TCP_ESTABLISHED: /* connection established */ sock->state = SS_CONNECTED; return 0; case TCP_CLOSE: /* connection refused */ case TCP_CLOSE_WAIT: sock->state = SS_UNCONNECTED; return -ECONNREFUSED; } } if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET) return -EISCONN; /* No reconnect on a seqpacket socket */ sk->state = TCP_CLOSE; sock->state = SS_UNCONNECTED; /* * some sanity checks. code further down depends on this */ if (addr_len == sizeof(struct sockaddr_ax25)) printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n", current->comm); else if (addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; /* * Handle digi-peaters to be used. */ SOCK_DEBUG(sk, "ax25_connect: ndigi=%d\n", fsa->fsax25_ndigis); addr = &sk->protinfo.ax25->addr; addr->dcount = 0; addr->lastrepeat = -1; if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsax25_ndigis != 0) { /* Valid number of digipeaters ? */ if (fsa->fsax25_ndigis < 0 || fsa->fsax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; addr->dcount = fsa->fsax25_ndigis; while (ct < fsa->fsax25_ndigis) { if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) addr->lastrepeat = ct; addr->digipeater[ct] = fsa->fsa_digipeater[ct]; ct++; } /* where to go next? either to next digipeater in path ...*/ dest = &addr->digipeater[addr->lastrepeat+1]; } else { /* ... or directly to the destination */ dest = &fsa->fsax25_call; } if (sk->protinfo.ax25->device == NULL) { if ((err = ax25_rt_fillin_dev(sk->protinfo.ax25, dest)) < 0) return err; SOCK_DEBUG(sk, "ax25_connect: device filled in\n"); } addr->dest = fsa->fsax25_call; if (sk->type == SOCK_SEQPACKET) { ax25_cb* ax25 = ax25_find_cb(addr, sk->protinfo.ax25->device); if (ax25) { if (ax25->state != AX25_STATE_0) return -EADDRINUSE; /* Already such a connection */ ax25_destroy_cb(ax25); } } ax25_insert_cb(sk->protinfo.ax25); /* First the easy one */ if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM) { sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; return 0; } /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; /* Start going SABM SABM until a UA or a give up and DM */ ax25_establish_data_link(sk->protinfo.ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; wait_for_con: cli(); /* To avoid races on the sleep */ /* A DM or timeout will go to closed, a UA will go to ABM */ while (sk->state == TCP_SYN_SENT) { interruptible_sleep_on(sk->sleep); if (signal_pending(current)) { sti(); return -ERESTARTSYS; } } if (sk->state != TCP_ESTABLISHED) { /* Not in ABM, not in WAIT_UA -> failed */ sti(); sock->state = SS_UNCONNECTED; return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; sti(); return 0; } /* ---------------------------------------------------------------------*/ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) { struct sock *sk; struct sock *newsk; struct sk_buff *skb; if (sock->state != SS_UNCONNECTED) return -EINVAL; if ((sk = sock->sk) == NULL) return -EINVAL; if (sk->type != SOCK_SEQPACKET) return -EOPNOTSUPP; if (sk->state != TCP_LISTEN) return -EINVAL; /* * The read queue this time is holding sockets ready to use * hooked into the SABM we saved */ do { if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { if (flags & O_NONBLOCK) return -EWOULDBLOCK; interruptible_sleep_on(sk->sleep); if (signal_pending(current)) return -ERESTARTSYS; } } while (skb == NULL); newsk = skb->sk; newsk->pair = NULL; newsk->socket = newsock; newsk->sleep = &newsock->wait; /* Now attach up the new socket */ skb->sk = NULL; skb->destructor = NULL; kfree_skb(skb); sk->ack_backlog--; newsock->sk = newsk; newsock->state = SS_CONNECTED; return 0; } /* ---------------------------------------------------------------------*/ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; struct sock *sk = sock->sk; unsigned char dcount; if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; sax->fsax25_family = AF_AX25; sax->fsax25_call = sk->protinfo.ax25->addr.dest; dcount = sax->fsax25_ndigis = sk->protinfo.ax25->addr.dcount; memcpy(sax->fsa_digipeater, sk->protinfo.ax25->addr.digipeater, dcount * AX25_ADDR_LEN); } else { sax->fsax25_family = AF_AX25; sax->fsax25_call = sk->protinfo.ax25->addr.src; if (sk->protinfo.ax25->device != NULL) { sax->fsax25_ndigis = 1; memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN); } else { sax->fsax25_ndigis = 0; } } *uaddr_len = sizeof (struct full_sockaddr_ax25); return 0; } /* ---------------------------------------------------------------------*/ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; struct sk_buff *skb; unsigned char *asmptr; int size; int lv; int addr_len = msg->msg_namelen; ax25_addr_t addr; if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) return -EINVAL; /* socket must be bound to a name */ if (sk->zapped) return -EADDRNOTAVAIL; /* socket ist shut down */ if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); return -EPIPE; } if (sk->protinfo.ax25->device == NULL) return -ENETUNREACH; if (addr_len != 0) { if (usax->sax25_family != AF_AX25) return -EINVAL; if (sk->type == SOCK_SEQPACKET) return -EISCONN; if (addr_len != sizeof(struct sockaddr_ax25)) { printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n", current->comm); } else if (addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; int ct; /* Valid number of digipeaters ? */ if (usax->sax25_ndigis < 0 || usax->sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; for (ct = 0; ct < usax->sax25_ndigis; ct++) addr.digipeater[ct] = fsa->fsa_digipeater[ct]; addr.lastrepeat = -1; } addr.dcount = usax->sax25_ndigis; addr.dest = usax->sax25_call; } else { if (sk->state != TCP_ESTABLISHED) { if (sk->dead) { send_sig(SIGPIPE, current, 0); return -EPIPE; } return -ENOTCONN; } addr = sk->protinfo.ax25->addr; } SOCK_DEBUG(sk, "AX.25: sendto: Addresses built, building packet.\n"); /* Assume the worst case */ size = len + 3 + ax25_sizeof_addr(&addr) + AX25_BPQ_HEADER_LEN; if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; skb_reserve(skb, size - len); SOCK_DEBUG(sk, "AX.25: Appending user data\n"); /* User data follows immediately after the AX.25 data */ memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); skb->nh.raw = skb->data; /* Add the PID if one is not supplied by the user in the skb */ if (!sk->protinfo.ax25->pidincl) *skb_push(skb, 1) = sk->protocol; SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n"); if (sk->type == SOCK_SEQPACKET) { /* Connected mode sockets go via the LAPB machine */ if (sk->state != TCP_ESTABLISHED) { kfree_skb(skb); return -ENOTCONN; } ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */ return len; } addr.src = sk->protinfo.ax25->addr.src; asmptr = skb_push(skb, ax25_sizeof_addr(&addr)+1); SOCK_DEBUG(sk, "Num digipeaters=%d\n", addr.dcount); /* Build an AX.25 header */ lv = ax25_build_addr(asmptr, &addr, AX25_COMMAND, AX25_SEQMASK); asmptr += lv; SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); *asmptr = AX25_UI; /* Datagram frames go straight out of the door as UI */ ax25_send_unproto(skb, sk->protinfo.ax25->device); return len; } /* ---------------------------------------------------------------------*/ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; int copied; struct sk_buff *skb; int err; /* * This works for seqpacket too. The receiver has ordered the * queue for us! We do one quick check first though */ if (sk->type == SOCK_SEQPACKET && sk->state != TCP_ESTABLISHED) return -ENOTCONN; /* Now we can treat all alike */ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL) return err; if (!sk->protinfo.ax25->pidincl) skb_pull(skb, 1); /* Remove PID */ skb->h.raw = skb->data; copied = skb->len; if (copied > size) { copied = size; msg->msg_flags |= MSG_TRUNC; } skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_namelen != 0) { struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)msg->msg_name; ax25_pktinfo pkt; ax25_parse_addr(skb->mac.raw, skb->data-skb->mac.raw, &pkt); sax->fsax25_family = AF_AX25; sax->fsax25_ndigis = pkt.addr.dcount; sax->fsax25_call = pkt.addr.dest; if (sax->fsax25_ndigis != 0) { int ct; for (ct = 0; ct < pkt.addr.dcount; ct++) sax->fsa_digipeater[ct] = pkt.addr.digipeater[ct]; } msg->msg_namelen = sizeof(struct full_sockaddr_ax25); } skb_free_datagram(sk, skb); return copied; } /* ---------------------------------------------------------------------*/ static int ax25_shutdown(struct socket *sock, int how) { switch (how) { case 0: sock->sk->shutdown = RCV_SHUTDOWN; break; case 1: sock->sk->shutdown = SEND_SHUTDOWN; break; case 2: sock->sk->shutdown = SHUTDOWN_MASK; break; default: return -EINVAL; } return 0; } /* ---------------------------------------------------------------------*/ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; switch (cmd) { case TIOCOUTQ: { long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; if (put_user(amount, (int *)arg)) return -EFAULT; return 0; } 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->receive_queue)) != NULL) amount = skb->len; if (put_user(amount, (int *)arg)) return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { if (sk->stamp.tv_sec == 0) return -ENOENT; if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) return -EFAULT; return 0; } return -EINVAL; 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, (void *)arg, sizeof(sax25))) return -EFAULT; return ax25_uid_ioctl(cmd, &sax25); } case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ long amount; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (get_user(amount, (long *)arg)) return -EFAULT; if (amount > AX25_NOUID_BLOCK) return -EINVAL; ax25_uid_policy = amount; return 0; } case SIOCADDRT: case SIOCDELRT: case SIOCAX25OPTRT: if (!capable(CAP_NET_ADMIN)) return -EPERM; return ax25_rt_ioctl(cmd, (void *)arg); case SIOCAX25CTLCON: if (!capable(CAP_NET_ADMIN)) return -EPERM; return ax25_ctl_ioctl(cmd, (void *)arg); case SIOCAX25GETINFO: case SIOCAX25GETINFOOLD: { struct ax25_info_struct ax25_info; read_lock(&sk->protinfo.ax25->timer_lock); ax25_info.t1 = sk->protinfo.ax25->t1; ax25_info.t2 = sk->protinfo.ax25->t2; ax25_info.t3 = sk->protinfo.ax25->t3 / AX25_SLOWHZ; ax25_info.idle = sk->protinfo.ax25->idle / AX25_SLOWHZ; ax25_info.n2 = sk->protinfo.ax25->n2; ax25_info.t1timer = sk->protinfo.ax25->wrt_timer; ax25_info.t3timer = sk->protinfo.ax25->wrt_timer / AX25_SLOWHZ; ax25_info.t2timer = sk->protinfo.ax25->ack_timer; ax25_info.idletimer = sk->protinfo.ax25->idletimer / AX25_SLOWHZ; ax25_info.n2count = sk->protinfo.ax25->n2count; ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); ax25_info.vs = sk->protinfo.ax25->vs; ax25_info.vr = sk->protinfo.ax25->vr; ax25_info.va = sk->protinfo.ax25->va; ax25_info.vs_max = sk->protinfo.ax25->vs_max; ax25_info.paclen = sk->protinfo.ax25->paclen; ax25_info.window = sk->protinfo.ax25->window; read_unlock(&sk->protinfo.ax25->timer_lock); /* old structure? */ if (cmd == SIOCAX25GETINFOOLD) { static int warned = 0; if (!warned) { printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", current->comm); warned=1; } if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) return -EFAULT; } else { if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct))) return -EINVAL; } return 0; } case SIOCAX25ADDFWD: case SIOCAX25DELFWD: { struct ax25_fwd_struct ax25_fwd; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) return -EFAULT; return ax25_fwd_ioctl(cmd, &ax25_fwd); } case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC: return -EINVAL; default: return dev_ioctl(cmd, (void *)arg); } /*NOTREACHED*/ return 0; } /* ---------------------------------------------------------------------*/ static int ax25_print_list(char *buffer, off_t *begin, off_t offset, int length, off_t *pos, ax25_cb *ax25, char *devname) { int len = 0; /* * New format: * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode */ for ( ; ax25 != NULL; ax25 = ax25->next) { int k; len += sprintf(buffer+len, "%8.8lx %s %s%s ", (long) ax25, devname, ax2asc(&ax25->addr.src), ax25->iamdigi? "*":""); len += sprintf(buffer+len, "%s", ax2asc(&ax25->addr.dest)); for (k=0; k < ax25->addr.dcount; k++) { len += sprintf(buffer+len, ",%s%s", ax2asc(&ax25->addr.digipeater[k]), ax25->addr.lastrepeat == k ? "*":""); } read_lock(&ax25->timer_lock); len += sprintf(buffer+len, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", ax25->state, ax25->vs, ax25->vr, ax25->va, ax25->wrt_timer , ax25->t1, ax25->ack_timer , ax25->t2, ax25->t3 , ax25->t3, ax25->idletimer / AX25_SLOWHZ, ax25->idle / AX25_SLOWHZ, ax25->n2count, ax25->n2, ax25->rtt / AX25_TICS, ax25->window, ax25->paclen); read_unlock(&ax25->timer_lock); if (ax25->sk != NULL) { len += sprintf(buffer + len, " %d %d %ld\n", atomic_read(&ax25->sk->wmem_alloc), atomic_read(&ax25->sk->rmem_alloc), ax25->sk->socket != NULL ? ax25->sk->socket->inode->i_ino : 0L); } else { len += sprintf(buffer + len, " * * *\n"); } *pos = *begin + len; if (*pos < offset) { len = 0; *begin = *pos; } if (*pos > offset + length) break; } return len; } /* ---------------------------------------------------------------------*/ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) { struct net_device *dev; int len = 0; off_t begin = 0; off_t pos = begin; int i; read_lock(&ax25_dev_lock); len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_list, "*"); for (i = 0; i < AX25_MAX_DEVICES; i++) { dev = ax25_devices[i]; if (!dev || !(dev->flags & IFF_UP)) continue; len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_dev_list(dev), dev->name); } read_unlock(&ax25_dev_lock); *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len; } /* ---------------------------------------------------------------------*/ static int ax25_gifconf(struct net_device *dev, char *buf, int len) { struct ifreq ifr; int done=0; if (!buf) { done += sizeof(ifr); return done; } if (len < (int) sizeof(ifr)) return done; memset(&ifr, 0, sizeof(struct ifreq)); strcpy(ifr.ifr_name, dev->name); (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_AX25; if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) return -EFAULT; done += sizeof(struct ifreq); return done; }