18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SNAP data link layer. Derived from 802.2 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk>, 68c2ecf20Sopenharmony_ci * from the 802.2 layer by Greg Page. 78c2ecf20Sopenharmony_ci * Merged in additions from Greg Page's psnap.c. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <net/datalink.h> 158c2ecf20Sopenharmony_ci#include <net/llc.h> 168c2ecf20Sopenharmony_ci#include <net/psnap.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <linux/in.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/rculist.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic LIST_HEAD(snap_list); 238c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(snap_lock); 248c2ecf20Sopenharmony_cistatic struct llc_sap *snap_sap; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Find a snap client by matching the 5 bytes. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic struct datalink_proto *find_snap_client(const unsigned char *desc) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct datalink_proto *proto = NULL, *p; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci list_for_each_entry_rcu(p, &snap_list, node, lockdep_is_held(&snap_lock)) { 348c2ecf20Sopenharmony_ci if (!memcmp(p->type, desc, 5)) { 358c2ecf20Sopenharmony_ci proto = p; 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci return proto; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * A SNAP packet has arrived 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic int snap_rcv(struct sk_buff *skb, struct net_device *dev, 468c2ecf20Sopenharmony_ci struct packet_type *pt, struct net_device *orig_dev) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int rc = 1; 498c2ecf20Sopenharmony_ci struct datalink_proto *proto; 508c2ecf20Sopenharmony_ci static struct packet_type snap_packet_type = { 518c2ecf20Sopenharmony_ci .type = cpu_to_be16(ETH_P_SNAP), 528c2ecf20Sopenharmony_ci }; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, 5))) 558c2ecf20Sopenharmony_ci goto drop; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rcu_read_lock(); 588c2ecf20Sopenharmony_ci proto = find_snap_client(skb_transport_header(skb)); 598c2ecf20Sopenharmony_ci if (proto) { 608c2ecf20Sopenharmony_ci /* Pass the frame on. */ 618c2ecf20Sopenharmony_ci skb->transport_header += 5; 628c2ecf20Sopenharmony_ci skb_pull_rcsum(skb, 5); 638c2ecf20Sopenharmony_ci rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci rcu_read_unlock(); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (unlikely(!proto)) 688c2ecf20Sopenharmony_ci goto drop; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciout: 718c2ecf20Sopenharmony_ci return rc; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cidrop: 748c2ecf20Sopenharmony_ci kfree_skb(skb); 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * Put a SNAP header on a frame and pass to 802.2 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic int snap_request(struct datalink_proto *dl, 828c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 *dest) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci memcpy(skb_push(skb, 5), dl->type, 5); 858c2ecf20Sopenharmony_ci llc_build_and_send_ui_pkt(snap_sap, skb, dest, snap_sap->laddr.lsap); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Set up the SNAP layer 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_snap_client); 938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_snap_client); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const char snap_err_msg[] __initconst = 968c2ecf20Sopenharmony_ci KERN_CRIT "SNAP - unable to register with 802.2\n"; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int __init snap_init(void) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci snap_sap = llc_sap_open(0xAA, snap_rcv); 1018c2ecf20Sopenharmony_ci if (!snap_sap) { 1028c2ecf20Sopenharmony_ci printk(snap_err_msg); 1038c2ecf20Sopenharmony_ci return -EBUSY; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cimodule_init(snap_init); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void __exit snap_exit(void) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci llc_sap_put(snap_sap); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cimodule_exit(snap_exit); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Register SNAP clients. We don't yet use this for IP. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistruct datalink_proto *register_snap_client(const unsigned char *desc, 1238c2ecf20Sopenharmony_ci int (*rcvfunc)(struct sk_buff *, 1248c2ecf20Sopenharmony_ci struct net_device *, 1258c2ecf20Sopenharmony_ci struct packet_type *, 1268c2ecf20Sopenharmony_ci struct net_device *)) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct datalink_proto *proto = NULL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_bh(&snap_lock); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (find_snap_client(desc)) 1338c2ecf20Sopenharmony_ci goto out; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci proto = kmalloc(sizeof(*proto), GFP_ATOMIC); 1368c2ecf20Sopenharmony_ci if (proto) { 1378c2ecf20Sopenharmony_ci memcpy(proto->type, desc, 5); 1388c2ecf20Sopenharmony_ci proto->rcvfunc = rcvfunc; 1398c2ecf20Sopenharmony_ci proto->header_length = 5 + 3; /* snap + 802.2 */ 1408c2ecf20Sopenharmony_ci proto->request = snap_request; 1418c2ecf20Sopenharmony_ci list_add_rcu(&proto->node, &snap_list); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ciout: 1448c2ecf20Sopenharmony_ci spin_unlock_bh(&snap_lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return proto; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Unregister SNAP clients. Protocols no longer want to play with us ... 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_civoid unregister_snap_client(struct datalink_proto *proto) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci spin_lock_bh(&snap_lock); 1558c2ecf20Sopenharmony_ci list_del_rcu(&proto->node); 1568c2ecf20Sopenharmony_ci spin_unlock_bh(&snap_lock); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci synchronize_net(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci kfree(proto); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 164