xref: /openbmc/linux/net/llc/llc_station.c (revision 5ecf9eea)
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