xref: /openbmc/linux/net/x25/x25_out.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*ee5d8f4dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	X.25 Packet Layer release 002
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	This is ALPHA test software. This code may break your machine,
61da177e4SLinus Torvalds  *	randomly fail to work with new releases, misbehave and/or generally
71da177e4SLinus Torvalds  *	screw up. It might even work.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	This code REQUIRES 2.1.15 or higher
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *	History
121da177e4SLinus Torvalds  *	X.25 001	Jonathan Naylor	Started coding.
131da177e4SLinus Torvalds  *	X.25 002	Jonathan Naylor	New timer architecture.
141da177e4SLinus Torvalds  *	2000-09-04	Henner Eisen	Prevented x25_output() skb leakage.
151da177e4SLinus Torvalds  *	2000-10-27	Henner Eisen	MSG_DONTWAIT for fragment allocation.
161da177e4SLinus Torvalds  *	2000-11-10	Henner Eisen	x25_send_iframe(): re-queued frames
171da177e4SLinus Torvalds  *					needed cleaned seq-number fields.
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds 
205a0e3ad6STejun Heo #include <linux/slab.h>
211da177e4SLinus Torvalds #include <linux/socket.h>
221da177e4SLinus Torvalds #include <linux/kernel.h>
231da177e4SLinus Torvalds #include <linux/string.h>
241da177e4SLinus Torvalds #include <linux/skbuff.h>
251da177e4SLinus Torvalds #include <net/sock.h>
261da177e4SLinus Torvalds #include <net/x25.h>
271da177e4SLinus Torvalds 
x25_pacsize_to_bytes(unsigned int pacsize)281da177e4SLinus Torvalds static int x25_pacsize_to_bytes(unsigned int pacsize)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	int bytes = 1;
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds 	if (!pacsize)
331da177e4SLinus Torvalds 		return 128;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	while (pacsize-- > 0)
361da177e4SLinus Torvalds 		bytes *= 2;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 	return bytes;
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /*
421da177e4SLinus Torvalds  *	This is where all X.25 information frames pass.
431da177e4SLinus Torvalds  *
441da177e4SLinus Torvalds  *      Returns the amount of user data bytes sent on success
451da177e4SLinus Torvalds  *      or a negative error code on failure.
461da177e4SLinus Torvalds  */
x25_output(struct sock * sk,struct sk_buff * skb)471da177e4SLinus Torvalds int x25_output(struct sock *sk, struct sk_buff *skb)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	struct sk_buff *skbn;
501da177e4SLinus Torvalds 	unsigned char header[X25_EXT_MIN_LEN];
511da177e4SLinus Torvalds 	int err, frontlen, len;
521da177e4SLinus Torvalds 	int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT;
531da177e4SLinus Torvalds 	struct x25_sock *x25 = x25_sk(sk);
541da177e4SLinus Torvalds 	int header_len = x25->neighbour->extended ? X25_EXT_MIN_LEN :
551da177e4SLinus Torvalds 						    X25_STD_MIN_LEN;
561da177e4SLinus Torvalds 	int max_len = x25_pacsize_to_bytes(x25->facilities.pacsize_out);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	if (skb->len - header_len > max_len) {
591da177e4SLinus Torvalds 		/* Save a copy of the Header */
60d626f62bSArnaldo Carvalho de Melo 		skb_copy_from_linear_data(skb, header, header_len);
611da177e4SLinus Torvalds 		skb_pull(skb, header_len);
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 		frontlen = skb_headroom(skb);
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 		while (skb->len > 0) {
6677b22836SArnd Bergmann 			release_sock(sk);
6777b22836SArnd Bergmann 			skbn = sock_alloc_send_skb(sk, frontlen + max_len,
6877b22836SArnd Bergmann 						   noblock, &err);
6977b22836SArnd Bergmann 			lock_sock(sk);
7077b22836SArnd Bergmann 			if (!skbn) {
711da177e4SLinus Torvalds 				if (err == -EWOULDBLOCK && noblock){
721da177e4SLinus Torvalds 					kfree_skb(skb);
731da177e4SLinus Torvalds 					return sent;
741da177e4SLinus Torvalds 				}
751da177e4SLinus Torvalds 				SOCK_DEBUG(sk, "x25_output: fragment alloc"
761da177e4SLinus Torvalds 					       " failed, err=%d, %d bytes "
771da177e4SLinus Torvalds 					       "sent\n", err, sent);
781da177e4SLinus Torvalds 				return err;
791da177e4SLinus Torvalds 			}
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 			skb_reserve(skbn, frontlen);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 			len = max_len > skb->len ? skb->len : max_len;
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 			/* Copy the user data */
86d626f62bSArnaldo Carvalho de Melo 			skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
871da177e4SLinus Torvalds 			skb_pull(skb, len);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 			/* Duplicate the Header */
901da177e4SLinus Torvalds 			skb_push(skbn, header_len);
9127d7ff46SArnaldo Carvalho de Melo 			skb_copy_to_linear_data(skbn, header, header_len);
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 			if (skb->len > 0) {
941da177e4SLinus Torvalds 				if (x25->neighbour->extended)
951da177e4SLinus Torvalds 					skbn->data[3] |= X25_EXT_M_BIT;
961da177e4SLinus Torvalds 				else
971da177e4SLinus Torvalds 					skbn->data[2] |= X25_STD_M_BIT;
981da177e4SLinus Torvalds 			}
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 			skb_queue_tail(&sk->sk_write_queue, skbn);
1011da177e4SLinus Torvalds 			sent += len;
1021da177e4SLinus Torvalds 		}
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 		kfree_skb(skb);
1051da177e4SLinus Torvalds 	} else {
1061da177e4SLinus Torvalds 		skb_queue_tail(&sk->sk_write_queue, skb);
1071da177e4SLinus Torvalds 		sent = skb->len - header_len;
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds 	return sent;
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds /*
1131da177e4SLinus Torvalds  *	This procedure is passed a buffer descriptor for an iframe. It builds
1141da177e4SLinus Torvalds  *	the rest of the control part of the frame and then writes it out.
1151da177e4SLinus Torvalds  */
x25_send_iframe(struct sock * sk,struct sk_buff * skb)1161da177e4SLinus Torvalds static void x25_send_iframe(struct sock *sk, struct sk_buff *skb)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds 	struct x25_sock *x25 = x25_sk(sk);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	if (!skb)
1211da177e4SLinus Torvalds 		return;
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	if (x25->neighbour->extended) {
1241da177e4SLinus Torvalds 		skb->data[2]  = (x25->vs << 1) & 0xFE;
1251da177e4SLinus Torvalds 		skb->data[3] &= X25_EXT_M_BIT;
1261da177e4SLinus Torvalds 		skb->data[3] |= (x25->vr << 1) & 0xFE;
1271da177e4SLinus Torvalds 	} else {
1281da177e4SLinus Torvalds 		skb->data[2] &= X25_STD_M_BIT;
1291da177e4SLinus Torvalds 		skb->data[2] |= (x25->vs << 1) & 0x0E;
1301da177e4SLinus Torvalds 		skb->data[2] |= (x25->vr << 5) & 0xE0;
1311da177e4SLinus Torvalds 	}
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	x25_transmit_link(skb, x25->neighbour);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
x25_kick(struct sock * sk)1361da177e4SLinus Torvalds void x25_kick(struct sock *sk)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds 	struct sk_buff *skb, *skbn;
1391da177e4SLinus Torvalds 	unsigned short start, end;
1401da177e4SLinus Torvalds 	int modulus;
1411da177e4SLinus Torvalds 	struct x25_sock *x25 = x25_sk(sk);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	if (x25->state != X25_STATE_3)
1441da177e4SLinus Torvalds 		return;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	/*
1471da177e4SLinus Torvalds 	 *	Transmit interrupt data.
1481da177e4SLinus Torvalds 	 */
149b7792e34Sandrew hendry 	if (skb_peek(&x25->interrupt_out_queue) != NULL &&
150b7792e34Sandrew hendry 		!test_and_set_bit(X25_INTERRUPT_FLAG, &x25->flags)) {
151b7792e34Sandrew hendry 
1521da177e4SLinus Torvalds 		skb = skb_dequeue(&x25->interrupt_out_queue);
1531da177e4SLinus Torvalds 		x25_transmit_link(skb, x25->neighbour);
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	if (x25->condition & X25_COND_PEER_RX_BUSY)
1571da177e4SLinus Torvalds 		return;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (!skb_peek(&sk->sk_write_queue))
1601da177e4SLinus Torvalds 		return;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	start   = skb_peek(&x25->ack_queue) ? x25->vs : x25->va;
1651da177e4SLinus Torvalds 	end     = (x25->va + x25->facilities.winsize_out) % modulus;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	if (start == end)
1681da177e4SLinus Torvalds 		return;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	x25->vs = start;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	/*
1731da177e4SLinus Torvalds 	 * Transmit data until either we're out of data to send or
1741da177e4SLinus Torvalds 	 * the window is full.
1751da177e4SLinus Torvalds 	 */
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	skb = skb_dequeue(&sk->sk_write_queue);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	do {
1801da177e4SLinus Torvalds 		if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
1811da177e4SLinus Torvalds 			skb_queue_head(&sk->sk_write_queue, skb);
1821da177e4SLinus Torvalds 			break;
1831da177e4SLinus Torvalds 		}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 		skb_set_owner_w(skbn, sk);
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 		/*
1881da177e4SLinus Torvalds 		 * Transmit the frame copy.
1891da177e4SLinus Torvalds 		 */
1901da177e4SLinus Torvalds 		x25_send_iframe(sk, skbn);
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds 		x25->vs = (x25->vs + 1) % modulus;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 		/*
1951da177e4SLinus Torvalds 		 * Requeue the original data frame.
1961da177e4SLinus Torvalds 		 */
1971da177e4SLinus Torvalds 		skb_queue_tail(&x25->ack_queue, skb);
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	} while (x25->vs != end &&
2001da177e4SLinus Torvalds 		 (skb = skb_dequeue(&sk->sk_write_queue)) != NULL);
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	x25->vl         = x25->vr;
2031da177e4SLinus Torvalds 	x25->condition &= ~X25_COND_ACK_PENDING;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	x25_stop_timer(sk);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds /*
2091da177e4SLinus Torvalds  * The following routines are taken from page 170 of the 7th ARRL Computer
2101da177e4SLinus Torvalds  * Networking Conference paper, as is the whole state machine.
2111da177e4SLinus Torvalds  */
2121da177e4SLinus Torvalds 
x25_enquiry_response(struct sock * sk)2131da177e4SLinus Torvalds void x25_enquiry_response(struct sock *sk)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds 	struct x25_sock *x25 = x25_sk(sk);
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	if (x25->condition & X25_COND_OWN_RX_BUSY)
2181da177e4SLinus Torvalds 		x25_write_internal(sk, X25_RNR);
2191da177e4SLinus Torvalds 	else
2201da177e4SLinus Torvalds 		x25_write_internal(sk, X25_RR);
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	x25->vl         = x25->vr;
2231da177e4SLinus Torvalds 	x25->condition &= ~X25_COND_ACK_PENDING;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	x25_stop_timer(sk);
2261da177e4SLinus Torvalds }
227