]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/netatm/spans/spans_cls.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sys / netatm / spans / spans_cls.c
1 /*-
2  * ===================================
3  * HARP  |  Host ATM Research Platform
4  * ===================================
5  *
6  *
7  * This Host ATM Research Platform ("HARP") file (the "Software") is
8  * made available by Network Computing Services, Inc. ("NetworkCS")
9  * "AS IS".  NetworkCS does not provide maintenance, improvements or
10  * support of any kind.
11  *
12  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
13  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
14  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
15  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
16  * In no event shall NetworkCS be responsible for any damages, including
17  * but not limited to consequential damages, arising from or relating to
18  * any use of the Software or related support.
19  *
20  * Copyright 1994-1998 Network Computing Services, Inc.
21  *
22  * Copies of this Software may be made, however, the above copyright
23  * notice must be reproduced on all copies.
24  */
25
26 /*
27  * SPANS Signalling Manager
28  * ---------------------------
29  *
30  * SPANS Connectionless Datagram Service (CLS) module
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/errno.h>
39 #include <sys/time.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/syslog.h>
43 #include <sys/kernel.h>
44 #include <sys/sysctl.h>
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <netinet/if_ether.h>
48 #include <netatm/port.h>
49 #include <netatm/queue.h>
50 #include <netatm/atm.h>
51 #include <netatm/atm_sys.h>
52 #include <netatm/atm_sap.h>
53 #include <netatm/atm_cm.h>
54 #include <netatm/atm_if.h>
55 #include <netatm/atm_vc.h>
56 #include <netatm/atm_sigmgr.h>
57 #include <netatm/atm_stack.h>
58 #include <netatm/atm_pcb.h>
59 #include <netatm/atm_var.h>
60
61 #include <netatm/ipatm/ipatm_var.h>
62 #include <netatm/ipatm/ipatm_serv.h>
63 #include "spans_xdr.h"
64 #include <netatm/spans/spans_var.h>
65 #include <netatm/spans/spans_cls.h>
66
67 #include <vm/uma.h>
68
69 /*
70  * Global variables
71  */
72 int     spanscls_print = 0;
73 SYSCTL_INT(_net_harp_spans, OID_AUTO, spanscls_print, CTLFLAG_RW,
74     &spanscls_print, 0, "dump SPANS packets");
75
76 struct spanscls *spanscls_head = NULL;
77
78 struct spans_addr       spans_bcastaddr = {
79         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
80 };
81
82 struct spanscls_hdr     spanscls_hdr = {
83         { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, /* dst */
84         { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, /* src */
85         0x00, 0x00, 0,
86         0xaa, 0xaa, 0x03, { 0x00, 0x00, 0x00 }, 0               /* LLC SNAP */
87 };
88
89
90 /*
91  * Local functions
92  */
93 static int      spanscls_ipact(struct ip_nif *);
94 static int      spanscls_ipdact(struct ip_nif *);
95 static int      spanscls_bcast_output(struct ip_nif *, KBuffer *);
96 static void     spanscls_cpcs_data(void *, KBuffer *);
97 static void     spanscls_connected(void *);
98 static void     spanscls_cleared(void *, struct t_atm_cause *);
99 static caddr_t  spanscls_getname(void *);
100 static void     spanscls_pdu_print(const struct spanscls *, const KBuffer *,
101                     const char *);
102
103 /*
104  * Local variables
105  */
106 static uma_zone_t       spanscls_zone;
107
108 static struct ip_serv   spanscls_ipserv = {
109         spanscls_ipact,
110         spanscls_ipdact,
111         spansarp_ioctl,
112         NULL,
113         spansarp_svcout,
114         spansarp_svcin,
115         spansarp_svcactive,
116         spansarp_vcclose,
117         spanscls_bcast_output,
118         {
119                 {ATM_AAL5, ATM_ENC_NULL},
120                 {ATM_AAL3_4, ATM_ENC_NULL}
121         }
122 };
123
124 static u_char   spanscls_bridged[] = {
125         0x00, 0x00, 0x00, 0x00,
126         0xaa, 0xaa, 0x03, 0x00, 0x80, 0xc2      /* LLC SNAP */
127 };
128
129 static Atm_endpoint     spanscls_endpt = {
130         NULL,
131         ENDPT_SPANS_CLS,
132         NULL,
133         spanscls_getname,
134         spanscls_connected,
135         spanscls_cleared,
136         NULL,
137         NULL,
138         NULL,
139         NULL,
140         spanscls_cpcs_data,
141         NULL,
142         NULL,
143         NULL,
144         NULL
145 };
146
147 static Atm_attributes   spanscls_attr = {
148         NULL,                   /* nif */
149         CMAPI_CPCS,             /* api */
150         0,                      /* api_init */
151         0,                      /* headin */
152         0,                      /* headout */
153         {                       /* aal */
154                 T_ATM_PRESENT,
155                 ATM_AAL3_4
156         },
157         {                       /* traffic */
158                 T_ATM_PRESENT,
159                 {
160                         {
161                                 T_ATM_ABSENT,
162                                 0,
163                                 T_ATM_ABSENT,
164                                 T_ATM_ABSENT,
165                                 T_ATM_ABSENT,
166                                 T_ATM_ABSENT,
167                                 T_NO
168                         },
169                         {
170                                 T_ATM_ABSENT,
171                                 0,
172                                 T_ATM_ABSENT,
173                                 T_ATM_ABSENT,
174                                 T_ATM_ABSENT,
175                                 T_ATM_ABSENT,
176                                 T_NO
177                         },
178                         T_YES
179                 },
180         },
181         {                       /* bearer */
182                 T_ATM_PRESENT,
183                 {
184                         T_ATM_CLASS_X,
185                         T_ATM_NULL,
186                         T_ATM_NULL,
187                         T_NO,
188                         T_ATM_1_TO_1
189                 }
190         },
191         {                       /* bhli */
192                 T_ATM_ABSENT
193         },
194         {                       /* blli */
195                 T_ATM_ABSENT,
196                 T_ATM_ABSENT
197         },
198         {                       /* llc */
199                 T_ATM_ABSENT
200         },
201         {                       /* called */
202                 T_ATM_PRESENT,
203         },
204         {                       /* calling */
205                 T_ATM_ABSENT
206         },
207         {                       /* qos */
208                 T_ATM_PRESENT,
209                 {
210                         T_ATM_NETWORK_CODING,
211                         {
212                                 T_ATM_QOS_CLASS_0,
213                         },
214                         {
215                                 T_ATM_QOS_CLASS_0
216                         }
217                 }
218         },
219         {                       /* transit */
220                 T_ATM_ABSENT
221         },
222         {                       /* cause */
223                 T_ATM_ABSENT
224         }
225 };
226
227 static struct t_atm_cause       spanscls_cause = {
228         T_ATM_ITU_CODING,
229         T_ATM_LOC_USER,
230         T_ATM_CAUSE_UNSPECIFIED_NORMAL,
231         {0, 0, 0, 0}
232 };
233
234
235 /*
236  * Process module loading
237  * 
238  * Called whenever the spans module is initializing.  
239  *
240  * Arguments:
241  *      none
242  *
243  * Returns:
244  *      0       initialization successful
245  *      errno   initialization failed - reason indicated
246  *
247  */
248 int
249 spanscls_start()
250 {
251         int     err;
252
253         spanscls_zone = uma_zcreate("spanscls", sizeof(struct spanscls),
254             NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
255         if (spanscls_zone == NULL)
256                 panic("spanscls_zone");
257
258         /*
259          * Fill in union fields
260          */
261         spanscls_attr.aal.v.aal4.forward_max_SDU_size = ATM_NIF_MTU;
262         spanscls_attr.aal.v.aal4.backward_max_SDU_size = ATM_NIF_MTU;
263         spanscls_attr.aal.v.aal4.SSCS_type = T_ATM_NULL;
264         spanscls_attr.aal.v.aal4.mid_low = 0;
265         spanscls_attr.aal.v.aal4.mid_high = 1023;
266
267         /*
268          * Register our endpoint
269          */
270         err = atm_endpoint_register(&spanscls_endpt);
271
272         return (err);
273 }
274
275
276 /*
277  * Process module unloading notification
278  * 
279  * Called whenever the spans module is about to be unloaded.  All signalling
280  * instances will have been previously detached.  All spanscls resources
281  * must be freed now.
282  *
283  * Arguments:
284  *      none
285  *
286  * Returns:
287  *      none
288  *
289  */
290 void
291 spanscls_stop()
292 {
293         int     s = splnet();
294
295         /*
296          * Tell ARP to stop
297          */
298         spansarp_stop();
299
300         /*
301          * Nothing should be left here...
302          */
303         if (spanscls_head) {
304                 panic("spanscls_stop: bad state");
305         }
306         (void) splx(s);
307
308         /*
309          * De-register ourselves
310          */
311         (void) atm_endpoint_deregister(&spanscls_endpt);
312
313         /*
314          * Free our storage pools
315          */
316         uma_zdestroy(spanscls_zone);
317 }
318
319
320 /*
321  * Process signalling interface attach
322  * 
323  * This function is called whenever a physical interface has been attached
324  * to spans.  We will open the CLS PVC and await further events.
325  *
326  * Called at splnet.
327  *
328  * Arguments:
329  *      spp     pointer to spans signalling protocol instance
330  *
331  * Returns:
332  *      0       attach successful
333  *      errno   attach failed - reason indicated
334  *
335  */
336 int
337 spanscls_attach(spp)
338         struct spans    *spp;
339 {
340         struct spanscls *clp;
341         Atm_addr_pvc    *pvcp;
342         int     err;
343
344         /*
345          * Get a new cls control block
346          */
347         clp = uma_zalloc(spanscls_zone, M_WAITOK);
348         if (clp == NULL)
349                 return (ENOMEM);
350
351         /*
352          * Initialize some stuff
353          */
354         clp->cls_state = CLS_CLOSED;
355         clp->cls_spans = spp;
356         spp->sp_ipserv = &spanscls_ipserv;
357
358         /*
359          * Fill out connection attributes
360          */
361         spanscls_attr.nif = spp->sp_pif->pif_nif;
362         spanscls_attr.traffic.v.forward.PCR_all_traffic = spp->sp_pif->pif_pcr;
363         spanscls_attr.traffic.v.backward.PCR_all_traffic = spp->sp_pif->pif_pcr;
364         spanscls_attr.called.addr.address_format = T_ATM_PVC_ADDR;
365         spanscls_attr.called.addr.address_length = sizeof(Atm_addr_pvc);
366         pvcp = (Atm_addr_pvc *)spanscls_attr.called.addr.address;
367         ATM_PVC_SET_VPI(pvcp, SPANS_CLS_VPI);
368         ATM_PVC_SET_VCI(pvcp, SPANS_CLS_VCI);
369         spanscls_attr.called.subaddr.address_format = T_ATM_ABSENT;
370         spanscls_attr.called.subaddr.address_length = 0;
371
372         /*
373          * Create SPANS Connectionless Service (CLS) PVC
374          */
375         err = atm_cm_connect(&spanscls_endpt, clp, &spanscls_attr,
376                         &clp->cls_conn);
377         if (err) {
378                 uma_zfree(spanscls_zone, clp);
379                 return (err);
380         }
381
382         /*
383          * Set new state and link instance
384          */
385         clp->cls_state = CLS_OPEN;
386         LINK2TAIL(clp, struct spanscls, spanscls_head, cls_next);
387         spp->sp_cls = clp;
388
389         return (0);
390 }
391
392
393 /*
394  * Process signalling interface detach
395  * 
396  * This function is called whenever a physical interface has been detached
397  * from spans.  We will close the CLS PVC and clean up everything.
398  *
399  * Called at splnet.
400  *
401  * Arguments:
402  *      spp     pointer to spans signalling protocol instance
403  *
404  * Returns:
405  *      none
406  *
407  */
408 void
409 spanscls_detach(spp)
410         struct spans    *spp;
411 {
412         struct spanscls *clp;
413
414         /*
415          * Get our control block
416          */
417         clp = spp->sp_cls;
418         if (clp == NULL)
419                 return;
420
421         /*
422          * Just checking up on things...
423          */
424         if (clp->cls_ipnif)
425                 panic("spanscls_detach: IP interface still active");
426
427         /*
428          * Close CLS PVC
429          */
430         spanscls_closevc(clp, &spanscls_cause);
431
432         /*
433          * Sever links and free server block, if possible
434          */
435         clp->cls_spans = NULL;
436         spp->sp_cls = NULL;
437         if (clp->cls_state == CLS_CLOSED) {
438                 UNLINK(clp, struct spanscls, spanscls_head, cls_next);
439                 uma_zfree(spanscls_zone, clp);
440         }
441 }
442
443
444 /*
445  * Process IP Network Interface Activation
446  * 
447  * Called whenever an IP network interface becomes active.
448  *
449  * Called at splnet.
450  *
451  * Arguments:
452  *      inp     pointer to IP network interface
453  *
454  * Returns:
455  *      0       command successful
456  *      errno   command failed - reason indicated
457  *
458  */
459 static int
460 spanscls_ipact(inp)
461         struct ip_nif   *inp;
462 {
463         struct spans            *spp;
464         struct spanscls         *clp;
465
466         /*
467          * Get corresponding cls instance
468          */
469         spp = (struct spans *)inp->inf_nif->nif_pif->pif_siginst;
470         if ((spp == NULL) || ((clp = spp->sp_cls) == NULL))
471                 return (ENXIO);
472
473         /*
474          * Make sure it's not already activated
475          */
476         if (clp->cls_ipnif)
477                 return (EEXIST);
478
479         /*
480          * Set two-way links with IP world
481          */
482         clp->cls_ipnif = inp;
483         inp->inf_isintf = (caddr_t)clp;
484
485         /*
486          * Tell arp about new interface
487          */
488         spansarp_ipact(clp);
489
490         return (0);
491 }
492
493
494 /*
495  * Process IP Network Interface Deactivation
496  * 
497  * Called whenever an IP network interface becomes inactive.
498  *
499  * Called at splnet.
500  *
501  * Arguments:
502  *      inp     pointer to IP network interface
503  *
504  * Returns:
505  *      0       command successful
506  *      errno   command failed - reason indicated
507  *
508  */
509 static int
510 spanscls_ipdact(inp)
511         struct ip_nif   *inp;
512 {
513         struct spanscls         *clp;
514
515         /*
516          * Get cls instance and make sure it's been activated
517          */
518         clp = (struct spanscls *)inp->inf_isintf;
519         if ((clp == NULL) || (clp->cls_ipnif == NULL))
520                 return (ENXIO);
521
522         /*
523          * Let arp know about this
524          */
525         spansarp_ipdact(clp);
526
527         /*
528          * Clear IP interface pointer
529          */
530         clp->cls_ipnif = NULL;
531         return (0);
532 }
533
534
535 /*
536  * Output IP Broadcast Packet
537  * 
538  * Called whenever an IP broadcast packet is sent to this interface.
539  *
540  * Arguments:
541  *      inp     pointer to IP network interface
542  *      m       pointer to packet buffer chain
543  *
544  * Returns:
545  *      0       packet sent successfully
546  *      errno   send failed - reason indicated
547  *
548  */
549 static int
550 spanscls_bcast_output(inp, m)
551         struct ip_nif   *inp;
552         KBuffer         *m;
553 {
554         struct spans            *spp;
555         struct spanscls         *clp;
556         struct spanscls_hdr     *chp;
557         int                     err, space;
558
559         /*
560          * Get cls instance and make sure it's been activated
561          */
562         clp = (struct spanscls *)inp->inf_isintf;
563         if ((clp == NULL) || (clp->cls_ipnif == NULL)) {
564                 KB_FREEALL(m);
565                 return (ENETDOWN);
566         }
567
568         /*
569          * Make sure that we know our addresses
570          */
571         spp = clp->cls_spans;
572         if (spp->sp_addr.address_format != T_ATM_SPANS_ADDR) {
573                 KB_FREEALL(m);
574                 return (ENETDOWN);
575         }
576
577         /*
578          * See if there's room to add CLS header to front of packet.
579          */
580         KB_HEADROOM(m, space);
581         if (space < sizeof(struct spanscls_hdr)) {
582                 KBuffer         *n;
583
584                 /*
585                  * We have to allocate another buffer and tack it
586                  * onto the front of the packet
587                  */
588                 KB_ALLOCPKT(n, sizeof(struct spanscls_hdr),
589                         KB_F_NOWAIT, KB_T_HEADER);
590                 if (n == 0) {
591                         KB_FREEALL(m);
592                         return (ENOBUFS);
593                 }
594                 KB_TAILALIGN(n, sizeof(struct spanscls_hdr));
595                 KB_LINKHEAD(n, m);
596                 m = n;
597         } else {
598                 /*
599                  * Header fits, just adjust buffer controls
600                  */
601                 KB_HEADADJ(m, sizeof(struct spanscls_hdr));
602         }
603
604         /*
605          * Now, build the CLS header
606          */
607         KB_DATASTART(m, chp, struct spanscls_hdr *);
608         spans_addr_copy(&spans_bcastaddr, &chp->ch_dst);
609         spans_addr_copy(spp->sp_addr.address, &chp->ch_src);
610         *(u_int *)&chp->ch_proto = *(u_int *)&spanscls_hdr.ch_proto;
611         *(u_int *)&chp->ch_dsap = *(u_int *)&spanscls_hdr.ch_dsap;
612         *(u_short *)&chp->ch_oui[1] = *(u_short *)&spanscls_hdr.ch_oui[1];
613         chp->ch_pid = htons(ETHERTYPE_IP);
614
615         if (spanscls_print)
616                 spanscls_pdu_print(clp, m, "output");
617
618         /*
619          * Finally, send the pdu via the CLS service
620          */
621         err = atm_cm_cpcs_data(clp->cls_conn, m);
622         if (err) {
623                 KB_FREEALL(m);
624                 return (ENOBUFS);
625         }
626
627         return (0);
628 }
629
630
631 /*
632  * Process VCC Input Data
633  * 
634  * All input packets received from CLS VCC lower layers are processed here.
635  *
636  * Arguments:
637  *      tok     connection token (pointer to CLS VCC control block)
638  *      m       pointer to input packet buffer chain
639  *
640  * Returns:
641  *      none
642  *
643  */
644 static void
645 spanscls_cpcs_data(tok, m)
646         void            *tok;
647         KBuffer         *m;
648 {
649         struct spanscls *clp = tok;
650         struct spans    *spp = clp->cls_spans;
651         struct spanscls_hdr     *chp;
652         struct ip_nif   *inp;
653
654         /*
655          * Make sure we're ready
656          */
657         if ((clp->cls_state != CLS_OPEN) || (spp->sp_state != SPANS_ACTIVE)) {
658                 KB_FREEALL(m);
659                 return;
660         }
661
662         if (spanscls_print)
663                 spanscls_pdu_print(clp, m, "input");
664
665         /*
666          * Get CLS header into buffer
667          */
668         if (KB_LEN(m) < sizeof(struct spanscls_hdr)) {
669                 KB_PULLUP(m, sizeof(struct spanscls_hdr), m);
670                 if (m == 0)
671                         return;
672         }
673         KB_DATASTART(m, chp, struct spanscls_hdr *);
674
675         /*
676          * Verify packet information
677          */
678         if ((*(u_int *)&chp->ch_proto != *(u_int *)&spanscls_hdr.ch_proto) ||
679             (*(u_int *)&chp->ch_dsap != *(u_int *)&spanscls_hdr.ch_dsap) ||
680             (*(u_short *)&chp->ch_oui[1] != 
681                                 *(u_short *)&spanscls_hdr.ch_oui[1])) {
682
683                 /*
684                  * Check for bridged PDU
685                  */
686                 if (bcmp((char *)&chp->ch_proto, (char *)spanscls_bridged, 
687                                 sizeof(spanscls_bridged))) {
688                         log(LOG_ERR, "spanscls_input: bad format\n");
689                         if (spanscls_print)
690                                 spanscls_pdu_print(clp, m, "input error"); 
691                 }
692
693                 KB_FREEALL(m);
694                 return;
695         }
696
697         /*
698          * Make sure packet is for us
699          */
700         if (spans_addr_cmp(&chp->ch_dst, spp->sp_addr.address) &&
701             spans_addr_cmp(&chp->ch_dst, &spans_bcastaddr)) {
702                 KB_FREEALL(m);
703                 return;
704         }
705
706         /*
707          * Do protocol processing
708          */
709         switch (ntohs(chp->ch_pid)) {
710
711         case ETHERTYPE_IP:
712                 /*
713                  * Drop CLS header
714                  */
715                 KB_HEADADJ(m, -sizeof(struct spanscls_hdr));
716                 KB_PLENADJ(m, -sizeof(struct spanscls_hdr));
717
718                 /*
719                  * Packet is ready for input to IP
720                  */
721                 if ((inp = clp->cls_ipnif) != NULL)
722                         (void) (*inp->inf_ipinput)(inp, m);
723                 else
724                         KB_FREEALL(m);
725                 break;
726
727         case ETHERTYPE_ARP:
728                 spansarp_input(clp, m);
729                 break;
730
731         default:
732                 log(LOG_ERR, "spanscls_input: unknown protocol 0x%x\n",
733                         chp->ch_pid);
734                 KB_FREEALL(m);
735                 return;
736         }
737 }
738
739
740 /*
741  * Close a SPANS CLS VCC
742  * 
743  * This function will close a SPANS CLS VCC.
744  *
745  * Arguments:
746  *      clp     pointer to CLS instance
747  *      cause   pointer to cause code
748  *
749  * Returns:
750  *      none
751  *
752  */
753 void
754 spanscls_closevc(clp, cause)
755         struct spanscls *clp;
756         struct t_atm_cause      *cause;
757 {
758         int     err;
759
760         /*
761          * Close VCC
762          */
763         if (clp->cls_conn) {
764                 err = atm_cm_release(clp->cls_conn, cause);
765                 if (err) {
766                         log(LOG_ERR, "spanscls_closevc: release err=%d\n", err);
767                 }
768                 clp->cls_conn = NULL;
769         }
770
771         clp->cls_state = CLS_CLOSED;
772 }
773
774
775 /*
776  * Process CLS VCC Connected Notification
777  * 
778  * Arguments:
779  *      toku    user's connection token (spanscls protocol block)
780  *
781  * Returns:
782  *      none
783  *
784  */
785 static void
786 spanscls_connected(toku)
787         void            *toku;
788 {
789         /*
790          * We should never get one of these
791          */
792         log(LOG_ERR, "spanscls: unexpected connected event\n");
793 }
794
795
796 /*
797  * Process CLS VCC Cleared Notification
798  * 
799  * Arguments:
800  *      toku    user's connection token (spanscls protocol block)
801  *      cause   pointer to cause code
802  *
803  * Returns:
804  *      none
805  *
806  */
807 static void
808 spanscls_cleared(toku, cause)
809         void            *toku;
810         struct t_atm_cause      *cause;
811 {
812         struct spanscls *clp = (struct spanscls *)toku;
813
814         /*
815          * CLS VCC has been closed, so clean up our side
816          */
817         clp->cls_conn = NULL;
818         spanscls_closevc(clp, cause);
819 }
820
821
822 /*
823  * Get Connection's Application/Owner Name
824  * 
825  * Arguments:
826  *      tok     spanscls connection token
827  *
828  * Returns:
829  *      addr    pointer to string containing our name
830  *
831  */
832 static caddr_t
833 spanscls_getname(tok)
834         void            *tok;
835 {
836         return ("SPANSCLS");
837 }
838
839 /*
840  * Print a SPANS CLS PDU
841  * 
842  * Arguments:
843  *      clp     pointer to cls instance
844  *      m       pointer to pdu buffer chain
845  *      msg     pointer to message string
846  *
847  * Returns:
848  *      none
849  *
850  */
851 static void
852 spanscls_pdu_print(const struct spanscls *clp, const KBuffer *m,
853     const char *msg)
854 {
855         char            buf[128];
856
857         snprintf(buf, sizeof(buf), "spanscls %s:\n", msg);
858         atm_pdu_print(m, buf);
859 }