1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * "LAPB via ethernet" driver release 001 |
4 | * |
5 | * This code REQUIRES 2.1.15 or higher/ NET3.038 |
6 | * |
7 | * This is a "pseudo" network driver to allow LAPB over Ethernet. |
8 | * |
9 | * This driver can use any ethernet destination address, and can be |
10 | * limited to accept frames from one dedicated ethernet card only. |
11 | * |
12 | * History |
13 | * LAPBETH 001 Jonathan Naylor Cloned from bpqether.c |
14 | * 2000-10-29 Henner Eisen lapb_data_indication() return status. |
15 | * 2000-11-14 Henner Eisen dev_hold/put, NETDEV_GOING_DOWN support |
16 | */ |
17 | |
18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
19 | |
20 | #include <linux/errno.h> |
21 | #include <linux/types.h> |
22 | #include <linux/socket.h> |
23 | #include <linux/in.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/kernel.h> |
26 | #include <linux/string.h> |
27 | #include <linux/net.h> |
28 | #include <linux/inet.h> |
29 | #include <linux/netdevice.h> |
30 | #include <linux/if_arp.h> |
31 | #include <linux/skbuff.h> |
32 | #include <net/sock.h> |
33 | #include <linux/uaccess.h> |
34 | #include <linux/mm.h> |
35 | #include <linux/interrupt.h> |
36 | #include <linux/notifier.h> |
37 | #include <linux/stat.h> |
38 | #include <linux/module.h> |
39 | #include <linux/lapb.h> |
40 | #include <linux/init.h> |
41 | |
42 | #include <net/x25device.h> |
43 | |
44 | static const u8 bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
45 | |
46 | /* If this number is made larger, check that the temporary string buffer |
47 | * in lapbeth_new_device is large enough to store the probe device name. |
48 | */ |
49 | #define MAXLAPBDEV 100 |
50 | |
51 | struct lapbethdev { |
52 | struct list_head node; |
53 | struct net_device *ethdev; /* link to ethernet device */ |
54 | struct net_device *axdev; /* lapbeth device (lapb#) */ |
55 | bool up; |
56 | spinlock_t up_lock; /* Protects "up" */ |
57 | struct sk_buff_head rx_queue; |
58 | struct napi_struct napi; |
59 | }; |
60 | |
61 | static LIST_HEAD(lapbeth_devices); |
62 | |
63 | static void lapbeth_connected(struct net_device *dev, int reason); |
64 | static void lapbeth_disconnected(struct net_device *dev, int reason); |
65 | |
66 | /* ------------------------------------------------------------------------ */ |
67 | |
68 | /* Get the LAPB device for the ethernet device |
69 | */ |
70 | static struct lapbethdev *lapbeth_get_x25_dev(struct net_device *dev) |
71 | { |
72 | struct lapbethdev *lapbeth; |
73 | |
74 | list_for_each_entry_rcu(lapbeth, &lapbeth_devices, node, lockdep_rtnl_is_held()) { |
75 | if (lapbeth->ethdev == dev) |
76 | return lapbeth; |
77 | } |
78 | return NULL; |
79 | } |
80 | |
81 | static __inline__ int dev_is_ethdev(struct net_device *dev) |
82 | { |
83 | return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy" , 5); |
84 | } |
85 | |
86 | /* ------------------------------------------------------------------------ */ |
87 | |
88 | static int lapbeth_napi_poll(struct napi_struct *napi, int budget) |
89 | { |
90 | struct lapbethdev *lapbeth = container_of(napi, struct lapbethdev, |
91 | napi); |
92 | struct sk_buff *skb; |
93 | int processed = 0; |
94 | |
95 | for (; processed < budget; ++processed) { |
96 | skb = skb_dequeue(list: &lapbeth->rx_queue); |
97 | if (!skb) |
98 | break; |
99 | netif_receive_skb_core(skb); |
100 | } |
101 | |
102 | if (processed < budget) |
103 | napi_complete(n: napi); |
104 | |
105 | return processed; |
106 | } |
107 | |
108 | /* Receive a LAPB frame via an ethernet interface. |
109 | */ |
110 | static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, |
111 | struct packet_type *ptype, struct net_device *orig_dev) |
112 | { |
113 | int len, err; |
114 | struct lapbethdev *lapbeth; |
115 | |
116 | if (dev_net(dev) != &init_net) |
117 | goto drop; |
118 | |
119 | skb = skb_share_check(skb, GFP_ATOMIC); |
120 | if (!skb) |
121 | return NET_RX_DROP; |
122 | |
123 | if (!pskb_may_pull(skb, len: 2)) |
124 | goto drop; |
125 | |
126 | rcu_read_lock(); |
127 | lapbeth = lapbeth_get_x25_dev(dev); |
128 | if (!lapbeth) |
129 | goto drop_unlock_rcu; |
130 | spin_lock_bh(lock: &lapbeth->up_lock); |
131 | if (!lapbeth->up) |
132 | goto drop_unlock; |
133 | |
134 | len = skb->data[0] + skb->data[1] * 256; |
135 | dev->stats.rx_packets++; |
136 | dev->stats.rx_bytes += len; |
137 | |
138 | skb_pull(skb, len: 2); /* Remove the length bytes */ |
139 | skb_trim(skb, len); /* Set the length of the data */ |
140 | |
141 | err = lapb_data_received(dev: lapbeth->axdev, skb); |
142 | if (err != LAPB_OK) { |
143 | printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n" , err); |
144 | goto drop_unlock; |
145 | } |
146 | out: |
147 | spin_unlock_bh(lock: &lapbeth->up_lock); |
148 | rcu_read_unlock(); |
149 | return 0; |
150 | drop_unlock: |
151 | kfree_skb(skb); |
152 | goto out; |
153 | drop_unlock_rcu: |
154 | rcu_read_unlock(); |
155 | drop: |
156 | kfree_skb(skb); |
157 | return 0; |
158 | } |
159 | |
160 | static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb) |
161 | { |
162 | struct lapbethdev *lapbeth = netdev_priv(dev); |
163 | unsigned char *ptr; |
164 | |
165 | if (skb_cow(skb, headroom: 1)) { |
166 | kfree_skb(skb); |
167 | return NET_RX_DROP; |
168 | } |
169 | |
170 | skb_push(skb, len: 1); |
171 | |
172 | ptr = skb->data; |
173 | *ptr = X25_IFACE_DATA; |
174 | |
175 | skb->protocol = x25_type_trans(skb, dev); |
176 | |
177 | skb_queue_tail(list: &lapbeth->rx_queue, newsk: skb); |
178 | napi_schedule(n: &lapbeth->napi); |
179 | return NET_RX_SUCCESS; |
180 | } |
181 | |
182 | /* Send a LAPB frame via an ethernet interface |
183 | */ |
184 | static netdev_tx_t lapbeth_xmit(struct sk_buff *skb, |
185 | struct net_device *dev) |
186 | { |
187 | struct lapbethdev *lapbeth = netdev_priv(dev); |
188 | int err; |
189 | |
190 | spin_lock_bh(lock: &lapbeth->up_lock); |
191 | if (!lapbeth->up) |
192 | goto drop; |
193 | |
194 | /* There should be a pseudo header of 1 byte added by upper layers. |
195 | * Check to make sure it is there before reading it. |
196 | */ |
197 | if (skb->len < 1) |
198 | goto drop; |
199 | |
200 | switch (skb->data[0]) { |
201 | case X25_IFACE_DATA: |
202 | break; |
203 | case X25_IFACE_CONNECT: |
204 | err = lapb_connect_request(dev); |
205 | if (err == LAPB_CONNECTED) |
206 | lapbeth_connected(dev, LAPB_OK); |
207 | else if (err != LAPB_OK) |
208 | pr_err("lapb_connect_request error: %d\n" , err); |
209 | goto drop; |
210 | case X25_IFACE_DISCONNECT: |
211 | err = lapb_disconnect_request(dev); |
212 | if (err == LAPB_NOTCONNECTED) |
213 | lapbeth_disconnected(dev, LAPB_OK); |
214 | else if (err != LAPB_OK) |
215 | pr_err("lapb_disconnect_request err: %d\n" , err); |
216 | fallthrough; |
217 | default: |
218 | goto drop; |
219 | } |
220 | |
221 | skb_pull(skb, len: 1); |
222 | |
223 | err = lapb_data_request(dev, skb); |
224 | if (err != LAPB_OK) { |
225 | pr_err("lapb_data_request error - %d\n" , err); |
226 | goto drop; |
227 | } |
228 | out: |
229 | spin_unlock_bh(lock: &lapbeth->up_lock); |
230 | return NETDEV_TX_OK; |
231 | drop: |
232 | kfree_skb(skb); |
233 | goto out; |
234 | } |
235 | |
236 | static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb) |
237 | { |
238 | struct lapbethdev *lapbeth = netdev_priv(dev: ndev); |
239 | unsigned char *ptr; |
240 | struct net_device *dev; |
241 | int size = skb->len; |
242 | |
243 | ptr = skb_push(skb, len: 2); |
244 | |
245 | *ptr++ = size % 256; |
246 | *ptr++ = size / 256; |
247 | |
248 | ndev->stats.tx_packets++; |
249 | ndev->stats.tx_bytes += size; |
250 | |
251 | skb->dev = dev = lapbeth->ethdev; |
252 | |
253 | skb->protocol = htons(ETH_P_DEC); |
254 | |
255 | skb_reset_network_header(skb); |
256 | |
257 | dev_hard_header(skb, dev, ETH_P_DEC, daddr: bcast_addr, NULL, len: 0); |
258 | |
259 | dev_queue_xmit(skb); |
260 | } |
261 | |
262 | static void lapbeth_connected(struct net_device *dev, int reason) |
263 | { |
264 | struct lapbethdev *lapbeth = netdev_priv(dev); |
265 | unsigned char *ptr; |
266 | struct sk_buff *skb = __dev_alloc_skb(length: 1, GFP_ATOMIC | __GFP_NOMEMALLOC); |
267 | |
268 | if (!skb) |
269 | return; |
270 | |
271 | ptr = skb_put(skb, len: 1); |
272 | *ptr = X25_IFACE_CONNECT; |
273 | |
274 | skb->protocol = x25_type_trans(skb, dev); |
275 | |
276 | skb_queue_tail(list: &lapbeth->rx_queue, newsk: skb); |
277 | napi_schedule(n: &lapbeth->napi); |
278 | } |
279 | |
280 | static void lapbeth_disconnected(struct net_device *dev, int reason) |
281 | { |
282 | struct lapbethdev *lapbeth = netdev_priv(dev); |
283 | unsigned char *ptr; |
284 | struct sk_buff *skb = __dev_alloc_skb(length: 1, GFP_ATOMIC | __GFP_NOMEMALLOC); |
285 | |
286 | if (!skb) |
287 | return; |
288 | |
289 | ptr = skb_put(skb, len: 1); |
290 | *ptr = X25_IFACE_DISCONNECT; |
291 | |
292 | skb->protocol = x25_type_trans(skb, dev); |
293 | |
294 | skb_queue_tail(list: &lapbeth->rx_queue, newsk: skb); |
295 | napi_schedule(n: &lapbeth->napi); |
296 | } |
297 | |
298 | /* Set AX.25 callsign |
299 | */ |
300 | static int lapbeth_set_mac_address(struct net_device *dev, void *addr) |
301 | { |
302 | struct sockaddr *sa = addr; |
303 | |
304 | dev_addr_set(dev, addr: sa->sa_data); |
305 | return 0; |
306 | } |
307 | |
308 | static const struct lapb_register_struct lapbeth_callbacks = { |
309 | .connect_confirmation = lapbeth_connected, |
310 | .connect_indication = lapbeth_connected, |
311 | .disconnect_confirmation = lapbeth_disconnected, |
312 | .disconnect_indication = lapbeth_disconnected, |
313 | .data_indication = lapbeth_data_indication, |
314 | .data_transmit = lapbeth_data_transmit, |
315 | }; |
316 | |
317 | /* open/close a device |
318 | */ |
319 | static int lapbeth_open(struct net_device *dev) |
320 | { |
321 | struct lapbethdev *lapbeth = netdev_priv(dev); |
322 | int err; |
323 | |
324 | napi_enable(n: &lapbeth->napi); |
325 | |
326 | err = lapb_register(dev, callbacks: &lapbeth_callbacks); |
327 | if (err != LAPB_OK) { |
328 | napi_disable(n: &lapbeth->napi); |
329 | pr_err("lapb_register error: %d\n" , err); |
330 | return -ENODEV; |
331 | } |
332 | |
333 | spin_lock_bh(lock: &lapbeth->up_lock); |
334 | lapbeth->up = true; |
335 | spin_unlock_bh(lock: &lapbeth->up_lock); |
336 | |
337 | return 0; |
338 | } |
339 | |
340 | static int lapbeth_close(struct net_device *dev) |
341 | { |
342 | struct lapbethdev *lapbeth = netdev_priv(dev); |
343 | int err; |
344 | |
345 | spin_lock_bh(lock: &lapbeth->up_lock); |
346 | lapbeth->up = false; |
347 | spin_unlock_bh(lock: &lapbeth->up_lock); |
348 | |
349 | err = lapb_unregister(dev); |
350 | if (err != LAPB_OK) |
351 | pr_err("lapb_unregister error: %d\n" , err); |
352 | |
353 | napi_disable(n: &lapbeth->napi); |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | /* ------------------------------------------------------------------------ */ |
359 | |
360 | static const struct net_device_ops lapbeth_netdev_ops = { |
361 | .ndo_open = lapbeth_open, |
362 | .ndo_stop = lapbeth_close, |
363 | .ndo_start_xmit = lapbeth_xmit, |
364 | .ndo_set_mac_address = lapbeth_set_mac_address, |
365 | }; |
366 | |
367 | static void lapbeth_setup(struct net_device *dev) |
368 | { |
369 | dev->netdev_ops = &lapbeth_netdev_ops; |
370 | dev->needs_free_netdev = true; |
371 | dev->type = ARPHRD_X25; |
372 | dev->hard_header_len = 0; |
373 | dev->mtu = 1000; |
374 | dev->addr_len = 0; |
375 | } |
376 | |
377 | /* Setup a new device. |
378 | */ |
379 | static int lapbeth_new_device(struct net_device *dev) |
380 | { |
381 | struct net_device *ndev; |
382 | struct lapbethdev *lapbeth; |
383 | int rc = -ENOMEM; |
384 | |
385 | ASSERT_RTNL(); |
386 | |
387 | if (dev->type != ARPHRD_ETHER) |
388 | return -EINVAL; |
389 | |
390 | ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d" , NET_NAME_UNKNOWN, |
391 | lapbeth_setup); |
392 | if (!ndev) |
393 | goto out; |
394 | |
395 | /* When transmitting data: |
396 | * first this driver removes a pseudo header of 1 byte, |
397 | * then the lapb module prepends an LAPB header of at most 3 bytes, |
398 | * then this driver prepends a length field of 2 bytes, |
399 | * then the underlying Ethernet device prepends its own header. |
400 | */ |
401 | ndev->needed_headroom = -1 + 3 + 2 + dev->hard_header_len |
402 | + dev->needed_headroom; |
403 | ndev->needed_tailroom = dev->needed_tailroom; |
404 | |
405 | lapbeth = netdev_priv(dev: ndev); |
406 | lapbeth->axdev = ndev; |
407 | |
408 | dev_hold(dev); |
409 | lapbeth->ethdev = dev; |
410 | |
411 | lapbeth->up = false; |
412 | spin_lock_init(&lapbeth->up_lock); |
413 | |
414 | skb_queue_head_init(list: &lapbeth->rx_queue); |
415 | netif_napi_add_weight(dev: ndev, napi: &lapbeth->napi, poll: lapbeth_napi_poll, weight: 16); |
416 | |
417 | rc = -EIO; |
418 | if (register_netdevice(dev: ndev)) |
419 | goto fail; |
420 | |
421 | list_add_rcu(new: &lapbeth->node, head: &lapbeth_devices); |
422 | rc = 0; |
423 | out: |
424 | return rc; |
425 | fail: |
426 | dev_put(dev); |
427 | free_netdev(dev: ndev); |
428 | goto out; |
429 | } |
430 | |
431 | /* Free a lapb network device. |
432 | */ |
433 | static void lapbeth_free_device(struct lapbethdev *lapbeth) |
434 | { |
435 | dev_put(dev: lapbeth->ethdev); |
436 | list_del_rcu(entry: &lapbeth->node); |
437 | unregister_netdevice(dev: lapbeth->axdev); |
438 | } |
439 | |
440 | /* Handle device status changes. |
441 | * |
442 | * Called from notifier with RTNL held. |
443 | */ |
444 | static int lapbeth_device_event(struct notifier_block *this, |
445 | unsigned long event, void *ptr) |
446 | { |
447 | struct lapbethdev *lapbeth; |
448 | struct net_device *dev = netdev_notifier_info_to_dev(info: ptr); |
449 | |
450 | if (dev_net(dev) != &init_net) |
451 | return NOTIFY_DONE; |
452 | |
453 | if (!dev_is_ethdev(dev) && !lapbeth_get_x25_dev(dev)) |
454 | return NOTIFY_DONE; |
455 | |
456 | switch (event) { |
457 | case NETDEV_UP: |
458 | /* New ethernet device -> new LAPB interface */ |
459 | if (!lapbeth_get_x25_dev(dev)) |
460 | lapbeth_new_device(dev); |
461 | break; |
462 | case NETDEV_GOING_DOWN: |
463 | /* ethernet device closes -> close LAPB interface */ |
464 | lapbeth = lapbeth_get_x25_dev(dev); |
465 | if (lapbeth) |
466 | dev_close(dev: lapbeth->axdev); |
467 | break; |
468 | case NETDEV_UNREGISTER: |
469 | /* ethernet device disappears -> remove LAPB interface */ |
470 | lapbeth = lapbeth_get_x25_dev(dev); |
471 | if (lapbeth) |
472 | lapbeth_free_device(lapbeth); |
473 | break; |
474 | } |
475 | |
476 | return NOTIFY_DONE; |
477 | } |
478 | |
479 | /* ------------------------------------------------------------------------ */ |
480 | |
481 | static struct packet_type lapbeth_packet_type __read_mostly = { |
482 | .type = cpu_to_be16(ETH_P_DEC), |
483 | .func = lapbeth_rcv, |
484 | }; |
485 | |
486 | static struct notifier_block lapbeth_dev_notifier = { |
487 | .notifier_call = lapbeth_device_event, |
488 | }; |
489 | |
490 | static const char banner[] __initconst = |
491 | KERN_INFO "LAPB Ethernet driver version 0.02\n" ; |
492 | |
493 | static int __init lapbeth_init_driver(void) |
494 | { |
495 | dev_add_pack(pt: &lapbeth_packet_type); |
496 | |
497 | register_netdevice_notifier(nb: &lapbeth_dev_notifier); |
498 | |
499 | printk(banner); |
500 | |
501 | return 0; |
502 | } |
503 | module_init(lapbeth_init_driver); |
504 | |
505 | static void __exit lapbeth_cleanup_driver(void) |
506 | { |
507 | struct lapbethdev *lapbeth; |
508 | struct list_head *entry, *tmp; |
509 | |
510 | dev_remove_pack(pt: &lapbeth_packet_type); |
511 | unregister_netdevice_notifier(nb: &lapbeth_dev_notifier); |
512 | |
513 | rtnl_lock(); |
514 | list_for_each_safe(entry, tmp, &lapbeth_devices) { |
515 | lapbeth = list_entry(entry, struct lapbethdev, node); |
516 | |
517 | dev_put(dev: lapbeth->ethdev); |
518 | unregister_netdevice(dev: lapbeth->axdev); |
519 | } |
520 | rtnl_unlock(); |
521 | } |
522 | module_exit(lapbeth_cleanup_driver); |
523 | |
524 | MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>" ); |
525 | MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver" ); |
526 | MODULE_LICENSE("GPL" ); |
527 | |