diff options
Diffstat (limited to 'net/ax25/ax25_ipax.c')
-rw-r--r-- | net/ax25/ax25_ipax.c | 708 |
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; +} |