summaryrefslogtreecommitdiffstats
path: root/solaris.c
diff options
context:
space:
mode:
Diffstat (limited to 'solaris.c')
-rw-r--r--solaris.c2131
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);
+}
OpenPOWER on IntegriCloud