diff options
Diffstat (limited to 'solaris.c')
-rw-r--r-- | solaris.c | 2131 |
1 files changed, 2131 insertions, 0 deletions
diff --git a/solaris.c b/solaris.c new file mode 100644 index 0000000..aa139d3 --- /dev/null +++ b/solaris.c @@ -0,0 +1,2131 @@ +/* + * Copyright (C) 1993-2002 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +/* #pragma ident "@(#)solaris.c 1.12 6/5/96 (C) 1995 Darren Reed"*/ +#pragma ident "@(#)$Id: solaris.c,v 2.15.2.30 2002/04/23 14:57:51 darrenr Exp $" + +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/uio.h> +#include <sys/buf.h> +#include <sys/modctl.h> +#include <sys/open.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/stat.h> +#include <sys/cred.h> +#include <sys/dditypes.h> +#include <sys/stream.h> +#include <sys/poll.h> +#include <sys/autoconf.h> +#include <sys/byteorder.h> +#include <sys/socket.h> +#include <sys/dlpi.h> +#include <sys/stropts.h> +#include <sys/sockio.h> +#include <net/if.h> +#if SOLARIS2 >= 6 +# include <net/if_types.h> +#endif +#include <net/af.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include "ip_compat.h" +#include "ipl.h" +#include "ip_fil.h" +#include "ip_nat.h" +#include "ip_state.h" + + +char _depends_on[] = "drv/ip"; + + +void solipdrvattach __P((void)); +int solipdrvdetach __P((void)); + +void solattach __P((void)); +int soldetach __P((void)); + +extern struct filterstats frstats[]; +extern KRWLOCK_T ipf_mutex, ipfs_mutex, ipf_nat, ipf_solaris; +extern kmutex_t ipf_rw; +extern int fr_running; +extern int fr_flags; + +extern ipnat_t *nat_list; + +static qif_t *qif_head = NULL; +static int ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t, + void *, void **)); +static int ipf_probe __P((dev_info_t *)); +static int ipf_identify __P((dev_info_t *)); +static int ipf_attach __P((dev_info_t *, ddi_attach_cmd_t)); +static int ipf_detach __P((dev_info_t *, ddi_detach_cmd_t)); +static qif_t *qif_from_queue __P((queue_t *)); +static void fr_donotip __P((int, qif_t *, queue_t *, mblk_t *, + mblk_t *, ip_t *, size_t)); +static char *ipf_devfiles[] = { IPL_NAME, IPL_NAT, IPL_STATE, IPL_AUTH, + NULL }; +static int (*ipf_ip_inp) __P((queue_t *, mblk_t *)) = NULL; + + +#if SOLARIS2 >= 7 +extern void ipfr_slowtimer __P((void *)); +timeout_id_t ipfr_timer_id; +static timeout_id_t synctimeoutid = 0; +#else +extern void ipfr_slowtimer __P((void)); +int ipfr_timer_id; +static int synctimeoutid = 0; +#endif +int ipf_debug = 0; +int ipf_debug_verbose = 0; + +/* #undef IPFDEBUG 1 */ +/* #undef IPFDEBUG_VERBOSE 1 */ +#ifdef IPFDEBUG +void printire __P((ire_t *)); +#endif +#define isdigit(x) ((x) >= '0' && (x) <= '9') + +static int fr_precheck __P((mblk_t **, queue_t *, qif_t *, int)); + + +static struct cb_ops ipf_cb_ops = { + iplopen, + iplclose, + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + iplread, + nodev, /* write */ + iplioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* poll */ + ddi_prop_op, + NULL, + D_MTSAFE, +#if SOLARIS2 > 4 + CB_REV, + nodev, /* aread */ + nodev, /* awrite */ +#endif +}; + +static struct dev_ops ipf_ops = { + DEVO_REV, + 0, + ipf_getinfo, + ipf_identify, + ipf_probe, + ipf_attach, + ipf_detach, + nodev, /* reset */ + &ipf_cb_ops, + (struct bus_ops *)0 +}; + +extern struct mod_ops mod_driverops; +static struct modldrv iplmod = { + &mod_driverops, IPL_VERSION, &ipf_ops }; +static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL }; + +#if SOLARIS2 >= 6 +static size_t hdrsizes[57][2] = { + { 0, 0 }, + { IFT_OTHER, 0 }, + { IFT_1822, 14 }, /* 14 for ire0 ?? */ + { IFT_HDH1822, 0 }, + { IFT_X25DDN, 0 }, + { IFT_X25, 0 }, + { IFT_ETHER, 14 }, + { IFT_ISO88023, 14 }, + { IFT_ISO88024, 0 }, + { IFT_ISO88025, 0 }, + { IFT_ISO88026, 0 }, + { IFT_STARLAN, 0 }, + { IFT_P10, 0 }, + { IFT_P80, 0 }, + { IFT_HY, 0 }, + { IFT_FDDI, 24 }, + { IFT_LAPB, 0 }, + { IFT_SDLC, 0 }, + { IFT_T1, 0 }, + { IFT_CEPT, 0 }, + { IFT_ISDNBASIC, 0 }, + { IFT_ISDNPRIMARY, 0 }, + { IFT_PTPSERIAL, 0 }, + { IFT_PPP, 0 }, + { IFT_LOOP, 0 }, + { IFT_EON, 0 }, + { IFT_XETHER, 0 }, + { IFT_NSIP, 0 }, + { IFT_SLIP, 0 }, + { IFT_ULTRA, 0 }, + { IFT_DS3, 0 }, + { IFT_SIP, 0 }, + { IFT_FRELAY, 0 }, + { IFT_RS232, 0 }, + { IFT_PARA, 0 }, + { IFT_ARCNET, 0 }, + { IFT_ARCNETPLUS, 0 }, + { IFT_ATM, 0 }, + { IFT_MIOX25, 0 }, + { IFT_SONET, 0 }, + { IFT_X25PLE, 0 }, + { IFT_ISO88022LLC, 0 }, + { IFT_LOCALTALK, 0 }, + { IFT_SMDSDXI, 0 }, + { IFT_FRELAYDCE, 0 }, + { IFT_V35, 0 }, + { IFT_HSSI, 0 }, + { IFT_HIPPI, 0 }, + { IFT_MODEM, 0 }, + { IFT_AAL5, 0 }, + { IFT_SONETPATH, 0 }, + { IFT_SONETVT, 0 }, + { IFT_SMDSICIP, 0 }, + { IFT_PROPVIRTUAL, 0 }, + { IFT_PROPMUX, 0 }, +}; +#endif /* SOLARIS2 >= 6 */ + +static dev_info_t *ipf_dev_info = NULL; + + +int _init() +{ + int ipfinst; + + ipfinst = mod_install(&modlink1); +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst); +#endif + return ipfinst; +} + + +int _fini(void) +{ + int ipfinst; + + ipfinst = mod_remove(&modlink1); +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: _fini() = %d", ipfinst); +#endif + return ipfinst; +} + + +int _info(modinfop) +struct modinfo *modinfop; +{ + int ipfinst; + + ipfinst = mod_info(&modlink1, modinfop); +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: _info(%x) = %x", + modinfop, ipfinst); +#endif + if (fr_running > 0) + ipfsync(); + return ipfinst; +} + + +static int ipf_probe(dip) +dev_info_t *dip; +{ + if (fr_running < 0) + return DDI_PROBE_FAILURE; +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: ipf_probe(%x)", dip); +#endif + return DDI_PROBE_SUCCESS; +} + + +static int ipf_identify(dip) +dev_info_t *dip; +{ +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: ipf_identify(%x)", dip); +#endif + if (strcmp(ddi_get_name(dip), "ipf") == 0) + return (DDI_IDENTIFIED); + return (DDI_NOT_IDENTIFIED); +} + + +static void ipf_ire_walk(ire, arg) +ire_t *ire; +void *arg; +{ + qif_t *qif = arg; + + if ((ire->ire_type == IRE_CACHE) && +#if SOLARIS2 >= 6 + (ire->ire_ipif != NULL) && + (ire->ire_ipif->ipif_ill == qif->qf_ill) +#else + (ire_to_ill(ire) == qif->qf_ill) +#endif + ) { +#if SOLARIS2 >= 8 + mblk_t *m = ire->ire_fp_mp; +#else + mblk_t *m = ire->ire_ll_hdr_mp; +#endif + if (m != NULL) + qif->qf_hl = m->b_wptr - m->b_rptr; + } +} + + +static int ipf_attach(dip, cmd) +dev_info_t *dip; +ddi_attach_cmd_t cmd; +{ +#ifdef IPFDEBUG + int instance; + + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: ipf_attach(%x,%x)", dip, cmd); +#endif + switch (cmd) { + case DDI_ATTACH: + if (fr_running < 0) + break; +#ifdef IPFDEBUG + instance = ddi_get_instance(dip); + + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: attach ipf instance %d", instance); +#endif + if (ddi_create_minor_node(dip, "ipf", S_IFCHR, IPL_LOGIPF, + DDI_PSEUDO, 0) == DDI_FAILURE) { + ddi_remove_minor_node(dip, NULL); + goto attach_failed; + } + if (ddi_create_minor_node(dip, "ipnat", S_IFCHR, IPL_LOGNAT, + DDI_PSEUDO, 0) == DDI_FAILURE) { + ddi_remove_minor_node(dip, NULL); + goto attach_failed; + } + if (ddi_create_minor_node(dip, "ipstate", S_IFCHR,IPL_LOGSTATE, + DDI_PSEUDO, 0) == DDI_FAILURE) { + ddi_remove_minor_node(dip, NULL); + goto attach_failed; + } + if (ddi_create_minor_node(dip, "ipauth", S_IFCHR, IPL_LOGAUTH, + DDI_PSEUDO, 0) == DDI_FAILURE) { + ddi_remove_minor_node(dip, NULL); + goto attach_failed; + } + ipf_dev_info = dip; + sync(); + /* + * Initialize mutex's + */ + if (iplattach() == -1) + goto attach_failed; + /* + * Lock people out while we set things up. + */ + WRITE_ENTER(&ipf_solaris); + solattach(); + solipdrvattach(); + RWLOCK_EXIT(&ipf_solaris); + cmn_err(CE_CONT, "%s, attaching complete.\n", + ipfilter_version); + sync(); + if (fr_running == 0) + fr_running = 1; + if (ipfr_timer_id == 0) + ipfr_timer_id = timeout(ipfr_slowtimer, NULL, + drv_usectohz(500000)); + if (fr_running == 1) + return DDI_SUCCESS; +#if SOLARIS2 >= 8 + case DDI_RESUME : + case DDI_PM_RESUME : + if (ipfr_timer_id == 0) + ipfr_timer_id = timeout(ipfr_slowtimer, NULL, + drv_usectohz(500000)); + return DDI_SUCCESS; +#endif + default: + return DDI_FAILURE; + } + +attach_failed: + cmn_err(CE_NOTE, "IP Filter: failed to attach\n"); + /* + * Use our own detach routine to toss + * away any stuff we allocated above. + */ + (void) ipf_detach(dip, DDI_DETACH); + return DDI_FAILURE; +} + + +static int ipf_detach(dip, cmd) +dev_info_t *dip; +ddi_detach_cmd_t cmd; +{ + int i; + +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: ipf_detach(%x,%x)", dip, cmd); +#endif + switch (cmd) { + case DDI_DETACH: + if (fr_running <= 0) + break; + /* + * Make sure we're the only one's modifying things. With + * this lock others should just fall out of the loop. + */ + mutex_enter(&ipf_rw); + if (ipfr_timer_id != 0) { + untimeout(ipfr_timer_id); + ipfr_timer_id = 0; + } + mutex_exit(&ipf_rw); + WRITE_ENTER(&ipf_solaris); + mutex_enter(&ipf_rw); + if (fr_running <= 0) { + mutex_exit(&ipf_rw); + return DDI_FAILURE; + } + fr_running = -1; + mutex_exit(&ipf_rw); + /* NOTE: ipf_solaris rwlock is released in ipldetach */ + + /* + * Undo what we did in ipf_attach, freeing resources + * and removing things we installed. The system + * framework guarantees we are not active with this devinfo + * node in any other entry points at this time. + */ + ddi_prop_remove_all(dip); + i = ddi_get_instance(dip); + ddi_remove_minor_node(dip, NULL); + sync(); + i = solipdrvdetach(); + if (i > 0) { + cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i); + return DDI_FAILURE; + } + if (!soldetach()) { + cmn_err(CE_CONT, "%s detached\n", ipfilter_version); + return (DDI_SUCCESS); + } +#if SOLARIS2 >= 8 + case DDI_SUSPEND : + case DDI_PM_SUSPEND : + if (ipfr_timer_id != 0) { + untimeout(ipfr_timer_id); + ipfr_timer_id = 0; + } + if (synctimeoutid) { + untimeout(synctimeoutid); + synctimeoutid = 0; + } + return DDI_SUCCESS; +#endif + default: + return (DDI_FAILURE); + } + return DDI_FAILURE; +} + + +static int ipf_getinfo(dip, infocmd, arg, result) +dev_info_t *dip; +ddi_info_cmd_t infocmd; +void *arg, **result; +{ + int error; + + if (fr_running <= 0) + return DDI_FAILURE; + error = DDI_FAILURE; +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%x,%x,%x)", + dip, infocmd, arg); +#endif + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = ipf_dev_info; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)getminor((dev_t) arg); + error = DDI_SUCCESS; + break; + default: + break; + } + return (error); +} + +/* + * find the filter structure setup for this queue + */ +static qif_t *qif_from_queue(q) +queue_t *q; +{ + qif_t *qif; + + for (qif = qif_head; qif; qif = qif->qf_next) + if ((qif->qf_iptr == q->q_ptr) || (qif->qf_optr == q->q_ptr)) + break; + return qif; +} + + +/* + * OK, this is pretty scrappy code, but then it's essentially just here for + * debug purposes and that's it. Packets should not normally come through + * here, and if they do, well, we would like to see as much information as + * possible about them and what they claim to hold. + */ +void fr_donotip(out, qif, q, m, mt, ip, off) +int out; +qif_t *qif; +queue_t *q; +mblk_t *m, *mt; +ip_t *ip; +size_t off; +{ + u_char *s, outb[256], *t; + int i; + + outb[0] = '\0'; + outb[1] = '\0'; + outb[2] = '\0'; + outb[3] = '\0'; + s = ip ? (u_char *)ip : outb; + if (!ip && (m == mt) && m->b_cont && (MTYPE(m) != M_DATA)) + m = m->b_cont; + + cmn_err(CE_CONT, " !IP %s:%d %d %p %p %p %d %p/%d %p/%d %p %d %d %p\n", + qif ? qif->qf_name : "?", out, qif ? qif->qf_hl : -1, q, + q ? q->q_ptr : NULL, q ? q->q_qinfo : NULL, + mt->b_wptr - mt->b_rptr, m, MTYPE(m), mt, MTYPE(mt), m->b_rptr, + m->b_wptr - m->b_rptr, off, ip); + cmn_err(CE_CONT, "%02x%02x%02x%02x\n", *s, *(s+1), *(s+2), *(s+3)); + while (m != mt) { + i = 0; + t = outb; + s = mt->b_rptr; + sprintf((char *)t, "%d:", MTYPE(mt)); + t += strlen((char *)t); + for (; (i < 100) && (s < mt->b_wptr); i++) { + sprintf((char *)t, "%02x%s", *s++, + ((i & 3) == 3) ? " " : ""); + t += ((i & 3) == 3) ? 3 : 2; + } + *t++ = '\n'; + *t = '\0'; + cmn_err(CE_CONT, "%s", outb); + mt = mt->b_cont; + } + i = 0; + t = outb; + s = m->b_rptr; + sprintf((char *)t, "%d:", MTYPE(m)); + t += strlen((char *)t); + for (; (i < 100) && (s < m->b_wptr); i++) { + sprintf((char *)t, "%02x%s", *s++, ((i & 3) == 3) ? " " : ""); + t += ((i & 3) == 3) ? 3 : 2; + } + *t++ = '\n'; + *t = '\0'; + cmn_err(CE_CONT, "%s", outb); +} + + +/* + * find the first data mblk, if present, in the chain we're processing. Also + * make a few sanity checks to try prevent the filter from causing a panic - + * none of the nice IP sanity checks (including checksumming) should have been + * done yet (for incoming packets) - dangerous! + */ +static int fr_precheck(mp, q, qif, out) +mblk_t **mp; +queue_t *q; +qif_t *qif; +int out; +{ + register mblk_t *m, *mt = *mp; + register ip_t *ip; + size_t hlen, len, off, off2, mlen, iphlen, plen, woff; + int err, synced = 0, sap, p, realigned = 0, multi = 0; + u_char *bp; +#if SOLARIS2 >= 8 + ip6_t *ip6; +#endif +#ifndef sparc + u_short __ipoff; +#endif +tryagain: + ip = NULL; + m = NULL; + /* + * If there is only M_DATA for a packet going out, then any header + * information (which would otherwise appear in an M_PROTO mblk before + * the M_DATA) is prepended before the IP header. We need to set the + * offset to account for this. - see MMM + */ + off = (out) ? qif->qf_hl : 0; + + /* + * If the message protocol block indicates that there isn't a data + * block following it, just return back. + */ + bp = (u_char *)ALIGN32(mt->b_rptr); + if (MTYPE(mt) == M_PROTO || MTYPE(mt) == M_PCPROTO) { + dl_unitdata_ind_t *dl = (dl_unitdata_ind_t *)bp; + if (dl->dl_primitive == DL_UNITDATA_IND) { + multi = dl->dl_group_address; + m = mt->b_cont; + /* + * This is a complete kludge to try and work around + * some bizarre packets which drop through into + * fr_donotip. + */ + if (m && multi && ((*((u_char *)m->b_rptr) == 0x0) && + ((*((u_char *)m->b_rptr + 2) == 0x45)))) { + ip = (ip_t *)(m->b_rptr + 2); + off = 2; + } else + off = 0; + } else if (dl->dl_primitive != DL_UNITDATA_REQ) { + ip = (ip_t *)dl; + if ((ip->ip_v == IPVERSION) && + (ip->ip_hl == (sizeof(*ip) >> 2)) && + (ntohs(ip->ip_len) == mt->b_wptr - mt->b_rptr)) { + off = 0; + m = mt; + } else { + frstats[out].fr_notdata++; + return 0; + } + } + } + + /* + * Find the first data block, count the data blocks in this chain and + * the total amount of data. + */ + if (ip == NULL) + for (m = mt; m && (MTYPE(m) != M_DATA); m = m->b_cont) + off = 0; /* Any non-M_DATA cancels the offset */ + + if (!m) { + frstats[out].fr_nodata++; + return 0; /* No data blocks */ + } + + ip = (ip_t *)(m->b_rptr + off); /* MMM */ + + /* + * We might have a 1st data block which is really M_PROTO, i.e. it is + * only big enough for the link layer header + */ + while ((u_char *)ip >= m->b_wptr) { + len = (u_char *)ip - m->b_wptr; + m = m->b_cont; + if (m == NULL) + return 0; /* not enough data for IP */ + ip = (ip_t *)(m->b_rptr + len); + } + off = (u_char *)ip - m->b_rptr; + if (off != 0) + m->b_rptr = (u_char *)ip; + + len = m->b_wptr - m->b_rptr; + if (m->b_wptr < m->b_rptr) { + cmn_err(CE_NOTE, "!IP Filter: Bad packet: wptr %p < rptr %p", + m->b_wptr, m->b_rptr); + frstats[out].fr_bad++; + return -1; + } + + mlen = msgdsize(m); + sap = qif->qf_ill->ill_sap; + + if (sap == 0x800) { + u_short tlen; + + hlen = sizeof(*ip); + + /* XXX - might not be aligned (from ppp?) */ + ((char *)&tlen)[0] = ((char *)&ip->ip_len)[0]; + ((char *)&tlen)[1] = ((char *)&ip->ip_len)[1]; + + plen = ntohs(tlen); + + sap = 0; + } +#if SOLARIS2 >= 8 + else if (sap == IP6_DL_SAP) { + u_short tlen; + + hlen = sizeof(ip6_t); + ip6 = (ip6_t *)ip; + /* XXX - might not be aligned (from ppp?) */ + ((char *)&tlen)[0] = ((char *)&ip6->ip6_plen)[0]; + ((char *)&tlen)[1] = ((char *)&ip6->ip6_plen)[1]; + plen = ntohs(tlen); + if (!plen) + return -1; /* Jumbo gram */ + plen += sizeof(*ip6); + } +#endif + else { + plen = 0; + hlen = 0; + sap = -1; + } + + /* + * Ok, the IP header isn't on a 32bit aligned address so junk it. + */ + if (((u_long)ip & 0x3) || (plen > mlen) || (len < hlen) || + (sap == -1)) { + mblk_t *m1, *m2; + u_char *s, c; + int v; + + /* + * Junk using pullupmsg - it's next to useless. + */ +fixalign: + if (off) + m->b_rptr -= off; + c = *(u_char *)ip; + c >>= 4; + if (c != 4 +#if SOLARIS2 >= 8 + && c != 6 +#endif + ) { + frstats[out].fr_notip++; + return (fr_flags & FF_BLOCKNONIP) ? -1 : 0; + } + + if (realigned) + return -1; + realigned = 1; + off2 = (size_t)((u_long)ip & 0x3); + if (off2) + off2 = 4 - off2; + len = msgdsize(m); + m2 = allocb(len + off2, BPRI_HI); + if (m2 == NULL) { + frstats[out].fr_pull[1]++; + return -1; + } + + MTYPE(m2) = M_DATA; + if (m->b_rptr != (u_char *)ip) + m2->b_rptr += off2; + m2->b_wptr = m2->b_rptr + len; + m1 = m; + s = (u_char *)m->b_rptr; + for (bp = m2->b_rptr; m1 && (bp < m2->b_wptr); bp += len) { + len = MIN(m1->b_wptr - s, m2->b_wptr - bp); + bcopy(s, bp, len); + m1 = m1->b_cont; + if (m1) + s = m1->b_rptr; + } + + if (mt != m && mt->b_cont == m && !off) { + /* + * check if the buffer we're changing is chained in- + * between other buffers and unlink/relink as required. + */ + (void) unlinkb(mt); /* should return 'm' */ + m1 = unlinkb(m); + if (m1) + linkb(m2, m1); + freemsg(m); + linkb(mt, m2); + } else { + if (m == mt) { + m1 = unlinkb(mt); + if (m1) + linkb(m2, m1); + } + freemsg(mt); + *mp = m2; + mt = m2; + } + + frstats[out].fr_pull[0]++; + synced = 1; + off = 0; + goto tryagain; + } + + if (((sap == 0) && (ip->ip_v != IP_VERSION)) +#if SOLARIS2 >= 8 + || ((sap == IP6_DL_SAP) && ((ip6->ip6_vfc >> 4) != 6)) +#endif + ) { + m->b_rptr -= off; + return -2; + } + +#ifndef sparc +# if SOLARIS2 >= 8 + if (sap == IP6_DL_SAP) { + ip6->ip6_plen = plen - sizeof(*ip6); + } else { +# endif + __ipoff = (u_short)ip->ip_off; + + ip->ip_len = plen; + ip->ip_off = ntohs(__ipoff); +# if SOLARIS2 >= 8 + } +# endif +#endif + if (sap == 0) + iphlen = ip->ip_hl << 2; +#if SOLARIS2 >= 8 + else if (sap == IP6_DL_SAP) + iphlen = sizeof(ip6_t); +#endif + + if (( +#if SOLARIS2 >= 8 + (sap == IP6_DL_SAP) && (mlen < plen)) || + ((sap == 0) && +#endif + ((iphlen < hlen) || (iphlen > plen) || (mlen < plen)))) { + /* + * Bad IP packet or not enough data/data length mismatches + */ +#ifndef sparc +# if SOLARIS2 >= 8 + if (sap == IP6_DL_SAP) { + ip6->ip6_plen = htons(plen - sizeof(*ip6)); + } else { +# endif + __ipoff = (u_short)ip->ip_off; + + ip->ip_len = htons(plen); + ip->ip_off = htons(__ipoff); +# if SOLARIS2 >= 8 + } +# endif +#endif + m->b_rptr -= off; + frstats[out].fr_bad++; + return -1; + } + + /* + * Make hlen the total size of the IP header plus TCP/UDP/ICMP header + * (if it is one of these three). + */ + if (sap == 0) + p = ip->ip_p; +#if SOLARIS2 >= 8 + else if (sap == IP6_DL_SAP) + p = ip6->ip6_nxt; + + if ((sap == IP6_DL_SAP) || ((ip->ip_off & IP_OFFMASK) == 0)) +#else + if ((ip->ip_off & IP_OFFMASK) == 0) +#endif + switch (p) + { + case IPPROTO_TCP : + hlen += sizeof(tcphdr_t); + break; + case IPPROTO_UDP : + hlen += sizeof(udphdr_t); + break; + case IPPROTO_ICMP : + /* 76 bytes is enough for a complete ICMP error. */ + hlen += 76 + sizeof(icmphdr_t); + break; + default : + break; + } + + woff = 0; + if (hlen > mlen) { + hlen = mlen; + } else if (m->b_wptr - m->b_rptr > plen) { + woff = m->b_wptr - m->b_rptr - plen; + m->b_wptr -= woff; + } + + /* + * If we don't have enough data in the mblk or we haven't yet copied + * enough (above), then copy some more. + */ + if ((hlen > len)) { + if (!pullupmsg(m, (int)hlen)) { + cmn_err(CE_NOTE, "pullupmsg failed"); + frstats[out].fr_pull[1]++; + return -1; + } + frstats[out].fr_pull[0]++; + ip = (ip_t *)ALIGN32(m->b_rptr); + } + qif->qf_m = m; + qif->qf_q = q; + qif->qf_off = off; + qif->qf_len = len; + err = fr_check(ip, iphlen, qif->qf_ill, out, qif, mp); + if (err == 2) { + goto fixalign; + } + /* + * Copy back the ip header data if it was changed, we haven't yet + * freed the message and we aren't going to drop the packet. + * BUT only do this if there were no changes to the buffer, else + * we can't be sure that the ip pointer is still correct! + */ + if (*mp != NULL) { + if (*mp == mt) { + m->b_wptr += woff; + m->b_rptr -= off; +#ifndef sparc +# if SOLARIS2 >= 8 + if (sap == IP6_DL_SAP) { + ip6->ip6_plen = htons(plen - sizeof(*ip6)); + } else { +# endif + __ipoff = (u_short)ip->ip_off; + /* + * plen is useless because of NAT. + */ + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(__ipoff); +# if SOLARIS2 >= 8 + } +# endif +#endif + } else + cmn_err(CE_NOTE, + "!IP Filter: *mp %p mt %p %s", *mp, mt, + "mblk changed, cannot revert ip_len, ip_off"); + } + return err; +} + + +/* + * Only called for M_IOCACK messages + */ +void fr_qif_update(qif, mp) +qif_t *qif; +mblk_t *mp; +{ + struct iocblk *iocp; + + if (!qif || !mp) + return; + iocp = (struct iocblk *)mp->b_rptr; + if (mp->b_cont && (iocp->ioc_cmd == DL_IOC_HDR_INFO)) { + mp = mp->b_cont; + if (MTYPE(mp) == M_PROTO && mp->b_cont) { + mp = mp->b_cont; + if (MTYPE(mp) == M_DATA) { + qif->qf_hl = mp->b_wptr - mp->b_rptr; + } + } + } +} + + +int fr_qin(q, mb) +queue_t *q; +mblk_t *mb; +{ + int (*pnext) __P((queue_t *, mblk_t *)), type, synced = 0, err = 0; + qif_t qf, *qif; + +#ifdef IPFDEBUG_VERBOSE + if (ipf_debug_verbose) + cmn_err(CE_CONT, + "fr_qin(%lx,%lx) ptr %lx type 0x%x ref %d len %d\n", + q, q->q_ptr, mb, MTYPE(mb), mb->b_datap->db_ref, + msgdsize(mb)); +#endif + + /* + * IPFilter is still in the packet path but not enabled. Drop whatever + * it is that has come through. + */ + if (fr_running <= 0) { + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + + type = MTYPE(mb); + + /* + * If a mblk has more than one reference, make a copy, filter that and + * free a reference to the original. + */ + if (mb->b_datap->db_ref > 1) { + mblk_t *m1; + + m1 = copymsg(mb); + if (!m1) { + frstats[0].fr_drop++; + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + mb->b_prev = NULL; + freemsg(mb); + mb = m1; + frstats[0].fr_copy++; + } + + READ_ENTER(&ipf_solaris); +again: + if (fr_running <= 0) { + mb->b_prev = NULL; + freemsg(mb); + RWLOCK_EXIT(&ipf_solaris); + return 0; + } + READ_ENTER(&ipfs_mutex); + if (!(qif = qif_from_queue(q))) { + for (qif = qif_head; qif; qif = qif->qf_next) + if (&qif->qf_rqinit == q->q_qinfo && qif->qf_rqinfo && + qif->qf_rqinfo->qi_putp) { + pnext = qif->qf_rqinfo->qi_putp; + frstats[0].fr_notip++; + RWLOCK_EXIT(&ipfs_mutex); + if (!synced) { + ipfsync(); + synced = 1; + goto again; + } + RWLOCK_EXIT(&ipf_solaris); + /* fr_donotip(0, NULL, q, mb, mb, NULL, 0); */ + return (*pnext)(q, mb); + } + RWLOCK_EXIT(&ipfs_mutex); + if (!synced) { + ipfsync(); + synced = 1; + goto again; + } + cmn_err(CE_WARN, + "!IP Filter: dropped: fr_qin(%x,%x): type %x qif %x", + q, mb, type, qif); + cmn_err(CE_CONT, + "!IP Filter: info %x next %x ptr %x fsrv %x bsrv %x\n", + q->q_qinfo, q->q_next, q->q_ptr, q->q_nfsrv, + q->q_nbsrv); + cmn_err(CE_CONT, "!IP Filter: info: putp %x srvp %x info %x\n", + q->q_qinfo->qi_putp, q->q_qinfo->qi_srvp, +#if SOLARIS > 3 + q->q_qinfo->qi_infop +#else + 0 +#endif + ); + frstats[0].fr_drop++; + mb->b_prev = NULL; + freemsg(mb); + RWLOCK_EXIT(&ipf_solaris); + return 0; + } + + qif->qf_incnt++; + pnext = qif->qf_rqinfo->qi_putp; + if (type == M_IOCACK) + fr_qif_update(qif, mb); + bcopy((char *)qif, (char *)&qf, sizeof(qf)); + if (datamsg(type) || (type == M_BREAK)) + err = fr_precheck(&mb, q, &qf, 0); + + RWLOCK_EXIT(&ipfs_mutex); + + if ((err == 0) && (mb != NULL)) { + if (pnext) { + RWLOCK_EXIT(&ipf_solaris); + return (*pnext)(q, mb); + } + + cmn_err(CE_WARN, + "!IP Filter: inp NULL: qif %x %s q %x info %x", + qif, qf.qf_name, q, q->q_qinfo); + } + + if (err == -2) { + if (synced == 0) { + ipfsync(); + synced = 1; + goto again; + } + frstats[0].fr_notip++; + if (!(fr_flags & FF_BLOCKNONIP) && (pnext != NULL)) { + RWLOCK_EXIT(&ipf_solaris); + return (*pnext)(q, mb); + } + } + + + if (mb) { + mb->b_prev = NULL; + freemsg(mb); + } + RWLOCK_EXIT(&ipf_solaris); + return 1; +} + + +int fr_qout(q, mb) +queue_t *q; +mblk_t *mb; +{ + int (*pnext) __P((queue_t *, mblk_t *)), type, synced = 0, err = 0; + qif_t qf, *qif; + +#ifdef IPFDEBUG_VERBOSE + if (ipf_debug_verbose) + cmn_err(CE_CONT, + "fr_qout(%lx,%lx) ptr %lx type 0x%x ref %d len %d\n", + q, q->q_ptr, mb, MTYPE(mb), mb->b_datap->db_ref, + msgdsize(mb)); +#endif + + if (fr_running <= 0) { + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + + type = MTYPE(mb); + +#if SOLARIS2 >= 6 + if ((!dohwcksum || mb->b_ick_flag != ICK_VALID) && + (mb->b_datap->db_ref > 1)) +#else + if (mb->b_datap->db_ref > 1) +#endif + { + mblk_t *m1; + + m1 = copymsg(mb); + if (!m1) { + frstats[1].fr_drop++; + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + mb->b_prev = NULL; + freemsg(mb); + mb = m1; + frstats[1].fr_copy++; + } + + READ_ENTER(&ipf_solaris); +again: + if (fr_running <= 0) { + mb->b_prev = NULL; + freemsg(mb); + RWLOCK_EXIT(&ipf_solaris); + return 0; + } + READ_ENTER(&ipfs_mutex); + if (!(qif = qif_from_queue(q))) { + for (qif = qif_head; qif; qif = qif->qf_next) + if (&qif->qf_wqinit == q->q_qinfo && qif->qf_wqinfo && + qif->qf_wqinfo->qi_putp) { + pnext = qif->qf_wqinfo->qi_putp; + RWLOCK_EXIT(&ipfs_mutex); + frstats[1].fr_notip++; + if (!synced) { + ipfsync(); + synced = 1; + goto again; + } + /* fr_donotip(1, NULL, q, mb, mb, NULL, 0); */ + RWLOCK_EXIT(&ipf_solaris); + return (*pnext)(q, mb); + } + RWLOCK_EXIT(&ipfs_mutex); + if (!synced) { + ipfsync(); + synced = 1; + goto again; + } + cmn_err(CE_WARN, + "!IP Filter: dropped: fr_qout(%x,%x): type %x: qif %x", + q, mb, type, qif); + cmn_err(CE_CONT, + "!IP Filter: info %x next %x ptr %x fsrv %x bsrv %x\n", + q->q_qinfo, q->q_next, q->q_ptr, q->q_nfsrv, + q->q_nbsrv); + cmn_err(CE_CONT, "!IP Filter: info: putp %x srvp %x info %x\n", + q->q_qinfo->qi_putp, q->q_qinfo->qi_srvp, +#if SOLARIS > 3 + q->q_qinfo->qi_infop +#else + 0 +#endif + ); + if (q->q_nfsrv) + cmn_err(CE_CONT, + "!IP Filter: nfsrv: info %x next %x ptr %x\n", + q->q_nfsrv->q_qinfo, q->q_nfsrv->q_next, + q->q_nfsrv->q_ptr); + if (q->q_nbsrv) + cmn_err(CE_CONT, + "!IP Filter: nbsrv: info %x next %x ptr %x\n", + q->q_nbsrv->q_qinfo, q->q_nbsrv->q_next, + q->q_nbsrv->q_ptr); + frstats[1].fr_drop++; + mb->b_prev = NULL; + freemsg(mb); + RWLOCK_EXIT(&ipf_solaris); + return 0; + } + + qif->qf_outcnt++; + pnext = qif->qf_wqinfo->qi_putp; + if (type == M_IOCACK) + fr_qif_update(qif, mb); + bcopy((char *)qif, (char *)&qf, sizeof(qf)); + if (datamsg(type) || (type == M_BREAK)) + err = fr_precheck(&mb, q, &qf, 1); + + RWLOCK_EXIT(&ipfs_mutex); + + if ((err == 0) && (mb != NULL)) { + if (pnext) { + RWLOCK_EXIT(&ipf_solaris); + return (*pnext)(q, mb); + } + + cmn_err(CE_WARN, + "!IP Filter: outp NULL: qif %x %s q %x info %x", + qif, qf.qf_name, q, q->q_qinfo); + } + + if (err == -2) { + if (synced == 0) { + ipfsync(); + synced = 1; + goto again; + } + frstats[1].fr_notip++; + if (!(fr_flags & FF_BLOCKNONIP) && (pnext != NULL)) { + RWLOCK_EXIT(&ipf_solaris); + return (*pnext)(q, mb); + } + } + + if (mb) { + mb->b_prev = NULL; + freemsg(mb); + } + RWLOCK_EXIT(&ipf_solaris); + return 1; +} + + +void ipf_synctimeout(arg) +void *arg; +{ + if (fr_running < 0) + return; + READ_ENTER(&ipf_solaris); + ipfsync(); + WRITE_ENTER(&ipfs_mutex); + synctimeoutid = 0; + RWLOCK_EXIT(&ipfs_mutex); + RWLOCK_EXIT(&ipf_solaris); +} + + +static int ipf_ip_qin(q, mb) +queue_t *q; +mblk_t *mb; +{ + struct iocblk *ioc; + int ret; + + if (fr_running <= 0) { + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + + if (MTYPE(mb) != M_IOCTL) + return (*ipf_ip_inp)(q, mb); + + READ_ENTER(&ipf_solaris); + if (fr_running <= 0) { + RWLOCK_EXIT(&ipf_solaris); + mb->b_prev = NULL; + freemsg(mb); + return 0; + } + ioc = (struct iocblk *)mb->b_rptr; + + switch (ioc->ioc_cmd) + { + case DL_IOC_HDR_INFO: + fr_qif_update(qif_from_queue(q), mb); + break; + case I_LINK: + case I_UNLINK: + case SIOCSIFADDR: + case SIOCSIFFLAGS: +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: ipf_ip_qin() M_IOCTL type=0x%x", + ioc->ioc_cmd); +#endif + WRITE_ENTER(&ipfs_mutex); + if (synctimeoutid == 0) { + synctimeoutid = timeout(ipf_synctimeout, + NULL, + drv_usectohz(1000000) /*1 sec*/ + ); + } + RWLOCK_EXIT(&ipfs_mutex); + break; + default: + break; + } + RWLOCK_EXIT(&ipf_solaris); + return (*ipf_ip_inp)(q, mb); +} + +static int ipdrvattcnt = 0; +extern struct streamtab ipinfo; + +void solipdrvattach() +{ +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: solipdrvattach() %d ipinfo=0x%lx", + ipdrvattcnt, &ipinfo); +#endif + + if (++ipdrvattcnt == 1) { + if (ipf_ip_inp == NULL) { + ipf_ip_inp = ipinfo.st_wrinit->qi_putp; + ipinfo.st_wrinit->qi_putp = ipf_ip_qin; + } + } +} + +int solipdrvdetach() +{ +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, "IP Filter: solipdrvdetach() %d ipinfo=0x%lx", + ipdrvattcnt, &ipinfo); +#endif + + WRITE_ENTER(&ipfs_mutex); + if (--ipdrvattcnt <= 0) { + if (ipf_ip_inp && (ipinfo.st_wrinit->qi_putp == ipf_ip_qin)) { + ipinfo.st_wrinit->qi_putp = ipf_ip_inp; + ipf_ip_inp = NULL; + } + if (synctimeoutid) { + untimeout(synctimeoutid); + synctimeoutid = 0; + } + } + RWLOCK_EXIT(&ipfs_mutex); + return ipdrvattcnt; +} + +/* + * attach the packet filter to each interface that is defined as having an + * IP address associated with it and save some of the info. for that struct + * so we're not out of date as soon as the ill disappears - but we must sync + * to be correct! + */ +void solattach() +{ + queue_t *in, *out; + struct frentry *f; + qif_t *qif, *qf2; + ipnat_t *np; + size_t len; + ill_t *il; + + for (il = ill_g_head; il; il = il->ill_next) { + in = il->ill_rq; + if (!in || !il->ill_wq) + continue; + + out = il->ill_wq->q_next; + + WRITE_ENTER(&ipfs_mutex); + /* + * Look for entry already setup for this device + */ + for (qif = qif_head; qif; qif = qif->qf_next) + if (qif->qf_iptr == in->q_ptr && + qif->qf_optr == out->q_ptr) + break; + if (qif) { + RWLOCK_EXIT(&ipfs_mutex); + continue; + } +#ifdef IPFDEBUGX + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: il %x ipt %x opt %x ipu %x opu %x i %x/%x", + il, in->q_ptr, out->q_ptr, in->q_qinfo->qi_putp, + out->q_qinfo->qi_putp, out->q_qinfo, in->q_qinfo); +#endif + KMALLOC(qif, qif_t *); + if (!qif) { + cmn_err(CE_WARN, + "IP Filter: malloc(%d) for qif_t failed", + sizeof(qif_t)); + RWLOCK_EXIT(&ipfs_mutex); + continue; + } + + if (in->q_qinfo->qi_putp == fr_qin) { + for (qf2 = qif_head; qf2; qf2 = qf2->qf_next) + if (&qf2->qf_rqinit == in->q_qinfo) { + qif->qf_rqinfo = qf2->qf_rqinfo; + break; + } + if (!qf2) { +#ifdef IPFDEBUGX + if (ipf_debug) + cmn_err(CE_WARN, + "IP Filter: rq:%s put %x qi %x", + il->ill_name, in->q_qinfo->qi_putp, + in->q_qinfo); +#endif + RWLOCK_EXIT(&ipfs_mutex); + KFREE(qif); + continue; + } + } else + qif->qf_rqinfo = in->q_qinfo; + + if (out->q_qinfo->qi_putp == fr_qout) { + for (qf2 = qif_head; qf2; qf2 = qf2->qf_next) + if (&qf2->qf_wqinit == out->q_qinfo) { + qif->qf_wqinfo = qf2->qf_wqinfo; + break; + } + if (!qf2) { +#ifdef IPFDEBUGX + if (ipf_debug) + cmn_err(CE_WARN, + "IP Filter: wq:%s put %x qi %x", + il->ill_name, out->q_qinfo->qi_putp, + out->q_qinfo); +#endif + RWLOCK_EXIT(&ipfs_mutex); + KFREE(qif); + continue; + } + } else + qif->qf_wqinfo = out->q_qinfo; + + qif->qf_ill = il; + qif->qf_in = in; + qif->qf_out = out; + qif->qf_iptr = in->q_ptr; + qif->qf_optr = out->q_ptr; +#if SOLARIS2 < 8 + qif->qf_hl = il->ill_hdr_length; +#else + { + ire_t *ire; + mblk_t *m; + + qif->qf_hl = 0; + qif->qf_sap = il->ill_sap; +# if 0 + /* + * Can't seem to lookup a route for the IP address on the + * interface itself. + */ + ire = ire_route_lookup(il->ill_ipif->ipif_lcl_addr, 0xffffffff, + 0, 0, NULL, NULL, NULL, + MATCH_IRE_DSTONLY|MATCH_IRE_RECURSIVE); + if ((ire != NULL) && (m = ire->ire_fp_mp)) + qif->qf_hl = m->b_wptr - m->b_rptr; +# endif + if ((qif->qf_hl == 0) && (il->ill_type > 0) && + (il->ill_type < 0x37) && + (hdrsizes[il->ill_type][0] == il->ill_type)) + qif->qf_hl = hdrsizes[il->ill_type][1]; + + /* DREADFUL VLAN HACK - JUST HERE TO CHECK IT WORKS */ + if (il->ill_type == IFT_ETHER && + il->ill_name[0] == 'c' && il->ill_name[1] == 'e' && + isdigit(il->ill_name[2]) && il->ill_name_length >= 6) { + cmn_err(CE_NOTE, "VLAN HACK ENABLED"); + qif->qf_hl += 4; + } + /* DREADFUL VLAN HACK - JUST HERE TO CHECK IT WORKS */ + + if (qif->qf_hl == 0 && il->ill_type != IFT_OTHER) + cmn_err(CE_WARN, + "Unknown layer 2 header size for %s type %d", + il->ill_name, il->ill_type); + } + + /* + * XXX Awful hack for PPP; fix when PPP/snoop fixed. + */ + if (il->ill_type == IFT_ETHER && !il->ill_bcast_addr_length) + qif->qf_hl = 0; +#endif + strncpy(qif->qf_name, il->ill_name, sizeof(qif->qf_name)); + qif->qf_name[sizeof(qif->qf_name) - 1] = '\0'; + + qif->qf_next = qif_head; + qif_head = qif; + + /* + * Activate any rules directly associated with this interface + */ + WRITE_ENTER(&ipf_mutex); + for (f = ipfilter[0][fr_active]; f; f = f->fr_next) { + if ((f->fr_ifa == (struct ifnet *)-1)) { + len = strlen(f->fr_ifname) + 1; + if ((len != 0) && + (len == (size_t)il->ill_name_length) && + !strncmp(il->ill_name, f->fr_ifname, len)) + f->fr_ifa = il; + } + } + for (f = ipfilter[1][fr_active]; f; f = f->fr_next) { + if ((f->fr_ifa == (struct ifnet *)-1)) { + len = strlen(f->fr_ifname) + 1; + if ((len != 0) && + (len == (size_t)il->ill_name_length) && + !strncmp(il->ill_name, f->fr_ifname, len)) + f->fr_ifa = il; + } + } +#if SOLARIS2 >= 8 + for (f = ipfilter6[0][fr_active]; f; f = f->fr_next) { + if ((f->fr_ifa == (struct ifnet *)-1)) { + len = strlen(f->fr_ifname) + 1; + if ((len != 0) && + (len == (size_t)il->ill_name_length) && + !strncmp(il->ill_name, f->fr_ifname, len)) + f->fr_ifa = il; + } + } + for (f = ipfilter6[1][fr_active]; f; f = f->fr_next) { + if ((f->fr_ifa == (struct ifnet *)-1)) { + len = strlen(f->fr_ifname) + 1; + if ((len != 0) && + (len == (size_t)il->ill_name_length) && + !strncmp(il->ill_name, f->fr_ifname, len)) + f->fr_ifa = il; + } + } +#endif + RWLOCK_EXIT(&ipf_mutex); + WRITE_ENTER(&ipf_nat); + for (np = nat_list; np; np = np->in_next) { + if ((np->in_ifp == (struct ifnet *)-1)) { + len = strlen(np->in_ifname) + 1; + if ((len != 0) && + (len == (size_t)il->ill_name_length) && + !strncmp(il->ill_name, np->in_ifname, len)) + np->in_ifp = il; + } + } + RWLOCK_EXIT(&ipf_nat); + + bcopy((caddr_t)qif->qf_rqinfo, (caddr_t)&qif->qf_rqinit, + sizeof(struct qinit)); + qif->qf_rqinit.qi_putp = fr_qin; +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: solattach: in queue(%lx)->q_qinfo FROM %lx TO %lx", + in, in->q_qinfo, &qif->qf_rqinit); +#endif + in->q_qinfo = &qif->qf_rqinit; + + bcopy((caddr_t)qif->qf_wqinfo, (caddr_t)&qif->qf_wqinit, + sizeof(struct qinit)); + qif->qf_wqinit.qi_putp = fr_qout; +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: solattach: out queue(%lx)->q_qinfo FROM %lx TO %lx", + out, out->q_qinfo, &qif->qf_wqinit); +#endif + out->q_qinfo = &qif->qf_wqinit; + + ire_walk(ipf_ire_walk, (char *)qif); + RWLOCK_EXIT(&ipfs_mutex); + cmn_err(CE_CONT, "IP Filter: attach to [%s,%d] - %s\n", + qif->qf_name, il->ill_ppa, +#if SOLARIS2 >= 8 + il->ill_isv6 ? "IPv6" : "IPv4" +#else + "IPv4" +#endif + ); + } + if (!qif_head) + cmn_err(CE_CONT, "IP Filter: not attached to any interfaces\n"); + return; +} + + +/* + * look for bad consistancies between the list of interfaces the filter knows + * about and those which are currently configured. + */ +int ipfsync() +{ + register struct frentry *f; + register ipnat_t *np; + register qif_t *qif, **qp; + register ill_t *il; + queue_t *in, *out; + + WRITE_ENTER(&ipfs_mutex); + for (qp = &qif_head; (qif = *qp); ) { + for (il = ill_g_head; il; il = il->ill_next) + if ((qif->qf_ill == il) && + !strcmp(qif->qf_name, il->ill_name)) { +#if SOLARIS2 < 8 + mblk_t *m = il->ill_hdr_mp; + + qif->qf_hl = il->ill_hdr_length; + if (m && qif->qf_hl != (m->b_wptr - m->b_rptr)) + cmn_err(CE_NOTE, + "IP Filter: ILL Header Length Mismatch\n"); +#endif + break; + } + if (il) { + qp = &qif->qf_next; + continue; + } + cmn_err(CE_CONT, "IP Filter: detaching [%s] - %s\n", + qif->qf_name, +#if SOLARIS2 >= 8 + (qif->qf_sap == IP6_DL_SAP) ? "IPv6" : "IPv4" +#else + "IPv4" +#endif + ); + *qp = qif->qf_next; + + /* + * Disable any rules directly associated with this interface + */ + WRITE_ENTER(&ipf_nat); + for (np = nat_list; np; np = np->in_next) + if (np->in_ifp == (void *)qif->qf_ill) + np->in_ifp = (struct ifnet *)-1; + RWLOCK_EXIT(&ipf_nat); + WRITE_ENTER(&ipf_mutex); + for (f = ipfilter[0][fr_active]; f; f = f->fr_next) + if (f->fr_ifa == (void *)qif->qf_ill) + f->fr_ifa = (struct ifnet *)-1; + for (f = ipfilter[1][fr_active]; f; f = f->fr_next) + if (f->fr_ifa == (void *)qif->qf_ill) + f->fr_ifa = (struct ifnet *)-1; +#if SOLARIS2 >= 8 + for (f = ipfilter6[0][fr_active]; f; f = f->fr_next) + if (f->fr_ifa == (void *)qif->qf_ill) + f->fr_ifa = (struct ifnet *)-1; + for (f = ipfilter6[1][fr_active]; f; f = f->fr_next) + if (f->fr_ifa == (void *)qif->qf_ill) + f->fr_ifa = (struct ifnet *)-1; +#endif + +#if 0 /* XXX */ + /* + * As well as the ill disappearing when a device is unplumb'd, + * it also appears that the associated queue structures also + * disappear - at least in the case of ppp, which is the most + * volatile here. Thanks to Greg for finding this problem. + */ + /* + * Restore q_qinfo pointers in interface queues + */ + out = qif->qf_out; + in = qif->qf_in; + if (in) { +# ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: ipfsync: in queue(%lx)->q_qinfo FROM %lx TO %lx", + in, in->q_qinfo, qif->qf_rqinfo); +# endif + in->q_qinfo = qif->qf_rqinfo; + } + if (out) { +# ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: ipfsync: out queue(%lx)->q_qinfo FROM %lx TO %lx", + out, out->q_qinfo, qif->qf_wqinfo); +# endif + out->q_qinfo = qif->qf_wqinfo; + } +#endif /* XXX */ + RWLOCK_EXIT(&ipf_mutex); + KFREE(qif); + qif = *qp; + } + RWLOCK_EXIT(&ipfs_mutex); + solattach(); + + frsync(); + /* + * Resync. any NAT `connections' using this interface and its IP #. + */ + for (il = ill_g_head; il; il = il->ill_next) { + ip_natsync((void *)il); + ip_statesync((void *)il); + } + return 0; +} + + +/* + * unhook the IP filter from all defined interfaces with IP addresses + */ +int soldetach() +{ + queue_t *in, *out; + qif_t *qif, **qp; + ill_t *il; + + WRITE_ENTER(&ipfs_mutex); + /* + * Make two passes, first get rid of all the unknown devices, next + * unlink known devices. + */ + for (qp = &qif_head; (qif = *qp); ) { + for (il = ill_g_head; il; il = il->ill_next) + if (qif->qf_ill == il) + break; + if (il) { + qp = &qif->qf_next; + continue; + } + cmn_err(CE_CONT, "IP Filter: removing [%s]\n", qif->qf_name); + *qp = qif->qf_next; + KFREE(qif); + } + + while ((qif = qif_head)) { + qif_head = qif->qf_next; + for (il = ill_g_head; il; il = il->ill_next) + if (qif->qf_ill == il) + break; + if (il) { + in = qif->qf_in; + out = qif->qf_out; + cmn_err(CE_CONT, "IP Filter: detaching [%s,%d] - %s\n", + qif->qf_name, il->ill_ppa, +#if SOLARIS2 >= 8 + (qif->qf_sap == IP6_DL_SAP) ? "IPv6" : "IPv4" +#else + "IPv4" +#endif + ); + +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: soldetach: in queue(%lx)->q_qinfo FROM %lx TO %lx", + in, in->q_qinfo, qif->qf_rqinfo); +#endif + in->q_qinfo = qif->qf_rqinfo; + + /* + * and the write queue... + */ +#ifdef IPFDEBUG + if (ipf_debug) + cmn_err(CE_NOTE, + "IP Filter: soldetach: out queue(%lx)->q_qinfo FROM %lx TO %lx", + out, out->q_qinfo, qif->qf_wqinfo); +#endif + out->q_qinfo = qif->qf_wqinfo; + } + KFREE(qif); + } + RWLOCK_EXIT(&ipfs_mutex); + return ipldetach(); +} + + +#ifdef IPFDEBUG +void printire(ire) +ire_t *ire; +{ + if (!ipf_debug) + return; + printf("ire: ll_hdr_mp %p rfq %p stq %p src_addr %x max_frag %d\n", +# if SOLARIS2 >= 8 + NULL, +# else + ire->ire_ll_hdr_mp, +# endif + ire->ire_rfq, ire->ire_stq, + ire->ire_src_addr, ire->ire_max_frag); + printf("ire: mask %x addr %x gateway_addr %x type %d\n", + ire->ire_mask, ire->ire_addr, ire->ire_gateway_addr, + ire->ire_type); + printf("ire: ll_hdr_length %d ll_hdr_saved_mp %p\n", + ire->ire_ll_hdr_length, +# if SOLARIS2 >= 8 + NULL +# else + ire->ire_ll_hdr_saved_mp +# endif + ); +} +#endif + + +int ipfr_fastroute(ip, mb, mpp, fin, fdp) +ip_t *ip; +mblk_t *mb, **mpp; +fr_info_t *fin; +frdest_t *fdp; +{ +#ifdef USE_INET6 + ip6_t *ip6 = (ip6_t *)ip; +#endif + ire_t *ir, *dir, *gw; + struct in_addr dst; + queue_t *q = NULL; + mblk_t *mp = NULL; + size_t hlen = 0; + frentry_t *fr; + frdest_t fd; + ill_t *ifp; + u_char *s; + qif_t *qf; + int p; + +#ifndef sparc + u_short __iplen, __ipoff; +#endif + qf = fin->fin_qif; + + /* + * If this is a duplicate mblk then we want ip to point at that + * data, not the original, if and only if it is already pointing at + * the current mblk data. + */ + if ((ip == (ip_t *)qf->qf_m->b_rptr) && (qf->qf_m != mb)) + ip = (ip_t *)mb->b_rptr; + + /* + * If there is another M_PROTO, we don't want it + */ + if (*mpp != mb) { + mp = *mpp; + (void) unlinkb(mp); + mp = (*mpp)->b_cont; + (*mpp)->b_cont = NULL; + (*mpp)->b_prev = NULL; + freemsg(*mpp); + *mpp = mp; + } + + if (!fdp) { + ipif_t *ipif; + + ifp = fin->fin_ifp; + ipif = ifp->ill_ipif; + if (!ipif) + goto bad_fastroute; +#if SOLARIS2 > 5 + ir = ire_ctable_lookup(ipif->ipif_local_addr, 0, IRE_LOCAL, + NULL, NULL, MATCH_IRE_TYPE); +#else + ir = ire_lookup_myaddr(ipif->ipif_local_addr); +#endif + if (!ir) + ir = (ire_t *)-1; + + fd.fd_ifp = (struct ifnet *)ir; + fd.fd_ip = ip->ip_dst; + fdp = &fd; + } + + ir = (ire_t *)fdp->fd_ifp; + + if (fdp->fd_ip.s_addr) + dst = fdp->fd_ip; + else + dst.s_addr = fin->fin_fi.fi_daddr; + +#if SOLARIS2 >= 6 + gw = NULL; + if (fin->fin_v == 4) { + p = ip->ip_p; + dir = ire_route_lookup(dst.s_addr, 0xffffffff, 0, 0, NULL, + &gw, NULL, MATCH_IRE_DSTONLY| + MATCH_IRE_DEFAULT|MATCH_IRE_RECURSIVE); + } +# ifdef USE_INET6 + else if (fin->fin_v == 6) { + p = ip6->ip6_nxt; + dir = ire_route_lookup_v6(&ip6->ip6_dst, NULL, 0, 0, + NULL, &gw, NULL, MATCH_IRE_DSTONLY| + MATCH_IRE_DEFAULT|MATCH_IRE_RECURSIVE); + } +# endif +#else + dir = ire_lookup(dst.s_addr); +#endif +#if SOLARIS2 < 8 + if (dir) + if (!dir->ire_ll_hdr_mp || !dir->ire_ll_hdr_length) + dir = NULL; +#else + if (dir) + if (!dir->ire_fp_mp || !dir->ire_dlureq_mp) + dir = NULL; +#endif + + if (!ir) + ir = dir; + + if (ir && dir) { + ifp = ire_to_ill(ir); + if (ifp == NULL) + goto bad_fastroute; + fr = fin->fin_fr; + + /* + * In case we're here due to "to <if>" being used with + * "keep state", check that we're going in the correct + * direction. + */ + if ((fr != NULL) && (fdp->fd_ifp != NULL) && + (fin->fin_rev != 0) && (fdp == &fr->fr_tif)) + return 1; + + fin->fin_ifp = ifp; + if (fin->fin_out == 0) { + fin->fin_fr = ipacct[1][fr_active]; + if ((fin->fin_fr != NULL) && + (fr_scanlist(FR_NOMATCH, ip, fin, mb)&FR_ACCOUNT)){ + ATOMIC_INCL(frstats[1].fr_acct); + } + fin->fin_fr = NULL; + if (!fr || !(fr->fr_flags & FR_RETMASK)) + (void) fr_checkstate(ip, fin); + (void) ip_natout(ip, fin); + } +#ifndef sparc + if (fin->fin_v == 4) { + __iplen = (u_short)ip->ip_len, + __ipoff = (u_short)ip->ip_off; + + ip->ip_len = htons(__iplen); + ip->ip_off = htons(__ipoff); + } +#endif + +#if SOLARIS2 < 8 + mp = dir->ire_ll_hdr_mp; + hlen = dir->ire_ll_hdr_length; +#else + mp = dir->ire_fp_mp; + hlen = mp ? mp->b_wptr - mp->b_rptr : 0; + mp = dir->ire_dlureq_mp; +#endif + if (mp != NULL) { + s = mb->b_rptr; + if ( +#if SOLARIS2 >= 6 + (dohwcksum && + ifp->ill_ick.ick_magic == ICK_M_CTL_MAGIC) || +#endif + (hlen && (s - mb->b_datap->db_base) >= hlen)) { + s -= hlen; + mb->b_rptr = (u_char *)s; + bcopy((char *)mp->b_rptr, (char *)s, hlen); + } else { + mblk_t *mp2; + + mp2 = copyb(mp); + if (!mp2) + goto bad_fastroute; + linkb(mp2, mb); + mb = mp2; + } + } + *mpp = mb; + + if (ir->ire_stq) + q = ir->ire_stq; + else if (ir->ire_rfq) + q = WR(ir->ire_rfq); + if (q) { + mb->b_prev = NULL; + mb->b_queue = q; + RWLOCK_EXIT(&ipfs_mutex); + RWLOCK_EXIT(&ipf_solaris); +#if SOLARIS2 >= 6 + if ((p == IPPROTO_TCP) && dohwcksum && + (ifp->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { + tcphdr_t *tcp; + u_32_t t; + + tcp = (tcphdr_t *)((char *)ip + fin->fin_hlen); + t = ip->ip_src.s_addr; + t += ip->ip_dst.s_addr; + t += 30; + t = (t & 0xffff) + (t >> 16); + tcp->th_sum = t & 0xffff; + } +#endif + putnext(q, mb); + READ_ENTER(&ipf_solaris); + READ_ENTER(&ipfs_mutex); + ipl_frouteok[0]++; + *mpp = NULL; + return 0; + } + } +bad_fastroute: + mb->b_prev = NULL; + freemsg(mb); + ipl_frouteok[1]++; + *mpp = NULL; + return -1; +} + + +void copyout_mblk(m, off, len, buf) +mblk_t *m; +size_t off, len; +char *buf; +{ + u_char *s, *bp = (u_char *)buf; + size_t mlen, olen, clen; + + for (; m && len; m = m->b_cont) { + if (MTYPE(m) != M_DATA) + continue; + s = m->b_rptr; + mlen = m->b_wptr - s; + olen = MIN(off, mlen); + if ((olen == mlen) || (olen < off)) { + off -= olen; + continue; + } else if (olen) { + off -= olen; + s += olen; + mlen -= olen; + } + clen = MIN(mlen, len); + bcopy(s, bp, clen); + len -= clen; + bp += clen; + } +} + + +void copyin_mblk(m, off, len, buf) +mblk_t *m; +size_t off, len; +char *buf; +{ + u_char *s, *bp = (u_char *)buf; + size_t mlen, olen, clen; + + for (; m && len; m = m->b_cont) { + if (MTYPE(m) != M_DATA) + continue; + s = m->b_rptr; + mlen = m->b_wptr - s; + olen = MIN(off, mlen); + if ((olen == mlen) || (olen < off)) { + off -= olen; + continue; + } else if (olen) { + off -= olen; + s += olen; + mlen -= olen; + } + clen = MIN(mlen, len); + bcopy(bp, s, clen); + len -= clen; + bp += clen; + } +} + + +int fr_verifysrc(ipa, ifp) +struct in_addr ipa; +void *ifp; +{ + ire_t *ir, *dir, *gw; + +#if SOLARIS2 >= 6 + dir = ire_route_lookup(ipa.s_addr, 0xffffffff, 0, 0, NULL, &gw, NULL, + MATCH_IRE_DSTONLY|MATCH_IRE_DEFAULT| + MATCH_IRE_RECURSIVE); +#else + dir = ire_lookup(ipa.s_addr); +#endif + + if (!dir) + return 0; + return (ire_to_ill(dir) == ifp); +} |