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