/* * Copyright (C) 1993-2001 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.20 2001/07/18 14:58:28 darrenr Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if SOLARIS2 >= 6 # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #ifdef IPFDEBUG void printire __P((ire_t *)); #endif 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, 0 }, { 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 cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst); #endif return ipfinst; } int _fini(void) { int ipfinst; ipfinst = mod_remove(&modlink1); #ifdef IPFDEBUG 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 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 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 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; 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); 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 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 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; if (!synced) { synced = 1; RWLOCK_EXIT(&ipfs_mutex); ipfsync(); READ_ENTER(&ipfs_mutex); goto tryagain; } frstats[out].fr_notip++; return (fr_flags & FF_BLOCKNONIP) ? -1 : 0; } #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; if (fr_running <= 0) { mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); return 0; } if (mb->b_datap->db_ref > 1) { mblk_t *m1; m1 = copymsg(mb); if (!m1) { frstats[0].fr_drop++; mb->b_next = NULL; mb->b_prev = NULL; freemsg(mb); return 0; } m1->b_next = mb->b_next; mb->b_next = NULL; m1->b_prev = mb->b_prev; mb->b_prev = NULL; freemsg(mb); mb = m1; frstats[0].fr_copy++; } READ_ENTER(&ipf_solaris); again: if (fr_running <= 0) { RWLOCK_EXIT(&ipf_solaris); mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); 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, MTYPE(mb), 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++; RWLOCK_EXIT(&ipf_solaris); mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); return 0; } pnext = qif->qf_rqinfo->qi_putp; type = MTYPE(mb); if (type == M_IOCACK) fr_qif_update(qif, mb); else { bcopy((char *)qif, (char *)&qf, sizeof(qf)); if (datamsg(type) || (type == M_BREAK)) err = fr_precheck(&mb, q, &qf, 0); } RWLOCK_EXIT(&ipfs_mutex); RWLOCK_EXIT(&ipf_solaris); if ((err == 0) && (mb != NULL)) { if (pnext) return (*pnext)(q, mb); cmn_err(CE_WARN, "!IP Filter: inp NULL: qif %x %s q %x info %x", &qf, qf.qf_name, q, q->q_qinfo); } if (mb) { mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); } return 0; } 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; if (fr_running <= 0) { mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); return 0; } #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_next = NULL; mb->b_prev = NULL; freemsg(mb); return 0; } m1->b_next = mb->b_next; mb->b_next = NULL; m1->b_prev = mb->b_prev; mb->b_prev = NULL; freemsg(mb); mb = m1; frstats[1].fr_copy++; } READ_ENTER(&ipf_solaris); again: if (fr_running <= 0) { RWLOCK_EXIT(&ipf_solaris); mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); 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, MTYPE(mb), 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++; RWLOCK_EXIT(&ipf_solaris); mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); return 0; } pnext = qif->qf_wqinfo->qi_putp; type = MTYPE(mb); if (type == M_IOCACK) fr_qif_update(qif, mb); else { bcopy((char *)qif, (char *)&qf, sizeof(qf)); if (datamsg(type) || (type == M_BREAK)) err = fr_precheck(&mb, q, &qf, 1); } RWLOCK_EXIT(&ipfs_mutex); RWLOCK_EXIT(&ipf_solaris); if ((err == 0) && (mb != NULL)) { if (pnext) return (*pnext)(q, mb); cmn_err(CE_WARN, "!IP Filter: outp NULL: qif %x %s q %x info %x", &qf, qf.qf_name, q, q->q_qinfo); } if (mb) { mb->b_prev = NULL; mb->b_next = NULL; freemsg(mb); } return 0; } 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; mb->b_next = 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; mb->b_next = 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 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 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 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 IPFDEBUG 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 IPFDEBUG 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 IPFDEBUG 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]; 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 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 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 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 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 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 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; { 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; (*mpp)->b_next = 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 " 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_next = 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; mb->b_next = 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); }