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