summaryrefslogtreecommitdiffstats
path: root/net/ax25/ax25_in.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ax25/ax25_in.c')
-rw-r--r--net/ax25/ax25_in.c984
1 files changed, 655 insertions, 329 deletions
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 7c1484d6e..1e51005c4 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -1,68 +1,333 @@
/*
- * AX.25 release 037
+ * ax25_in.c: Routines for processing of incoming frames (NEW-AX.25)
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS), HaJo (DD8NE)
*
- * This module:
- * This module is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
- * 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.
- * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
- * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file.
- * Joerg(DL1BKE) Fixed DAMA Slave.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Thomas(DL9SAU) Fixed missing initialization of skb->protocol.
+ * License: This module is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
-#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <linux/netfilter.h>
+#include <linux/netdevice.h>
#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <net/arp.h> /* For arp_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_route.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_netlink.h"
+
+static int ax25_rx_fragment(ax25_cb*, struct sk_buff*);
+static void ax25_decode(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+static struct sock* ax25_find_socket(ax25_address*, ax25_address*, struct net_device*, int);
+static int ax25_match_addr(ax25_address*, struct net_device*);
+/* static void ax25_send_to_raw(struct sock*, struct sk_buff*, int); */
+static int ax25_rcv(struct sk_buff*, struct net_device*, struct packet_type*);
+
+/*
+ * Packet type registration data
+ */
+struct packet_type ax25_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_AX25),*/
+ 0, /* copy */
+ ax25_rcv,
+ NULL,
+ NULL,
+};
+
+/*
+ * Higher level upcall for a LAPB frame
+ */
+int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo* pkt_info)
+{
+ struct net_device *dev = skb->dev;
+
+ ax25_decode(ax25, skb, pkt_info);
+ ax25_dev_set_dama(dev, pkt_info->dama);
+ if (pkt_info->pf && (pkt_info->cmdrsp == AX25_COMMAND))
+ AX25_PTR(dev)->dama_polled = 1;
+ return ax25_lapb_table[ax25->state](ax25, skb, pkt_info);
+}
+
+/*
+ * Resequencer
+ */
+static inline unsigned int ax25_csum_skb(struct sk_buff *skb)
+{
+ unsigned char *cp = skb->data;
+ unsigned int csum = 0;
+
+ while (cp < (skb->data + skb->len))
+ csum += *cp++;
+
+ return csum;
+}
+
+void ax25_reseq_update(ax25_cb* ax25, struct sk_buff *skb, int ns)
+{
+ if (ax25->reseq[ns].skb != NULL)
+ return;
+
+ ax25->reseq[ns].csum = ax25_csum_skb(skb);
+}
+
+int ax25_reseq_in(ax25_cb *ax25, struct sk_buff *skb, int ns, int pf)
+{
+ unsigned int csum;
+
+ /*
+ * if either
+ * - the poll flag is set
+ * - the slot is not empty
+ * - we've already seen this frame
+ * drop it
+ */
+
+ if (pf || ax25->reseq[ns].skb != NULL ||
+ ax25->reseq[ns].csum == (csum = ax25_csum_skb(skb)))
+ return 0;
+
+ /*
+ * otherwise queue it
+ */
+ ax25->reseq[ns].skb = skb;
+ ax25->reseq[ns].csum = csum;
+
+ return 1;
+}
+
+void ax25_reseq_out(ax25_cb *ax25)
+{
+ struct sk_buff *skb;
+
+ while ((skb = ax25->reseq[ax25->vr].skb) != NULL && ax25_rx_iframe(ax25, skb)) {
+ ax25->reseq[ax25->vr].skb = NULL;
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ }
+}
+
+/*
+ * This is where all valid I frames are sent to, to be dispatched to
+ * whichever protocol requires them.
+ */
+int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+{
+ ax25_cb *peer;
+ unsigned char pid = *skb->data;
+ struct sock *sk = NULL;
+ int queued = 0;
+
+ int (*func)(struct sk_buff *, ax25_cb *);
+
+ if (ax25->device) {
+ AX25_PTR(ax25->device)->rx_iframes++;
+ AX25_PTR(ax25->device)->rx_bytes += skb->len;
+ }
+
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+
+ /*
+ * don't take any more data if we're about to close
+ * the connection.
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ ax25_set_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ kfree_skb(skb);
+ return 1;
+ }
+
+ /*
+ * we're only digipeating, don't care about pid
+ * and idle timeouts.
+ */
+ if ((peer = ax25->peer) != NULL) {
+ int max_qlen;
+
+ /*
+ * don't queue any more data if the peer is just about to
+ * be disconnected.
+ */
+ if (peer->state < AX25_STATE_3 || peer->condition & AX25_COND_RELEASE) {
+ kfree_skb(skb);
+ return 1;
+ }
+
+ skb_queue_tail(&peer->write_queue, skb);
+
+ /*
+ * check the length of write_queue, set OWN_RX_BUSY if
+ * enough queued.
+ */
+ max_qlen = peer->seqmask << 1;
+ if (max_qlen > 16)
+ max_qlen = 16;
+ if (skb_queue_len(&peer->write_queue) > max_qlen) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ }
+ ax25_kick(peer);
+
+ return 1;
+ }
+
+ ax25->idletimer = ax25->idle;
+
+ if (pid == AX25_P_SEGMENT) {
+ skb_pull(skb, 1);
+ queued = ax25_rx_fragment(ax25, skb);
+
+ } else if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 1); /* Remove PID */
+ queued = func(skb, ax25);
+
+ } else if ((sk = ax25->sk) != NULL) {
+ struct sk_buff *oskb;
+ /*
+ * check if we have frames left in ax25->rcv_queue, these
+ * must be delivered first to maintain the sequence.
+ */
+ while ((oskb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&sk->rmem_alloc) + oskb->truesize < sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(sk, oskb);
+ } else
+ break;
+ }
+ if (oskb == NULL)
+ ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * now handle the frame that has just come in.
+ */
+ if (ax25->pidincl || sk->protocol == pid) {
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ kfree_skb(skb);
+ } else if (sock_queue_rcv_skb(sk, skb) != 0) {
+ /*
+ * no space left on the socket, become busy
+ * but keep queueing up the data.
+ */
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * don't queue an infinite amount of
+ * data, 10 frames should be enough
+ * and we'll send an RNR anyway. If
+ * the peer keeps bombing us with
+ * valid iframes, "return 0" forces
+ * the l2 to drop the frame without
+ * ACK'ng it. This will produce heavy
+ * load on the channel due to constantly
+ * retransmitted frames, but we cannot
+ * just drop the packets, the application
+ * cannot handle the data loss induced.
+ */
+ if (skb_queue_len(&ax25->rcv_queue) < ax25->seqmask)
+ skb_queue_tail(&ax25->rcv_queue, skb);
+ else
+ return 0;
+ }
+ queued = 1;
+ }
+ }
+
+ /*
+ * this is a kludge to satisfy the IMHO broken interface
+ * between the L2 and upper protocol handlers. There was no
+ * handler for the PID and the socket didn't accept the PID
+ * either, so we free the buffer and pretend to have queued
+ * it.
+ */
+ if (!queued)
+ kfree_skb(skb);
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+struct sock *ax25_make_new(struct sock *osk, struct net_device *dev)
+{
+ struct sock *sk;
+ ax25_cb *ax25;
+
+ if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
+ return NULL;
+
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ switch (osk->type) {
+ case SOCK_DGRAM:
+ break;
+ case SOCK_SEQPACKET:
+ break;
+ default:
+ sk_free(sk);
+ ax25_free_cb(ax25);
+ return NULL;
+ }
+
+ sock_init_data(NULL, sk);
+
+ sk->destruct = ax25_free_sock;
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
+
+ ax25->seqmask = osk->protinfo.ax25->seqmask;
+ ax25->backoff = osk->protinfo.ax25->backoff;
+ ax25->pidincl = osk->protinfo.ax25->pidincl;
+ ax25->iamdigi = osk->protinfo.ax25->iamdigi;
+ 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->device = dev;
+ ax25->addr = osk->protinfo.ax25->addr;
+
+ sk->protinfo.ax25 = ax25;
+ ax25->sk = sk;
+
+ return sk;
+}
/*
* Given a fragment, queue it on the fragment queue and if the fragment
@@ -83,16 +348,15 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
/* Last fragment received ? */
if (ax25->fragno == 0) {
- if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) {
+ if ((skbn = alloc_skb(MAX_HEADER + ax25->fraglen, GFP_ATOMIC)) == NULL) {
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL)
kfree_skb(skbo);
return 1;
}
- skb_reserve(skbn, AX25_MAX_HEADER_LEN);
+ skb_reserve(skbn, MAX_HEADER);
- skbn->dev = ax25->ax25_dev->dev;
- skbn->h.raw = skbn->data;
+ skbn->dev = ax25->device;
skbn->nh.raw = skbn->data;
/* Copy data from the fragments */
@@ -126,360 +390,422 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
return 0;
}
+
/*
- * This is where all valid I frames are sent to, to be dispatched to
- * whichever protocol requires them.
+ * This routine is the centralised routine for parsing the control
+ * information for the different frame formats.
*/
-int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+static void ax25_decode(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt_info)
{
- int (*func)(struct sk_buff *, ax25_cb *);
- volatile int queued = 0;
- unsigned char pid;
-
- if (skb == NULL) return 0;
-
- ax25_start_idletimer(ax25);
+ unsigned char *frame;
+
+ frame = skb->data;
+ pkt_info->frametype = AX25_ILLEGAL;
+ pkt_info->ns = pkt_info->nr = pkt_info->pf = 0;
+
+ if (ax25->seqmask == AX25_SEQMASK) {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x07;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ }
+ skb_pull(skb, 1);
+ } else {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x7F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ skb_pull(skb, 1);
+ }
+ }
+}
- pid = *skb->data;
+/* ---------------------------------------------------------------------*/
+/*
+ * Find a control block that wants to accept the SABM we have just
+ * received.
+ */
+ax25_cb *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev)
+{
+ ax25_cb *s;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP) {
- /* working around a TCP bug to keep additional listeners
- * happy. TCP re-uses the buffer and destroys the original
- * content.
- */
- struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
- if (skbn != NULL) {
- kfree_skb(skb);
- skb = skbn;
- }
+ if (dev != NULL)
+ if ((s = ax25_dev_find_listener(addr, digi, dev)) != NULL)
+ return s;
- skb_pull(skb, 1); /* Remove PID */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = ax25->ax25_dev->dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */
- return 1;
- }
-#endif
- if (pid == AX25_P_SEGMENT) {
- skb_pull(skb, 1); /* Remove PID */
- return ax25_rx_fragment(ax25, skb);
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN
+ && s->iamdigi == digi
+ && !ax25cmp(&s->addr.src, addr))
+ return s;
}
+ return NULL;
+}
- if ((func = ax25_protocol_function(pid)) != NULL) {
- skb_pull(skb, 1); /* Remove PID */
- return (*func)(skb, ax25);
- }
+/* ---------------------------------------------------------------------*/
+/*
+ * Find an AX.25 socket given both ends.
+ */
+static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type)
+{
+ ax25_cb *s;
+ struct sock *sk;
- if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
- if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) {
- if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
- queued = 1;
- else
- ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ if (dev != NULL)
+ if ((sk = ax25_dev_find_socket(my_addr, dest_addr, dev, type)) != NULL)
+ return sk;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->sk != NULL && s->sk->type == type
+ && !ax25cmp(&s->addr.src, my_addr)
+ && !ax25cmp(&s->addr.dest, dest_addr))
+ {
+ return s->sk;
}
}
+ return NULL;
+}
+
+/* ---------------------------------------------------------------------*/
- return queued;
+static int ax25_match_addr(ax25_address *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ if (dev != NULL && ax25_dev_match_addr(addr, dev))
+ return 1;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0)
+ return 1;
+ }
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
+#ifdef notdef
/*
- * Higher level upcall for a LAPB frame
+ * this is completely broken.
+ *
+ * it depends on that sk is the first of a linked list of sockets
+ * and traverses the list to find them all, cloning the packet if the
+ * protocol and socket type both match.
*/
-static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
+static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
{
- int queued = 0;
+ struct sk_buff *copy;
- if (ax25->state == AX25_STATE_0)
- return 0;
+ while (sk != NULL) {
+ if (sk->type == SOCK_RAW &&
+ sk->protocol == proto &&
+ atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
+ if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ return;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
+ if (sock_queue_rcv_skb(sk, copy) != 0)
+ kfree_skb(copy);
+ }
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (dama || ax25->ax25_dev->dama.slave)
- queued = ax25_ds_frame_in(ax25, skb, type);
- else
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
-#endif
+ sk = sk->next;
}
-
- return queued;
}
+#endif
-static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *dev_addr, struct packet_type *ptype)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
{
struct sock *make;
struct sock *sk;
- int type = 0;
- ax25_digi dp, reverse_dp;
- ax25_cb *ax25;
- ax25_address src, dest;
- ax25_address *next_digi = NULL;
- ax25_dev *ax25_dev;
- struct sock *raw;
- int mine = 0;
- int dama;
+ ax25_cb *ax25, *peer = NULL;
+ ax25_address *next_digi;
+ ax25_pktinfo pinf;
+ ax25_addr_t reverse_addr;
+ int queued;
/*
* Process the AX.25/LAPB frame.
*/
- skb->h.raw = skb->data;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+ skb->sk = NULL;
+ queued = 0;
+ AX25_PTR(dev)->dama_polled = 0;
/*
- * Parse the address header.
+ * TODO: packet filter
*/
-
- if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+#ifdef undef
+ if (call_in_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
+ goto out_normal;
+#endif
/*
- * Ours perhaps ?
+ * Parse the frame and Pull of the AX.25 headers
+ * leaving the CTRL/PID byte
*/
- if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */
- next_digi = &dp.calls[dp.lastrepeat + 1];
+ if (ax25_parse_addr(skb->data, skb->len, &pinf) == NULL)
+ goto out_normal;
+ skb_pull(skb, ax25_sizeof_addr(&pinf.addr));
+
/*
- * Pull of the AX.25 headers leaving the CTRL/PID bytes
+ * Steps to perform while processing the frame, fast path'd for
+ * digipeating.
*/
- skb_pull(skb, ax25_addr_size(&dp));
-
- /* For our port addresses ? */
- if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* Also match on any registered callsign from L3/4 */
- if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* UI frame - bypass LAPB processing */
- if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
- skb->h.raw = skb->data + 2; /* skip control and pid */
-
- if ((raw = ax25_addr_match(&dest)) != NULL)
- ax25_send_to_raw(raw, skb, skb->data[1]);
-
- if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
- kfree_skb(skb);
+ next_digi = NULL;
+ if (pinf.addr.lastrepeat + 1 < pinf.addr.dcount) {
+ /* Not yet digipeated completely */
+ struct net_device *out_dev;
+ next_digi = &pinf.addr.digipeater[pinf.addr.lastrepeat + 1];
+
+ /* check the frame type. do the easy thing first */
+ if ((*skb->data & ~AX25_PF) == AX25_UI) {
+ /* digipeat UI frame */
+
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* Now we are pointing at the pid byte */
- switch (skb->data[1]) {
-#ifdef CONFIG_INET
- case AX25_P_IP:
- skb_pull(skb,2); /* drop PID/CTRL */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
- break;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
- case AX25_P_ARP:
- skb_pull(skb,2);
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_ARP);
- arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
- break;
-#endif
- case AX25_P_TEXT:
- /* Now find a suitable dgram socket */
- if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
- if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
- kfree_skb(skb);
- } else {
- /*
- * Remove the control and PID.
- */
- skb_pull(skb, 2);
- if (sock_queue_rcv_skb(sk, skb) != 0)
- kfree_skb(skb);
- }
- } else {
- kfree_skb(skb);
- }
- break;
-
- default:
- kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
- break;
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /* no UI, but matches a given context */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- return 0;
- }
+ if ((*skb->data & ~AX25_PF) == AX25_SABM || (*skb->data & ~AX25_PF) == AX25_SABME) {
+ if ((ax25 = ax25_find_listener(next_digi, 1, dev)) != NULL)
+ goto listener_found;
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+ if ((peer = ax25_create_cb()) == NULL)
+ goto out_normal;
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_free_cb(peer);
+ goto out_normal;
+ }
- /*
- * 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->values[AX25_VALUES_CONMODE] == 0) {
- kfree_skb(skb);
- return 0;
- }
+ out_dev = ax25_rt_set_addr(&peer->addr, &pinf.addr, dev, out_dev);
+ ax25_fillin_cb(peer, out_dev);
+ ax25_nlpost_route(&pinf, dev);
- /* LAPB */
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- /* AX.25 state 1-4 */
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_digi_invert(&dp, &reverse_dp);
+ /* crosslink the peers */
+ ax25->peer = peer;
+ peer->peer = ax25;
- if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
- /*
- * 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);
+ /* set window and modulus */
+ peer->window = ax25->window;
+ peer->seqmask = ax25->seqmask;
- return 0;
- }
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->state = AX25_STATE_0;
+ ax25_insert_cb(ax25);
- /* AX.25 state 0 (disconnected) */
+ peer->state = AX25_STATE_1;
+ ax25_insert_cb(peer);
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
- /* a) received not a SABM(E) */
+ goto out_normal;
+ }
- if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
/*
- * Never reply to a DM. Also ignore any connects for
- * addresses that are not our interfaces and not a socket.
+ * the packet was no UI or SABM(E) and didn't match an existing
+ * context. If it's via us, digipeat blindly.
*/
- if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* b) received SABM(E) */
-
- if (dp.lastrepeat + 1 == dp.ndigi)
- sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
- else
- sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+ /*
+ * UI frame - bypass LAPB processing
+ */
+ if ((*skb->data & ~0x10) == AX25_UI) {
+ int queued = 0;
+ int pid;
+ /* skip control and pid */
+ skb->nh.raw = skb->data + 2;
- if (sk != NULL) {
- if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) {
- if (mine) ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
- }
+ /* copy the datagram to all raw sockets */
+/* ax25_send_to_raw(&dest, skb, dev); */
- ax25 = make->protinfo.ax25;
- skb_set_owner_r(skb, make);
- skb_queue_head(&sk->receive_queue, skb);
+ /* if it's not a broadcast or one of our floating listeners, drop it */
+ if (!ax25_match_addr(&pinf.addr.dest, dev)
+ && ax25cmp(&pinf.addr.dest, (ax25_address *)dev->broadcast))
+ goto out_normal;
- make->state = TCP_ESTABLISHED;
- make->pair = sk;
+ /* Now we are pointing at the pid byte */
+ switch (pid = skb->data[1]) {
+ int (*func)(struct sk_buff *, ax25_cb *);
- sk->ack_backlog++;
- } else {
- if (!mine) {
- kfree_skb(skb);
- return 0;
- }
+ case AX25_P_TEXT:
+ /* Now find a suitable dgram socket */
+ if ((sk = ax25_find_socket(&pinf.addr.dest, &pinf.addr.src, dev, SOCK_DGRAM)) != NULL) {
+ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
+ kfree_skb(skb);
+ } else {
+ /*
+ * Remove the control and PID.
+ */
+ skb_pull(skb, 2);
+ if (sock_queue_rcv_skb(sk, skb) != 0)
+ kfree_skb(skb);
+ }
+ } else {
+ kfree_skb(skb);
+ }
+ break;
- if ((ax25 = ax25_create_cb()) == NULL) {
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
+ default:
+ if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 2); /* Remove CTL/PID */
+ queued = func(skb, NULL);
+ }
+ if (!queued)
+ kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
+ break;
}
- ax25_fillin_cb(ax25, ax25_dev);
+ return 0;
}
- ax25->source_addr = dest;
- ax25->dest_addr = src;
-
+ /* LAPB */
/*
- * Sort out any digipeated paths.
+ * invert the digipeater path and try to find a context for the
+ * received packet.
*/
- if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree_skb(skb);
- ax25_destroy_socket(ax25);
- return 0;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /*
+ * 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
+ */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- if (dp.ndigi == 0) {
- if (ax25->digipeat != NULL) {
- kfree(ax25->digipeat);
- ax25->digipeat = NULL;
+ if ((ax25 = ax25_find_listener(&pinf.addr.dest, 0, dev)) != NULL) {
+ listener_found:
+ /*
+ * if this frame is not a SABM(E) return DM to the sender.
+ * it belongs to a stale connection. maybe we have rebootet or the
+ * peer didn't see our UA/DM
+ */
+ if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
}
- } else {
- /* Reverse the source SABM's path */
- memcpy(&ax25->digipeat, &reverse_dp, sizeof(ax25_digi));
- }
- if ((*skb->data & ~AX25_PF) == AX25_SABME) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
- }
-
- ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
+ /* ok, we have a listener */
+ ax25_nlpost_route(&pinf, dev);
+ if ((sk = ax25->sk) != NULL) {
+ /* the listener is a socket */
+ if (sk->type != SOCK_SEQPACKET || sk->state != TCP_LISTEN)
+ goto out_normal;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
- ax25_dama_on(ax25);
-#endif
+ if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ skb->sk = make;
+ make->state = TCP_ESTABLISHED;
+ make->pair = sk;
+ ax25 = make->protinfo.ax25;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ if (!sk->dead)
+ sk->data_ready(sk, skb->len);
+ queued = 1;
+ } else {
+ /*
+ * the listener is no socket, must be a callsign registered
+ * by a higher protocol, we just take the connection
+ */
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ ax25->slcomp_enable = ax25_rt_mode_get(&pinf.addr.src) == 'C';
+ }
- ax25->state = AX25_STATE_3;
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- ax25_insert_socket(ax25);
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_start_heartbeat(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
+ ax25->device = dev;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->state = AX25_STATE_3;
+ ax25_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ }
- if (sk != NULL) {
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
- } else {
+ out_queued:
+ if (!queued) {
+ out_normal:
kfree_skb(skb);
}
-
return 0;
}
-
-/*
- * Receive an AX.25 frame via a SLIP interface.
- */
-int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
-{
- skb->sk = NULL; /* Initially we don't know who it's for */
- skb->destructor = NULL; /* Who initializes this, dammit?! */
-
- if ((*skb->data & 0x0F) != 0) {
- kfree_skb(skb); /* 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);
-}
-