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 */ 68c2ecf20Sopenharmony_ci#include <linux/errno.h> 78c2ecf20Sopenharmony_ci#include <linux/types.h> 88c2ecf20Sopenharmony_ci#include <linux/socket.h> 98c2ecf20Sopenharmony_ci#include <linux/in.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/timer.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/sockios.h> 148c2ecf20Sopenharmony_ci#include <linux/net.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <net/ax25.h> 178c2ecf20Sopenharmony_ci#include <linux/inet.h> 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci#include <net/sock.h> 218c2ecf20Sopenharmony_ci#include <net/tcp_states.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 routine purges all of the queues of frames. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_civoid nr_clear_queues(struct sock *sk) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci skb_queue_purge(&sk->sk_write_queue); 368c2ecf20Sopenharmony_ci skb_queue_purge(&nr->ack_queue); 378c2ecf20Sopenharmony_ci skb_queue_purge(&nr->reseq_queue); 388c2ecf20Sopenharmony_ci skb_queue_purge(&nr->frag_queue); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * This routine purges the input queue of those frames that have been 438c2ecf20Sopenharmony_ci * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the 448c2ecf20Sopenharmony_ci * SDL diagram. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_civoid nr_frames_acked(struct sock *sk, unsigned short nr) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct nr_sock *nrom = nr_sk(sk); 498c2ecf20Sopenharmony_ci struct sk_buff *skb; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * Remove all the ack-ed frames from the ack queue. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci if (nrom->va != nr) { 558c2ecf20Sopenharmony_ci while (skb_peek(&nrom->ack_queue) != NULL && nrom->va != nr) { 568c2ecf20Sopenharmony_ci skb = skb_dequeue(&nrom->ack_queue); 578c2ecf20Sopenharmony_ci kfree_skb(skb); 588c2ecf20Sopenharmony_ci nrom->va = (nrom->va + 1) % NR_MODULUS; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Requeue all the un-ack-ed frames on the output queue to be picked 658c2ecf20Sopenharmony_ci * up by nr_kick called from the timer. This arrangement handles the 668c2ecf20Sopenharmony_ci * possibility of an empty output queue. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_civoid nr_requeue_frames(struct sock *sk) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb_prev = NULL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&nr_sk(sk)->ack_queue)) != NULL) { 738c2ecf20Sopenharmony_ci if (skb_prev == NULL) 748c2ecf20Sopenharmony_ci skb_queue_head(&sk->sk_write_queue, skb); 758c2ecf20Sopenharmony_ci else 768c2ecf20Sopenharmony_ci skb_append(skb_prev, skb, &sk->sk_write_queue); 778c2ecf20Sopenharmony_ci skb_prev = skb; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Validate that the value of nr is between va and vs. Return true or 838c2ecf20Sopenharmony_ci * false for testing. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ciint nr_validate_nr(struct sock *sk, unsigned short nr) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct nr_sock *nrom = nr_sk(sk); 888c2ecf20Sopenharmony_ci unsigned short vc = nrom->va; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci while (vc != nrom->vs) { 918c2ecf20Sopenharmony_ci if (nr == vc) return 1; 928c2ecf20Sopenharmony_ci vc = (vc + 1) % NR_MODULUS; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return nr == nrom->vs; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * Check that ns is within the receive window. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ciint nr_in_rx_window(struct sock *sk, unsigned short ns) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 1048c2ecf20Sopenharmony_ci unsigned short vc = nr->vr; 1058c2ecf20Sopenharmony_ci unsigned short vt = (nr->vl + nr->window) % NR_MODULUS; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci while (vc != vt) { 1088c2ecf20Sopenharmony_ci if (ns == vc) return 1; 1098c2ecf20Sopenharmony_ci vc = (vc + 1) % NR_MODULUS; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * This routine is called when the HDLC layer internally generates a 1178c2ecf20Sopenharmony_ci * control frame. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_civoid nr_write_internal(struct sock *sk, int frametype) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 1228c2ecf20Sopenharmony_ci struct sk_buff *skb; 1238c2ecf20Sopenharmony_ci unsigned char *dptr; 1248c2ecf20Sopenharmony_ci int len, timeout; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci len = NR_TRANSPORT_LEN; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (frametype & 0x0F) { 1298c2ecf20Sopenharmony_ci case NR_CONNREQ: 1308c2ecf20Sopenharmony_ci len += 17; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case NR_CONNACK: 1338c2ecf20Sopenharmony_ci len += (nr->bpqext) ? 2 : 1; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case NR_DISCREQ: 1368c2ecf20Sopenharmony_ci case NR_DISCACK: 1378c2ecf20Sopenharmony_ci case NR_INFOACK: 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci default: 1408c2ecf20Sopenharmony_ci printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype); 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci skb = alloc_skb(NR_NETWORK_LEN + len, GFP_ATOMIC); 1458c2ecf20Sopenharmony_ci if (!skb) 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * Space for AX.25 and NET/ROM network header 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci skb_reserve(skb, NR_NETWORK_LEN); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dptr = skb_put(skb, len); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (frametype & 0x0F) { 1568c2ecf20Sopenharmony_ci case NR_CONNREQ: 1578c2ecf20Sopenharmony_ci timeout = nr->t1 / HZ; 1588c2ecf20Sopenharmony_ci *dptr++ = nr->my_index; 1598c2ecf20Sopenharmony_ci *dptr++ = nr->my_id; 1608c2ecf20Sopenharmony_ci *dptr++ = 0; 1618c2ecf20Sopenharmony_ci *dptr++ = 0; 1628c2ecf20Sopenharmony_ci *dptr++ = frametype; 1638c2ecf20Sopenharmony_ci *dptr++ = nr->window; 1648c2ecf20Sopenharmony_ci memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); 1658c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 1668c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_EBIT; 1678c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 1688c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 1698c2ecf20Sopenharmony_ci memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); 1708c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 1718c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_EBIT; 1728c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 1738c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 1748c2ecf20Sopenharmony_ci *dptr++ = timeout % 256; 1758c2ecf20Sopenharmony_ci *dptr++ = timeout / 256; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci case NR_CONNACK: 1798c2ecf20Sopenharmony_ci *dptr++ = nr->your_index; 1808c2ecf20Sopenharmony_ci *dptr++ = nr->your_id; 1818c2ecf20Sopenharmony_ci *dptr++ = nr->my_index; 1828c2ecf20Sopenharmony_ci *dptr++ = nr->my_id; 1838c2ecf20Sopenharmony_ci *dptr++ = frametype; 1848c2ecf20Sopenharmony_ci *dptr++ = nr->window; 1858c2ecf20Sopenharmony_ci if (nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci case NR_DISCREQ: 1898c2ecf20Sopenharmony_ci case NR_DISCACK: 1908c2ecf20Sopenharmony_ci *dptr++ = nr->your_index; 1918c2ecf20Sopenharmony_ci *dptr++ = nr->your_id; 1928c2ecf20Sopenharmony_ci *dptr++ = 0; 1938c2ecf20Sopenharmony_ci *dptr++ = 0; 1948c2ecf20Sopenharmony_ci *dptr++ = frametype; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci case NR_INFOACK: 1988c2ecf20Sopenharmony_ci *dptr++ = nr->your_index; 1998c2ecf20Sopenharmony_ci *dptr++ = nr->your_id; 2008c2ecf20Sopenharmony_ci *dptr++ = 0; 2018c2ecf20Sopenharmony_ci *dptr++ = nr->vr; 2028c2ecf20Sopenharmony_ci *dptr++ = frametype; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci nr_transmit_buffer(sk, skb); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * This routine is called to send an error reply. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_civoid __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct sk_buff *skbn; 2158c2ecf20Sopenharmony_ci unsigned char *dptr; 2168c2ecf20Sopenharmony_ci int len; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci len = NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL) 2218c2ecf20Sopenharmony_ci return; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci skb_reserve(skbn, 0); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, 7, dptr, AX25_ADDR_LEN); 2288c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 2298c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_EBIT; 2308c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 2318c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, dptr, AX25_ADDR_LEN); 2348c2ecf20Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 2358c2ecf20Sopenharmony_ci dptr[6] |= AX25_EBIT; 2368c2ecf20Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 2378c2ecf20Sopenharmony_ci dptr += AX25_ADDR_LEN; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci *dptr++ = sysctl_netrom_network_ttl_initialiser; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (mine) { 2428c2ecf20Sopenharmony_ci *dptr++ = 0; 2438c2ecf20Sopenharmony_ci *dptr++ = 0; 2448c2ecf20Sopenharmony_ci *dptr++ = skb->data[15]; 2458c2ecf20Sopenharmony_ci *dptr++ = skb->data[16]; 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci *dptr++ = skb->data[15]; 2488c2ecf20Sopenharmony_ci *dptr++ = skb->data[16]; 2498c2ecf20Sopenharmony_ci *dptr++ = 0; 2508c2ecf20Sopenharmony_ci *dptr++ = 0; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci *dptr++ = cmdflags; 2548c2ecf20Sopenharmony_ci *dptr++ = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!nr_route_frame(skbn, NULL)) 2578c2ecf20Sopenharmony_ci kfree_skb(skbn); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_civoid nr_disconnect(struct sock *sk, int reason) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci nr_stop_t1timer(sk); 2638c2ecf20Sopenharmony_ci nr_stop_t2timer(sk); 2648c2ecf20Sopenharmony_ci nr_stop_t4timer(sk); 2658c2ecf20Sopenharmony_ci nr_stop_idletimer(sk); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci nr_clear_queues(sk); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci nr_sk(sk)->state = NR_STATE_0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci sk->sk_state = TCP_CLOSE; 2728c2ecf20Sopenharmony_ci sk->sk_err = reason; 2738c2ecf20Sopenharmony_ci sk->sk_shutdown |= SEND_SHUTDOWN; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) { 2768c2ecf20Sopenharmony_ci sk->sk_state_change(sk); 2778c2ecf20Sopenharmony_ci sock_set_flag(sk, SOCK_DEAD); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 280