blob: 2021423418f7905d7c11b0e00e68dc0d2d3a6ae6 [file] [log] [blame] [raw]
/*
* 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$"
#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 streamtab ipinfo;
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 int ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t,
void *, void **));
static int ipf_probe __P((dev_info_t *));
#if SOLARIS2 < 10
static int ipf_identify __P((dev_info_t *));
#endif
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;
qif_t *_qif_head = NULL;
/* #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,
#if SOLARIS2 >= 10
nulldev,
#else
ipf_identify,
#endif
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) {
WRITE_ENTER(&ipf_solaris);
ipfsync();
RWLOCK_EXIT(&ipf_solaris);
}
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;
}
#if SOLARIS2 < 10
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);
}
#endif
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
#ifdef DDI_NO_AUTODETACH
if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
DDI_NO_AUTODETACH, 1) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "!updating DDI_NO_AUTODETACH failed");
return DDI_FAILURE;
}
#else
if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
"ddi-no-autodetach", 1) !=
DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "!updating ddi-no-autodetach failed");
return DDI_FAILURE;
}
#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;
/*
* 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);
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);
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 */
}
#if SOLARIS2 >= 8
/* Skip tunnel header for tunnel devices */
if (out && off == 0 && qif->qf_tunoff > 0) {
ipha_t *ipha;
off = qif->qf_tunoff;
ipha = (ipha_t *)(m->b_rptr + off);
off += IPH_HDR_LENGTH(ipha);
}
#endif
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_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).
*/
hlen = iphlen;
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;
case IPPROTO_ESP :
hlen += 8;
break;
default :
break;
}
woff = 0;
if (hlen > mlen) {
hlen = mlen;
} else if ((char *)m->b_wptr - (char *)ip > plen) {
woff = (char *)m->b_wptr - (char *)ip - 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 + off)) {
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_data = ip;
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) && ((void *)ip == (void *)qif->qf_data)) {
m->b_wptr += woff;
m->b_rptr -= off;
}
ip = qif->qf_data;
#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
}
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;
{
int (*func) __P((queue_t *, mblk_t *));
struct iocblk *ioc;
int ret;
if (fr_running <= 0) {
func = ipf_ip_inp;
if (func == NULL) /* already restored ? */
return (*ipinfo.st_wrinit->qi_putp)(q,mb);
return (*func)(q, mb);
}
if (MTYPE(mb) != M_IOCTL)
return (*ipf_ip_inp)(q, mb);
READ_ENTER(&ipf_solaris);
if (fr_running <= 0) {
RWLOCK_EXIT(&ipf_solaris);
func = ipf_ip_inp;
if (func == NULL) /* already restored ? */
return (*ipinfo.st_wrinit->qi_putp)(q,mb);
return (*func)(q, mb);
}
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;
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()
{
static int first_run = 1;
queue_t *in, *out;
struct frentry *f;
size_t len, illen;
qif_t *qif, *qf2;
ipnat_t *np;
ill_t *il;
#if SOLARIS2 >= 10
ill_walk_context_t ctx;
for (il = ILL_START_WALK_ALL(&ctx); il; il = ill_next(&ctx, il))
#else
for (il = ill_g_head; il; il = il->ill_next)
#endif
{
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;
/*
* Tunnels are special; they have a 32 byte header followed
* by the encapsulated header.
*/
if (strncmp(il->ill_name, "ip.tun", 6) == 0)
qif->qf_tunoff = 32; /* no constant defined */
else
qif->qf_tunoff = 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
*/
illen = (size_t)il->ill_name_length;
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);
if ((len != 0) &&
(len + 1 == illen) &&
!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);
if ((len != 0) &&
(len + 1 == illen) &&
!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);
if ((len != 0) &&
(len + 1 == illen) &&
!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);
if ((len != 0) &&
(len + 1 == illen) &&
!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);
if ((len != 0) &&
(len + 1 == illen) &&
!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;
if (first_run == 1)
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
);
}
first_run = 0;
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;
#if SOLARIS2 >= 10
ill_walk_context_t ctx;
#endif
queue_t *in, *out;
void *ifp;
WRITE_ENTER(&ipfs_mutex);
for (qp = &_qif_head; (qif = *qp); ) {
#if SOLARIS2 >= 10
il = ILL_START_WALK_ALL(&ctx);
for (; il; il = ill_next(&ctx, il))
#else
for (il = ill_g_head; il; il = il->ill_next)
#endif
if ((qif->qf_sap == il->ill_sap) &&
!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 != NULL) &&
(qif->qf_hl != (m->b_wptr - m->b_rptr)))
cmn_err(CE_NOTE,
"IP Filter: ILL Header Length Mismatch\n");
#endif
qif->qf_ill = il;
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;
ifp = qif->qf_ill;
qif->qf_ill = (void *)-1;
/*
* 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 == ifp)
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 == ifp)
f->fr_ifa = (struct ifnet *)-1;
for (f = ipfilter[1][fr_active]; f; f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (struct ifnet *)-1;
#if SOLARIS2 >= 8
for (f = ipfilter6[0][fr_active]; f; f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (struct ifnet *)-1;
for (f = ipfilter6[1][fr_active]; f; f = f->fr_next)
if (f->fr_ifa == ifp)
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();
/*
* Resync. any NAT `connections' using this interface and its IP #.
*/
frsync(NULL);
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;
#if SOLARIS2 >= 10
ill_walk_context_t ctx;
#endif
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); ) {
#if SOLARIS2 >= 10
il = ILL_START_WALK_ALL(&ctx);
for (; il; il = ill_next(&ctx, il))
#else
for (il = ill_g_head; il; il = il->ill_next)
#endif
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;
#if SOLARIS2 >= 10
il = ILL_START_WALK_ALL(&ctx);
for (; il; il = ill_next(&ctx, il))
#else
for (il = ill_g_head; il; il = il->ill_next)
#endif
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;
if (mp == NULL)
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);
}