162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	STP SAP demux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/mutex.h>
862306a36Sopenharmony_ci#include <linux/skbuff.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/llc.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <net/llc.h>
1462306a36Sopenharmony_ci#include <net/llc_pdu.h>
1562306a36Sopenharmony_ci#include <net/stp.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* 01:80:c2:00:00:20 - 01:80:c2:00:00:2F */
1862306a36Sopenharmony_ci#define GARP_ADDR_MIN	0x20
1962306a36Sopenharmony_ci#define GARP_ADDR_MAX	0x2F
2062306a36Sopenharmony_ci#define GARP_ADDR_RANGE	(GARP_ADDR_MAX - GARP_ADDR_MIN)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct stp_proto __rcu *garp_protos[GARP_ADDR_RANGE + 1] __read_mostly;
2362306a36Sopenharmony_cistatic const struct stp_proto __rcu *stp_proto __read_mostly;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic struct llc_sap *sap __read_mostly;
2662306a36Sopenharmony_cistatic unsigned int sap_registered;
2762306a36Sopenharmony_cistatic DEFINE_MUTEX(stp_proto_mutex);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* Called under rcu_read_lock from LLC */
3062306a36Sopenharmony_cistatic int stp_pdu_rcv(struct sk_buff *skb, struct net_device *dev,
3162306a36Sopenharmony_ci		       struct packet_type *pt, struct net_device *orig_dev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	const struct ethhdr *eh = eth_hdr(skb);
3462306a36Sopenharmony_ci	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
3562306a36Sopenharmony_ci	const struct stp_proto *proto;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (pdu->ssap != LLC_SAP_BSPAN ||
3862306a36Sopenharmony_ci	    pdu->dsap != LLC_SAP_BSPAN ||
3962306a36Sopenharmony_ci	    pdu->ctrl_1 != LLC_PDU_TYPE_U)
4062306a36Sopenharmony_ci		goto err;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (eh->h_dest[5] >= GARP_ADDR_MIN && eh->h_dest[5] <= GARP_ADDR_MAX) {
4362306a36Sopenharmony_ci		proto = rcu_dereference(garp_protos[eh->h_dest[5] -
4462306a36Sopenharmony_ci						    GARP_ADDR_MIN]);
4562306a36Sopenharmony_ci		if (proto &&
4662306a36Sopenharmony_ci		    !ether_addr_equal(eh->h_dest, proto->group_address))
4762306a36Sopenharmony_ci			goto err;
4862306a36Sopenharmony_ci	} else
4962306a36Sopenharmony_ci		proto = rcu_dereference(stp_proto);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!proto)
5262306a36Sopenharmony_ci		goto err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	proto->rcv(proto, skb, dev);
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cierr:
5862306a36Sopenharmony_ci	kfree_skb(skb);
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint stp_proto_register(const struct stp_proto *proto)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int err = 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	mutex_lock(&stp_proto_mutex);
6762306a36Sopenharmony_ci	if (sap_registered++ == 0) {
6862306a36Sopenharmony_ci		sap = llc_sap_open(LLC_SAP_BSPAN, stp_pdu_rcv);
6962306a36Sopenharmony_ci		if (!sap) {
7062306a36Sopenharmony_ci			err = -ENOMEM;
7162306a36Sopenharmony_ci			goto out;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci	if (is_zero_ether_addr(proto->group_address))
7562306a36Sopenharmony_ci		rcu_assign_pointer(stp_proto, proto);
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		rcu_assign_pointer(garp_protos[proto->group_address[5] -
7862306a36Sopenharmony_ci					       GARP_ADDR_MIN], proto);
7962306a36Sopenharmony_ciout:
8062306a36Sopenharmony_ci	mutex_unlock(&stp_proto_mutex);
8162306a36Sopenharmony_ci	return err;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stp_proto_register);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid stp_proto_unregister(const struct stp_proto *proto)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	mutex_lock(&stp_proto_mutex);
8862306a36Sopenharmony_ci	if (is_zero_ether_addr(proto->group_address))
8962306a36Sopenharmony_ci		RCU_INIT_POINTER(stp_proto, NULL);
9062306a36Sopenharmony_ci	else
9162306a36Sopenharmony_ci		RCU_INIT_POINTER(garp_protos[proto->group_address[5] -
9262306a36Sopenharmony_ci					       GARP_ADDR_MIN], NULL);
9362306a36Sopenharmony_ci	synchronize_rcu();
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (--sap_registered == 0)
9662306a36Sopenharmony_ci		llc_sap_put(sap);
9762306a36Sopenharmony_ci	mutex_unlock(&stp_proto_mutex);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(stp_proto_unregister);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
102