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