www.pudn.com > Firewall_PNE_3_3.zip > fwRewrite.c, change:2009-03-16,size:8549b


/* fwRewrite.c - Firewall rewrite filter */

/* Copyright 2000-2005 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/*
modification history
--------------------
01g,07oct05,myz  Fixed ipv6 tcp reset problem.
01f,10mar05,svk  Fix compilation warning
01e,13dec04,myz  added IPV6 support
01d,13jul04,myz  port to dual stack
01c,07apr04,myz  Removed TH_ACK flag from the reset packet.
01b,29jan04,myz  Not to zero rcvif to fix the problem of sending RESET packet. 
01a,24nov03,myz  created from BSD/OS IPFW rewrite_filter
*/

/* includes */

#include "fw.h"

#undef FW_REWRITE_DEBUG

#ifdef FW_REWRITE_DEBUG

int fwRewriteDebug = 1;
#define DBG_PRINT(X) \
    do { \
    if (fwRewriteDebug) \
        printf X; \
    }while(0)
#else

#define DBG_PRINT(X)

#endif

u_long
fwRewrite (filterp, m, dir, iopt)
	ipfw_t *filterp;
	struct mbuf *m;
	int dir;
	ipfw_opt_t *iopt;
    {
    struct mbuf *m0;
    struct tcphdr *tcp, *tcp0;
    u_long w, x, y, z;
    RULE_LIST_DESC * pDesc;
    int hlen;
    int nhlen;
    int pktLen;

    pDesc = (RULE_LIST_DESC *)(filterp->private);


    /*
     * Flag 1 means we reassemble this packet
     */
    if ((dir & IPFWF_FILTERM) == IPFWF_FILTER1) 
        {
        int errStatus;

#ifdef INET6  
        if (((*mtod(m,UINT8 *)) & IPV6_VERSION_MASK) == IPV6_VERSION)
            {
	    extern int frag6_input (struct mbuf **, int *,int);
            int nxtExtHdr;

            nxtExtHdr = frag6_input(&m,&iopt->extra,IPPROTO_FRAGMENT);
            iopt->mbuf = m;
            iopt->bits |= IPFWB_MBUF;

            if (nxtExtHdr == IPPROTO_DONE)
                {
       
                DBG_PRINT(("Rewrite: consume one fragment\n"));
		m = 0; /* prevent to be freed */
                goto drop;
                }
            else
                {
                DBG_PRINT(("Rewrite: Fragments Assembled,pktLen:0x%x\n",
		    m->m_pkthdr.len));
                goto accept;
                }
            }
        else
#endif
            {
	    extern struct mbuf * ipFragReassemble (struct mbuf *, 
                                                struct ip *, int, int *);
            struct ip *ip = NULL;
	    ip = mtod(m, struct ip *);
            hlen = ip->ip_hl << 2;

	    if ((ip->ip_off & (IP_OFFMASK|IP_MF)) == 0) 
                goto drop;
            
            /*
             * Look for queue of fragments
             * of this datagram.
             */

            m = ipFragReassemble(m,ip,hlen,&errStatus);

            if (errStatus == ERROR)
                {
                DBG_PRINT(("Rewrite: error, packet dropped\n"));
                goto drop;
                }

            iopt->mbuf = m;
            iopt->bits |= IPFWB_MBUF;

            if (m == 0)
                {
                DBG_PRINT(("Rewrite: consume one fragment\n"));
                goto drop;
                }
 
            ip = mtod(m, struct ip *);

            /* Get the header length of the reassembled packet */

            /*
             * NOTE: the BSD stack originally did IP_DIVERT
             * processing here. This has been removed in the
             * WRS stack.
             */
            hlen = ip->ip_hl << 2; 
            ip->ip_len += hlen;
            DBG_PRINT(("Rewrite: Fragments Assembled,ipLen:0x%x,pktLen:0x%x\n",
		    ip->ip_len,m->m_pkthdr.len));
            goto accept;
	    }
        }

#ifdef INET6   
    if (((*mtod(m,UINT8 *)) & IPV6_VERSION_MASK) == IPV6_VERSION)
        {
        struct ip6_hdr * pIpv6;
        int proto;

        pIpv6 = mtod(m,struct ip6_hdr *);

        pktLen = ntohs(pIpv6->ip6_plen);
        if (!ipv6UpHdrOffsetGet(pIpv6,&proto,&hlen,pktLen))
            goto drop;

        nhlen = sizeof(struct ip6_hdr);
 
	}
    else   /* IPV4 */
#endif
        {
        struct ip * ip;

        ip = mtod(m,struct ip *);

	if (ip->ip_p != IPPROTO_TCP)
            goto drop;

        hlen = ip->ip_hl << 2;
        nhlen = sizeof(struct ip);
        pktLen = ntohs(ip->ip_len) - hlen;
        }

    /*
     * If the first mbuf does not have both headers, make it so.
     */
    if (m->m_len < hlen + sizeof(struct tcphdr)) {
	m = m_pullup(m, hlen + sizeof(struct tcphdr));
	if (m == NULL) {
		iopt->mbuf = m;
		iopt->bits |= IPFWB_MBUF;
		goto drop;
	}
    }

    /*
     * Truncate the mbuf down.
     * We know everything fits in the first mbuf
     * Also let the upper level know we have changed something.
     */
    m->m_len = m->m_pkthdr.len = hlen + sizeof(struct tcphdr);

/* don't zero it, so we don't get the bogus value when ip_input routine checks
 * the flag IFF_DONT_FORWARD in if_flags. 
 */
/*	m->m_pkthdr.rcvif = NULL; */

    iopt->mbuf = m;
    iopt->bits |= IPFWB_MBUF;

    if ((m0 = m->m_next) != NULL) {
	m->m_next = NULL;
	m_freem(m0);
    }

    tcp = (struct tcphdr *)(mtod(m,char *) + nhlen);
    tcp0 = (struct tcphdr *)(mtod(m,char *) + hlen);

    /*
     * tcp0 and tcp will almost certainly point to the
     * same address or overlapping addresses.
     * So don't write into tcp until we have read out from tcp0.
     */

    x = tcp0->th_sport;
    y = tcp0->th_dport;
    z = ntohl(tcp0->th_seq) + pktLen - (tcp0->th_off << 2);
    w = tcp0->th_ack;
    ++z;
	
    tcp->th_sport = y;
    tcp->th_dport = x;

    if (tcp0->th_flags & TH_ACK)
        {
        tcp->th_seq = w;
        tcp->th_flags = TH_RST;
        }
    else
        {
        tcp->th_seq = 0;
        tcp->th_flags = TH_ACK|TH_RST;
        }
    tcp->th_ack = htonl(z);
    tcp->th_off = 5;
    tcp->th_win = 0;
    tcp->th_sum = 0;
    tcp->th_urp = 0;

    /*
     * Set up the IP header for a TCP Checksum
     * We need only save the TTL field for later use
     */

#ifdef INET6    
    if (((*mtod(m,UINT8 *)) & IPV6_VERSION_MASK) == IPV6_VERSION)
        {
        struct ip6_hdr * pIpv6;
        char savedSrc[sizeof(struct in6_addr)];

        pIpv6 = mtod(m,struct ip6_hdr *);

        y = pIpv6->ip6_hlim;
        pIpv6->ip6_hlim = IPPROTO_TCP; /* temp, set for tcp cksum */
        pIpv6->ip6_vfc  = 0;   /* upper four class bits set to 0 */
        pIpv6->ip6_flow = 20;     /* temp,  set as len for tcp cksum */
        pIpv6->ip6_flow = htonl(pIpv6->ip6_flow);
        pIpv6->ip6_plen = 0;
        pIpv6->ip6_nxt  = 0;

        /*
         * Swap the addresses
         */
        bcopy((char *)pIpv6->ip6_src.s6_addr8,
	      savedSrc,
	      sizeof(struct in6_addr));
        bcopy((char *)pIpv6->ip6_dst.s6_addr8,
	      (char *)pIpv6->ip6_src.s6_addr8,
              sizeof(struct in6_addr));
        bcopy(savedSrc,
	      (char *)pIpv6->ip6_dst.s6_addr8,
	      sizeof(struct in6_addr));
        tcp->th_sum = in_cksum(m, m->m_len);

#if 0
        /* assign the source address to INADDR_ANY instead of ip->ip_dst
         * to avoid to get a ICMP redirect message because of this TCP
         * RESET packet. Can't do this trick for IPV6. the stack will generate
	 * a ICMPV6 and send it to itself because of this TCP RESET packet.
	 * Should cause no harm.
         */

        bzero((char *)pIpv6->ip6_src.s6_addr8,
	      sizeof(struct in6_addr)); /*IN6ADDR_ANY*/
#endif
        pIpv6->ip6_hlim = y;
        pIpv6->ip6_flow = 0;     
        pIpv6->ip6_vfc = IPV6_VERSION;   /* upper four class bits set to 0 */
        pIpv6->ip6_plen = htons(sizeof(struct tcphdr));
        pIpv6->ip6_nxt  = IPPROTO_TCP;
        }
    else   /* V4 */
#endif
        {
        struct ip * ip;

        ip = mtod(m,struct ip *);

        y = ip->ip_ttl;

        ip->ip_v = 0;
        ip->ip_hl = 0;
        ip->ip_tos = 0;
        ip->ip_len = 20;
        HTONS(ip->ip_len);
        ip->ip_id = 0;
        ip->ip_off = 0;
        /* ip->ip_p = IPPROTO_TCP;  This is just true */
        ip->ip_ttl = 0;
        ip->ip_sum = 0;
        
        /*
         * Swap the addresses
         */
        x = ip->ip_src.s_addr;
        ip->ip_src.s_addr = ip->ip_dst.s_addr;
        ip->ip_dst.s_addr = x;
        tcp->th_sum = in_cksum(m, m->m_len);

        /* assign the source address to INADDR_ANY instead of ip->ip_dst
         * to avoid to get a ICMP redirect message because of this TCP
         * RESET packet.
         */

        ip->ip_src.s_addr = INADDR_ANY;


        /*
         * TCP checksum is done, put in the real data
         */
        ip->ip_v = 4;
        ip->ip_hl = 5;
        ip->ip_ttl = y;
        ip->ip_len = 40;
        }
accept:
	pDesc->stats.accepted++; 
	return (FW_ACCEPT);

drop:
	pDesc->stats.denied++; 

	if (((dir & IPFWF_DONTFREE) == 0) && m) 
		m_freem(m);
	return(FW_REJECT);  
    }