diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /net/ax25 | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'net/ax25')
-rw-r--r-- | net/ax25/Makefile | 32 | ||||
-rw-r--r-- | net/ax25/README.AX25 | 20 | ||||
-rw-r--r-- | net/ax25/af_ax25.c | 1722 | ||||
-rw-r--r-- | net/ax25/ax25_in.c | 503 | ||||
-rw-r--r-- | net/ax25/ax25_out.c | 295 | ||||
-rw-r--r-- | net/ax25/ax25_route.c | 505 | ||||
-rw-r--r-- | net/ax25/ax25_subr.c | 363 | ||||
-rw-r--r-- | net/ax25/ax25_timer.c | 413 | ||||
-rw-r--r-- | net/ax25/sysctl_net_ax25.c | 60 |
9 files changed, 2933 insertions, 980 deletions
diff --git a/net/ax25/Makefile b/net/ax25/Makefile index 77301561c..172d89eed 100644 --- a/net/ax25/Makefile +++ b/net/ax25/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the Linux TCP/IP (INET) layer. +# Makefile for the Linux AX.25 layer. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -7,34 +7,14 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< -.c.s: - $(CC) $(CFLAGS) -S $< +O_TARGET := ax25.o +O_OBJS := sysctl_net_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o +M_OBJS := $(O_TARGET) -OBJS := af_ax25.o +OX_OBJS += af_ax25.o -ifdef CONFIG_AX25 - -OBJS := $(OBJS) ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o - -endif - -ax25.o: $(OBJS) - $(LD) -r -o ax25.o $(OBJS) - -dep: - $(CPP) -M *.c > .depend +include $(TOPDIR)/Rules.make tar: tar -cvf /dev/f1 . - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff --git a/net/ax25/README.AX25 b/net/ax25/README.AX25 deleted file mode 100644 index 90b6a037a..000000000 --- a/net/ax25/README.AX25 +++ /dev/null @@ -1,20 +0,0 @@ -This is a working version of the new state machine code for AX25 under -Linux. It is closely based on the SDL diagrams published in the ARRL 7th -Computer Networking Conference papers, and they should be referred to when -reading the code, notably the stuff in ax25_in.c. The next stage is to -separate the ax25 control block from the socket and then add NET/ROM and -connected mode IP. I would also like to add the extended AX25 designed by a -Dutch station which allows for window sizes up to 127. - -This code will work the same as the old code, although the display in -/proc/net/ax25 is a little different, but should be understandable. Please -give this code a work out and report any bugs to me either at -jsn@cs.nott.ac.uk or at GB7DAD.GBR.EU. - -This code has taught me a lot about the internals of the networking side of -Linux especially skbuff handling and I now feel happy about implementing the -higher level protocols. - -73's - -Jonathan diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 725939698..7d08fed10 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,10 +1,10 @@ /* - * AX.25 release 029 + * AX.25 release 034 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.10 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -13,7 +13,7 @@ * 2 of the License, or (at your option) any later version. * * History - * AX.25 006 Alan(GW4PTS) Nearly died of shock - its working 8-) + * AX.25 006 Alan(GW4PTS) Nearly died of shock - it's working 8-) * AX.25 007 Alan(GW4PTS) Removed the silliest bugs * AX.25 008 Alan(GW4PTS) Cleaned up, fixed a few state machine problems, added callbacks * AX.25 009 Alan(GW4PTS) Emergency patch kit to fix memory corruption @@ -25,7 +25,7 @@ * Correct receive on SOCK_DGRAM. * AX.25 013 Alan(GW4PTS) Send DM to all unknown frames, missing initialiser fixed * Leave spare SSID bits set (DAMA etc) - thanks for bug report, - * removed device registration (its not used or needed). Clean up for + * removed device registration (it's not used or needed). Clean up for * gcc 2.5.8. PID to AX25_P_ * AX.25 014 Alan(GW4PTS) Cleanup and NET3 merge * AX.25 015 Alan(GW4PTS) Internal test version. @@ -56,17 +56,46 @@ * Jonathan(G4KLX) and removed all the old Berkeley, added IP mode registration. * Darryl(G7LED) stuff. Cross-port digipeating. Minor fixes and enhancements. * Alan(GW4PTS) Missed suser() on axassociate checks + * AX.25 030 Alan(GW4PTS) Added variable length headers. + * Jonathan(G4KLX) Added BPQ Ethernet interface. + * Steven(GW7RRM) Added digi-peating control ioctl. + * Added extended AX.25 support. + * Added AX.25 frame segmentation. + * Darryl(G7LED) Changed connect(), recvfrom(), sendto() sockaddr/addrlen to + * fall inline with bind() and new policy. + * Moved digipeating ctl to new ax25_dev structs. + * Fixed ax25_release(), set TCP_CLOSE, wakeup app + * context, THEN make the sock dead. + * Alan(GW4PTS) Cleaned up for single recvmsg methods. + * Alan(GW4PTS) Fixed not clearing error on connect failure. + * AX.25 031 Jonathan(G4KLX) Added binding to any device. + * Joerg(DL1BKE) Added DAMA support, fixed (?) digipeating, fixed buffer locking + * for "virtual connect" mode... Result: Probably the + * "Most Buggiest Code You've Ever Seen" (TM) + * HaJo(DD8NE) Implementation of a T5 (idle) timer + * Joerg(DL1BKE) Renamed T5 to IDLE and changed behaviour: + * the timer gets reloaded on every received or transmitted + * I frame for IP or NETROM. The idle timer is not active + * on "vanilla AX.25" connections. Furthermore added PACLEN + * to provide AX.25-layer based fragmentation (like WAMPES) + * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout error. + * ax25_send_frame() limits the number of enqueued + * datagrams per socket. + * AX.25 033 Jonathan(G4KLX) Removed auto-router. + * Hans(PE1AYX) Converted to Module. + * Joerg(DL1BKE) Moved BPQ Ethernet to seperate driver. + * AX.25 034 Jonathan(G4KLX) 2.1 changes + * Alan(GW4PTS) Small POSIXisations * * To do: - * Support use as digipeater, including an on/off ioctl * Restructure the ax25_rcv code to be cleaner/faster and * copy only when needed. - * Consider better arbitary protocol support. - * Fix non-blocking connect failure. + * Consider better arbitrary protocol support. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +#include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -80,28 +109,30 @@ #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/segment.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/firewall.h> +#include <linux/sysctl.h> #include <net/ip.h> #include <net/arp.h> -#define CONFIG_AX25_XDIGI /* Cross port (band) digi stuff */ - -/**********************************************************************************************************************\ -* * -* Handlers for the socket list. * -* * -\**********************************************************************************************************************/ +/* + * The null address is defined as a callsign of all spaces with an + * SSID of zero. + */ +ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; -static ax25_cb *volatile ax25_list = NULL; +ax25_cb *volatile ax25_list = NULL; /* * ax25 -> ascii conversion @@ -112,8 +143,7 @@ char *ax2asc(ax25_address *a) char c, *s; int n; - for (n = 0, s = buf; n < 6; n++) - { + for (n = 0, s = buf; n < 6; n++) { c = (a->ax25_call[n] >> 1) & 0x7F; if (c != ' ') *s++ = c; @@ -121,8 +151,7 @@ char *ax2asc(ax25_address *a) *s++ = '-'; - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) - { + if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { *s++ = '1'; n -= 10; } @@ -130,11 +159,50 @@ char *ax2asc(ax25_address *a) *s++ = n + '0'; *s++ = '\0'; - return(buf); + if (*buf == '\0' || *buf == '-') + return "*"; + + return buf; } /* + * ascii -> ax25 conversion + */ +ax25_address *asc2ax(char *callsign) +{ + static ax25_address addr; + char *s; + int n; + + for (s = callsign, n = 0; n < 6; n++) { + if (*s != '\0' && *s != '-') + addr.ax25_call[n] = *s++; + else + addr.ax25_call[n] = ' '; + addr.ax25_call[n] <<= 1; + addr.ax25_call[n] &= 0xFE; + } + + if (*s++ == '\0') { + addr.ax25_call[6] = 0x00; + return &addr; + } + + addr.ax25_call[6] = *s++ - '0'; + + if (*s != '\0') { + addr.ax25_call[6] *= 10; + addr.ax25_call[6] += *s++ - '0'; + } + + addr.ax25_call[6] <<= 1; + addr.ax25_call[6] &= 0x1E; + + return &addr; +} + +/* * Compare two ax.25 addresses */ int ax25cmp(ax25_address *a, ax25_address *b) @@ -154,6 +222,22 @@ int ax25cmp(ax25_address *a, ax25_address *b) } /* + * Free an allocated ax25 control block. This is done to centralise + * the MOD count code. + */ +static void ax25_free_cb(ax25_cb *ax25) +{ + if (ax25->digipeat != NULL) { + kfree_s(ax25->digipeat, sizeof(ax25_digi)); + ax25->digipeat = NULL; + } + + kfree_s(ax25, sizeof(ax25_cb)); + + MOD_DEC_USE_COUNT; +} + +/* * Socket removal during an interrupt is now safe. */ static void ax25_remove_socket(ax25_cb *ax25) @@ -192,30 +276,44 @@ static void ax25_kill_by_device(struct device *dev) for (s = ax25_list; s != NULL; s = s->next) { if (s->device == dev) { + s->state = AX25_STATE_0; s->device = NULL; if (s->sk != NULL) { - s->sk->state = TCP_CLOSE; - s->sk->err = ENETUNREACH; + s->sk->state = TCP_CLOSE; + s->sk->err = ENETUNREACH; + s->sk->shutdown |= SEND_SHUTDOWN; if (!s->sk->dead) s->sk->state_change(s->sk); s->sk->dead = 1; } } } - - ax25_rt_device_down(dev); } /* * Handle device status changes. */ -static int ax25_device_event(unsigned long event, void *ptr) +static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) { - if (event != NETDEV_DOWN) + struct device *dev = (struct device *)ptr; + + /* Reject non AX.25 devices */ + if (dev->type != ARPHRD_AX25) return NOTIFY_DONE; - - ax25_kill_by_device(ptr); - + + 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; } @@ -236,7 +334,7 @@ static void ax25_insert_socket(ax25_cb *ax25) } /* - * Find a socket that wants to accept the SABM we just + * Find a socket that wants to accept the SABM we have just * received. */ static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, int type) @@ -311,7 +409,7 @@ static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, str } /* - * Look for any matching address - RAW sockets can bind to arbitary names + * Look for any matching address - RAW sockets can bind to arbitrary names */ static struct sock *ax25_addr_match(ax25_address *addr) { @@ -343,10 +441,10 @@ static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) return; copy->sk = sk; - sk->rmem_alloc += copy->mem_len; + atomic_add(copy->truesize, &sk->rmem_alloc); skb_queue_tail(&sk->receive_queue, copy); if (!sk->dead) - sk->data_ready(sk, skb->len - 2); + sk->data_ready(sk, skb->len); } sk = sk->next; @@ -356,7 +454,7 @@ static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) /* * Deferred destroy. */ -void ax25_destory_socket(ax25_cb *); +void ax25_destroy_socket(ax25_cb *); /* * Handler for deferred kills. @@ -372,7 +470,7 @@ static void ax25_destroy_timer(unsigned long data) * 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) /* Not static as its used by the timer */ +void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */ { struct sk_buff *skb; unsigned long flags; @@ -383,38 +481,33 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as its used by the timer * del_timer(&ax25->timer); ax25_remove_socket(ax25); - ax25_clear_tx_queue(ax25); /* Flush the send queue */ + ax25_clear_queues(ax25); /* Flush the queues */ if (ax25->sk != NULL) { while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { if (skb->sk != ax25->sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_set_timer(skb->sk->ax25); - skb->sk->ax25->state = AX25_STATE_0; + ax25_set_timer(skb->sk->protinfo.ax25); + skb->sk->protinfo.ax25->state = AX25_STATE_0; } kfree_skb(skb, FREE_READ); } } - if (ax25->digipeat != NULL) { - kfree_s(ax25->digipeat, sizeof(ax25_digi)); - ax25->digipeat = NULL; - } - if (ax25->sk != NULL) { if (ax25->sk->wmem_alloc || ax25->sk->rmem_alloc) { /* Defer: outstanding buffers */ init_timer(&ax25->timer); - ax25->timer.expires = 10 * HZ; + ax25->timer.expires = jiffies + 10 * HZ; ax25->timer.function = ax25_destroy_timer; ax25->timer.data = (unsigned long)ax25; add_timer(&ax25->timer); } else { - kfree_s(ax25->sk, sizeof(*ax25->sk)); - kfree_s(ax25, sizeof(*ax25)); + sk_free(ax25->sk); + ax25_free_cb(ax25); } } else { - kfree_s(ax25, sizeof(*ax25)); + ax25_free_cb(ax25); } restore_flags(flags); @@ -451,22 +544,27 @@ static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) return a->uid; } return -ENOENT; + case SIOCAX25ADDUID: - if(!suser()) + if (!suser()) return -EPERM; if (ax25_findbyuid(sax->sax25_uid)) return -EEXIST; + if (sax->sax25_uid == 0) + return -EINVAL; a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL); + if (a == NULL) + return -ENOMEM; a->uid = sax->sax25_uid; a->call = sax->sax25_call; a->next = ax25_uid_list; ax25_uid_list = a; return 0; - case SIOCAX25DELUID: - { + + case SIOCAX25DELUID: { ax25_uid_assoc **l; - if(!suser()) + if (!suser()) return -EPERM; l = &ax25_uid_list; while ((*l) != NULL) { @@ -481,10 +579,138 @@ static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) } return -ENOENT; } + + default: + return -EINVAL; } return -EINVAL; /*NOTREACHED */ -} +} + +/* + * 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 *arg) +{ + struct ax25_ctl_struct ax25_ctl; + struct device *dev; + ax25_cb *ax25; + unsigned long flags; + int err; + + if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0) + return err; + + copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)); + + if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL) + return -ENODEV; + + if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, dev)) == NULL) + return -ENOTCONN; + + switch (ax25_ctl.cmd) { + case AX25_KILL: + ax25_clear_queues(ax25); + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); + + ax25->state = AX25_STATE_0; + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ENETRESET; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } + + ax25_dama_off(ax25); + ax25_set_timer(ax25); + break; + + case AX25_WINDOW: + if (ax25->modulus == MODULUS) { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) + return -EINVAL; + } else { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) + return -EINVAL; + } + ax25->window = ax25_ctl.arg; + break; + + case AX25_T1: + if (ax25_ctl.arg < 1) + return -EINVAL; + ax25->rtt = (ax25_ctl.arg * PR_SLOWHZ) / 2; + ax25->t1 = ax25_ctl.arg * PR_SLOWHZ; + save_flags(flags); cli(); + if (ax25->t1timer > ax25->t1) + ax25->t1timer = ax25->t1; + restore_flags(flags); + break; + + case AX25_T2: + if (ax25_ctl.arg < 1) + return -EINVAL; + save_flags(flags); cli(); + ax25->t2 = ax25_ctl.arg * PR_SLOWHZ; + if (ax25->t2timer > ax25->t2) + ax25->t2timer = ax25->t2; + restore_flags(flags); + break; + + case AX25_N2: + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) + return -EINVAL; + ax25->n2count = 0; + ax25->n2 = ax25_ctl.arg; + break; + + case AX25_T3: + if (ax25_ctl.arg < 0) + return -EINVAL; + save_flags(flags); cli(); + ax25->t3 = ax25_ctl.arg * PR_SLOWHZ; + if (ax25->t3timer != 0) + ax25->t3timer = ax25->t3; + restore_flags(flags); + break; + + case AX25_IDLE: + if (ax25_ctl.arg < 0) + return -EINVAL; + save_flags(flags); cli(); + ax25->idle = ax25_ctl.arg * PR_SLOWHZ * 60; + if (ax25->idletimer != 0) + ax25->idletimer = ax25->idle; + restore_flags(flags); + break; + + case AX25_PACLEN: + if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) + return -EINVAL; +#if 0 + if (ax25_ctl.arg > 256) /* we probably want this */ + printk(KERN_WARNING "ax25_ctl_ioctl: Warning --- huge paclen %d\n", (int)ax25_ctl.arg); +#endif + ax25->paclen = ax25_ctl.arg; + break; + + case AX25_MAXQUEUE: + if (ax25_ctl.arg < 1) + return -EINVAL; + ax25->maxqueue = ax25_ctl.arg; + break; + + default: + return -EINVAL; + } + + return 0; +} /* * Create an empty AX.25 control block. @@ -496,49 +722,120 @@ static ax25_cb *ax25_create_cb(void) if ((ax25 = (ax25_cb *)kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) return NULL; + MOD_INC_USE_COUNT; + 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); init_timer(&ax25->timer); - ax25->rtt = DEFAULT_T1; - ax25->t1 = DEFAULT_T1; - ax25->t2 = DEFAULT_T2; - ax25->n2 = DEFAULT_N2; - ax25->t3 = DEFAULT_T3; + ax25->dama_slave = 0; + + ax25->rtt = AX25_DEF_T1 / 2; + ax25->t1 = AX25_DEF_T1; + ax25->t2 = AX25_DEF_T2; + ax25->t3 = AX25_DEF_T3; + ax25->n2 = AX25_DEF_N2; + ax25->paclen = AX25_DEF_PACLEN; + ax25->maxqueue= AX25_DEF_MAXQUEUE; + ax25->idle = AX25_DEF_IDLE; + + if (AX25_DEF_AXDEFMODE) { + ax25->modulus = EMODULUS; + ax25->window = AX25_DEF_EWINDOW; + } else { + ax25->modulus = MODULUS; + ax25->window = AX25_DEF_WINDOW; + } + ax25->fragno = 0; + ax25->fraglen = 0; + ax25->hdrincl = 0; + ax25->backoff = AX25_DEF_BACKOFF; ax25->condition = 0x00; ax25->t1timer = 0; ax25->t2timer = 0; ax25->t3timer = 0; ax25->n2count = 0; + ax25->idletimer = 0; ax25->va = 0; ax25->vr = 0; ax25->vs = 0; - ax25->window = DEFAULT_WINDOW; ax25->device = NULL; ax25->digipeat = NULL; ax25->sk = NULL; ax25->state = AX25_STATE_0; - memset(&ax25->dest_addr, '\0', sizeof(ax25_address)); - memset(&ax25->source_addr, '\0', sizeof(ax25_address)); + memset(&ax25->dest_addr, '\0', AX25_ADDR_LEN); + memset(&ax25->source_addr, '\0', AX25_ADDR_LEN); return ax25; } -int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, struct device *dev) +/* + * Find out if we are a DAMA slave for this device and count the + * number of connections. + * + * dl1bke 951121 + */ +int ax25_dev_is_dama_slave(struct device *dev) +{ + ax25_cb *ax25; + int count = 0; + + for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { + if (ax25->device == dev && ax25->dama_slave) { + count++; + break; + } + } + + return count; +} + +/* + * Fill in a created AX.25 created control block with the default + * values for a particular device. + */ +static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev) +{ + ax25->device = dev; + + ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1); + ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1); + ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2); + ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3); + ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2); + ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); + ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE); + ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE); + + ax25->dama_slave = 0; + + if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) { + ax25->modulus = EMODULUS; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } + + ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF); +} + +int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, + ax25_digi *digi, struct device *dev) { ax25_cb *ax25; if (skb == NULL) return 0; - skb->h.raw = skb->data + 15; - /* * Look for an existing connection. */ @@ -547,7 +844,12 @@ int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, continue; if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) { - ax25_output(ax25, skb); + if (ax25_queue_length(ax25, skb) > ax25->maxqueue * ax25->window) { + kfree_skb(skb, FREE_WRITE); + } else { + ax25_output(ax25, skb); + } + ax25->idletimer = ax25->idle; return 1; /* It already existed */ } } @@ -555,12 +857,30 @@ int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, if ((ax25 = ax25_create_cb()) == NULL) return 0; - ax25->device = dev; + ax25_fillin_cb(ax25, dev); - memcpy(&ax25->source_addr, src, sizeof(ax25_address)); - memcpy(&ax25->dest_addr, dest, sizeof(ax25_address)); + ax25->source_addr = *src; + ax25->dest_addr = *dest; - ax25_establish_data_link(ax25); + if (digi != NULL) { + if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { + ax25_free_cb(ax25); + return 0; + } + *ax25->digipeat = *digi; + } else { + ax25_rt_build_path(ax25, dest, dev); + } + + if (ax25_dev_is_dama_slave(ax25->device)) + dama_establish_data_link(ax25); + else + ax25_establish_data_link(ax25); + + /* idle timeouts only for mode vc connections */ + + ax25->idletimer = ax25->idle; + ax25_insert_socket(ax25); ax25->state = AX25_STATE_1; @@ -572,39 +892,47 @@ int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest, return 1; /* We had to create it */ } -/*******************************************************************************************************************\ -* * -* Routing rules for AX.25: Basically iterate over the active interfaces * -* * -\*******************************************************************************************************************/ +/* + * Return the state of an AX.25 link given source, destination, and + * device. + */ +int ax25_link_up(ax25_address *src, ax25_address *dest, struct device *dev) +{ + ax25_cb *ax25; + + for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { + if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET) + continue; + + if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) + return 1; + } + + return 0; +} +/* + * Find the AX.25 device that matches the hardware address supplied. + */ struct device *ax25rtr_get_dev(ax25_address *addr) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) { /* Active kiss ax25 mode */ - if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) - return dev; - } - } + for (dev = dev_base; dev != NULL; dev = dev->next) + if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25 && + ax25cmp(addr, (ax25_address*) dev->dev_addr) == 0) + return dev; return NULL; } -/*******************************************************************************************************************\ -* * -* Handling for system calls applied via the various interfaces to an AX25 socket object * -* * -\*******************************************************************************************************************/ - +/* + * Handling for system calls applied via the various interfaces to an + * AX25 socket object + */ static int ax25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) { - switch(cmd) - { - default: - return(-EINVAL); - } + return -EINVAL; } static int ax25_setsockopt(struct socket *sock, int level, int optname, @@ -627,39 +955,68 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0) return err; - opt = get_fs_long((unsigned long *)optval); - + get_user(opt, (int *)optval); + switch (optname) { case AX25_WINDOW: - if (opt < 1 || opt > 7) - return -EINVAL; - sk->ax25->window = opt; + if (sk->protinfo.ax25->modulus == MODULUS) { + 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->ax25->t1 = opt * PR_SLOWHZ; + sk->protinfo.ax25->rtt = (opt * PR_SLOWHZ) / 2; return 0; case AX25_T2: if (opt < 1) return -EINVAL; - sk->ax25->t2 = opt * PR_SLOWHZ; + sk->protinfo.ax25->t2 = opt * PR_SLOWHZ; return 0; - + case AX25_N2: if (opt < 1 || opt > 31) return -EINVAL; - sk->ax25->n2 = opt; + sk->protinfo.ax25->n2 = opt; return 0; - + case AX25_T3: if (opt < 1) return -EINVAL; - sk->ax25->t3 = opt * PR_SLOWHZ; + sk->protinfo.ax25->t3 = opt * PR_SLOWHZ; return 0; - + + case AX25_IDLE: + if (opt < 0) + return -EINVAL; + sk->protinfo.ax25->idle = opt * PR_SLOWHZ * 60; + return 0; + + case AX25_BACKOFF: + sk->protinfo.ax25->backoff = opt ? 1 : 0; + return 0; + + case AX25_EXTSEQ: + sk->protinfo.ax25->modulus = opt ? EMODULUS : MODULUS; + return 0; + + case AX25_HDRINCL: + sk->protinfo.ax25->hdrincl = opt ? 1 : 0; + return 0; + + case AX25_PACLEN: + if (opt < 16 || opt > 65535) + return -EINVAL; + sk->protinfo.ax25->paclen = opt; + return 0; + default: return -ENOPROTOOPT; } @@ -679,28 +1036,48 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_AX25) return -EOPNOTSUPP; - + switch (optname) { case AX25_WINDOW: - val = sk->ax25->window; + val = sk->protinfo.ax25->window; break; case AX25_T1: - val = sk->ax25->t1 / PR_SLOWHZ; + val = (sk->protinfo.ax25->t1 * 2) / PR_SLOWHZ; break; - + case AX25_T2: - val = sk->ax25->t2 / PR_SLOWHZ; + val = sk->protinfo.ax25->t2 / PR_SLOWHZ; break; - + case AX25_N2: - val = sk->ax25->n2; + val = sk->protinfo.ax25->n2; break; - + case AX25_T3: - val = sk->ax25->t3 / PR_SLOWHZ; + val = sk->protinfo.ax25->t3 / PR_SLOWHZ; break; - + + case AX25_IDLE: + val = sk->protinfo.ax25->idle / (PR_SLOWHZ * 60); + break; + + case AX25_BACKOFF: + val = sk->protinfo.ax25->backoff; + break; + + case AX25_EXTSEQ: + val = (sk->protinfo.ax25->modulus == EMODULUS); + break; + + case AX25_HDRINCL: + val = sk->protinfo.ax25->hdrincl; + break; + + case AX25_PACLEN: + val = sk->protinfo.ax25->paclen; + break; + default: return -ENOPROTOOPT; } @@ -708,12 +1085,12 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0) return err; - put_fs_long(sizeof(int), (unsigned long *)optlen); + put_user(sizeof(int), optlen); if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0) return err; - put_fs_long(val, (unsigned long *)optval); + put_user(val, (int *) optval); return 0; } @@ -748,53 +1125,60 @@ static int ax25_create(struct socket *sock, int protocol) struct sock *sk; ax25_cb *ax25; - if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - if ((ax25 = ax25_create_cb()) == NULL) { - kfree_s(sk, sizeof(*sk)); - return -ENOMEM; - } - - sk->type = sock->type; - switch (sock->type) { case SOCK_DGRAM: - case SOCK_SEQPACKET: - if (protocol == 0) + if (protocol == 0 || protocol == AF_AX25) protocol = AX25_P_TEXT; break; + case SOCK_SEQPACKET: + switch (protocol) { + case 0: + case AF_AX25: /* For CLX */ + protocol = AX25_P_TEXT; + break; + case AX25_P_SEGMENT: +#ifdef CONFIG_INET + case AX25_P_ARP: + case AX25_P_IP: +#endif +#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) + case AX25_P_NETROM: +#endif +#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) + case AX25_P_ROSE: +#endif + return -ESOCKTNOSUPPORT; + default: + break; + } + break; case SOCK_RAW: break; default: - kfree_s((void *)sk, sizeof(*sk)); - kfree_s((void *)ax25, sizeof(*ax25)); return -ESOCKTNOSUPPORT; } + if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + return -ENOMEM; + + if ((ax25 = ax25_create_cb()) == NULL) { + sk_free(sk); + return -ENOMEM; + } + skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->back_log); sk->socket = sock; + sk->type = sock->type; sk->protocol = protocol; - sk->dead = 0; sk->next = NULL; - sk->broadcast = 0; + sk->allocation = GFP_KERNEL; sk->rcvbuf = SK_RMEM_MAX; sk->sndbuf = SK_WMEM_MAX; - sk->wmem_alloc = 0; - sk->rmem_alloc = 0; - sk->inuse = 0; - sk->debug = 0; - sk->prot = NULL; /* So we use default free mechanisms */ - sk->err = 0; - sk->localroute = 0; - sk->send_head = NULL; sk->state = TCP_CLOSE; - sk->shutdown = 0; sk->priority = SOPRI_NORMAL; - sk->ack_backlog = 0; sk->mtu = AX25_MTU; /* 256 */ sk->zapped = 1; @@ -808,8 +1192,8 @@ static int ax25_create(struct socket *sock, int protocol) sk->sleep = sock->wait; } - ax25->sk = sk; - sk->ax25 = ax25; + ax25->sk = sk; + sk->protinfo.ax25 = ax25; return 0; } @@ -819,26 +1203,27 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev) struct sock *sk; ax25_cb *ax25; - if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) return NULL; if ((ax25 = ax25_create_cb()) == NULL) { - kfree_s(sk, sizeof(*sk)); + sk_free(sk); return NULL; } + ax25_fillin_cb(ax25, dev); + sk->type = osk->type; sk->socket = osk->socket; - switch(osk->type) - { + switch (osk->type) { case SOCK_DGRAM: break; case SOCK_SEQPACKET: break; default: - kfree_s((void *)sk, sizeof(*sk)); - kfree_s((void *)ax25, sizeof(*ax25)); + sk_free(sk); + ax25_free_cb(ax25); return NULL; } @@ -846,25 +1231,14 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev) skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->back_log); - sk->dead = 0; sk->next = NULL; sk->priority = osk->priority; - sk->broadcast = 0; sk->protocol = osk->protocol; sk->rcvbuf = osk->rcvbuf; sk->sndbuf = osk->sndbuf; - sk->wmem_alloc = 0; - sk->rmem_alloc = 0; - sk->inuse = 0; - sk->ack_backlog = 0; - sk->prot = NULL; /* So we use default free mechanisms */ - sk->err = 0; - sk->localroute = 0; - sk->send_head = NULL; sk->debug = osk->debug; sk->state = TCP_ESTABLISHED; sk->window = osk->window; - sk->shutdown = 0; sk->mtu = osk->mtu; sk->sleep = osk->sleep; sk->zapped = osk->zapped; @@ -874,27 +1248,34 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev) sk->write_space = def_callback1; sk->error_report = def_callback1; - ax25->rtt = osk->ax25->rtt; - ax25->t1 = osk->ax25->t1; - ax25->t2 = osk->ax25->t2; - ax25->t3 = osk->ax25->t3; - ax25->n2 = osk->ax25->n2; - - ax25->window = osk->ax25->window; - ax25->device = dev; - - memcpy(&ax25->source_addr, &osk->ax25->source_addr, sizeof(ax25_address)); + ax25->modulus = osk->protinfo.ax25->modulus; + ax25->backoff = osk->protinfo.ax25->backoff; + ax25->hdrincl = osk->protinfo.ax25->hdrincl; + ax25->rtt = osk->protinfo.ax25->rtt; + ax25->t1 = osk->protinfo.ax25->t1; + ax25->t2 = osk->protinfo.ax25->t2; + ax25->t3 = osk->protinfo.ax25->t3; + ax25->n2 = osk->protinfo.ax25->n2; + ax25->idle = osk->protinfo.ax25->idle; + ax25->paclen = osk->protinfo.ax25->paclen; + + ax25->window = osk->protinfo.ax25->window; + ax25->maxqueue = osk->protinfo.ax25->maxqueue; + + ax25->source_addr = osk->protinfo.ax25->source_addr; - if (osk->ax25->digipeat != NULL) { + if (osk->protinfo.ax25->digipeat != NULL) { if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree_s(sk, sizeof(*sk)); - kfree_s(ax25, sizeof(*ax25)); + sk_free(sk); + ax25_free_cb(ax25); return NULL; } + + *ax25->digipeat = *osk->protinfo.ax25->digipeat; } - sk->ax25 = ax25; - ax25->sk = sk; + sk->protinfo.ax25 = ax25; + ax25->sk = sk; return sk; } @@ -913,51 +1294,70 @@ static int ax25_release(struct socket *sock, struct socket *peer) if (sk == NULL) return 0; if (sk->type == SOCK_SEQPACKET) { - switch (sk->ax25->state) { + switch (sk->protinfo.ax25->state) { case AX25_STATE_0: - sk->dead = 1; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - ax25_destroy_socket(sk->ax25); + sk->dead = 1; + ax25_destroy_socket(sk->protinfo.ax25); break; case AX25_STATE_1: - ax25_send_control(sk->ax25, DISC | PF, C_RESPONSE); - sk->ax25->state = AX25_STATE_0; - sk->dead = 1; + ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND); + sk->protinfo.ax25->state = AX25_STATE_0; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - ax25_destroy_socket(sk->ax25); + sk->dead = 1; + ax25_destroy_socket(sk->protinfo.ax25); break; case AX25_STATE_2: - ax25_send_control(sk->ax25, DM | PF, C_RESPONSE); - sk->ax25->state = AX25_STATE_0; - sk->dead = 1; + if (sk->protinfo.ax25->dama_slave) + ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND); + else + ax25_send_control(sk->protinfo.ax25, DM, POLLON, C_RESPONSE); + sk->protinfo.ax25->state = AX25_STATE_0; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - ax25_destroy_socket(sk->ax25); + sk->dead = 1; + ax25_destroy_socket(sk->protinfo.ax25); break; case AX25_STATE_3: case AX25_STATE_4: - ax25_clear_tx_queue(sk->ax25); - sk->ax25->n2count = 0; - ax25_send_control(sk->ax25, DISC | PF, C_COMMAND); - sk->ax25->t3timer = 0; - sk->ax25->t1timer = sk->ax25->t1 = ax25_calculate_t1(sk->ax25); - sk->ax25->state = AX25_STATE_2; + ax25_clear_queues(sk->protinfo.ax25); + sk->protinfo.ax25->n2count = 0; + if (!sk->protinfo.ax25->dama_slave) { + ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND); + sk->protinfo.ax25->t3timer = 0; + } else { + sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3; /* DAMA slave timeout */ + } + sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25); + sk->protinfo.ax25->state = AX25_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; + sk->dead = 1; + sk->destroy = 1; break; default: break; } } else { - sk->dead = 1; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - ax25_destroy_socket(sk->ax25); + sk->dead = 1; + ax25_destroy_socket(sk->protinfo.ax25); } sock->data = NULL; + sk->socket = NULL; /* Not used, but we should do this. **/ return 0; } @@ -968,7 +1368,7 @@ static int ax25_release(struct socket *sock, struct socket *peer) * BSD 4.4 ADDIFADDR type support. It is however small and trivially backward * compatible 8) */ -static int ax25_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) +static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; @@ -978,44 +1378,49 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) sk = (struct sock *)sock->data; if (sk->zapped == 0) - return -EIO; + return -EINVAL; if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; -#ifdef DONTDO - if (ax25_find_socket(&addr->fsa_ax25.sax25_call, sk->type) != NULL) { - if (sk->debug) - printk("AX25: bind failed: in use\n"); - return -EADDRINUSE; - } -#endif - call = ax25_findbyuid(current->euid); if (call == NULL && ax25_uid_policy && !suser()) - return -EPERM; + return -EACCES; if (call == NULL) - memcpy(&sk->ax25->source_addr, &addr->fsa_ax25.sax25_call, sizeof(ax25_address)); + sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call; else - memcpy(&sk->ax25->source_addr, call, sizeof(ax25_address)); + sk->protinfo.ax25->source_addr = *call; + + if (sk->debug) + printk("AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr)); if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { - if (!suser()) - return -EPERM; - call = &addr->fsa_digipeater[0]; + if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) { + dev = NULL; + if (sk->debug) + printk("AX25: bound to any device\n"); + } else { + if ((dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) { + if (sk->debug) + printk("AX25: bind failed - no device\n"); + return -EADDRNOTAVAIL; + } + if (sk->debug) + printk("AX25: bound to device %s\n", dev->name); + } } else { - call = &addr->fsa_ax25.sax25_call; - } - - if ((dev = ax25rtr_get_dev(call)) == NULL) { + if ((dev = ax25rtr_get_dev(&addr->fsa_ax25.sax25_call)) == NULL) { + if (sk->debug) + printk("AX25: bind failed - no device\n"); + return -EADDRNOTAVAIL; + } if (sk->debug) - printk("AX25 bind failed: no device\n"); - return -EADDRNOTAVAIL; + printk("AX25: bound to device %s\n", dev->name); } - sk->ax25->device = dev; - ax25_insert_socket(sk->ax25); + ax25_fillin_cb(sk->protinfo.ax25, dev); + ax25_insert_socket(sk->protinfo.ax25); sk->zapped = 0; @@ -1048,51 +1453,55 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, sk->state = TCP_CLOSE; sock->state = SS_UNCONNECTED; - if (addr_len > sizeof(*addr)) { - int ct = 0; - int ndigi = addr_len - sizeof(*addr); - ax25_address *ap = (ax25_address *)(((char *)addr) + sizeof(*addr)); - - /* Size is an exact number of digipeaters ? */ - if (ndigi % sizeof(ax25_address)) - return -EINVAL; + if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) + return -EINVAL; - ndigi /= sizeof(ax25_address); + /* + * Handle digi-peaters to be used. + */ + if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) { + int ct = 0; + struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr; /* Valid number of digipeaters ? */ - if (ndigi < 1 || ndigi > 6) + if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - if (sk->ax25->digipeat == NULL) { - if ((sk->ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOMEM; + if (sk->protinfo.ax25->digipeat == NULL) { + if ((sk->protinfo.ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) + return -ENOBUFS; } - sk->ax25->digipeat->ndigi = ndigi; + sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis; - while (ct < ndigi) { - sk->ax25->digipeat->repeated[ct] = 0; - memcpy(&sk->ax25->digipeat->calls[ct], &ap[ct], sizeof(ax25_address)); + while (ct < addr->sax25_ndigis) { + sk->protinfo.ax25->digipeat->repeated[ct] = 0; + sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct]; ct++; } - sk->ax25->digipeat->lastrepeat = 0; - addr_len -= ndigi * sizeof(ax25_address); + sk->protinfo.ax25->digipeat->lastrepeat = 0; } - if (addr_len != sizeof(struct sockaddr_ax25)) - return -EINVAL; - - if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ - if ((err = ax25_rt_autobind(sk->ax25, &addr->sax25_call)) < 0) + /* + * Must bind first - autobinding in this may or may not work. If + * the socket is already bound, check to see if the device has + * been filled in, error if it hasn't. + */ + if (sk->zapped) { + if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0) return err; - ax25_insert_socket(sk->ax25); /* Finish the bind */ + ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->device); + ax25_insert_socket(sk->protinfo.ax25); + } else { + if (sk->protinfo.ax25->device == NULL) + return -EHOSTUNREACH; } - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->ax25->source_addr, &addr->sax25_call, sk->ax25->device) != NULL) - return -EBUSY; /* Already such a connection */ - - memcpy(&sk->ax25->dest_addr, &addr->sax25_call, sizeof(ax25_address)); + if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, sk->protinfo.ax25->device) != NULL) + return -EADDRINUSE; /* Already such a connection */ + + sk->protinfo.ax25->dest_addr = addr->sax25_call; /* First the easy one */ if (sk->type != SOCK_SEQPACKET) { @@ -1104,9 +1513,14 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; - ax25_establish_data_link(sk->ax25); - sk->ax25->state = AX25_STATE_1; - ax25_set_timer(sk->ax25); /* Start going SABM SABM until a UA or a give up and DM */ + + if (ax25_dev_is_dama_slave(sk->protinfo.ax25->device)) + dama_establish_data_link(sk->protinfo.ax25); + else + ax25_establish_data_link(sk->protinfo.ax25); + + sk->protinfo.ax25->state = AX25_STATE_1; + ax25_set_timer(sk->protinfo.ax25); /* Start going SABM SABM until a UA or a give up and DM */ /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -1123,10 +1537,11 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, } } - if (sk->state != TCP_ESTABLISHED) { /* Not in ABM, not in WAIT_UA -> failed */ + if (sk->state != TCP_ESTABLISHED) { + /* Not in ABM, not in WAIT_UA -> failed */ sti(); sock->state = SS_UNCONNECTED; - return -sk->err; /* Always set at this point */ + return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; @@ -1148,7 +1563,7 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) struct sk_buff *skb; if (newsock->data) - kfree_s(newsock->data, sizeof(struct sock)); + sk_free(newsock->data); newsock->data = NULL; @@ -1160,8 +1575,10 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) if (sk->state != TCP_LISTEN) return -EINVAL; - /* The write queue this time is holding sockets ready to use - hooked into the SABM we saved */ + /* + * The write queue this time is holding sockets ready to use + * hooked into the SABM we saved + */ do { cli(); if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { @@ -1193,7 +1610,6 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { - ax25_address *addr; struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; struct sock *sk; unsigned char ndigi, i; @@ -1203,31 +1619,35 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, if (peer != 0) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - addr = &sk->ax25->dest_addr; + + sax->fsa_ax25.sax25_family = AF_AX25; + sax->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr; + sax->fsa_ax25.sax25_ndigis = 0; + *uaddr_len = sizeof(struct full_sockaddr_ax25); + + if (sk->protinfo.ax25->digipeat != NULL) { + ndigi = sk->protinfo.ax25->digipeat->ndigi; + sax->fsa_ax25.sax25_ndigis = ndigi; + for (i = 0; i < ndigi; i++) + sax->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i]; + } } else { - addr = &sk->ax25->source_addr; - } - - sax->fsa_ax25.sax25_family = AF_AX25; - memcpy(&sax->fsa_ax25.sax25_call, addr, sizeof(ax25_address)); - sax->fsa_ax25.sax25_ndigis = 0; - *uaddr_len = sizeof(struct sockaddr_ax25); + sax->fsa_ax25.sax25_family = AF_AX25; + sax->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr; + sax->fsa_ax25.sax25_ndigis = 1; + *uaddr_len = sizeof(struct full_sockaddr_ax25); - /* This will supply digipeat path on both getpeername() and getsockname() */ - if (sk->ax25->digipeat != NULL) { - ndigi = sk->ax25->digipeat->ndigi; - sax->fsa_ax25.sax25_ndigis = ndigi; - *uaddr_len += sizeof(ax25_address) * ndigi; - for (i = 0; i < ndigi; i++) - memcpy(&sax->fsa_digipeater[i], &sk->ax25->digipeat->calls[i], sizeof(ax25_address)); + if (sk->protinfo.ax25->device != NULL) + memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN); + else + sax->fsa_digipeater[0] = null_ax25_address; } - + return 0; } -int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) +static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype) { - unsigned char *data = skb->data; struct sock *make; struct sock *sk; int type = 0; @@ -1236,52 +1656,76 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) ax25_address src, dest; struct sock *raw; int mine = 0; + int dama; - skb->sk = NULL; /* Initially we don't know who its for */ + /* + * Process the AX.25/LAPB frame. + */ + + skb->h.raw = skb->data; - if ((*data & 0x0F) != 0) { - kfree_skb(skb, FREE_READ); /* Not a KISS data frame */ +#ifdef CONFIG_FIREWALL + if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) { + kfree_skb(skb, FREE_READ); return 0; } +#endif - data++; - /* * Parse the address header. */ - if ((data = ax25_parse_addr(data, skb->len + dev->hard_header_len - 1, &src, &dest, &dp, &type)) == NULL) { + + if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) { kfree_skb(skb, FREE_READ); return 0; } - - /* - * Send the frame to the AX.25 auto-router - */ - ax25_rt_rx_frame(&src, dev); - + /* * Ours perhaps ? */ if (dp.lastrepeat + 1 < dp.ndigi) { /* Not yet digipeated completely */ - if (ax25cmp(&dp.calls[dp.lastrepeat + 1], (ax25_address *)dev->dev_addr) == 0) { + if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) { + struct device *dev_out = dev; + + skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ); + if(skb==NULL) + return 0; + /* We are the digipeater. Mark ourselves as repeated and throw the packet back out of the same device */ dp.lastrepeat++; dp.repeated[(int)dp.lastrepeat] = 1; -#ifdef CONFIG_AX25_XDIGI - while (dp.lastrepeat + 1 < dp.ndigi) { - struct device *dev_scan; - if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL) - break; - dp.lastrepeat++; - dp.repeated[(int)dp.lastrepeat] = 1; - dev = dev_scan; + + if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) { + while (dp.lastrepeat + 1 < dp.ndigi) { + struct device *dev_scan; + if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL) + break; + dp.lastrepeat++; + dp.repeated[(int)dp.lastrepeat] = 1; + dev_out = dev_scan; + } + if (dev != dev_out && (ax25_dev_get_value(dev_out, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) == 0) { + kfree_skb(skb, FREE_READ); + return 0; + } + } + + if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0) { + kfree_skb(skb, FREE_READ); + return 0; + } + + build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS); +#ifdef CONFIG_FIREWALL + if (call_fw_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) { + kfree_skb(skb, FREE_READ); + return 0; } #endif - build_ax25_addr(skb->data + 1, &src, &dest, &dp, type); - skb->len += dev->hard_header_len; + skb->arp = 1; - dev_queue_xmit(skb, dev, SOPRI_NORMAL); + ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL); } else { kfree_skb(skb, FREE_READ); } @@ -1290,26 +1734,23 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) } /* - * Adjust the lengths for digipeated input + * Pull of the AX.25 headers leaving the CTRL/PID bytes */ - skb->len -= sizeof(ax25_address) * dp.ndigi; - - /* For our port addreses ? */ - if (ax25cmp(&dest, (ax25_address *)dev->dev_addr) == 0) + skb_pull(skb, size_ax25_addr(&dp)); + + /* For our port addresses ? */ + if (ax25cmp(&dest, dev_addr) == 0) mine = 1; -#ifdef CONFIG_NETROM - /* Also match on any NET/ROM callsign */ - if (!mine && nr_dev_get(&dest) != NULL) + /* Also match on any registered callsign from L3/4 */ + if (!mine && ax25_listen_mine(&dest, dev)) mine = 1; -#endif - if ((*data & ~0x10) == LAPB_UI) { /* UI frame - bypass LAPB processing */ - data++; - skb->h.raw = data + 1; /* skip pid */ + if ((*skb->data & ~0x10) == LAPB_UI) { /* UI frame - bypass LAPB processing */ + skb->h.raw = skb->data + 2; /* skip control and pid */ if ((raw = ax25_addr_match(&dest)) != NULL) - ax25_send_to_raw(raw, skb, (int)*data); + ax25_send_to_raw(raw, skb, skb->data[1]); if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { kfree_skb(skb, FREE_READ); @@ -1317,14 +1758,15 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) } /* Now we are pointing at the pid byte */ - switch (*data++) { + switch (skb->data[1]) { #ifdef CONFIG_INET case AX25_P_IP: - ax25_ip_mode_set(&src, dev, 'D'); + skb_pull(skb,2); /* drop PID/CTRL */ ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */ break; case AX25_P_ARP: + skb_pull(skb,2); arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */ break; #endif @@ -1334,11 +1776,15 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) if (sk->rmem_alloc >= sk->rcvbuf) { kfree_skb(skb, FREE_READ); } else { + /* + * Remove the control and PID. + */ + skb_pull(skb, 2); skb_queue_tail(&sk->receive_queue, skb); skb->sk = sk; - sk->rmem_alloc += skb->mem_len; + atomic_add(skb->truesize, &sk->rmem_alloc); if (!sk->dead) - sk->data_ready(sk, skb->len - 2); + sk->data_ready(sk, skb->len); } } else { kfree_skb(skb, FREE_READ); @@ -1352,31 +1798,51 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) return 0; } + + /* + * Is connected mode supported on this device ? + * If not, should we DM the incoming frame (except DMs) or + * silently ignore them. For now we stay quiet. + */ + if (!ax25_dev_get_value(dev, AX25_VALUES_CONMODE)) { + kfree_skb(skb, FREE_READ); + return 0; + } /* LAPB */ + + /* AX.25 state 1-4 */ + if ((ax25 = ax25_find_cb(&dest, &src, dev)) != NULL) { - skb->h.raw = data; - /* Process the frame. If it is queued up internally it returns one otherwise we - free it immediately. This routine itself wakes the user context layers so we - do no further work */ - if (ax25_process_rx_frame(ax25, skb, type) == 0) + /* + * Process the frame. If it is queued up internally it returns one otherwise we + * free it immediately. This routine itself wakes the user context layers so we + * do no further work + */ + if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) kfree_skb(skb, FREE_READ); return 0; } - if ((data[0] & 0xEF) != SABM) { + /* AX.25 state 0 (disconnected) */ + + /* a) received not a SABM(E) */ + + if ((*skb->data & ~PF) != SABM && (*skb->data & ~PF) != SABME) { /* * Never reply to a DM. Also ignore any connects for * addresses that are not our interfaces and not a socket. */ - if ((data[0] & 0xEF) != DM && mine) + if ((*skb->data & ~PF) != DM && mine) ax25_return_dm(dev, &src, &dest, &dp); kfree_skb(skb, FREE_READ); return 0; } + /* b) received SABM(E) */ + if ((sk = ax25_find_listener(&dest, dev, SOCK_SEQPACKET)) != NULL) { if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) { if (mine) @@ -1386,26 +1852,7 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) return 0; } - ax25 = make->ax25; - - /* - * Sort out any digipeated paths. - */ - if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree_skb(skb, FREE_READ); - ax25_destroy_socket(ax25); - return 0; - } - - if (dp.ndigi == 0) { - if (ax25->digipeat != NULL) { - kfree_s(ax25->digipeat, sizeof(ax25_digi)); - ax25->digipeat = NULL; - } - } else { - /* Reverse the source SABM's path */ - ax25_digi_invert(&dp, ax25->digipeat); - } + ax25 = make->protinfo.ax25; skb_queue_head(&sk->receive_queue, skb); @@ -1415,39 +1862,58 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) sk->ack_backlog++; } else { -#ifdef CONFIG_NETROM if (!mine) { kfree_skb(skb, FREE_READ); return 0; } - - if (dp.ndigi != 0) { - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb, FREE_READ); - return 0; - } - + if ((ax25 = ax25_create_cb()) == NULL) { ax25_return_dm(dev, &src, &dest, &dp); kfree_skb(skb, FREE_READ); return 0; } -#else - if (mine) - ax25_return_dm(dev, &src, &dest, &dp); + ax25_fillin_cb(ax25, dev); + ax25->idletimer = ax25->idle; + } + + ax25->source_addr = dest; + ax25->dest_addr = src; + + /* + * Sort out any digipeated paths. + */ + if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { kfree_skb(skb, FREE_READ); + ax25_destroy_socket(ax25); return 0; -#endif } - memcpy(&ax25->source_addr, &dest, sizeof(ax25_address)); - memcpy(&ax25->dest_addr, &src, sizeof(ax25_address)); + if (dp.ndigi == 0) { + if (ax25->digipeat != NULL) { + kfree_s(ax25->digipeat, sizeof(ax25_digi)); + ax25->digipeat = NULL; + } + } else { + /* Reverse the source SABM's path */ + ax25_digi_invert(&dp, ax25->digipeat); + } + + if ((*skb->data & ~PF) == SABME) { + ax25->modulus = EMODULUS; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } ax25->device = dev; - ax25_send_control(ax25, UA | PF, C_RESPONSE); + ax25_send_control(ax25, UA, POLLON, C_RESPONSE); + if (dama) ax25_dama_on(ax25); /* bke 951121 */ + + ax25->dama_slave = dama; ax25->t3timer = ax25->t3; ax25->state = AX25_STATE_3; @@ -1457,7 +1923,7 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) if (sk != NULL) { if (!sk->dead) - sk->data_ready(sk, skb->len - 2); + sk->data_ready(sk, skb->len); } else { kfree_skb(skb, FREE_READ); } @@ -1465,12 +1931,28 @@ int ax25_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) return 0; } -static int ax25_sendto(struct socket *sock, void *ubuf, int len, int noblock, - unsigned flags, struct sockaddr *usip, int addr_len) +/* + * Receive an AX.25 frame via a SLIP interface. + */ +static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) +{ + skb->sk = NULL; /* Initially we don't know who it's for */ + + if ((*skb->data & 0x0F) != 0) { + kfree_skb(skb, FREE_READ); /* Not a KISS data frame */ + return 0; + } + + skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ + + return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype); +} + + +static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags) { struct sock *sk = (struct sock *)sock->data; - struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)usip; - unsigned char *uaddr = (unsigned char *)usip; + struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; struct sockaddr_ax25 sax; struct sk_buff *skb; @@ -1479,68 +1961,67 @@ static int ax25_sendto(struct socket *sock, void *ubuf, int len, int noblock, ax25_digi *dp; ax25_digi dtmp; int lv; + int addr_len = msg->msg_namelen; - if (sk->err) { - err = sk->err; - sk->err = 0; - return -err; - } + if (sk->err) + return sock_error(sk); - if (flags) + if (flags || msg->msg_control) return -EINVAL; if (sk->zapped) return -EADDRNOTAVAIL; - if (sk->ax25->device == NULL) + if (sk->shutdown & SEND_SHUTDOWN) { + send_sig(SIGPIPE, current, 0); + return -EPIPE; + } + + if (sk->protinfo.ax25->device == NULL) return -ENETUNREACH; if (usax) { - int ndigi = addr_len - sizeof(sax); - if (addr_len < sizeof(sax)) + if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; - - /* Trailing digipeaters on address ?? */ - if (addr_len > sizeof(sax)) { - int ct = 0; - - ax25_address *ap = (ax25_address *)(((char *)uaddr) + sizeof(sax)); - /* Size is an exact number of digipeaters ? */ - if (ndigi % sizeof(ax25_address)) - return -EINVAL; - ndigi /= sizeof(ax25_address); + if (usax->sax25_family != AF_AX25) + return -EINVAL; + if (addr_len == sizeof(struct full_sockaddr_ax25) && usax->sax25_ndigis != 0) { + int ct = 0; + struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; /* Valid number of digipeaters ? */ - if (ndigi < 1 || ndigi > 6) + if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - /* Copy data into digipeat structure */ - while (ct < ndigi) { + dtmp.ndigi = usax->sax25_ndigis; + + while (ct < usax->sax25_ndigis) { dtmp.repeated[ct] = 0; - memcpy(&dtmp.calls[ct], &ap[ct], sizeof(ax25_address)); + dtmp.calls[ct] = fsa->fsa_digipeater[ct]; ct++; } dtmp.lastrepeat = 0; - dtmp.ndigi = ndigi; - addr_len -= ndigi * sizeof(ax25_address); } - memcpy(&sax, usax, sizeof(sax)); - if (sk->type == SOCK_SEQPACKET && memcmp(&sk->ax25->dest_addr, &sax.sax25_call, sizeof(ax25_address)) != 0) + sax = *usax; + if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0) return -EISCONN; - if (sax.sax25_family != AF_AX25) - return -EINVAL; - if (ndigi != 0) - dp = &dtmp; - else + 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->state != TCP_ESTABLISHED) return -ENOTCONN; sax.sax25_family = AF_AX25; - memcpy(&sax.sax25_call, &sk->ax25->dest_addr, sizeof(ax25_address)); - dp = sk->ax25->digipeat; + sax.sax25_call = sk->protinfo.ax25->dest_addr; + dp = sk->protinfo.ax25->digipeat; } if (sk->debug) @@ -1550,88 +2031,87 @@ static int ax25_sendto(struct socket *sock, void *ubuf, int len, int noblock, if (sk->debug) printk("AX.25: sendto: building packet.\n"); - size = 2 + len + 1 + size_ax25_addr(dp); - /* 2 bytes for PID and (U)I frame byte: 15+ for KISS data & calls */ + /* Assume the worst case */ + size = len + 3 + size_ax25_addr(dp) + AX25_BPQ_HEADER_LEN; - if ((skb = sock_alloc_send_skb(sk, size, 0, &err)) == NULL) + if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL) return err; skb->sk = sk; skb->free = 1; skb->arp = 1; - skb->len = size; - - asmptr = skb->data; - if (sk->debug) { - printk("Building AX.25 Header (dp=%p).\n", dp); - if (dp != 0) - printk("Num digipeaters=%d\n", dp->ndigi); - } - /* Build an AX.25 header */ - *asmptr++ = 0; /* KISS data */ - asmptr += (lv = build_ax25_addr(asmptr, &sk->ax25->source_addr, &sax.sax25_call, dp, C_COMMAND)); - if (sk->debug) - printk("Built header (%d bytes)\n",lv); - skb->h.raw = asmptr; - - if (sk->debug) - printk("base=%p pos=%p\n", skb->data, asmptr); - *asmptr++ = LAPB_UI; /* Datagram - will get replaced for I frames */ - *asmptr++ = sk->protocol; /* AX.25 TEXT by default */ - + skb_reserve(skb, size - len); + if (sk->debug) printk("AX.25: Appending user data\n"); /* User data follows immediately after the AX.25 data */ - memcpy_fromfs(asmptr, ubuf, len); + memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + + /* Add the PID, usually AX25_TEXT */ + asmptr = skb_push(skb, 1); + *asmptr = sk->protocol; + if (sk->debug) printk("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, FREE_WRITE); return -ENOTCONN; } - ax25_output(sk->ax25, skb); /* Shove it onto the queue and kick */ + + ax25_output(sk->protinfo.ax25, skb); /* Shove it onto the queue and kick */ + return len; } else { + asmptr = skb_push(skb, 1 + size_ax25_addr(dp)); + + if (sk->debug) { + printk("Building AX.25 Header (dp=%p).\n", dp); + if (dp != 0) + printk("Num digipeaters=%d\n", dp->ndigi); + } + + /* Build an AX.25 header */ + asmptr += (lv = build_ax25_addr(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, C_COMMAND, MODULUS)); + + if (sk->debug) + printk("Built header (%d bytes)\n",lv); + + skb->h.raw = asmptr; + + if (sk->debug) + printk("base=%p pos=%p\n", skb->data, asmptr); + + *asmptr = LAPB_UI; + /* Datagram frames go straight out of the door as UI */ - dev_queue_xmit(skb, sk->ax25->device, SOPRI_NORMAL); + ax25_queue_xmit(skb, sk->protinfo.ax25->device, SOPRI_NORMAL); + return len; } + } -static int ax25_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags) -{ - return ax25_sendto(sock, ubuf, size, noblock, flags, NULL, 0); -} - -static int ax25_write(struct socket *sock, char *ubuf, int size, int noblock) -{ - return ax25_send(sock, ubuf, size, noblock, 0); -} - -static int ax25_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, - unsigned flags, struct sockaddr *sip, int *addr_len) +static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) { struct sock *sk = (struct sock *)sock->data; - struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)sip; - char *addrptr = (char *)sip; - int copied = 0; + struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name; + int copied, length; struct sk_buff *skb; int er; + int dama; - if (sk->err) { - er = -sk->err; - sk->err = 0; - return er; - } - if (addr_len != NULL) *addr_len = sizeof(*sax); - /* This works for seqpacket too. The receiver has ordered the queue for us! We do one quick check first though */ + /* + * 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; @@ -1639,51 +2119,61 @@ static int ax25_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL) return er; - copied= (size < skb->len) ? size : skb->len; - skb_copy_datagram(skb, sk->type == SOCK_SEQPACKET ? 2 : 0, ubuf, copied); + if (sk->protinfo.ax25->hdrincl) { + length = skb->len + (skb->data - skb->h.raw); + } else { + if (sk->type == SOCK_SEQPACKET) + skb_pull(skb, 1); /* Remove PID */ + length = skb->len; + skb->h.raw = skb->data; + } + + copied = length; + + if (copied > size) { + copied = size; + msg->msg_flags |= MSG_TRUNC; + } + + skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (sax) { - struct sockaddr_ax25 addr; ax25_digi digi; ax25_address dest; - unsigned char *dp = skb->data; - int ct = 0; - - ax25_parse_addr(dp, skb->len, NULL, &dest, &digi, NULL); - addr.sax25_family = AF_AX25; - memcpy(&addr.sax25_call, &dest, sizeof(ax25_address)); - memcpy(sax,&addr, sizeof(*sax)); - addrptr += sizeof(*sax); - - while (ct < digi.ndigi) { - memcpy(addrptr, &digi. calls[ct], 7); - addrptr += 7; - ct++; - } - if (addr_len) - *addr_len = sizeof(*sax) + 7 * digi.ndigi; - } - skb_free_datagram(skb); + if (addr_len == (int *)0) + return -EINVAL; + if (*addr_len != sizeof(struct sockaddr_ax25) && *addr_len != sizeof(struct full_sockaddr_ax25)) + return -EINVAL; - return copied; -} + ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama); -static int ax25_recv(struct socket *sock, void *ubuf, int size , int noblock, - unsigned flags) -{ - struct sock *sk = (struct sock *)sock->data; + 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 = dest; - if (sk->zapped) - return -ENOTCONN; + *addr_len = sizeof(struct sockaddr_ax25); - return ax25_recvfrom(sock, ubuf, size, noblock, flags, NULL, NULL); -} + if (*addr_len == sizeof(struct full_sockaddr_ax25) && sax->sax25_ndigis != 0) { + int ct = 0; + struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; -static int ax25_read(struct socket *sock, char *ubuf, int size, int noblock) -{ - return ax25_recv(sock, ubuf, size, noblock, 0); -} + while (ct < digi.ndigi) { + fsa->fsa_digipeater[ct] = digi.calls[ct]; + ct++; + } + + *addr_len = sizeof(struct full_sockaddr_ax25); + } + } + + skb_free_datagram(sk, skb); + + return copied; +} static int ax25_shutdown(struct socket *sk, int how) { @@ -1706,23 +2196,22 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) switch (cmd) { case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) + if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) return err; amount = sk->sndbuf - sk->wmem_alloc; if (amount < 0) amount = 0; - put_fs_long(amount, (unsigned long *)arg); + put_user(amount, (int *)arg); return 0; - case TIOCINQ: - { + case TIOCINQ: { struct sk_buff *skb; /* 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 ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) + if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) return err; - put_fs_long(amount, (unsigned long *)arg); + put_user(amount, (int *)arg); return 0; } @@ -1732,33 +2221,44 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -ENOENT; if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) return err; - memcpy_tofs((void *)arg, &sk->stamp, sizeof(struct timeval)); + copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); 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: - { + case SIOCAX25GETUID: { struct sockaddr_ax25 sax25; if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct sockaddr_ax25))) != 0) return err; - memcpy_fromfs(&sax25, (void *)arg, sizeof(sax25)); + copy_from_user(&sax25, (void *)arg, sizeof(sax25)); return ax25_uid_ioctl(cmd, &sax25); } case SIOCAX25NOUID: /* Set the default policy (default/bar) */ if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0) return err; - if(!suser()) + if (!suser()) return -EPERM; - amount = get_fs_long((void *)arg); + get_user(amount, (long *)arg); if (amount > AX25_NOUID_BLOCK) return -EINVAL; ax25_uid_policy = amount; return 0; + case SIOCADDRT: + case SIOCDELRT: + case SIOCAX25OPTRT: + if (!suser()) + return -EPERM; + return ax25_rt_ioctl(cmd, (void *)arg); + + case SIOCAX25CTLCON: + if (!suser()) + return -EPERM; + return ax25_ctl_ioctl(cmd, (void *)arg); + case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: @@ -1772,25 +2272,26 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EINVAL; default: - return(dev_ioctl(cmd, (void *)arg)); + return dev_ioctl(cmd, (void *)arg); } /*NOTREACHED*/ - return(0); + return 0; } -int ax25_get_info(char *buffer, char **start, off_t offset, int length) + +static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ax25_cb *ax25; struct device *dev; - char *devname; + const char *devname; int len = 0; off_t pos = 0; off_t begin = 0; - + cli(); - len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 n2 rtt wnd Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen dama Snd-Q Rcv-Q\n"); for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { if ((dev = ax25->device) == NULL) @@ -1800,7 +2301,7 @@ int ax25_get_info(char *buffer, char **start, off_t offset, int length) len += sprintf(buffer + len, "%-9s ", ax2asc(&ax25->dest_addr)); - len += sprintf(buffer + len, "%-9s %-3s %2d %2d %2d %2d %3d/%03d %2d/%02d %3d/%03d %2d/%02d %3d %3d", + len += sprintf(buffer + len, "%-9s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d %5d", ax2asc(&ax25->source_addr), devname, ax25->state, ax25->vs, ax25->vr, ax25->va, @@ -1810,12 +2311,17 @@ int ax25_get_info(char *buffer, char **start, off_t offset, int length) ax25->t2 / PR_SLOWHZ, ax25->t3timer / PR_SLOWHZ, ax25->t3 / PR_SLOWHZ, + ax25->idletimer / (PR_SLOWHZ * 60), + ax25->idle / (PR_SLOWHZ * 60), ax25->n2count, ax25->n2, ax25->rtt / PR_SLOWHZ, - ax25->window); + ax25->window, + ax25->paclen); + + len += sprintf(buffer + len, " %s", ax25->dama_slave ? " slave" : " no"); if (ax25->sk != NULL) { - len += sprintf(buffer + len, " %5ld %5ld\n", + len += sprintf(buffer + len, " %5d %5d\n", ax25->sk->wmem_alloc, ax25->sk->rmem_alloc); } else { @@ -1854,28 +2360,25 @@ static struct proto_ops ax25_proto_ops = { ax25_socketpair, ax25_accept, ax25_getname, - ax25_read, - ax25_write, ax25_select, ax25_ioctl, ax25_listen, - ax25_send, - ax25_recv, - ax25_sendto, - ax25_recvfrom, ax25_shutdown, ax25_setsockopt, ax25_getsockopt, ax25_fcntl, + ax25_sendmsg, + ax25_recvmsg }; -/* Called by socket.c on kernel start up */ - +/* + * Called by socket.c on kernel start up + */ static struct packet_type ax25_packet_type = { 0, /* MUTTER ntohs(ETH_P_AX25),*/ 0, /* copy */ - ax25_rcv, + kiss_rcv, NULL, NULL, }; @@ -1885,21 +2388,92 @@ static struct notifier_block ax25_dev_notifier = { 0 }; +static struct symbol_table ax25_syms = { +#include <linux/symtab_begin.h> + X(ax25_encapsulate), + X(ax25_rebuild_header), +#if defined(CONFIG_NETROM_MODULE) || defined(CONFIG_ROSE_MODULE) + X(ax25_findbyuid), + X(ax25_link_up), + X(ax25_linkfail_register), + X(ax25_linkfail_release), + X(ax25_listen_register), + X(ax25_listen_release), + X(ax25_protocol_register), + X(ax25_protocol_release), + X(ax25_send_frame), + X(ax25_uid_policy), + X(ax25cmp), + X(ax2asc), + X(asc2ax), + X(null_ax25_address), +#endif +#include <linux/symtab_end.h> +}; + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry proc_ax25_route = { + PROC_NET_AX25_ROUTE, 10, "ax25_route", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_rt_get_info +}; +static struct proc_dir_entry proc_ax25 = { + PROC_NET_AX25, 4, "ax25", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_get_info +}; +static struct proc_dir_entry proc_ax25_calls = { + PROC_NET_AX25_CALLS, 10, "ax25_calls", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ax25_cs_get_info +}; +#endif + void ax25_proto_init(struct net_proto *pro) { sock_register(ax25_proto_ops.family, &ax25_proto_ops); ax25_packet_type.type = htons(ETH_P_AX25); dev_add_pack(&ax25_packet_type); register_netdevice_notifier(&ax25_dev_notifier); - printk("GW4PTS/G4KLX AX.25 for Linux. Version 0.29 ALPHA for Linux NET3.029 (Linux 1.3.0)\n"); + register_symtab(&ax25_syms); + ax25_register_sysctl(); + +#ifdef CONFIG_PROC_FS + proc_net_register(&proc_ax25_route); + proc_net_register(&proc_ax25); + proc_net_register(&proc_ax25_calls); +#endif + + printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.34 for Linux NET3.037 (Linux 2.1)\n"); } -/*******************************************************************************************************************\ -* * -* Driver encapsulation support: Moved out of SLIP because a) it should be here * -* b) for HDLC cards * -* * -\*******************************************************************************************************************/ +/* + * A small shim to dev_queue_xmit to add the KISS control byte. + */ +void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) +{ + unsigned char *ptr; + +#ifdef CONFIG_FIREWALL + if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) { + dev_kfree_skb(skb, FREE_WRITE); + return; + } +#endif + + skb->protocol = htons(ETH_P_AX25); + + ptr = skb_push(skb, 1); + *ptr++ = 0; /* KISS */ + dev_queue_xmit(skb, dev, pri); +} + +/* + * IP over AX.25 encapsulation. + */ /* * Shove an AX.25 UI header on an IP packet and handle ARP @@ -1907,18 +2481,21 @@ void ax25_proto_init(struct net_proto *pro) #ifdef CONFIG_INET -int ax25_encapsulate(unsigned char *buff, struct device *dev, unsigned short type, void *daddr, - void *saddr, unsigned len, struct sk_buff *skb) +int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, + void *saddr, unsigned len) { /* header is an AX.25 UI frame from us to them */ + unsigned char *buff = skb_push(skb, AX25_HEADER_LEN); + *buff++ = 0; /* KISS DATA */ if (daddr != NULL) memcpy(buff, daddr, dev->addr_len); /* Address specified */ + buff[6] &= ~LAPB_C; buff[6] &= ~LAPB_E; - buff[6] |= SSID_SPARE; - buff += 7; + buff[6] |= SSSID_SPARE; + buff += AX25_ADDR_LEN; if (saddr != NULL) memcpy(buff, saddr, dev->addr_len); @@ -1927,8 +2504,9 @@ int ax25_encapsulate(unsigned char *buff, struct device *dev, unsigned short typ buff[6] &= ~LAPB_C; buff[6] |= LAPB_E; - buff[6] |= SSID_SPARE; - buff += 7; + buff[6] |= SSSID_SPARE; + buff += AX25_ADDR_LEN; + *buff++ = LAPB_UI; /* UI */ /* Append a suitable AX.25 PID */ @@ -1940,33 +2518,109 @@ int ax25_encapsulate(unsigned char *buff, struct device *dev, unsigned short typ case ETH_P_ARP: *buff++ = AX25_P_ARP; break; - default: + printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type); *buff++ = 0; break; } - if (daddr != NULL) - return 17; + if (daddr != NULL) + return AX25_HEADER_LEN; - return -17; /* Unfinished header */ + return -AX25_HEADER_LEN; /* Unfinished header */ } int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long dest, struct sk_buff *skb) { + struct sk_buff *ourskb; + int mode; + if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb)) return 1; + if (bp[16] == AX25_P_IP) { + mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev); + if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) { + /* + * This is a workaround to try to keep the device locking + * straight until skb->free=0 is abolished post 1.4. + * + * We clone the buffer and release the original thereby + * keeping it straight + * + * Note: we report 1 back so the caller will + * not feed the frame direct to the physical device + * We don't want that to happen. (It won't be upset + * as we have pulled the frame from the queue by + * freeing it). + */ + if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) { + dev_kfree_skb(skb, FREE_WRITE); + return 1; + } + + ourskb->sk = skb->sk; + + if (ourskb->sk != NULL) + atomic_add(ourskb->truesize, &ourskb->sk->wmem_alloc); + + dev_kfree_skb(skb, FREE_WRITE); + + skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ + + ax25_send_frame(ourskb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev); + + return 1; + } + } + bp[7] &= ~LAPB_C; bp[7] &= ~LAPB_E; - bp[7] |= SSID_SPARE; + bp[7] |= SSSID_SPARE; + bp[14] &= ~LAPB_C; bp[14] |= LAPB_E; - bp[14] |= SSID_SPARE; - - return 0; + bp[14] |= SSSID_SPARE; + + /* + * dl1bke 960317: we use ax25_queue_xmit here to allow mode datagram + * over ethernet. I don't know if this is valid, though. + */ + ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev); + ax25_queue_xmit(skb, dev, SOPRI_NORMAL); + + return 1; } #endif +#ifdef MODULE +int init_module(void) +{ + ax25_proto_init(NULL); + + return 0; +} + +void cleanup_module(void) +{ +#ifdef CONFIG_PROC_FS + proc_net_unregister(PROC_NET_AX25_ROUTE); + proc_net_unregister(PROC_NET_AX25); + proc_net_unregister(PROC_NET_AX25_CALLS); + proc_net_unregister(PROC_NET_AX25_ROUTE); +#endif + ax25_rt_free(); + + ax25_unregister_sysctl(); + + unregister_netdevice_notifier(&ax25_dev_notifier); + + ax25_packet_type.type = htons(ETH_P_AX25); + dev_remove_pack(&ax25_packet_type); + + sock_unregister(ax25_proto_ops.family); +} +#endif + #endif diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index ab22a8f6d..3ef1c3fdf 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 029 + * AX.25 release 033 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -24,10 +24,21 @@ * the sock structure. * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. * Jonathan(G4KLX) Added IP mode registration. + * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. + * Upgraded state machine for SABME. + * Added arbitrary protocol id support. + * AX.25 031 Joerg(DL1BKE) Added DAMA support + * HaJo(DD8NE) Added Idle Disc Timer T5 + * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly + * different behaviour. Fixed defrag + * routine (I hope) + * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. + * AX.25 033 Jonathan(G4KLX) Remove auto-router. + * Modularisation changes. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -44,52 +55,135 @@ #include <linux/skbuff.h> #include <net/sock.h> #include <net/ip.h> /* For ip_rcv */ -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/interrupt.h> -#ifdef CONFIG_NETROM -#include <net/netrom.h> -#endif + +static int ax25_rx_iframe(ax25_cb *, struct sk_buff *); + +/* + * Given a fragment, queue it on the fragment queue and if the fragment + * is complete, send it back to ax25_rx_iframe. + */ +static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) +{ + struct sk_buff *skbn, *skbo; + int hdrlen, nhdrlen; + + if (ax25->fragno != 0) { + if (!(*skb->data & SEG_FIRST)) { + if ((ax25->fragno - 1) == (*skb->data & SEG_REM)) { + /* Enqueue fragment */ + ax25->fragno = *skb->data & SEG_REM; + skb_pull(skb, 1); /* skip fragno */ + ax25->fraglen += skb->len; + skb_queue_tail(&ax25->frag_queue, skb); + + /* Last fragment received ? */ + if (ax25->fragno == 0) { + if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) { + while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) + kfree_skb(skbo, FREE_READ); + return 1; + } + + skbn->free = 1; + skbn->arp = 1; + skbn->dev = ax25->device; + + if (ax25->sk != NULL) { + skbn->sk = ax25->sk; + atomic_add(skbn->truesize, &ax25->sk->rmem_alloc); + } + + skb_reserve(skbn, AX25_MAX_HEADER_LEN); + + /* Get first fragment from queue */ + skbo = skb_dequeue(&ax25->frag_queue); + hdrlen = skbo->data - skbo->h.raw; + nhdrlen = hdrlen - 2; + + skb_push(skbo, hdrlen); + skb_push(skbn, nhdrlen); + skbn->h.raw = skbn->data; + + /* Copy AX.25 headers */ + memcpy(skbn->data, skbo->data, nhdrlen); + skb_pull(skbn, nhdrlen); + skb_pull(skbo, hdrlen); + + /* Copy data from the fragments */ + do { + memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + kfree_skb(skbo, FREE_READ); + } while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL); + + ax25->fraglen = 0; + + if (ax25_rx_iframe(ax25, skbn) == 0) + kfree_skb(skbn, FREE_READ); + } + + return 1; + } + } + } else { + /* First fragment received */ + if (*skb->data & SEG_FIRST) { + while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) + kfree_skb(skbo, FREE_READ); + ax25->fragno = *skb->data & SEG_REM; + skb_pull(skb, 1); /* skip fragno */ + ax25->fraglen = skb->len; + skb_queue_tail(&ax25->frag_queue, skb); + return 1; + } + } + + return 0; +} /* * This is where all valid I frames are sent to, to be dispatched to * whichever protocol requires them. */ -static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb, unsigned char *iframe) +static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) { - int queued = 0; + int (*func)(struct sk_buff *, ax25_cb *); + volatile int queued = 0; + unsigned char pid; + + if (skb == NULL) return 0; + + ax25->idletimer = ax25->idle; + + pid = *skb->data; - switch (iframe[1]) { -#ifdef CONFIG_NETROM - case AX25_P_NETROM: - /* We can't handle digipeated NET/ROM frames */ - if (ax25->digipeat == NULL) - queued = nr_route_frame(skb, ax25->device); - break; -#endif #ifdef CONFIG_INET - case AX25_P_IP: - ax25_ip_mode_set(&ax25->dest_addr, ax25->device, 'V'); - skb->h.raw = ((char *)(iframe)) + 2; - skb->len -= 2; - ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */ - queued = 1; - break; + if (pid == AX25_P_IP) { + skb_pull(skb, 1); /* Remove PID */ + skb->h.raw = skb->data; + ip_rcv(skb, ax25->device, NULL); /* Wrong ptype */ + return 1; + } #endif - case AX25_P_TEXT: - if (ax25->sk != NULL) { - if (sock_queue_rcv_skb(ax25->sk, skb) == 0) { - queued = 1; - } else { - ax25->condition |= OWN_RX_BUSY_CONDITION; - } - } - break; - - default: - break; + if (pid == AX25_P_SEGMENT) { + skb_pull(skb, 1); /* Remove PID */ + return ax25_rx_fragment(ax25, skb); + } + + if ((func = ax25_protocol_function(pid)) != NULL) { + skb_pull(skb, 1); /* Remove PID */ + return (*func)(skb, ax25); + } + + if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) { + if (sock_queue_rcv_skb(ax25->sk, skb) == 0) + queued = 1; + else + ax25->condition |= OWN_RX_BUSY_CONDITION; } return queued; @@ -100,29 +194,40 @@ static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb, unsigned char *ifr * The handling of the timer(s) is in file ax25_timer.c. * Handling of state 0 and connection release is in ax25.c. */ -static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type) +static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type, int dama) { - int pf = frame[0] & PF; - switch (frametype) { case SABM: - ax25_send_control(ax25, UA | pf, C_RESPONSE); + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); + break; + + case SABME: + ax25->modulus = EMODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); break; case DISC: - ax25_send_control(ax25, DM | pf, C_RESPONSE); + ax25_send_control(ax25, DM, pf, C_RESPONSE); break; case UA: - if (pf) { + if (pf || dama) { + if (dama) ax25_dama_on(ax25); /* bke */ + ax25_calculate_rtt(ax25); ax25->t1timer = 0; ax25->t3timer = ax25->t3; + ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; ax25->state = AX25_STATE_3; ax25->n2count = 0; + ax25->dama_slave = dama; /* bke */ + if (ax25->sk != NULL) { ax25->sk->state = TCP_ESTABLISHED; /* For WAIT_SABM connections we will produce an accept ready socket here */ @@ -134,19 +239,27 @@ static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char case DM: if (pf) { - ax25_clear_tx_queue(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNREFUSED; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + if (ax25->modulus == MODULUS) { + ax25_clear_queues(ax25); + ax25->state = AX25_STATE_0; + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ECONNREFUSED; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } + } else { + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); } } break; default: + if (dama && pf) + ax25_send_control(ax25, SABM, POLLON, C_COMMAND); break; } @@ -158,28 +271,43 @@ static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char * The handling of the timer(s) is in file ax25_timer.c * Handling of state 0 and connection release is in ax25.c. */ -static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type) +static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) { - int pf = frame[0] & PF; - switch (frametype) { case SABM: - ax25_send_control(ax25, DM | pf, C_RESPONSE); + case SABME: + ax25_send_control(ax25, DM, pf, C_RESPONSE); + if (ax25->dama_slave) + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); break; case DISC: - ax25_send_control(ax25, UA | pf, C_RESPONSE); + ax25_send_control(ax25, UA, pf, C_RESPONSE); + if (ax25->dama_slave) { + ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } + } break; case UA: if (pf) { ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } } break; @@ -187,12 +315,14 @@ static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char case DM: if (pf) { ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } } break; @@ -201,8 +331,12 @@ static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char case REJ: case RNR: case RR: - if (pf) - ax25_send_control(ax25, DM | PF, C_RESPONSE); + if (pf) { + if (ax25->dama_slave) + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); + else + ax25_send_control(ax25, DM, POLLON, C_RESPONSE); + } break; default: @@ -217,53 +351,69 @@ static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char * The handling of the timer(s) is in file ax25_timer.c * Handling of state 0 and connection release is in ax25.c. */ -static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type) +static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama) { - unsigned short nr = (frame[0] >> 5) & 7; - unsigned short ns = (frame[0] >> 1) & 7; - int pf = frame[0] & PF; int queued = 0; switch (frametype) { case SABM: - ax25_send_control(ax25, UA | pf, C_RESPONSE); + if (dama) ax25_dama_on(ax25); + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); + ax25->condition = 0x00; + ax25->t1timer = 0; + ax25->t3timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->dama_slave = dama; + break; + + case SABME: + if (dama) ax25_dama_on(ax25); + ax25->modulus = EMODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); ax25->condition = 0x00; ax25->t1timer = 0; ax25->t3timer = ax25->t3; + ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; + ax25->dama_slave = dama; break; case DISC: - ax25_clear_tx_queue(ax25); - ax25_send_control(ax25, UA | pf, C_RESPONSE); + ax25_clear_queues(ax25); + ax25_send_control(ax25, UA, pf, C_RESPONSE); ax25->t3timer = 0; ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } break; - case UA: - ax25_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - case DM: - ax25_clear_tx_queue(ax25); + ax25_clear_queues(ax25); ax25->t3timer = 0; ax25->state = AX25_STATE_0; - if (ax25->sk) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; + ax25_dama_off(ax25); + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ECONNRESET; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } break; @@ -272,6 +422,7 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25_check_need_response(ax25, type, pf); if (ax25_validate_nr(ax25, nr)) { ax25_check_iframes_acked(ax25, nr); + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -283,6 +434,7 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25_check_need_response(ax25, type, pf); if (ax25_validate_nr(ax25, nr)) { ax25_check_iframes_acked(ax25, nr); + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -297,6 +449,8 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25_calculate_rtt(ax25); ax25->t1timer = 0; ax25->t3timer = ax25->t3; + ax25_requeue_frames(ax25); + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -304,8 +458,10 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char break; case I: +#ifndef AX25_BROKEN_NETMAC if (type != C_COMMAND) break; +#endif if (!ax25_validate_nr(ax25, nr)) { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -317,19 +473,33 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25_check_iframes_acked(ax25, nr); } if (ax25->condition & OWN_RX_BUSY_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + if (pf) { + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); + } break; } if (ns == ax25->vr) { - queued = ax25_rx_iframe(ax25, skb, frame); + ax25->vr = (ax25->vr + 1) % ax25->modulus; + queued = ax25_rx_iframe(ax25, skb); if (ax25->condition & OWN_RX_BUSY_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + ax25->vr = ns; /* ax25->vr - 1 */ + if (pf) { + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); + } break; } - ax25->vr = (ax25->vr + 1) % MODULUS; ax25->condition &= ~REJECT_CONDITION; if (pf) { - ax25_enquiry_response(ax25); + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); } else { if (!(ax25->condition & ACK_PENDING_CONDITION)) { ax25->t2timer = ax25->t2; @@ -338,10 +508,18 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char } } else { if (ax25->condition & REJECT_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + if (pf) { + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); + } } else { ax25->condition |= REJECT_CONDITION; - ax25_send_control(ax25, REJ | pf, C_RESPONSE); + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_send_control(ax25, REJ, pf, C_RESPONSE); ax25->condition &= ~ACK_PENDING_CONDITION; } } @@ -365,19 +543,38 @@ static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char * The handling of the timer(s) is in file ax25_timer.c * Handling of state 0 and connection release is in ax25.c. */ -static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char *frame, int frametype, int type) +static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama) { - unsigned short nr = (frame[0] >> 5) & 7; - unsigned short ns = (frame[0] >> 1) & 7; - int pf = frame[0] & PF; int queued = 0; switch (frametype) { case SABM: - ax25_send_control(ax25, UA | pf, C_RESPONSE); + if (dama) ax25_dama_on(ax25); + ax25->dama_slave = dama; + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); + ax25->condition = 0x00; + ax25->t1timer = 0; + ax25->t3timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->state = AX25_STATE_3; + ax25->n2count = 0; + break; + + case SABME: + if (dama) ax25_dama_on(ax25); + ax25->dama_slave = dama; + ax25->modulus = EMODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + ax25_send_control(ax25, UA, pf, C_RESPONSE); ax25->condition = 0x00; ax25->t1timer = 0; ax25->t3timer = ax25->t3; + ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -386,34 +583,33 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char break; case DISC: - ax25_clear_tx_queue(ax25); - ax25_send_control(ax25, UA | pf, C_RESPONSE); + ax25_clear_queues(ax25); + ax25_send_control(ax25, UA, pf, C_RESPONSE); ax25->t3timer = 0; ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } break; - case UA: - ax25_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - case DM: - ax25_clear_tx_queue(ax25); + ax25_clear_queues(ax25); ax25->t3timer = 0; ax25->state = AX25_STATE_0; + ax25_dama_off(ax25); if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ECONNRESET; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } break; @@ -434,10 +630,11 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char } break; } - if (type == C_COMMAND && pf) - ax25_enquiry_response(ax25); + + ax25_check_need_response(ax25, type, pf); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -446,7 +643,7 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char case RR: ax25->condition &= ~PEER_RX_BUSY_CONDITION; - if (type == C_RESPONSE && pf) { + if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) { ax25->t1timer = 0; if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); @@ -454,17 +651,21 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25->t3timer = ax25->t3; ax25->n2count = 0; ax25->state = AX25_STATE_3; + } else { + ax25_requeue_frames(ax25); } + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; } break; } - if (type == C_COMMAND && pf) - ax25_enquiry_response(ax25); + + ax25_check_need_response(ax25, type, pf); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -473,7 +674,7 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char case REJ: ax25->condition &= ~PEER_RX_BUSY_CONDITION; - if (type == C_RESPONSE && pf) { + if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) { ax25->t1timer = 0; if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); @@ -481,17 +682,24 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char ax25->t3timer = ax25->t3; ax25->n2count = 0; ax25->state = AX25_STATE_3; + } else { + ax25_requeue_frames(ax25); } + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; } break; } - if (type == C_COMMAND && pf) - ax25_enquiry_response(ax25); + + ax25_check_need_response(ax25, type, pf); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); + if(ax25->vs != ax25->va) { + ax25_requeue_frames(ax25); + } + dama_check_need_response(ax25, type, pf); } else { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -499,8 +707,10 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char break; case I: +#ifndef AX25_BROKEN_NETMAC if (type != C_COMMAND) break; +#endif if (!ax25_validate_nr(ax25, nr)) { ax25_nr_error_recovery(ax25); ax25->state = AX25_STATE_1; @@ -508,19 +718,33 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char } ax25_frames_acked(ax25, nr); if (ax25->condition & OWN_RX_BUSY_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + if (pf) { + if (ax25->dama_slave) + ax25_enquiry_response(ax25); + else + dama_enquiry_response(ax25); + } break; } if (ns == ax25->vr) { - queued = ax25_rx_iframe(ax25, skb, frame); + ax25->vr = (ax25->vr + 1) % ax25->modulus; + queued = ax25_rx_iframe(ax25, skb); if (ax25->condition & OWN_RX_BUSY_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + ax25->vr = ns; /* ax25->vr - 1 */ + if (pf) { + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); + } break; } - ax25->vr = (ax25->vr + 1) % MODULUS; ax25->condition &= ~REJECT_CONDITION; if (pf) { - ax25_enquiry_response(ax25); + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); } else { if (!(ax25->condition & ACK_PENDING_CONDITION)) { ax25->t2timer = ax25->t2; @@ -529,10 +753,18 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char } } else { if (ax25->condition & REJECT_CONDITION) { - if (pf) ax25_enquiry_response(ax25); + if (pf) { + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_enquiry_response(ax25); + } } else { ax25->condition |= REJECT_CONDITION; - ax25_send_control(ax25, REJ | pf, C_RESPONSE); + if (ax25->dama_slave) + dama_enquiry_response(ax25); + else + ax25_send_control(ax25, REJ, pf, C_RESPONSE); ax25->condition &= ~ACK_PENDING_CONDITION; } } @@ -554,38 +786,35 @@ static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, unsigned char /* * Higher level upcall for a LAPB frame */ -int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type) +int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) { - int queued = 0, frametype; - unsigned char *frame; + int queued = 0, frametype, ns, nr, pf; + + if (ax25->state == AX25_STATE_0) + return 0; del_timer(&ax25->timer); - frame = skb->h.raw; - - frametype = ax25_decode(frame); + frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); switch (ax25->state) { case AX25_STATE_1: - queued = ax25_state1_machine(ax25, skb, frame, frametype, type); + queued = ax25_state1_machine(ax25, skb, frametype, pf, type, dama); break; case AX25_STATE_2: - queued = ax25_state2_machine(ax25, skb, frame, frametype, type); + queued = ax25_state2_machine(ax25, skb, frametype, pf, type); break; case AX25_STATE_3: - queued = ax25_state3_machine(ax25, skb, frame, frametype, type); + queued = ax25_state3_machine(ax25, skb, frametype, ns, nr, pf, type, dama); break; case AX25_STATE_4: - queued = ax25_state4_machine(ax25, skb, frame, frametype, type); - break; - default: - printk("ax25_process_rx_frame: frame received - state = %d\n", ax25->state); + queued = ax25_state4_machine(ax25, skb, frametype, ns, nr, pf, type, dama); break; } ax25_set_timer(ax25); - return(queued); + return queued; } #endif diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 73cd056c7..be265b344 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,5 +1,5 @@ /* - * AX.25 release 029 + * AX.25 release 033 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -22,10 +22,17 @@ * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. * Jonathan(G4KLX) Only poll when window is full. + * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output. + * Added support for extended AX.25. + * AX.25 031 Joerg(DL1BKE) Added DAMA support + * Joerg(DL1BKE) Modified fragmenter to fragment vanilla + * AX.25 I-Frames. Added PACLEN parameter. + * Joerg(DL1BKE) Fixed a problem with buffer allocation + * for fragments. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -41,20 +48,108 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <net/sock.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/interrupt.h> -int ax25_output(ax25_cb *ax25, struct sk_buff *skb) +/* + * All outgoing AX.25 I frames pass via this routine. Therefore this is + * where the fragmentation of frames takes place. + */ +void ax25_output(ax25_cb *ax25, struct sk_buff *skb) { - skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ + struct sk_buff *skbn; + unsigned char *p; + int frontlen, mtu, len, fragno, ka9qfrag, first = 1; + long flags; + + /* + * dl1bke 960301: We use the new PACLEN parameter as MTU of the AX.25 layer. + * This will (hopefully) allow user programs to write() data + * w/o having to think of the maximal amount of data we can + * send with one call. It's called PACLEN to (1) avoid confusion + * with (IP) MTU and (2) TAPR calls this PACLEN, too ;-) + */ + + mtu = ax25->paclen; + + if ((skb->len - 1) > mtu) { + if (*skb->data == AX25_P_TEXT) { + skb_pull(skb, 1); /* skip PID */ + ka9qfrag = 0; + } else { + mtu -= 2; /* Allow for fragment control info */ + ka9qfrag = 1; + } + + fragno = skb->len / mtu; + if (skb->len % mtu == 0) fragno--; + + frontlen = skb_headroom(skb); /* Address space + CTRL */ + + while (skb->len > 0) { + save_flags(flags); + cli(); + /* + * do _not_ use sock_alloc_send_skb, our socket may have + * sk->shutdown set... + */ + if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) { + restore_flags(flags); + printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n"); + if (skb_device_locked(skb)) + skb_device_unlock(skb); + return; + } - if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) - ax25_kick(ax25); + skbn->sk = skb->sk; + + if (skbn->sk) + atomic_add(skbn->truesize, &skbn->sk->wmem_alloc); + + restore_flags(flags); + + skbn->free = 1; + skbn->arp = 1; + + len = (mtu > skb->len) ? skb->len : mtu; + + if (ka9qfrag == 1) { + skb_reserve(skbn, frontlen + 2); + + memcpy(skb_put(skbn, len), skb->data, len); + p = skb_push(skbn, 2); + + *p++ = AX25_P_SEGMENT; + + *p = fragno--; + if (first) { + *p |= SEG_FIRST; + first = 0; + } + } else { + skb_reserve(skbn, frontlen + 1); + memcpy(skb_put(skbn, len), skb->data, len); + p = skb_push(skbn, 1); + *p = AX25_P_TEXT; + } - return 0; + skb_pull(skb, len); + skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */ + } + + skb->free = 1; + kfree_skb(skb, FREE_WRITE); + } else { + skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ + } + + if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) { + if (!ax25->dama_slave) /* bke 960114: we aren't allowed to transmit */ + ax25_kick(ax25); /* in DAMA mode unless we received a Poll */ + } } /* @@ -67,13 +162,22 @@ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) if (skb == NULL) return; - - frame = skb->h.raw; /* KISS + header */ - *frame = I; - *frame |= poll_bit; - *frame |= (ax25->vr << 5); - *frame |= (ax25->vs << 1); + if (ax25->modulus == MODULUS) { + frame = skb_push(skb, 1); + + *frame = I; + *frame |= (poll_bit) ? PF : 0; + *frame |= (ax25->vr << 5); + *frame |= (ax25->vs << 1); + } else { + frame = skb_push(skb, 2); + + frame[0] = I; + frame[0] |= (ax25->vs << 1); + frame[1] = (poll_bit) ? EPF : 0; + frame[1] |= (ax25->vr << 1); + } ax25_transmit_buffer(ax25, skb, C_COMMAND); } @@ -87,7 +191,7 @@ void ax25_kick(ax25_cb *ax25) del_timer(&ax25->timer); start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; - end = (ax25->va + ax25->window) % MODULUS; + end = (ax25->va + ax25->window) % ax25->modulus; if (!(ax25->condition & PEER_RX_BUSY_CONDITION) && start != end && @@ -100,18 +204,19 @@ void ax25_kick(ax25_cb *ax25) * the window is full. Send a poll on the final I frame if * the window is filled. */ - do { - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&ax25->write_queue); + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&ax25->write_queue); + + do { if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { skb_queue_head(&ax25->write_queue, skb); - return; + break; } - next = (ax25->vs + 1) % MODULUS; + next = (ax25->vs + 1) % ax25->modulus; #ifdef notdef last = (next == end) || skb_peek(&ax25->write_queue) == NULL; #else @@ -119,8 +224,10 @@ void ax25_kick(ax25_cb *ax25) #endif /* * Transmit the frame copy. + * bke 960114: do not set the Poll bit on the last frame + * in DAMA mode. */ - ax25_send_iframe(ax25, skbn, (last) ? PF : 0); + ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? POLLON : POLLOFF); ax25->vs = next; @@ -131,7 +238,7 @@ void ax25_kick(ax25_cb *ax25) #ifdef notdef } while (!last); #else - } while (!last && skb_peek(&ax25->write_queue) != NULL); + } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); #endif ax25->condition &= ~ACK_PENDING_CONDITION; @@ -146,25 +253,33 @@ void ax25_kick(ax25_cb *ax25) void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) { - unsigned char *ptr = skb->data; + unsigned char *ptr; if (ax25->device == NULL) { if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ENETUNREACH; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ENETUNREACH; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } return; } - *ptr++ = 0; /* KISS data */ - ptr += build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type); + if (skb_headroom(skb) < size_ax25_addr(ax25->digipeat)) { + printk(KERN_CRIT "ax25_transmit_buffer: not enough room for digi-peaters\n"); + skb->free = 1; + kfree_skb(skb, FREE_WRITE); + return; + } + + ptr = skb_push(skb, size_ax25_addr(ax25->digipeat)); + build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); skb->arp = 1; - dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL); + ax25_queue_xmit(skb, ax25->device, SOPRI_NORMAL); } /* @@ -182,8 +297,12 @@ void ax25_establish_data_link(ax25_cb *ax25) ax25->condition = 0x00; ax25->n2count = 0; - ax25_send_control(ax25, SABM | PF, C_COMMAND); - + if (ax25->modulus == MODULUS) { + ax25_send_control(ax25, SABM, POLLON, C_COMMAND); + } else { + ax25_send_control(ax25, SABME, POLLON, C_COMMAND); + } + ax25->t3timer = 0; ax25->t2timer = 0; ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); @@ -192,9 +311,9 @@ void ax25_establish_data_link(ax25_cb *ax25) void ax25_transmit_enquiry(ax25_cb *ax25) { if (ax25->condition & OWN_RX_BUSY_CONDITION) - ax25_send_control(ax25, RNR | PF, C_COMMAND); + ax25_send_control(ax25, RNR, POLLON, C_COMMAND); else - ax25_send_control(ax25, RR | PF, C_COMMAND); + ax25_send_control(ax25, RR, POLLON, C_COMMAND); ax25->condition &= ~ACK_PENDING_CONDITION; @@ -204,9 +323,19 @@ void ax25_transmit_enquiry(ax25_cb *ax25) void ax25_enquiry_response(ax25_cb *ax25) { if (ax25->condition & OWN_RX_BUSY_CONDITION) - ax25_send_control(ax25, RNR | PF, C_RESPONSE); + ax25_send_control(ax25, RNR, POLLON, C_RESPONSE); else - ax25_send_control(ax25, RR | PF, C_RESPONSE); + ax25_send_control(ax25, RR, POLLON, C_RESPONSE); + + ax25->condition &= ~ACK_PENDING_CONDITION; +} + +void ax25_timeout_response(ax25_cb *ax25) +{ + if (ax25->condition & OWN_RX_BUSY_CONDITION) + ax25_send_control(ax25, RNR, POLLOFF, C_RESPONSE); + else + ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE); ax25->condition &= ~ACK_PENDING_CONDITION; } @@ -226,10 +355,100 @@ void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) } } +/* + * dl1bke 960114: shouldn't ax25/dama_check_need_response reside as + * static inline void ...() in ax25.h, should it? ;-) + */ void ax25_check_need_response(ax25_cb *ax25, int type, int pf) { - if (type == C_COMMAND && pf) + if (!ax25->dama_slave && type == C_COMMAND && pf) + ax25_enquiry_response(ax25); +} + +/* + * dl1bke 960114: transmit I frames on DAMA poll + */ +void dama_enquiry_response(ax25_cb *ax25) +{ + ax25_cb *ax25o; + + if (!(ax25->condition & PEER_RX_BUSY_CONDITION)) { + ax25_requeue_frames(ax25); + ax25_kick(ax25); + } + + if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || + skb_peek(&ax25->ack_queue) != NULL) { + ax25_t1_timeout(ax25); + } else { + ax25->n2count = 0; + } + + ax25->t3timer = ax25->t3; + + + /* The FLEXNET DAMA master implementation refuses to send us ANY */ + /* I frame for this connection if we send a REJ here, probably */ + /* due to its frame collector scheme? A simple RR or RNR will */ + /* invoke the retransmission, and in fact REJs are superfluous */ + /* in DAMA mode anyway... */ + +#if 0 + if (ax25->condition & REJECT_CONDITION) + ax25_send_control(ax25, REJ, POLLOFF, C_RESPONSE); + else +#endif ax25_enquiry_response(ax25); + + /* Note that above response to the poll could be sent behind the */ + /* transmissions of the other channels as well... This version */ + /* gives better performance on FLEXNET nodes. (Why, Gunter?) */ + + for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { + if (ax25o == ax25) + continue; + + if (ax25o->device != ax25->device) + continue; + + if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { + ax25_t1_timeout(ax25o); + continue; + } + + if (!ax25o->dama_slave) + continue; + + if ( !(ax25o->condition & PEER_RX_BUSY_CONDITION) && + (ax25o->state == AX25_STATE_3 || + (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) { + ax25_requeue_frames(ax25o); + ax25_kick(ax25o); + } + + if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || + skb_peek(&ax25o->ack_queue) != NULL) { + ax25_t1_timeout(ax25o); + } + + ax25o->t3timer = ax25o->t3; + } +} + +void dama_check_need_response(ax25_cb *ax25, int type, int pf) +{ + if (ax25->dama_slave && type == C_COMMAND && pf) + dama_enquiry_response(ax25); +} + +void dama_establish_data_link(ax25_cb *ax25) +{ + ax25->condition = 0x00; + ax25->n2count = 0; + + ax25->t3timer = ax25->t3; + ax25->t2timer = 0; + ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); } #endif diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index b0ffcea58..b4606111e 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,5 +1,5 @@ /* - * AX.25 release 029 + * AX.25 release 033 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -17,17 +17,32 @@ * * History * AX.25 020 Jonathan(G4KLX) First go. - * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice mheard list. + * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list. * AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan. * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the * sock structure. Device removal now * removes the heard structure. * AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping. * Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry. + * AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and + * ioctls to manipulate them. Added port + * configuration. + * AX.25 031 Jonathan(G4KLX) Added concept of default route. + * Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by + * destination call. Needed for IP routing via digipeater + * Jonathan(G4KLX) Added routing for IP datagram packets. + * Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default + * route if available. Does not overwrite default routes + * on route-table overflow anymore. + * Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl() + * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag + * on routes. + * AX.25 033 Jonathan(G4KLX) Remove auto-router. + * Joerg(DL1BKE) Moved BPQ Ethernet driver to seperate device. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -41,74 +56,46 @@ #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/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/interrupt.h> -#define AX25_ROUTE_MAX 40 - static struct ax25_route { struct ax25_route *next; ax25_address callsign; struct device *dev; - struct timeval stamp; - int n; + ax25_digi *digipeat; char ip_mode; } *ax25_route = NULL; -void ax25_rt_rx_frame(ax25_address *src, struct device *dev) -{ - unsigned long flags; - extern struct timeval xtime; - struct ax25_route *ax25_rt; - struct ax25_route *oldest; - int count; - - count = 0; - oldest = NULL; - - for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (count == 0 || ax25_rt->stamp.tv_sec < oldest->stamp.tv_sec) - oldest = ax25_rt; - - if (ax25cmp(&ax25_rt->callsign, src) == 0 && ax25_rt->dev == dev) { - ax25_rt->stamp = xtime; - ax25_rt->n++; - return; - } - - count++; - } - - if (count > AX25_ROUTE_MAX) { - oldest->callsign = *src; - oldest->dev = dev; - oldest->stamp = xtime; - oldest->n = 1; - oldest->ip_mode = ' '; - return; - } +struct ax25_dev ax25_device[AX25_MAX_DEVICES] = { + {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, + {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, + {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, + {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL} +}; - if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL) - return; /* No space */ +static struct ax25_route *ax25_find_route(ax25_address *, struct device *); - ax25_rt->callsign = *src; - ax25_rt->dev = dev; - ax25_rt->stamp = xtime; - ax25_rt->n = 1; - ax25_rt->ip_mode = ' '; +/* + * small macro to drop non-digipeated digipeaters and reverse path + */ +static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) +{ + int k; - save_flags(flags); - cli(); + for (k = 0; k < in->ndigi; k++) + if (!in->repeated[k]) + break; - ax25_rt->next = ax25_route; - ax25_route = ax25_rt; + in->ndigi = k; - restore_flags(flags); + ax25_digi_invert(in, out); } void ax25_rt_device_down(struct device *dev) @@ -122,11 +109,15 @@ void ax25_rt_device_down(struct device *dev) if (s->dev == dev) { if (ax25_route == s) { ax25_route = s->next; + if (s->digipeat != NULL) + kfree_s((void *)s->digipeat, sizeof(ax25_digi)); kfree_s((void *)s, (sizeof *s)); } else { for (t = ax25_route; t != NULL; t = t->next) { if (t->next == s) { t->next = s->next; + if (s->digipeat != NULL) + kfree_s((void *)s->digipeat, sizeof(ax25_digi)); kfree_s((void *)s, sizeof(*s)); break; } @@ -136,37 +127,172 @@ void ax25_rt_device_down(struct device *dev) } } -int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) +int ax25_rt_ioctl(unsigned int cmd, void *arg) +{ + unsigned long flags; + struct ax25_route *s, *t, *ax25_rt; + struct ax25_routes_struct route; + struct ax25_route_opt_struct rt_option; + struct device *dev; + int i, err; + + switch (cmd) { + case SIOCADDRT: + if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) + return err; + copy_from_user(&route, arg, sizeof(route)); + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) + return -EINVAL; + if (route.digi_count > AX25_MAX_DIGIS) + return -EINVAL; + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == dev) { + if (ax25_rt->digipeat != NULL) { + kfree_s(ax25_rt->digipeat, sizeof(ax25_digi)); + ax25_rt->digipeat = NULL; + } + if (route.digi_count != 0) { + if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) + return -ENOMEM; + ax25_rt->digipeat->lastrepeat = 0; + ax25_rt->digipeat->ndigi = route.digi_count; + for (i = 0; i < route.digi_count; i++) { + ax25_rt->digipeat->repeated[i] = 0; + ax25_rt->digipeat->calls[i] = route.digi_addr[i]; + } + } + return 0; + } + } + if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL) + return -ENOMEM; + ax25_rt->callsign = route.dest_addr; + ax25_rt->dev = dev; + ax25_rt->digipeat = NULL; + ax25_rt->ip_mode = ' '; + if (route.digi_count != 0) { + if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { + kfree_s(ax25_rt, sizeof(struct ax25_route)); + return -ENOMEM; + } + ax25_rt->digipeat->lastrepeat = 0; + ax25_rt->digipeat->ndigi = route.digi_count; + for (i = 0; i < route.digi_count; i++) { + ax25_rt->digipeat->repeated[i] = 0; + ax25_rt->digipeat->calls[i] = route.digi_addr[i]; + } + } + save_flags(flags); + cli(); + ax25_rt->next = ax25_route; + ax25_route = ax25_rt; + restore_flags(flags); + break; + + case SIOCDELRT: + if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) + return err; + copy_from_user(&route, arg, sizeof(route)); + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) + return -EINVAL; + ax25_rt = ax25_route; + while (ax25_rt != NULL) { + s = ax25_rt; + ax25_rt = ax25_rt->next; + if (s->dev == dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { + if (ax25_route == s) { + ax25_route = s->next; + if (s->digipeat != NULL) + kfree_s((void *)s->digipeat, sizeof(ax25_digi)); + kfree_s((void *)s, (sizeof *s)); + } else { + for (t = ax25_route; t != NULL; t = t->next) { + if (t->next == s) { + t->next = s->next; + if (s->digipeat != NULL) + kfree_s((void *)s->digipeat, sizeof(ax25_digi)); + kfree_s((void *)s, sizeof(*s)); + break; + } + } + } + } + } + break; + + case SIOCAX25OPTRT: + if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0) + return err; + copy_from_user(&rt_option, arg, sizeof(rt_option)); + if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL) + return -EINVAL; + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25_rt->dev == dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) { + switch (rt_option.cmd) { + case AX25_SET_RT_IPMODE: + switch (rt_option.arg) { + case ' ': + case 'D': + case 'V': + ax25_rt->ip_mode = rt_option.arg; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { struct ax25_route *ax25_rt; int len = 0; off_t pos = 0; off_t begin = 0; + char *callsign; + int i; cli(); - len += sprintf(buffer, "callsign dev count time mode\n"); + len += sprintf(buffer, "callsign dev mode digipeaters\n"); for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - len += sprintf(buffer + len, "%-9s %-3s %5d %9ld", - ax2asc(&ax25_rt->callsign), - ax25_rt->dev ? ax25_rt->dev->name : "???", - ax25_rt->n, - ax25_rt->stamp.tv_sec); + if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) + callsign = "default"; + else + callsign = ax2asc(&ax25_rt->callsign); + len += sprintf(buffer + len, "%-9s %-4s", + callsign, + ax25_rt->dev ? ax25_rt->dev->name : "???"); switch (ax25_rt->ip_mode) { case 'V': - case 'v': - len += sprintf(buffer + len, " vc\n"); + len += sprintf(buffer + len, " vc"); break; case 'D': - case 'd': - len += sprintf(buffer + len, " dg\n"); + len += sprintf(buffer + len, " dg"); break; default: - len += sprintf(buffer + len, "\n"); + len += sprintf(buffer + len, " *"); break; } + + if (ax25_rt->digipeat != NULL) + for (i = 0; i < ax25_rt->digipeat->ndigi; i++) + len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i])); + + len += sprintf(buffer + len, "\n"); pos = begin + len; @@ -189,7 +315,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) return len; } -int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length) +int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { ax25_uid_assoc *pt; int len = 0; @@ -225,50 +351,145 @@ int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length) } /* - * Find what interface to use. + * Find AX.25 route */ -int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) +static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev) { + struct ax25_route *ax25_spe_rt = NULL; + struct ax25_route *ax25_def_rt = NULL; struct ax25_route *ax25_rt; - ax25_address *call; + /* + * Bind to the physical interface we heard them on, or the default + * route if none is found; + */ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0) { - /* - * Bind to the physical interface we heard them on. - */ - if ((ax25->device = ax25_rt->dev) == NULL) - continue; - if ((call = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !suser()) - return -EPERM; - call = (ax25_address *)ax25->device->dev_addr; - } - memcpy(&ax25->source_addr, call, sizeof(ax25_address)); - if (ax25->sk != NULL) - ax25->sk->zapped = 0; - - return 0; + if (dev == NULL) { + if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) + ax25_spe_rt = ax25_rt; + if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) + ax25_def_rt = ax25_rt; + } else { + if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) + ax25_spe_rt = ax25_rt; + if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) + ax25_def_rt = ax25_rt; } } - return -EINVAL; + if (ax25_spe_rt != NULL) + return ax25_spe_rt; + + return ax25_def_rt; } /* - * Register the mode of an incoming IP frame. It is assumed that an entry - * already exists in the routing table. + * Adjust path: If you specify a default route and want to connect + * a target on the digipeater path but w/o having a special route + * set before, the path has to be truncated from your target on. */ -void ax25_ip_mode_set(ax25_address *callsign, struct device *dev, char ip_mode) +static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) +{ + int k; + + for (k = 0; k < digipeat->ndigi; k++) { + if (ax25cmp(addr, &digipeat->calls[k]) == 0) + break; + } + + digipeat->ndigi = k; +} + + +/* + * Find which interface to use. + */ +int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) { struct ax25_route *ax25_rt; + ax25_address *call; - for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev) { - ax25_rt->ip_mode = ip_mode; - return; - } + if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) + return -EHOSTUNREACH; + + ax25->device = ax25_rt->dev; + + if ((call = ax25_findbyuid(current->euid)) == NULL) { + if (ax25_uid_policy && !suser()) + return -EPERM; + call = (ax25_address *)ax25->device->dev_addr; + } + + ax25->source_addr = *call; + + if (ax25_rt->digipeat != NULL) { + if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) + return -ENOMEM; + *ax25->digipeat = *ax25_rt->digipeat; + ax25_adjust_path(addr, ax25->digipeat); + } + + if (ax25->sk != NULL) + ax25->sk->zapped = 0; + + return 0; +} + +/* + * dl1bke 960117: build digipeater path + * dl1bke 960301: use the default route if it exists + */ +void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev) +{ + struct ax25_route *ax25_rt; + + if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) + return; + + if (ax25_rt->digipeat == NULL) + return; + + if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) + return; + + ax25->device = ax25_rt->dev; + *ax25->digipeat = *ax25_rt->digipeat; + ax25_adjust_path(addr, ax25->digipeat); +} + +void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev) +{ + struct ax25_route *ax25_rt; + ax25_digi digipeat; + ax25_address src, dest; + unsigned char *bp; + int len; + + skb_pull(skb, 1); /* skip KISS command */ + + if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) + return; + + if (ax25_rt->digipeat == NULL) + return; + + digipeat = *ax25_rt->digipeat; + + ax25_adjust_path(addr, &digipeat); + + len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN; + + if (skb_headroom(skb) < len) { + printk(KERN_CRIT "ax25_dg_build_path: not enough headroom for digis in skb\n"); + return; } + + memcpy(&dest, skb->data , AX25_ADDR_LEN); + memcpy(&src, skb->data + 7, AX25_ADDR_LEN); + + bp = skb_push(skb, len); + + build_ax25_addr(bp, &src, &dest, ax25_rt->digipeat, C_COMMAND, MODULUS); } /* @@ -285,4 +506,102 @@ char ax25_ip_mode_get(ax25_address *callsign, struct device *dev) return ' '; } +/* + * Wow, a bit of data hiding. Is this C++ or what ? + */ +int ax25_dev_get_value(struct device *dev, int valueno) +{ + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) + if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev) + return ax25_device[i].values[valueno]; + + printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n"); + + return 0; +} + +/* + * This is called when an interface is brought up. These are + * reasonable defaults. + */ +void ax25_dev_device_up(struct device *dev) +{ + struct ax25_dev *ax25_dev = NULL; + int i; + + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_device[i].dev == NULL) { + ax25_dev = ax25_device + i; + break; + } + } + + if (ax25_dev == NULL) { + printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n"); + return; + } + + ax25_unregister_sysctl(); + + sprintf(ax25_dev->name, "%s.parms", dev->name); + + ax25_dev->dev = dev; + + ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; + ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; + ax25_dev->values[AX25_VALUES_TEXT] = AX25_DEF_TEXT; + ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; + ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; + ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; + ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; + ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; + ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; + ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; + ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; + ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; + ax25_dev->values[AX25_VALUES_DIGI] = AX25_DEF_DIGI; + ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; + ax25_dev->values[AX25_VALUES_MAXQUEUE] = AX25_DEF_MAXQUEUE; + + ax25_register_sysctl(); +} + +void ax25_dev_device_down(struct device *dev) +{ + int i; + + ax25_unregister_sysctl(); + + for (i = 0; i < AX25_MAX_DEVICES; i++) + if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev) + ax25_device[i].dev = NULL; + + ax25_register_sysctl(); +} + +#ifdef MODULE + +/* + * Free all memory associated with routing and device structures. + */ +void ax25_rt_free(void) +{ + struct ax25_route *s, *ax25_rt = ax25_route; + + while (ax25_rt != NULL) { + s = ax25_rt; + ax25_rt = ax25_rt->next; + + if (s->digipeat != NULL) + kfree_s(s->digipeat, sizeof(ax25_digi)); + + kfree_s(s, sizeof(struct ax25_route)); + } +} + +#endif + #endif + diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index 2530346e5..071043d1e 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,10 +1,10 @@ /* - * AX.25 release 029 + * AX.25 release 033 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 1.3.61 or higher/ NET3.029 * * This module: * This module is free software; you can redistribute it and/or @@ -21,10 +21,21 @@ * History * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. Removed * old BSD code. + * AX.25 030 Jonathan(G4KLX) Added support for extended AX.25. + * Added fragmentation support. + * Darryl(G7LED) Added function ax25_requeue_frames() to split + * it up from ax25_frames_acked(). + * AX.25 031 Joerg(DL1BKE) DAMA needs KISS Fullduplex ON/OFF. + * Thus we have ax25_kiss_cmd() now... ;-) + * Dave Brown(N2RJT) + * Killed a silly bug in the DAMA code. + * Joerg(DL1BKE) Found the real bug in ax25.h, sri. + * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of + * enqueued buffers of a socket.. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -40,16 +51,16 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <net/sock.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/interrupt.h> /* - * This routine purges the input queue of frames. + * This routine purges all the queues of frames. */ -void ax25_clear_tx_queue(ax25_cb *ax25) +void ax25_clear_queues(ax25_cb *ax25) { struct sk_buff *skb; @@ -62,6 +73,14 @@ void ax25_clear_tx_queue(ax25_cb *ax25) skb->free = 1; kfree_skb(skb, FREE_WRITE); } + + while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL) { + kfree_skb(skb, FREE_READ); + } + + while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL) { + kfree_skb(skb, FREE_READ); + } } /* @@ -71,7 +90,7 @@ void ax25_clear_tx_queue(ax25_cb *ax25) */ void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) { - struct sk_buff *skb, *skb_prev = NULL; + struct sk_buff *skb; /* * Remove all the ack-ed frames from the ack queue. @@ -81,9 +100,20 @@ void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) skb = skb_dequeue(&ax25->ack_queue); skb->free = 1; kfree_skb(skb, FREE_WRITE); - ax25->va = (ax25->va + 1) % MODULUS; + ax25->va = (ax25->va + 1) % ax25->modulus; + if (ax25->dama_slave) + ax25->n2count = 0; } } +} + +/* Maybe this should be your ax25_invoke_retransmission(), which appears + * to be used but not do anything. ax25_invoke_retransmission() used to + * be in AX 0.29, but has now gone in 0.30. + */ +void ax25_requeue_frames(ax25_cb *ax25) +{ + struct sk_buff *skb, *skb_prev = NULL; /* * Requeue all the un-ack-ed frames on the output queue to be picked @@ -109,7 +139,7 @@ int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) while (vc != ax25->vs) { if (nr == vc) return 1; - vc = (vc + 1) % MODULUS; + vc = (vc + 1) % ax25->modulus; } if (nr == ax25->vs) return 1; @@ -117,16 +147,51 @@ int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) return 0; } -int ax25_decode(unsigned char *frame) +/* + * This routine is the centralised routine for parsing the control + * information for the different frame formats. + */ +int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) { + unsigned char *frame; int frametype = ILLEGAL; - if ((frame[0] & S) == 0) - frametype = I; /* I frame - carries NR/NS/PF */ - else if ((frame[0] & U) == 1) /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - else if ((frame[0] & U) == 3) /* U frame - take out PF */ - frametype = frame[0] & ~PF; + frame = skb->data; + *ns = *nr = *pf = 0; + + if (ax25->modulus == MODULUS) { + if ((frame[0] & S) == 0) { + frametype = I; /* I frame - carries NR/NS/PF */ + *ns = (frame[0] >> 1) & 0x07; + *nr = (frame[0] >> 5) & 0x07; + *pf = frame[0] & PF; + } else if ((frame[0] & U) == 1) { /* S frame - take out PF/NR */ + frametype = frame[0] & 0x0F; + *nr = (frame[0] >> 5) & 0x07; + *pf = frame[0] & PF; + } else if ((frame[0] & U) == 3) { /* U frame - take out PF */ + frametype = frame[0] & ~PF; + *pf = frame[0] & PF; + } + skb_pull(skb, 1); + } else { + if ((frame[0] & S) == 0) { + frametype = I; /* I frame - carries NR/NS/PF */ + *ns = (frame[0] >> 1) & 0x7F; + *nr = (frame[1] >> 1) & 0x7F; + *pf = frame[1] & EPF; + skb_pull(skb, 2); + } else if ((frame[0] & U) == 1) { /* S frame - take out PF/NR */ + frametype = frame[0] & 0x0F; + *nr = (frame[1] >> 1) & 0x7F; + *pf = frame[1] & EPF; + skb_pull(skb, 2); + } else if ((frame[0] & U) == 3) { /* U frame - take out PF */ + frametype = frame[0] & ~PF; + *pf = frame[0] & PF; + skb_pull(skb, 1); + } + } return frametype; } @@ -136,38 +201,46 @@ int ax25_decode(unsigned char *frame) * command or response for the remote machine ( eg. RR, UA etc. ). * Only supervisory or unnumbered frames are processed. */ -void ax25_send_control(ax25_cb *ax25, int frametype, int type) +void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) { struct sk_buff *skb; unsigned char *dptr; - int len; struct device *dev; if ((dev = ax25->device) == NULL) return; /* Route died */ - if ((skb = alloc_skb(16 + 1 + size_ax25_addr(ax25->digipeat), GFP_ATOMIC)) == NULL) + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL) return; + skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat)); + if (ax25->sk != NULL) { skb->sk = ax25->sk; - ax25->sk->wmem_alloc += skb->mem_len; + atomic_add(skb->truesize, &ax25->sk->wmem_alloc); } - dptr = skb->data; - - dptr += 1 + size_ax25_addr(ax25->digipeat); /* KISS byte & 2 calls */ - /* Assume a response - address structure for DTE */ - len = 1; /* Normal size */ - - if ((frametype & U) == S) /* S frames carry NR */ - frametype |= (ax25->vr << 5); - - *dptr = frametype; + if (ax25->modulus == MODULUS) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? PF : 0; + if ((frametype & U) == S) /* S frames carry NR */ + *dptr |= (ax25->vr << 5); + } else { + if ((frametype & U) == U) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? PF : 0; + } else { + dptr = skb_put(skb, 2); + dptr[0] = frametype; + dptr[1] = (ax25->vr << 1); + dptr[1] |= (poll_bit) ? EPF : 0; + } + } skb->free = 1; - skb->len = len + size_ax25_addr(ax25->digipeat) + 1; ax25_transmit_buffer(ax25, skb, type); } @@ -175,38 +248,40 @@ void ax25_send_control(ax25_cb *ax25, int frametype, int type) /* * Send a 'DM' to an unknown connection attempt, or an invalid caller. * - * Note: src here is the sender, thus its the target of the DM + * Note: src here is the sender, thus it's the target of the DM */ void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) { struct sk_buff *skb; char *dptr; ax25_digi retdigi; - int len = 2 + size_ax25_addr(digi); - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) + if (dev == NULL) + return; + + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(digi) + 1, GFP_ATOMIC)) == NULL) return; /* Next SABM will get DM'd */ - skb->len = len; + skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(digi)); ax25_digi_invert(digi, &retdigi); - dptr = skb->data + 1 + size_ax25_addr(digi); + dptr = skb_put(skb, 1); skb->sk = NULL; - *dptr = DM; + *dptr = DM | PF; - if (dev == NULL) - return; + /* + * Do the address ourselves + */ - dptr = skb->data; - *dptr++ = 0; - dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE); + dptr = skb_push(skb, size_ax25_addr(digi)); + dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE, MODULUS); skb->arp = 1; skb->free = 1; - dev_queue_xmit(skb, dev, SOPRI_NORMAL); + ax25_queue_xmit(skb, dev, SOPRI_NORMAL); } /* @@ -214,25 +289,39 @@ void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, a */ unsigned short ax25_calculate_t1(ax25_cb *ax25) { - int t, n; - - for (t = 2, n = 0; n < ax25->n2count; n++) - t *= 2; - + int n, t = 2; + + if (ax25->backoff) { + for (n = 0; n < ax25->n2count; n++) + t *= 2; + + if (t > 8) t = 8; + } + return t * ax25->rtt; } /* - * Calculate the r Round Trip Time + * Calculate the Round Trip Time */ void ax25_calculate_rtt(ax25_cb *ax25) { - if (ax25->n2count == 0) + if (ax25->t1timer > 0 && ax25->n2count == 0) ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10; - /* Don't go below one second */ - if (ax25->rtt < 1 * PR_SLOWHZ) - ax25->rtt = 1 * PR_SLOWHZ; +#ifdef AX25_T1CLAMPLO + /* Don't go below one tenth of a second */ + if (ax25->rtt < (AX25_T1CLAMPLO)) + ax25->rtt = (AX25_T1CLAMPLO); +#else /* Failsafe - some people might have sub 1/10th RTTs :-) **/ + if (ax25->rtt == 0) + ax25->rtt = PR_SLOWHZ; +#endif +#ifdef AX25_T1CLAMPHI + /* OR above clamped seconds **/ + if (ax25->rtt > (AX25_T1CLAMPHI)) + ax25->rtt = (AX25_T1CLAMPHI); +#endif } /* @@ -244,8 +333,7 @@ void ax25_calculate_rtt(ax25_cb *ax25) * Given an AX.25 address pull of to, from, digi list, command/response and the start of data * */ - -unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags) +unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama) { int d = 0; @@ -262,21 +350,25 @@ unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, a } } + if (dama != NULL) + *dama = ~buf[13] & DAMA_FLAG; + /* Copy to, from */ - if (dest != NULL) memcpy(dest, buf + 0, 7); - if (src != NULL) memcpy(src, buf + 7, 7); - buf += 14; - len -= 14; + if (dest != NULL) + memcpy(dest, buf + 0, AX25_ADDR_LEN); + if (src != NULL) + memcpy(src, buf + 7, AX25_ADDR_LEN); + buf += 2 * AX25_ADDR_LEN; + len -= 2 * AX25_ADDR_LEN; digi->lastrepeat = -1; digi->ndigi = 0; - while (!(buf[-1] & LAPB_E)) - { - if (d >= 6) return NULL; /* Max of 6 digis */ + while (!(buf[-1] & LAPB_E)) { + if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ if (len < 7) return NULL; /* Short packet */ if (digi != NULL) { - memcpy(&digi->calls[d], buf, 7); + memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); digi->ndigi = d + 1; if (buf[6] & AX25_REPEATED) { digi->repeated[d] = 1; @@ -286,8 +378,8 @@ unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, a } } - buf += 7; - len -= 7; + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; d++; } @@ -297,50 +389,54 @@ unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, a /* * Assemble an AX.25 header from the bits */ - -int build_ax25_addr(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag) +int build_ax25_addr(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus) { int len = 0; int ct = 0; - memcpy(buf, dest, 7); - - if (flag != C_COMMAND && flag != C_RESPONSE) - printk("build_ax25_addr: Bogus flag %d\n!", flag); + memcpy(buf, dest, AX25_ADDR_LEN); buf[6] &= ~(LAPB_E | LAPB_C); - buf[6] |= SSID_SPARE; + buf[6] |= SSSID_SPARE; if (flag == C_COMMAND) buf[6] |= LAPB_C; - buf += 7; - len += 7; - memcpy(buf, src, 7); + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + + memcpy(buf, src, AX25_ADDR_LEN); buf[6] &= ~(LAPB_E | LAPB_C); - buf[6] |= SSID_SPARE; + buf[6] &= ~SSSID_SPARE; + + if (modulus == MODULUS) { + buf[6] |= SSSID_SPARE; + } else { + buf[6] |= ESSID_SPARE; + } if (flag == C_RESPONSE) buf[6] |= LAPB_C; + /* * Fast path the normal digiless path */ if (d == NULL || d->ndigi == 0) { buf[6] |= LAPB_E; - return 14; + return 2 * AX25_ADDR_LEN; } - buf += 7; - len += 7; + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; while (ct < d->ndigi) { - memcpy(buf, &d->calls[ct], 7); + memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); if (d->repeated[ct]) buf[6] |= AX25_REPEATED; else buf[6] &= ~AX25_REPEATED; buf[6] &= ~LAPB_E; - buf[6] |= SSID_SPARE; + buf[6] |= SSSID_SPARE; - buf += 7; - len += 7; + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; ct++; } @@ -352,15 +448,14 @@ int build_ax25_addr(unsigned char *buf, ax25_address *src, ax25_address *dest, a int size_ax25_addr(ax25_digi *dp) { if (dp == NULL) - return 14; + return 2 * AX25_ADDR_LEN; - return 14 + (7 * dp->ndigi); + return AX25_ADDR_LEN * (2 + dp->ndigi); } /* * Reverse Digipeat List. May not pass both parameters as same struct */ - void ax25_digi_invert(ax25_digi *in, ax25_digi *out) { int ct = 0; @@ -380,4 +475,100 @@ void ax25_digi_invert(ax25_digi *in, ax25_digi *out) out->lastrepeat = 0; } +/* + * count the number of buffers on a list belonging to the same + * socket as skb + */ + +static int ax25_list_length(struct sk_buff_head *list, struct sk_buff *skb) +{ + int count = 0; + long flags; + struct sk_buff *skbq; + + save_flags(flags); + cli(); + + if (list == NULL) { + restore_flags(flags); + return 0; + } + + for (skbq = list->next; skbq != (struct sk_buff *)list; skbq = skbq->next) + if (skb->sk == skbq->sk) + count++; + + restore_flags(flags); + return count; +} + +/* + * count the number of buffers of one socket on the write/ack-queue + */ + +int ax25_queue_length(ax25_cb *ax25, struct sk_buff *skb) +{ + return ax25_list_length(&ax25->write_queue, skb) + ax25_list_length(&ax25->ack_queue, skb); +} + +/* + * :::FIXME::: + * This is ****NOT**** the right approach. Not all drivers do kiss. We + * need a driver level request to switch duplex mode, that does either + * SCC changing, PI config or KISS as required. + * + * Not to mention this request isn't currently reliable. + */ + +void ax25_kiss_cmd(ax25_cb *ax25, unsigned char cmd, unsigned char param) +{ + struct sk_buff *skb; + unsigned char *p; + + if (ax25->device == NULL) + return; + + if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) + return; + + skb->free = 1; + skb->arp = 1; + + if (ax25->sk != NULL) { + skb->sk = ax25->sk; + atomic_add(skb->truesize, &ax25->sk->wmem_alloc); + } + + skb->protocol = htons(ETH_P_AX25); + + p = skb_put(skb, 2); + + *p++=cmd; + *p =param; + + dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL); +} + +void ax25_dama_on(ax25_cb *ax25) +{ + if (ax25_dev_is_dama_slave(ax25->device) == 0) { + if (ax25->sk != NULL && ax25->sk->debug) + printk("ax25_dama_on: DAMA on\n"); + ax25_kiss_cmd(ax25, 5, 1); + } +} + +void ax25_dama_off(ax25_cb *ax25) +{ + if (ax25->dama_slave == 0) + return; + + ax25->dama_slave = 0; + if (ax25_dev_is_dama_slave(ax25->device) == 0) { + if (ax25->sk != NULL && ax25->sk->debug) + printk("ax25_dama_off: DAMA off\n"); + ax25_kiss_cmd(ax25, 5, 0); + } +} + #endif diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index daa3bd657..f6ce6e00b 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 029 + * AX.25 release 033 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -17,10 +17,13 @@ * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the * sock structure. * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. + * AX.25 031 Joerg(DL1BKE) Added DAMA support + * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug + * AX.25 033 Jonathan(G4KLX) Modularisation functions. */ #include <linux/config.h> -#ifdef CONFIG_AX25 +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> @@ -36,14 +39,11 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <net/sock.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/interrupt.h> -#ifdef CONFIG_NETROM -#include <net/netrom.h> -#endif static void ax25_timer(unsigned long); @@ -52,8 +52,8 @@ static void ax25_timer(unsigned long); */ void ax25_set_timer(ax25_cb *ax25) { - unsigned long flags; - + unsigned long flags; + save_flags(flags); cli(); del_timer(&ax25->timer); @@ -63,7 +63,7 @@ void ax25_set_timer(ax25_cb *ax25) ax25->timer.data = (unsigned long)ax25; ax25->timer.function = &ax25_timer; - ax25->timer.expires = 10; + ax25->timer.expires = jiffies + 10; add_timer(&ax25->timer); } @@ -78,14 +78,14 @@ static void ax25_reset_timer(ax25_cb *ax25) ax25->timer.data = (unsigned long)ax25; ax25->timer.function = &ax25_timer; - ax25->timer.expires = 10; + ax25->timer.expires = jiffies + 10; add_timer(&ax25->timer); } /* * AX.25 TIMER * - * This routine is called every 500ms. Decrement timer by this + * This routine is called every 100ms. Decrement timer by this * amount - if expired then process the event. */ static void ax25_timer(unsigned long param) @@ -95,8 +95,8 @@ static void ax25_timer(unsigned long param) switch (ax25->state) { case AX25_STATE_0: /* Magic here: If we listen() and a new link dies before it - is accepted() it isnt 'dead' so doesnt get removed. */ - if ((ax25->sk != NULL && ax25->sk->dead) || ax25->sk == NULL) { + is accepted() it isn't 'dead' so doesn't get removed. */ + if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { del_timer(&ax25->timer); ax25_destroy_socket(ax25); return; @@ -111,7 +111,8 @@ static void ax25_timer(unsigned long param) if (ax25->sk != NULL) { if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & OWN_RX_BUSY_CONDITION)) { ax25->condition &= ~OWN_RX_BUSY_CONDITION; - ax25_send_control(ax25, RR, C_RESPONSE); + if (!ax25->dama_slave) + ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE); ax25->condition &= ~ACK_PENDING_CONDITION; break; } @@ -119,7 +120,8 @@ static void ax25_timer(unsigned long param) /* * Check for frames to transmit. */ - ax25_kick(ax25); + if (!ax25->dama_slave) + ax25_kick(ax25); break; default: @@ -130,12 +132,36 @@ static void ax25_timer(unsigned long param) if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) { if (ax25->condition & ACK_PENDING_CONDITION) { ax25->condition &= ~ACK_PENDING_CONDITION; - ax25_enquiry_response(ax25); + if (!ax25->dama_slave) + ax25_timeout_response(ax25); } } } if (ax25->t3timer > 0 && --ax25->t3timer == 0) { + /* dl1bke 960114: T3 expires and we are in DAMA mode: */ + /* send a DISC and abort the connection */ + if (ax25->dama_slave) { + ax25_link_failed(&ax25->dest_addr, ax25->device); + ax25_clear_queues(ax25); + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); + + ax25->state = AX25_STATE_0; + if (ax25->sk != NULL) { + if (ax25->sk->debug) + printk(KERN_DEBUG "AX.25 T3 Timeout\n"); + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ETIMEDOUT; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } + + ax25_reset_timer(ax25); + return; + } + if (ax25->state == AX25_STATE_3) { ax25->n2count = 0; ax25_transmit_enquiry(ax25); @@ -143,77 +169,148 @@ static void ax25_timer(unsigned long param) } ax25->t3timer = ax25->t3; } + + if (ax25->idletimer > 0 && --ax25->idletimer == 0) { + /* dl1bke 960228: close the connection when IDLE expires */ + /* similar to DAMA T3 timeout but with */ + /* a "clean" disconnect of the connection */ + ax25_clear_queues(ax25); + + ax25->n2count = 0; + if (!ax25->dama_slave) { + ax25->t3timer = 0; + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); + } else { + ax25->t3timer = ax25->t3; + } + + /* state 1 or 2 should not happen, but... */ + + if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2) + ax25->state = AX25_STATE_0; + else + ax25->state = AX25_STATE_2; + + ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + ax25->sk->destroy = 1; + } + } + + /* dl1bke 960114: DAMA T1 timeouts are handled in ax25_dama_slave_transmit */ + /* nevertheless we have to re-enqueue the timer struct... */ + if (ax25->t1timer == 0 || --ax25->t1timer > 0) { ax25_reset_timer(ax25); return; } + if (!ax25_dev_is_dama_slave(ax25->device)) { + if (ax25->dama_slave) + ax25->dama_slave = 0; + ax25_t1_timeout(ax25); + } +} + + +/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC + * within the poll of any connected channel. Remember + * that we are not allowed to send anything unless we + * get polled by the Master. + * + * Thus we'll have to do parts of our T1 handling in + * ax25_enquiry_response(). + */ +void ax25_t1_timeout(ax25_cb * ax25) +{ switch (ax25->state) { case AX25_STATE_1: if (ax25->n2count == ax25->n2) { -#ifdef CONFIG_NETROM - nr_link_failed(&ax25->dest_addr, ax25->device); -#endif - ax25_clear_tx_queue(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + if (ax25->modulus == MODULUS) { + ax25_link_failed(&ax25->dest_addr, ax25->device); + ax25_clear_queues(ax25); + ax25->state = AX25_STATE_0; + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ETIMEDOUT; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } + } else { + ax25->modulus = MODULUS; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25->n2count = 0; + ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND); } } else { ax25->n2count++; - ax25_send_control(ax25, SABM | PF, C_COMMAND); + if (ax25->modulus == MODULUS) { + ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND); + } else { + ax25_send_control(ax25, SABME, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND); + } } break; case AX25_STATE_2: if (ax25->n2count == ax25->n2) { -#ifdef CONFIG_NETROM - nr_link_failed(&ax25->dest_addr, ax25->device); -#endif - ax25_clear_tx_queue(ax25); + ax25_link_failed(&ax25->dest_addr, ax25->device); + ax25_clear_queues(ax25); ax25->state = AX25_STATE_0; + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); + if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ETIMEDOUT; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } } else { ax25->n2count++; - ax25_send_control(ax25, DISC | PF, C_COMMAND); + if (!ax25_dev_is_dama_slave(ax25->device)) + ax25_send_control(ax25, DISC, POLLON, C_COMMAND); } break; case AX25_STATE_3: ax25->n2count = 1; - ax25_transmit_enquiry(ax25); + if (!ax25->dama_slave) + ax25_transmit_enquiry(ax25); ax25->state = AX25_STATE_4; break; case AX25_STATE_4: if (ax25->n2count == ax25->n2) { -#ifdef CONFIG_NETROM - nr_link_failed(&ax25->dest_addr, ax25->device); -#endif - ax25_clear_tx_queue(ax25); - ax25_send_control(ax25, DM | PF, C_RESPONSE); + ax25_link_failed(&ax25->dest_addr, ax25->device); + ax25_clear_queues(ax25); + ax25_send_control(ax25, DM, POLLON, C_RESPONSE); ax25->state = AX25_STATE_0; if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; + if (ax25->sk->debug) + printk(KERN_DEBUG "AX.25 link Failure\n"); + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = ETIMEDOUT; + ax25->sk->shutdown |= SEND_SHUTDOWN; if (!ax25->sk->dead) ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; + ax25->sk->dead = 1; } } else { ax25->n2count++; - ax25_transmit_enquiry(ax25); + if (!ax25->dama_slave) + ax25_transmit_enquiry(ax25); } break; } @@ -223,4 +320,228 @@ static void ax25_timer(unsigned long param) ax25_set_timer(ax25); } +/************************************************************************/ +/* Module support functions follow. */ +/************************************************************************/ + +static struct protocol_struct { + struct protocol_struct *next; + unsigned int pid; + int (*func)(struct sk_buff *, ax25_cb *); +} *protocol_list = NULL; + +static struct linkfail_struct { + struct linkfail_struct *next; + void (*func)(ax25_address *, struct device *); +} *linkfail_list = NULL; + +static struct listen_struct { + struct listen_struct *next; + ax25_address callsign; + struct device *dev; +} *listen_list = NULL; + +int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) +{ + struct protocol_struct *protocol; + unsigned long flags; + + if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) + return 0; +#ifdef CONFIG_INET + if (pid == AX25_P_IP || pid == AX25_P_ARP) + return 0; +#endif + if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) + return 0; + + protocol->pid = pid; + protocol->func = func; + + save_flags(flags); + cli(); + + protocol->next = protocol_list; + protocol_list = protocol; + + restore_flags(flags); + + return 1; +} + +void ax25_protocol_release(unsigned int pid) +{ + struct protocol_struct *s, *protocol = protocol_list; + unsigned long flags; + + if (protocol == NULL) + return; + + save_flags(flags); + cli(); + + if (protocol->pid == pid) { + protocol_list = protocol->next; + restore_flags(flags); + kfree_s(protocol, sizeof(struct protocol_struct)); + return; + } + + while (protocol != NULL && protocol->next != NULL) { + if (protocol->next->pid == pid) { + s = protocol->next; + protocol->next = protocol->next->next; + restore_flags(flags); + kfree_s(s, sizeof(struct protocol_struct)); + return; + } + + protocol = protocol->next; + } + + restore_flags(flags); +} + +int ax25_linkfail_register(void (*func)(ax25_address *, struct device *)) +{ + struct linkfail_struct *linkfail; + unsigned long flags; + + if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) + return 0; + + linkfail->func = func; + + save_flags(flags); + cli(); + + linkfail->next = linkfail_list; + linkfail_list = linkfail; + + restore_flags(flags); + + return 1; +} + +void ax25_linkfail_release(void (*func)(ax25_address *, struct device *)) +{ + struct linkfail_struct *s, *linkfail = linkfail_list; + unsigned long flags; + + if (linkfail == NULL) + return; + + save_flags(flags); + cli(); + + if (linkfail->func == func) { + linkfail_list = linkfail->next; + restore_flags(flags); + kfree_s(linkfail, sizeof(struct linkfail_struct)); + return; + } + + while (linkfail != NULL && linkfail->next != NULL) { + if (linkfail->next->func == func) { + s = linkfail->next; + linkfail->next = linkfail->next->next; + restore_flags(flags); + kfree_s(s, sizeof(struct linkfail_struct)); + return; + } + + linkfail = linkfail->next; + } + + restore_flags(flags); +} + +int ax25_listen_register(ax25_address *callsign, struct device *dev) +{ + struct listen_struct *listen; + unsigned long flags; + + if (ax25_listen_mine(callsign, dev)) + return 0; + + if ((listen = (struct listen_struct *)kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL) + return 0; + + listen->callsign = *callsign; + listen->dev = dev; + + save_flags(flags); + cli(); + + listen->next = listen_list; + listen_list = listen; + + restore_flags(flags); + + return 1; +} + +void ax25_listen_release(ax25_address *callsign, struct device *dev) +{ + struct listen_struct *s, *listen = listen_list; + unsigned long flags; + + if (listen == NULL) + return; + + save_flags(flags); + cli(); + + if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { + listen_list = listen->next; + restore_flags(flags); + kfree_s(listen, sizeof(struct listen_struct)); + return; + } + + while (listen != NULL && listen->next != NULL) { + if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { + s = listen->next; + listen->next = listen->next->next; + restore_flags(flags); + kfree_s(s, sizeof(struct listen_struct)); + return; + } + + listen = listen->next; + } + + restore_flags(flags); +} + +int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) +{ + struct protocol_struct *protocol; + + for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) + if (protocol->pid == pid) + return protocol->func; + + return NULL; +} + +int ax25_listen_mine(ax25_address *callsign, struct device *dev) +{ + struct listen_struct *listen; + + for (listen = listen_list; listen != NULL; listen = listen->next) + if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) + return 1; + + return 0; +} + +void ax25_link_failed(ax25_address *callsign, struct device *dev) +{ + struct linkfail_struct *linkfail; + + for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) + (linkfail->func)(callsign, dev); +} + #endif diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c new file mode 100644 index 000000000..302d210f8 --- /dev/null +++ b/net/ax25/sysctl_net_ax25.c @@ -0,0 +1,60 @@ +/* -*- linux-c -*- + * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem. + * + * Begun April 1, 1996, Mike Shaver. + * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS] + */ + +#include <linux/mm.h> +#include <linux/sysctl.h> +#include <net/ax25.h> + +static int min_ax25[] = {0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 0x00}; +static int max_ax25[] = {1, 1, 1, 1, 1, 7, 63, 30 * PR_SLOWHZ, 20 * PR_SLOWHZ, + 3600 * PR_SLOWHZ, 65535 * PR_SLOWHZ, 31, 512, 20, 0x03}; + +static struct ctl_table_header *ax25_table_header; + +static ctl_table ax25_table[AX25_MAX_DEVICES + 1]; + +static ctl_table ax25_dir_table[] = { + {NET_AX25, "ax25", NULL, 0, 0555, ax25_table}, + {0} +}; + +static ctl_table ax25_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, ax25_dir_table}, + {0} +}; + +void ax25_register_sysctl(void) +{ + int i, n; + + memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table)); + + for (n = 0, i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_device[i].dev != NULL) { + ax25_table[n].ctl_name = n + 1; + ax25_table[n].procname = ax25_device[i].name; + ax25_table[n].data = &ax25_device[i].values; + ax25_table[n].maxlen = AX25_MAX_VALUES * sizeof(int); + ax25_table[n].mode = 0644; + ax25_table[n].child = NULL; + ax25_table[n].proc_handler = &proc_dointvec_minmax; + ax25_table[n].strategy = &sysctl_intvec; + ax25_table[n].de = NULL; + ax25_table[n].extra1 = &min_ax25; + ax25_table[n].extra2 = &max_ax25; + n++; + } + } + + ax25_table_header = register_sysctl_table(ax25_root_table, 1); +} + +void ax25_unregister_sysctl(void) +{ + unregister_sysctl_table(ax25_table_header); +} |