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