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