11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * llc_input.c - Minimal input path for LLC 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 1997 by Procom Technology, Inc. 51da177e4SLinus Torvalds * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program can be redistributed or modified under the terms of the 81da177e4SLinus Torvalds * GNU General Public License as published by the Free Software Foundation. 91da177e4SLinus Torvalds * This program is distributed without any warranty or implied warranty 101da177e4SLinus Torvalds * of merchantability or fitness for a particular purpose. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * See the GNU General Public License for more details. 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds #include <linux/netdevice.h> 151da177e4SLinus Torvalds #include <net/llc.h> 161da177e4SLinus Torvalds #include <net/llc_pdu.h> 171da177e4SLinus Torvalds #include <net/llc_sap.h> 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds #if 0 201da177e4SLinus Torvalds #define dprintk(args...) printk(KERN_DEBUG args) 211da177e4SLinus Torvalds #else 221da177e4SLinus Torvalds #define dprintk(args...) 231da177e4SLinus Torvalds #endif 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds /* 261da177e4SLinus Torvalds * Packet handler for the station, registerable because in the minimal 271da177e4SLinus Torvalds * LLC core that is taking shape only the very minimal subset of LLC that 281da177e4SLinus Torvalds * is needed for things like IPX, Appletalk, etc will stay, with all the 291da177e4SLinus Torvalds * rest in the llc1 and llc2 modules. 301da177e4SLinus Torvalds */ 311da177e4SLinus Torvalds static void (*llc_station_handler)(struct sk_buff *skb); 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds /* 341da177e4SLinus Torvalds * Packet handlers for LLC_DEST_SAP and LLC_DEST_CONN. 351da177e4SLinus Torvalds */ 361da177e4SLinus Torvalds static void (*llc_type_handlers[2])(struct llc_sap *sap, 371da177e4SLinus Torvalds struct sk_buff *skb); 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, 401da177e4SLinus Torvalds struct sk_buff *skb)) 411da177e4SLinus Torvalds { 421da177e4SLinus Torvalds if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) 431da177e4SLinus Torvalds llc_type_handlers[type - 1] = handler; 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds void llc_remove_pack(int type) 471da177e4SLinus Torvalds { 481da177e4SLinus Torvalds if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) 491da177e4SLinus Torvalds llc_type_handlers[type - 1] = NULL; 501da177e4SLinus Torvalds } 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds void llc_set_station_handler(void (*handler)(struct sk_buff *skb)) 531da177e4SLinus Torvalds { 541da177e4SLinus Torvalds llc_station_handler = handler; 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds /** 581da177e4SLinus Torvalds * llc_pdu_type - returns which LLC component must handle for PDU 591da177e4SLinus Torvalds * @skb: input skb 601da177e4SLinus Torvalds * 611da177e4SLinus Torvalds * This function returns which LLC component must handle this PDU. 621da177e4SLinus Torvalds */ 631da177e4SLinus Torvalds static __inline__ int llc_pdu_type(struct sk_buff *skb) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds int type = LLC_DEST_CONN; /* I-PDU or S-PDU type */ 661da177e4SLinus Torvalds struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb); 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) != LLC_PDU_TYPE_U) 691da177e4SLinus Torvalds goto out; 701da177e4SLinus Torvalds switch (LLC_U_PDU_CMD(pdu)) { 711da177e4SLinus Torvalds case LLC_1_PDU_CMD_XID: 721da177e4SLinus Torvalds case LLC_1_PDU_CMD_UI: 731da177e4SLinus Torvalds case LLC_1_PDU_CMD_TEST: 741da177e4SLinus Torvalds type = LLC_DEST_SAP; 751da177e4SLinus Torvalds break; 761da177e4SLinus Torvalds case LLC_2_PDU_CMD_SABME: 771da177e4SLinus Torvalds case LLC_2_PDU_CMD_DISC: 781da177e4SLinus Torvalds case LLC_2_PDU_RSP_UA: 791da177e4SLinus Torvalds case LLC_2_PDU_RSP_DM: 801da177e4SLinus Torvalds case LLC_2_PDU_RSP_FRMR: 811da177e4SLinus Torvalds break; 821da177e4SLinus Torvalds default: 831da177e4SLinus Torvalds type = LLC_DEST_INVALID; 841da177e4SLinus Torvalds break; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds out: 871da177e4SLinus Torvalds return type; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /** 911da177e4SLinus Torvalds * llc_fixup_skb - initializes skb pointers 921da177e4SLinus Torvalds * @skb: This argument points to incoming skb 931da177e4SLinus Torvalds * 941da177e4SLinus Torvalds * Initializes internal skb pointer to start of network layer by deriving 951da177e4SLinus Torvalds * length of LLC header; finds length of LLC control field in LLC header 961da177e4SLinus Torvalds * by looking at the two lowest-order bits of the first control field 971da177e4SLinus Torvalds * byte; field is either 3 or 4 bytes long. 981da177e4SLinus Torvalds */ 991da177e4SLinus Torvalds static inline int llc_fixup_skb(struct sk_buff *skb) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds u8 llc_len = 2; 102096f0eb1SJochen Friedrich struct llc_pdu_un *pdu; 1031da177e4SLinus Torvalds 104af426d32SArnaldo Carvalho de Melo if (unlikely(!pskb_may_pull(skb, sizeof(*pdu)))) 1051da177e4SLinus Torvalds return 0; 1061da177e4SLinus Torvalds 107096f0eb1SJochen Friedrich pdu = (struct llc_pdu_un *)skb->data; 1081da177e4SLinus Torvalds if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U) 1091da177e4SLinus Torvalds llc_len = 1; 1101da177e4SLinus Torvalds llc_len += 2; 111096f0eb1SJochen Friedrich 112096f0eb1SJochen Friedrich if (unlikely(!pskb_may_pull(skb, llc_len))) 113096f0eb1SJochen Friedrich return 0; 114096f0eb1SJochen Friedrich 1151da177e4SLinus Torvalds skb->h.raw += llc_len; 1161da177e4SLinus Torvalds skb_pull(skb, llc_len); 1171da177e4SLinus Torvalds if (skb->protocol == htons(ETH_P_802_2)) { 1181da177e4SLinus Torvalds u16 pdulen = eth_hdr(skb)->h_proto, 1191da177e4SLinus Torvalds data_size = ntohs(pdulen) - llc_len; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds skb_trim(skb, data_size); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds return 1; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds /** 1271da177e4SLinus Torvalds * llc_rcv - 802.2 entry point from net lower layers 1281da177e4SLinus Torvalds * @skb: received pdu 1291da177e4SLinus Torvalds * @dev: device that receive pdu 1301da177e4SLinus Torvalds * @pt: packet type 1311da177e4SLinus Torvalds * 1321da177e4SLinus Torvalds * When the system receives a 802.2 frame this function is called. It 1331da177e4SLinus Torvalds * checks SAP and connection of received pdu and passes frame to 1341da177e4SLinus Torvalds * llc_{station,sap,conn}_rcv for sending to proper state machine. If 1351da177e4SLinus Torvalds * the frame is related to a busy connection (a connection is sending 1361da177e4SLinus Torvalds * data now), it queues this frame in the connection's backlog. 1371da177e4SLinus Torvalds */ 1381da177e4SLinus Torvalds int llc_rcv(struct sk_buff *skb, struct net_device *dev, 139f2ccd8faSDavid S. Miller struct packet_type *pt, struct net_device *orig_dev) 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds struct llc_sap *sap; 1421da177e4SLinus Torvalds struct llc_pdu_sn *pdu; 1431da177e4SLinus Torvalds int dest; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds /* 1461da177e4SLinus Torvalds * When the interface is in promisc. mode, drop all the crap that it 1471da177e4SLinus Torvalds * receives, do not try to analyse it. 1481da177e4SLinus Torvalds */ 1491da177e4SLinus Torvalds if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { 1501da177e4SLinus Torvalds dprintk("%s: PACKET_OTHERHOST\n", __FUNCTION__); 1511da177e4SLinus Torvalds goto drop; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds skb = skb_share_check(skb, GFP_ATOMIC); 1541da177e4SLinus Torvalds if (unlikely(!skb)) 1551da177e4SLinus Torvalds goto out; 1561da177e4SLinus Torvalds if (unlikely(!llc_fixup_skb(skb))) 1571da177e4SLinus Torvalds goto drop; 1581da177e4SLinus Torvalds pdu = llc_pdu_sn_hdr(skb); 1591da177e4SLinus Torvalds if (unlikely(!pdu->dsap)) /* NULL DSAP, refer to station */ 1601da177e4SLinus Torvalds goto handle_station; 1611da177e4SLinus Torvalds sap = llc_sap_find(pdu->dsap); 1621da177e4SLinus Torvalds if (unlikely(!sap)) {/* unknown SAP */ 1631da177e4SLinus Torvalds dprintk("%s: llc_sap_find(%02X) failed!\n", __FUNCTION__, 1641da177e4SLinus Torvalds pdu->dsap); 1651da177e4SLinus Torvalds goto drop; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds /* 1681da177e4SLinus Torvalds * First the upper layer protocols that don't need the full 1691da177e4SLinus Torvalds * LLC functionality 1701da177e4SLinus Torvalds */ 1711da177e4SLinus Torvalds if (sap->rcv_func) { 172f2ccd8faSDavid S. Miller sap->rcv_func(skb, dev, pt, orig_dev); 1736e2144b7SArnaldo Carvalho de Melo goto out_put; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds dest = llc_pdu_type(skb); 1761da177e4SLinus Torvalds if (unlikely(!dest || !llc_type_handlers[dest - 1])) 1776e2144b7SArnaldo Carvalho de Melo goto drop_put; 1781da177e4SLinus Torvalds llc_type_handlers[dest - 1](sap, skb); 1796e2144b7SArnaldo Carvalho de Melo out_put: 1806e2144b7SArnaldo Carvalho de Melo llc_sap_put(sap); 1811da177e4SLinus Torvalds out: 1821da177e4SLinus Torvalds return 0; 1831da177e4SLinus Torvalds drop: 1841da177e4SLinus Torvalds kfree_skb(skb); 1851da177e4SLinus Torvalds goto out; 1866e2144b7SArnaldo Carvalho de Melo drop_put: 1876e2144b7SArnaldo Carvalho de Melo kfree_skb(skb); 1886e2144b7SArnaldo Carvalho de Melo goto out_put; 1891da177e4SLinus Torvalds handle_station: 1901da177e4SLinus Torvalds if (!llc_station_handler) 1911da177e4SLinus Torvalds goto drop; 1921da177e4SLinus Torvalds llc_station_handler(skb); 1931da177e4SLinus Torvalds goto out; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds EXPORT_SYMBOL(llc_add_pack); 1971da177e4SLinus Torvalds EXPORT_SYMBOL(llc_remove_pack); 1981da177e4SLinus Torvalds EXPORT_SYMBOL(llc_set_station_handler); 199