blob: 48c21184bf2ca185983e9c1cc950021f084119df [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * llc_station.c - station component of LLC
3 *
4 * Copyright (c) 1997 by Procom Technology, Inc.
5 * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6 *
7 * This program can be redistributed or modified under the terms of the
8 * GNU General Public License as published by the Free Software Foundation.
9 * This program is distributed without any warranty or implied warranty
10 * of merchantability or fitness for a particular purpose.
11 *
12 * See the GNU General Public License for more details.
13 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/init.h>
15#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090016#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <net/llc.h>
18#include <net/llc_sap.h>
19#include <net/llc_conn.h>
20#include <net/llc_c_ac.h>
21#include <net/llc_s_ac.h>
22#include <net/llc_c_ev.h>
23#include <net/llc_c_st.h>
24#include <net/llc_s_ev.h>
25#include <net/llc_s_st.h>
26#include <net/llc_pdu.h>
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028typedef int (*llc_station_ev_t)(struct sk_buff *skb);
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030typedef int (*llc_station_action_t)(struct sk_buff *skb);
31
32/* Station component state table structure */
33struct llc_station_state_trans {
34 llc_station_ev_t ev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 llc_station_action_t *ev_actions;
36};
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
39{
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
41
Ben Hutchings025e3632012-09-15 17:11:18 +000042 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
44 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
45 !pdu->dsap ? 0 : 1; /* NULL DSAP value */
46}
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
49{
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
51
Ben Hutchings025e3632012-09-15 17:11:18 +000052 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
54 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
55 !pdu->dsap ? 0 : 1; /* NULL DSAP */
56}
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058static int llc_station_ac_send_xid_r(struct sk_buff *skb)
59{
60 u8 mac_da[ETH_ALEN], dsap;
61 int rc = 1;
Joonwoo Parkf83f1762008-03-31 21:02:47 -070062 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U,
63 sizeof(struct llc_xid_info));
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65 if (!nskb)
66 goto out;
67 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 llc_pdu_decode_sa(skb, mac_da);
69 llc_pdu_decode_ssap(skb, &dsap);
70 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
71 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127);
Joonwoo Parka5a04812008-03-28 16:28:36 -070072 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
Arnaldo Carvalho de Melo249ff1c2005-09-22 04:32:10 -030073 if (unlikely(rc))
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 goto free;
Ben Hutchings5ecf9ee2012-09-15 17:11:32 +000075 dev_queue_xmit(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076out:
77 return rc;
78free:
Sorin Dumitru91d27a82012-08-06 02:35:58 +000079 kfree_skb(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 goto out;
81}
82
83static int llc_station_ac_send_test_r(struct sk_buff *skb)
84{
85 u8 mac_da[ETH_ALEN], dsap;
86 int rc = 1;
Joonwoo Parkf83f1762008-03-31 21:02:47 -070087 u32 data_size;
88 struct sk_buff *nskb;
89
90 /* The test request command is type U (llc_len = 3) */
91 data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
92 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94 if (!nskb)
95 goto out;
96 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 llc_pdu_decode_sa(skb, mac_da);
98 llc_pdu_decode_ssap(skb, &dsap);
99 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
YOSHIFUJI Hideakid57b1862007-02-09 23:25:01 +0900100 llc_pdu_init_as_test_rsp(nskb, skb);
Joonwoo Parka5a04812008-03-28 16:28:36 -0700101 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
Arnaldo Carvalho de Melo249ff1c2005-09-22 04:32:10 -0300102 if (unlikely(rc))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 goto free;
Ben Hutchings5ecf9ee2012-09-15 17:11:32 +0000104 dev_queue_xmit(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105out:
106 return rc;
107free:
Sorin Dumitru91d27a82012-08-06 02:35:58 +0000108 kfree_skb(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 goto out;
110}
111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
113static llc_station_action_t llc_stat_up_state_actions_2[] = {
114 [0] = llc_station_ac_send_xid_r,
115 [1] = NULL,
116};
117
118static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
119 .ev = llc_stat_ev_rx_null_dsap_xid_c,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 .ev_actions = llc_stat_up_state_actions_2,
121};
122
123/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
124static llc_station_action_t llc_stat_up_state_actions_3[] = {
125 [0] = llc_station_ac_send_test_r,
126 [1] = NULL,
127};
128
129static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
130 .ev = llc_stat_ev_rx_null_dsap_test_c,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 .ev_actions = llc_stat_up_state_actions_3,
132};
133
134/* array of pointers; one to each transition */
135static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
Ben Hutchings025e3632012-09-15 17:11:18 +0000136 &llc_stat_up_state_trans_2,
137 &llc_stat_up_state_trans_3,
138 NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139};
140
141/**
142 * llc_exec_station_trans_actions - executes actions for transition
143 * @trans: Address of the transition
144 * @skb: Address of the event that caused the transition
145 *
146 * Executes actions of a transition of the station state machine. Returns
147 * 0 if all actions complete successfully, nonzero otherwise.
148 */
149static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans,
150 struct sk_buff *skb)
151{
152 u16 rc = 0;
153 llc_station_action_t *next_action = trans->ev_actions;
154
155 for (; next_action && *next_action; next_action++)
156 if ((*next_action)(skb))
157 rc = 1;
158 return rc;
159}
160
161/**
162 * llc_find_station_trans - finds transition for this event
163 * @skb: Address of the event
164 *
165 * Search thru events of the current state of the station until list
166 * exhausted or it's obvious that the event is not valid for the current
167 * state. Returns the address of the transition if cound, %NULL otherwise.
168 */
169static struct llc_station_state_trans *
170 llc_find_station_trans(struct sk_buff *skb)
171{
172 int i = 0;
173 struct llc_station_state_trans *rc = NULL;
174 struct llc_station_state_trans **next_trans;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Ben Hutchings025e3632012-09-15 17:11:18 +0000176 for (next_trans = llc_stat_up_state_trans; next_trans[i]; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 if (!next_trans[i]->ev(skb)) {
178 rc = next_trans[i];
179 break;
180 }
181 return rc;
182}
183
184/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 * llc_station_rcv - send received pdu to the station state machine
186 * @skb: received frame.
187 *
188 * Sends data unit to station state machine.
189 */
190static void llc_station_rcv(struct sk_buff *skb)
191{
Ben Hutchings04d191c2012-09-15 17:11:25 +0000192 struct llc_station_state_trans *trans;
193
194 trans = llc_find_station_trans(skb);
195 if (trans)
196 llc_exec_station_trans_actions(trans, skb);
197 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198}
199
Ben Hutchings60249352012-08-13 02:49:59 +0000200void __init llc_station_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
Ben Hutchingsaadf31d2012-08-13 02:50:55 +0000202 llc_set_station_handler(llc_station_rcv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
Ben Hutchingsf4f87202012-08-13 02:50:43 +0000205void llc_station_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 llc_set_station_handler(NULL);
208}