#include <sys/kdebug.h>
#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 <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/vm.h>
#include <kern/cpu_number.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/if_llc.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include "if_blue.h"
#include "ndrv.h"
#if INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#endif
#include <netinet/if_ether.h>
#if NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#if ISO
#include <netiso/argo_debug.h>
#include <netiso/iso.h>
#include <netiso/iso_var.h>
#include <netiso/iso_snpac.h>
#endif
#if LLC
#include <netccitt/dll.h>
#include <netccitt/llc_var.h>
#endif
#include <sys/systm.h>
#include <machine/spl.h>
#include <kern/thread.h>
#include <kern/queue.h>
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
);
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);
if (np->nd_signature != NDRV_SIGNATURE)
return(EINVAL);
if ((ifp = np->nd_if) == NULL)
return(EINVAL);
if (ifp->if_flags & IFF_SPLITTER)
return(EBUSY);
if ((ifp->if_flags&IFF_UP) == 0)
return(ENXIO);
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;
#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);
}
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;
if ((rv = BlueFilter_check(&m1, ifb)) == -1)
return(m1);
m = m1;
if (rv == 0)
{ m0 = m_dup(m, M_DONTWAIT);
if (m0 == NULL)
{ blue_if->no_bufs1++;
return(m);
}
} else
{
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
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 *);
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);
}
int
blue_notify1(register struct mbuf *m)
{ register int rv;
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);
}
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;
p = mtod(m, unsigned char *);
if (p[0] & 0x01)
{ 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)
{ 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)
{ 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)
{
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)
{ KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 5, 0, 0, 0, 0 );
return(0);
}
KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 6, s[13], p[30], 0, 0 );
return(-1);
} else
{ KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 7, s[7], 0, 0, 0 );
return(-1);
}
}
}
bf++;
if (bf->BF_flags)
{
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)
{
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
{ KERNEL_DEBUG(DBG_SPLT_BFCHK | DBG_FUNC_END, 9, *l, 0, 0, 0 );
return(-1);
}
} else if (s[6] == 0x806)
{
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);
}
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 (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;
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);
}
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;
if (m->m_next == NULL)
{
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)
{
n->m_pkthdr = m->m_pkthdr;
n->m_pkthdr.aux = (struct mbuf *)NULL;
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;
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);
}