]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/if_ethersubr.c
This commit was generated by cvs2svn to compensate for changes in r55682,
[FreeBSD/FreeBSD.git] / sys / net / if_ethersubr.c
1 /*
2  * Copyright (c) 1982, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *      @(#)if_ethersubr.c      8.1 (Berkeley) 6/10/93
34  * $FreeBSD$
35  */
36
37 #include "opt_atalk.h"
38 #include "opt_inet.h"
39 #include "opt_inet6.h"
40 #include "opt_ipx.h"
41 #include "opt_bdg.h"
42 #include "opt_netgraph.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/mbuf.h>
49 #include <sys/socket.h>
50 #include <sys/sockio.h>
51 #include <sys/sysctl.h>
52
53 #include <net/if.h>
54 #include <net/netisr.h>
55 #include <net/route.h>
56 #include <net/if_llc.h>
57 #include <net/if_dl.h>
58 #include <net/if_types.h>
59
60 #if defined(INET) || defined(INET6)
61 #include <netinet/in.h>
62 #include <netinet/in_var.h>
63 #include <netinet/if_ether.h>
64 #endif
65 #ifdef INET6
66 #include <netinet6/nd6.h>
67 #include <netinet6/in6_ifattach.h>
68 #endif
69
70 #ifdef IPX
71 #include <netipx/ipx.h>
72 #include <netipx/ipx_if.h>
73 int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m);
74 int (*ef_outputp)(struct ifnet *ifp, struct mbuf *m,
75                 struct sockaddr *dst, short *tp);
76 #endif
77
78 #ifdef NS
79 #include <netns/ns.h>
80 #include <netns/ns_if.h>
81 ushort ns_nettype;
82 int ether_outputdebug = 0;
83 int ether_inputdebug = 0;
84 #endif
85
86 #ifdef ISO
87 #include <netiso/argo_debug.h>
88 #include <netiso/iso.h>
89 #include <netiso/iso_var.h>
90 #include <netiso/iso_snpac.h>
91 #endif
92
93 /*#ifdef LLC
94 #include <netccitt/dll.h>
95 #include <netccitt/llc_var.h>
96 #endif*/
97
98 #if defined(LLC) && defined(CCITT)
99 extern struct ifqueue pkintrq;
100 #endif
101
102 #ifdef NETATALK
103 #include <netatalk/at.h>
104 #include <netatalk/at_var.h>
105 #include <netatalk/at_extern.h>
106
107 #define llc_snap_org_code llc_un.type_snap.org_code
108 #define llc_snap_ether_type llc_un.type_snap.ether_type
109
110 extern u_char   at_org_code[3];
111 extern u_char   aarp_org_code[3];
112 #endif /* NETATALK */
113
114 #ifdef BRIDGE
115 #include <net/bridge.h>
116 #endif
117
118 #include "vlan.h"
119 #if NVLAN > 0
120 #include <net/if_vlan_var.h>
121 #endif /* NVLAN > 0 */
122
123 static  int ether_resolvemulti __P((struct ifnet *, struct sockaddr **,
124                                     struct sockaddr *));
125 u_char  etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
126 #define senderr(e) do { error = (e); goto bad;} while (0)
127 #define IFP2AC(IFP) ((struct arpcom *)IFP)
128
129 #ifdef NETGRAPH
130 #include <netgraph/ng_ether.h>
131 #include <netgraph/ng_message.h>
132 #include <netgraph/netgraph.h>
133
134 static  void    ngether_init(void* ignored);
135 static void     ngether_send(struct arpcom *ac,
136                         struct ether_header *eh, struct mbuf *m);
137 static  ng_constructor_t        ngether_constructor;
138 static  ng_rcvmsg_t             ngether_rcvmsg;
139 static  ng_shutdown_t           ngether_rmnode;
140 static  ng_newhook_t            ngether_newhook;
141 static  ng_connect_t            ngether_connect;
142 static  ng_rcvdata_t            ngether_rcvdata;
143 static  ng_disconnect_t         ngether_disconnect;
144
145 static struct ng_type typestruct = {
146         NG_VERSION,
147         NG_ETHER_NODE_TYPE,
148         NULL,
149         ngether_constructor,
150         ngether_rcvmsg,
151         ngether_rmnode,
152         ngether_newhook,
153         NULL,
154         ngether_connect,
155         ngether_rcvdata,
156         ngether_rcvdata,
157         ngether_disconnect,
158         NULL
159 };
160
161 #define AC2NG(AC) ((node_p)((AC)->ac_ng))
162 #define NGEF_DIVERT NGF_TYPE1   /* all packets sent to netgraph */
163 #endif /* NETGRAPH */
164
165 /*
166  * Ethernet output routine.
167  * Encapsulate a packet of type family for the local net.
168  * Use trailer local net encapsulation if enough data in first
169  * packet leaves a multiple of 512 bytes of data in remainder.
170  * Assumes that ifp is actually pointer to arpcom structure.
171  */
172 int
173 ether_output(ifp, m, dst, rt0)
174         register struct ifnet *ifp;
175         struct mbuf *m;
176         struct sockaddr *dst;
177         struct rtentry *rt0;
178 {
179         short type;
180         int s, error = 0, hdrcmplt = 0;
181         u_char esrc[6], edst[6];
182         register struct rtentry *rt;
183         register struct ether_header *eh;
184         int off, len = m->m_pkthdr.len, loop_copy = 0;
185         int hlen;       /* link layer header lenght */
186         struct arpcom *ac = IFP2AC(ifp);
187
188         if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
189                 senderr(ENETDOWN);
190         rt = rt0;
191         if (rt) {
192                 if ((rt->rt_flags & RTF_UP) == 0) {
193                         rt0 = rt = rtalloc1(dst, 1, 0UL);
194                         if (rt0)
195                                 rt->rt_refcnt--;
196                         else
197                                 senderr(EHOSTUNREACH);
198                 }
199                 if (rt->rt_flags & RTF_GATEWAY) {
200                         if (rt->rt_gwroute == 0)
201                                 goto lookup;
202                         if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
203                                 rtfree(rt); rt = rt0;
204                         lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1,
205                                                           0UL);
206                                 if ((rt = rt->rt_gwroute) == 0)
207                                         senderr(EHOSTUNREACH);
208                         }
209                 }
210                 if (rt->rt_flags & RTF_REJECT)
211                         if (rt->rt_rmx.rmx_expire == 0 ||
212                             time_second < rt->rt_rmx.rmx_expire)
213                                 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
214         }
215         hlen = ETHER_HDR_LEN;
216         switch (dst->sa_family) {
217 #ifdef INET
218         case AF_INET:
219                 if (!arpresolve(ac, rt, m, dst, edst, rt0))
220                         return (0);     /* if not yet resolved */
221                 off = m->m_pkthdr.len - m->m_len;
222                 type = htons(ETHERTYPE_IP);
223                 break;
224 #endif
225 #ifdef INET6
226         case AF_INET6:
227                 if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) {
228                         /* this must be impossible, so we bark */
229                         printf("nd6_storelladdr failed\n");
230                         return(0);
231                 }
232                 off = m->m_pkthdr.len - m->m_len;
233                 type = htons(ETHERTYPE_IPV6);
234                 break;
235 #endif
236 #ifdef IPX
237         case AF_IPX:
238                 if (ef_outputp) {
239                     error = ef_outputp(ifp, m, dst, &type);
240                     if (error < 0)
241                         senderr(EPFNOSUPPORT);
242                     if (error > 0)
243                         type = htons(ETHERTYPE_IPX);
244                 } else
245                     type = htons(ETHERTYPE_IPX);
246                 bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
247                     (caddr_t)edst, sizeof (edst));
248                 break;
249 #endif
250 #ifdef NETATALK
251         case AF_APPLETALK:
252           {
253             struct at_ifaddr *aa;
254
255             if ((aa = at_ifawithnet((struct sockaddr_at *)dst)) == NULL) {
256                     goto bad;
257             }
258             if (!aarpresolve(ac, m, (struct sockaddr_at *)dst, edst))
259                     return (0);
260             /*
261              * In the phase 2 case, need to prepend an mbuf for the llc header.
262              * Since we must preserve the value of m, which is passed to us by
263              * value, we m_copy() the first mbuf, and use it for our llc header.
264              */
265             if ( aa->aa_flags & AFA_PHASE2 ) {
266                 struct llc llc;
267
268                 M_PREPEND(m, sizeof(struct llc), M_WAIT);
269                 len += sizeof(struct llc);
270                 llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
271                 llc.llc_control = LLC_UI;
272                 bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code));
273                 llc.llc_snap_ether_type = htons( ETHERTYPE_AT );
274                 bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc));
275                 type = htons(m->m_pkthdr.len);
276                 hlen = sizeof(struct llc) + ETHER_HDR_LEN;
277             } else {
278                 type = htons(ETHERTYPE_AT);
279             }
280             break;
281           }
282 #endif NETATALK
283 #ifdef NS
284         case AF_NS:
285                 switch(ns_nettype){
286                 default:
287                 case 0x8137: /* Novell Ethernet_II Ethernet TYPE II */
288                         type = 0x8137;
289                         break;
290                 case 0x0: /* Novell 802.3 */
291                         type = htons( m->m_pkthdr.len);
292                         break;
293                 case 0xe0e0: /* Novell 802.2 and Token-Ring */
294                         M_PREPEND(m, 3, M_WAIT);
295                         type = htons( m->m_pkthdr.len);
296                         cp = mtod(m, u_char *);
297                         *cp++ = 0xE0;
298                         *cp++ = 0xE0;
299                         *cp++ = 0x03;
300                         break;
301                 }
302                 bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
303                     (caddr_t)edst, sizeof (edst));
304                 /*
305                  * XXX if ns_thishost is the same as the node's ethernet
306                  * address then just the default code will catch this anyhow.
307                  * So I'm not sure if this next clause should be here at all?
308                  * [JRE]
309                  */
310                 if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst))){
311                         m->m_pkthdr.rcvif = ifp;
312                         schednetisr(NETISR_NS);
313                         inq = &nsintrq;
314                         s = splimp();
315                         if (IF_QFULL(inq)) {
316                                 IF_DROP(inq);
317                                 m_freem(m);
318                         } else
319                                 IF_ENQUEUE(inq, m);
320                         splx(s);
321                         return (error);
322                 }
323                 if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost, sizeof(edst))){
324                         m->m_flags |= M_BCAST;
325                 }
326                 break;
327 #endif /* NS */
328 #ifdef  ISO
329         case AF_ISO: {
330                 int     snpalen;
331                 struct  llc *l;
332                 register struct sockaddr_dl *sdl;
333
334                 if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) &&
335                     sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) {
336                         bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst));
337                 } else if (error =
338                             iso_snparesolve(ifp, (struct sockaddr_iso *)dst,
339                                             (char *)edst, &snpalen))
340                         goto bad; /* Not Resolved */
341                 /* If broadcasting on a simplex interface, loopback a copy */
342                 if (*edst & 1)
343                         m->m_flags |= (M_BCAST|M_MCAST);
344                 M_PREPEND(m, 3, M_DONTWAIT);
345                 if (m == NULL)
346                         return (0);
347                 type = htons(m->m_pkthdr.len);
348                 l = mtod(m, struct llc *);
349                 l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP;
350                 l->llc_control = LLC_UI;
351                 len += 3;
352                 IFDEBUG(D_ETHER)
353                         int i;
354                         printf("unoutput: sending pkt to: ");
355                         for (i=0; i<6; i++)
356                                 printf("%x ", edst[i] & 0xff);
357                         printf("\n");
358                 ENDDEBUG
359                 } break;
360 #endif /* ISO */
361 #ifdef  LLC
362 /*      case AF_NSAP: */
363         case AF_CCITT: {
364                 register struct sockaddr_dl *sdl =
365                         (struct sockaddr_dl *) rt -> rt_gateway;
366
367                 if (sdl && sdl->sdl_family == AF_LINK
368                     && sdl->sdl_alen > 0) {
369                         bcopy(LLADDR(sdl), (char *)edst, sizeof(edst));
370                 } else goto bad; /* Not a link interface ? Funny ... */
371                 if (*edst & 1)
372                         loop_copy = 1;
373                 type = htons(m->m_pkthdr.len);
374 #ifdef LLC_DEBUG
375                 {
376                         int i;
377                         register struct llc *l = mtod(m, struct llc *);
378
379                         printf("ether_output: sending LLC2 pkt to: ");
380                         for (i=0; i<6; i++)
381                                 printf("%x ", edst[i] & 0xff);
382                         printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n",
383                                type & 0xff, l->llc_dsap & 0xff, l->llc_ssap &0xff,
384                                l->llc_control & 0xff);
385
386                 }
387 #endif /* LLC_DEBUG */
388                 } break;
389 #endif /* LLC */
390
391         case pseudo_AF_HDRCMPLT:
392                 hdrcmplt = 1;
393                 eh = (struct ether_header *)dst->sa_data;
394                 (void)memcpy(esrc, eh->ether_shost, sizeof (esrc));
395                 /* FALLTHROUGH */
396
397         case AF_UNSPEC:
398                 loop_copy = -1; /* if this is for us, don't do it */
399                 eh = (struct ether_header *)dst->sa_data;
400                 (void)memcpy(edst, eh->ether_dhost, sizeof (edst));
401                 type = eh->ether_type;
402                 break;
403
404         default:
405                 printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
406                         dst->sa_family);
407                 senderr(EAFNOSUPPORT);
408         }
409
410         /*
411          * Add local net header.  If no space in first mbuf,
412          * allocate another.
413          */
414         M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
415         if (m == 0)
416                 senderr(ENOBUFS);
417         eh = mtod(m, struct ether_header *);
418         (void)memcpy(&eh->ether_type, &type,
419                 sizeof(eh->ether_type));
420         (void)memcpy(eh->ether_dhost, edst, sizeof (edst));
421         if (hdrcmplt)
422                 (void)memcpy(eh->ether_shost, esrc,
423                         sizeof(eh->ether_shost));
424         else
425                 (void)memcpy(eh->ether_shost, ac->ac_enaddr,
426                         sizeof(eh->ether_shost));
427
428         /*
429          * If a simplex interface, and the packet is being sent to our
430          * Ethernet address or a broadcast address, loopback a copy.
431          * XXX To make a simplex device behave exactly like a duplex
432          * device, we should copy in the case of sending to our own
433          * ethernet address (thus letting the original actually appear
434          * on the wire). However, we don't do that here for security
435          * reasons and compatibility with the original behavior.
436          */
437         if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
438                 if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
439                         struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
440
441                         (void) if_simloop(ifp, n, dst, hlen);
442                 } else if (bcmp(eh->ether_dhost,
443                     eh->ether_shost, ETHER_ADDR_LEN) == 0) {
444                         (void) if_simloop(ifp, m, dst, hlen);
445                         return (0);     /* XXX */
446                 }
447         }
448 #ifdef BRIDGE
449         if (do_bridge) {
450                 struct mbuf *m0 = m ;
451
452                 if (m->m_pkthdr.rcvif)
453                         m->m_pkthdr.rcvif = NULL ;
454                 ifp = bridge_dst_lookup(m);
455                 bdg_forward(&m0, ifp);
456                 if (m0)
457                         m_freem(m0);
458                 return (0);
459         }
460 #endif
461         s = splimp();
462         /*
463          * Queue message on interface, and start output if interface
464          * not yet active.
465          */
466         if (IF_QFULL(&ifp->if_snd)) {
467                 IF_DROP(&ifp->if_snd);
468                 splx(s);
469                 senderr(ENOBUFS);
470         }
471         IF_ENQUEUE(&ifp->if_snd, m);
472         if ((ifp->if_flags & IFF_OACTIVE) == 0)
473                 (*ifp->if_start)(ifp);
474         splx(s);
475         ifp->if_obytes += len + sizeof (struct ether_header);
476         if (m->m_flags & M_MCAST)
477                 ifp->if_omcasts++;
478         return (error);
479
480 bad:
481         if (m)
482                 m_freem(m);
483         return (error);
484 }
485
486 /*
487  * Process a received Ethernet packet;
488  * the packet is in the mbuf chain m without
489  * the ether header, which is provided separately.
490  */
491 void
492 ether_input(ifp, eh, m)
493         struct ifnet *ifp;
494         register struct ether_header *eh;
495         struct mbuf *m;
496 {
497         register struct ifqueue *inq;
498         u_short ether_type;
499         int s;
500 #if defined (ISO) || defined (LLC) || defined(NETATALK)
501         register struct llc *l;
502 #endif
503
504         if ((ifp->if_flags & IFF_UP) == 0) {
505                 m_freem(m);
506                 return;
507         }
508         ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
509         if (eh->ether_dhost[0] & 1) {
510                 if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
511                          sizeof(etherbroadcastaddr)) == 0)
512                         m->m_flags |= M_BCAST;
513                 else
514                         m->m_flags |= M_MCAST;
515         }
516         if (m->m_flags & (M_BCAST|M_MCAST))
517                 ifp->if_imcasts++;
518
519         ether_type = ntohs(eh->ether_type);
520
521 #ifdef  NETGRAPH
522         {
523                 struct arpcom *ac = IFP2AC(ifp);
524                 if (AC2NG(ac) && (AC2NG(ac)->flags & NGEF_DIVERT)) {
525                         ngether_send(ac, eh, m);
526                         return;
527                 }
528         }
529 #endif  /* NETGRAPH */
530                 
531 #if NVLAN > 0
532         if (ether_type == vlan_proto) {
533                 if (vlan_input(eh, m) < 0)
534                         ifp->if_data.ifi_noproto++;
535                 return;
536         }
537 #endif /* NVLAN > 0 */
538
539         switch (ether_type) {
540 #ifdef INET
541         case ETHERTYPE_IP:
542                 if (ipflow_fastforward(m))
543                         return;
544                 schednetisr(NETISR_IP);
545                 inq = &ipintrq;
546                 break;
547
548         case ETHERTYPE_ARP:
549                 schednetisr(NETISR_ARP);
550                 inq = &arpintrq;
551                 break;
552 #endif
553 #ifdef IPX
554         case ETHERTYPE_IPX:
555                 if (ef_inputp && ef_inputp(ifp, eh, m) == 0)
556                         return;
557                 schednetisr(NETISR_IPX);
558                 inq = &ipxintrq;
559                 break;
560 #endif
561 #ifdef INET6
562         case ETHERTYPE_IPV6:
563                 schednetisr(NETISR_IPV6);
564                 inq = &ip6intrq;
565                 break;
566 #endif
567 #ifdef NS
568         case 0x8137: /* Novell Ethernet_II Ethernet TYPE II */
569                 schednetisr(NETISR_NS);
570                 inq = &nsintrq;
571                 break;
572
573 #endif /* NS */
574 #ifdef NETATALK
575         case ETHERTYPE_AT:
576                 schednetisr(NETISR_ATALK);
577                 inq = &atintrq1;
578                 break;
579         case ETHERTYPE_AARP:
580                 /* probably this should be done with a NETISR as well */
581                 aarpinput(IFP2AC(ifp), m); /* XXX */
582                 return;
583 #endif NETATALK
584         default:
585 #ifdef IPX
586                 if (ef_inputp && ef_inputp(ifp, eh, m) == 0)
587                         return;
588 #endif /* IPX */
589 #ifdef NS
590                 checksum = mtod(m, ushort *);
591                 /* Novell 802.3 */
592                 if ((ether_type <= ETHERMTU) &&
593                         ((*checksum == 0xffff) || (*checksum == 0xE0E0))){
594                         if(*checksum == 0xE0E0) {
595                                 m->m_pkthdr.len -= 3;
596                                 m->m_len -= 3;
597                                 m->m_data += 3;
598                         }
599                                 schednetisr(NETISR_NS);
600                                 inq = &nsintrq;
601                                 break;
602                 }
603 #endif /* NS */
604 #if defined (ISO) || defined (LLC) || defined(NETATALK)
605                 if (ether_type > ETHERMTU)
606                         goto dropanyway;
607                 l = mtod(m, struct llc *);
608                 switch (l->llc_dsap) {
609 #ifdef NETATALK
610                 case LLC_SNAP_LSAP:
611                     switch (l->llc_control) {
612                     case LLC_UI:
613                         if (l->llc_ssap != LLC_SNAP_LSAP)
614                             goto dropanyway;
615         
616                         if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code,
617                                    sizeof(at_org_code)) == 0 &&
618                              ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) {
619                             inq = &atintrq2;
620                             m_adj( m, sizeof( struct llc ));
621                             schednetisr(NETISR_ATALK);
622                             break;
623                         }
624
625                         if (Bcmp(&(l->llc_snap_org_code)[0], aarp_org_code,
626                                    sizeof(aarp_org_code)) == 0 &&
627                              ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) {
628                             m_adj( m, sizeof( struct llc ));
629                             aarpinput(IFP2AC(ifp), m); /* XXX */
630                             return;
631                         }
632                 
633                     default:
634                         goto dropanyway;
635                     }
636                     break;
637 #endif NETATALK 
638 #ifdef  ISO
639                 case LLC_ISO_LSAP:
640                         switch (l->llc_control) {
641                         case LLC_UI:
642                                 /* LLC_UI_P forbidden in class 1 service */
643                                 if ((l->llc_dsap == LLC_ISO_LSAP) &&
644                                     (l->llc_ssap == LLC_ISO_LSAP)) {
645                                         /* LSAP for ISO */
646                                         if (m->m_pkthdr.len > ether_type)
647                                                 m_adj(m, ether_type - m->m_pkthdr.len);
648                                         m->m_data += 3;         /* XXX */
649                                         m->m_len -= 3;          /* XXX */
650                                         m->m_pkthdr.len -= 3;   /* XXX */
651                                         M_PREPEND(m, sizeof *eh, M_DONTWAIT);
652                                         if (m == 0)
653                                                 return;
654                                         *mtod(m, struct ether_header *) = *eh;
655                                         IFDEBUG(D_ETHER)
656                                                 printf("clnp packet");
657                                         ENDDEBUG
658                                         schednetisr(NETISR_ISO);
659                                         inq = &clnlintrq;
660                                         break;
661                                 }
662                                 goto dropanyway;
663
664                         case LLC_XID:
665                         case LLC_XID_P:
666                                 if(m->m_len < 6)
667                                         goto dropanyway;
668                                 l->llc_window = 0;
669                                 l->llc_fid = 9;
670                                 l->llc_class = 1;
671                                 l->llc_dsap = l->llc_ssap = 0;
672                                 /* Fall through to */
673                         case LLC_TEST:
674                         case LLC_TEST_P:
675                         {
676                                 struct sockaddr sa;
677                                 register struct ether_header *eh2;
678                                 int i;
679                                 u_char c = l->llc_dsap;
680
681                                 l->llc_dsap = l->llc_ssap;
682                                 l->llc_ssap = c;
683                                 if (m->m_flags & (M_BCAST | M_MCAST))
684                                         bcopy((caddr_t)ac->ac_enaddr,
685                                               (caddr_t)eh->ether_dhost, 6);
686                                 sa.sa_family = AF_UNSPEC;
687                                 sa.sa_len = sizeof(sa);
688                                 eh2 = (struct ether_header *)sa.sa_data;
689                                 for (i = 0; i < 6; i++) {
690                                         eh2->ether_shost[i] = c = eh->ether_dhost[i];
691                                         eh2->ether_dhost[i] =
692                                                 eh->ether_dhost[i] = eh->ether_shost[i];
693                                         eh->ether_shost[i] = c;
694                                 }
695                                 ifp->if_output(ifp, m, &sa, NULL);
696                                 return;
697                         }
698                         default:
699                                 m_freem(m);
700                                 return;
701                         }
702                         break;
703 #endif /* ISO */
704 #ifdef LLC
705                 case LLC_X25_LSAP:
706                 {
707                         if (m->m_pkthdr.len > ether_type)
708                                 m_adj(m, ether_type - m->m_pkthdr.len);
709                         M_PREPEND(m, sizeof(struct sdl_hdr) , M_DONTWAIT);
710                         if (m == 0)
711                                 return;
712                         if ( !sdl_sethdrif(ifp, eh->ether_shost, LLC_X25_LSAP,
713                                             eh->ether_dhost, LLC_X25_LSAP, 6,
714                                             mtod(m, struct sdl_hdr *)))
715                                 panic("ETHER cons addr failure");
716                         mtod(m, struct sdl_hdr *)->sdlhdr_len = ether_type;
717 #ifdef LLC_DEBUG
718                                 printf("llc packet\n");
719 #endif /* LLC_DEBUG */
720                         schednetisr(NETISR_CCITT);
721                         inq = &llcintrq;
722                         break;
723                 }
724 #endif /* LLC */
725                 dropanyway:
726                 default:
727 #ifdef  NETGRAPH
728                         ngether_send(IFP2AC(ifp), eh, m);
729 #else   /* NETGRAPH */
730                         m_freem(m);
731 #endif  /* NETGRAPH */
732                         return;
733                 }
734 #else /* ISO || LLC || NETATALK */
735 #ifdef  NETGRAPH
736             ngether_send(IFP2AC(ifp), eh, m);
737 #else   /* NETGRAPH */
738             m_freem(m);
739 #endif  /* NETGRAPH */
740             return;
741 #endif /* ISO || LLC || NETATALK */
742         }
743
744         s = splimp();
745         if (IF_QFULL(inq)) {
746                 IF_DROP(inq);
747                 m_freem(m);
748         } else
749                 IF_ENQUEUE(inq, m);
750         splx(s);
751 }
752
753 /*
754  * Perform common duties while attaching to interface list
755  */
756 void
757 ether_ifattach(ifp)
758         register struct ifnet *ifp;
759 {
760         register struct ifaddr *ifa;
761         register struct sockaddr_dl *sdl;
762
763         ifp->if_type = IFT_ETHER;
764         ifp->if_addrlen = 6;
765         ifp->if_hdrlen = 14;
766         ifp->if_mtu = ETHERMTU;
767         ifp->if_resolvemulti = ether_resolvemulti;
768         if (ifp->if_baudrate == 0)
769             ifp->if_baudrate = 10000000;
770         ifa = ifnet_addrs[ifp->if_index - 1];
771         if (ifa == 0) {
772                 printf("ether_ifattach: no lladdr!\n");
773                 return;
774         }
775         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
776         sdl->sdl_type = IFT_ETHER;
777         sdl->sdl_alen = ifp->if_addrlen;
778         bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
779 #ifdef  NETGRAPH
780         ngether_init(ifp);
781 #endif /* NETGRAPH */
782 #ifdef INET6
783         in6_ifattach_getifid(ifp);
784 #endif
785 }
786
787 SYSCTL_DECL(_net_link);
788 SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");
789
790 int
791 ether_ioctl(ifp, command, data)
792         struct ifnet *ifp;
793         int command;
794         caddr_t data;
795 {
796         struct ifaddr *ifa = (struct ifaddr *) data;
797         struct ifreq *ifr = (struct ifreq *) data;
798         int error = 0;
799
800         switch (command) {
801         case SIOCSIFADDR:
802                 ifp->if_flags |= IFF_UP;
803
804                 switch (ifa->ifa_addr->sa_family) {
805 #ifdef INET
806                 case AF_INET:
807                         ifp->if_init(ifp->if_softc);    /* before arpwhohas */
808                         arp_ifinit(IFP2AC(ifp), ifa);
809                         break;
810 #endif
811 #ifdef IPX
812                 /*
813                  * XXX - This code is probably wrong
814                  */
815                 case AF_IPX:
816                         {
817                         register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
818                         struct arpcom *ac = IFP2AC(ifp);
819
820                         if (ipx_nullhost(*ina))
821                                 ina->x_host =
822                                     *(union ipx_host *)
823                                     ac->ac_enaddr;
824                         else {
825                                 bcopy((caddr_t) ina->x_host.c_host,
826                                       (caddr_t) ac->ac_enaddr,
827                                       sizeof(ac->ac_enaddr));
828                         }
829
830                         /*
831                          * Set new address
832                          */
833                         ifp->if_init(ifp->if_softc);
834                         break;
835                         }
836 #endif
837 #ifdef NS
838                 /*
839                  * XXX - This code is probably wrong
840                  */
841                 case AF_NS:
842                 {
843                         register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
844                         struct arpcom *ac = IFP2AC(ifp);
845
846                         if (ns_nullhost(*ina))
847                                 ina->x_host =
848                                     *(union ns_host *) (ac->ac_enaddr);
849                         else {
850                                 bcopy((caddr_t) ina->x_host.c_host,
851                                       (caddr_t) ac->ac_enaddr,
852                                       sizeof(ac->ac_enaddr));
853                         }
854
855                         /*
856                          * Set new address
857                          */
858                         ifp->if_init(ifp->if_softc);
859                         break;
860                 }
861 #endif
862                 default:
863                         ifp->if_init(ifp->if_softc);
864                         break;
865                 }
866                 break;
867
868         case SIOCGIFADDR:
869                 {
870                         struct sockaddr *sa;
871
872                         sa = (struct sockaddr *) & ifr->ifr_data;
873                         bcopy(IFP2AC(ifp)->ac_enaddr,
874                               (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
875                 }
876                 break;
877
878         case SIOCSIFMTU:
879                 /*
880                  * Set the interface MTU.
881                  */
882                 if (ifr->ifr_mtu > ETHERMTU) {
883                         error = EINVAL;
884                 } else {
885                         ifp->if_mtu = ifr->ifr_mtu;
886                 }
887                 break;
888         }
889         return (error);
890 }
891
892 int
893 ether_resolvemulti(ifp, llsa, sa)
894         struct ifnet *ifp;
895         struct sockaddr **llsa;
896         struct sockaddr *sa;
897 {
898         struct sockaddr_dl *sdl;
899         struct sockaddr_in *sin;
900 #ifdef INET6
901         struct sockaddr_in6 *sin6;
902 #endif
903         u_char *e_addr;
904
905         switch(sa->sa_family) {
906         case AF_LINK:
907                 /*
908                  * No mapping needed. Just check that it's a valid MC address.
909                  */
910                 sdl = (struct sockaddr_dl *)sa;
911                 e_addr = LLADDR(sdl);
912                 if ((e_addr[0] & 1) != 1)
913                         return EADDRNOTAVAIL;
914                 *llsa = 0;
915                 return 0;
916
917 #ifdef INET
918         case AF_INET:
919                 sin = (struct sockaddr_in *)sa;
920                 if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
921                         return EADDRNOTAVAIL;
922                 MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
923                        M_WAITOK);
924                 sdl->sdl_len = sizeof *sdl;
925                 sdl->sdl_family = AF_LINK;
926                 sdl->sdl_index = ifp->if_index;
927                 sdl->sdl_type = IFT_ETHER;
928                 sdl->sdl_nlen = 0;
929                 sdl->sdl_alen = ETHER_ADDR_LEN;
930                 sdl->sdl_slen = 0;
931                 e_addr = LLADDR(sdl);
932                 ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
933                 *llsa = (struct sockaddr *)sdl;
934                 return 0;
935 #endif
936 #ifdef INET6
937         case AF_INET6:
938                 sin6 = (struct sockaddr_in6 *)sa;
939                 if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
940                         return EADDRNOTAVAIL;
941                 MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
942                        M_WAITOK);
943                 sdl->sdl_len = sizeof *sdl;
944                 sdl->sdl_family = AF_LINK;
945                 sdl->sdl_index = ifp->if_index;
946                 sdl->sdl_type = IFT_ETHER;
947                 sdl->sdl_nlen = 0;
948                 sdl->sdl_alen = ETHER_ADDR_LEN;
949                 sdl->sdl_slen = 0;
950                 e_addr = LLADDR(sdl);
951                 ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
952                 *llsa = (struct sockaddr *)sdl;
953                 return 0;
954 #endif
955
956         default:
957                 /*
958                  * Well, the text isn't quite right, but it's the name
959                  * that counts...
960                  */
961                 return EAFNOSUPPORT;
962         }
963 }
964
965 #ifdef  NETGRAPH
966
967 /***********************************************************************
968  * This section contains the methods for the Netgraph interface
969  ***********************************************************************/
970 /* It's Ascii-art time!
971  * The ifnet is the first part of the arpcom which must be
972  * the first part of the device's softc.. yuk.
973  *
974  *      +--------------------------+-----+---------+
975  *      |   struct ifnet (*ifp)    |     |         |
976  *      |                          |     |         |
977  *      +--------------------------+     |         |
978  *   +--|[ac_ng]     struct arpcom (*ac) |         |
979  *   |  +--------------------------------+         |
980  *   |  |   struct softc (*ifp->if_softc) (device) |
981  *   |  +------------------------------------------+
982  *   |               ^
983  * AC2NG()           |
984  *   |               v
985  *   |       +----------------------+
986  *   |       |   [private] [flags]  |
987  *   +------>| struct ng_node       |
988  *           |    [hooks]           | ** we only allow one hook
989  *           +----------------------+
990  *                   ^
991  *                   |
992  *                   v
993  *           +-------------+
994  *           |    [node]   |
995  *           |    hook     |
996  *           |    [private]|-- *unused*
997  *           +-------------+
998  */
999
1000 /*
1001  * called during interface attaching
1002  */
1003 static void
1004 ngether_init(void *ifpvoid)
1005 {
1006         struct  ifnet *ifp = ifpvoid;
1007         struct arpcom *ac = IFP2AC(ifp);
1008         static int      ngether_done_init;
1009         char    namebuf[32];
1010         node_p node;
1011
1012         /*
1013          * we have found a node, make sure our 'type' is availabe.
1014          */
1015         if (ngether_done_init == 0) {
1016                 if (ng_newtype(&typestruct)) {
1017                         printf("ngether install failed\n");
1018                         return;
1019                 }
1020                 ngether_done_init = 1;
1021         }
1022         if (ng_make_node_common(&typestruct, &node) != 0)
1023                 return;
1024         ac->ac_ng = node;
1025         node->private = ifp;
1026         sprintf(namebuf, "%s%d", ifp->if_name, ifp->if_unit);
1027         ng_name_node(AC2NG(ac), namebuf);
1028 }
1029
1030 /*
1031  * It is not possible or allowable to create a node of this type.
1032  * If the hardware exists, it will already have created it.
1033  */
1034 static  int
1035 ngether_constructor(node_p *nodep)
1036 {
1037         return (EINVAL);
1038 }
1039
1040 /*
1041  * Give our ok for a hook to be added...
1042  *
1043  * Allow one hook at a time (rawdata).
1044  * It can eiteh rdivert everything or only unclaimed packets.
1045  */
1046 static  int
1047 ngether_newhook(node_p node, hook_p hook, const char *name)
1048 {
1049
1050         /* check if there is already a hook */
1051         if (LIST_FIRST(&(node->hooks)))
1052                 return(EISCONN);
1053         /*
1054          * Check for which mode hook we want.
1055          */
1056         if (strcmp(name, NG_ETHER_HOOK_ORPHAN) != 0) {
1057                 if (strcmp(name, NG_ETHER_HOOK_DIVERT) != 0) {
1058                         return (EINVAL);
1059                 }
1060                 node->flags |= NGEF_DIVERT;
1061         } else {
1062                 node->flags &= ~NGEF_DIVERT;
1063         }
1064         return (0);
1065 }
1066
1067 /*
1068  * incoming messages.
1069  * Just respond to the generic TEXT_STATUS message
1070  */
1071 static  int
1072 ngether_rcvmsg(node_p node,
1073         struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp)
1074 {
1075         struct ifnet    *ifp;
1076         int error = 0;
1077
1078         ifp = node->private;
1079         switch (msg->header.typecookie) {
1080             case        NGM_ETHER_COOKIE:
1081                 error = EINVAL;
1082                 break;
1083             case        NGM_GENERIC_COOKIE:
1084                 switch(msg->header.cmd) {
1085                     case NGM_TEXT_STATUS: {
1086                             char        *arg;
1087                             int pos = 0;
1088                             int resplen = sizeof(struct ng_mesg) + 512;
1089                             MALLOC(*resp, struct ng_mesg *, resplen,
1090                                         M_NETGRAPH, M_NOWAIT);
1091                             if (*resp == NULL) {
1092                                 error = ENOMEM;
1093                                 break;
1094                             }
1095                             bzero(*resp, resplen);
1096                             arg = (*resp)->data;
1097
1098                             /*
1099                              * Put in the throughput information.
1100                              */
1101                             pos = sprintf(arg, "%ld bytes in, %ld bytes out\n",
1102                             ifp->if_ibytes, ifp->if_obytes);
1103                             pos += sprintf(arg + pos,
1104                                 "%ld output errors\n",
1105                                 ifp->if_oerrors);
1106                             pos += sprintf(arg + pos,
1107                                 "ierrors = %ld\n",
1108                                 ifp->if_ierrors);
1109
1110                             (*resp)->header.version = NG_VERSION;
1111                             (*resp)->header.arglen = strlen(arg) + 1;
1112                             (*resp)->header.token = msg->header.token;
1113                             (*resp)->header.typecookie = NGM_ETHER_COOKIE;
1114                             (*resp)->header.cmd = msg->header.cmd;
1115                             strncpy((*resp)->header.cmdstr, "status",
1116                                         NG_CMDSTRLEN);
1117                         }
1118                         break;
1119                     default:
1120                         error = EINVAL;
1121                         break;
1122                     }
1123                 break;
1124             default:
1125                 error = EINVAL;
1126                 break;
1127         }
1128         free(msg, M_NETGRAPH);
1129         return (error);
1130 }
1131
1132 /*
1133  * Receive a completed ethernet packet.
1134  * Queue it for output.
1135  */
1136 static  int
1137 ngether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
1138 {
1139         struct ifnet *ifp;
1140         int     error = 0;
1141         int     s;
1142         struct ether_header *eh;
1143         
1144         ifp = hook->node->private;
1145
1146         if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
1147                 senderr(ENETDOWN);
1148         /* drop in the MAC address */
1149         eh = mtod(m, struct ether_header *);
1150         bcopy(IFP2AC(ifp)->ac_enaddr, eh->ether_shost, 6);
1151         /*
1152          * If a simplex interface, and the packet is being sent to our
1153          * Ethernet address or a broadcast address, loopback a copy.
1154          * XXX To make a simplex device behave exactly like a duplex
1155          * device, we should copy in the case of sending to our own
1156          * ethernet address (thus letting the original actually appear
1157          * on the wire). However, we don't do that here for security
1158          * reasons and compatibility with the original behavior.
1159          */
1160         if (ifp->if_flags & IFF_SIMPLEX) {
1161                 if (m->m_flags & M_BCAST) {
1162                         struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
1163
1164                         ng_queue_data(hook, n, meta);
1165                 } else if (bcmp(eh->ether_dhost,
1166                     eh->ether_shost, ETHER_ADDR_LEN) == 0) {
1167                         ng_queue_data(hook, m, meta);
1168                         return (0);     /* XXX */
1169                 }
1170         }
1171         s = splimp();
1172         /*
1173          * Queue message on interface, and start output if interface
1174          * not yet active.
1175          * XXX if we lookead at the priority in the meta data we could
1176          * queue high priority items at the head.
1177          */
1178         if (IF_QFULL(&ifp->if_snd)) {
1179                 IF_DROP(&ifp->if_snd);
1180                 splx(s);
1181                 senderr(ENOBUFS);
1182         }
1183         IF_ENQUEUE(&ifp->if_snd, m);
1184         if ((ifp->if_flags & IFF_OACTIVE) == 0)
1185                 (*ifp->if_start)(ifp);
1186         splx(s);
1187         ifp->if_obytes += m->m_pkthdr.len;
1188         if (m->m_flags & M_MCAST)
1189                 ifp->if_omcasts++;
1190         return (error);
1191
1192 bad:
1193         NG_FREE_DATA(m, meta);
1194         return (error);
1195 }
1196
1197 /*
1198  * pass an mbuf out to the connected hook
1199  * More complicated than just an m_prepend, as it tries to save later nodes
1200  * from needing to do lots of m_pullups.
1201  */     
1202 static void
1203 ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m)
1204 {
1205         int room;
1206         node_p node = AC2NG(ac);
1207         struct ether_header *eh2;
1208                 
1209         if (node && LIST_FIRST(&(node->hooks))) {
1210                 /*
1211                  * Possibly the header is already on the front,
1212                  */
1213                 eh2 = mtod(m, struct ether_header *) - 1;
1214                 if ( eh == eh2) {
1215                         /*
1216                          * This is the case so just move the markers back to
1217                          * re-include it. We lucked out.
1218                          * This allows us to avoid a yucky m_pullup
1219                          * in later nodes if it works.
1220                          */
1221                         m->m_len += sizeof(*eh);
1222                         m->m_data -= sizeof(*eh);
1223                         m->m_pkthdr.len += sizeof(*eh);
1224                 } else {
1225                         /*
1226                          * Alternatively there may be room even though
1227                          * it is stored somewhere else. If so, copy it in.
1228                          * This only safe because we KNOW that this packet has
1229                          * just been generated by an ethernet card, so there
1230                          * are no aliases to the buffer. (unlike in outgoing
1231                          * packets).
1232                          * Nearly all ethernet cards will end up producing mbufs
1233                          * that fall into these cases. So we are not optimising
1234                          * contorted cases.
1235                          */
1236         
1237                         if (m->m_flags & M_EXT) {
1238                                 room = (mtod(m, caddr_t) - m->m_ext.ext_buf);
1239                                 if (room > m->m_ext.ext_size) /* garbage */
1240                                         room = 0; /* fail immediatly */
1241                         } else {
1242                                 room = (mtod(m, caddr_t) - m->m_pktdat);
1243                         }
1244                         if (room > sizeof (*eh)) {
1245                                 /* we have room, just copy it and adjust */
1246                                 m->m_len += sizeof(*eh);
1247                                 m->m_data -= sizeof(*eh);
1248                                 m->m_pkthdr.len += sizeof(*eh);
1249                         } else {
1250                                 /*
1251                                  * Doing anything more is likely to get more
1252                                  * expensive than it's worth..
1253                                  * it's probable that everything else is in one
1254                                  * big lump. The next node will do an m_pullup()
1255                                  * for exactly the amount of data it needs and
1256                                  * hopefully everything after that will not
1257                                  * need one. So let's just use M_PREPEND.
1258                                  */
1259                                 M_PREPEND(m, sizeof (*eh), M_DONTWAIT);
1260                                 if (m == NULL)
1261                                         return;
1262                         }
1263                         bcopy ((caddr_t)eh, mtod(m, struct ether_header *),
1264                             sizeof(*eh));
1265                 }
1266                 ng_queue_data(LIST_FIRST(&(node->hooks)), m, NULL);
1267         } else {
1268                 m_freem(m);
1269         }
1270 }
1271
1272 /*
1273  * do local shutdown processing..
1274  * This node will refuse to go away, unless the hardware says to..
1275  * don't unref the node, or remove our name. just clear our links up.
1276  */
1277 static  int
1278 ngether_rmnode(node_p node)
1279 {
1280         ng_cutlinks(node);
1281         node->flags &= ~NG_INVALID; /* bounce back to life */
1282         return (0);
1283 }
1284
1285 /* already linked */
1286 static  int
1287 ngether_connect(hook_p hook)
1288 {
1289         /* be really amiable and just say "YUP that's OK by me! " */
1290         return (0);
1291 }
1292
1293 /*
1294  * notify on hook disconnection (destruction)
1295  *
1296  * For this type, removal of the last lins no effect. The interface can run
1297  * independently.
1298  * Since we have no per-hook information, this is rather simple.
1299  */
1300 static  int
1301 ngether_disconnect(hook_p hook)
1302 {
1303         hook->node->flags &= ~NGEF_DIVERT;
1304         return (0);
1305 }
1306 #endif /* NETGRAPH */
1307
1308 /********************************** END *************************************/