diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/llc/llc_sap.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/llc/llc_sap.c')
-rw-r--r-- | net/llc/llc_sap.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c new file mode 100644 index 00000000000..965c94eb4bb --- /dev/null +++ b/net/llc/llc_sap.c | |||
@@ -0,0 +1,316 @@ | |||
1 | /* | ||
2 | * llc_sap.c - driver routines for SAP component. | ||
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 | */ | ||
14 | |||
15 | #include <net/llc.h> | ||
16 | #include <net/llc_if.h> | ||
17 | #include <net/llc_conn.h> | ||
18 | #include <net/llc_pdu.h> | ||
19 | #include <net/llc_sap.h> | ||
20 | #include <net/llc_s_ac.h> | ||
21 | #include <net/llc_s_ev.h> | ||
22 | #include <net/llc_s_st.h> | ||
23 | #include <net/sock.h> | ||
24 | #include <linux/tcp.h> | ||
25 | #include <linux/llc.h> | ||
26 | |||
27 | /** | ||
28 | * llc_alloc_frame - allocates sk_buff for frame | ||
29 | * | ||
30 | * Allocates an sk_buff for frame and initializes sk_buff fields. | ||
31 | * Returns allocated skb or %NULL when out of memory. | ||
32 | */ | ||
33 | struct sk_buff *llc_alloc_frame(void) | ||
34 | { | ||
35 | struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC); | ||
36 | |||
37 | if (skb) { | ||
38 | skb_reserve(skb, 50); | ||
39 | skb->nh.raw = skb->h.raw = skb->data; | ||
40 | skb->protocol = htons(ETH_P_802_2); | ||
41 | skb->dev = dev_base->next; | ||
42 | skb->mac.raw = skb->head; | ||
43 | } | ||
44 | return skb; | ||
45 | } | ||
46 | |||
47 | void llc_save_primitive(struct sk_buff* skb, u8 prim) | ||
48 | { | ||
49 | struct sockaddr_llc *addr = llc_ui_skb_cb(skb); | ||
50 | |||
51 | /* save primitive for use by the user. */ | ||
52 | addr->sllc_family = skb->sk->sk_family; | ||
53 | addr->sllc_arphrd = skb->dev->type; | ||
54 | addr->sllc_test = prim == LLC_TEST_PRIM; | ||
55 | addr->sllc_xid = prim == LLC_XID_PRIM; | ||
56 | addr->sllc_ua = prim == LLC_DATAUNIT_PRIM; | ||
57 | llc_pdu_decode_sa(skb, addr->sllc_mac); | ||
58 | llc_pdu_decode_ssap(skb, &addr->sllc_sap); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * llc_sap_rtn_pdu - Informs upper layer on rx of an UI, XID or TEST pdu. | ||
63 | * @sap: pointer to SAP | ||
64 | * @skb: received pdu | ||
65 | */ | ||
66 | void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb) | ||
67 | { | ||
68 | struct llc_sap_state_ev *ev = llc_sap_ev(skb); | ||
69 | struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
70 | |||
71 | switch (LLC_U_PDU_RSP(pdu)) { | ||
72 | case LLC_1_PDU_CMD_TEST: | ||
73 | ev->prim = LLC_TEST_PRIM; break; | ||
74 | case LLC_1_PDU_CMD_XID: | ||
75 | ev->prim = LLC_XID_PRIM; break; | ||
76 | case LLC_1_PDU_CMD_UI: | ||
77 | ev->prim = LLC_DATAUNIT_PRIM; break; | ||
78 | } | ||
79 | ev->ind_cfm_flag = LLC_IND; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * llc_find_sap_trans - finds transition for event | ||
84 | * @sap: pointer to SAP | ||
85 | * @skb: happened event | ||
86 | * | ||
87 | * This function finds transition that matches with happened event. | ||
88 | * Returns the pointer to found transition on success or %NULL for | ||
89 | * failure. | ||
90 | */ | ||
91 | static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap, | ||
92 | struct sk_buff* skb) | ||
93 | { | ||
94 | int i = 0; | ||
95 | struct llc_sap_state_trans *rc = NULL; | ||
96 | struct llc_sap_state_trans **next_trans; | ||
97 | struct llc_sap_state *curr_state = &llc_sap_state_table[sap->state - 1]; | ||
98 | /* | ||
99 | * Search thru events for this state until list exhausted or until | ||
100 | * its obvious the event is not valid for the current state | ||
101 | */ | ||
102 | for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) | ||
103 | if (!next_trans[i]->ev(sap, skb)) { | ||
104 | rc = next_trans[i]; /* got event match; return it */ | ||
105 | break; | ||
106 | } | ||
107 | return rc; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * llc_exec_sap_trans_actions - execute actions related to event | ||
112 | * @sap: pointer to SAP | ||
113 | * @trans: pointer to transition that it's actions must be performed | ||
114 | * @skb: happened event. | ||
115 | * | ||
116 | * This function executes actions that is related to happened event. | ||
117 | * Returns 0 for success and 1 for failure of at least one action. | ||
118 | */ | ||
119 | static int llc_exec_sap_trans_actions(struct llc_sap *sap, | ||
120 | struct llc_sap_state_trans *trans, | ||
121 | struct sk_buff *skb) | ||
122 | { | ||
123 | int rc = 0; | ||
124 | llc_sap_action_t *next_action = trans->ev_actions; | ||
125 | |||
126 | for (; next_action && *next_action; next_action++) | ||
127 | if ((*next_action)(sap, skb)) | ||
128 | rc = 1; | ||
129 | return rc; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * llc_sap_next_state - finds transition, execs actions & change SAP state | ||
134 | * @sap: pointer to SAP | ||
135 | * @skb: happened event | ||
136 | * | ||
137 | * This function finds transition that matches with happened event, then | ||
138 | * executes related actions and finally changes state of SAP. It returns | ||
139 | * 0 on success and 1 for failure. | ||
140 | */ | ||
141 | static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb) | ||
142 | { | ||
143 | int rc = 1; | ||
144 | struct llc_sap_state_trans *trans; | ||
145 | |||
146 | if (sap->state > LLC_NR_SAP_STATES) | ||
147 | goto out; | ||
148 | trans = llc_find_sap_trans(sap, skb); | ||
149 | if (!trans) | ||
150 | goto out; | ||
151 | /* | ||
152 | * Got the state to which we next transition; perform the actions | ||
153 | * associated with this transition before actually transitioning to the | ||
154 | * next state | ||
155 | */ | ||
156 | rc = llc_exec_sap_trans_actions(sap, trans, skb); | ||
157 | if (rc) | ||
158 | goto out; | ||
159 | /* | ||
160 | * Transition SAP to next state if all actions execute successfully | ||
161 | */ | ||
162 | sap->state = trans->next_state; | ||
163 | out: | ||
164 | return rc; | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * llc_sap_state_process - sends event to SAP state machine | ||
169 | * @sap: sap to use | ||
170 | * @skb: pointer to occurred event | ||
171 | * | ||
172 | * After executing actions of the event, upper layer will be indicated | ||
173 | * if needed(on receiving an UI frame). sk can be null for the | ||
174 | * datalink_proto case. | ||
175 | */ | ||
176 | static void llc_sap_state_process(struct llc_sap *sap, struct sk_buff *skb) | ||
177 | { | ||
178 | struct llc_sap_state_ev *ev = llc_sap_ev(skb); | ||
179 | |||
180 | /* | ||
181 | * We have to hold the skb, because llc_sap_next_state | ||
182 | * will kfree it in the sending path and we need to | ||
183 | * look at the skb->cb, where we encode llc_sap_state_ev. | ||
184 | */ | ||
185 | skb_get(skb); | ||
186 | ev->ind_cfm_flag = 0; | ||
187 | llc_sap_next_state(sap, skb); | ||
188 | if (ev->ind_cfm_flag == LLC_IND) { | ||
189 | if (skb->sk->sk_state == TCP_LISTEN) | ||
190 | kfree_skb(skb); | ||
191 | else { | ||
192 | llc_save_primitive(skb, ev->prim); | ||
193 | |||
194 | /* queue skb to the user. */ | ||
195 | if (sock_queue_rcv_skb(skb->sk, skb)) | ||
196 | kfree_skb(skb); | ||
197 | } | ||
198 | } | ||
199 | kfree_skb(skb); | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * llc_build_and_send_test_pkt - TEST interface for upper layers. | ||
204 | * @sap: sap to use | ||
205 | * @skb: packet to send | ||
206 | * @dmac: destination mac address | ||
207 | * @dsap: destination sap | ||
208 | * | ||
209 | * This function is called when upper layer wants to send a TEST pdu. | ||
210 | * Returns 0 for success, 1 otherwise. | ||
211 | */ | ||
212 | void llc_build_and_send_test_pkt(struct llc_sap *sap, | ||
213 | struct sk_buff *skb, u8 *dmac, u8 dsap) | ||
214 | { | ||
215 | struct llc_sap_state_ev *ev = llc_sap_ev(skb); | ||
216 | |||
217 | ev->saddr.lsap = sap->laddr.lsap; | ||
218 | ev->daddr.lsap = dsap; | ||
219 | memcpy(ev->saddr.mac, skb->dev->dev_addr, IFHWADDRLEN); | ||
220 | memcpy(ev->daddr.mac, dmac, IFHWADDRLEN); | ||
221 | |||
222 | ev->type = LLC_SAP_EV_TYPE_PRIM; | ||
223 | ev->prim = LLC_TEST_PRIM; | ||
224 | ev->prim_type = LLC_PRIM_TYPE_REQ; | ||
225 | llc_sap_state_process(sap, skb); | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * llc_build_and_send_xid_pkt - XID interface for upper layers | ||
230 | * @sap: sap to use | ||
231 | * @skb: packet to send | ||
232 | * @dmac: destination mac address | ||
233 | * @dsap: destination sap | ||
234 | * | ||
235 | * This function is called when upper layer wants to send a XID pdu. | ||
236 | * Returns 0 for success, 1 otherwise. | ||
237 | */ | ||
238 | void llc_build_and_send_xid_pkt(struct llc_sap *sap, struct sk_buff *skb, | ||
239 | u8 *dmac, u8 dsap) | ||
240 | { | ||
241 | struct llc_sap_state_ev *ev = llc_sap_ev(skb); | ||
242 | |||
243 | ev->saddr.lsap = sap->laddr.lsap; | ||
244 | ev->daddr.lsap = dsap; | ||
245 | memcpy(ev->saddr.mac, skb->dev->dev_addr, IFHWADDRLEN); | ||
246 | memcpy(ev->daddr.mac, dmac, IFHWADDRLEN); | ||
247 | |||
248 | ev->type = LLC_SAP_EV_TYPE_PRIM; | ||
249 | ev->prim = LLC_XID_PRIM; | ||
250 | ev->prim_type = LLC_PRIM_TYPE_REQ; | ||
251 | llc_sap_state_process(sap, skb); | ||
252 | } | ||
253 | |||
254 | /** | ||
255 | * llc_sap_rcv - sends received pdus to the sap state machine | ||
256 | * @sap: current sap component structure. | ||
257 | * @skb: received frame. | ||
258 | * | ||
259 | * Sends received pdus to the sap state machine. | ||
260 | */ | ||
261 | static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb) | ||
262 | { | ||
263 | struct llc_sap_state_ev *ev = llc_sap_ev(skb); | ||
264 | |||
265 | ev->type = LLC_SAP_EV_TYPE_PDU; | ||
266 | ev->reason = 0; | ||
267 | llc_sap_state_process(sap, skb); | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * llc_lookup_dgram - Finds dgram socket for the local sap/mac | ||
272 | * @sap: SAP | ||
273 | * @laddr: address of local LLC (MAC + SAP) | ||
274 | * | ||
275 | * Search socket list of the SAP and finds connection using the local | ||
276 | * mac, and local sap. Returns pointer for socket found, %NULL otherwise. | ||
277 | */ | ||
278 | static struct sock *llc_lookup_dgram(struct llc_sap *sap, | ||
279 | struct llc_addr *laddr) | ||
280 | { | ||
281 | struct sock *rc; | ||
282 | struct hlist_node *node; | ||
283 | |||
284 | read_lock_bh(&sap->sk_list.lock); | ||
285 | sk_for_each(rc, node, &sap->sk_list.list) { | ||
286 | struct llc_sock *llc = llc_sk(rc); | ||
287 | |||
288 | if (rc->sk_type == SOCK_DGRAM && | ||
289 | llc->laddr.lsap == laddr->lsap && | ||
290 | llc_mac_match(llc->laddr.mac, laddr->mac)) { | ||
291 | sock_hold(rc); | ||
292 | goto found; | ||
293 | } | ||
294 | } | ||
295 | rc = NULL; | ||
296 | found: | ||
297 | read_unlock_bh(&sap->sk_list.lock); | ||
298 | return rc; | ||
299 | } | ||
300 | |||
301 | void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb) | ||
302 | { | ||
303 | struct llc_addr laddr; | ||
304 | struct sock *sk; | ||
305 | |||
306 | llc_pdu_decode_da(skb, laddr.mac); | ||
307 | llc_pdu_decode_dsap(skb, &laddr.lsap); | ||
308 | |||
309 | sk = llc_lookup_dgram(sap, &laddr); | ||
310 | if (sk) { | ||
311 | skb->sk = sk; | ||
312 | llc_sap_rcv(sap, skb); | ||
313 | sock_put(sk); | ||
314 | } else | ||
315 | kfree_skb(skb); | ||
316 | } | ||