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