18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	STP SAP demux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/mutex.h>
88c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
98c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
108c2ecf20Sopenharmony_ci#include <linux/llc.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <net/llc.h>
148c2ecf20Sopenharmony_ci#include <net/llc_pdu.h>
158c2ecf20Sopenharmony_ci#include <net/stp.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* 01:80:c2:00:00:20 - 01:80:c2:00:00:2F */
188c2ecf20Sopenharmony_ci#define GARP_ADDR_MIN	0x20
198c2ecf20Sopenharmony_ci#define GARP_ADDR_MAX	0x2F
208c2ecf20Sopenharmony_ci#define GARP_ADDR_RANGE	(GARP_ADDR_MAX - GARP_ADDR_MIN)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const struct stp_proto __rcu *garp_protos[GARP_ADDR_RANGE + 1] __read_mostly;
238c2ecf20Sopenharmony_cistatic const struct stp_proto __rcu *stp_proto __read_mostly;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct llc_sap *sap __read_mostly;
268c2ecf20Sopenharmony_cistatic unsigned int sap_registered;
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(stp_proto_mutex);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Called under rcu_read_lock from LLC */
308c2ecf20Sopenharmony_cistatic int stp_pdu_rcv(struct sk_buff *skb, struct net_device *dev,
318c2ecf20Sopenharmony_ci		       struct packet_type *pt, struct net_device *orig_dev)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	const struct ethhdr *eh = eth_hdr(skb);
348c2ecf20Sopenharmony_ci	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
358c2ecf20Sopenharmony_ci	const struct stp_proto *proto;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (pdu->ssap != LLC_SAP_BSPAN ||
388c2ecf20Sopenharmony_ci	    pdu->dsap != LLC_SAP_BSPAN ||
398c2ecf20Sopenharmony_ci	    pdu->ctrl_1 != LLC_PDU_TYPE_U)
408c2ecf20Sopenharmony_ci		goto err;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (eh->h_dest[5] >= GARP_ADDR_MIN && eh->h_dest[5] <= GARP_ADDR_MAX) {
438c2ecf20Sopenharmony_ci		proto = rcu_dereference(garp_protos[eh->h_dest[5] -
448c2ecf20Sopenharmony_ci						    GARP_ADDR_MIN]);
458c2ecf20Sopenharmony_ci		if (proto &&
468c2ecf20Sopenharmony_ci		    !ether_addr_equal(eh->h_dest, proto->group_address))
478c2ecf20Sopenharmony_ci			goto err;
488c2ecf20Sopenharmony_ci	} else
498c2ecf20Sopenharmony_ci		proto = rcu_dereference(stp_proto);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!proto)
528c2ecf20Sopenharmony_ci		goto err;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	proto->rcv(proto, skb, dev);
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cierr:
588c2ecf20Sopenharmony_ci	kfree_skb(skb);
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciint stp_proto_register(const struct stp_proto *proto)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int err = 0;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	mutex_lock(&stp_proto_mutex);
678c2ecf20Sopenharmony_ci	if (sap_registered++ == 0) {
688c2ecf20Sopenharmony_ci		sap = llc_sap_open(LLC_SAP_BSPAN, stp_pdu_rcv);
698c2ecf20Sopenharmony_ci		if (!sap) {
708c2ecf20Sopenharmony_ci			err = -ENOMEM;
718c2ecf20Sopenharmony_ci			goto out;
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(proto->group_address))
758c2ecf20Sopenharmony_ci		rcu_assign_pointer(stp_proto, proto);
768c2ecf20Sopenharmony_ci	else
778c2ecf20Sopenharmony_ci		rcu_assign_pointer(garp_protos[proto->group_address[5] -
788c2ecf20Sopenharmony_ci					       GARP_ADDR_MIN], proto);
798c2ecf20Sopenharmony_ciout:
808c2ecf20Sopenharmony_ci	mutex_unlock(&stp_proto_mutex);
818c2ecf20Sopenharmony_ci	return err;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stp_proto_register);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid stp_proto_unregister(const struct stp_proto *proto)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	mutex_lock(&stp_proto_mutex);
888c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(proto->group_address))
898c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(stp_proto, NULL);
908c2ecf20Sopenharmony_ci	else
918c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(garp_protos[proto->group_address[5] -
928c2ecf20Sopenharmony_ci					       GARP_ADDR_MIN], NULL);
938c2ecf20Sopenharmony_ci	synchronize_rcu();
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (--sap_registered == 0)
968c2ecf20Sopenharmony_ci		llc_sap_put(sap);
978c2ecf20Sopenharmony_ci	mutex_unlock(&stp_proto_mutex);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stp_proto_unregister);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
102