1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Generic HDLC support routines for Linux
4*4882a593Smuzhiyun * X.25 support
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/errno.h>
10*4882a593Smuzhiyun #include <linux/gfp.h>
11*4882a593Smuzhiyun #include <linux/hdlc.h>
12*4882a593Smuzhiyun #include <linux/if_arp.h>
13*4882a593Smuzhiyun #include <linux/inetdevice.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/lapb.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/pkt_sched.h>
19*4882a593Smuzhiyun #include <linux/poll.h>
20*4882a593Smuzhiyun #include <linux/rtnetlink.h>
21*4882a593Smuzhiyun #include <linux/skbuff.h>
22*4882a593Smuzhiyun #include <net/x25device.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun struct x25_state {
25*4882a593Smuzhiyun x25_hdlc_proto settings;
26*4882a593Smuzhiyun bool up;
27*4882a593Smuzhiyun spinlock_t up_lock; /* Protects "up" */
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
31*4882a593Smuzhiyun
state(hdlc_device * hdlc)32*4882a593Smuzhiyun static struct x25_state *state(hdlc_device *hdlc)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun return hdlc->state;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* These functions are callbacks called by LAPB layer */
38*4882a593Smuzhiyun
x25_connect_disconnect(struct net_device * dev,int reason,int code)39*4882a593Smuzhiyun static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun struct sk_buff *skb;
42*4882a593Smuzhiyun unsigned char *ptr;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun if ((skb = dev_alloc_skb(1)) == NULL) {
45*4882a593Smuzhiyun netdev_err(dev, "out of memory\n");
46*4882a593Smuzhiyun return;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun ptr = skb_put(skb, 1);
50*4882a593Smuzhiyun *ptr = code;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun skb->protocol = x25_type_trans(skb, dev);
53*4882a593Smuzhiyun netif_rx(skb);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun
x25_connected(struct net_device * dev,int reason)58*4882a593Smuzhiyun static void x25_connected(struct net_device *dev, int reason)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun x25_connect_disconnect(dev, reason, X25_IFACE_CONNECT);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun
x25_disconnected(struct net_device * dev,int reason)65*4882a593Smuzhiyun static void x25_disconnected(struct net_device *dev, int reason)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun x25_connect_disconnect(dev, reason, X25_IFACE_DISCONNECT);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun
x25_data_indication(struct net_device * dev,struct sk_buff * skb)72*4882a593Smuzhiyun static int x25_data_indication(struct net_device *dev, struct sk_buff *skb)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun unsigned char *ptr;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun if (skb_cow(skb, 1)) {
77*4882a593Smuzhiyun kfree_skb(skb);
78*4882a593Smuzhiyun return NET_RX_DROP;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun skb_push(skb, 1);
82*4882a593Smuzhiyun skb_reset_network_header(skb);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun ptr = skb->data;
85*4882a593Smuzhiyun *ptr = X25_IFACE_DATA;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun skb->protocol = x25_type_trans(skb, dev);
88*4882a593Smuzhiyun return netif_rx(skb);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun
x25_data_transmit(struct net_device * dev,struct sk_buff * skb)93*4882a593Smuzhiyun static void x25_data_transmit(struct net_device *dev, struct sk_buff *skb)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun skb_reset_network_header(skb);
98*4882a593Smuzhiyun skb->protocol = hdlc_type_trans(skb, dev);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (dev_nit_active(dev))
101*4882a593Smuzhiyun dev_queue_xmit_nit(skb, dev);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun hdlc->xmit(skb, dev); /* Ignore return value :-( */
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun
x25_xmit(struct sk_buff * skb,struct net_device * dev)108*4882a593Smuzhiyun static netdev_tx_t x25_xmit(struct sk_buff *skb, struct net_device *dev)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
111*4882a593Smuzhiyun struct x25_state *x25st = state(hdlc);
112*4882a593Smuzhiyun int result;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* There should be a pseudo header of 1 byte added by upper layers.
115*4882a593Smuzhiyun * Check to make sure it is there before reading it.
116*4882a593Smuzhiyun */
117*4882a593Smuzhiyun if (skb->len < 1) {
118*4882a593Smuzhiyun kfree_skb(skb);
119*4882a593Smuzhiyun return NETDEV_TX_OK;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun spin_lock_bh(&x25st->up_lock);
123*4882a593Smuzhiyun if (!x25st->up) {
124*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
125*4882a593Smuzhiyun kfree_skb(skb);
126*4882a593Smuzhiyun return NETDEV_TX_OK;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun switch (skb->data[0]) {
130*4882a593Smuzhiyun case X25_IFACE_DATA: /* Data to be transmitted */
131*4882a593Smuzhiyun skb_pull(skb, 1);
132*4882a593Smuzhiyun skb_reset_network_header(skb);
133*4882a593Smuzhiyun if ((result = lapb_data_request(dev, skb)) != LAPB_OK)
134*4882a593Smuzhiyun dev_kfree_skb(skb);
135*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
136*4882a593Smuzhiyun return NETDEV_TX_OK;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun case X25_IFACE_CONNECT:
139*4882a593Smuzhiyun if ((result = lapb_connect_request(dev))!= LAPB_OK) {
140*4882a593Smuzhiyun if (result == LAPB_CONNECTED)
141*4882a593Smuzhiyun /* Send connect confirm. msg to level 3 */
142*4882a593Smuzhiyun x25_connected(dev, 0);
143*4882a593Smuzhiyun else
144*4882a593Smuzhiyun netdev_err(dev, "LAPB connect request failed, error code = %i\n",
145*4882a593Smuzhiyun result);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun case X25_IFACE_DISCONNECT:
150*4882a593Smuzhiyun if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
151*4882a593Smuzhiyun if (result == LAPB_NOTCONNECTED)
152*4882a593Smuzhiyun /* Send disconnect confirm. msg to level 3 */
153*4882a593Smuzhiyun x25_disconnected(dev, 0);
154*4882a593Smuzhiyun else
155*4882a593Smuzhiyun netdev_err(dev, "LAPB disconnect request failed, error code = %i\n",
156*4882a593Smuzhiyun result);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun break;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun default: /* to be defined */
161*4882a593Smuzhiyun break;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
165*4882a593Smuzhiyun dev_kfree_skb(skb);
166*4882a593Smuzhiyun return NETDEV_TX_OK;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun
x25_open(struct net_device * dev)171*4882a593Smuzhiyun static int x25_open(struct net_device *dev)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun static const struct lapb_register_struct cb = {
174*4882a593Smuzhiyun .connect_confirmation = x25_connected,
175*4882a593Smuzhiyun .connect_indication = x25_connected,
176*4882a593Smuzhiyun .disconnect_confirmation = x25_disconnected,
177*4882a593Smuzhiyun .disconnect_indication = x25_disconnected,
178*4882a593Smuzhiyun .data_indication = x25_data_indication,
179*4882a593Smuzhiyun .data_transmit = x25_data_transmit,
180*4882a593Smuzhiyun };
181*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
182*4882a593Smuzhiyun struct x25_state *x25st = state(hdlc);
183*4882a593Smuzhiyun struct lapb_parms_struct params;
184*4882a593Smuzhiyun int result;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun result = lapb_register(dev, &cb);
187*4882a593Smuzhiyun if (result != LAPB_OK)
188*4882a593Smuzhiyun return -ENOMEM;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun result = lapb_getparms(dev, ¶ms);
191*4882a593Smuzhiyun if (result != LAPB_OK)
192*4882a593Smuzhiyun return -EINVAL;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun if (state(hdlc)->settings.dce)
195*4882a593Smuzhiyun params.mode = params.mode | LAPB_DCE;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun if (state(hdlc)->settings.modulo == 128)
198*4882a593Smuzhiyun params.mode = params.mode | LAPB_EXTENDED;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun params.window = state(hdlc)->settings.window;
201*4882a593Smuzhiyun params.t1 = state(hdlc)->settings.t1;
202*4882a593Smuzhiyun params.t2 = state(hdlc)->settings.t2;
203*4882a593Smuzhiyun params.n2 = state(hdlc)->settings.n2;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun result = lapb_setparms(dev, ¶ms);
206*4882a593Smuzhiyun if (result != LAPB_OK)
207*4882a593Smuzhiyun return -EINVAL;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun spin_lock_bh(&x25st->up_lock);
210*4882a593Smuzhiyun x25st->up = true;
211*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun return 0;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun
x25_close(struct net_device * dev)218*4882a593Smuzhiyun static void x25_close(struct net_device *dev)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
221*4882a593Smuzhiyun struct x25_state *x25st = state(hdlc);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun spin_lock_bh(&x25st->up_lock);
224*4882a593Smuzhiyun x25st->up = false;
225*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun lapb_unregister(dev);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun
x25_rx(struct sk_buff * skb)232*4882a593Smuzhiyun static int x25_rx(struct sk_buff *skb)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun struct net_device *dev = skb->dev;
235*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
236*4882a593Smuzhiyun struct x25_state *x25st = state(hdlc);
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
239*4882a593Smuzhiyun dev->stats.rx_dropped++;
240*4882a593Smuzhiyun return NET_RX_DROP;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun spin_lock_bh(&x25st->up_lock);
244*4882a593Smuzhiyun if (!x25st->up) {
245*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
246*4882a593Smuzhiyun kfree_skb(skb);
247*4882a593Smuzhiyun dev->stats.rx_dropped++;
248*4882a593Smuzhiyun return NET_RX_DROP;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun if (lapb_data_received(dev, skb) == LAPB_OK) {
252*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
253*4882a593Smuzhiyun return NET_RX_SUCCESS;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun spin_unlock_bh(&x25st->up_lock);
257*4882a593Smuzhiyun dev->stats.rx_errors++;
258*4882a593Smuzhiyun dev_kfree_skb_any(skb);
259*4882a593Smuzhiyun return NET_RX_DROP;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun static struct hdlc_proto proto = {
264*4882a593Smuzhiyun .open = x25_open,
265*4882a593Smuzhiyun .close = x25_close,
266*4882a593Smuzhiyun .ioctl = x25_ioctl,
267*4882a593Smuzhiyun .netif_rx = x25_rx,
268*4882a593Smuzhiyun .xmit = x25_xmit,
269*4882a593Smuzhiyun .module = THIS_MODULE,
270*4882a593Smuzhiyun };
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun
x25_ioctl(struct net_device * dev,struct ifreq * ifr)273*4882a593Smuzhiyun static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun x25_hdlc_proto __user *x25_s = ifr->ifr_settings.ifs_ifsu.x25;
276*4882a593Smuzhiyun const size_t size = sizeof(x25_hdlc_proto);
277*4882a593Smuzhiyun hdlc_device *hdlc = dev_to_hdlc(dev);
278*4882a593Smuzhiyun x25_hdlc_proto new_settings;
279*4882a593Smuzhiyun int result;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun switch (ifr->ifr_settings.type) {
282*4882a593Smuzhiyun case IF_GET_PROTO:
283*4882a593Smuzhiyun if (dev_to_hdlc(dev)->proto != &proto)
284*4882a593Smuzhiyun return -EINVAL;
285*4882a593Smuzhiyun ifr->ifr_settings.type = IF_PROTO_X25;
286*4882a593Smuzhiyun if (ifr->ifr_settings.size < size) {
287*4882a593Smuzhiyun ifr->ifr_settings.size = size; /* data size wanted */
288*4882a593Smuzhiyun return -ENOBUFS;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun if (copy_to_user(x25_s, &state(hdlc)->settings, size))
291*4882a593Smuzhiyun return -EFAULT;
292*4882a593Smuzhiyun return 0;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun case IF_PROTO_X25:
295*4882a593Smuzhiyun if (!capable(CAP_NET_ADMIN))
296*4882a593Smuzhiyun return -EPERM;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun if (dev->flags & IFF_UP)
299*4882a593Smuzhiyun return -EBUSY;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /* backward compatibility */
302*4882a593Smuzhiyun if (ifr->ifr_settings.size == 0) {
303*4882a593Smuzhiyun new_settings.dce = 0;
304*4882a593Smuzhiyun new_settings.modulo = 8;
305*4882a593Smuzhiyun new_settings.window = 7;
306*4882a593Smuzhiyun new_settings.t1 = 3;
307*4882a593Smuzhiyun new_settings.t2 = 1;
308*4882a593Smuzhiyun new_settings.n2 = 10;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun else {
311*4882a593Smuzhiyun if (copy_from_user(&new_settings, x25_s, size))
312*4882a593Smuzhiyun return -EFAULT;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun if ((new_settings.dce != 0 &&
315*4882a593Smuzhiyun new_settings.dce != 1) ||
316*4882a593Smuzhiyun (new_settings.modulo != 8 &&
317*4882a593Smuzhiyun new_settings.modulo != 128) ||
318*4882a593Smuzhiyun new_settings.window < 1 ||
319*4882a593Smuzhiyun (new_settings.modulo == 8 &&
320*4882a593Smuzhiyun new_settings.window > 7) ||
321*4882a593Smuzhiyun (new_settings.modulo == 128 &&
322*4882a593Smuzhiyun new_settings.window > 127) ||
323*4882a593Smuzhiyun new_settings.t1 < 1 ||
324*4882a593Smuzhiyun new_settings.t1 > 255 ||
325*4882a593Smuzhiyun new_settings.t2 < 1 ||
326*4882a593Smuzhiyun new_settings.t2 > 255 ||
327*4882a593Smuzhiyun new_settings.n2 < 1 ||
328*4882a593Smuzhiyun new_settings.n2 > 255)
329*4882a593Smuzhiyun return -EINVAL;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
333*4882a593Smuzhiyun if (result)
334*4882a593Smuzhiyun return result;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun if ((result = attach_hdlc_protocol(dev, &proto,
337*4882a593Smuzhiyun sizeof(struct x25_state))))
338*4882a593Smuzhiyun return result;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun memcpy(&state(hdlc)->settings, &new_settings, size);
341*4882a593Smuzhiyun state(hdlc)->up = false;
342*4882a593Smuzhiyun spin_lock_init(&state(hdlc)->up_lock);
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun /* There's no header_ops so hard_header_len should be 0. */
345*4882a593Smuzhiyun dev->hard_header_len = 0;
346*4882a593Smuzhiyun /* When transmitting data:
347*4882a593Smuzhiyun * first we'll remove a pseudo header of 1 byte,
348*4882a593Smuzhiyun * then we'll prepend an LAPB header of at most 3 bytes.
349*4882a593Smuzhiyun */
350*4882a593Smuzhiyun dev->needed_headroom = 3 - 1;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun dev->type = ARPHRD_X25;
353*4882a593Smuzhiyun call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
354*4882a593Smuzhiyun netif_dormant_off(dev);
355*4882a593Smuzhiyun return 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun return -EINVAL;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun
mod_init(void)362*4882a593Smuzhiyun static int __init mod_init(void)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun register_hdlc_protocol(&proto);
365*4882a593Smuzhiyun return 0;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun
mod_exit(void)370*4882a593Smuzhiyun static void __exit mod_exit(void)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun unregister_hdlc_protocol(&proto);
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun module_init(mod_init);
377*4882a593Smuzhiyun module_exit(mod_exit);
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
380*4882a593Smuzhiyun MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
381*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
382