Root/
1 | /* |
2 | * X.25 Packet Layer release 002 |
3 | * |
4 | * This is ALPHA test software. This code may break your machine, |
5 | * randomly fail to work with new releases, misbehave and/or generally |
6 | * screw up. It might even work. |
7 | * |
8 | * This code REQUIRES 2.1.15 or higher |
9 | * |
10 | * This module: |
11 | * This module is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU General Public License |
13 | * as published by the Free Software Foundation; either version |
14 | * 2 of the License, or (at your option) any later version. |
15 | * |
16 | * History |
17 | * X.25 001 Jonathan Naylor Started coding. |
18 | * X.25 002 Jonathan Naylor New timer architecture. |
19 | * mar/20/00 Daniela Squassoni Disabling/enabling of facilities |
20 | * negotiation. |
21 | * 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh. |
22 | */ |
23 | |
24 | #include <linux/kernel.h> |
25 | #include <linux/jiffies.h> |
26 | #include <linux/timer.h> |
27 | #include <linux/slab.h> |
28 | #include <linux/netdevice.h> |
29 | #include <linux/skbuff.h> |
30 | #include <asm/uaccess.h> |
31 | #include <linux/init.h> |
32 | #include <net/x25.h> |
33 | |
34 | static LIST_HEAD(x25_neigh_list); |
35 | static DEFINE_RWLOCK(x25_neigh_list_lock); |
36 | |
37 | static void x25_t20timer_expiry(unsigned long); |
38 | |
39 | static void x25_transmit_restart_confirmation(struct x25_neigh *nb); |
40 | static void x25_transmit_restart_request(struct x25_neigh *nb); |
41 | |
42 | /* |
43 | * Linux set/reset timer routines |
44 | */ |
45 | static inline void x25_start_t20timer(struct x25_neigh *nb) |
46 | { |
47 | mod_timer(&nb->t20timer, jiffies + nb->t20); |
48 | } |
49 | |
50 | static void x25_t20timer_expiry(unsigned long param) |
51 | { |
52 | struct x25_neigh *nb = (struct x25_neigh *)param; |
53 | |
54 | x25_transmit_restart_request(nb); |
55 | |
56 | x25_start_t20timer(nb); |
57 | } |
58 | |
59 | static inline void x25_stop_t20timer(struct x25_neigh *nb) |
60 | { |
61 | del_timer(&nb->t20timer); |
62 | } |
63 | |
64 | static inline int x25_t20timer_pending(struct x25_neigh *nb) |
65 | { |
66 | return timer_pending(&nb->t20timer); |
67 | } |
68 | |
69 | /* |
70 | * This handles all restart and diagnostic frames. |
71 | */ |
72 | void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb, |
73 | unsigned short frametype) |
74 | { |
75 | struct sk_buff *skbn; |
76 | int confirm; |
77 | |
78 | switch (frametype) { |
79 | case X25_RESTART_REQUEST: |
80 | confirm = !x25_t20timer_pending(nb); |
81 | x25_stop_t20timer(nb); |
82 | nb->state = X25_LINK_STATE_3; |
83 | if (confirm) |
84 | x25_transmit_restart_confirmation(nb); |
85 | break; |
86 | |
87 | case X25_RESTART_CONFIRMATION: |
88 | x25_stop_t20timer(nb); |
89 | nb->state = X25_LINK_STATE_3; |
90 | break; |
91 | |
92 | case X25_DIAGNOSTIC: |
93 | printk(KERN_WARNING "x25: diagnostic #%d - " |
94 | "%02X %02X %02X\n", |
95 | skb->data[3], skb->data[4], |
96 | skb->data[5], skb->data[6]); |
97 | break; |
98 | |
99 | default: |
100 | printk(KERN_WARNING "x25: received unknown %02X " |
101 | "with LCI 000\n", frametype); |
102 | break; |
103 | } |
104 | |
105 | if (nb->state == X25_LINK_STATE_3) |
106 | while ((skbn = skb_dequeue(&nb->queue)) != NULL) |
107 | x25_send_frame(skbn, nb); |
108 | } |
109 | |
110 | /* |
111 | * This routine is called when a Restart Request is needed |
112 | */ |
113 | static void x25_transmit_restart_request(struct x25_neigh *nb) |
114 | { |
115 | unsigned char *dptr; |
116 | int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; |
117 | struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); |
118 | |
119 | if (!skb) |
120 | return; |
121 | |
122 | skb_reserve(skb, X25_MAX_L2_LEN); |
123 | |
124 | dptr = skb_put(skb, X25_STD_MIN_LEN + 2); |
125 | |
126 | *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; |
127 | *dptr++ = 0x00; |
128 | *dptr++ = X25_RESTART_REQUEST; |
129 | *dptr++ = 0x00; |
130 | *dptr++ = 0; |
131 | |
132 | skb->sk = NULL; |
133 | |
134 | x25_send_frame(skb, nb); |
135 | } |
136 | |
137 | /* |
138 | * This routine is called when a Restart Confirmation is needed |
139 | */ |
140 | static void x25_transmit_restart_confirmation(struct x25_neigh *nb) |
141 | { |
142 | unsigned char *dptr; |
143 | int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN; |
144 | struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); |
145 | |
146 | if (!skb) |
147 | return; |
148 | |
149 | skb_reserve(skb, X25_MAX_L2_LEN); |
150 | |
151 | dptr = skb_put(skb, X25_STD_MIN_LEN); |
152 | |
153 | *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; |
154 | *dptr++ = 0x00; |
155 | *dptr++ = X25_RESTART_CONFIRMATION; |
156 | |
157 | skb->sk = NULL; |
158 | |
159 | x25_send_frame(skb, nb); |
160 | } |
161 | |
162 | /* |
163 | * This routine is called when a Clear Request is needed outside of the context |
164 | * of a connected socket. |
165 | */ |
166 | void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci, |
167 | unsigned char cause) |
168 | { |
169 | unsigned char *dptr; |
170 | int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; |
171 | struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); |
172 | |
173 | if (!skb) |
174 | return; |
175 | |
176 | skb_reserve(skb, X25_MAX_L2_LEN); |
177 | |
178 | dptr = skb_put(skb, X25_STD_MIN_LEN + 2); |
179 | |
180 | *dptr++ = ((lci >> 8) & 0x0F) | (nb->extended ? |
181 | X25_GFI_EXTSEQ : |
182 | X25_GFI_STDSEQ); |
183 | *dptr++ = (lci >> 0) & 0xFF; |
184 | *dptr++ = X25_CLEAR_REQUEST; |
185 | *dptr++ = cause; |
186 | *dptr++ = 0x00; |
187 | |
188 | skb->sk = NULL; |
189 | |
190 | x25_send_frame(skb, nb); |
191 | } |
192 | |
193 | void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb) |
194 | { |
195 | switch (nb->state) { |
196 | case X25_LINK_STATE_0: |
197 | skb_queue_tail(&nb->queue, skb); |
198 | nb->state = X25_LINK_STATE_1; |
199 | x25_establish_link(nb); |
200 | break; |
201 | case X25_LINK_STATE_1: |
202 | case X25_LINK_STATE_2: |
203 | skb_queue_tail(&nb->queue, skb); |
204 | break; |
205 | case X25_LINK_STATE_3: |
206 | x25_send_frame(skb, nb); |
207 | break; |
208 | } |
209 | } |
210 | |
211 | /* |
212 | * Called when the link layer has become established. |
213 | */ |
214 | void x25_link_established(struct x25_neigh *nb) |
215 | { |
216 | switch (nb->state) { |
217 | case X25_LINK_STATE_0: |
218 | nb->state = X25_LINK_STATE_2; |
219 | break; |
220 | case X25_LINK_STATE_1: |
221 | x25_transmit_restart_request(nb); |
222 | nb->state = X25_LINK_STATE_2; |
223 | x25_start_t20timer(nb); |
224 | break; |
225 | } |
226 | } |
227 | |
228 | /* |
229 | * Called when the link layer has terminated, or an establishment |
230 | * request has failed. |
231 | */ |
232 | |
233 | void x25_link_terminated(struct x25_neigh *nb) |
234 | { |
235 | nb->state = X25_LINK_STATE_0; |
236 | /* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */ |
237 | x25_kill_by_neigh(nb); |
238 | } |
239 | |
240 | /* |
241 | * Add a new device. |
242 | */ |
243 | void x25_link_device_up(struct net_device *dev) |
244 | { |
245 | struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC); |
246 | |
247 | if (!nb) |
248 | return; |
249 | |
250 | skb_queue_head_init(&nb->queue); |
251 | setup_timer(&nb->t20timer, x25_t20timer_expiry, (unsigned long)nb); |
252 | |
253 | dev_hold(dev); |
254 | nb->dev = dev; |
255 | nb->state = X25_LINK_STATE_0; |
256 | nb->extended = 0; |
257 | /* |
258 | * Enables negotiation |
259 | */ |
260 | nb->global_facil_mask = X25_MASK_REVERSE | |
261 | X25_MASK_THROUGHPUT | |
262 | X25_MASK_PACKET_SIZE | |
263 | X25_MASK_WINDOW_SIZE; |
264 | nb->t20 = sysctl_x25_restart_request_timeout; |
265 | atomic_set(&nb->refcnt, 1); |
266 | |
267 | write_lock_bh(&x25_neigh_list_lock); |
268 | list_add(&nb->node, &x25_neigh_list); |
269 | write_unlock_bh(&x25_neigh_list_lock); |
270 | } |
271 | |
272 | /** |
273 | * __x25_remove_neigh - remove neighbour from x25_neigh_list |
274 | * @nb - neigh to remove |
275 | * |
276 | * Remove neighbour from x25_neigh_list. If it was there. |
277 | * Caller must hold x25_neigh_list_lock. |
278 | */ |
279 | static void __x25_remove_neigh(struct x25_neigh *nb) |
280 | { |
281 | skb_queue_purge(&nb->queue); |
282 | x25_stop_t20timer(nb); |
283 | |
284 | if (nb->node.next) { |
285 | list_del(&nb->node); |
286 | x25_neigh_put(nb); |
287 | } |
288 | } |
289 | |
290 | /* |
291 | * A device has been removed, remove its links. |
292 | */ |
293 | void x25_link_device_down(struct net_device *dev) |
294 | { |
295 | struct x25_neigh *nb; |
296 | struct list_head *entry, *tmp; |
297 | |
298 | write_lock_bh(&x25_neigh_list_lock); |
299 | |
300 | list_for_each_safe(entry, tmp, &x25_neigh_list) { |
301 | nb = list_entry(entry, struct x25_neigh, node); |
302 | |
303 | if (nb->dev == dev) { |
304 | __x25_remove_neigh(nb); |
305 | dev_put(dev); |
306 | } |
307 | } |
308 | |
309 | write_unlock_bh(&x25_neigh_list_lock); |
310 | } |
311 | |
312 | /* |
313 | * Given a device, return the neighbour address. |
314 | */ |
315 | struct x25_neigh *x25_get_neigh(struct net_device *dev) |
316 | { |
317 | struct x25_neigh *nb, *use = NULL; |
318 | struct list_head *entry; |
319 | |
320 | read_lock_bh(&x25_neigh_list_lock); |
321 | list_for_each(entry, &x25_neigh_list) { |
322 | nb = list_entry(entry, struct x25_neigh, node); |
323 | |
324 | if (nb->dev == dev) { |
325 | use = nb; |
326 | break; |
327 | } |
328 | } |
329 | |
330 | if (use) |
331 | x25_neigh_hold(use); |
332 | read_unlock_bh(&x25_neigh_list_lock); |
333 | return use; |
334 | } |
335 | |
336 | /* |
337 | * Handle the ioctls that control the subscription functions. |
338 | */ |
339 | int x25_subscr_ioctl(unsigned int cmd, void __user *arg) |
340 | { |
341 | struct x25_subscrip_struct x25_subscr; |
342 | struct x25_neigh *nb; |
343 | struct net_device *dev; |
344 | int rc = -EINVAL; |
345 | |
346 | if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP) |
347 | goto out; |
348 | |
349 | rc = -EFAULT; |
350 | if (copy_from_user(&x25_subscr, arg, sizeof(x25_subscr))) |
351 | goto out; |
352 | |
353 | rc = -EINVAL; |
354 | if ((dev = x25_dev_get(x25_subscr.device)) == NULL) |
355 | goto out; |
356 | |
357 | if ((nb = x25_get_neigh(dev)) == NULL) |
358 | goto out_dev_put; |
359 | |
360 | dev_put(dev); |
361 | |
362 | if (cmd == SIOCX25GSUBSCRIP) { |
363 | x25_subscr.extended = nb->extended; |
364 | x25_subscr.global_facil_mask = nb->global_facil_mask; |
365 | rc = copy_to_user(arg, &x25_subscr, |
366 | sizeof(x25_subscr)) ? -EFAULT : 0; |
367 | } else { |
368 | rc = -EINVAL; |
369 | if (!(x25_subscr.extended && x25_subscr.extended != 1)) { |
370 | rc = 0; |
371 | nb->extended = x25_subscr.extended; |
372 | nb->global_facil_mask = x25_subscr.global_facil_mask; |
373 | } |
374 | } |
375 | x25_neigh_put(nb); |
376 | out: |
377 | return rc; |
378 | out_dev_put: |
379 | dev_put(dev); |
380 | goto out; |
381 | } |
382 | |
383 | |
384 | /* |
385 | * Release all memory associated with X.25 neighbour structures. |
386 | */ |
387 | void __exit x25_link_free(void) |
388 | { |
389 | struct x25_neigh *nb; |
390 | struct list_head *entry, *tmp; |
391 | |
392 | write_lock_bh(&x25_neigh_list_lock); |
393 | |
394 | list_for_each_safe(entry, tmp, &x25_neigh_list) { |
395 | nb = list_entry(entry, struct x25_neigh, node); |
396 | __x25_remove_neigh(nb); |
397 | } |
398 | write_unlock_bh(&x25_neigh_list_lock); |
399 | } |
400 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9