11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * llc_station.c - station component of 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/init.h> 151da177e4SLinus Torvalds #include <linux/module.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 171da177e4SLinus Torvalds #include <net/llc.h> 181da177e4SLinus Torvalds #include <net/llc_sap.h> 191da177e4SLinus Torvalds #include <net/llc_conn.h> 201da177e4SLinus Torvalds #include <net/llc_c_ac.h> 211da177e4SLinus Torvalds #include <net/llc_s_ac.h> 221da177e4SLinus Torvalds #include <net/llc_c_ev.h> 231da177e4SLinus Torvalds #include <net/llc_c_st.h> 241da177e4SLinus Torvalds #include <net/llc_s_ev.h> 251da177e4SLinus Torvalds #include <net/llc_s_st.h> 261da177e4SLinus Torvalds #include <net/llc_pdu.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds typedef int (*llc_station_ev_t)(struct sk_buff *skb); 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds typedef int (*llc_station_action_t)(struct sk_buff *skb); 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds /* Station component state table structure */ 331da177e4SLinus Torvalds struct llc_station_state_trans { 341da177e4SLinus Torvalds llc_station_ev_t ev; 351da177e4SLinus Torvalds llc_station_action_t *ev_actions; 361da177e4SLinus Torvalds }; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 411da177e4SLinus Torvalds 42025e3633SBen Hutchings return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 431da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 441da177e4SLinus Torvalds LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && 451da177e4SLinus Torvalds !pdu->dsap ? 0 : 1; /* NULL DSAP value */ 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 491da177e4SLinus Torvalds { 501da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 511da177e4SLinus Torvalds 52025e3633SBen Hutchings return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 531da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 541da177e4SLinus Torvalds LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && 551da177e4SLinus Torvalds !pdu->dsap ? 0 : 1; /* NULL DSAP */ 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static int llc_station_ac_send_xid_r(struct sk_buff *skb) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 611da177e4SLinus Torvalds int rc = 1; 62f83f1768SJoonwoo Park struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 63f83f1768SJoonwoo Park sizeof(struct llc_xid_info)); 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds if (!nskb) 661da177e4SLinus Torvalds goto out; 671da177e4SLinus Torvalds rc = 0; 681da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 691da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 701da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 711da177e4SLinus Torvalds llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 72a5a04819SJoonwoo Park rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 73249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 741da177e4SLinus Torvalds goto free; 755ecf9eeaSBen Hutchings dev_queue_xmit(nskb); 761da177e4SLinus Torvalds out: 771da177e4SLinus Torvalds return rc; 781da177e4SLinus Torvalds free: 7991d27a86SSorin Dumitru kfree_skb(nskb); 801da177e4SLinus Torvalds goto out; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static int llc_station_ac_send_test_r(struct sk_buff *skb) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 861da177e4SLinus Torvalds int rc = 1; 87f83f1768SJoonwoo Park u32 data_size; 88f83f1768SJoonwoo Park struct sk_buff *nskb; 89f83f1768SJoonwoo Park 90f83f1768SJoonwoo Park /* The test request command is type U (llc_len = 3) */ 91f83f1768SJoonwoo Park data_size = ntohs(eth_hdr(skb)->h_proto) - 3; 92f83f1768SJoonwoo Park nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size); 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds if (!nskb) 951da177e4SLinus Torvalds goto out; 961da177e4SLinus Torvalds rc = 0; 971da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 981da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 991da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 1001da177e4SLinus Torvalds llc_pdu_init_as_test_rsp(nskb, skb); 101a5a04819SJoonwoo Park rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 102249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 1031da177e4SLinus Torvalds goto free; 1045ecf9eeaSBen Hutchings dev_queue_xmit(nskb); 1051da177e4SLinus Torvalds out: 1061da177e4SLinus Torvalds return rc; 1071da177e4SLinus Torvalds free: 10891d27a86SSorin Dumitru kfree_skb(nskb); 1091da177e4SLinus Torvalds goto out; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 1131da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_2[] = { 1141da177e4SLinus Torvalds [0] = llc_station_ac_send_xid_r, 1151da177e4SLinus Torvalds [1] = NULL, 1161da177e4SLinus Torvalds }; 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 1191da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_xid_c, 1201da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_2, 1211da177e4SLinus Torvalds }; 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 1241da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_3[] = { 1251da177e4SLinus Torvalds [0] = llc_station_ac_send_test_r, 1261da177e4SLinus Torvalds [1] = NULL, 1271da177e4SLinus Torvalds }; 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 1301da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_test_c, 1311da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_3, 1321da177e4SLinus Torvalds }; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* array of pointers; one to each transition */ 1351da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 136025e3633SBen Hutchings &llc_stat_up_state_trans_2, 137025e3633SBen Hutchings &llc_stat_up_state_trans_3, 138025e3633SBen Hutchings NULL, 1391da177e4SLinus Torvalds }; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds /** 1421da177e4SLinus Torvalds * llc_exec_station_trans_actions - executes actions for transition 1431da177e4SLinus Torvalds * @trans: Address of the transition 1441da177e4SLinus Torvalds * @skb: Address of the event that caused the transition 1451da177e4SLinus Torvalds * 1461da177e4SLinus Torvalds * Executes actions of a transition of the station state machine. Returns 1471da177e4SLinus Torvalds * 0 if all actions complete successfully, nonzero otherwise. 1481da177e4SLinus Torvalds */ 1491da177e4SLinus Torvalds static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 1501da177e4SLinus Torvalds struct sk_buff *skb) 1511da177e4SLinus Torvalds { 1521da177e4SLinus Torvalds u16 rc = 0; 1531da177e4SLinus Torvalds llc_station_action_t *next_action = trans->ev_actions; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds for (; next_action && *next_action; next_action++) 1561da177e4SLinus Torvalds if ((*next_action)(skb)) 1571da177e4SLinus Torvalds rc = 1; 1581da177e4SLinus Torvalds return rc; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /** 1621da177e4SLinus Torvalds * llc_find_station_trans - finds transition for this event 1631da177e4SLinus Torvalds * @skb: Address of the event 1641da177e4SLinus Torvalds * 1651da177e4SLinus Torvalds * Search thru events of the current state of the station until list 1661da177e4SLinus Torvalds * exhausted or it's obvious that the event is not valid for the current 1671da177e4SLinus Torvalds * state. Returns the address of the transition if cound, %NULL otherwise. 1681da177e4SLinus Torvalds */ 1691da177e4SLinus Torvalds static struct llc_station_state_trans * 1701da177e4SLinus Torvalds llc_find_station_trans(struct sk_buff *skb) 1711da177e4SLinus Torvalds { 1721da177e4SLinus Torvalds int i = 0; 1731da177e4SLinus Torvalds struct llc_station_state_trans *rc = NULL; 1741da177e4SLinus Torvalds struct llc_station_state_trans **next_trans; 1751da177e4SLinus Torvalds 176025e3633SBen Hutchings for (next_trans = llc_stat_up_state_trans; next_trans[i]; i++) 1771da177e4SLinus Torvalds if (!next_trans[i]->ev(skb)) { 1781da177e4SLinus Torvalds rc = next_trans[i]; 1791da177e4SLinus Torvalds break; 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds return rc; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds /** 1851da177e4SLinus Torvalds * llc_station_rcv - send received pdu to the station state machine 1861da177e4SLinus Torvalds * @skb: received frame. 1871da177e4SLinus Torvalds * 1881da177e4SLinus Torvalds * Sends data unit to station state machine. 1891da177e4SLinus Torvalds */ 1901da177e4SLinus Torvalds static void llc_station_rcv(struct sk_buff *skb) 1911da177e4SLinus Torvalds { 19204d191c2SBen Hutchings struct llc_station_state_trans *trans; 19304d191c2SBen Hutchings 19404d191c2SBen Hutchings trans = llc_find_station_trans(skb); 19504d191c2SBen Hutchings if (trans) 19604d191c2SBen Hutchings llc_exec_station_trans_actions(trans, skb); 19704d191c2SBen Hutchings kfree_skb(skb); 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2006024935fSBen Hutchings void __init llc_station_init(void) 2011da177e4SLinus Torvalds { 202aadf31deSBen Hutchings llc_set_station_handler(llc_station_rcv); 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 205f4f8720fSBen Hutchings void llc_station_exit(void) 2061da177e4SLinus Torvalds { 2071da177e4SLinus Torvalds llc_set_station_handler(NULL); 2081da177e4SLinus Torvalds } 209