ng_atm.c revision 117157
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 *
29 * Netgraph module to connect NATM interfaces to netgraph.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/netgraph/atm/ng_atm.c 117157 2003-07-02 11:52:46Z harti $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/mbuf.h>
40#include <sys/errno.h>
41#include <sys/syslog.h>
42#include <sys/socket.h>
43#include <sys/socketvar.h>
44#include <sys/sbuf.h>
45#include <sys/ioccom.h>
46#include <sys/sysctl.h>
47
48#include <net/if.h>
49#include <net/if_types.h>
50#include <net/if_arp.h>
51#include <net/if_var.h>
52#include <net/if_media.h>
53#include <net/if_atm.h>
54
55#include <netgraph/ng_message.h>
56#include <netgraph/netgraph.h>
57#include <netgraph/ng_parse.h>
58#include <netgraph/atm/ng_atm.h>
59
60/*
61 * Hooks in the NATM code
62 */
63extern void	(*ng_atm_attach_p)(struct ifnet *);
64extern void	(*ng_atm_detach_p)(struct ifnet *);
65extern int	(*ng_atm_output_p)(struct ifnet *, struct mbuf **);
66extern void	(*ng_atm_input_p)(struct ifnet *, struct mbuf **,
67		    struct atm_pseudohdr *, void *);
68extern void	(*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
69		    struct atm_pseudohdr *, void *);
70extern void	(*ng_atm_message_p)(struct ifnet *, uint32_t, uint32_t);
71
72/*
73 * Sysctl stuff.
74 */
75SYSCTL_NODE(_net_graph, OID_AUTO, atm, CTLFLAG_RW, 0, "atm related stuff");
76
77#ifdef NGATM_DEBUG
78static int allow_shutdown;
79
80SYSCTL_INT(_net_graph_atm, OID_AUTO, allow_shutdown, CTLFLAG_RW,
81    &allow_shutdown, 0, "allow ng_atm nodes to shutdown");
82#endif
83
84/*
85 * Hook private data
86 */
87struct ngvcc {
88	uint16_t	vpi;	/* VPI of this hook */
89	uint16_t	vci;	/* VCI of this hook, 0 if none */
90	uint32_t	flags;	/* private flags */
91	hook_p		hook;	/* the connected hook */
92
93	LIST_ENTRY(ngvcc) link;
94};
95#define	VCC_OPEN	0x0001	/* open */
96
97/*
98 * Node private data
99 */
100struct priv {
101	struct ifnet	*ifp;		/* the ATM interface */
102	hook_p		input;		/* raw input hook */
103	hook_p		orphans;	/* packets to nowhere */
104	hook_p		output;		/* catch output packets */
105	hook_p		manage;		/* has also entry in vccs */
106	uint64_t	in_packets;
107	uint64_t	in_errors;
108	uint64_t	out_packets;
109	uint64_t	out_errors;
110
111	LIST_HEAD(, ngvcc) vccs;
112};
113
114/*
115 * Parse carrier state
116 */
117static const struct ng_parse_struct_field ng_atm_carrier_change_info[] =
118    NGM_ATM_CARRIER_CHANGE_INFO;
119static const struct ng_parse_type ng_atm_carrier_change_type = {
120	&ng_parse_struct_type,
121	&ng_atm_carrier_change_info
122};
123
124/*
125 * Parse the configuration structure ng_atm_config
126 */
127static const struct ng_parse_struct_field ng_atm_config_type_info[] =
128    NGM_ATM_CONFIG_INFO;
129
130static const struct ng_parse_type ng_atm_config_type = {
131	&ng_parse_struct_type,
132	&ng_atm_config_type_info
133};
134
135/*
136 * Parse a single vcc structure and a variable array of these ng_atm_vccs
137 */
138static const struct ng_parse_struct_field ng_atm_tparam_type_info[] =
139    NGM_ATM_TPARAM_INFO;
140static const struct ng_parse_type ng_atm_tparam_type = {
141	&ng_parse_struct_type,
142	&ng_atm_tparam_type_info
143};
144static const struct ng_parse_struct_field ng_atm_vcc_type_info[] =
145    NGM_ATM_VCC_INFO;
146static const struct ng_parse_type ng_atm_vcc_type = {
147	&ng_parse_struct_type,
148	&ng_atm_vcc_type_info
149};
150
151
152static int
153ng_atm_vccarray_getlen(const struct ng_parse_type *type,
154	const u_char *start, const u_char *buf)
155{
156	const struct atmio_vcctable *vp;
157
158	vp = (const struct atmio_vcctable *)
159	    (buf - offsetof(struct atmio_vcctable, vccs));
160
161	return (vp->count);
162}
163static const struct ng_parse_array_info ng_atm_vccarray_info =
164    NGM_ATM_VCCARRAY_INFO;
165static const struct ng_parse_type ng_atm_vccarray_type = {
166	&ng_parse_array_type,
167	&ng_atm_vccarray_info
168};
169
170
171static const struct ng_parse_struct_field ng_atm_vcctable_type_info[] =
172    NGM_ATM_VCCTABLE_INFO;
173
174static const struct ng_parse_type ng_atm_vcctable_type = {
175	&ng_parse_struct_type,
176	&ng_atm_vcctable_type_info
177};
178
179/*
180 * Parse CPCS INIT structure ng_atm_cpcs_init
181 */
182static const struct ng_parse_struct_field ng_atm_cpcs_init_type_info[] =
183    NGM_ATM_CPCS_INIT_INFO;
184
185static const struct ng_parse_type ng_atm_cpcs_init_type = {
186	&ng_parse_struct_type,
187	&ng_atm_cpcs_init_type_info
188};
189
190/*
191 * Parse CPCS TERM structure ng_atm_cpcs_term
192 */
193static const struct ng_parse_struct_field ng_atm_cpcs_term_type_info[] =
194    NGM_ATM_CPCS_TERM_INFO;
195
196static const struct ng_parse_type ng_atm_cpcs_term_type = {
197	&ng_parse_struct_type,
198	&ng_atm_cpcs_term_type_info
199};
200
201/*
202 * Parse statistic struct
203 */
204static const struct ng_parse_struct_field ng_atm_stats_type_info[] =
205    NGM_ATM_STATS_INFO;
206
207static const struct ng_parse_type ng_atm_stats_type = {
208	&ng_parse_struct_type,
209	&ng_atm_stats_type_info
210};
211
212static const struct ng_cmdlist ng_atm_cmdlist[] = {
213	{
214	  NGM_ATM_COOKIE,
215	  NGM_ATM_GET_IFNAME,
216	  "getifname",
217	  NULL,
218	  &ng_parse_string_type
219	},
220	{
221	  NGM_ATM_COOKIE,
222	  NGM_ATM_GET_CONFIG,
223	  "getconfig",
224	  NULL,
225	  &ng_atm_config_type
226	},
227	{
228	  NGM_ATM_COOKIE,
229	  NGM_ATM_GET_VCCS,
230	  "getvccs",
231	  NULL,
232	  &ng_atm_vcctable_type
233	},
234	{
235	  NGM_ATM_COOKIE,
236	  NGM_ATM_CPCS_INIT,
237	  "cpcsinit",
238	  &ng_atm_cpcs_init_type,
239	  NULL
240	},
241	{
242	  NGM_ATM_COOKIE,
243	  NGM_ATM_CPCS_TERM,
244	  "cpcsterm",
245	  &ng_atm_cpcs_term_type,
246	  NULL
247	},
248	{
249	  NGM_ATM_COOKIE,
250	  NGM_ATM_CARRIER_CHANGE,
251	  "carrier",
252	  &ng_atm_carrier_change_type,
253	  &ng_atm_carrier_change_type,
254	},
255	{
256	  NGM_ATM_COOKIE,
257	  NGM_ATM_GET_VCC,
258	  "getvcc",
259	  &ng_parse_hookbuf_type,
260	  &ng_atm_vcc_type
261	},
262	{
263	  NGM_ATM_COOKIE,
264	  NGM_ATM_GET_VCCID,
265	  "getvccid",
266	  &ng_atm_vcc_type,
267	  &ng_atm_vcc_type
268	},
269	{
270	  NGM_ATM_COOKIE,
271	  NGM_ATM_GET_STATS,
272	  "getstats",
273	  NULL,
274	  &ng_atm_stats_type
275	},
276	{ 0 }
277};
278
279static int ng_atm_mod_event(module_t, int, void *);
280
281static ng_constructor_t ng_atm_constructor;
282static ng_shutdown_t	ng_atm_shutdown;
283static ng_rcvmsg_t	ng_atm_rcvmsg;
284static ng_newhook_t	ng_atm_newhook;
285static ng_connect_t	ng_atm_connect;
286static ng_disconnect_t	ng_atm_disconnect;
287static ng_rcvdata_t	ng_atm_rcvdata;
288static ng_rcvdata_t	ng_atm_rcvdrop;
289
290static struct ng_type ng_atm_typestruct = {
291	NG_ABI_VERSION,
292	NG_ATM_NODE_TYPE,
293	ng_atm_mod_event,	/* Module event handler (optional) */
294	ng_atm_constructor,	/* Node constructor */
295	ng_atm_rcvmsg,		/* control messages come here */
296	ng_atm_shutdown,	/* reset, and free resources */
297	ng_atm_newhook,		/* first notification of new hook */
298	NULL,			/* findhook */
299	ng_atm_connect,		/* connect */
300	ng_atm_rcvdata,		/* rcvdata */
301	ng_atm_disconnect,	/* notify on disconnect */
302	ng_atm_cmdlist,
303};
304NETGRAPH_INIT(atm, &ng_atm_typestruct);
305
306static const struct {
307	u_int	media;
308	const char *name;
309} atmmedia[] = IFM_SUBTYPE_ATM_DESCRIPTIONS;
310
311
312#define	IFP2NG(IFP)	((node_p)((struct ifatm *)(IFP))->ngpriv)
313
314#define	IFFLAGS "\020\001UP\002BROADCAST\003DEBUG\004LOOPBACK" \
315		 "\005POINTOPOINT\006SMART\007RUNNING\010NOARP" \
316		 "\011PROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX" \
317		 "\015LINK0\016LINK1\017LINK2\020MULTICAST"
318
319
320/************************************************************/
321/*
322 * INPUT
323 */
324/*
325 * A packet is received from an interface.
326 * If we have an input hook, prepend the pseudoheader to the data and
327 * deliver it out to that hook. If not, look whether it is destined for
328 * use. If so locate the appropriate hook, deliver the packet without the
329 * header and we are done. If it is not for us, leave it alone.
330 */
331static void
332ng_atm_input(struct ifnet *ifp, struct mbuf **mp,
333	struct atm_pseudohdr *ah, void *rxhand)
334{
335	node_p node = IFP2NG(ifp);
336	struct priv *priv;
337	const struct ngvcc *vcc;
338	int error;
339
340	if (node == NULL)
341		return;
342	priv = NG_NODE_PRIVATE(node);
343	if (priv->input != NULL) {
344		/*
345		 * Prepend the atm_pseudoheader.
346		 */
347		M_PREPEND(*mp, sizeof(*ah), M_DONTWAIT);
348		if (*mp == NULL)
349			return;
350		memcpy(mtod(*mp, struct atm_pseudohdr *), ah, sizeof(*ah));
351		NG_SEND_DATA_ONLY(error, priv->input, *mp);
352		if (error == 0) {
353			priv->in_packets++;
354			*mp = NULL;
355		} else {
356#ifdef NGATM_DEBUG
357			printf("%s: error=%d\n", __func__, error);
358#endif
359			priv->in_errors++;
360		}
361		return;
362	}
363	if ((ATM_PH_FLAGS(ah) & ATMIO_FLAG_NG) == 0)
364		return;
365
366	vcc = (struct ngvcc *)rxhand;
367
368	NG_SEND_DATA_ONLY(error, vcc->hook, *mp);
369	if (error == 0) {
370		priv->in_packets++;
371		*mp = NULL;
372	} else {
373#ifdef NGATM_DEBUG
374		printf("%s: error=%d\n", __func__, error);
375#endif
376		priv->in_errors++;
377	}
378}
379
380/*
381 * ATM packet is about to be output. The atm_pseudohdr is already prepended.
382 * If the hook is set, reroute the packet to the hook.
383 */
384static int
385ng_atm_output(struct ifnet *ifp, struct mbuf **mp)
386{
387	const node_p node = IFP2NG(ifp);
388	const struct priv *priv;
389	int error = 0;
390
391	if (node == NULL)
392		return (0);
393	priv = NG_NODE_PRIVATE(node);
394	if (priv->output) {
395		NG_SEND_DATA_ONLY(error, priv->output, *mp);
396		*mp = NULL;
397	}
398
399	return (error);
400}
401
402/*
403 * Well, this doesn't make much sense for ATM.
404 */
405static void
406ng_atm_input_orphans(struct ifnet *ifp, struct mbuf *m,
407	struct atm_pseudohdr *ah, void *rxhand)
408{
409	node_p node = IFP2NG(ifp);
410	struct priv *priv;
411	int error;
412
413	if (node == NULL) {
414		m_freem(m);
415		return;
416	}
417	priv = NG_NODE_PRIVATE(node);
418	if (priv->orphans == NULL) {
419		m_freem(m);
420		return;
421	}
422	/*
423	 * Prepend the atm_pseudoheader.
424	 */
425	M_PREPEND(m, sizeof(*ah), M_DONTWAIT);
426	if (m == NULL)
427		return;
428	memcpy(mtod(m, struct atm_pseudohdr *), ah, sizeof(*ah));
429	NG_SEND_DATA_ONLY(error, priv->orphans, m);
430	if (error == 0)
431		priv->in_packets++;
432	else {
433		priv->in_errors++;
434#ifdef NGATM_DEBUG
435		printf("%s: error=%d\n", __func__, error);
436#endif
437	}
438}
439
440/************************************************************/
441/*
442 * OUTPUT
443 */
444static int
445ng_atm_rcvdata(hook_p hook, item_p item)
446{
447	node_p node = NG_HOOK_NODE(hook);
448	struct priv *priv = NG_NODE_PRIVATE(node);
449	const struct ngvcc *vcc = NG_HOOK_PRIVATE(hook);
450	struct mbuf *m;
451	struct atm_pseudohdr *aph;
452	int error;
453
454	if (vcc->vci == 0) {
455		NG_FREE_ITEM(item);
456		return (ENOTCONN);
457	}
458
459	NGI_GET_M(item, m);
460	NG_FREE_ITEM(item);
461
462	/*
463	 * Prepend pseudo-hdr. Drivers don't care about the flags.
464	 */
465	M_PREPEND(m, sizeof(*aph), M_DONTWAIT);
466	if (m == NULL) {
467		NG_FREE_M(m);
468		return (ENOMEM);
469	}
470	aph = mtod(m, struct atm_pseudohdr *);
471	ATM_PH_VPI(aph) = vcc->vpi;
472	ATM_PH_SETVCI(aph, vcc->vci);
473	ATM_PH_FLAGS(aph) = 0;
474
475	if ((error = atm_output(priv->ifp, m, NULL, NULL)) == 0)
476		priv->out_packets++;
477	else
478		priv->out_errors++;
479	return (error);
480}
481
482static int
483ng_atm_rcvdrop(hook_p hook, item_p item)
484{
485	NG_FREE_ITEM(item);
486	return (0);
487}
488
489
490/************************************************************
491 *
492 * Message from driver.
493 */
494#ifdef notyet
495static void
496ng_atm_message_func(node_p node, hook_p hook, void *arg1, int arg2)
497{
498	uint32_t msg = (uintptr_t)arg1;
499	uint32_t arg = (uint32_t)arg2;
500	const struct priv *priv = NG_NODE_PRIVATE(node);
501	struct ngvcc *vcc;
502	u_int vci, vpi, state;
503	struct ng_mesg *mesg;
504	int error;
505
506	switch (msg) {
507
508	  case ATM_MSG_FLOW_CONTROL:
509	    {
510		struct ngm_queue_state *qstate;
511
512		/* find the connection */
513		vci = arg & 0xffff;
514		vpi = (arg >> 16) & 0xff;
515		state = (arg >> 24) & 1;
516		LIST_FOREACH(vcc, &priv->vccs, link)
517			if (vcc->vci == vci && vcc->vpi == vpi)
518				break;
519		if (vcc == NULL)
520			break;
521
522		/* convert into a flow control message */
523		NG_MKMESSAGE(mesg, NGM_FLOW_COOKIE,
524		    state ? NGM_HIGH_WATER_PASSED : NGM_LOW_WATER_PASSED,
525		    sizeof(struct ngm_queue_state), M_NOWAIT);
526		if (mesg == NULL)
527			break;
528		qstate = (struct ngm_queue_state *)mesg->data;
529
530		/* XXX have to figure out how to get that info */
531
532		NG_SEND_MSG_HOOK(error, node, mesg, vcc->hook, NULL);
533		break;
534	    }
535
536	  case ATM_MSG_VCC_CHANGED:
537	    {
538		struct ngm_atm_vcc_change *chg;
539
540		if (priv->manage == NULL)
541			break;
542		NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_VCC_CHANGE,
543		    sizeof(struct ngm_atm_vcc_change), M_NOWAIT);
544		if (mesg == NULL)
545			break;
546		chg = (struct ngm_atm_vcc_change *)mesg->data;
547		chg->vci = arg & 0xffff;
548		chg->vpi = (arg >> 16) & 0xff;
549		chg->state = (arg >> 24) & 1;
550		chg->node = NG_NODE_ID(node);
551		NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, NULL);
552		break;
553	    }
554
555	  case ATM_MSG_CARRIER_CHANGE:
556	    {
557		struct ngm_atm_carrier_change *chg;
558
559		if (priv->manage == NULL)
560			break;
561		NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_CARRIER_CHANGE,
562		    sizeof(struct ngm_atm_carrier_change), M_NOWAIT);
563		if (mesg == NULL)
564			break;
565		chg = (struct ngm_atm_carrier_change *)mesg->data;
566		chg->state = arg & 1;
567		chg->node = NG_NODE_ID(node);
568		NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, NULL);
569		break;
570	    }
571	}
572}
573#endif
574
575/*
576 * Use send_fn to get the right lock
577 */
578static void
579ng_atm_message(struct ifnet *ifp, uint32_t msg, uint32_t arg)
580{
581#ifdef notyet
582	const node_p node = IFP2NG(ifp);
583
584	(void)ng_send_fn(node, NULL, ng_atm_message_func,
585	    (void *)(uintptr_t)msg, arg);
586#endif
587}
588
589/************************************************************
590 *
591 * CPCS
592 */
593/*
594 * Open a channel for the user
595 */
596static int
597ng_atm_cpcs_init(node_p node, const struct ngm_atm_cpcs_init *arg)
598{
599	struct priv *priv = NG_NODE_PRIVATE(node);
600	const struct ifatm_mib *mib;
601	struct ngvcc *vcc;
602	struct atmio_openvcc data;
603	int err;
604
605	if(priv->ifp->if_ioctl == NULL)
606		return (ENXIO);
607
608	mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib);
609
610	LIST_FOREACH(vcc, &priv->vccs, link)
611		if (strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0)
612			break;
613	if (vcc == NULL)
614		return (ENOTCONN);
615	if (vcc->vci != 0)
616		return (EISCONN);
617
618	/*
619	 * Check user arguments and construct ioctl argument
620	 */
621	memset(&data, 0, sizeof(data));
622
623	data.rxhand = vcc;
624
625	switch (data.param.aal = arg->aal) {
626
627	  case ATMIO_AAL_34:
628	  case ATMIO_AAL_5:
629	  case ATMIO_AAL_0:
630	  case ATMIO_AAL_RAW:
631		break;
632
633	  default:
634		return (EINVAL);
635	}
636
637	if (arg->vpi > 0xff)
638		return (EINVAL);
639	data.param.vpi = arg->vpi;
640
641	if (arg->vci > 0xffff)
642		return (EINVAL);
643	/* allow 0.0 as catch all receive channel */
644	if (arg->vci == 0 && (arg->vpi != 0 || !(arg->flags & ATMIO_FLAG_NOTX)))
645		return (EINVAL);
646	data.param.vci = arg->vci;
647
648	data.param.tparam.pcr = arg->pcr;
649
650	if (arg->mcr > arg->pcr)
651		return (EINVAL);
652	data.param.tparam.mcr = arg->mcr;
653
654	if (!(arg->flags & ATMIO_FLAG_NOTX)) {
655		if (arg->tmtu == 0)
656			data.param.tmtu = priv->ifp->if_mtu;
657		else {
658			if (arg->tmtu > (1 << 16))
659				return (EINVAL);
660			data.param.tmtu = arg->tmtu;
661		}
662	}
663	if (!(arg->flags & ATMIO_FLAG_NORX)) {
664		if (arg->rmtu == 0)
665			data.param.rmtu = priv->ifp->if_mtu;
666		else {
667			if (arg->rmtu > (1 << 16))
668				return (EINVAL);
669			data.param.rmtu = arg->rmtu;
670		}
671	}
672
673	switch (data.param.traffic = arg->traffic) {
674
675	  case ATMIO_TRAFFIC_UBR:
676	  case ATMIO_TRAFFIC_CBR:
677		break;
678
679	  case ATMIO_TRAFFIC_VBR:
680		if (arg->scr > arg->pcr)
681			return (EINVAL);
682		data.param.tparam.scr = arg->scr;
683
684		if (arg->mbs > (1 << 24))
685			return (EINVAL);
686		data.param.tparam.mbs = arg->mbs;
687		break;
688
689	  case ATMIO_TRAFFIC_ABR:
690		if (arg->icr > arg->pcr || arg->icr < arg->mcr)
691			return (EINVAL);
692		data.param.tparam.icr = arg->icr;
693
694		if (arg->tbe == 0 || arg->tbe > (1 << 24))
695			return (EINVAL);
696		data.param.tparam.tbe = arg->tbe;
697
698		if (arg->nrm > 0x7)
699			return (EINVAL);
700		data.param.tparam.nrm = arg->nrm;
701
702		if (arg->trm > 0x7)
703			return (EINVAL);
704		data.param.tparam.trm = arg->trm;
705
706		if (arg->adtf > 0x3ff)
707			return (EINVAL);
708		data.param.tparam.adtf = arg->adtf;
709
710		if (arg->rif > 0xf)
711			return (EINVAL);
712		data.param.tparam.rif = arg->rif;
713
714		if (arg->rdf > 0xf)
715			return (EINVAL);
716		data.param.tparam.rdf = arg->rdf;
717
718		if (arg->cdf > 0x7)
719			return (EINVAL);
720		data.param.tparam.cdf = arg->cdf;
721
722		break;
723
724	  default:
725		return (EINVAL);
726	}
727
728	if ((arg->flags & ATMIO_FLAG_NORX) && (arg->flags & ATMIO_FLAG_NOTX))
729		return (EINVAL);
730
731	data.param.flags = arg->flags & ~(ATM_PH_AAL5 | ATM_PH_LLCSNAP);
732	data.param.flags |= ATMIO_FLAG_NG;
733
734	err = (*priv->ifp->if_ioctl)(priv->ifp, SIOCATMOPENVCC, (caddr_t)&data);
735
736	if (err == 0) {
737		vcc->vci = data.param.vci;
738		vcc->vpi = data.param.vpi;
739		vcc->flags = VCC_OPEN;
740	}
741
742	return (err);
743}
744
745/*
746 * Issue the close command to the driver
747 */
748static int
749cpcs_term(const struct priv *priv, u_int vpi, u_int vci)
750{
751	struct atmio_closevcc data;
752
753	if (priv->ifp->if_ioctl == NULL)
754		return ENXIO;
755
756	data.vpi = vpi;
757	data.vci = vci;
758
759	return ((*priv->ifp->if_ioctl)(priv->ifp,
760	    SIOCATMCLOSEVCC, (caddr_t)&data));
761}
762
763
764/*
765 * Close a channel by request of the user
766 */
767static int
768ng_atm_cpcs_term(node_p node, const struct ngm_atm_cpcs_term *arg)
769{
770	struct priv *priv = NG_NODE_PRIVATE(node);
771	struct ngvcc *vcc;
772	int error;
773
774	LIST_FOREACH(vcc, &priv->vccs, link)
775		if(strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0)
776			break;
777	if (vcc == NULL)
778		return (ENOTCONN);
779	if (vcc->vci == 0)
780		return (ENOTCONN);
781
782	error = cpcs_term(priv, vcc->vpi, vcc->vci);
783
784	vcc->vci = 0;
785	vcc->vpi = 0;
786	vcc->flags = 0;
787
788	return (error);
789}
790
791/************************************************************/
792/*
793 * CONTROL MESSAGES
794 */
795
796/*
797 * Produce a textual description of the current status
798 */
799static int
800text_status(node_p node, char *arg, u_int len)
801{
802	const struct priv *priv = NG_NODE_PRIVATE(node);
803	const struct ifatm_mib *mib;
804	struct sbuf sbuf;
805	u_int i;
806
807	static const struct {
808		const char	*name;
809		const char	*vendor;
810	} devices[] = {
811		ATM_DEVICE_NAMES
812	};
813
814	mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib);
815
816	sbuf_new(&sbuf, arg, len, SBUF_FIXEDLEN);
817	sbuf_printf(&sbuf, "interface: %s%d\n", priv->ifp->if_name,
818	    priv->ifp->if_unit);
819
820	if (mib->device >= sizeof(devices) / sizeof(devices[0]))
821		sbuf_printf(&sbuf, "device=unknown\nvendor=unknown\n");
822	else
823		sbuf_printf(&sbuf, "device=%s\nvendor=%s\n",
824		    devices[mib->device].name, devices[mib->device].vendor);
825
826	for (i = 0; atmmedia[i].name; i++)
827		if(mib->media == atmmedia[i].media) {
828			sbuf_printf(&sbuf, "media=%s\n", atmmedia[i].name);
829			break;
830		}
831	if(atmmedia[i].name == NULL)
832		sbuf_printf(&sbuf, "media=unknown\n");
833
834	sbuf_printf(&sbuf, "serial=%u esi=%6D hardware=%u software=%u\n",
835	    mib->serial, mib->esi, ":", mib->hw_version, mib->sw_version);
836	sbuf_printf(&sbuf, "pcr=%u vpi_bits=%u vci_bits=%u max_vpcs=%u "
837	    "max_vccs=%u\n", mib->pcr, mib->vpi_bits, mib->vci_bits,
838	    mib->max_vpcs, mib->max_vccs);
839	sbuf_printf(&sbuf, "ifflags=%b\n", priv->ifp->if_flags, IFFLAGS);
840
841	sbuf_finish(&sbuf);
842
843	return (sbuf_len(&sbuf));
844}
845
846/*
847 * Get control message
848 */
849static int
850ng_atm_rcvmsg(node_p node, item_p item, hook_p lasthook)
851{
852	const struct priv *priv = NG_NODE_PRIVATE(node);
853	struct ng_mesg *resp = NULL;
854	struct ng_mesg *msg;
855	struct ifatm_mib *mib = (struct ifatm_mib *)(priv->ifp->if_linkmib);
856	int error = 0;
857
858	NGI_GET_MSG(item, msg);
859
860	switch (msg->header.typecookie) {
861
862	  case NGM_GENERIC_COOKIE:
863		switch (msg->header.cmd) {
864
865		  case NGM_TEXT_STATUS:
866			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
867			if(resp == NULL) {
868				error = ENOMEM;
869				break;
870			}
871
872			resp->header.arglen = text_status(node,
873			    (char *)resp->data, resp->header.arglen) + 1;
874			break;
875
876		  default:
877			error = EINVAL;
878			break;
879		}
880		break;
881
882	  case NGM_ATM_COOKIE:
883		switch (msg->header.cmd) {
884
885		  case NGM_ATM_GET_IFNAME:
886			NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT);
887			if (resp == NULL) {
888				error = ENOMEM;
889				break;
890			}
891			snprintf(resp->data, IFNAMSIZ + 1, "%s%d",
892			    priv->ifp->if_name, priv->ifp->if_unit);
893			break;
894
895		  case NGM_ATM_GET_CONFIG:
896		    {
897			struct ngm_atm_config *config;
898
899			NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
900			if (resp == NULL) {
901				error = ENOMEM;
902				break;
903			}
904			config = (struct ngm_atm_config *)resp->data;
905			config->pcr = mib->pcr;
906			config->vpi_bits = mib->vpi_bits;
907			config->vci_bits = mib->vci_bits;
908			config->max_vpcs = mib->max_vpcs;
909			config->max_vccs = mib->max_vccs;
910			break;
911		    }
912
913		  case NGM_ATM_GET_VCCS:
914		    {
915			struct atmio_vcctable *vccs;
916			size_t len;
917
918			if (priv->ifp->if_ioctl == NULL) {
919				error = ENXIO;
920				break;
921			}
922			error = (*priv->ifp->if_ioctl)(priv->ifp,
923			    SIOCATMGETVCCS, (caddr_t)&vccs);
924			if (error)
925				break;
926
927			len = sizeof(*vccs) +
928			    vccs->count * sizeof(vccs->vccs[0]);
929			NG_MKRESPONSE(resp, msg, len, M_NOWAIT);
930			if (resp == NULL) {
931				error = ENOMEM;
932				free(vccs, M_DEVBUF);
933				break;
934			}
935
936			(void)memcpy(resp->data, vccs, len);
937			free(vccs, M_DEVBUF);
938
939			break;
940		    }
941
942		  case NGM_ATM_GET_VCC:
943		    {
944			char hook[NG_HOOKLEN + 1];
945			struct atmio_vcctable *vccs;
946			struct ngvcc *vcc;
947			u_int i;
948
949			if (priv->ifp->if_ioctl == NULL) {
950				error = ENXIO;
951				break;
952			}
953			if (msg->header.arglen != NG_HOOKLEN + 1) {
954				error = EINVAL;
955				break;
956			}
957			strncpy(hook, msg->data, NG_HOOKLEN + 1);
958			hook[NG_HOOKLEN] = '\0';
959			LIST_FOREACH(vcc, &priv->vccs, link)
960				if (strcmp(NG_HOOK_NAME(vcc->hook), hook) == 0)
961					break;
962			if (vcc == NULL) {
963				error = ENOTCONN;
964				break;
965			}
966			error = (*priv->ifp->if_ioctl)(priv->ifp,
967			    SIOCATMGETVCCS, (caddr_t)&vccs);
968			if (error)
969				break;
970
971			for (i = 0; i < vccs->count; i++)
972				if (vccs->vccs[i].vpi == vcc->vpi &&
973				    vccs->vccs[i].vci == vcc->vci)
974					break;
975			if (i == vccs->count) {
976				error = ENOTCONN;
977				free(vccs, M_DEVBUF);
978				break;
979			}
980
981			NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]),
982			    M_NOWAIT);
983			if (resp == NULL) {
984				error = ENOMEM;
985				free(vccs, M_DEVBUF);
986				break;
987			}
988
989			*(struct atmio_vcc *)resp->data = vccs->vccs[i];
990			free(vccs, M_DEVBUF);
991			break;
992		    }
993
994		  case NGM_ATM_GET_VCCID:
995		    {
996			struct atmio_vcc *arg;
997			struct atmio_vcctable *vccs;
998			u_int i;
999
1000			if (priv->ifp->if_ioctl == NULL) {
1001				error = ENXIO;
1002				break;
1003			}
1004			if (msg->header.arglen != sizeof(*arg)) {
1005				error = EINVAL;
1006				break;
1007			}
1008			arg = (struct atmio_vcc *)msg->data;
1009
1010			error = (*priv->ifp->if_ioctl)(priv->ifp,
1011			    SIOCATMGETVCCS, (caddr_t)&vccs);
1012			if (error)
1013				break;
1014
1015			for (i = 0; i < vccs->count; i++)
1016				if (vccs->vccs[i].vpi == arg->vpi &&
1017				    vccs->vccs[i].vci == arg->vci)
1018					break;
1019			if (i == vccs->count) {
1020				error = ENOTCONN;
1021				free(vccs, M_DEVBUF);
1022				break;
1023			}
1024
1025			NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]),
1026			    M_NOWAIT);
1027			if (resp == NULL) {
1028				error = ENOMEM;
1029				free(vccs, M_DEVBUF);
1030				break;
1031			}
1032
1033			*(struct atmio_vcc *)resp->data = vccs->vccs[i];
1034			free(vccs, M_DEVBUF);
1035			break;
1036		    }
1037
1038		  case NGM_ATM_CPCS_INIT:
1039			if (msg->header.arglen !=
1040			    sizeof(struct ngm_atm_cpcs_init)) {
1041				error = EINVAL;
1042				break;
1043			}
1044			error = ng_atm_cpcs_init(node,
1045			    (struct ngm_atm_cpcs_init *)msg->data);
1046			break;
1047
1048		  case NGM_ATM_CPCS_TERM:
1049			if (msg->header.arglen !=
1050			    sizeof(struct ngm_atm_cpcs_term)) {
1051				error = EINVAL;
1052				break;
1053			}
1054			error = ng_atm_cpcs_term(node,
1055			    (struct ngm_atm_cpcs_term *)msg->data);
1056			break;
1057
1058		  case NGM_ATM_GET_STATS:
1059		    {
1060			struct ngm_atm_stats *p;
1061
1062			if (msg->header.arglen != 0) {
1063				error = EINVAL;
1064				break;
1065			}
1066			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
1067			if (resp == NULL) {
1068				error = ENOMEM;
1069				break;
1070			}
1071			p = (struct ngm_atm_stats *)resp->data;
1072			p->in_packets = priv->in_packets;
1073			p->out_packets = priv->out_packets;
1074			p->in_errors = priv->in_errors;
1075			p->out_errors = priv->out_errors;
1076
1077			break;
1078		    }
1079
1080		  default:
1081			error = EINVAL;
1082			break;
1083		}
1084		break;
1085
1086	  default:
1087		error = EINVAL;
1088		break;
1089	}
1090
1091	NG_RESPOND_MSG(error, node, item, resp);
1092	NG_FREE_MSG(msg);
1093	return (error);
1094}
1095
1096/************************************************************/
1097/*
1098 * HOOK MANAGEMENT
1099 */
1100
1101/*
1102 * A new hook is create that will be connected to the node.
1103 * Check, whether the name is one of the predefined ones.
1104 * If not, create a new entry into the vcc list.
1105 */
1106static int
1107ng_atm_newhook(node_p node, hook_p hook, const char *name)
1108{
1109	struct priv *priv = NG_NODE_PRIVATE(node);
1110	struct ngvcc *vcc;
1111
1112	if (strcmp(name, "input") == 0) {
1113		priv->input = hook;
1114		NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
1115		return (0);
1116	}
1117	if (strcmp(name, "output") == 0) {
1118		priv->output = hook;
1119		NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
1120		return (0);
1121	}
1122	if (strcmp(name, "orphans") == 0) {
1123		priv->orphans = hook;
1124		NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop);
1125		return (0);
1126	}
1127
1128	/*
1129	 * Allocate a new entry
1130	 */
1131	vcc = malloc(sizeof(*vcc), M_NETGRAPH, M_NOWAIT | M_ZERO);
1132	if (vcc == NULL)
1133		return (ENOMEM);
1134
1135	vcc->hook = hook;
1136	NG_HOOK_SET_PRIVATE(hook, vcc);
1137
1138	LIST_INSERT_HEAD(&priv->vccs, vcc, link);
1139
1140	if (strcmp(name, "manage") == 0)
1141		priv->manage = hook;
1142
1143	return (0);
1144}
1145
1146/*
1147 * Connect. Set the peer to queuing.
1148 */
1149static int
1150ng_atm_connect(hook_p hook)
1151{
1152	if (NG_HOOK_PRIVATE(hook) != NULL)
1153		NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
1154
1155	return (0);
1156}
1157
1158/*
1159 * Disconnect a HOOK
1160 */
1161static int
1162ng_atm_disconnect(hook_p hook)
1163{
1164	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1165	struct ngvcc *vcc = NG_HOOK_PRIVATE(hook);
1166
1167	if (vcc == NULL) {
1168		if (hook == priv->output) {
1169			priv->output = NULL;
1170			return (0);
1171		}
1172		if (hook == priv->input) {
1173			priv->input = NULL;
1174			return (0);
1175		}
1176		if (hook == priv->orphans) {
1177			priv->orphans = NULL;
1178			return (0);
1179		}
1180		log(LOG_ERR, "ng_atm: bad hook '%s'", NG_HOOK_NAME(hook));
1181		return (0);
1182	}
1183
1184	/* don't terminate if we are detaching from the interface */
1185	if ((vcc->flags & VCC_OPEN) && priv->ifp != NULL)
1186		(void)cpcs_term(priv, vcc->vpi, vcc->vci);
1187
1188	NG_HOOK_SET_PRIVATE(hook, NULL);
1189
1190	LIST_REMOVE(vcc, link);
1191	free(vcc, M_NETGRAPH);
1192
1193	if (hook == priv->manage)
1194		priv->manage = NULL;
1195
1196	return (0);
1197}
1198
1199/************************************************************/
1200/*
1201 * NODE MANAGEMENT
1202 */
1203
1204/*
1205 * ATM interface attached - create a node and name it like the interface.
1206 */
1207static void
1208ng_atm_attach(struct ifnet *ifp)
1209{
1210	char name[IFNAMSIZ+1];
1211	node_p node;
1212	struct priv *priv;
1213
1214	KASSERT(IFP2NG(ifp) == 0, ("%s: node alreay exists?", __FUNCTION__));
1215
1216	snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit);
1217
1218	if (ng_make_node_common(&ng_atm_typestruct, &node) != 0) {
1219		log(LOG_ERR, "%s: can't create node for %s\n",
1220		    __FUNCTION__, name);
1221		return;
1222	}
1223
1224	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
1225	if (priv == NULL) {
1226		log(LOG_ERR, "%s: can't allocate memory for %s\n",
1227		    __FUNCTION__, name);
1228		NG_NODE_UNREF(node);
1229		return;
1230	}
1231	NG_NODE_SET_PRIVATE(node, priv);
1232	priv->ifp = ifp;
1233	LIST_INIT(&priv->vccs);
1234	IFP2NG(ifp) = node;
1235
1236	if (ng_name_node(node, name) != 0) {
1237		log(LOG_WARNING, "%s: can't name node %s\n",
1238		    __FUNCTION__, name);
1239	}
1240}
1241
1242/*
1243 * ATM interface detached - destroy node.
1244 */
1245static void
1246ng_atm_detach(struct ifnet *ifp)
1247{
1248	const node_p node = IFP2NG(ifp);
1249	struct priv *priv;
1250
1251	if(node == NULL)
1252		return;
1253
1254	NG_NODE_REALLY_DIE(node);
1255
1256	priv = NG_NODE_PRIVATE(node);
1257	IFP2NG(priv->ifp) = NULL;
1258	priv->ifp = NULL;
1259
1260	ng_rmnode_self(node);
1261}
1262
1263/*
1264 * Shutdown the node. This is called from the shutdown message processing.
1265 */
1266static int
1267ng_atm_shutdown(node_p node)
1268{
1269	struct priv *priv = NG_NODE_PRIVATE(node);
1270
1271	if (node->nd_flags & NG_REALLY_DIE) {
1272		/*
1273		 * We are called from unloading the ATM driver. Really,
1274		 * really need to shutdown this node. The ifp was
1275		 * already handled in the detach routine.
1276		 */
1277		NG_NODE_SET_PRIVATE(node, NULL);
1278		free(priv, M_NETGRAPH);
1279
1280		NG_NODE_UNREF(node);
1281		return (0);
1282	}
1283
1284#ifdef NGATM_DEBUG
1285	if (!allow_shutdown)
1286		node->nd_flags &= ~NG_INVALID;
1287	else {
1288		IFP2NG(priv->ifp) = NULL;
1289		NG_NODE_SET_PRIVATE(node, NULL);
1290		free(priv, M_NETGRAPH);
1291		NG_NODE_UNREF(node);
1292	}
1293#else
1294	/*
1295	 * We are persistant - reinitialize
1296	 */
1297	node->nd_flags &= ~NG_INVALID;
1298#endif
1299	return (0);
1300}
1301
1302/*
1303 * Nodes are constructed only via interface attaches.
1304 */
1305static int
1306ng_atm_constructor(node_p nodep)
1307{
1308	return (EINVAL);
1309}
1310
1311/************************************************************/
1312/*
1313 * INITIALISATION
1314 */
1315/*
1316 * Loading and unloading of node type
1317 *
1318 * The assignments to the globals for the hooks should be ok without
1319 * a special hook. The use pattern is generally: check that the pointer
1320 * is not NULL, call the function. In the attach case this is no problem.
1321 * In the detach case we can detach only when no ATM node exists. That
1322 * means that there is no ATM interface anymore. So we are sure that
1323 * we are not in the code path in if_atmsubr.c. To prevent someone
1324 * from adding an interface after we have started to unload the node, we
1325 * take the iflist lock so an if_attach will be blocked until we are done.
1326 * XXX: perhaps the function pointers should be 'volatile' for this to work
1327 * properly.
1328 */
1329static int
1330ng_atm_mod_event(module_t mod, int event, void *data)
1331{
1332	struct ifnet *ifp;
1333	int error = 0;
1334
1335	switch (event) {
1336
1337	  case MOD_LOAD:
1338		/*
1339		 * Register function hooks
1340		 */
1341		if (ng_atm_attach_p != NULL) {
1342			error = EEXIST;
1343			break;
1344		}
1345		IFNET_RLOCK();
1346
1347		ng_atm_attach_p = ng_atm_attach;
1348		ng_atm_detach_p = ng_atm_detach;
1349		ng_atm_output_p = ng_atm_output;
1350		ng_atm_input_p = ng_atm_input;
1351		ng_atm_input_orphan_p = ng_atm_input_orphans;
1352		ng_atm_message_p = ng_atm_message;
1353
1354		/* Create nodes for existing ATM interfaces */
1355		TAILQ_FOREACH(ifp, &ifnet, if_link) {
1356			if (ifp->if_type == IFT_ATM)
1357				ng_atm_attach(ifp);
1358		}
1359		IFNET_RUNLOCK();
1360		break;
1361
1362	  case MOD_UNLOAD:
1363		IFNET_RLOCK();
1364
1365		ng_atm_attach_p = NULL;
1366		ng_atm_detach_p = NULL;
1367		ng_atm_output_p = NULL;
1368		ng_atm_input_p = NULL;
1369		ng_atm_input_orphan_p = NULL;
1370		ng_atm_message_p = NULL;
1371
1372		TAILQ_FOREACH(ifp, &ifnet, if_link) {
1373			if (ifp->if_type == IFT_ATM)
1374				ng_atm_detach(ifp);
1375		}
1376		IFNET_RUNLOCK();
1377		break;
1378
1379	  default:
1380		error = EOPNOTSUPP;
1381		break;
1382	}
1383	return (error);
1384}
1385