[ This is an accumulation of earlier patches plus a couple additional
changes. The general intent is to simplify / harden streams buffer
handling when running on Solaris. ]
IP Filter bug report form.
--------------------------
IP Filter Version: 4.1.6
Operating System Version: Solaris 10 x86
Configuration: LKM
Description of problem:
Using IP Filter with an IP in IP tunnel protected by transport mode
IPSEC panics under Solaris 10 x86. It possible that the same panic
may be triggered by other things.
How to repeat:
Install IP Filter, create /etc/hostname.ip.tun0 containing:
feith-bb-vpn feith-asp-vpn netmask 0xfffffffc \
tsrc feith-bb tdst prepnet-rt mtu 1440 up
Also configure /etc/inet/ipsecinit.conf, /etc/inet/ike/config,
and /etc/inet/secret/ike.preshared so that transport mode IPSEC
protects the tunnel. Setup ipf.conf so it simply passes all
(the actual rules don't appear to affect the problem).
Attempt to connect to the system through the tunnel and watch
it panic. The enclosed * lightly tested * patch to SunOS/pfildrv.c
fixes the problem.
*** SunOS/pfildrv.c.ORIGINAL Sat May 8 13:25:55 2004
--- SunOS/pfildrv.c Mon Feb 28 19:23:21 2005
*************** int pfil_precheck(queue_t *q, mblk_t **m
*** 431,437 ****
{
register struct ip *ip;
size_t hlen, len, off, mlen, iphlen, plen;
! int err, out, sap, realigned = 0;
packet_filter_hook_t *pfh;
qpktinfo_t qpkt, *qpi;
struct pfil_head *ph;
--- 431,437 ----
{
register struct ip *ip;
size_t hlen, len, off, mlen, iphlen, plen;
! int err, out, sap;
packet_filter_hook_t *pfh;
qpktinfo_t qpkt, *qpi;
struct pfil_head *ph;
*************** int pfil_precheck(queue_t *q, mblk_t **m
*** 466,472 ****
*/
out = (flags & PFIL_OUT) ? 1 : 0;
off = (out) ? qpi->qpi_hl : 0;
! tryagain:
ip = NULL;
m = NULL;
--- 466,472 ----
*/
out = (flags & PFIL_OUT) ? 1 : 0;
off = (out) ? qpi->qpi_hl : 0;
!
ip = NULL;
m = NULL;
*************** tryagain:
*** 542,594 ****
}
/*
- * If there is more than one copy of this message traversing the
- * STREAMS stack (ie packet is being used for snoop data) then make a
- * copy of it for our use so we become the sole owner of the new
- * message and do a freemsg() on the one passed in as we're no longer
- * using it or passing it up.
- */
- if ((pfil_delayed_copy == 0) && (m->b_datap->db_ref > 1)) {
- mblk_t *new;
-
- forced_copy:
- new = copymsg(m);
- if (new == NULL) {
- atomic_add_long(&qif->qf_copyfail, 1);
- return -3;
- }
- atomic_add_long(&qif->qf_copy, 1);
-
- if (mt != m)
- mt->b_cont = new;
- else {
- *mp = new;
- mt = new;
- }
- freemsg(m);
- m = new;
- }
-
- ip = (struct ip *)(m->b_rptr + off);
-
- /*
* 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) {
atomic_add_long(&qif->qf_nodata, 1);
return -4; /* not enough data for IP */
}
- ip = (struct ip *)(m->b_rptr + len);
}
! off = (u_char *)ip - m->b_rptr;
! mlen = msgdsize(m) - off;
sap = qif->qf_sap;
if (mlen == 0)
! mlen = mt->b_wptr - mt->b_rptr;
if (sap == ETHERTYPE_IP) {
u_short tlen;
--- 542,661 ----
}
/*
* 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 ((len = m->b_wptr - m->b_rptr) <= off) {
! off -= len;
m = m->b_cont;
if (m == NULL) {
atomic_add_long(&qif->qf_nodata, 1);
return -4; /* not enough data for IP */
}
}
!
sap = qif->qf_sap;
+
+ ip = (struct ip *)(m->b_rptr + off);
+ len = m->b_wptr - m->b_rptr - off;
+ mlen = msgdsize(m);
if (mlen == 0)
! mlen = m->b_wptr - m->b_rptr;
! mlen -= off;
!
! #ifdef PFILDEBUG
! /*LINTED: E_CONSTANT_CONDITION*/
! PRINT(10,(CE_CONT,
! "!IP Filter[%s]: out %d len %ld/%ld sap %d ip %p b_rptr %p
off %ld m %p/%d/%ld/%p mt %p/%d/%ld/%p\n",
! qif->qf_name, out, len, mlen, sap,
! (void *)ip, (void *)m->b_rptr, off,
! (void *)m, MTYPE(m), MLEN(m), (void *)m->b_cont,
! (void *)mt, MTYPE(mt), MLEN(mt), (void *)mt->b_cont));
! #endif
!
! /*
! * If there is more than one copy of this message traversing the
! * STREAMS stack (ie the packet is being used for snoop data), the
! * IP header isn't on a 32bit aligned address, or the IP header
! * isn't contain within a single block, then make a copy which
! * meets our requirements and do a freemsg on the one passed in
! * since we're no longer using it or passing it up.
! */
!
! if ((pfil_delayed_copy == 0 && m->b_datap->db_ref > 1)
! || ((u_int)ip & 0x3) || len < sizeof(*ip)
! || (sap != ETHERTYPE_IP
! #if SOLARIS2 >= 8
! && sap != IP6_DL_SAP
! #endif
! )) {
! mblk_t *b;
! mblk_t *nm;
! mblk_t *nmt;
! mblk_t *previous_nm;
!
! forced_copy:
! nmt = NULL;
! previous_nm = NULL;
!
! /*
! * Duplicate the message block descriptors up to (and
! * including if the offset is non-zero) the block where
! * IP begins.
! */
! for (b = mt; b != m || off; b = b->b_cont) {
! nm = dupb(b);
! if (nm == NULL) {
! atomic_add_long(&qif->qf_copyfail, 1);
! if (nmt)
! freemsg(nmt);
! return -3;
! }
!
! nm->b_cont = NULL;
! if (nmt)
! linkb(previous_nm, nm);
! else
! nmt = nm;
! previous_nm = nm;
!
! /*
! * Set the length so the block only contains what
! * appears before IP.
! */
! if (b == m) {
! nm->b_wptr = nm->b_rptr + off;
! break;
! }
! }
!
! m->b_rptr += off;
! nm = msgpullup(m, -1);
! m->b_rptr -= off;
!
! if (nm == NULL) {
! atomic_add_long(&qif->qf_copyfail, 1);
! if (nmt)
! freemsg(nmt);
! return -3;
! }
!
! if (nmt)
! linkb(previous_nm, nm);
! else
! nmt = nm;
!
! freemsg(mt);
!
! *mp = nmt;
! mt = nmt;
! m = nm;
!
! ip = (struct ip *)m->b_rptr;
! len = m->b_wptr - m->b_rptr;
! mlen = len;
! off = 0;
! }
if (sap == ETHERTYPE_IP) {
u_short tlen;
*************** forced_copy:
*** 623,734 ****
}
#endif
else {
- hlen = 0;
sap = -1;
}
- len = m->b_wptr - m->b_rptr - off;
- #ifdef PFILDEBUG
- /*LINTED: E_CONSTANT_CONDITION*/
- PRINT(10,(CE_CONT,
- "!IP Filter[%s]: out %d len %ld/%ld sap %d ip %p b_rptr %p
off %ld m %p/%d/%ld/%p mt %p/%d/%ld/%p\n",
- qif->qf_name, out, len, mlen, sap,
- (void *)ip, (void *)m->b_rptr, off,
- (void *)m, MTYPE(m), MLEN(m), (void *)m->b_cont,
- (void *)mt, MTYPE(mt), MLEN(mt), (void *)mt->b_cont));
- #endif
-
- /*
- * Ok, the IP header isn't on a 32bit aligned address so fix this.
- */
- if (((u_int)ip & 0x3) || (len < sizeof(*ip)) || (sap == -1)) {
- mblk_t *m2, *m1;
- int off2;
-
- if (m->b_datap->db_ref > 1)
- goto forced_copy;
- /*
- * If we have already tried to realign the IP header and we
- * are back here, then the attempt has failed, so stop now
- * rather than try again (could keep on retrying with no
- * benefit.)
- */
- if (realigned) {
- atomic_add_long(&qif->qf_drop, 1);
- return EINVAL;
- }
- realigned = 1;
-
- len = msgdsize(m);
- if (len < sizeof(*ip)) {
- atomic_add_long(&qif->qf_bad, 1);
- return EINVAL;
- }
-
- /*
- * XXX - Now I understand how pullupmsg() & STREAMS messages
- * work better, this can possibly be junked in favour of using
- * pullupmsg() which will preserve all the dblk bits correctly,
- * as is done in fr_pullup in the ipf code.
- */
-
- /*
- * Junk using pullupmsg()
- */
- off2 = (u_int)ip & 0x3;
- if (off2)
- off2 = 4 - off2;
-
- m2 = allocb(len + off2, BPRI_HI);
- if (m2 == NULL) {
- atomic_add_long(&qif->qf_drop, 1);
- return ENOBUFS;
- }
-
- 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 != NULL)
- s = m1->b_rptr;
- }
-
- if ((mt != m) && (mt->b_cont == m) && (off == 0)) {
- /*
- * 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 != NULL)
- 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;
- }
-
- off = 0;
- goto tryagain;
- }
-
if (((sap == 0) && (ip->ip_v != IPVERSION))
#if SOLARIS2 >= 8
|| ((sap == IP6_DL_SAP) && (((ip6->ip6_vfc) & 0xf0) != 0x60))
#endif
) {
atomic_add_long(&qif->qf_notip, 1);
#ifdef PFILDEBUG
--- 690,703 ----
}
#endif
else {
sap = -1;
}
if (((sap == 0) && (ip->ip_v != IPVERSION))
#if SOLARIS2 >= 8
|| ((sap == IP6_DL_SAP) && (((ip6->ip6_vfc) & 0xf0) != 0x60))
#endif
+ || sap == -1
) {
atomic_add_long(&qif->qf_notip, 1);
#ifdef PFILDEBUG
*************** forced_copy:
*** 737,765 ****
return EINVAL;
}
- /*
- * The code in IPFilter assumes that both the ip_off and ip_len
- * fields are in host byte order, so convert them here to fulfill
- * that expectation.
- *
- * If the target compile host is non-SPARC, assume it is a little
- * endian machine, requiring the conversion of offset/length fields
- * to both be host byte ordered.
- */
- #ifndef sparc
- # if SOLARIS2 >= 8
- if (sap == IP6_DL_SAP) {
- ip6->ip6_plen = plen;
- } 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
--- 706,711 ----
*************** forced_copy:
*** 776,795 ****
/*
* 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);
- } else {
- # endif
- __ipoff = (u_short)ip->ip_off;
-
- ip->ip_len = htons(plen);
- ip->ip_off = htons(__ipoff);
- # if SOLARIS2 >= 8
- }
- # endif
- #endif
atomic_add_long(&qif->qf_bad, 1);
return EINVAL;
}
--- 722,727 ----
*************** forced_copy:
*** 808,815 ****
ip = (struct ip *)ALIGN32(m->b_rptr + off);
}
! if ((len > plen) && (off == 0))
! m->b_wptr -= len - plen;
qpi->qpi_m = m;
qpi->qpi_off = off;
--- 740,773 ----
ip = (struct ip *)ALIGN32(m->b_rptr + off);
}
! /*
! * Discard any excess data.
! */
! if (len > plen)
! m->b_wptr = m->b_rptr + off + plen;
!
! /*
! * The code in IPFilter assumes that both the ip_off and ip_len
! * fields are in host byte order, so convert them here to fulfill
! * that expectation.
! *
! * If the target compile host is non-SPARC, assume it is a little
! * endian machine, requiring the conversion of offset/length fields
! * to both be host byte ordered.
! */
! #ifndef sparc
! # if SOLARIS2 >= 8
! if (sap == IP6_DL_SAP) {
! ip6->ip6_plen = plen;
! } else {
! # endif
! __ipoff = (u_short)ip->ip_off;
! ip->ip_len = plen;
! ip->ip_off = ntohs(__ipoff);
! # if SOLARIS2 >= 8
! }
! # endif
! #endif
qpi->qpi_m = m;
qpi->qpi_off = off;
*************** forced_copy:
*** 829,837 ****
for (; pfh; pfh = pfh->pfil_next)
if (pfh->pfil_func) {
err = (*pfh->pfil_func)(ip, iphlen, qif, out, qpi, mp);
if (err || !*mp)
break;
- ip = qpi->qpi_data;
}
RW_EXIT(&ph->ph_lock);
--- 787,800 ----
for (; pfh; pfh = pfh->pfil_next)
if (pfh->pfil_func) {
err = (*pfh->pfil_func)(ip, iphlen, qif, out, qpi, mp);
+
+ /*
+ * fr_pullup may have allocated a new buffer.
+ */
+ ip = qpi->qpi_data;
+
if (err || !*mp)
break;
}
RW_EXIT(&ph->ph_lock);
-------------------------------------------------------------------------
| Feith Systems | Voice: 1-215-646-8000 | Email: [EMAIL PROTECTED] |
| John Wehle | Fax: 1-215-540-5495 | |
-------------------------------------------------------------------------