18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	X.25 Packet Layer release 002
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	This is ALPHA test software. This code may break your machine,
68c2ecf20Sopenharmony_ci *	randomly fail to work with new releases, misbehave and/or generally
78c2ecf20Sopenharmony_ci *	screw up. It might even work.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *	This code REQUIRES 2.1.15 or higher
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	History
128c2ecf20Sopenharmony_ci *	X.25 001	Jonathan Naylor	Started coding.
138c2ecf20Sopenharmony_ci *	X.25 002	Jonathan Naylor	New timer architecture.
148c2ecf20Sopenharmony_ci *	2000-09-04	Henner Eisen	Prevented x25_output() skb leakage.
158c2ecf20Sopenharmony_ci *	2000-10-27	Henner Eisen	MSG_DONTWAIT for fragment allocation.
168c2ecf20Sopenharmony_ci *	2000-11-10	Henner Eisen	x25_send_iframe(): re-queued frames
178c2ecf20Sopenharmony_ci *					needed cleaned seq-number fields.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/socket.h>
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/string.h>
248c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
258c2ecf20Sopenharmony_ci#include <net/sock.h>
268c2ecf20Sopenharmony_ci#include <net/x25.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int x25_pacsize_to_bytes(unsigned int pacsize)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int bytes = 1;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (!pacsize)
338c2ecf20Sopenharmony_ci		return 128;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	while (pacsize-- > 0)
368c2ecf20Sopenharmony_ci		bytes *= 2;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return bytes;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci *	This is where all X.25 information frames pass.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci *      Returns the amount of user data bytes sent on success
458c2ecf20Sopenharmony_ci *      or a negative error code on failure.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ciint x25_output(struct sock *sk, struct sk_buff *skb)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct sk_buff *skbn;
508c2ecf20Sopenharmony_ci	unsigned char header[X25_EXT_MIN_LEN];
518c2ecf20Sopenharmony_ci	int err, frontlen, len;
528c2ecf20Sopenharmony_ci	int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT;
538c2ecf20Sopenharmony_ci	struct x25_sock *x25 = x25_sk(sk);
548c2ecf20Sopenharmony_ci	int header_len = x25->neighbour->extended ? X25_EXT_MIN_LEN :
558c2ecf20Sopenharmony_ci						    X25_STD_MIN_LEN;
568c2ecf20Sopenharmony_ci	int max_len = x25_pacsize_to_bytes(x25->facilities.pacsize_out);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (skb->len - header_len > max_len) {
598c2ecf20Sopenharmony_ci		/* Save a copy of the Header */
608c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, header, header_len);
618c2ecf20Sopenharmony_ci		skb_pull(skb, header_len);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		frontlen = skb_headroom(skb);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		while (skb->len > 0) {
668c2ecf20Sopenharmony_ci			release_sock(sk);
678c2ecf20Sopenharmony_ci			skbn = sock_alloc_send_skb(sk, frontlen + max_len,
688c2ecf20Sopenharmony_ci						   noblock, &err);
698c2ecf20Sopenharmony_ci			lock_sock(sk);
708c2ecf20Sopenharmony_ci			if (!skbn) {
718c2ecf20Sopenharmony_ci				if (err == -EWOULDBLOCK && noblock){
728c2ecf20Sopenharmony_ci					kfree_skb(skb);
738c2ecf20Sopenharmony_ci					return sent;
748c2ecf20Sopenharmony_ci				}
758c2ecf20Sopenharmony_ci				SOCK_DEBUG(sk, "x25_output: fragment alloc"
768c2ecf20Sopenharmony_ci					       " failed, err=%d, %d bytes "
778c2ecf20Sopenharmony_ci					       "sent\n", err, sent);
788c2ecf20Sopenharmony_ci				return err;
798c2ecf20Sopenharmony_ci			}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci			skb_reserve(skbn, frontlen);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci			len = max_len > skb->len ? skb->len : max_len;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci			/* Copy the user data */
868c2ecf20Sopenharmony_ci			skb_copy_from_linear_data(skb, skb_put(skbn, len), len);
878c2ecf20Sopenharmony_ci			skb_pull(skb, len);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci			/* Duplicate the Header */
908c2ecf20Sopenharmony_ci			skb_push(skbn, header_len);
918c2ecf20Sopenharmony_ci			skb_copy_to_linear_data(skbn, header, header_len);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci			if (skb->len > 0) {
948c2ecf20Sopenharmony_ci				if (x25->neighbour->extended)
958c2ecf20Sopenharmony_ci					skbn->data[3] |= X25_EXT_M_BIT;
968c2ecf20Sopenharmony_ci				else
978c2ecf20Sopenharmony_ci					skbn->data[2] |= X25_STD_M_BIT;
988c2ecf20Sopenharmony_ci			}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci			skb_queue_tail(&sk->sk_write_queue, skbn);
1018c2ecf20Sopenharmony_ci			sent += len;
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		kfree_skb(skb);
1058c2ecf20Sopenharmony_ci	} else {
1068c2ecf20Sopenharmony_ci		skb_queue_tail(&sk->sk_write_queue, skb);
1078c2ecf20Sopenharmony_ci		sent = skb->len - header_len;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	return sent;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci *	This procedure is passed a buffer descriptor for an iframe. It builds
1148c2ecf20Sopenharmony_ci *	the rest of the control part of the frame and then writes it out.
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_cistatic void x25_send_iframe(struct sock *sk, struct sk_buff *skb)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct x25_sock *x25 = x25_sk(sk);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!skb)
1218c2ecf20Sopenharmony_ci		return;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (x25->neighbour->extended) {
1248c2ecf20Sopenharmony_ci		skb->data[2]  = (x25->vs << 1) & 0xFE;
1258c2ecf20Sopenharmony_ci		skb->data[3] &= X25_EXT_M_BIT;
1268c2ecf20Sopenharmony_ci		skb->data[3] |= (x25->vr << 1) & 0xFE;
1278c2ecf20Sopenharmony_ci	} else {
1288c2ecf20Sopenharmony_ci		skb->data[2] &= X25_STD_M_BIT;
1298c2ecf20Sopenharmony_ci		skb->data[2] |= (x25->vs << 1) & 0x0E;
1308c2ecf20Sopenharmony_ci		skb->data[2] |= (x25->vr << 5) & 0xE0;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	x25_transmit_link(skb, x25->neighbour);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_civoid x25_kick(struct sock *sk)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct sk_buff *skb, *skbn;
1398c2ecf20Sopenharmony_ci	unsigned short start, end;
1408c2ecf20Sopenharmony_ci	int modulus;
1418c2ecf20Sopenharmony_ci	struct x25_sock *x25 = x25_sk(sk);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (x25->state != X25_STATE_3)
1448c2ecf20Sopenharmony_ci		return;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/*
1478c2ecf20Sopenharmony_ci	 *	Transmit interrupt data.
1488c2ecf20Sopenharmony_ci	 */
1498c2ecf20Sopenharmony_ci	if (skb_peek(&x25->interrupt_out_queue) != NULL &&
1508c2ecf20Sopenharmony_ci		!test_and_set_bit(X25_INTERRUPT_FLAG, &x25->flags)) {
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		skb = skb_dequeue(&x25->interrupt_out_queue);
1538c2ecf20Sopenharmony_ci		x25_transmit_link(skb, x25->neighbour);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (x25->condition & X25_COND_PEER_RX_BUSY)
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!skb_peek(&sk->sk_write_queue))
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	modulus = x25->neighbour->extended ? X25_EMODULUS : X25_SMODULUS;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	start   = skb_peek(&x25->ack_queue) ? x25->vs : x25->va;
1658c2ecf20Sopenharmony_ci	end     = (x25->va + x25->facilities.winsize_out) % modulus;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (start == end)
1688c2ecf20Sopenharmony_ci		return;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	x25->vs = start;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Transmit data until either we're out of data to send or
1748c2ecf20Sopenharmony_ci	 * the window is full.
1758c2ecf20Sopenharmony_ci	 */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	skb = skb_dequeue(&sk->sk_write_queue);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	do {
1808c2ecf20Sopenharmony_ci		if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
1818c2ecf20Sopenharmony_ci			skb_queue_head(&sk->sk_write_queue, skb);
1828c2ecf20Sopenharmony_ci			break;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		skb_set_owner_w(skbn, sk);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		/*
1888c2ecf20Sopenharmony_ci		 * Transmit the frame copy.
1898c2ecf20Sopenharmony_ci		 */
1908c2ecf20Sopenharmony_ci		x25_send_iframe(sk, skbn);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		x25->vs = (x25->vs + 1) % modulus;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		/*
1958c2ecf20Sopenharmony_ci		 * Requeue the original data frame.
1968c2ecf20Sopenharmony_ci		 */
1978c2ecf20Sopenharmony_ci		skb_queue_tail(&x25->ack_queue, skb);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	} while (x25->vs != end &&
2008c2ecf20Sopenharmony_ci		 (skb = skb_dequeue(&sk->sk_write_queue)) != NULL);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	x25->vl         = x25->vr;
2038c2ecf20Sopenharmony_ci	x25->condition &= ~X25_COND_ACK_PENDING;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	x25_stop_timer(sk);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/*
2098c2ecf20Sopenharmony_ci * The following routines are taken from page 170 of the 7th ARRL Computer
2108c2ecf20Sopenharmony_ci * Networking Conference paper, as is the whole state machine.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_civoid x25_enquiry_response(struct sock *sk)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct x25_sock *x25 = x25_sk(sk);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (x25->condition & X25_COND_OWN_RX_BUSY)
2188c2ecf20Sopenharmony_ci		x25_write_internal(sk, X25_RNR);
2198c2ecf20Sopenharmony_ci	else
2208c2ecf20Sopenharmony_ci		x25_write_internal(sk, X25_RR);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	x25->vl         = x25->vr;
2238c2ecf20Sopenharmony_ci	x25->condition &= ~X25_COND_ACK_PENDING;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	x25_stop_timer(sk);
2268c2ecf20Sopenharmony_ci}
227