1/*-
2 * Copyright (c) 2001-2002
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 * Copyright (c) 2003-2004
6 *	Hartmut Brandt
7 *	All rights reserved.
8 *
9 * Author: Harti Brandt <harti@freebsd.org>
10 *
11 * Redistribution of this software and documentation and use in source and
12 * binary forms, with or without modification, are permitted provided that
13 * the following conditions are met:
14 *
15 * 1. Redistributions of source code or documentation must retain the above
16 *    copyright notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
25 * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $FreeBSD$
34 *
35 * ATM call control and API
36 */
37
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD$");
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/mbuf.h>
46#include <sys/errno.h>
47#include <sys/socket.h>
48#include <sys/socketvar.h>
49#include <sys/sbuf.h>
50#include <machine/stdarg.h>
51
52#include <netgraph/ng_message.h>
53#include <netgraph/netgraph.h>
54#include <netgraph/ng_parse.h>
55#include <netnatm/unimsg.h>
56#include <netnatm/msg/unistruct.h>
57#include <netnatm/api/unisap.h>
58#include <netnatm/sig/unidef.h>
59#include <netgraph/atm/ngatmbase.h>
60#include <netgraph/atm/ng_uni.h>
61#include <netnatm/api/atmapi.h>
62#include <netgraph/atm/ng_ccatm.h>
63#include <netnatm/api/ccatm.h>
64
65MODULE_DEPEND(ng_ccatm, ngatmbase, 1, 1, 1);
66
67MALLOC_DEFINE(M_NG_CCATM, "ng_ccatm", "netgraph uni api node");
68
69/*
70 * Command structure parsing
71 */
72
73/* ESI */
74static const struct ng_parse_fixedarray_info ng_ccatm_esi_type_info =
75    NGM_CCATM_ESI_INFO;
76static const struct ng_parse_type ng_ccatm_esi_type = {
77	&ng_parse_fixedarray_type,
78	&ng_ccatm_esi_type_info
79};
80
81/* PORT PARAMETERS */
82static const struct ng_parse_struct_field ng_ccatm_atm_port_type_info[] =
83    NGM_CCATM_ATM_PORT_INFO;
84static const struct ng_parse_type ng_ccatm_atm_port_type = {
85	&ng_parse_struct_type,
86	ng_ccatm_atm_port_type_info
87};
88
89/* PORT structure */
90static const struct ng_parse_struct_field ng_ccatm_port_type_info[] =
91    NGM_CCATM_PORT_INFO;
92static const struct ng_parse_type ng_ccatm_port_type = {
93	&ng_parse_struct_type,
94	ng_ccatm_port_type_info
95};
96
97/* the ADDRESS array itself */
98static const struct ng_parse_fixedarray_info ng_ccatm_addr_array_type_info =
99    NGM_CCATM_ADDR_ARRAY_INFO;
100static const struct ng_parse_type ng_ccatm_addr_array_type = {
101	&ng_parse_fixedarray_type,
102	&ng_ccatm_addr_array_type_info
103};
104
105/* one ADDRESS */
106static const struct ng_parse_struct_field ng_ccatm_uni_addr_type_info[] =
107    NGM_CCATM_UNI_ADDR_INFO;
108static const struct ng_parse_type ng_ccatm_uni_addr_type = {
109	&ng_parse_struct_type,
110	ng_ccatm_uni_addr_type_info
111};
112
113/* ADDRESS request */
114static const struct ng_parse_struct_field ng_ccatm_addr_req_type_info[] =
115    NGM_CCATM_ADDR_REQ_INFO;
116static const struct ng_parse_type ng_ccatm_addr_req_type = {
117	&ng_parse_struct_type,
118	ng_ccatm_addr_req_type_info
119};
120
121/* ADDRESS var-array */
122static int
123ng_ccatm_addr_req_array_getlen(const struct ng_parse_type *type,
124    const u_char *start, const u_char *buf)
125{
126	const struct ngm_ccatm_get_addresses *p;
127
128	p = (const struct ngm_ccatm_get_addresses *)
129	    (buf - offsetof(struct ngm_ccatm_get_addresses, addr));
130	return (p->count);
131}
132static const struct ng_parse_array_info ng_ccatm_addr_req_array_type_info =
133    NGM_CCATM_ADDR_REQ_ARRAY_INFO;
134static const struct ng_parse_type ng_ccatm_addr_req_array_type = {
135	&ng_parse_array_type,
136	&ng_ccatm_addr_req_array_type_info
137};
138
139/* Outer get_ADDRESSes structure */
140static const struct ng_parse_struct_field ng_ccatm_get_addresses_type_info[] =
141    NGM_CCATM_GET_ADDRESSES_INFO;
142static const struct ng_parse_type ng_ccatm_get_addresses_type = {
143	&ng_parse_struct_type,
144	ng_ccatm_get_addresses_type_info
145};
146
147/* Port array */
148static int
149ng_ccatm_port_array_getlen(const struct ng_parse_type *type,
150    const u_char *start, const u_char *buf)
151{
152	const struct ngm_ccatm_portlist *p;
153
154	p = (const struct ngm_ccatm_portlist *)
155	    (buf - offsetof(struct ngm_ccatm_portlist, ports));
156	return (p->nports);
157}
158static const struct ng_parse_array_info ng_ccatm_port_array_type_info =
159    NGM_CCATM_PORT_ARRAY_INFO;
160static const struct ng_parse_type ng_ccatm_port_array_type = {
161	&ng_parse_array_type,
162	&ng_ccatm_port_array_type_info
163};
164
165/* Portlist structure */
166static const struct ng_parse_struct_field ng_ccatm_portlist_type_info[] =
167    NGM_CCATM_PORTLIST_INFO;
168static const struct ng_parse_type ng_ccatm_portlist_type = {
169	&ng_parse_struct_type,
170	ng_ccatm_portlist_type_info
171};
172
173/*
174 * Command list
175 */
176static const struct ng_cmdlist ng_ccatm_cmdlist[] = {
177	{
178	  NGM_CCATM_COOKIE,
179	  NGM_CCATM_DUMP,
180	  "dump",
181	  NULL,
182	  NULL
183	},
184	{
185	  NGM_CCATM_COOKIE,
186	  NGM_CCATM_STOP,
187	  "stop",
188	  &ng_ccatm_port_type,
189	  NULL
190	},
191	{
192	  NGM_CCATM_COOKIE,
193	  NGM_CCATM_START,
194	  "start",
195	  &ng_ccatm_port_type,
196	  NULL
197	},
198	{
199	  NGM_CCATM_COOKIE,
200	  NGM_CCATM_GETSTATE,
201	  "getstate",
202	  &ng_ccatm_port_type,
203	  &ng_parse_uint32_type
204	},
205	{
206	  NGM_CCATM_COOKIE,
207	  NGM_CCATM_GET_ADDRESSES,
208	  "get_addresses",
209	  &ng_ccatm_port_type,
210	  &ng_ccatm_get_addresses_type
211	},
212	{
213	  NGM_CCATM_COOKIE,
214	  NGM_CCATM_CLEAR,
215	  "clear",
216	  &ng_ccatm_port_type,
217	  NULL
218	},
219	{
220	  NGM_CCATM_COOKIE,
221	  NGM_CCATM_ADDRESS_REGISTERED,
222	  "address_reg",
223	  &ng_ccatm_addr_req_type,
224	  NULL
225	},
226	{
227	  NGM_CCATM_COOKIE,
228	  NGM_CCATM_ADDRESS_UNREGISTERED,
229	  "address_unreg",
230	  &ng_ccatm_addr_req_type,
231	  NULL
232	},
233	{
234	  NGM_CCATM_COOKIE,
235	  NGM_CCATM_SET_PORT_PARAM,
236	  "set_port_param",
237	  &ng_ccatm_atm_port_type,
238	  NULL
239	},
240	{
241	  NGM_CCATM_COOKIE,
242	  NGM_CCATM_GET_PORT_PARAM,
243	  "get_port_param",
244	  &ng_ccatm_port_type,
245	  &ng_ccatm_atm_port_type,
246	},
247	{
248	  NGM_CCATM_COOKIE,
249	  NGM_CCATM_GET_PORTLIST,
250	  "get_portlist",
251	  NULL,
252	  &ng_ccatm_portlist_type,
253	},
254	{
255	  NGM_CCATM_COOKIE,
256	  NGM_CCATM_SETLOG,
257	  "setlog",
258	  &ng_parse_hint32_type,
259	  &ng_parse_hint32_type,
260	},
261	{
262	  NGM_CCATM_COOKIE,
263	  NGM_CCATM_RESET,
264	  "reset",
265	  NULL,
266	  NULL,
267	},
268	{ 0 }
269};
270
271/*
272 * Module data
273 */
274static ng_constructor_t		ng_ccatm_constructor;
275static ng_rcvmsg_t		ng_ccatm_rcvmsg;
276static ng_shutdown_t		ng_ccatm_shutdown;
277static ng_newhook_t		ng_ccatm_newhook;
278static ng_rcvdata_t		ng_ccatm_rcvdata;
279static ng_disconnect_t		ng_ccatm_disconnect;
280static int ng_ccatm_mod_event(module_t, int, void *);
281
282static struct ng_type ng_ccatm_typestruct = {
283	.version =	NG_ABI_VERSION,
284	.name =		NG_CCATM_NODE_TYPE,
285	.mod_event =	ng_ccatm_mod_event,
286	.constructor =	ng_ccatm_constructor,	/* Node constructor */
287	.rcvmsg =	ng_ccatm_rcvmsg,	/* Control messages */
288	.shutdown =	ng_ccatm_shutdown,	/* Node destructor */
289	.newhook =	ng_ccatm_newhook,	/* Arrival of new hook */
290	.rcvdata =	ng_ccatm_rcvdata,	/* receive data */
291	.disconnect =	ng_ccatm_disconnect,	/* disconnect a hook */
292	.cmdlist =	ng_ccatm_cmdlist,
293};
294NETGRAPH_INIT(ccatm, &ng_ccatm_typestruct);
295
296static ng_rcvdata_t	ng_ccatm_rcvuni;
297static ng_rcvdata_t	ng_ccatm_rcvdump;
298static ng_rcvdata_t	ng_ccatm_rcvmanage;
299
300/*
301 * Private node data.
302 */
303struct ccnode {
304	node_p	node;		/* the owning node */
305	hook_p	dump;		/* dump hook */
306	hook_p	manage;		/* hook to ILMI */
307
308	struct ccdata *data;
309	struct mbuf *dump_first;
310	struct mbuf *dump_last;	/* first and last mbuf when dumping */
311
312	u_int	hook_cnt;	/* count user and port hooks */
313};
314
315/*
316 * Private UNI hook data
317 */
318struct cchook {
319	int		is_uni;	/* true if uni hook, user otherwise */
320	struct ccnode	*node;	/* the owning node */
321	hook_p		hook;
322	void		*inst;	/* port or user */
323};
324
325static void ng_ccatm_send_user(struct ccuser *, void *, u_int, void *, size_t);
326static void ng_ccatm_respond_user(struct ccuser *, void *, int, u_int,
327    void *, size_t);
328static void ng_ccatm_send_uni(struct ccconn *, void *, u_int, u_int,
329    struct uni_msg *);
330static void ng_ccatm_send_uni_glob(struct ccport *, void *, u_int, u_int,
331    struct uni_msg *);
332static void ng_ccatm_log(const char *, ...) __printflike(1, 2);
333
334static const struct cc_funcs cc_funcs = {
335	.send_user =		ng_ccatm_send_user,
336	.respond_user =		ng_ccatm_respond_user,
337	.send_uni =		ng_ccatm_send_uni,
338	.send_uni_glob =	ng_ccatm_send_uni_glob,
339	.log =			ng_ccatm_log,
340};
341
342/************************************************************
343 *
344 * Create a new node
345 */
346static int
347ng_ccatm_constructor(node_p node)
348{
349	struct ccnode *priv;
350
351	priv = malloc(sizeof(*priv), M_NG_CCATM, M_WAITOK | M_ZERO);
352
353	priv->node = node;
354	priv->data = cc_create(&cc_funcs);
355	if (priv->data == NULL) {
356		free(priv, M_NG_CCATM);
357		return (ENOMEM);
358	}
359
360	NG_NODE_SET_PRIVATE(node, priv);
361
362	return (0);
363}
364
365/*
366 * Destroy a node. The user list is empty here, because all hooks are
367 * previously disconnected. The connection lists may not be empty, because
368 * connections may be waiting for responses from the stack. This also means,
369 * that no orphaned connections will be made by the port_destroy routine.
370 */
371static int
372ng_ccatm_shutdown(node_p node)
373{
374	struct ccnode *priv = NG_NODE_PRIVATE(node);
375
376	cc_destroy(priv->data);
377
378	free(priv, M_NG_CCATM);
379	NG_NODE_SET_PRIVATE(node, NULL);
380
381	NG_NODE_UNREF(node);
382
383	return (0);
384}
385
386/*
387 * Retrieve the registered addresses for one port or all ports.
388 * Returns an error code or 0 on success.
389 */
390static int
391ng_ccatm_get_addresses(node_p node, uint32_t portno, struct ng_mesg *msg,
392    struct ng_mesg **resp)
393{
394	struct ccnode *priv = NG_NODE_PRIVATE(node);
395	struct uni_addr *addrs;
396	u_int *ports;
397	struct ngm_ccatm_get_addresses *list;
398	u_int count, i;
399	size_t len;
400	int err;
401
402	err = cc_get_addrs(priv->data, portno, &addrs, &ports, &count);
403	if (err != 0)
404		return (err);
405
406	len = sizeof(*list) + count * sizeof(list->addr[0]);
407	NG_MKRESPONSE(*resp, msg, len, M_NOWAIT);
408	if (*resp == NULL) {
409		free(addrs, M_NG_CCATM);
410		free(ports, M_NG_CCATM);
411		return (ENOMEM);
412	}
413	list = (struct ngm_ccatm_get_addresses *)(*resp)->data;
414
415	list->count = count;
416	for (i = 0; i < count; i++) {
417		list->addr[i].port = ports[i];
418		list->addr[i].addr = addrs[i];
419	}
420
421	free(addrs, M_NG_CCATM);
422	free(ports, M_NG_CCATM);
423
424	return (0);
425}
426
427/*
428 * Dumper function. Pack the data into an mbuf chain.
429 */
430static int
431send_dump(struct ccdata *data, void *uarg, const char *buf)
432{
433	struct mbuf *m;
434	struct ccnode *priv = uarg;
435
436	if (priv->dump == NULL) {
437		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
438		if (m == NULL)
439			return (ENOBUFS);
440		priv->dump_first = priv->dump_last = m;
441		m->m_pkthdr.len = 0;
442	} else {
443		m = m_getcl(M_NOWAIT, MT_DATA, 0);
444		if (m == 0) {
445			m_freem(priv->dump_first);
446			return (ENOBUFS);
447		}
448		priv->dump_last->m_next = m;
449		priv->dump_last = m;
450	}
451
452	strcpy(m->m_data, buf);
453	priv->dump_first->m_pkthdr.len += (m->m_len = strlen(buf));
454
455	return (0);
456}
457
458/*
459 * Dump current status to dump hook
460 */
461static int
462ng_ccatm_dump(node_p node)
463{
464	struct ccnode *priv = NG_NODE_PRIVATE(node);
465	struct mbuf *m;
466	int error;
467
468	priv->dump_first = priv->dump_last = NULL;
469	error = cc_dump(priv->data, MCLBYTES, send_dump, priv);
470	if (error != 0)
471		return (error);
472
473	if ((m = priv->dump_first) != NULL) {
474		priv->dump_first = priv->dump_last = NULL;
475		NG_SEND_DATA_ONLY(error, priv->dump, m);
476		return (error);
477	}
478	return (0);
479}
480
481/*
482 * Control message
483 */
484static int
485ng_ccatm_rcvmsg(node_p node, item_p item, hook_p lasthook)
486{
487	struct ng_mesg *resp = NULL;
488	struct ng_mesg *msg;
489	struct ccnode *priv = NG_NODE_PRIVATE(node);
490	int error = 0;
491
492	NGI_GET_MSG(item, msg);
493
494	switch (msg->header.typecookie) {
495
496	  case NGM_CCATM_COOKIE:
497		switch (msg->header.cmd) {
498
499		  case NGM_CCATM_DUMP:
500			if (priv->dump)
501				error = ng_ccatm_dump(node);
502			else
503				error = ENOTCONN;
504			break;
505
506		  case NGM_CCATM_STOP:
507		    {
508			struct ngm_ccatm_port *arg;
509
510			if (msg->header.arglen != sizeof(*arg)) {
511				error = EINVAL;
512				break;
513			}
514			arg = (struct ngm_ccatm_port *)msg->data;
515			error = cc_port_stop(priv->data, arg->port);
516			break;
517		    }
518
519		  case NGM_CCATM_START:
520		    {
521			struct ngm_ccatm_port *arg;
522
523			if (msg->header.arglen != sizeof(*arg)) {
524				error = EINVAL;
525				break;
526			}
527			arg = (struct ngm_ccatm_port *)msg->data;
528			error = cc_port_start(priv->data, arg->port);
529			break;
530		    }
531
532		  case NGM_CCATM_GETSTATE:
533		    {
534			struct ngm_ccatm_port *arg;
535			int state;
536
537			if (msg->header.arglen != sizeof(*arg)) {
538				error = EINVAL;
539				break;
540			}
541			arg = (struct ngm_ccatm_port *)msg->data;
542			error = cc_port_isrunning(priv->data, arg->port,
543			    &state);
544			if (error == 0) {
545				NG_MKRESPONSE(resp, msg, sizeof(uint32_t),
546				    M_NOWAIT);
547				if (resp == NULL) {
548					error = ENOMEM;
549					break;
550				}
551				*(uint32_t *)resp->data = state;
552			}
553			break;
554		    }
555
556		  case NGM_CCATM_GET_ADDRESSES:
557		   {
558			struct ngm_ccatm_port *arg;
559
560			if (msg->header.arglen != sizeof(*arg)) {
561				error = EINVAL;
562				break;
563			}
564			arg = (struct ngm_ccatm_port *)msg->data;
565			error = ng_ccatm_get_addresses(node, arg->port, msg,
566			    &resp);
567			break;
568		    }
569
570		  case NGM_CCATM_CLEAR:
571		    {
572			struct ngm_ccatm_port *arg;
573
574			if (msg->header.arglen != sizeof(*arg)) {
575				error = EINVAL;
576				break;
577			}
578			arg = (struct ngm_ccatm_port *)msg->data;
579			error = cc_port_clear(priv->data, arg->port);
580			break;
581		    }
582
583		  case NGM_CCATM_ADDRESS_REGISTERED:
584		    {
585			struct ngm_ccatm_addr_req *arg;
586
587			if (msg->header.arglen != sizeof(*arg)) {
588				error = EINVAL;
589				break;
590			}
591			arg = (struct ngm_ccatm_addr_req *)msg->data;
592			error = cc_addr_register(priv->data, arg->port,
593			    &arg->addr);
594			break;
595		    }
596
597		  case NGM_CCATM_ADDRESS_UNREGISTERED:
598		    {
599			struct ngm_ccatm_addr_req *arg;
600
601			if (msg->header.arglen != sizeof(*arg)) {
602				error = EINVAL;
603				break;
604			}
605			arg = (struct ngm_ccatm_addr_req *)msg->data;
606			error = cc_addr_unregister(priv->data, arg->port,
607			    &arg->addr);
608			break;
609		    }
610
611		  case NGM_CCATM_GET_PORT_PARAM:
612		    {
613			struct ngm_ccatm_port *arg;
614
615			if (msg->header.arglen != sizeof(*arg)) {
616				error = EINVAL;
617				break;
618			}
619			arg = (struct ngm_ccatm_port *)msg->data;
620			NG_MKRESPONSE(resp, msg, sizeof(struct atm_port_info),
621			    M_NOWAIT);
622			if (resp == NULL) {
623				error = ENOMEM;
624				break;
625			}
626			error = cc_port_get_param(priv->data, arg->port,
627			    (struct atm_port_info *)resp->data);
628			if (error != 0) {
629				free(resp, M_NETGRAPH_MSG);
630				resp = NULL;
631			}
632			break;
633		    }
634
635		  case NGM_CCATM_SET_PORT_PARAM:
636		    {
637			struct atm_port_info *arg;
638
639			if (msg->header.arglen != sizeof(*arg)) {
640				error = EINVAL;
641				break;
642			}
643			arg = (struct atm_port_info *)msg->data;
644			error = cc_port_set_param(priv->data, arg);
645			break;
646		    }
647
648		  case NGM_CCATM_GET_PORTLIST:
649		    {
650			struct ngm_ccatm_portlist *arg;
651			u_int n, *ports;
652
653			if (msg->header.arglen != 0) {
654				error = EINVAL;
655				break;
656			}
657			error = cc_port_getlist(priv->data, &n, &ports);
658			if (error != 0)
659				break;
660
661			NG_MKRESPONSE(resp, msg, sizeof(*arg) +
662			    n * sizeof(arg->ports[0]), M_NOWAIT);
663			if (resp == NULL) {
664				free(ports, M_NG_CCATM);
665				error = ENOMEM;
666				break;
667			}
668			arg = (struct ngm_ccatm_portlist *)resp->data;
669
670			arg->nports = 0;
671			for (arg->nports = 0; arg->nports < n; arg->nports++)
672				arg->ports[arg->nports] = ports[arg->nports];
673			free(ports, M_NG_CCATM);
674			break;
675		    }
676
677		  case NGM_CCATM_SETLOG:
678		    {
679			uint32_t log_level;
680
681			log_level = cc_get_log(priv->data);
682			if (msg->header.arglen != 0) {
683				if (msg->header.arglen != sizeof(log_level)) {
684					error = EINVAL;
685					break;
686				}
687				cc_set_log(priv->data, *(uint32_t *)msg->data);
688			}
689
690			NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
691			if (resp == NULL) {
692				error = ENOMEM;
693				if (msg->header.arglen != 0)
694					cc_set_log(priv->data, log_level);
695				break;
696			}
697			*(uint32_t *)resp->data = log_level;
698			break;
699		    }
700
701		  case NGM_CCATM_RESET:
702			if (msg->header.arglen != 0) {
703				error = EINVAL;
704				break;
705			}
706
707			if (priv->hook_cnt != 0) {
708				error = EBUSY;
709				break;
710			}
711			cc_reset(priv->data);
712			break;
713
714		  case NGM_CCATM_GET_EXSTAT:
715		    {
716			struct atm_exstatus s;
717			struct atm_exstatus_ep *eps;
718			struct atm_exstatus_port *ports;
719			struct atm_exstatus_conn *conns;
720			struct atm_exstatus_party *parties;
721			size_t offs;
722
723			if (msg->header.arglen != 0) {
724				error = EINVAL;
725				break;
726			}
727			error = cc_get_extended_status(priv->data,
728			    &s, &eps, &ports, &conns, &parties);
729			if (error != 0)
730				break;
731
732			offs = sizeof(s) + s.neps * sizeof(*eps) +
733			    s.nports * sizeof(*ports) +
734			    s.nconns * sizeof(*conns) +
735			    s.nparties * sizeof(*parties);
736
737			NG_MKRESPONSE(resp, msg, offs, M_NOWAIT);
738			if (resp == NULL) {
739				error = ENOMEM;
740				break;
741			}
742
743			memcpy(resp->data, &s, sizeof(s));
744			offs = sizeof(s);
745
746			memcpy(resp->data + offs, eps,
747			    sizeof(*eps) * s.neps);
748			offs += sizeof(*eps) * s.neps;
749
750			memcpy(resp->data + offs, ports,
751			    sizeof(*ports) * s.nports);
752			offs += sizeof(*ports) * s.nports;
753
754			memcpy(resp->data + offs, conns,
755			    sizeof(*conns) * s.nconns);
756			offs += sizeof(*conns) * s.nconns;
757
758			memcpy(resp->data + offs, parties,
759			    sizeof(*parties) * s.nparties);
760			offs += sizeof(*parties) * s.nparties;
761
762			free(eps, M_NG_CCATM);
763			free(ports, M_NG_CCATM);
764			free(conns, M_NG_CCATM);
765			free(parties, M_NG_CCATM);
766			break;
767		    }
768
769		  default:
770			error = EINVAL;
771			break;
772
773		}
774		break;
775
776	  default:
777		error = EINVAL;
778		break;
779
780	}
781
782	NG_RESPOND_MSG(error, node, item, resp);
783	NG_FREE_MSG(msg);
784	return (error);
785}
786
787/************************************************************
788 *
789 * New hook arrival
790 */
791static int
792ng_ccatm_newhook(node_p node, hook_p hook, const char *name)
793{
794	struct ccnode *priv = NG_NODE_PRIVATE(node);
795	struct ccport *port;
796	struct ccuser *user;
797	struct cchook *hd;
798	u_long lport;
799	char *end;
800
801	if (strncmp(name, "uni", 3) == 0) {
802		/*
803		 * This is a UNI hook. Should be a new port.
804		 */
805		if (name[3] == '\0')
806			return (EINVAL);
807		lport = strtoul(name + 3, &end, 10);
808		if (*end != '\0' || lport == 0 || lport > 0xffffffff)
809			return (EINVAL);
810
811		hd = malloc(sizeof(*hd), M_NG_CCATM, M_NOWAIT);
812		if (hd == NULL)
813			return (ENOMEM);
814		hd->is_uni = 1;
815		hd->node = priv;
816		hd->hook = hook;
817
818		port = cc_port_create(priv->data, hd, (u_int)lport);
819		if (port == NULL) {
820			free(hd, M_NG_CCATM);
821			return (ENOMEM);
822		}
823		hd->inst = port;
824
825		NG_HOOK_SET_PRIVATE(hook, hd);
826		NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvuni);
827		NG_HOOK_FORCE_QUEUE(hook);
828
829		priv->hook_cnt++;
830
831		return (0);
832	}
833
834	if (strcmp(name, "dump") == 0) {
835		priv->dump = hook;
836		NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvdump);
837		return (0);
838	}
839
840	if (strcmp(name, "manage") == 0) {
841		priv->manage = hook;
842		NG_HOOK_SET_RCVDATA(hook, ng_ccatm_rcvmanage);
843		return (0);
844	}
845
846	/*
847	 * User hook
848	 */
849	hd = malloc(sizeof(*hd), M_NG_CCATM, M_NOWAIT);
850	if (hd == NULL)
851		return (ENOMEM);
852	hd->is_uni = 0;
853	hd->node = priv;
854	hd->hook = hook;
855
856	user = cc_user_create(priv->data, hd, NG_HOOK_NAME(hook));
857	if (user == NULL) {
858		free(hd, M_NG_CCATM);
859		return (ENOMEM);
860	}
861
862	hd->inst = user;
863	NG_HOOK_SET_PRIVATE(hook, hd);
864	NG_HOOK_FORCE_QUEUE(hook);
865
866	priv->hook_cnt++;
867
868	return (0);
869}
870
871/*
872 * Disconnect a hook
873 */
874static int
875ng_ccatm_disconnect(hook_p hook)
876{
877	node_p node = NG_HOOK_NODE(hook);
878	struct ccnode *priv = NG_NODE_PRIVATE(node);
879	struct cchook *hd = NG_HOOK_PRIVATE(hook);
880	struct ccdata *cc;
881
882	if (hook == priv->dump) {
883		priv->dump = NULL;
884
885	} else if (hook == priv->manage) {
886		priv->manage = NULL;
887		cc_unmanage(priv->data);
888
889	} else {
890		if (hd->is_uni)
891			cc_port_destroy(hd->inst, 0);
892		else
893			cc_user_destroy(hd->inst);
894
895		cc = hd->node->data;
896
897		free(hd, M_NG_CCATM);
898		NG_HOOK_SET_PRIVATE(hook, NULL);
899
900		priv->hook_cnt--;
901
902		cc_work(cc);
903	}
904
905	/*
906	 * When the number of hooks drops to zero, delete the node.
907	 */
908	if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node))
909		ng_rmnode_self(node);
910
911	return (0);
912}
913
914/************************************************************
915 *
916 * Receive data from user hook
917 */
918static int
919ng_ccatm_rcvdata(hook_p hook, item_p item)
920{
921	struct cchook *hd = NG_HOOK_PRIVATE(hook);
922	struct uni_msg *msg;
923	struct mbuf *m;
924	struct ccatm_op op;
925	int err;
926
927	NGI_GET_M(item, m);
928	NG_FREE_ITEM(item);
929
930	if ((err = uni_msg_unpack_mbuf(m, &msg)) != 0) {
931		m_freem(m);
932		return (err);
933	}
934	m_freem(m);
935
936	if (uni_msg_len(msg) < sizeof(op)) {
937		printf("%s: packet too short\n", __func__);
938		uni_msg_destroy(msg);
939		return (EINVAL);
940	}
941
942	bcopy(msg->b_rptr, &op, sizeof(op));
943	msg->b_rptr += sizeof(op);
944
945	err = cc_user_signal(hd->inst, op.op, msg);
946	cc_work(hd->node->data);
947	return (err);
948}
949
950/*
951 * Pack a header and a data area into an mbuf chain
952 */
953static struct mbuf *
954pack_buf(void *h, size_t hlen, void *t, size_t tlen)
955{
956	struct mbuf *m, *m0, *last;
957	u_char *buf = (u_char *)t;
958	size_t n;
959
960	/* header should fit into a normal mbuf */
961	MGETHDR(m0, M_NOWAIT, MT_DATA);
962	if (m0 == NULL)
963		return NULL;
964
965	KASSERT(hlen <= MHLEN, ("hlen > MHLEN"));
966
967	bcopy(h, m0->m_data, hlen);
968	m0->m_len = hlen;
969	m0->m_pkthdr.len = hlen;
970
971	last = m0;
972	while ((n = tlen) != 0) {
973		if (n > MLEN) {
974			m = m_getcl(M_NOWAIT, MT_DATA, 0);
975			if (n > MCLBYTES)
976				n = MCLBYTES;
977		} else
978			MGET(m, M_NOWAIT, MT_DATA);
979
980		if(m == NULL)
981			goto drop;
982
983		last->m_next = m;
984		last = m;
985
986		bcopy(buf, m->m_data, n);
987		buf += n;
988		tlen -= n;
989		m->m_len = n;
990		m0->m_pkthdr.len += n;
991	}
992
993	return (m0);
994
995  drop:
996	m_freem(m0);
997	return NULL;
998}
999
1000/*
1001 * Send an indication to the user.
1002 */
1003static void
1004ng_ccatm_send_user(struct ccuser *user, void *uarg, u_int op,
1005    void *val, size_t len)
1006{
1007	struct cchook *hd = uarg;
1008	struct mbuf *m;
1009	struct ccatm_op	h;
1010	int error;
1011
1012	h.op = op;
1013	m = pack_buf(&h, sizeof(h), val, len);
1014	if (m == NULL)
1015		return;
1016
1017	NG_SEND_DATA_ONLY(error, hd->hook, m);
1018	if (error != 0)
1019		printf("%s: error=%d\n", __func__, error);
1020}
1021
1022/*
1023 * Send a response to the user.
1024 */
1025static void
1026ng_ccatm_respond_user(struct ccuser *user, void *uarg, int err, u_int data,
1027    void *val, size_t len)
1028{
1029	struct cchook *hd = uarg;
1030	struct mbuf *m;
1031	struct {
1032		struct ccatm_op	op;
1033		struct atm_resp resp;
1034	} resp;
1035	int error;
1036
1037	resp.op.op = ATMOP_RESP;
1038	resp.resp.resp = err;
1039	resp.resp.data = data;
1040	m = pack_buf(&resp, sizeof(resp), val, len);
1041	if (m == NULL)
1042		return;
1043
1044	NG_SEND_DATA_ONLY(error, hd->hook, m);
1045	if (error != 0)
1046		printf("%s: error=%d\n", __func__, error);
1047}
1048
1049/*
1050 * Receive data from UNI.
1051 */
1052static int
1053ng_ccatm_rcvuni(hook_p hook, item_p item)
1054{
1055	struct cchook *hd = NG_HOOK_PRIVATE(hook);
1056	struct uni_msg *msg;
1057	struct uni_arg arg;
1058	struct mbuf *m;
1059	int err;
1060
1061	NGI_GET_M(item, m);
1062	NG_FREE_ITEM(item);
1063
1064	if ((err = uni_msg_unpack_mbuf(m, &msg)) != 0) {
1065		m_freem(m);
1066		return (err);
1067	}
1068	m_freem(m);
1069
1070	if (uni_msg_len(msg) < sizeof(arg)) {
1071		printf("%s: packet too short\n", __func__);
1072		uni_msg_destroy(msg);
1073		return (EINVAL);
1074	}
1075
1076	bcopy(msg->b_rptr, &arg, sizeof(arg));
1077	msg->b_rptr += sizeof(arg);
1078
1079	if (arg.sig == UNIAPI_ERROR) {
1080		if (uni_msg_len(msg) != sizeof(struct uniapi_error)) {
1081			printf("%s: bad UNIAPI_ERROR size %zu\n", __func__,
1082			    uni_msg_len(msg));
1083			uni_msg_destroy(msg);
1084			return (EINVAL);
1085		}
1086		err = cc_uni_response(hd->inst, arg.cookie,
1087		    ((struct uniapi_error *)msg->b_rptr)->reason,
1088		    ((struct uniapi_error *)msg->b_rptr)->state);
1089		uni_msg_destroy(msg);
1090	} else
1091		err = cc_uni_signal(hd->inst, arg.cookie, arg.sig, msg);
1092
1093	cc_work(hd->node->data);
1094	return (err);
1095}
1096
1097/*
1098 * Uarg is the port's uarg.
1099 */
1100static void
1101ng_ccatm_send_uni(struct ccconn *conn, void *uarg, u_int op, u_int cookie,
1102    struct uni_msg *msg)
1103{
1104	struct cchook *hd = uarg;
1105	struct uni_arg arg;
1106	struct mbuf *m;
1107	int error;
1108
1109	arg.sig = op;
1110	arg.cookie = cookie;
1111
1112	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
1113	uni_msg_destroy(msg);
1114	if (m == NULL)
1115		return;
1116
1117	NG_SEND_DATA_ONLY(error, hd->hook, m);
1118	if (error != 0)
1119		printf("%s: error=%d\n", __func__, error);
1120}
1121
1122/*
1123 * Send a global message to the UNI
1124 */
1125static void
1126ng_ccatm_send_uni_glob(struct ccport *port, void *uarg, u_int op, u_int cookie,
1127    struct uni_msg *msg)
1128{
1129	struct cchook *hd = uarg;
1130	struct uni_arg arg;
1131	struct mbuf *m;
1132	int error;
1133
1134	arg.sig = op;
1135	arg.cookie = cookie;
1136
1137	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
1138	if (msg != NULL)
1139		uni_msg_destroy(msg);
1140	if (m == NULL)
1141		return;
1142
1143	NG_SEND_DATA_ONLY(error, hd->hook, m);
1144	if (error != 0)
1145		printf("%s: error=%d\n", __func__, error);
1146}
1147/*
1148 * Receive from ILMID
1149 */
1150static int
1151ng_ccatm_rcvmanage(hook_p hook, item_p item)
1152{
1153	NG_FREE_ITEM(item);
1154	return (0);
1155}
1156
1157static int
1158ng_ccatm_rcvdump(hook_p hook, item_p item)
1159{
1160	NG_FREE_ITEM(item);
1161	return (0);
1162}
1163
1164static void
1165ng_ccatm_log(const char *fmt, ...)
1166{
1167	va_list ap;
1168
1169	va_start(ap, fmt);
1170	vprintf(fmt, ap);
1171	printf("\n");
1172	va_end(ap);
1173}
1174
1175/*
1176 * Loading and unloading of node type
1177 */
1178static int
1179ng_ccatm_mod_event(module_t mod, int event, void *data)
1180{
1181	int error = 0;
1182
1183	switch (event) {
1184
1185	  case MOD_LOAD:
1186		break;
1187
1188	  case MOD_UNLOAD:
1189		break;
1190
1191	  default:
1192		error = EOPNOTSUPP;
1193		break;
1194	}
1195	return (error);
1196}
1197