xref: /linux/net/rose/rose_subr.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds #include <linux/errno.h>
71da177e4SLinus Torvalds #include <linux/types.h>
81da177e4SLinus Torvalds #include <linux/socket.h>
91da177e4SLinus Torvalds #include <linux/in.h>
101da177e4SLinus Torvalds #include <linux/kernel.h>
111da177e4SLinus Torvalds #include <linux/timer.h>
121da177e4SLinus Torvalds #include <linux/string.h>
131da177e4SLinus Torvalds #include <linux/sockios.h>
141da177e4SLinus Torvalds #include <linux/net.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
161da177e4SLinus Torvalds #include <net/ax25.h>
171da177e4SLinus Torvalds #include <linux/inet.h>
181da177e4SLinus Torvalds #include <linux/netdevice.h>
191da177e4SLinus Torvalds #include <linux/skbuff.h>
201da177e4SLinus Torvalds #include <net/sock.h>
21c752f073SArnaldo Carvalho de Melo #include <net/tcp_states.h>
221da177e4SLinus Torvalds #include <linux/fcntl.h>
231da177e4SLinus Torvalds #include <linux/mm.h>
241da177e4SLinus Torvalds #include <linux/interrupt.h>
251da177e4SLinus Torvalds #include <net/rose.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose);
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /*
301da177e4SLinus Torvalds  *	This routine purges all of the queues of frames.
311da177e4SLinus Torvalds  */
rose_clear_queues(struct sock * sk)321da177e4SLinus Torvalds void rose_clear_queues(struct sock *sk)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	skb_queue_purge(&sk->sk_write_queue);
351da177e4SLinus Torvalds 	skb_queue_purge(&rose_sk(sk)->ack_queue);
361da177e4SLinus Torvalds }
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * This routine purges the input queue of those frames that have been
401da177e4SLinus Torvalds  * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
411da177e4SLinus Torvalds  * SDL diagram.
421da177e4SLinus Torvalds  */
rose_frames_acked(struct sock * sk,unsigned short nr)431da177e4SLinus Torvalds void rose_frames_acked(struct sock *sk, unsigned short nr)
441da177e4SLinus Torvalds {
451da177e4SLinus Torvalds 	struct sk_buff *skb;
461da177e4SLinus Torvalds 	struct rose_sock *rose = rose_sk(sk);
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	/*
491da177e4SLinus Torvalds 	 * Remove all the ack-ed frames from the ack queue.
501da177e4SLinus Torvalds 	 */
511da177e4SLinus Torvalds 	if (rose->va != nr) {
521da177e4SLinus Torvalds 		while (skb_peek(&rose->ack_queue) != NULL && rose->va != nr) {
531da177e4SLinus Torvalds 			skb = skb_dequeue(&rose->ack_queue);
541da177e4SLinus Torvalds 			kfree_skb(skb);
551da177e4SLinus Torvalds 			rose->va = (rose->va + 1) % ROSE_MODULUS;
561da177e4SLinus Torvalds 		}
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
rose_requeue_frames(struct sock * sk)601da177e4SLinus Torvalds void rose_requeue_frames(struct sock *sk)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	struct sk_buff *skb, *skb_prev = NULL;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	/*
651da177e4SLinus Torvalds 	 * Requeue all the un-ack-ed frames on the output queue to be picked
661da177e4SLinus Torvalds 	 * up by rose_kick. This arrangement handles the possibility of an
671da177e4SLinus Torvalds 	 * empty output queue.
681da177e4SLinus Torvalds 	 */
691da177e4SLinus Torvalds 	while ((skb = skb_dequeue(&rose_sk(sk)->ack_queue)) != NULL) {
701da177e4SLinus Torvalds 		if (skb_prev == NULL)
711da177e4SLinus Torvalds 			skb_queue_head(&sk->sk_write_queue, skb);
721da177e4SLinus Torvalds 		else
738728b834SDavid S. Miller 			skb_append(skb_prev, skb, &sk->sk_write_queue);
741da177e4SLinus Torvalds 		skb_prev = skb;
751da177e4SLinus Torvalds 	}
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /*
791da177e4SLinus Torvalds  *	Validate that the value of nr is between va and vs. Return true or
801da177e4SLinus Torvalds  *	false for testing.
811da177e4SLinus Torvalds  */
rose_validate_nr(struct sock * sk,unsigned short nr)821da177e4SLinus Torvalds int rose_validate_nr(struct sock *sk, unsigned short nr)
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds 	struct rose_sock *rose = rose_sk(sk);
851da177e4SLinus Torvalds 	unsigned short vc = rose->va;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	while (vc != rose->vs) {
881da177e4SLinus Torvalds 		if (nr == vc) return 1;
891da177e4SLinus Torvalds 		vc = (vc + 1) % ROSE_MODULUS;
901da177e4SLinus Torvalds 	}
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	return nr == rose->vs;
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds /*
961da177e4SLinus Torvalds  *  This routine is called when the packet layer internally generates a
971da177e4SLinus Torvalds  *  control frame.
981da177e4SLinus Torvalds  */
rose_write_internal(struct sock * sk,int frametype)991da177e4SLinus Torvalds void rose_write_internal(struct sock *sk, int frametype)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	struct rose_sock *rose = rose_sk(sk);
1021da177e4SLinus Torvalds 	struct sk_buff *skb;
1031da177e4SLinus Torvalds 	unsigned char  *dptr;
1041da177e4SLinus Torvalds 	unsigned char  lci1, lci2;
105e5dcc0c3SEric Dumazet 	int maxfaclen = 0;
106e5dcc0c3SEric Dumazet 	int len, faclen;
107e5dcc0c3SEric Dumazet 	int reserve;
1081da177e4SLinus Torvalds 
109e5dcc0c3SEric Dumazet 	reserve = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1;
110e5dcc0c3SEric Dumazet 	len = ROSE_MIN_LEN;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	switch (frametype) {
1131da177e4SLinus Torvalds 	case ROSE_CALL_REQUEST:
1141da177e4SLinus Torvalds 		len   += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
115e5dcc0c3SEric Dumazet 		maxfaclen = 256;
1161da177e4SLinus Torvalds 		break;
1171da177e4SLinus Torvalds 	case ROSE_CALL_ACCEPTED:
1181da177e4SLinus Torvalds 	case ROSE_CLEAR_REQUEST:
1191da177e4SLinus Torvalds 	case ROSE_RESET_REQUEST:
1201da177e4SLinus Torvalds 		len   += 2;
1211da177e4SLinus Torvalds 		break;
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds 
124e5dcc0c3SEric Dumazet 	skb = alloc_skb(reserve + len + maxfaclen, GFP_ATOMIC);
125e5dcc0c3SEric Dumazet 	if (!skb)
1261da177e4SLinus Torvalds 		return;
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	/*
1291da177e4SLinus Torvalds 	 *	Space for AX.25 header and PID.
1301da177e4SLinus Torvalds 	 */
131e5dcc0c3SEric Dumazet 	skb_reserve(skb, reserve);
1321da177e4SLinus Torvalds 
133e5dcc0c3SEric Dumazet 	dptr = skb_put(skb, len);
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	lci1 = (rose->lci >> 8) & 0x0F;
1361da177e4SLinus Torvalds 	lci2 = (rose->lci >> 0) & 0xFF;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	switch (frametype) {
1391da177e4SLinus Torvalds 	case ROSE_CALL_REQUEST:
1401da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1411da177e4SLinus Torvalds 		*dptr++ = lci2;
1421da177e4SLinus Torvalds 		*dptr++ = frametype;
143e0bccd31SBen Hutchings 		*dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL;
1441da177e4SLinus Torvalds 		memcpy(dptr, &rose->dest_addr,  ROSE_ADDR_LEN);
1451da177e4SLinus Torvalds 		dptr   += ROSE_ADDR_LEN;
1461da177e4SLinus Torvalds 		memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN);
1471da177e4SLinus Torvalds 		dptr   += ROSE_ADDR_LEN;
148e5dcc0c3SEric Dumazet 		faclen = rose_create_facilities(dptr, rose);
149e5dcc0c3SEric Dumazet 		skb_put(skb, faclen);
1501da177e4SLinus Torvalds 		dptr   += faclen;
1511da177e4SLinus Torvalds 		break;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	case ROSE_CALL_ACCEPTED:
1541da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1551da177e4SLinus Torvalds 		*dptr++ = lci2;
1561da177e4SLinus Torvalds 		*dptr++ = frametype;
1571da177e4SLinus Torvalds 		*dptr++ = 0x00;		/* Address length */
1581da177e4SLinus Torvalds 		*dptr++ = 0;		/* Facilities length */
1591da177e4SLinus Torvalds 		break;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	case ROSE_CLEAR_REQUEST:
1621da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1631da177e4SLinus Torvalds 		*dptr++ = lci2;
1641da177e4SLinus Torvalds 		*dptr++ = frametype;
1651da177e4SLinus Torvalds 		*dptr++ = rose->cause;
1661da177e4SLinus Torvalds 		*dptr++ = rose->diagnostic;
1671da177e4SLinus Torvalds 		break;
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	case ROSE_RESET_REQUEST:
1701da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1711da177e4SLinus Torvalds 		*dptr++ = lci2;
1721da177e4SLinus Torvalds 		*dptr++ = frametype;
1731da177e4SLinus Torvalds 		*dptr++ = ROSE_DTE_ORIGINATED;
1741da177e4SLinus Torvalds 		*dptr++ = 0;
1751da177e4SLinus Torvalds 		break;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	case ROSE_RR:
1781da177e4SLinus Torvalds 	case ROSE_RNR:
1791da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1801da177e4SLinus Torvalds 		*dptr++ = lci2;
1811da177e4SLinus Torvalds 		*dptr   = frametype;
1821da177e4SLinus Torvalds 		*dptr++ |= (rose->vr << 5) & 0xE0;
1831da177e4SLinus Torvalds 		break;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	case ROSE_CLEAR_CONFIRMATION:
1861da177e4SLinus Torvalds 	case ROSE_RESET_CONFIRMATION:
1871da177e4SLinus Torvalds 		*dptr++ = ROSE_GFI | lci1;
1881da177e4SLinus Torvalds 		*dptr++ = lci2;
1891da177e4SLinus Torvalds 		*dptr++  = frametype;
1901da177e4SLinus Torvalds 		break;
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds 	default:
1931da177e4SLinus Torvalds 		printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype);
1941da177e4SLinus Torvalds 		kfree_skb(skb);
1951da177e4SLinus Torvalds 		return;
1961da177e4SLinus Torvalds 	}
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	rose_transmit_link(skb, rose->neighbour);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
rose_decode(struct sk_buff * skb,int * ns,int * nr,int * q,int * d,int * m)2011da177e4SLinus Torvalds int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	unsigned char *frame;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	frame = skb->data;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	*ns = *nr = *q = *d = *m = 0;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	switch (frame[2]) {
2101da177e4SLinus Torvalds 	case ROSE_CALL_REQUEST:
2111da177e4SLinus Torvalds 	case ROSE_CALL_ACCEPTED:
2121da177e4SLinus Torvalds 	case ROSE_CLEAR_REQUEST:
2131da177e4SLinus Torvalds 	case ROSE_CLEAR_CONFIRMATION:
2141da177e4SLinus Torvalds 	case ROSE_RESET_REQUEST:
2151da177e4SLinus Torvalds 	case ROSE_RESET_CONFIRMATION:
2161da177e4SLinus Torvalds 		return frame[2];
2171da177e4SLinus Torvalds 	default:
2181da177e4SLinus Torvalds 		break;
2191da177e4SLinus Torvalds 	}
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	if ((frame[2] & 0x1F) == ROSE_RR  ||
2221da177e4SLinus Torvalds 	    (frame[2] & 0x1F) == ROSE_RNR) {
2231da177e4SLinus Torvalds 		*nr = (frame[2] >> 5) & 0x07;
2241da177e4SLinus Torvalds 		return frame[2] & 0x1F;
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	if ((frame[2] & 0x01) == ROSE_DATA) {
2281da177e4SLinus Torvalds 		*q  = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
2291da177e4SLinus Torvalds 		*d  = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT;
2301da177e4SLinus Torvalds 		*m  = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT;
2311da177e4SLinus Torvalds 		*nr = (frame[2] >> 5) & 0x07;
2321da177e4SLinus Torvalds 		*ns = (frame[2] >> 1) & 0x07;
2331da177e4SLinus Torvalds 		return ROSE_DATA;
2341da177e4SLinus Torvalds 	}
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	return ROSE_ILLEGAL;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
rose_parse_national(unsigned char * p,struct rose_facilities_struct * facilities,int len)2391da177e4SLinus Torvalds static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds 	unsigned char *pt;
2421da177e4SLinus Torvalds 	unsigned char l, lg, n = 0;
2431da177e4SLinus Torvalds 	int fac_national_digis_received = 0;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	do {
2461da177e4SLinus Torvalds 		switch (*p & 0xC0) {
2471da177e4SLinus Torvalds 		case 0x00:
248e0bccd31SBen Hutchings 			if (len < 2)
249e0bccd31SBen Hutchings 				return -1;
2501da177e4SLinus Torvalds 			p   += 2;
2511da177e4SLinus Torvalds 			n   += 2;
2521da177e4SLinus Torvalds 			len -= 2;
2531da177e4SLinus Torvalds 			break;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 		case 0x40:
256e0bccd31SBen Hutchings 			if (len < 3)
257e0bccd31SBen Hutchings 				return -1;
2581da177e4SLinus Torvalds 			if (*p == FAC_NATIONAL_RAND)
2591da177e4SLinus Torvalds 				facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
2601da177e4SLinus Torvalds 			p   += 3;
2611da177e4SLinus Torvalds 			n   += 3;
2621da177e4SLinus Torvalds 			len -= 3;
2631da177e4SLinus Torvalds 			break;
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 		case 0x80:
266e0bccd31SBen Hutchings 			if (len < 4)
267e0bccd31SBen Hutchings 				return -1;
2681da177e4SLinus Torvalds 			p   += 4;
2691da177e4SLinus Torvalds 			n   += 4;
2701da177e4SLinus Torvalds 			len -= 4;
2711da177e4SLinus Torvalds 			break;
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 		case 0xC0:
274e0bccd31SBen Hutchings 			if (len < 2)
275e0bccd31SBen Hutchings 				return -1;
2761da177e4SLinus Torvalds 			l = p[1];
277e0bccd31SBen Hutchings 			if (len < 2 + l)
278e0bccd31SBen Hutchings 				return -1;
2791da177e4SLinus Torvalds 			if (*p == FAC_NATIONAL_DEST_DIGI) {
2801da177e4SLinus Torvalds 				if (!fac_national_digis_received) {
281e0bccd31SBen Hutchings 					if (l < AX25_ADDR_LEN)
282e0bccd31SBen Hutchings 						return -1;
2831da177e4SLinus Torvalds 					memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN);
2841da177e4SLinus Torvalds 					facilities->source_ndigis = 1;
2851da177e4SLinus Torvalds 				}
2861da177e4SLinus Torvalds 			}
2871da177e4SLinus Torvalds 			else if (*p == FAC_NATIONAL_SRC_DIGI) {
2881da177e4SLinus Torvalds 				if (!fac_national_digis_received) {
289e0bccd31SBen Hutchings 					if (l < AX25_ADDR_LEN)
290e0bccd31SBen Hutchings 						return -1;
2911da177e4SLinus Torvalds 					memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN);
2921da177e4SLinus Torvalds 					facilities->dest_ndigis = 1;
2931da177e4SLinus Torvalds 				}
2941da177e4SLinus Torvalds 			}
2951da177e4SLinus Torvalds 			else if (*p == FAC_NATIONAL_FAIL_CALL) {
296e0bccd31SBen Hutchings 				if (l < AX25_ADDR_LEN)
297e0bccd31SBen Hutchings 					return -1;
2981da177e4SLinus Torvalds 				memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN);
2991da177e4SLinus Torvalds 			}
3001da177e4SLinus Torvalds 			else if (*p == FAC_NATIONAL_FAIL_ADD) {
301e0bccd31SBen Hutchings 				if (l < 1 + ROSE_ADDR_LEN)
302e0bccd31SBen Hutchings 					return -1;
3031da177e4SLinus Torvalds 				memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN);
3041da177e4SLinus Torvalds 			}
3051da177e4SLinus Torvalds 			else if (*p == FAC_NATIONAL_DIGIS) {
306e0bccd31SBen Hutchings 				if (l % AX25_ADDR_LEN)
307e0bccd31SBen Hutchings 					return -1;
3081da177e4SLinus Torvalds 				fac_national_digis_received = 1;
3091da177e4SLinus Torvalds 				facilities->source_ndigis = 0;
3101da177e4SLinus Torvalds 				facilities->dest_ndigis   = 0;
3111da177e4SLinus Torvalds 				for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) {
312be20250cSDan Rosenberg 					if (pt[6] & AX25_HBIT) {
313be20250cSDan Rosenberg 						if (facilities->dest_ndigis >= ROSE_MAX_DIGIS)
314be20250cSDan Rosenberg 							return -1;
3151da177e4SLinus Torvalds 						memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN);
316be20250cSDan Rosenberg 					} else {
317be20250cSDan Rosenberg 						if (facilities->source_ndigis >= ROSE_MAX_DIGIS)
318be20250cSDan Rosenberg 							return -1;
3191da177e4SLinus Torvalds 						memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN);
3201da177e4SLinus Torvalds 					}
3211da177e4SLinus Torvalds 				}
322be20250cSDan Rosenberg 			}
3231da177e4SLinus Torvalds 			p   += l + 2;
3241da177e4SLinus Torvalds 			n   += l + 2;
3251da177e4SLinus Torvalds 			len -= l + 2;
3261da177e4SLinus Torvalds 			break;
3271da177e4SLinus Torvalds 		}
3281da177e4SLinus Torvalds 	} while (*p != 0x00 && len > 0);
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	return n;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
rose_parse_ccitt(unsigned char * p,struct rose_facilities_struct * facilities,int len)3331da177e4SLinus Torvalds static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	unsigned char l, n = 0;
3361da177e4SLinus Torvalds 	char callsign[11];
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	do {
3391da177e4SLinus Torvalds 		switch (*p & 0xC0) {
3401da177e4SLinus Torvalds 		case 0x00:
341e0bccd31SBen Hutchings 			if (len < 2)
342e0bccd31SBen Hutchings 				return -1;
3431da177e4SLinus Torvalds 			p   += 2;
3441da177e4SLinus Torvalds 			n   += 2;
3451da177e4SLinus Torvalds 			len -= 2;
3461da177e4SLinus Torvalds 			break;
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 		case 0x40:
349e0bccd31SBen Hutchings 			if (len < 3)
350e0bccd31SBen Hutchings 				return -1;
3511da177e4SLinus Torvalds 			p   += 3;
3521da177e4SLinus Torvalds 			n   += 3;
3531da177e4SLinus Torvalds 			len -= 3;
3541da177e4SLinus Torvalds 			break;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 		case 0x80:
357e0bccd31SBen Hutchings 			if (len < 4)
358e0bccd31SBen Hutchings 				return -1;
3591da177e4SLinus Torvalds 			p   += 4;
3601da177e4SLinus Torvalds 			n   += 4;
3611da177e4SLinus Torvalds 			len -= 4;
3621da177e4SLinus Torvalds 			break;
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 		case 0xC0:
365e0bccd31SBen Hutchings 			if (len < 2)
366e0bccd31SBen Hutchings 				return -1;
3671da177e4SLinus Torvalds 			l = p[1];
368be20250cSDan Rosenberg 
369be20250cSDan Rosenberg 			/* Prevent overflows*/
370be20250cSDan Rosenberg 			if (l < 10 || l > 20)
371be20250cSDan Rosenberg 				return -1;
372be20250cSDan Rosenberg 
3731da177e4SLinus Torvalds 			if (*p == FAC_CCITT_DEST_NSAP) {
3741da177e4SLinus Torvalds 				memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
3751da177e4SLinus Torvalds 				memcpy(callsign, p + 12,   l - 10);
3761da177e4SLinus Torvalds 				callsign[l - 10] = '\0';
377baed16a7SRalf Baechle 				asc2ax(&facilities->source_call, callsign);
3781da177e4SLinus Torvalds 			}
3791da177e4SLinus Torvalds 			if (*p == FAC_CCITT_SRC_NSAP) {
3801da177e4SLinus Torvalds 				memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
3811da177e4SLinus Torvalds 				memcpy(callsign, p + 12, l - 10);
3821da177e4SLinus Torvalds 				callsign[l - 10] = '\0';
383baed16a7SRalf Baechle 				asc2ax(&facilities->dest_call, callsign);
3841da177e4SLinus Torvalds 			}
3851da177e4SLinus Torvalds 			p   += l + 2;
3861da177e4SLinus Torvalds 			n   += l + 2;
3871da177e4SLinus Torvalds 			len -= l + 2;
3881da177e4SLinus Torvalds 			break;
3891da177e4SLinus Torvalds 		}
3901da177e4SLinus Torvalds 	} while (*p != 0x00 && len > 0);
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	return n;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
rose_parse_facilities(unsigned char * p,unsigned packet_len,struct rose_facilities_struct * facilities)395e0bccd31SBen Hutchings int rose_parse_facilities(unsigned char *p, unsigned packet_len,
3961da177e4SLinus Torvalds 	struct rose_facilities_struct *facilities)
3971da177e4SLinus Torvalds {
3981da177e4SLinus Torvalds 	int facilities_len, len;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 	facilities_len = *p++;
4011da177e4SLinus Torvalds 
40295c96174SEric Dumazet 	if (facilities_len == 0 || (unsigned int)facilities_len > packet_len)
4031da177e4SLinus Torvalds 		return 0;
4041da177e4SLinus Torvalds 
405e0bccd31SBen Hutchings 	while (facilities_len >= 3 && *p == 0x00) {
4061da177e4SLinus Torvalds 		facilities_len--;
4071da177e4SLinus Torvalds 		p++;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 		switch (*p) {
4101da177e4SLinus Torvalds 		case FAC_NATIONAL:		/* National */
4111da177e4SLinus Torvalds 			len = rose_parse_national(p + 1, facilities, facilities_len - 1);
4121da177e4SLinus Torvalds 			break;
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 		case FAC_CCITT:		/* CCITT */
4151da177e4SLinus Torvalds 			len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
4161da177e4SLinus Torvalds 			break;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 		default:
4191da177e4SLinus Torvalds 			printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p);
420e0bccd31SBen Hutchings 			len = 1;
4211da177e4SLinus Torvalds 			break;
4221da177e4SLinus Torvalds 		}
423e0bccd31SBen Hutchings 
424e0bccd31SBen Hutchings 		if (len < 0)
425e0bccd31SBen Hutchings 			return 0;
426e0bccd31SBen Hutchings 		if (WARN_ON(len >= facilities_len))
427e0bccd31SBen Hutchings 			return 0;
428e0bccd31SBen Hutchings 		facilities_len -= len + 1;
429e0bccd31SBen Hutchings 		p += len + 1;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 
432e0bccd31SBen Hutchings 	return facilities_len == 0;
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
rose_create_facilities(unsigned char * buffer,struct rose_sock * rose)4351da177e4SLinus Torvalds static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose)
4361da177e4SLinus Torvalds {
4371da177e4SLinus Torvalds 	unsigned char *p = buffer + 1;
4381da177e4SLinus Torvalds 	char *callsign;
439f75268cdSRalf Baechle 	char buf[11];
4401da177e4SLinus Torvalds 	int len, nb;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	/* National Facilities */
4431da177e4SLinus Torvalds 	if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
4441da177e4SLinus Torvalds 		*p++ = 0x00;
4451da177e4SLinus Torvalds 		*p++ = FAC_NATIONAL;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 		if (rose->rand != 0) {
4481da177e4SLinus Torvalds 			*p++ = FAC_NATIONAL_RAND;
4491da177e4SLinus Torvalds 			*p++ = (rose->rand >> 8) & 0xFF;
4501da177e4SLinus Torvalds 			*p++ = (rose->rand >> 0) & 0xFF;
4511da177e4SLinus Torvalds 		}
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 		/* Sent before older facilities */
4541da177e4SLinus Torvalds 		if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) {
4551da177e4SLinus Torvalds 			int maxdigi = 0;
4561da177e4SLinus Torvalds 			*p++ = FAC_NATIONAL_DIGIS;
4571da177e4SLinus Torvalds 			*p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis);
4581da177e4SLinus Torvalds 			for (nb = 0 ; nb < rose->source_ndigis ; nb++) {
4591da177e4SLinus Torvalds 				if (++maxdigi >= ROSE_MAX_DIGIS)
4601da177e4SLinus Torvalds 					break;
4611da177e4SLinus Torvalds 				memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN);
4621da177e4SLinus Torvalds 				p[6] |= AX25_HBIT;
4631da177e4SLinus Torvalds 				p += AX25_ADDR_LEN;
4641da177e4SLinus Torvalds 			}
4651da177e4SLinus Torvalds 			for (nb = 0 ; nb < rose->dest_ndigis ; nb++) {
4661da177e4SLinus Torvalds 				if (++maxdigi >= ROSE_MAX_DIGIS)
4671da177e4SLinus Torvalds 					break;
4681da177e4SLinus Torvalds 				memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN);
4691da177e4SLinus Torvalds 				p[6] &= ~AX25_HBIT;
4701da177e4SLinus Torvalds 				p += AX25_ADDR_LEN;
4711da177e4SLinus Torvalds 			}
4721da177e4SLinus Torvalds 		}
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 		/* For compatibility */
4751da177e4SLinus Torvalds 		if (rose->source_ndigis > 0) {
4761da177e4SLinus Torvalds 			*p++ = FAC_NATIONAL_SRC_DIGI;
4771da177e4SLinus Torvalds 			*p++ = AX25_ADDR_LEN;
4781da177e4SLinus Torvalds 			memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN);
4791da177e4SLinus Torvalds 			p   += AX25_ADDR_LEN;
4801da177e4SLinus Torvalds 		}
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 		/* For compatibility */
4831da177e4SLinus Torvalds 		if (rose->dest_ndigis > 0) {
4841da177e4SLinus Torvalds 			*p++ = FAC_NATIONAL_DEST_DIGI;
4851da177e4SLinus Torvalds 			*p++ = AX25_ADDR_LEN;
4861da177e4SLinus Torvalds 			memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN);
4871da177e4SLinus Torvalds 			p   += AX25_ADDR_LEN;
4881da177e4SLinus Torvalds 		}
4891da177e4SLinus Torvalds 	}
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	*p++ = 0x00;
4921da177e4SLinus Torvalds 	*p++ = FAC_CCITT;
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds 	*p++ = FAC_CCITT_DEST_NSAP;
4951da177e4SLinus Torvalds 
496f75268cdSRalf Baechle 	callsign = ax2asc(buf, &rose->dest_call);
4971da177e4SLinus Torvalds 
4981da177e4SLinus Torvalds 	*p++ = strlen(callsign) + 10;
4991da177e4SLinus Torvalds 	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
5021da177e4SLinus Torvalds 	*p++ = ROSE_ADDR_LEN * 2;
5031da177e4SLinus Torvalds 	memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN);
5041da177e4SLinus Torvalds 	p   += ROSE_ADDR_LEN;
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 	memcpy(p, callsign, strlen(callsign));
5071da177e4SLinus Torvalds 	p   += strlen(callsign);
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	*p++ = FAC_CCITT_SRC_NSAP;
5101da177e4SLinus Torvalds 
511f75268cdSRalf Baechle 	callsign = ax2asc(buf, &rose->source_call);
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	*p++ = strlen(callsign) + 10;
5141da177e4SLinus Torvalds 	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
5171da177e4SLinus Torvalds 	*p++ = ROSE_ADDR_LEN * 2;
5181da177e4SLinus Torvalds 	memcpy(p, &rose->source_addr, ROSE_ADDR_LEN);
5191da177e4SLinus Torvalds 	p   += ROSE_ADDR_LEN;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	memcpy(p, callsign, strlen(callsign));
5221da177e4SLinus Torvalds 	p   += strlen(callsign);
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	len       = p - buffer;
5251da177e4SLinus Torvalds 	buffer[0] = len - 1;
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds 	return len;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
rose_disconnect(struct sock * sk,int reason,int cause,int diagnostic)5301da177e4SLinus Torvalds void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic)
5311da177e4SLinus Torvalds {
5321da177e4SLinus Torvalds 	struct rose_sock *rose = rose_sk(sk);
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	rose_stop_timer(sk);
5351da177e4SLinus Torvalds 	rose_stop_idletimer(sk);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	rose_clear_queues(sk);
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	rose->lci   = 0;
5401da177e4SLinus Torvalds 	rose->state = ROSE_STATE_0;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	if (cause != -1)
5431da177e4SLinus Torvalds 		rose->cause = cause;
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	if (diagnostic != -1)
5461da177e4SLinus Torvalds 		rose->diagnostic = diagnostic;
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	sk->sk_state     = TCP_CLOSE;
5491da177e4SLinus Torvalds 	sk->sk_err       = reason;
5501da177e4SLinus Torvalds 	sk->sk_shutdown |= SEND_SHUTDOWN;
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 	if (!sock_flag(sk, SOCK_DEAD)) {
5531da177e4SLinus Torvalds 		sk->sk_state_change(sk);
5541da177e4SLinus Torvalds 		sock_set_flag(sk, SOCK_DEAD);
5551da177e4SLinus Torvalds 	}
5561da177e4SLinus Torvalds }
557