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