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