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