/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* Copyright (c) 1997, 1998 Apple Computer, Inc. All Rights Reserved */ /* * @(#)if_blue.c 1.1 (MacOSX) 6/10/43 * Justin Walker, 9970520 * First wave - splitter and notification support for the Blue Box * 980130 - Second wave - Performance improvements, reorg and cleanup */ #include #if KDEBUG #define DBG_SPLT_BFCHK DRVDBG_CODE(DBG_DRVSPLT, 0) #define DBG_SPLT_APPND DRVDBG_CODE(DBG_DRVSPLT, 1) #define DBG_SPLT_MBUF DRVDBG_CODE(DBG_DRVSPLT, 2) #define DBG_SPLT_DUP DRVDBG_CODE(DBG_DRVSPLT, 3) #define DBG_SPLT_PAD DRVDBG_CODE(DBG_DRVSPLT, 4) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "if_blue.h" #include "ndrv.h" #if INET #include #include #endif #include #if NS #include #include #endif #if ISO #include #include #include #include #endif #if LLC #include #include #endif #include #include #include #include /* Dummy IFs to differentiate source of looped packets */ struct ifnet rhap_if_s; struct ifnet *rhap_if = &rhap_if_s; struct ifnet_blue *blue_if; struct sockaddr_dl ndrvsrc = {sizeof (struct sockaddr_dl), AF_NDRV}; struct ifqueue blueq; extern int if_register(register struct BlueFilter *f #ifdef BF_if , register struct ifnet *ifp #endif ); /* * Blue Box support: * 1st cut: the Y splitter * A process turns on the splitter by opening the "raw" device * (socket() for AF_NDRV) and issuing an SIOCSSPLITTER ioctl. * Incoming packets are routed into MacOSX as well as to the requesting * interface. * Outbound packets are sent, and are examined to see if they should go * back up (loopback, sort of). Packets that are looped back include: * broadcast * multicast */ int new_splitter(register struct socket *so) { register struct ifnet_blue *ifb; register struct ndrv_cb *np; register struct ifnet *ifp; struct BlueFilter filter; int retval; if ((ifb = _MALLOC(sizeof (struct ifnet_blue), M_PCB, M_WAITOK)) == NULL) { #if BLUE_DEBUG kprintf("Can't create new splitter\n"); #endif return(ENOBUFS); } bzero(ifb, sizeof(struct ifnet_blue)); np = (struct ndrv_cb *)so->so_pcb; #if BLUE_DEBUG kprintf("NEW SPLT: %x, %x\n", so, np); if (np) printf("SIG: %x, ifp: %x\n", np->nd_signature, np->nd_if); #endif if (np == NULL) return(EINVAL); /* XXX */ if (np->nd_signature != NDRV_SIGNATURE) return(EINVAL); /* XXX */ if ((ifp = np->nd_if) == NULL) return(EINVAL); /* XXX */ if (ifp->if_flags & IFF_SPLITTER) return(EBUSY); if ((ifp->if_flags&IFF_UP) == 0) return(ENXIO); /* * Bump the receive sockbuf size - need a big buffer * to offset the scheduling latencies of the system * Try to get something if our grandiose design fails. */ if (sbreserve(&so->so_rcv, 131072) == 0) { if (sbreserve(&so->so_rcv, 65536) == 0 && sbreserve(&so->so_rcv, 32768) == 0 && sbreserve(&so->so_rcv, 16384) == 0) return(ENOBUFS); } ifp->if_flags |= IFF_SPLITTER; /* * Register each IP address associated with this ifnet * This takes care of addresses registered prior to startup * of the BlueBox. * TODO: Appletalk sockaddrs */ #define IFA2IN(ifa) \ ((struct in_addr) \ ((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr).s_addr { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET) { filter.BF_flags = (BF_ALLOC|BF_IP); filter.BF_address = IFA2IN(ifa); #if BLUE_DEBUG kprintf("[1] IP registering [%x] %x\n", filter.BF_flags, (unsigned int)filter.BF_address); #endif retval = if_register(&filter); #if BLUE_DEBUG if (retval) kprintf("if_register(IP) returns %d\n", retval); #endif } } } blue_if = (struct ifnet_blue *)ifb; ifb->blue_pid = ((struct proc *)current_proc())->p_pid; ifb->ifb_so = so; ifp->if_Y = (void *)ifb; return(0); } /* * Determine if destined for BlueBox or not. Called from ether_output() * and ether_input(). * Returns NULL if we ate the packet, otherwise, the mbuf to continue with. */ struct mbuf * splitter_input(register struct mbuf *m, register struct ifnet *ifp) { register struct ifnet_blue *ifb; #if 0 register int s, flags; #else register int flags; #endif int rv; register struct mbuf *m0 = NULL; struct mbuf *m1; extern struct mbuf *m_dup(struct mbuf *, int); extern int BlueFilter_check(struct mbuf **, struct ifnet_blue *); extern void blue_notify(struct mbuf *); extern int blue_notify1(struct mbuf *); if ((ifb = (struct ifnet_blue *)ifp->if_Y) == NULL) { ifp->if_flags &= ~IFF_SPLITTER; return(m); } flags = m->m_flags; m1 = m; /* Check filters */ if ((rv = BlueFilter_check(&m1, ifb)) == -1) return(m1); /* Not for BB, MacOSX will want to see it. */ m = m1; if (rv == 0) /* It's for both - dup the packet */ { m0 = m_dup(m, M_DONTWAIT); if (m0 == NULL) { blue_if->no_bufs1++; return(m); /* Give it to MacOSX */ } } else { /* Oy, veh! The depths to which we stoop! */ /* We'll just assume M_PKTHDR is set */ if (m->m_next == 0 && (m->m_flags & M_EXT) && m->m_pkthdr.len <= MHLEN) { m0 = m_dup(m, M_DONTWAIT); if (m0) { m_freem(m); m = NULL; } else m0 = m; } else m0 = m; } if (flags & 0x10) blue_if->pkts_looped_r2b++; #if 0 schednetisr(NETISR_BLUE); s = splimp(); if (IF_QFULL(&blueq)) { IF_DROP(&blueq); m_freem(m0); } else IF_ENQUEUE(&blueq, m0); splx(s); #else blue_notify1(m0); sorwakeup(blue_if->ifb_so); blue_if->sig_sent++; #endif /* If we eat the packet (rv==1) return NULL */ return(rv == 0 ? m : NULL); } void blue_notify() { register int do_notify = 0; register int s; register struct mbuf *m; extern int blue_notify1(struct mbuf *); /* * Move the packets from the blue queue to the indicated socket * If we haven't told anyone yet, send a signal. */ for (;;) { s = splimp(); IF_DEQUEUE(&blueq, m); splx(s); if (m == 0) break; do_notify = blue_notify1(m); } if (do_notify) sorwakeup(blue_if->ifb_so); /* Start by using SIGIO */ } int blue_notify1(register struct mbuf *m) { register int rv; /* move packet from if queue to socket */ /* !!!Fix this to work generically!!! */ ndrvsrc.sdl_type = IFT_ETHER; ndrvsrc.sdl_nlen = 0; ndrvsrc.sdl_alen = 6; ndrvsrc.sdl_slen = 0; bcopy(m->m_data+6, &ndrvsrc.sdl_data, 6); if (sbappendaddr(&(blue_if->ifb_so->so_rcv), (struct sockaddr *)&ndrvsrc, m, (struct mbuf *)0) == 0) { register struct mbuf *n; KERNEL_DEBUG(DBG_SPLT_APPND | DBG_FUNC_NONE, blue_if->ifb_so->so_rcv.sb_cc, blue_if->ifb_so->so_rcv.sb_hiwat, blue_if->ifb_so->so_rcv.sb_mbcnt, blue_if->ifb_so->so_rcv.sb_mbmax, blue_if->ifb_so->so_rcv.sb_lowat ); if (m->m_flags & M_PKTHDR) KERNEL_DEBUG(DBG_SPLT_MBUF, 0, m->m_pkthdr.len, m->m_flags, 0, 0); for (n = m; n; n = n->m_next) KERNEL_DEBUG(DBG_SPLT_MBUF, 1, (int)n, (int)n->m_next, n->m_len, n->m_flags); m_freem(m); blue_if->full_sockbuf++; rv = 1; } else { register struct mbuf *n; KERNEL_DEBUG(DBG_SPLT_APPND | DBG_FUNC_NONE, blue_if->ifb_so->so_rcv.sb_cc, blue_if->ifb_so->so_rcv.sb_hiwat, blue_if->ifb_so->so_rcv.sb_mbcnt, blue_if->ifb_so->so_rcv.sb_mbmax, blue_if->ifb_so->so_rcv.sb_lowat ); if (m->m_flags & M_PKTHDR) KERNEL_DEBUG(DBG_SPLT_MBUF, 2, m->m_pkthdr.len, m->m_flags, 0, 0); for (n = m; n; n = n->m_next) KERNEL_DEBUG(DBG_SPLT_MBUF, 3, (int)n, (int)n->m_next, n->m_len, n->m_flags); blue_if->pkts_up++; rv = 0; } return(rv); } /* * Check the incoming packet against the registered filters * Rules (the rules are subtly different for input to the * y-adapter customer and the "real" stacks): * For BB: return 1 * For Both: return 0 * Not For BB: return -1 * Multicast/Broadcast => For Both * Hack: * if no registered filters, For Both * Atalk filter registered * filter matches => For BB else Not For BB * IP filter registered * filter matches => For BB else Not For BB * Not For BB * WARNING: this is a big-endian routine. * WARNING 2: m_pullup can give you a new mbuf! */ int BlueFilter_check(struct mbuf **m0, register struct ifnet_blue *ifb) { register struct BlueFilter *bf; register unsigned char *p; register unsigned short *s; register unsigned long *l; int total, flags; register struct mbuf *m; extern struct mbuf *m_pullup(struct mbuf *, int); #define FILTER_LEN 32 KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_START, 0, 0, 0, 0, 0 ); m = *m0; if (FILTER_LEN > m->m_pkthdr.len) { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 0, 0, 0, 0, 0 ); return(-1); } flags = m->m_flags; while ((FILTER_LEN > m->m_len) && m->m_next) { total = m->m_len + (m->m_next)->m_len; if ((m = m_pullup(m, min(FILTER_LEN, total))) == 0) { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 1, flags, total, 0, 0); return(-1); } } *m0 = m; /* Update, just in case */ p = mtod(m, unsigned char *); /* Point to destination media addr */ if (p[0] & 0x01) /* Multicast/broadcast */ { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 2, 0, 0, 0, 0 ); return(0); } s = (unsigned short *)p; bf = &ifb->filter[BFS_ATALK]; if (!bf->BF_flags && !bf[1].BF_flags) /* Hack for Developer Release Blue Box */ { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 3, 0, 0, 0, 0 ); return(0); } #if BLUE_DEBUG kprintf("PKT: %x, %x, %x\n", s[6], s[7], s[8]); #endif if (bf->BF_flags) /* Filtering Appletalk */ { l = (unsigned long *)&s[8]; #if BLUE_DEBUG kprintf("AT: %x, %x, %x, %x, %x, %x\n", s[6], s[7], *l, s[10], s[13], p[30]); #endif if (s[6] <= ETHERMTU) { if (s[7] == 0xaaaa) /* Could be Atalk */ { /* Verify SNAP header */ if (*l == 0x03080007 && s[10] == 0x809b) { if ((bf->BF_flags&BF_VALID) == 0 || (s[13] == bf->BF_address && p[30] == bf->BF_node)) { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 4, s[13], p[30], 0, 0 ); return(1); } } else if (*l == 0x03000000 && s[10] == 0x80f3) /* AARP pkts aren't net-addressed */ { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 5, 0, 0, 0, 0 ); return(0); } /* Not for us */ KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 6, s[13], p[30], 0, 0 ); return(-1); } else /* Not for us? */ { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 7, s[7], 0, 0, 0 ); return(-1); } } /* Fall through */ } /* Fall through */ bf++; /* Look for IP next */ if (bf->BF_flags) /* Filtering IP */ { l = (unsigned long *)&s[15]; #if BLUE_DEBUG kprintf("IP: %x, %x\n", s[6], *l); #endif if (s[6] > ETHERMTU) { if (s[6] == 0x800) /* Is IP */ { /* Verify IP address */ if ((bf->BF_flags&BF_VALID) == 0 || *l == bf->BF_address) { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 8, *l, 0, 0, 0 ); return(1); } else /* Not for us */ { KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 9, *l, 0, 0, 0 ); return(-1); } } else if (s[6] == 0x806) { /* ARP pkts aren't net-addressed */ KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 10, 0, 0, 0, 0 ); return(0); } } } KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 11, s[6], 0, 0, 0 ); return(-1); } int splitter_ctl(register struct socket *so, register int cmd, register caddr_t data, register struct ifnet *ifp) { register struct ndrv_cb *np = sotondrvcb(so); register struct ifnet_blue *ifb; register struct BlueFilter *bf = (struct BlueFilter *)data, *bf1; u_long at_dl_tag; if ((ifb = np->nd_if->if_Y) == NULL) return(ENXIO); if (cmd == SIOCSSPLTFILT) { #if BLUE_DEBUG kprintf("Filter: %s, %x, %x, %x\n", bf->ifr_name, bf->BF_flags, bf->BF_address, bf->BF_node); #endif if (bf->BF_flags & BF_ATALK) bf1 = &ifb->filter[BFS_ATALK]; else if (bf->BF_flags & BF_IP) bf1 = &ifb->filter[BFS_IP]; else return(EINVAL); if (bf->BF_flags&BF_ALLOC) { if ((bf1->BF_flags&(BF_ALLOC|BF_VALID)) == (BF_ALLOC|BF_VALID)) return(EBUSY); *bf1 = *bf; bf1->BF_flags |= BF_VALID; } else if (bf->BF_flags&BF_DEALLOC) { if (bf1->BF_flags&BF_ALLOC) bf1->BF_flags = 0; else return(EINVAL); } /* Register AppleTalk Tags if not registered */ ether_attach_at(ifp, &at_dl_tag, &at_dl_tag); } else if (cmd == SIOCZSPLTSTAT) { ifb->pkts_up = 0; ifb->pkts_out = 0; ifb->pkts_looped_r2b = 0; ifb->pkts_looped_b2r = 0; ifb->no_bufs1 = 0; ifb->no_bufs2 = 0; ifb->full_sockbuf = 0; } else if (cmd == SIOCGSPLTSTAT) { register struct Ystats *ys = (struct Ystats *)data; ys->YS_blue_pid = ifb->blue_pid; ys->YS_filter[BFS_ATALK] = ifb->filter[BFS_ATALK]; ys->YS_filter[BFS_IP] = ifb->filter[BFS_IP]; ys->YS_pkts_up = ifb->pkts_up; ys->YS_pkts_out = ifb->pkts_out; ys->YS_pkts_looped_b2r = ifb->pkts_looped_b2r; ys->YS_pkts_looped_r2b = ifb->pkts_looped_r2b; ys->YS_no_bufs1 = ifb->no_bufs1; ys->YS_no_bufs2 = ifb->no_bufs2; ys->YS_full_sockbuf = ifb->full_sockbuf; } else return(EINVAL); return(0); } void splitter_close(register struct ndrv_cb *np) { extern struct ifnet_blue *blue_if; extern void ndrv_flushq(struct ifqueue *); if (blue_if) { /* If we're the guy holding the Y-adapter, clean it up */ if (blue_if->blue_pid == ((struct proc *)current_proc())->p_pid) { if (np->nd_if) { np->nd_if->if_flags &= ~IFF_SPLITTER; np->nd_if->if_Y = 0; } BFIx = 0; /* Clean out the filter supply */ bzero(RhapFilter, sizeof(struct BlueFilter) * BFCount); blue_if->ifb_so = 0; blue_if->filter[0].BF_flags = 0; blue_if->filter[1].BF_flags = 0; ndrv_flushq(&blueq); if (np->nd_laddr) { FREE((caddr_t) np->nd_laddr, M_IFADDR); np->nd_laddr = 0; } } } remque((queue_t)np); FREE((caddr_t)np, M_PCB); } /* * Dup the mbuf chain passed in. The whole thing. No cute additional cruft. * And really copy the thing. That way, we don't "precompute" checksums * for unsuspecting consumers. * Assumption: m->m_nextpkt == 0. * Trick: for small packets, don't dup into a cluster. That way received * packets don't take up too much room in the sockbuf (cf. sbspace()). */ int MDFail; struct mbuf * m_dup(register struct mbuf *m, int how) { register struct mbuf *n, **np; struct mbuf *top; int copyhdr = 0; KERNEL_DEBUG(DBG_SPLT_DUP | DBG_FUNC_START, m->m_flags, m->m_len, m->m_pkthdr.len, 0, 0 ); np = ⊤ top = 0; if (m->m_flags & M_PKTHDR) copyhdr = 1; /* * Quick check: if we have one mbuf and its data fits in an * mbuf with packet header, just copy and go. */ if (m->m_next == NULL) { /* Then just move the data into an mbuf and be done... */ if (copyhdr) { if (m->m_pkthdr.len <= MHLEN) { if ((n = m_gethdr(how, m->m_type)) == NULL) return(NULL); bcopy(m->m_data, n->m_data, m->m_pkthdr.len); n->m_pkthdr.len = m->m_pkthdr.len; n->m_len = m->m_len; KERNEL_DEBUG(DBG_SPLT_DUP | DBG_FUNC_END, 2, m->m_pkthdr.len, m->m_flags, n->m_flags, 0 ); return(n); } } else if (m->m_len <= MLEN) { if ((n = m_get(how, m->m_type)) == NULL) return(NULL); bcopy(m->m_data, n->m_data, m->m_len); n->m_len = m->m_len; KERNEL_DEBUG(DBG_SPLT_DUP | DBG_FUNC_END, 3, m->m_len, m->m_flags, n->m_flags, 0 ); return(n); } } while (m) { #if BLUE_DEBUG kprintf("<%x: %x, %x, %x\n", m, m->m_flags, m->m_len, m->m_data); #endif if (copyhdr) n = m_gethdr(how, m->m_type); else n = m_get(how, m->m_type); if (n == 0) goto nospace; if (m->m_flags & M_EXT) { MCLGET(n, how); if ((n->m_flags & M_EXT) == 0) goto nospace; } *np = n; if (copyhdr) { /* Don't use M_COPY_PKTHDR: preserve m_data */ n->m_pkthdr = m->m_pkthdr; n->m_pkthdr.aux = (struct mbuf *)NULL; /*###LD080800 Avoid problems with IPsec */ n->m_flags |= (m->m_flags & M_COPYFLAGS); copyhdr = 0; if ((n->m_flags & M_EXT) == 0) n->m_data = n->m_pktdat; } n->m_len = m->m_len; /* * Get the dup on the same bdry as the original * Assume that the two mbufs have the same offset to data area * (up to word bdries) */ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), (unsigned)n->m_len); m = m->m_next; np = &n->m_next; #if BLUE_DEBUG kprintf(">%x: %x, %x, %x\n", n, n->m_flags, n->m_len, n->m_data); #endif } if (top == 0) MDFail++; KERNEL_DEBUG(DBG_SPLT_DUP | DBG_FUNC_END, 0, (int)top, 0, 0, 0 ); return (top); nospace: m_freem(top); MDFail++; KERNEL_DEBUG(DBG_SPLT_DUP | DBG_FUNC_END, 1, 0, 0, 0, 0 ); return (0); }