162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 562306a36Sopenharmony_ci * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/socket.h> 1062306a36Sopenharmony_ci#include <linux/in.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/timer.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/sockios.h> 1562306a36Sopenharmony_ci#include <linux/net.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <net/ax25.h> 1862306a36Sopenharmony_ci#include <linux/inet.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <net/sock.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include <linux/fcntl.h> 2462306a36Sopenharmony_ci#include <linux/mm.h> 2562306a36Sopenharmony_ci#include <linux/interrupt.h> 2662306a36Sopenharmony_ci#include <net/netrom.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * This is where all NET/ROM frames pass, except for IP-over-NET/ROM which 3062306a36Sopenharmony_ci * cannot be fragmented in this manner. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_civoid nr_output(struct sock *sk, struct sk_buff *skb) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct sk_buff *skbn; 3562306a36Sopenharmony_ci unsigned char transport[NR_TRANSPORT_LEN]; 3662306a36Sopenharmony_ci int err, frontlen, len; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) { 3962306a36Sopenharmony_ci /* Save a copy of the Transport Header */ 4062306a36Sopenharmony_ci skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN); 4162306a36Sopenharmony_ci skb_pull(skb, NR_TRANSPORT_LEN); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci frontlen = skb_headroom(skb); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci while (skb->len > 0) { 4662306a36Sopenharmony_ci if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) 4762306a36Sopenharmony_ci return; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci skb_reserve(skbn, frontlen); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Copy the user data */ 5462306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skbn, len), len); 5562306a36Sopenharmony_ci skb_pull(skb, len); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Duplicate the Transport Header */ 5862306a36Sopenharmony_ci skb_push(skbn, NR_TRANSPORT_LEN); 5962306a36Sopenharmony_ci skb_copy_to_linear_data(skbn, transport, 6062306a36Sopenharmony_ci NR_TRANSPORT_LEN); 6162306a36Sopenharmony_ci if (skb->len > 0) 6262306a36Sopenharmony_ci skbn->data[4] |= NR_MORE_FLAG; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci kfree_skb(skb); 6862306a36Sopenharmony_ci } else { 6962306a36Sopenharmony_ci skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci nr_kick(sk); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * This procedure is passed a buffer descriptor for an iframe. It builds 7762306a36Sopenharmony_ci * the rest of the control part of the frame and then writes it out. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic void nr_send_iframe(struct sock *sk, struct sk_buff *skb) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (skb == NULL) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci skb->data[2] = nr->vs; 8762306a36Sopenharmony_ci skb->data[3] = nr->vr; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) 9062306a36Sopenharmony_ci skb->data[4] |= NR_CHOKE_FLAG; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci nr_start_idletimer(sk); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci nr_transmit_buffer(sk, skb); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid nr_send_nak_frame(struct sock *sk) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct sk_buff *skb, *skbn; 10062306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if ((skb = skb_peek(&nr->ack_queue)) == NULL) 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) 10662306a36Sopenharmony_ci return; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci skbn->data[2] = nr->va; 10962306a36Sopenharmony_ci skbn->data[3] = nr->vr; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) 11262306a36Sopenharmony_ci skbn->data[4] |= NR_CHOKE_FLAG; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci nr_transmit_buffer(sk, skbn); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 11762306a36Sopenharmony_ci nr->vl = nr->vr; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci nr_stop_t1timer(sk); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid nr_kick(struct sock *sk) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 12562306a36Sopenharmony_ci struct sk_buff *skb, *skbn; 12662306a36Sopenharmony_ci unsigned short start, end; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (nr->state != NR_STATE_3) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (nr->condition & NR_COND_PEER_RX_BUSY) 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!skb_peek(&sk->sk_write_queue)) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci start = (skb_peek(&nr->ack_queue) == NULL) ? nr->va : nr->vs; 13862306a36Sopenharmony_ci end = (nr->va + nr->window) % NR_MODULUS; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (start == end) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci nr->vs = start; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Transmit data until either we're out of data to send or 14762306a36Sopenharmony_ci * the window is full. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Dequeue the frame and copy it. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci skb = skb_dequeue(&sk->sk_write_queue); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci do { 15662306a36Sopenharmony_ci if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { 15762306a36Sopenharmony_ci skb_queue_head(&sk->sk_write_queue, skb); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci skb_set_owner_w(skbn, sk); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Transmit the frame copy. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci nr_send_iframe(sk, skbn); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci nr->vs = (nr->vs + 1) % NR_MODULUS; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Requeue the original data frame. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci skb_queue_tail(&nr->ack_queue, skb); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci } while (nr->vs != end && 17662306a36Sopenharmony_ci (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci nr->vl = nr->vr; 17962306a36Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!nr_t1timer_running(sk)) 18262306a36Sopenharmony_ci nr_start_t1timer(sk); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_civoid nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 18862306a36Sopenharmony_ci unsigned char *dptr; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Add the protocol byte and network header. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci dptr = skb_push(skb, NR_NETWORK_LEN); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); 19662306a36Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 19762306a36Sopenharmony_ci dptr[6] &= ~AX25_EBIT; 19862306a36Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 19962306a36Sopenharmony_ci dptr += AX25_ADDR_LEN; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci memcpy(dptr, &nr->dest_addr, AX25_ADDR_LEN); 20262306a36Sopenharmony_ci dptr[6] &= ~AX25_CBIT; 20362306a36Sopenharmony_ci dptr[6] |= AX25_EBIT; 20462306a36Sopenharmony_ci dptr[6] |= AX25_SSSID_SPARE; 20562306a36Sopenharmony_ci dptr += AX25_ADDR_LEN; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!nr_route_frame(skb, NULL)) { 21062306a36Sopenharmony_ci kfree_skb(skb); 21162306a36Sopenharmony_ci nr_disconnect(sk, ENETUNREACH); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * The following routines are taken from page 170 of the 7th ARRL Computer 21762306a36Sopenharmony_ci * Networking Conference paper, as is the whole state machine. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_civoid nr_establish_data_link(struct sock *sk) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci nr->condition = 0x00; 22562306a36Sopenharmony_ci nr->n2count = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci nr_write_internal(sk, NR_CONNREQ); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci nr_stop_t2timer(sk); 23062306a36Sopenharmony_ci nr_stop_t4timer(sk); 23162306a36Sopenharmony_ci nr_stop_idletimer(sk); 23262306a36Sopenharmony_ci nr_start_t1timer(sk); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * Never send a NAK when we are CHOKEd. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_civoid nr_enquiry_response(struct sock *sk) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct nr_sock *nr = nr_sk(sk); 24162306a36Sopenharmony_ci int frametype = NR_INFOACK; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (nr->condition & NR_COND_OWN_RX_BUSY) { 24462306a36Sopenharmony_ci frametype |= NR_CHOKE_FLAG; 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci if (skb_peek(&nr->reseq_queue) != NULL) 24762306a36Sopenharmony_ci frametype |= NR_NAK_FLAG; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci nr_write_internal(sk, frametype); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci nr->vl = nr->vr; 25362306a36Sopenharmony_ci nr->condition &= ~NR_COND_ACK_PENDING; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_civoid nr_check_iframes_acked(struct sock *sk, unsigned short nr) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct nr_sock *nrom = nr_sk(sk); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (nrom->vs == nr) { 26162306a36Sopenharmony_ci nr_frames_acked(sk, nr); 26262306a36Sopenharmony_ci nr_stop_t1timer(sk); 26362306a36Sopenharmony_ci nrom->n2count = 0; 26462306a36Sopenharmony_ci } else { 26562306a36Sopenharmony_ci if (nrom->va != nr) { 26662306a36Sopenharmony_ci nr_frames_acked(sk, nr); 26762306a36Sopenharmony_ci nr_start_t1timer(sk); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 271