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