diff options
Diffstat (limited to 'drivers/net/hamradio/bpqether.c')
-rw-r--r-- | drivers/net/hamradio/bpqether.c | 247 |
1 files changed, 134 insertions, 113 deletions
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 75539ecef..db96de7b9 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -1,5 +1,5 @@ /* - * G8BPQ compatible "AX.25 via ethernet" driver release 004 + * G8BPQ compatible "AX.25 via ethernet" driver release 003 * * This code REQUIRES 2.0.0 or higher/ NET3.029 * @@ -62,10 +62,9 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/net.h> -#include <net/ax25.h> +#include <net/ax25dev.h> #include <linux/inet.h> #include <linux/netdevice.h> -#include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/skbuff.h> #include <net/sock.h> @@ -77,25 +76,16 @@ #include <linux/notifier.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/netfilter.h> #include <linux/module.h> #include <linux/init.h> #include <linux/rtnetlink.h> - #include <net/ip.h> #include <net/arp.h> - #include <linux/bpqether.h> -static const char banner[] __initdata = KERN_INFO "AX.25: bpqether driver version 004\n"; - -static unsigned char ax25_bcast[AX25_ADDR_LEN] = - {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static unsigned char ax25_defaddr[AX25_ADDR_LEN] = - {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; +/* ------------------------------------------------------------------------ */ static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - static char bpq_eth_addr[6]; static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *); @@ -103,56 +93,62 @@ static int bpq_device_event(struct notifier_block *, unsigned long, void *); static char *bpq_print_ethaddr(unsigned char *); static struct packet_type bpq_packet_type = { - type: __constant_htons(ETH_P_BPQ), - func: bpq_rcv, + 0, /* ntohs(ETH_P_BPQ),*/ + 0, /* copy */ + bpq_rcv, + NULL, + NULL, }; static struct notifier_block bpq_dev_notifier = { - notifier_call: bpq_device_event, + bpq_device_event, + 0 }; - #define MAXBPQDEV 100 static struct bpqdev { struct bpqdev *next; char ethname[14]; /* ether device name */ - struct net_device *ethdev; /* link to ethernet device */ - struct net_device axdev; /* bpq device (bpq#) */ + struct net_device *ethdev; /* link to ethernet device */ + struct net_device axdev; /* bpq device (bpq#) */ struct net_device_stats stats; /* some statistics */ char dest_addr[6]; /* ether destination address */ char acpt_addr[6]; /* accept ether frames from this address only */ -} *bpq_devices; - + struct ax25_dev ax25dev; /* AX.25 device control structure */ +} *bpq_devices = NULL; /* ------------------------------------------------------------------------ */ - /* * Get the ethernet device for a BPQ device */ -static inline struct net_device *bpq_get_ether_dev(struct net_device *dev) +static __inline__ struct net_device *bpq_get_ether_dev(struct net_device *dev) { - struct bpqdev *bpq = (struct bpqdev *) dev->priv; + struct bpqdev *bpq; - return bpq ? bpq->ethdev : NULL; + bpq = (struct bpqdev *)dev->priv; + return (bpq != NULL) ? bpq->ethdev : NULL; } +/* ------------------------------------------------------------------------ */ + /* * Get the BPQ device for the ethernet device */ -static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) +static __inline__ struct net_device *bpq_get_ax25_dev(struct net_device *dev) { struct bpqdev *bpq; for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) if (bpq->ethdev == dev) return &bpq->axdev; - return NULL; } -static inline int dev_is_ethdev(struct net_device *dev) +/* ------------------------------------------------------------------------ */ + +static __inline__ int dev_is_ethdev(struct net_device *dev) { return ( dev->type == ARPHRD_ETHER @@ -160,6 +156,8 @@ static inline int dev_is_ethdev(struct net_device *dev) ); } +/* ------------------------------------------------------------------------ */ + /* * Sanity check: remove all devices that ceased to exists and * return '1' if the given BPQ device was affected. @@ -174,7 +172,6 @@ static int bpq_check_devices(struct net_device *dev) cli(); bpq_prev = NULL; - for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) { if (!dev_get(bpq->ethname)) { if (bpq_prev) @@ -195,23 +192,18 @@ static int bpq_check_devices(struct net_device *dev) bpq_prev = bpq; } - restore_flags(flags); - return result; } - /* ------------------------------------------------------------------------ */ - /* * Receive an AX.25 frame via an ethernet interface. */ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) { int len; - char * ptr; struct ethhdr *eth = (struct ethhdr *)skb->mac.raw; struct bpqdev *bpq; @@ -242,11 +234,8 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty skb_pull(skb, 2); /* Remove the length bytes */ skb_trim(skb, len); /* Set the length of the data */ - bpq->stats.rx_packets++; - bpq->stats.rx_bytes += len; - - ptr = skb_push(skb, 1); - *ptr = 0; + ((struct bpqdev *)dev->priv)->stats.rx_packets++; + ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len; skb->dev = dev; skb->protocol = htons(ETH_P_AX25); @@ -254,10 +243,11 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty skb->pkt_type = PACKET_HOST; netif_rx(skb); - return 0; } +/* ------------------------------------------------------------------------ */ + /* * Send an AX.25 frame via an ethernet interface */ @@ -265,7 +255,8 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *newskb; unsigned char *ptr; - struct bpqdev *bpq; + struct sk_buff *skb_cp; + struct bpqdev *bpq = (struct bpqdev *) dev->priv; int size; /* @@ -278,63 +269,72 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) return -ENODEV; } - skb_pull(skb, 1); - size = skb->len; + /* NOTE non-obvious race condition prevention here */ + skb_cp = skb_copy(skb, GFP_ATOMIC); + kfree_skb(skb); + if (skb_cp == NULL) { /* out of memory */ + bpq->stats.tx_dropped++; + return 0; + } + + size = skb_cp->len; /* * The AX.25 code leaves enough room for the ethernet header, but * sendto() does not. */ - if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) { /* Ough! */ - if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) { + if (skb_headroom(skb_cp) < AX25_BPQ_HEADER_LEN) { /* Ough! */ + if ((newskb = skb_realloc_headroom(skb_cp, AX25_BPQ_HEADER_LEN)) == NULL) { printk(KERN_WARNING "bpqether: out of memory\n"); - kfree_skb(skb); + kfree_skb(skb_cp); return -ENOMEM; } - if (skb->sk != NULL) - skb_set_owner_w(newskb, skb->sk); + if (skb_cp->sk != NULL) + skb_set_owner_w(newskb, skb_cp->sk); - kfree_skb(skb); - skb = newskb; + kfree_skb(skb_cp); + skb_cp = newskb; } - skb->protocol = htons(ETH_P_AX25); + skb_cp->protocol = htons(ETH_P_AX25); - ptr = skb_push(skb, 2); + ptr = skb_push(skb_cp, 2); *ptr++ = (size + 5) % 256; *ptr++ = (size + 5) / 256; - bpq = (struct bpqdev *)dev->priv; - if ((dev = bpq_get_ether_dev(dev)) == NULL) { bpq->stats.tx_dropped++; - kfree_skb(skb); + kfree_skb(skb_cp); return -ENODEV; } - skb->dev = dev; - skb->nh.raw = skb->data; - dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); + skb_cp->dev = dev; + skb_cp->nh.raw = skb_cp->data; + dev->hard_header(skb_cp, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); bpq->stats.tx_packets++; - bpq->stats.tx_bytes+=skb->len; + bpq->stats.tx_bytes+=skb_cp->len; - dev_queue_xmit(skb); - netif_wake_queue(dev); + dev_queue_xmit(skb_cp); return 0; } +/* ------------------------------------------------------------------------ */ + /* * Statistics */ static struct net_device_stats *bpq_get_stats(struct net_device *dev) { - struct bpqdev *bpq = (struct bpqdev *) dev->priv; + struct bpqdev *bpq; + bpq = (struct bpqdev *)dev->priv; return &bpq->stats; } +/* ------------------------------------------------------------------------ */ + /* * Set AX.25 callsign */ @@ -343,10 +343,11 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr) struct sockaddr *sa = (struct sockaddr *)addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); - return 0; } +/* ------------------------------------------------------------------------ */ + /* Ioctl commands * * SIOCSBPQETHOPT reserved for enhancements @@ -356,11 +357,12 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr) */ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { + int err; struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data; struct bpqdev *bpq = dev->priv; struct bpq_req req; - if (!capable(CAP_NET_ADMIN)) + if (!suser()) return -EPERM; if (bpq == NULL) /* woops! */ @@ -368,8 +370,9 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCSBPQETHOPT: - if (copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req))) - return -EFAULT; + if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0) + return err; + copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req)); switch (req.cmd) { case SIOCGBPQETHPARAM: case SIOCSBPQETHPARAM: @@ -380,19 +383,37 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case SIOCSBPQETHADDR: - if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN)) - return -EFAULT; - if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN)) - return -EFAULT; + if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0) + return err; + copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN); + copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN); break; default: return -EINVAL; } - return 0; } +/* ------------------------------------------------------------------------ */ + +void bpq_param_notify(struct net_device *dev, int value, int old, int new) +{ + /* we reject some parameters because they are not implemented (yet) */ + switch (value) { + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + case AX25_VALUES_MEDIA_TXBITRATE: + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_DUPLEX: + ax25_dev_set_value(dev, value, old); + default: break; + } + return; +} + +/* ------------------------------------------------------------------------ */ + /* * open/close a device */ @@ -400,32 +421,60 @@ static int bpq_open(struct net_device *dev) { if (bpq_check_devices(dev)) return -ENODEV; /* oops, it's gone */ - + MOD_INC_USE_COUNT; netif_start_queue(dev); return 0; } +/* ------------------------------------------------------------------------ */ + static int bpq_close(struct net_device *dev) { netif_stop_queue(dev); + MOD_DEC_USE_COUNT; return 0; } +/* ------------------------------------------------------------------------ */ + /* * currently unused */ static int bpq_dev_init(struct net_device *dev) { + struct bpqdev *bpqdev = (struct bpqdev *) dev->priv; + + dev->hard_start_xmit = bpq_xmit; + dev->open = bpq_open; + dev->stop = bpq_close; + dev->set_mac_address = bpq_set_mac_address; + dev->get_stats = bpq_get_stats; + dev->do_ioctl = bpq_ioctl; + dev->flags = 0; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + AX25_PTR(dev) = &bpqdev->ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + AX25_PTR(dev)->hw.fast = 1; + AX25_PTR(dev)->hw.parameter_change_notify = bpq_param_notify; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 1); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, 10000000); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, 10000000); + + dev_init_buffers(dev); return 0; } - /* ------------------------------------------------------------------------ */ - /* * Proc filesystem */ @@ -435,10 +484,11 @@ static char * bpq_print_ethaddr(unsigned char *e) sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", e[0], e[1], e[2], e[3], e[4], e[5]); - return buf; } +/* ------------------------------------------------------------------------ */ + static int bpq_get_info(char *buffer, char **start, off_t offset, int length) { struct bpqdev *bpqdev; @@ -475,14 +525,11 @@ static int bpq_get_info(char *buffer, char **start, off_t offset, int length) len -= (offset - begin); if (len > length) len = length; - return len; } - /* ------------------------------------------------------------------------ */ - /* * Setup a new device. */ @@ -524,36 +571,11 @@ static int bpq_new_device(struct net_device *dev) dev->init = bpq_dev_init; /* We should be locked, call register_netdevice() directly. */ - if (register_netdevice(dev) != 0) { kfree(bpq); return -EIO; } - dev_init_buffers(dev); - - dev->hard_start_xmit = bpq_xmit; - dev->open = bpq_open; - dev->stop = bpq_close; - dev->set_mac_address = bpq_set_mac_address; - dev->get_stats = bpq_get_stats; - dev->do_ioctl = bpq_ioctl; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN); - - dev->flags = 0; - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#endif - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - cli(); if (bpq_devices == NULL) { @@ -564,10 +586,10 @@ static int bpq_new_device(struct net_device *dev) } sti(); - return 0; } +/* ------------------------------------------------------------------------ */ /* * Handle device status changes. @@ -595,32 +617,30 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi default: break; } - return NOTIFY_DONE; } - /* ------------------------------------------------------------------------ */ /* * Initialize driver. To be called from af_ax25 if not compiled as a * module */ -static int __init bpq_init_driver(void) +int __init bpq_init_driver(void) { struct net_device *dev; + bpq_packet_type.type = htons(ETH_P_BPQ); dev_add_pack(&bpq_packet_type); - register_netdevice_notifier(&bpq_dev_notifier); - - printk(banner); + printk(KERN_INFO "AX.25: bqpether driver version 0.02\n"); proc_net_create("bpqether", 0, bpq_get_info); read_lock_bh(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { - if (dev_is_ethdev(dev)) { + if (dev_is_ethdev(dev)) + { read_unlock_bh(&dev_base_lock); bpq_new_device(dev); read_lock_bh(&dev_base_lock); @@ -630,12 +650,13 @@ static int __init bpq_init_driver(void) return 0; } -static void __exit bpq_cleanup_driver(void) +/* ------------------------------------------------------------------------ */ + +void __exit bpq_cleanup_driver(void) { struct bpqdev *bpq; dev_remove_pack(&bpq_packet_type); - unregister_netdevice_notifier(&bpq_dev_notifier); proc_net_remove("bpqether"); |