psnap.c 3.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 *	SNAP data link layer. Derived from 802.2
 *
 *		Alan Cox <Alan.Cox@linux.org>,
 *		from the 802.2 layer by Greg Page.
 *		Merged in additions from Greg Page's psnap.c.
 *
 *		This program 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/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/datalink.h>
#include <net/llc.h>
#include <net/psnap.h>
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/init.h>
23
#include <linux/rculist.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

static LIST_HEAD(snap_list);
static DEFINE_SPINLOCK(snap_lock);
static struct llc_sap *snap_sap;

/*
 *	Find a snap client by matching the 5 bytes.
 */
static struct datalink_proto *find_snap_client(unsigned char *desc)
{
	struct list_head *entry;
	struct datalink_proto *proto = NULL, *p;

	list_for_each_rcu(entry, &snap_list) {
		p = list_entry(entry, struct datalink_proto, node);
		if (!memcmp(p->type, desc, 5)) {
			proto = p;
			break;
		}
	}
	return proto;
}

/*
 *	A SNAP packet has arrived
 */
static int snap_rcv(struct sk_buff *skb, struct net_device *dev,
D
David S. Miller 已提交
51
		    struct packet_type *pt, struct net_device *orig_dev)
L
Linus Torvalds 已提交
52 53 54 55 56 57 58
{
	int rc = 1;
	struct datalink_proto *proto;
	static struct packet_type snap_packet_type = {
		.type = __constant_htons(ETH_P_SNAP),
	};

59 60 61
	if (unlikely(!pskb_may_pull(skb, 5)))
		goto drop;

L
Linus Torvalds 已提交
62
	rcu_read_lock();
63
	proto = find_snap_client(skb_transport_header(skb));
L
Linus Torvalds 已提交
64 65
	if (proto) {
		/* Pass the frame on. */
66
		skb->transport_header += 5;
67
		skb_pull_rcsum(skb, 5);
D
David S. Miller 已提交
68
		rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev);
L
Linus Torvalds 已提交
69 70
	}
	rcu_read_unlock();
71 72 73 74 75

	if (unlikely(!proto))
		goto drop;

out:
L
Linus Torvalds 已提交
76
	return rc;
77 78 79 80

drop:
	kfree_skb(skb);
	goto out;
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
}

/*
 *	Put a SNAP header on a frame and pass to 802.2
 */
static int snap_request(struct datalink_proto *dl,
			struct sk_buff *skb, u8 *dest)
{
	memcpy(skb_push(skb, 5), dl->type, 5);
	llc_build_and_send_ui_pkt(snap_sap, skb, dest, snap_sap->laddr.lsap);
	return 0;
}

/*
 *	Set up the SNAP layer
 */
EXPORT_SYMBOL(register_snap_client);
EXPORT_SYMBOL(unregister_snap_client);

static char snap_err_msg[] __initdata =
	KERN_CRIT "SNAP - unable to register with 802.2\n";

static int __init snap_init(void)
{
	snap_sap = llc_sap_open(0xAA, snap_rcv);

	if (!snap_sap)
		printk(snap_err_msg);

	return 0;
}

module_init(snap_init);

static void __exit snap_exit(void)
{
117
	llc_sap_put(snap_sap);
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125 126 127
}

module_exit(snap_exit);


/*
 *	Register SNAP clients. We don't yet use this for IP.
 */
struct datalink_proto *register_snap_client(unsigned char *desc,
					    int (*rcvfunc)(struct sk_buff *,
128
							   struct net_device *,
D
David S. Miller 已提交
129 130
							   struct packet_type *,
							   struct net_device *))
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
{
	struct datalink_proto *proto = NULL;

	spin_lock_bh(&snap_lock);

	if (find_snap_client(desc))
		goto out;

	proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
	if (proto) {
		memcpy(proto->type, desc,5);
		proto->rcvfunc		= rcvfunc;
		proto->header_length	= 5 + 3; /* snap + 802.2 */
		proto->request		= snap_request;
		list_add_rcu(&proto->node, &snap_list);
	}
out:
	spin_unlock_bh(&snap_lock);

	synchronize_net();
	return proto;
}

/*
 *	Unregister SNAP clients. Protocols no longer want to play with us ...
 */
void unregister_snap_client(struct datalink_proto *proto)
{
	spin_lock_bh(&snap_lock);
	list_del_rcu(&proto->node);
	spin_unlock_bh(&snap_lock);

	synchronize_net();

	kfree(proto);
}

MODULE_LICENSE("GPL");