162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	SNAP data link layer. Derived from 802.2
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *		Alan Cox <alan@lxorguk.ukuu.org.uk>,
662306a36Sopenharmony_ci *		from the 802.2 layer by Greg Page.
762306a36Sopenharmony_ci *		Merged in additions from Greg Page's psnap.c.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/skbuff.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <net/datalink.h>
1562306a36Sopenharmony_ci#include <net/llc.h>
1662306a36Sopenharmony_ci#include <net/psnap.h>
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/rculist.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic LIST_HEAD(snap_list);
2362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(snap_lock);
2462306a36Sopenharmony_cistatic struct llc_sap *snap_sap;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci *	Find a snap client by matching the 5 bytes.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic struct datalink_proto *find_snap_client(const unsigned char *desc)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct datalink_proto *proto = NULL, *p;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	list_for_each_entry_rcu(p, &snap_list, node, lockdep_is_held(&snap_lock)) {
3462306a36Sopenharmony_ci		if (!memcmp(p->type, desc, 5)) {
3562306a36Sopenharmony_ci			proto = p;
3662306a36Sopenharmony_ci			break;
3762306a36Sopenharmony_ci		}
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	return proto;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci *	A SNAP packet has arrived
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistatic int snap_rcv(struct sk_buff *skb, struct net_device *dev,
4662306a36Sopenharmony_ci		    struct packet_type *pt, struct net_device *orig_dev)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int rc = 1;
4962306a36Sopenharmony_ci	struct datalink_proto *proto;
5062306a36Sopenharmony_ci	static struct packet_type snap_packet_type = {
5162306a36Sopenharmony_ci		.type = cpu_to_be16(ETH_P_SNAP),
5262306a36Sopenharmony_ci	};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, 5)))
5562306a36Sopenharmony_ci		goto drop;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	rcu_read_lock();
5862306a36Sopenharmony_ci	proto = find_snap_client(skb_transport_header(skb));
5962306a36Sopenharmony_ci	if (proto) {
6062306a36Sopenharmony_ci		/* Pass the frame on. */
6162306a36Sopenharmony_ci		skb->transport_header += 5;
6262306a36Sopenharmony_ci		skb_pull_rcsum(skb, 5);
6362306a36Sopenharmony_ci		rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	rcu_read_unlock();
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (unlikely(!proto))
6862306a36Sopenharmony_ci		goto drop;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciout:
7162306a36Sopenharmony_ci	return rc;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cidrop:
7462306a36Sopenharmony_ci	kfree_skb(skb);
7562306a36Sopenharmony_ci	goto out;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci *	Put a SNAP header on a frame and pass to 802.2
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic int snap_request(struct datalink_proto *dl,
8262306a36Sopenharmony_ci			struct sk_buff *skb, const u8 *dest)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	memcpy(skb_push(skb, 5), dl->type, 5);
8562306a36Sopenharmony_ci	llc_build_and_send_ui_pkt(snap_sap, skb, dest, snap_sap->laddr.lsap);
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci *	Set up the SNAP layer
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciEXPORT_SYMBOL(register_snap_client);
9362306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_snap_client);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const char snap_err_msg[] __initconst =
9662306a36Sopenharmony_ci	KERN_CRIT "SNAP - unable to register with 802.2\n";
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int __init snap_init(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	snap_sap = llc_sap_open(0xAA, snap_rcv);
10162306a36Sopenharmony_ci	if (!snap_sap) {
10262306a36Sopenharmony_ci		printk(snap_err_msg);
10362306a36Sopenharmony_ci		return -EBUSY;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cimodule_init(snap_init);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void __exit snap_exit(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	llc_sap_put(snap_sap);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cimodule_exit(snap_exit);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci *	Register SNAP clients. We don't yet use this for IP.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistruct datalink_proto *register_snap_client(const unsigned char *desc,
12362306a36Sopenharmony_ci					    int (*rcvfunc)(struct sk_buff *,
12462306a36Sopenharmony_ci							   struct net_device *,
12562306a36Sopenharmony_ci							   struct packet_type *,
12662306a36Sopenharmony_ci							   struct net_device *))
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct datalink_proto *proto = NULL;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	spin_lock_bh(&snap_lock);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (find_snap_client(desc))
13362306a36Sopenharmony_ci		goto out;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
13662306a36Sopenharmony_ci	if (proto) {
13762306a36Sopenharmony_ci		memcpy(proto->type, desc, 5);
13862306a36Sopenharmony_ci		proto->rcvfunc		= rcvfunc;
13962306a36Sopenharmony_ci		proto->header_length	= 5 + 3; /* snap + 802.2 */
14062306a36Sopenharmony_ci		proto->request		= snap_request;
14162306a36Sopenharmony_ci		list_add_rcu(&proto->node, &snap_list);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ciout:
14462306a36Sopenharmony_ci	spin_unlock_bh(&snap_lock);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return proto;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci *	Unregister SNAP clients. Protocols no longer want to play with us ...
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_civoid unregister_snap_client(struct datalink_proto *proto)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	spin_lock_bh(&snap_lock);
15562306a36Sopenharmony_ci	list_del_rcu(&proto->node);
15662306a36Sopenharmony_ci	spin_unlock_bh(&snap_lock);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	synchronize_net();
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	kfree(proto);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
164