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 *
40 * $FreeBSD$
41 * $Whistle: ng_frame_relay.c,v 1.20 1999/11/01 09:24:51 julian Exp $
42 */
43
44/*
45 * This node implements the frame relay protocol, not including
46 * the LMI line management. This means basically keeping track
47 * of which DLCI's are active, doing frame (de)multiplexing, etc.
48 *
49 * It has a 'downstream' hook that goes to the line, and a
50 * hook for each DLCI (eg, 'dlci16').
51 */
52
53#include <sys/param.h>
54#include <sys/systm.h>
55#include <sys/kernel.h>
56#include <sys/errno.h>
57#include <sys/malloc.h>
58#include <sys/mbuf.h>
59#include <sys/syslog.h>
60#include <sys/ctype.h>
61
62#include <netgraph/ng_message.h>
63#include <netgraph/netgraph.h>
64#include <netgraph/ng_frame_relay.h>
65
66/*
67 * Line info, and status per channel.
68 */
69struct ctxinfo {		/* one per active hook */
70	u_int   flags;
71#define CHAN_VALID	0x01	/* assigned to a channel */
72#define CHAN_ACTIVE	0x02	/* bottom level active */
73	int     dlci;		/* the dlci assigned to this context */
74	hook_p  hook;		/* if there's a hook assigned.. */
75};
76
77#define MAX_CT 16		/* # of dlci's active at a time (POWER OF 2!) */
78struct frmrel_softc {
79	int     unit;		/* which card are we? */
80	int     datahooks;	/* number of data hooks attached */
81	node_p  node;		/* netgraph node */
82	int     addrlen;	/* address header length */
83	int     flags;		/* state */
84	int     mtu;		/* guess */
85	u_char  remote_seq;	/* sequence number the remote sent */
86	u_char  local_seq;	/* sequence number the remote rcvd */
87	u_short ALT[1024];	/* map DLCIs to CTX */
88#define	CTX_VALID	0x8000		/* this bit means it's a valid CTX */
89#define	CTX_VALUE	(MAX_CT - 1)	/* mask for context part */
90	struct	ctxinfo channel[MAX_CT];
91	struct	ctxinfo downstream;
92};
93typedef struct frmrel_softc *sc_p;
94
95#define BYTEX_EA	0x01	/* End Address. Always 0 on byte1 */
96#define BYTE1_C_R	0x02
97#define BYTE2_FECN	0x08	/* forwards congestion notification */
98#define BYTE2_BECN	0x04	/* Backward congestion notification */
99#define BYTE2_DE	0x02	/* Discard elligability */
100#define LASTBYTE_D_C	0x02	/* last byte is dl_core or dlci info */
101
102/* Used to do headers */
103const static struct segment {
104	u_char  mask;
105	u_char  shift;
106	u_char  width;
107} makeup[] = {
108	{ 0xfc, 2, 6 },
109	{ 0xf0, 4, 4 },
110	{ 0xfe, 1, 7 },
111	{ 0xfc, 2, 6 }
112};
113
114#define SHIFTIN(segment, byte, dlci) 					     \
115	{								     \
116		(dlci) <<= (segment)->width;				     \
117		(dlci) |=						     \
118			(((byte) & (segment)->mask) >> (segment)->shift);    \
119	}
120
121#define SHIFTOUT(segment, byte, dlci)					     \
122	{								     \
123		(byte) |= (((dlci) << (segment)->shift) & (segment)->mask);  \
124		(dlci) >>= (segment)->width;				     \
125	}
126
127/* Netgraph methods */
128static ng_constructor_t	ngfrm_constructor;
129static ng_shutdown_t	ngfrm_shutdown;
130static ng_newhook_t	ngfrm_newhook;
131static ng_rcvdata_t	ngfrm_rcvdata;
132static ng_disconnect_t	ngfrm_disconnect;
133
134/* Other internal functions */
135static int ngfrm_decode(node_p node, item_p item);
136static int ngfrm_addrlen(char *hdr);
137static int ngfrm_allocate_CTX(sc_p sc, int dlci);
138
139/* Netgraph type */
140static struct ng_type typestruct = {
141	.version =	NG_ABI_VERSION,
142	.name =		NG_FRAMERELAY_NODE_TYPE,
143	.constructor =	ngfrm_constructor,
144	.shutdown =	ngfrm_shutdown,
145	.newhook =	ngfrm_newhook,
146	.rcvdata =	ngfrm_rcvdata,
147	.disconnect =	ngfrm_disconnect,
148};
149NETGRAPH_INIT(framerelay, &typestruct);
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
254		/* It must be the downstream connection */
255		if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0)
256			return EINVAL;
257
258		/* Make sure we haven't already got one (paranoid) */
259		if (sc->downstream.hook)
260			return (EADDRINUSE);
261
262		/* OK add it */
263		NG_HOOK_SET_PRIVATE(hook, &sc->downstream);
264		sc->downstream.hook = hook;
265		sc->downstream.dlci = -1;
266		sc->downstream.flags |= CHAN_ACTIVE;
267		sc->datahooks++;
268		return (0);
269	}
270
271	/* Must be a dlci hook at this point */
272	cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI);
273	if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
274		return (EINVAL);
275	dlci = (int)strtoul(cp, &eptr, 10);
276	if (*eptr != '\0' || dlci < 0 || dlci > 1023)
277		return (EINVAL);
278
279	/*
280	 * We have a dlci, now either find it, or allocate it. It's possible
281	 * that we might have seen packets for it already and made an entry
282	 * for it.
283	 */
284	ctxnum = ngfrm_allocate_CTX(sc, dlci);
285	if (ctxnum == -1)
286		return (ENOBUFS);
287
288	/*
289	 * Be paranoid: if it's got a hook already, that dlci is in use .
290	 * Generic code can not catch all the synonyms (e.g. dlci016 vs
291	 * dlci16)
292	 */
293	if (sc->channel[ctxnum].hook != NULL)
294		return (EADDRINUSE);
295
296	/*
297	 * Put our hooks into it (pun not intended)
298	 */
299	sc->channel[ctxnum].flags |= CHAN_ACTIVE;
300	NG_HOOK_SET_PRIVATE(hook, sc->channel + ctxnum);
301	sc->channel[ctxnum].hook = hook;
302	sc->datahooks++;
303	return (0);
304}
305
306/*
307 * Count up the size of the address header if we don't already know
308 */
309int
310ngfrm_addrlen(char *hdr)
311{
312	if (hdr[0] & BYTEX_EA)
313		return 0;
314	if (hdr[1] & BYTEX_EA)
315		return 2;
316	if (hdr[2] & BYTEX_EA)
317		return 3;
318	if (hdr[3] & BYTEX_EA)
319		return 4;
320	return 0;
321}
322
323/*
324 * Receive data packet
325 */
326static int
327ngfrm_rcvdata(hook_p hook, item_p item)
328{
329	struct	ctxinfo *const ctxp = NG_HOOK_PRIVATE(hook);
330	struct	mbuf *m = NULL;
331	int     error = 0;
332	int     dlci;
333	sc_p    sc;
334	int     alen;
335	char   *data;
336
337	/* Data doesn't come in from just anywhere (e.g debug hook) */
338	if (ctxp == NULL) {
339		error = ENETDOWN;
340		goto bad;
341	}
342
343	/* If coming from downstream, decode it to a channel */
344	dlci = ctxp->dlci;
345	if (dlci == -1)
346		return (ngfrm_decode(NG_HOOK_NODE(hook), item));
347
348	NGI_GET_M(item, m);
349	/* Derive the softc we will need */
350	sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
351
352	/* If there is no live channel, throw it away */
353	if ((sc->downstream.hook == NULL)
354	    || ((ctxp->flags & CHAN_ACTIVE) == 0)) {
355		error = ENETDOWN;
356		goto bad;
357	}
358
359	/* Store the DLCI on the front of the packet */
360	alen = sc->addrlen;
361	if (alen == 0)
362		alen = 2;	/* default value for transmit */
363	M_PREPEND(m, alen, M_NOWAIT);
364	if (m == NULL) {
365		error = ENOBUFS;
366		goto bad;
367	}
368	data = mtod(m, char *);
369
370	/*
371	 * Shift the lowest bits into the address field untill we are done.
372	 * First byte is MSBits of addr so work backwards.
373	 */
374	switch (alen) {
375	case 2:
376		data[0] = data[1] = '\0';
377		SHIFTOUT(makeup + 1, data[1], dlci);
378		SHIFTOUT(makeup + 0, data[0], dlci);
379		data[1] |= BYTEX_EA;
380		break;
381	case 3:
382		data[0] = data[1] = data[2] = '\0';
383		SHIFTOUT(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
384		SHIFTOUT(makeup + 1, data[1], dlci);
385		SHIFTOUT(makeup + 0, data[0], dlci);
386		data[2] |= BYTEX_EA;
387		break;
388	case 4:
389		data[0] = data[1] = data[2] = data[3] = '\0';
390		SHIFTOUT(makeup + 3, data[3], dlci);
391		SHIFTOUT(makeup + 2, data[2], dlci);
392		SHIFTOUT(makeup + 1, data[1], dlci);
393		SHIFTOUT(makeup + 0, data[0], dlci);
394		data[3] |= BYTEX_EA;
395		break;
396	default:
397		panic("%s", __func__);
398	}
399
400	/* Send it */
401	NG_FWD_NEW_DATA(error, item, sc->downstream.hook, m);
402	return (error);
403
404bad:
405	NG_FREE_ITEM(item);
406	NG_FREE_M(m);
407	return (error);
408}
409
410/*
411 * Decode an incoming frame coming from the switch
412 */
413static int
414ngfrm_decode(node_p node, item_p item)
415{
416	const sc_p  sc = NG_NODE_PRIVATE(node);
417	char       *data;
418	int         alen;
419	u_int	    dlci = 0;
420	int	    error = 0;
421	int	    ctxnum;
422	struct mbuf *m;
423
424	NGI_GET_M(item, m);
425	if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
426		error = ENOBUFS;
427		goto out;
428	}
429	data = mtod(m, char *);
430	if ((alen = sc->addrlen) == 0) {
431		sc->addrlen = alen = ngfrm_addrlen(data);
432	}
433	switch (alen) {
434	case 2:
435		SHIFTIN(makeup + 0, data[0], dlci);
436		SHIFTIN(makeup + 1, data[1], dlci);
437		break;
438	case 3:
439		SHIFTIN(makeup + 0, data[0], dlci);
440		SHIFTIN(makeup + 1, data[1], dlci);
441		SHIFTIN(makeup + 3, data[2], dlci);	/* 3 and 2 is correct */
442		break;
443	case 4:
444		SHIFTIN(makeup + 0, data[0], dlci);
445		SHIFTIN(makeup + 1, data[1], dlci);
446		SHIFTIN(makeup + 2, data[2], dlci);
447		SHIFTIN(makeup + 3, data[3], dlci);
448		break;
449	default:
450		error = EINVAL;
451		goto out;
452	}
453
454	if (dlci > 1023) {
455		error = EINVAL;
456		goto out;
457	}
458	ctxnum = sc->ALT[dlci];
459	if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) {
460		/* Send it */
461		m_adj(m, alen);
462		NG_FWD_NEW_DATA(error, item, sc->channel[ctxnum].hook, m);
463		return (error);
464	} else {
465		error = ENETDOWN;
466	}
467out:
468	NG_FREE_ITEM(item);
469	NG_FREE_M(m);
470	return (error);
471}
472
473/*
474 * Shutdown node
475 */
476static int
477ngfrm_shutdown(node_p node)
478{
479	const sc_p sc = NG_NODE_PRIVATE(node);
480
481	NG_NODE_SET_PRIVATE(node, NULL);
482	free(sc, M_NETGRAPH);
483	NG_NODE_UNREF(node);
484	return (0);
485}
486
487/*
488 * Hook disconnection
489 *
490 * Invalidate the private data associated with this dlci.
491 * For this type, removal of the last link resets tries to destroy the node.
492 */
493static int
494ngfrm_disconnect(hook_p hook)
495{
496	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
497	struct ctxinfo *const cp = NG_HOOK_PRIVATE(hook);
498	int dlci;
499
500	/* If it's a regular dlci hook, then free resources etc.. */
501	if (cp != NULL) {
502		cp->hook = NULL;
503		dlci = cp->dlci;
504		if (dlci != -1)
505			sc->ALT[dlci] = 0;
506		cp->flags = 0;
507		sc->datahooks--;
508	}
509	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
510	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
511		ng_rmnode_self(NG_HOOK_NODE(hook));
512	return (0);
513}
514