18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 58c2ecf20Sopenharmony_ci * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/socket.h> 108c2ecf20Sopenharmony_ci#include <linux/in.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/timer.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/sockios.h> 158c2ecf20Sopenharmony_ci#include <linux/net.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <net/ax25.h> 188c2ecf20Sopenharmony_ci#include <linux/inet.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 218c2ecf20Sopenharmony_ci#include <net/sock.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 248c2ecf20Sopenharmony_ci#include <linux/mm.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <net/netrom.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * This is where all NET/ROM frames pass, except for IP-over-NET/ROM which 308c2ecf20Sopenharmony_ci * cannot be fragmented in this manner. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_civoid nr_output(struct sock *sk, struct sk_buff *skb) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct sk_buff *skbn; 358c2ecf20Sopenharmony_ci unsigned char transport[NR_TRANSPORT_LEN]; 368c2ecf20Sopenharmony_ci int err, frontlen, len; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) { 398c2ecf20Sopenharmony_ci /* Save a copy of the Transport Header */ 408c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN); 418c2ecf20Sopenharmony_ci skb_pull(skb, NR_TRANSPORT_LEN); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci frontlen = skb_headroom(skb); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci while (skb->len > 0) { 468c2ecf20Sopenharmony_ci if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) 478c2ecf20Sopenharmony_ci return; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci skb_reserve(skbn, frontlen); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Copy the user data */ 548c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skbn, len), len); 558c2ecf20Sopenharmony_ci skb_pull(skb, len); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Duplicate the Transport Header */ 588c2ecf20Sopenharmony_ci skb_push(skbn, NR_TRANSPORT_LEN); 598c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skbn, transport, 608c2ecf20Sopenharmony_ci NR_TRANSPORT_LEN); 618c2ecf20Sopenharmony_ci if (skb->len > 0) 628c2ecf20Sopenharmony_ci skbn->data[4] |= NR_MORE_FLAG; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci kfree_skb(skb); 688c2ecf20Sopenharmony_ci } else { 698c2ecf20Sopenharmony_ci skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci nr_kick(sk); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * This procedure is passed a buffer descriptor for an iframe. It builds 778c2ecf20Sopenharmony_ci * the rest of the control part of the frame and then writes it out. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic void nr_send_iframe(struct sock *sk, struct sk_buff *skb) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (skb == NULL) 848c2ecf20Sopenharmony_ci return; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci skb->data[2] = nr->vs; 878c2ecf20Sopenharmony_ci skb->data[3] = nr->vr; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) 908c2ecf20Sopenharmony_ci skb->data[4] |= NR_CHOKE_FLAG; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci nr_start_idletimer(sk); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci nr_transmit_buffer(sk, skb); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid nr_send_nak_frame(struct sock *sk) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct sk_buff *skb, *skbn; 1008c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if ((skb = skb_peek(&nr->ack_queue)) == NULL) 1038c2ecf20Sopenharmony_ci return; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) 1068c2ecf20Sopenharmony_ci return; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci skbn->data[2] = nr->va; 1098c2ecf20Sopenharmony_ci skbn->data[3] = nr->vr; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) 1128c2ecf20Sopenharmony_ci skbn->data[4] |= NR_CHOKE_FLAG; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci nr_transmit_buffer(sk, skbn); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 1178c2ecf20Sopenharmony_ci nr->vl = nr->vr; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci nr_stop_t1timer(sk); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_civoid nr_kick(struct sock *sk) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 1258c2ecf20Sopenharmony_ci struct sk_buff *skb, *skbn; 1268c2ecf20Sopenharmony_ci unsigned short start, end; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (nr->state != NR_STATE_3) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (nr->condition & NR_COND_PEER_RX_BUSY) 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!skb_peek(&sk->sk_write_queue)) 1358c2ecf20Sopenharmony_ci return; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci start = (skb_peek(&nr->ack_queue) == NULL) ? nr->va : nr->vs; 1388c2ecf20Sopenharmony_ci end = (nr->va + nr->window) % NR_MODULUS; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (start == end) 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci nr->vs = start; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * Transmit data until either we're out of data to send or 1478c2ecf20Sopenharmony_ci * the window is full. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Dequeue the frame and copy it. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci skb = skb_dequeue(&sk->sk_write_queue); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci do { 1568c2ecf20Sopenharmony_ci if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { 1578c2ecf20Sopenharmony_ci skb_queue_head(&sk->sk_write_queue, skb); 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci skb_set_owner_w(skbn, sk); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Transmit the frame copy. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci nr_send_iframe(sk, skbn); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci nr->vs = (nr->vs + 1) % NR_MODULUS; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * Requeue the original data frame. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci skb_queue_tail(&nr->ack_queue, skb); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci } while (nr->vs != end && 1768c2ecf20Sopenharmony_ci (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci nr->vl = nr->vr; 1798c2ecf20Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!nr_t1timer_running(sk)) 1828c2ecf20Sopenharmony_ci nr_start_t1timer(sk); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_civoid nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 1888c2ecf20Sopenharmony_ci unsigned char *dptr; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * Add the protocol byte and network header. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci dptr = skb_push(skb, NR_NETWORK_LEN); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); 1968c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 1978c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_EBIT; 1988c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 1998c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci memcpy(dptr, &nr->dest_addr, AX25_ADDR_LEN); 2028c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 2038c2ecf20Sopenharmony_ci dptr[6] |= AX25_EBIT; 2048c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 2058c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci *dptr++ = sysctl_netrom_network_ttl_initialiser; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!nr_route_frame(skb, NULL)) { 2108c2ecf20Sopenharmony_ci kfree_skb(skb); 2118c2ecf20Sopenharmony_ci nr_disconnect(sk, ENETUNREACH); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * The following routines are taken from page 170 of the 7th ARRL Computer 2178c2ecf20Sopenharmony_ci * Networking Conference paper, as is the whole state machine. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_civoid nr_establish_data_link(struct sock *sk) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci nr->condition = 0x00; 2258c2ecf20Sopenharmony_ci nr->n2count = 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci nr_write_internal(sk, NR_CONNREQ); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci nr_stop_t2timer(sk); 2308c2ecf20Sopenharmony_ci nr_stop_t4timer(sk); 2318c2ecf20Sopenharmony_ci nr_stop_idletimer(sk); 2328c2ecf20Sopenharmony_ci nr_start_t1timer(sk); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * Never send a NAK when we are CHOKEd. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_civoid nr_enquiry_response(struct sock *sk) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 2418c2ecf20Sopenharmony_ci int frametype = NR_INFOACK; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) { 2448c2ecf20Sopenharmony_ci frametype |= NR_CHOKE_FLAG; 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci if (skb_peek(&nr->reseq_queue) != NULL) 2478c2ecf20Sopenharmony_ci frametype |= NR_NAK_FLAG; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci nr_write_internal(sk, frametype); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci nr->vl = nr->vr; 2538c2ecf20Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_civoid nr_check_iframes_acked(struct sock *sk, unsigned short nr) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct nr_sock *nrom = nr_sk(sk); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (nrom->vs == nr) { 2618c2ecf20Sopenharmony_ci nr_frames_acked(sk, nr); 2628c2ecf20Sopenharmony_ci nr_stop_t1timer(sk); 2638c2ecf20Sopenharmony_ci nrom->n2count = 0; 2648c2ecf20Sopenharmony_ci } else { 2658c2ecf20Sopenharmony_ci if (nrom->va != nr) { 2668c2ecf20Sopenharmony_ci nr_frames_acked(sk, nr); 2678c2ecf20Sopenharmony_ci nr_start_t1timer(sk); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci} 271