summaryrefslogtreecommitdiffstats
path: root/net/ax25/ax25_ipax.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ax25/ax25_ipax.c')
-rw-r--r--net/ax25/ax25_ipax.c708
1 files changed, 708 insertions, 0 deletions
diff --git a/net/ax25/ax25_ipax.c b/net/ax25/ax25_ipax.c
new file mode 100644
index 000000000..65e8090a3
--- /dev/null
+++ b/net/ax25/ax25_ipax.c
@@ -0,0 +1,708 @@
+/*
+ * ax25_ipax.c: implements "virtual" interface ipax0 and en/decapsulation of IP
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Includes "Protocol Booster" and VJ compression switch.
+ * Written from scratch by Matthias Welwarsky in 1998.
+ *
+ * Changelog:
+ *
+ * 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.
+ */
+
+#define AX25_ENCAP_MODE_IGNORE_PROTOCOL
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "ax25_route.h"
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_netlink.h"
+#include "ax25_subr.h"
+
+
+
+static int ipax_hard_header(struct sk_buff*, struct net_device*, unsigned short, void*, void*, unsigned);
+static int ipax_set_mac_address(struct net_device*, void*);
+static int ipax_probe(struct net_device*);
+static int ipax_device_event(struct notifier_block*, unsigned long, void*);
+static int ipax_open(struct net_device*);
+static int ipax_close(struct net_device*);
+static int ipax_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_arp_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_vjc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_vjunc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_send_packet(struct sk_buff*, struct net_device*);
+static struct net_device_stats *ipax_get_stats(struct net_device*);
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device ipax_device;
+
+static struct ipax_local_t {
+ struct net_device_stats stats;
+} ipax_local;
+
+/*
+ * Device up/down notifier block
+ */
+static struct notifier_block ipax_dev_notifier = {
+ ipax_device_event,
+ 0
+};
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* ---------------------------------------------------------------------*/
+
+int ipax_init(void)
+{
+ struct net_device *dev = &ipax_device;
+
+ memset(dev, 0, sizeof (struct net_device));
+ strcpy(dev->name, "ipax0");
+ dev->if_port = 0;
+ dev->init = ipax_probe;
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ register_netdevice_notifier(&ipax_dev_notifier);
+
+ if (register_netdev(dev)) {
+ printk(KERN_WARNING "ipax: cannot register net device\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int ipax_cleanup(void)
+{
+ unregister_netdevice_notifier(&ipax_dev_notifier);
+ unregister_netdev(&ipax_device);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ return NOTIFY_DONE;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * set the MAC layer address of the interface. (i.e. the callsign)
+ *
+ */
+static int ipax_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ if (netif_running(dev))
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ if (netif_running(dev))
+ ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Interface startup.
+ *
+ */
+static int ipax_probe(struct net_device *dev)
+{
+ if (dev == NULL)
+ return -ENXIO;
+
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ /* Initialize the device structure. */
+ dev->priv = &ipax_local;
+
+ memset(dev->priv, 0, sizeof(struct ipax_local_t));
+
+ dev->open = ipax_open;
+ dev->stop = ipax_close;
+ dev->hard_start_xmit = ipax_send_packet;
+ dev->get_stats = ipax_get_stats;
+
+ dev->hard_header = ipax_hard_header;
+ dev->set_mac_address = ipax_set_mac_address;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN;
+ dev->mtu = 1500;
+ dev->addr_len = AX25_ADDR_LEN;
+ dev->tx_queue_len = 8;
+
+ memcpy(dev->broadcast, ax25_bcast, dev->addr_len);
+ memcpy(dev->dev_addr, ax25_test, dev->addr_len);
+
+ dev->flags = IFF_BROADCAST;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ */
+static int ipax_open(struct net_device *dev)
+{
+ if (netif_running(dev))
+ return 0;
+
+ if (!ax25_listen_register((ax25_address *)dev->dev_addr, NULL))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_IP, ipax_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_ARP, ipax_arp_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJCOMP, ipax_vjc_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJUNCOMP, ipax_vjunc_rcv))
+ return 0;
+
+ /* dev_restart(dev); */ /* FIXME: anything to do here at all? */
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * the inverse routine to ipax_open. close the interface end
+ * decrement the module use count.
+ */
+static int ipax_close(struct net_device *dev)
+{
+ if (!netif_running(dev))
+ return 0;
+
+ netif_stop_queue(dev);
+
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ ax25_protocol_release(AX25_P_IP);
+ ax25_protocol_release(AX25_P_ARP);
+ ax25_protocol_release(AX25_P_VJCOMP);
+ ax25_protocol_release(AX25_P_VJUNCOMP);
+
+ return 0;
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ if (ax25) /* FIXME: works only for VC */
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL); /* Wrong ptype */
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_arp_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_ARP);
+ arp_rcv(skb, dev, NULL); /* Wrong ptype */
+
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * check if we already have initialized slots,
+ * if not, it's too late. flush the frame.
+ */
+ if (ax25->slcomp == NULL) {
+ ax25->slcomp = axhc_init(32, 32);
+ printk(KERN_DEBUG "ax25_vjc_recv: no vjc-slots allocated, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+
+ /*
+ * the data will grow upwards while decompressing,
+ * crippling the ax.25 header. copy the packet only if
+ * it's smaller than 256 bytes, else the defragmenter has
+ * already copied it, we don't do that twice.
+ */
+ if (skb->len <= 256) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+ kfree_skb(skb);
+ skb = skbn;
+ }
+ }
+
+ /* set device the packet came in */
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+
+ /*
+ * make headroom for MAX_HEADER bytes of TCP/IP header
+ */
+ if (skb_headroom(skb) < MAX_HEADER) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_realloc_headroom(skb, MAX_HEADER)) == NULL) {
+ printk(KERN_DEBUG "ax25_vjc_recv: cannot reallocate headroom, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ kfree_skb(skb);
+ skb = skbn;
+ }
+
+ if (axhc_uncompress(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjc_rcv: error decompressing packet, dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ /* adjust pointer to network header */
+ skb->nh.raw = skb->data;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjunc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * MW:
+ * check if we already have initialized slots,
+ * do so if not.
+ */
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+
+ if (axhc_remember(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjunc_rcv: unable to remember slot, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static void ipax_fragment(struct sk_buff *skb, struct net_device *dev, ax25_addr_t *addr)
+{
+ struct iphdr *iph;
+ unsigned char *raw;
+ unsigned char *ptr;
+ struct sk_buff *skb2;
+ unsigned int mtu, hlen, left, len;
+ int offset;
+ int not_last_frag;
+
+ /*
+ * Point into the IP datagram header.
+ */
+
+ raw = skb->nh.raw;
+ iph = (struct iphdr*)raw;
+
+ /*
+ * Setup starting values.
+ */
+
+ hlen = iph->ihl * 4;
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ mtu = dev->mtu; /* Size of data space */
+ ptr = raw + hlen; /* Where to start from */
+
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ *
+ * It is impossible, because mtu>=68. --ANK (980801)
+ */
+
+#ifdef CONFIG_NET_PARANOIA
+ if (mtu<8)
+ goto fail;
+#endif
+
+ /*
+ * Fragment the datagram.
+ */
+
+ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+ not_last_frag = iph->frag_off & htons(IP_MF);
+
+ /*
+ * Keep copying data until we run out.
+ */
+
+ while(left > 0) {
+ len = left;
+ /* IF: it doesn't fit, use 'mtu' - the data space left */
+ if (len > mtu)
+ len = mtu;
+ /* IF: we are not sending upto and including the packet end
+ then align the next start on an eight byte boundary */
+ if (len < left) {
+ len &= ~7;
+ }
+ /*
+ * Allocate buffer.
+ */
+
+ if ((skb2 = alloc_skb(len+hlen+AX25_MAX_HEADER_LEN+15,GFP_ATOMIC)) == NULL) {
+ NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+ goto fail;
+ }
+
+ /*
+ * Set up data on packet
+ */
+
+ skb2->pkt_type = skb->pkt_type;
+ skb2->priority = skb->priority;
+ skb_reserve(skb2, (AX25_MAX_HEADER_LEN+15)&~15);
+ skb_put(skb2, len + hlen);
+ skb2->nh.raw = skb2->data;
+ skb2->h.raw = skb2->data + hlen;
+
+ /*
+ * Charge the memory for the fragment to any owner
+ * it might possess
+ */
+
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ skb2->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
+ memcpy(skb2->nh.raw, raw, hlen);
+
+ /*
+ * Copy a block of the IP datagram.
+ */
+ memcpy(skb2->h.raw, ptr, len);
+ left -= len;
+
+ /*
+ * Fill in the new header fields.
+ */
+ iph = skb2->nh.iph;
+ iph->frag_off = htons((offset >> 3));
+
+ /* ANK: dirty, but effective trick. Upgrade options only if
+ * the segment to be fragmented was THE FIRST (otherwise,
+ * options are already fixed) and make it ONCE
+ * on the initial skb, so that all the following fragments
+ * will inherit fixed options.
+ */
+ if (offset == 0)
+ ip_options_fragment(skb);
+
+ /*
+ * Added AC : If we are fragmenting a fragment that's not the
+ * last fragment then keep MF on each bit
+ */
+ if (left > 0 || not_last_frag)
+ iph->frag_off |= htons(IP_MF);
+ ptr += len;
+ offset += len;
+
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragCreates++;
+ */
+
+ iph->tot_len = htons(len + hlen);
+
+ ip_send_check(iph);
+
+ /* build UI packet header */
+ *skb_push(skb2, 1) = AX25_P_IP;
+ *skb_push(skb2, 1) = AX25_UI;
+ skb_push(skb2, ax25_sizeof_addr(addr));
+ ax25_build_addr(skb2->data, addr, AX25_COMMAND, AX25_SEQMASK);
+ ax25_send_unproto(skb2, dev);
+ }
+ kfree_skb(skb);
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragOKs++;
+ */
+ return;
+
+fail:
+ kfree_skb(skb);
+ /* FIXME: where did this go?
+ ip_statistics.IpFragFails++;
+ */
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static unsigned char ipax_try_compress(ax25_cb *ax25, struct sk_buff *skb)
+{
+ unsigned char pid;
+
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+ /* attempt to compress the frame */
+ pid = axhc_compress(ax25->slcomp, skb, ax25->slcomp_enable);
+ if (pid)
+ *skb_push(skb, 1) = pid;
+ return pid;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ ax25_address *dest, *source;
+ struct ax25_route *axrt;
+ struct net_device *axdev;
+ ax25_addr_t addr;
+
+ /*
+ * send the packet down to the tunneling protocol
+ */
+ dest = (ax25_address *)skb->data;
+ source = (ax25_address *)(skb->data+AX25_ADDR_LEN);
+
+ if (!ax25cmp(dest, (ax25_address *)ax25_bcast)) {
+ ax25_send_broadcast(skb);
+ kfree_skb(skb);
+ lp->stats.tx_packets++;
+ return 0;
+ }
+
+ axrt = ax25_find_route(dest);
+ if (axrt == NULL) {
+ printk(KERN_DEBUG "ax25: ipax_send_packet(): no route found, discarding packet.\n");
+ kfree_skb(skb);
+ lp->stats.tx_dropped++;
+ return 0;
+ }
+
+ axdev = axrt->dev;
+
+ addr.dest = *dest;
+ addr.src = *source;
+ addr.dcount = axrt->path.dcount;
+ addr.lastrepeat = -1;
+ memcpy(addr.digipeater, axrt->path.digipeater, sizeof(ax25_address)*addr.dcount);
+
+ if (skb->data[15] == AX25_P_IP) {
+#ifndef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ struct iphdr *iph = (struct iphdr *)(skb->data+16);
+#endif
+ int mode = axrt->ip_mode;
+
+#ifdef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ if (1) {
+#else
+ if (iph->protocol != IPPROTO_UDP) {
+#endif
+ if (mode == 'V' || mode == 'C'
+ || (mode == ' ' && ax25_dev_get_value(axdev, AX25_VALUES_IPDEFMODE)))
+ {
+ int paclen = ax25_dev_get_value(axdev, AX25_VALUES_PACLEN);
+ ax25_cb* ax25 = ax25_find_cb(&addr, axdev);
+
+ if (ax25 != NULL) {
+ /* reuse a just disconnected control block */
+ if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) {
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ goto out_reused;
+ }
+ goto out_ok;
+ }
+ if ((ax25 = ax25_create_cb()) == NULL)
+ goto out_dropped;
+ ax25_fillin_cb(ax25, axdev);
+ ax25->addr = addr;
+ ax25_insert_cb(ax25);
+ out_reused:
+ ax25->slcomp_enable = (mode == 'C');
+ ax25_establish_data_link(ax25);
+ out_ok:
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ if (!ipax_try_compress(ax25, skb))
+ goto out_dropped;
+ ax25_output(ax25, paclen, skb);
+ dev->trans_start = jiffies;
+ /* netif_stop_queue(dev) */
+ lp->stats.tx_packets++;
+ return 0;
+ out_dropped:
+ lp->stats.tx_dropped++;
+ kfree_skb(skb);
+ /* netif_stop_queue(dev) */
+ return 0;
+ }
+ } else if (skb->len > axdev->mtu) {
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ ipax_fragment(skb, axdev, &addr);
+ return 0;
+ }
+ }
+
+ /* rebuild the header if digipeaters are to be used */
+ if (addr.dcount != 0) {
+ /* strip address field, keep the control byte */
+ skb_pull(skb, AX25_MIN_HEADER_LEN-1);
+ skb_push(skb, ax25_sizeof_addr(&addr));
+ ax25_build_addr(skb->data, &addr, AX25_COMMAND, AX25_SEQMASK);
+ }
+
+ /*
+ * queue a completely assembled frame to the unproto
+ * queue of an interface
+ */
+ ax25_send_unproto(skb, axdev);
+ dev->trans_start = jiffies;
+ lp->stats.tx_packets++;
+ /* netif_stop_queue(dev) */
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *ipax_get_stats(struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ return &lp->stats;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_hard_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len)
+{
+ unsigned char pid;
+ unsigned char *buf;
+ int hlen;
+
+ if (type == ETH_P_IP)
+ pid = AX25_P_IP;
+ else if (type == ETH_P_ARP)
+ pid = AX25_P_ARP;
+ else {
+ printk(KERN_ERR "ax25: ipax_hard_header: unknown packet type %d.\n", type);
+ pid = 0;
+ }
+ *skb_push(skb, 1) = pid;
+ *skb_push(skb, 1) = AX25_UI;
+
+ if (saddr != NULL)
+ memcpy(skb_push(skb, AX25_ADDR_LEN), saddr, AX25_ADDR_LEN);
+ else
+ memcpy(skb_push(skb, dev->addr_len), dev->dev_addr, dev->addr_len);
+
+ if (daddr != NULL) {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), daddr, AX25_ADDR_LEN);
+ hlen = (AX25_MIN_HEADER_LEN+1);
+ } else {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), &null_ax25_address, AX25_ADDR_LEN);
+ hlen = -(AX25_MIN_HEADER_LEN+1);
+ }
+
+ buf = skb->data;
+ buf[6] &= ~(AX25_CBIT|AX25_EBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+ buf[13] &= ~AX25_CBIT;
+ buf[13] |= (AX25_EBIT|AX25_SSSID_SPARE);
+
+ return hlen;
+}