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 1215185db09SDavid S. Miller if (unlikely(pskb_trim_rcsum(skb, data_size))) 1225185db09SDavid S. Miller return 0; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds return 1; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds /** 1281da177e4SLinus Torvalds * llc_rcv - 802.2 entry point from net lower layers 1291da177e4SLinus Torvalds * @skb: received pdu 1301da177e4SLinus Torvalds * @dev: device that receive pdu 1311da177e4SLinus Torvalds * @pt: packet type 1321da177e4SLinus Torvalds * 1331da177e4SLinus Torvalds * When the system receives a 802.2 frame this function is called. It 1341da177e4SLinus Torvalds * checks SAP and connection of received pdu and passes frame to 1351da177e4SLinus Torvalds * llc_{station,sap,conn}_rcv for sending to proper state machine. If 1361da177e4SLinus Torvalds * the frame is related to a busy connection (a connection is sending 1371da177e4SLinus Torvalds * data now), it queues this frame in the connection's backlog. 1381da177e4SLinus Torvalds */ 1391da177e4SLinus Torvalds int llc_rcv(struct sk_buff *skb, struct net_device *dev, 140f2ccd8faSDavid S. Miller struct packet_type *pt, struct net_device *orig_dev) 1411da177e4SLinus Torvalds { 1421da177e4SLinus Torvalds struct llc_sap *sap; 1431da177e4SLinus Torvalds struct llc_pdu_sn *pdu; 1441da177e4SLinus Torvalds int dest; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds /* 1471da177e4SLinus Torvalds * When the interface is in promisc. mode, drop all the crap that it 1481da177e4SLinus Torvalds * receives, do not try to analyse it. 1491da177e4SLinus Torvalds */ 1501da177e4SLinus Torvalds if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { 1511da177e4SLinus Torvalds dprintk("%s: PACKET_OTHERHOST\n", __FUNCTION__); 1521da177e4SLinus Torvalds goto drop; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds skb = skb_share_check(skb, GFP_ATOMIC); 1551da177e4SLinus Torvalds if (unlikely(!skb)) 1561da177e4SLinus Torvalds goto out; 1571da177e4SLinus Torvalds if (unlikely(!llc_fixup_skb(skb))) 1581da177e4SLinus Torvalds goto drop; 1591da177e4SLinus Torvalds pdu = llc_pdu_sn_hdr(skb); 1601da177e4SLinus Torvalds if (unlikely(!pdu->dsap)) /* NULL DSAP, refer to station */ 1611da177e4SLinus Torvalds goto handle_station; 1621da177e4SLinus Torvalds sap = llc_sap_find(pdu->dsap); 1631da177e4SLinus Torvalds if (unlikely(!sap)) {/* unknown SAP */ 1641da177e4SLinus Torvalds dprintk("%s: llc_sap_find(%02X) failed!\n", __FUNCTION__, 1651da177e4SLinus Torvalds pdu->dsap); 1661da177e4SLinus Torvalds goto drop; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds /* 1691da177e4SLinus Torvalds * First the upper layer protocols that don't need the full 1701da177e4SLinus Torvalds * LLC functionality 1711da177e4SLinus Torvalds */ 1721da177e4SLinus Torvalds if (sap->rcv_func) { 173f2ccd8faSDavid S. Miller sap->rcv_func(skb, dev, pt, orig_dev); 1746e2144b7SArnaldo Carvalho de Melo goto out_put; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds dest = llc_pdu_type(skb); 1771da177e4SLinus Torvalds if (unlikely(!dest || !llc_type_handlers[dest - 1])) 1786e2144b7SArnaldo Carvalho de Melo goto drop_put; 1791da177e4SLinus Torvalds llc_type_handlers[dest - 1](sap, skb); 1806e2144b7SArnaldo Carvalho de Melo out_put: 1816e2144b7SArnaldo Carvalho de Melo llc_sap_put(sap); 1821da177e4SLinus Torvalds out: 1831da177e4SLinus Torvalds return 0; 1841da177e4SLinus Torvalds drop: 1851da177e4SLinus Torvalds kfree_skb(skb); 1861da177e4SLinus Torvalds goto out; 1876e2144b7SArnaldo Carvalho de Melo drop_put: 1886e2144b7SArnaldo Carvalho de Melo kfree_skb(skb); 1896e2144b7SArnaldo Carvalho de Melo goto out_put; 1901da177e4SLinus Torvalds handle_station: 1911da177e4SLinus Torvalds if (!llc_station_handler) 1921da177e4SLinus Torvalds goto drop; 1931da177e4SLinus Torvalds llc_station_handler(skb); 1941da177e4SLinus Torvalds goto out; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds EXPORT_SYMBOL(llc_add_pack); 1981da177e4SLinus Torvalds EXPORT_SYMBOL(llc_remove_pack); 1991da177e4SLinus Torvalds EXPORT_SYMBOL(llc_set_station_handler); 200