18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/types.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/socket.h>
98c2ecf20Sopenharmony_ci#include <linux/timer.h>
108c2ecf20Sopenharmony_ci#include <net/ax25.h>
118c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
128c2ecf20Sopenharmony_ci#include <net/rose.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic struct sk_buff_head loopback_queue;
168c2ecf20Sopenharmony_ci#define ROSE_LOOPBACK_LIMIT 1000
178c2ecf20Sopenharmony_cistatic struct timer_list loopback_timer;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void rose_set_loopback_timer(void);
208c2ecf20Sopenharmony_cistatic void rose_loopback_timer(struct timer_list *unused);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_civoid rose_loopback_init(void)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	skb_queue_head_init(&loopback_queue);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	timer_setup(&loopback_timer, rose_loopback_timer, 0);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int rose_loopback_running(void)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	return timer_pending(&loopback_timer);
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciint rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct sk_buff *skbn = NULL;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (skb_queue_len(&loopback_queue) < ROSE_LOOPBACK_LIMIT)
398c2ecf20Sopenharmony_ci		skbn = skb_clone(skb, GFP_ATOMIC);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (skbn) {
428c2ecf20Sopenharmony_ci		consume_skb(skb);
438c2ecf20Sopenharmony_ci		skb_queue_tail(&loopback_queue, skbn);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if (!rose_loopback_running())
468c2ecf20Sopenharmony_ci			rose_set_loopback_timer();
478c2ecf20Sopenharmony_ci	} else {
488c2ecf20Sopenharmony_ci		kfree_skb(skb);
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return 1;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void rose_set_loopback_timer(void)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	mod_timer(&loopback_timer, jiffies + 10);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void rose_loopback_timer(struct timer_list *unused)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct sk_buff *skb;
628c2ecf20Sopenharmony_ci	struct net_device *dev;
638c2ecf20Sopenharmony_ci	rose_address *dest;
648c2ecf20Sopenharmony_ci	struct sock *sk;
658c2ecf20Sopenharmony_ci	unsigned short frametype;
668c2ecf20Sopenharmony_ci	unsigned int lci_i, lci_o;
678c2ecf20Sopenharmony_ci	int count;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	for (count = 0; count < ROSE_LOOPBACK_LIMIT; count++) {
708c2ecf20Sopenharmony_ci		skb = skb_dequeue(&loopback_queue);
718c2ecf20Sopenharmony_ci		if (!skb)
728c2ecf20Sopenharmony_ci			return;
738c2ecf20Sopenharmony_ci		if (skb->len < ROSE_MIN_LEN) {
748c2ecf20Sopenharmony_ci			kfree_skb(skb);
758c2ecf20Sopenharmony_ci			continue;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci		lci_i     = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
788c2ecf20Sopenharmony_ci		frametype = skb->data[2];
798c2ecf20Sopenharmony_ci		if (frametype == ROSE_CALL_REQUEST &&
808c2ecf20Sopenharmony_ci		    (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF ||
818c2ecf20Sopenharmony_ci		     skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] !=
828c2ecf20Sopenharmony_ci		     ROSE_CALL_REQ_ADDR_LEN_VAL)) {
838c2ecf20Sopenharmony_ci			kfree_skb(skb);
848c2ecf20Sopenharmony_ci			continue;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci		dest      = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF);
878c2ecf20Sopenharmony_ci		lci_o     = ROSE_DEFAULT_MAXVC + 1 - lci_i;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		skb_reset_transport_header(skb);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		sk = rose_find_socket(lci_o, rose_loopback_neigh);
928c2ecf20Sopenharmony_ci		if (sk) {
938c2ecf20Sopenharmony_ci			if (rose_process_rx_frame(sk, skb) == 0)
948c2ecf20Sopenharmony_ci				kfree_skb(skb);
958c2ecf20Sopenharmony_ci			continue;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (frametype == ROSE_CALL_REQUEST) {
998c2ecf20Sopenharmony_ci			if (!rose_loopback_neigh->dev &&
1008c2ecf20Sopenharmony_ci			    !rose_loopback_neigh->loopback) {
1018c2ecf20Sopenharmony_ci				kfree_skb(skb);
1028c2ecf20Sopenharmony_ci				continue;
1038c2ecf20Sopenharmony_ci			}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci			dev = rose_dev_get(dest);
1068c2ecf20Sopenharmony_ci			if (!dev) {
1078c2ecf20Sopenharmony_ci				kfree_skb(skb);
1088c2ecf20Sopenharmony_ci				continue;
1098c2ecf20Sopenharmony_ci			}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci			if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0) {
1128c2ecf20Sopenharmony_ci				dev_put(dev);
1138c2ecf20Sopenharmony_ci				kfree_skb(skb);
1148c2ecf20Sopenharmony_ci			}
1158c2ecf20Sopenharmony_ci		} else {
1168c2ecf20Sopenharmony_ci			kfree_skb(skb);
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&loopback_queue))
1208c2ecf20Sopenharmony_ci		mod_timer(&loopback_timer, jiffies + 1);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_civoid __exit rose_loopback_clear(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	del_timer(&loopback_timer);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
1308c2ecf20Sopenharmony_ci		skb->sk = NULL;
1318c2ecf20Sopenharmony_ci		kfree_skb(skb);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci}
134