1/*
2 * ng_frame_relay.c
3 */
4
5/*-
6 * Copyright (c) 1996-1999 Whistle Communications, Inc.
7 * All rights reserved.
8 *
9 * Subject to the following obligations and disclaimer of warranty, use and
10 * redistribution of this software, in source or object code forms, with or
11 * without modifications are expressly permitted by Whistle Communications;
12 * provided, however, that:
13 * 1. Any and all reproductions of the source or object code must include the
14 *    copyright notice above and the following disclaimer of warranties; and
15 * 2. No rights are granted, in any manner or form, to use Whistle
16 *    Communications, Inc. trademarks, including the mark "WHISTLE
17 *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18 *    such appears in the above copyright notice or in the software.
19 *
20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36 * OF SUCH DAMAGE.
37 *
38 * Author: Julian Elischer <julian@freebsd.org>
39 * $Whistle: ng_frame_relay.c,v 1.20 1999/11/01 09:24:51 julian Exp $
40 */
41
42/*
43 * This node implements the frame relay protocol, not including
44 * the LMI line management. This means basically keeping track
45 * of which DLCI's are active, doing frame (de)multiplexing, etc.
46 *
47 * It has a 'downstream' hook that goes to the line, and a
48 * hook for each DLCI (eg, 'dlci16').
49 */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/kernel.h>
54#include <sys/errno.h>
55#include <sys/malloc.h>
56#include <sys/mbuf.h>
57#include <sys/syslog.h>
58#include <sys/ctype.h>
59
60#include <netgraph/ng_message.h>
61#include <netgraph/netgraph.h>
62#include <netgraph/ng_frame_relay.h>
63
64/*
65 * Line info, and status per channel.
66 */
67struct ctxinfo {		/* one per active hook */
68	u_int   flags;
69#define CHAN_VALID	0x01	/* assigned to a channel */
70#define CHAN_ACTIVE	0x02	/* bottom level active */
71	int     dlci;		/* the dlci assigned to this context */
72	hook_p  hook;		/* if there's a hook assigned.. */
73};
74
75#define MAX_CT 16		/* # of dlci's active at a time (POWER OF 2!) */
76struct frmrel_softc {
77	int     unit;		/* which card are we? */
78	int     datahooks;	/* number of data hooks attached */
79	node_p  node;		/* netgraph node */
80	int     addrlen;	/* address header length */
81	int     flags;		/* state */
82	int     mtu;		/* guess */
83	u_char  remote_seq;	/* sequence number the remote sent */
84	u_char  local_seq;	/* sequence number the remote rcvd */
85	u_short ALT[1024];	/* map DLCIs to CTX */
86#define	CTX_VALID	0x8000		/* this bit means it's a valid CTX */
87#define	CTX_VALUE	(MAX_CT - 1)	/* mask for context part */
88	struct	ctxinfo channel[MAX_CT];
89	struct	ctxinfo downstream;
90};
91typedef struct frmrel_softc *sc_p;
92
93#define BYTEX_EA	0x01	/* End Address. Always 0 on byte1 */
94#define BYTE1_C_R	0x02
95#define BYTE2_FECN	0x08	/* forwards congestion notification */
96#define BYTE2_BECN	0x04	/* Backward congestion notification */
97#define BYTE2_DE	0x02	/* Discard elligability */
98#define LASTBYTE_D_C	0x02	/* last byte is dl_core or dlci info */
99
100/* Used to do headers */
101const static struct segment {
102	u_char  mask;
103	u_char  shift;
104	u_char  width;
105} makeup[] = {
106	{ 0xfc, 2, 6 },
107	{ 0xf0, 4, 4 },
108	{ 0xfe, 1, 7 },
109	{ 0xfc, 2, 6 }
110};
111
112#define SHIFTIN(segment, byte, dlci) 					     \
113	{								     \
114		(dlci) <<= (segment)->width;				     \
115		(dlci) |=						     \
116			(((byte) & (segment)->mask) >> (segment)->shift);    \
117	}
118
119#define SHIFTOUT(segment, byte, dlci)					     \
120	{								     \
121		(byte) |= (((dlci) << (segment)->shift) & (segment)->mask);  \
122		(dlci) >>= (segment)->width;				     \
123	}
124
125/* Netgraph methods */
126static ng_constructor_t	ngfrm_constructor;
127static ng_shutdown_t	ngfrm_shutdown;
128static ng_newhook_t	ngfrm_newhook;
129static ng_rcvdata_t	ngfrm_rcvdata;
130static ng_disconnect_t	ngfrm_disconnect;
131
132/* Other internal functions */
133static int ngfrm_decode(node_p node, item_p item);
134static int ngfrm_addrlen(char *hdr);
135static int ngfrm_allocate_CTX(sc_p sc, int dlci);
136
137/* Netgraph type */
138static struct ng_type typestruct = {
139	.version =	NG_ABI_VERSION,
140	.name =		NG_FRAMERELAY_NODE_TYPE,
141	.constructor =	ngfrm_constructor,
142	.shutdown =	ngfrm_shutdown,
143	.newhook =	ngfrm_newhook,
144	.rcvdata =	ngfrm_rcvdata,
145	.disconnect =	ngfrm_disconnect,
146};
147NETGRAPH_INIT(framerelay, &typestruct);
148
149#define ERROUT(x)		do { error = (x); goto done; } while (0)
150
151/*
152 * Given a DLCI, return the index of the  context table entry for it,
153 * Allocating a new one if needs be, or -1 if none available.
154 */
155static int
156ngfrm_allocate_CTX(sc_p sc, int dlci)
157{
158	u_int   ctxnum = -1;	/* what ctx number we are using */
159	volatile struct ctxinfo *CTXp = NULL;
160
161	/* Sanity check the dlci value */
162	if (dlci > 1023)
163		return (-1);
164
165	/* Check to see if we already have an entry for this DLCI */
166	if (sc->ALT[dlci]) {
167		if ((ctxnum = sc->ALT[dlci] & CTX_VALUE) < MAX_CT) {
168			CTXp = sc->channel + ctxnum;
169		} else {
170			ctxnum = -1;
171			sc->ALT[dlci] = 0;	/* paranoid but... */
172		}
173	}
174
175	/*
176	 * If the index has no valid entry yet, then we need to allocate a
177	 * CTX number to it
178	 */
179	if (CTXp == NULL) {
180		for (ctxnum = 0; ctxnum < MAX_CT; ctxnum++) {
181			/*
182			 * If the VALID flag is empty it is unused
183			 */
184			if ((sc->channel[ctxnum].flags & CHAN_VALID) == 0) {
185				bzero(sc->channel + ctxnum,
186				      sizeof(struct ctxinfo));
187				CTXp = sc->channel + ctxnum;
188				sc->ALT[dlci] = ctxnum | CTX_VALID;
189				sc->channel[ctxnum].dlci = dlci;
190				sc->channel[ctxnum].flags = CHAN_VALID;
191				break;
192			}
193		}
194	}
195
196	/*
197	 * If we still don't have a CTX pointer, then we never found a free
198	 * spot so give up now..
199	 */
200	if (!CTXp) {
201		log(LOG_ERR, "No CTX available for dlci %d\n", dlci);
202		return (-1);
203	}
204	return (ctxnum);
205}
206
207/*
208 * Node constructor
209 */
210static int
211ngfrm_constructor(node_p node)
212{
213	sc_p sc;
214
215	sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
216	sc->addrlen = 2;	/* default */
217
218	/* Link the node and our private info */
219	NG_NODE_SET_PRIVATE(node, sc);
220	sc->node = node;
221	return (0);
222}
223
224/*
225 * Add a new hook
226 *
227 * We allow hooks called "debug", "downstream" and dlci[0-1023]
228 * The hook's private info points to our stash of info about that
229 * channel. A NULL pointer is debug and a DLCI of -1 means downstream.
230 */
231static int
232ngfrm_newhook(node_p node, hook_p hook, const char *name)
233{
234	const sc_p sc = NG_NODE_PRIVATE(node);
235	const char *cp;
236	char *eptr;
237	int dlci = 0;
238	int ctxnum;
239
240	/* Check if it's our friend the control hook */
241	if (strcmp(name, NG_FRAMERELAY_HOOK_DEBUG) == 0) {
242		NG_HOOK_SET_PRIVATE(hook, NULL);	/* paranoid */
243		return (0);
244	}
245
246	/*
247	 * All other hooks either start with 'dlci' and have a decimal
248	 * trailing channel number up to 4 digits, or are the downstream
249	 * hook.
250	 */
251	if (strncmp(name, NG_FRAMERELAY_HOOK_DLCI,
252	    strlen(NG_FRAMERELAY_HOOK_DLCI)) != 0) {
253		/* It must be the downstream connection */
254		if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0)
255			return EINVAL;
256
257		/* Make sure we haven't already got one (paranoid) */
258		if (sc->downstream.hook)
259			return (EADDRINUSE);
260
261		/* OK add it */
262		NG_HOOK_SET_PRIVATE(hook, &sc->downstream);
263		sc->downstream.hook = hook;
264		sc->downstream.dlci = -1;
265		sc->downstream.flags |= CHAN_ACTIVE;
266		sc->datahooks++;
267		return (0);
268	}
269
270	/* Must be a dlci hook at this point */
271	cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI);
272	if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
273		return (EINVAL);
274	dlci = (int)strtoul(cp, &eptr, 10);
275	if (*eptr != '\0' || dlci < 0 || dlci > 1023)
276		return (EINVAL);
277
278	/*
279	 * We have a dlci, now either find it, or allocate it. It's possible
280	 * that we might have seen packets for it already and made an entry
281	 * for it.
282	 */
283	ctxnum = ngfrm_allocate_CTX(sc, dlci);
284	if (ctxnum == -1)
285		return (ENOBUFS);
286
287	/*
288	 * Be paranoid: if it's got a hook already, that dlci is in use .
289	 * Generic code can not catch all the synonyms (e.g. dlci016 vs
290	 * dlci16)
291	 */
292	if (sc->channel[ctxnum].hook != NULL)
293		return (EADDRINUSE);
294
295	/*
296	 * Put our hooks into it (pun not intended)
297	 */
298	sc->channel[ctxnum].flags |= CHAN_ACTIVE;
299	NG_HOOK_SET_PRIVATE(hook, sc->channel + ctxnum);
300	sc->channel[ctxnum].hook = hook;
301	sc->datahooks++;
302	return (0);
303}
304
305/*
306 * Count up the size of the address header if we don't already know
307 */
308int
309ngfrm_addrlen(char *hdr)
310{
311	if (hdr[0] & BYTEX_EA)
312		return 0;
313	if (hdr[1] & BYTEX_EA)
314		return 2;
315	if (hdr[2] & BYTEX_EA)
316		return 3;
317	if (hdr[3] & BYTEX_EA)
318		return 4;
319	return 0;
320}
321
322/*
323 * Receive data packet
324 */
325static int
326ngfrm_rcvdata(hook_p hook, item_p item)
327{
328	struct	ctxinfo *const ctxp = NG_HOOK_PRIVATE(hook);
329	struct	mbuf *m = NULL;
330	int     error = 0;
331	int     dlci;
332	sc_p    sc;
333	int     alen;
334	char   *data;
335
336	/* Data doesn't come in from just anywhere (e.g debug hook) */
337	if (ctxp == NULL)
338		ERROUT(ENETDOWN);
339
340	/* If coming from downstream, decode it to a channel */
341	dlci = ctxp->dlci;
342	if (dlci == -1)
343		return (ngfrm_decode(NG_HOOK_NODE(hook), item));
344
345	NGI_GET_M(item, m);
346	/* Derive the softc we will need */
347	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
348
349	/* If there is no live channel, throw it away */
350	if ((sc->downstream.hook == NULL)
351	    || ((ctxp->flags & CHAN_ACTIVE) == 0))
352		ERROUT(ENETDOWN);
353
354	/* Store the DLCI on the front of the packet */
355	alen = sc->addrlen;
356	if (alen == 0)
357		alen = 2;	/* default value for transmit */
358	M_PREPEND(m, alen, M_NOWAIT);
359	if (m == NULL)
360		ERROUT(ENOBUFS);
361	data = mtod(m, char *);
362
363	/*
364	 * Shift the lowest bits into the address field until we are done.
365	 * First byte is MSBits of addr so work backwards.
366	 */
367	switch (alen) {
368	case 2:
369		data[0] = data[1] = '\0';
370		SHIFTOUT(makeup + 1, data[1], dlci);
371		SHIFTOUT(makeup + 0, data[0], dlci);
372		data[1] |= BYTEX_EA;
373		break;
374	case 3:
375		data[0] = data[1] = data[2] = '\0';
376		SHIFTOUT(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
377		SHIFTOUT(makeup + 1, data[1], dlci);
378		SHIFTOUT(makeup + 0, data[0], dlci);
379		data[2] |= BYTEX_EA;
380		break;
381	case 4:
382		data[0] = data[1] = data[2] = data[3] = '\0';
383		SHIFTOUT(makeup + 3, data[3], dlci);
384		SHIFTOUT(makeup + 2, data[2], dlci);
385		SHIFTOUT(makeup + 1, data[1], dlci);
386		SHIFTOUT(makeup + 0, data[0], dlci);
387		data[3] |= BYTEX_EA;
388		break;
389	default:
390		panic("%s", __func__);
391	}
392
393	/* Send it */
394	NG_FWD_NEW_DATA(error, item, sc->downstream.hook, m);
395	return (error);
396
397done:
398	NG_FREE_ITEM(item);
399	NG_FREE_M(m);
400	return (error);
401}
402
403/*
404 * Decode an incoming frame coming from the switch
405 */
406static int
407ngfrm_decode(node_p node, item_p item)
408{
409	const sc_p  sc = NG_NODE_PRIVATE(node);
410	char       *data;
411	int         alen;
412	u_int	    dlci = 0;
413	int	    error = 0;
414	int	    ctxnum;
415	struct mbuf *m;
416
417	NGI_GET_M(item, m);
418	if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL)
419		ERROUT(ENOBUFS);
420	data = mtod(m, char *);
421	if ((alen = sc->addrlen) == 0) {
422		sc->addrlen = alen = ngfrm_addrlen(data);
423	}
424	switch (alen) {
425	case 2:
426		SHIFTIN(makeup + 0, data[0], dlci);
427		SHIFTIN(makeup + 1, data[1], dlci);
428		break;
429	case 3:
430		SHIFTIN(makeup + 0, data[0], dlci);
431		SHIFTIN(makeup + 1, data[1], dlci);
432		SHIFTIN(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
433		break;
434	case 4:
435		SHIFTIN(makeup + 0, data[0], dlci);
436		SHIFTIN(makeup + 1, data[1], dlci);
437		SHIFTIN(makeup + 2, data[2], dlci);
438		SHIFTIN(makeup + 3, data[3], dlci);
439		break;
440	default:
441		ERROUT(EINVAL);
442	}
443
444	if (dlci > 1023)
445		ERROUT(EINVAL);
446	ctxnum = sc->ALT[dlci];
447	if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) {
448		/* Send it */
449		m_adj(m, alen);
450		NG_FWD_NEW_DATA(error, item, sc->channel[ctxnum].hook, m);
451		return (error);
452	} else {
453		error = ENETDOWN;
454	}
455done:
456	NG_FREE_ITEM(item);
457	NG_FREE_M(m);
458	return (error);
459}
460
461/*
462 * Shutdown node
463 */
464static int
465ngfrm_shutdown(node_p node)
466{
467	const sc_p sc = NG_NODE_PRIVATE(node);
468
469	NG_NODE_SET_PRIVATE(node, NULL);
470	free(sc, M_NETGRAPH);
471	NG_NODE_UNREF(node);
472	return (0);
473}
474
475/*
476 * Hook disconnection
477 *
478 * Invalidate the private data associated with this dlci.
479 * For this type, removal of the last link resets tries to destroy the node.
480 */
481static int
482ngfrm_disconnect(hook_p hook)
483{
484	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
485	struct ctxinfo *const cp = NG_HOOK_PRIVATE(hook);
486	int dlci;
487
488	/* If it's a regular dlci hook, then free resources etc.. */
489	if (cp != NULL) {
490		cp->hook = NULL;
491		dlci = cp->dlci;
492		if (dlci != -1)
493			sc->ALT[dlci] = 0;
494		cp->flags = 0;
495		sc->datahooks--;
496	}
497	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
498	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
499		ng_rmnode_self(NG_HOOK_NODE(hook));
500	return (0);
501}
502