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