--- linux/net/ipv4/ip_masq.c.orig Sun Oct 7 11:43:11 2001 +++ linux/net/ipv4/ip_masq.c Sun Oct 7 15:56:42 2001 @@ -1414,6 +1414,107 @@ return ret; } +static int ip_masq_check_tcpudp(struct sk_buff *skb, + struct iphdr *iph, + union ip_masq_tphdr *h, + int size, int doff) +{ + int csum; + + if (h->uh->check == 0 && iph->protocol == IPPROTO_UDP) + return 0; + +#ifdef CONFIG_IP_MASQ_DEBUG + if (ip_masq_get_debug_level() > 3) { + skb->ip_summed = CHECKSUM_NONE; + } +#endif + /* Check that the checksum is OK */ + switch (skb->ip_summed) + { + case CHECKSUM_NONE: + csum = csum_partial(h->raw + doff, size - doff, 0); + skb->csum = csum_partial(h->raw, doff, csum); + case CHECKSUM_HW: + if (csum_tcpudp_magic(iph->saddr, iph->daddr, + size, iph->protocol, skb->csum)) + { + IP_MASQ_DEBUG(0, "Wrong %s checksum in %u.%u.%u.%u->%u.%u.%u.%u (size=%d)!\n", + masq_proto_name(iph->protocol), + NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr), + size); + return -1; + } + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + default: + /* CHECKSUM_UNNECESSARY */ + } + return 0; +} + +static inline u16 ip_masq_check_diff(u32 old, u32 new, u16 oldsum) +{ + u32 diff[2] = { old, new }; + + return csum_fold(csum_partial((char *) diff, sizeof(diff), + oldsum ^ 0xFFFF)); +} + +/* Incremental checksum update */ + +static inline void ip_masq_check_inc_update(union ip_masq_tphdr *h, + u32 oldip, u32 newip, u16 oldport, u16 newport, u8 protocol) +{ + u16 *checkp; + + if (protocol == IPPROTO_TCP) + checkp = &h->th->check; + else + checkp = &h->uh->check; + *checkp = ip_masq_check_diff(~oldip, newip, + ip_masq_check_diff(oldport ^ 0xFFFF, newport, *checkp)); + if (!*checkp && protocol == IPPROTO_UDP) + *checkp = 0xFFFF; +} + +/* Full checksum update */ +static inline void ip_masq_check_full_update(struct iphdr *iph, + union ip_masq_tphdr *h, + int size, int doff, int csum) +{ + switch (iph->protocol) { + case IPPROTO_TCP: + h->th->check = 0; + h->th->check=csum_tcpudp_magic(iph->saddr, iph->daddr, + size, iph->protocol, + csum_partial(h->raw , doff, csum)); + IP_MASQ_DEBUG(3, "%s %u.%u.%u.%u->%u.%u.%u.%u csum=%d (+%d)\n", + masq_proto_name(iph->protocol), + NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr), + h->th->check, + (char*) & (h->th->check) - (char*) h->raw); + + break; + case IPPROTO_UDP: + h->uh->check = 0; + h->uh->check=csum_tcpudp_magic(iph->saddr, iph->daddr, + size, iph->protocol, + csum_partial(h->raw , doff, csum)); + if (h->uh->check == 0) + h->uh->check = 0xFFFF; + IP_MASQ_DEBUG(3, "%s %u.%u.%u.%u->%u.%u.%u.%u csum=%d (+%d)\n", + masq_proto_name(iph->protocol), + NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr), + h->uh->check, + (char*) &(h->uh->check)- (char*) h->raw); + break; + } +} + int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb = *skb_p; @@ -1425,11 +1526,9 @@ /* * doff holds transport protocol data offset * csum holds its checksum - * csum_ok says if csum is valid */ int doff = 0; int csum = 0; - int csum_ok = 0; /* * We can only masquerade protocols with ports... and hack some ICMPs @@ -1438,7 +1537,6 @@ h.raw = (char*) iph + iph->ihl * 4; size = ntohs(iph->tot_len) - (iph->ihl * 4); - doff = proto_doff(iph->protocol, h.raw, size); if (doff <= 0) { /* @@ -1455,57 +1553,13 @@ } #endif - switch (iph->protocol) { - case IPPROTO_ICMP: + if (iph->protocol == IPPROTO_ICMP) { #ifdef CONFIG_IP_MASQUERADE_VS if (!maddr && (ip_masq_select_addr(skb,&maddr) < 0)) { return -1; } #endif return(ip_fw_masq_icmp(skb_p, maddr)); - case IPPROTO_UDP: - if (h.uh->check == 0) - /* No UDP checksum */ - break; - case IPPROTO_TCP: - /* Make sure packet is in the masq range */ - IP_MASQ_DEBUG(3, "O-pkt: %s size=%d\n", - masq_proto_name(iph->protocol), - size); - -#ifdef CONFIG_IP_MASQ_DEBUG - if (ip_masq_get_debug_level() > 3) { - skb->ip_summed = CHECKSUM_NONE; - } -#endif - /* Check that the checksum is OK */ - switch (skb->ip_summed) - { - case CHECKSUM_NONE: - { - csum = csum_partial(h.raw + doff, size - doff, 0); - IP_MASQ_DEBUG(3, "O-pkt: %s I-datacsum=%d\n", - masq_proto_name(iph->protocol), - csum); - - skb->csum = csum_partial(h.raw , doff, csum); - } - case CHECKSUM_HW: - if (csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, skb->csum)) - { - IP_MASQ_DEBUG(0, "Outgoing failed %s checksum from %d.%d.%d.%d (size=%d)!\n", - masq_proto_name(iph->protocol), - NIPQUAD(iph->saddr), - size); - return -1; - } - default: - /* CHECKSUM_UNNECESSARY */ - } - break; - default: - return -1; } /* * Now hunt the list to see if we have an old entry @@ -1532,6 +1586,11 @@ } #endif + if (ms->app && ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } + /* * If sysctl !=0 and no pkt has been received yet * in this tunnel and routing iface address has changed... @@ -1539,6 +1598,10 @@ */ if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) { + if (!ms->app && ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } if (sysctl_ip_dynaddr > 1) { IP_MASQ_INFO( "ip_fw_masquerade(): change masq.addr from %d.%d.%d.%d to %d.%d.%d.%d\n", NIPQUAD(ms->maddr),NIPQUAD(maddr)); @@ -1561,6 +1624,11 @@ if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) { + if (!ms->app && ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } + write_lock(&__ip_masq_lock); ip_masq_unhash(ms); @@ -1592,6 +1660,9 @@ iph->saddr, h.portp[0]); read_unlock(&__ip_vs_lock); if (dest) { + if (ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) + return -1; + /* * Notify the real server: there is * no existing entry if it is not RST packet @@ -1624,6 +1695,11 @@ return -1; if (!ms->app && skb->fwmark) ip_masq_bind_app_fwmark(ms, skb->fwmark); + + if (ms->app && ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } } /* @@ -1640,19 +1716,20 @@ size = skb->len - (h.raw - skb->nh.raw); + if (!ms->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { + /* Only port and addr are changed, make fast csum update */ + ip_masq_check_inc_update(&h, iph->saddr, ms->maddr, + h.portp[0], ms->mport, iph->protocol); + if (skb->ip_summed == CHECKSUM_HW) + skb->ip_summed = CHECKSUM_NONE; + } + /* * Set iph addr and port from ip_masq obj. */ iph->saddr = ms->maddr; h.portp[0] = ms->mport; - /* - * Invalidate csum saving if tunnel has masq helper - */ - - if (ms->app) - csum_ok = 0; - /* * Attempt ip_masq_app call. * will fix ip_masq and iph seq stuff @@ -1677,44 +1754,17 @@ * Transport's payload partial csum */ - if (!csum_ok) { + if (ms->app) { csum = csum_partial(h.raw + doff, size - doff, 0); - } - skb->csum = csum; - - IP_MASQ_DEBUG(3, "O-pkt: %s size=%d O-datacsum=%d\n", - masq_proto_name(iph->protocol), - size, - csum); - - /* - * Protocol csum - */ - switch (iph->protocol) { - case IPPROTO_TCP: - h.th->check = 0; - h.th->check=csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, - csum_partial(h.raw , doff, csum)); - IP_MASQ_DEBUG(3, "O-pkt: %s O-csum=%d (+%d)\n", - masq_proto_name(iph->protocol), - h.th->check, - (char*) & (h.th->check) - (char*) h.raw); + skb->csum = csum; - break; - case IPPROTO_UDP: - h.uh->check = 0; - h.uh->check=csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, - csum_partial(h.raw , doff, csum)); - if (h.uh->check == 0) - h.uh->check = 0xFFFF; - IP_MASQ_DEBUG(3, "O-pkt: %s O-csum=%d (+%d)\n", - masq_proto_name(iph->protocol), - h.uh->check, - (char*) &(h.uh->check)- (char*) h.raw); - break; + IP_MASQ_DEBUG(3, "O-pkt: %s size=%d O-datacsum=%d\n", + masq_proto_name(iph->protocol), + size, + csum); + ip_masq_check_full_update(iph, &h, size, doff, csum); } + ip_send_check(iph); IP_MASQ_DEBUG(2, "O-routed from %08X:%04X with masq.addr %08X\n", @@ -2462,7 +2512,6 @@ unsigned short size; int doff = 0; int csum = 0; - int csum_ok = 0; __u32 maddr; #ifdef CONFIG_IP_MASQUERADE_VS struct ip_vs_service *svc = NULL; @@ -2528,37 +2577,6 @@ #endif && atomic_read(&mport_count) == 0 ) return 0; - - /* Check that the checksum is OK */ - if ((iph->protocol == IPPROTO_UDP) && (h.uh->check == 0)) - /* No UDP checksum */ - break; -#ifdef CONFIG_IP_MASQ_DEBUG - if (ip_masq_get_debug_level() > 3) { - skb->ip_summed = CHECKSUM_NONE; - } -#endif - - switch (skb->ip_summed) - { - case CHECKSUM_NONE: - csum = csum_partial(h.raw + doff, size - doff, 0); - csum_ok++; - skb->csum = csum_partial(h.raw , doff, csum); - - case CHECKSUM_HW: - if (csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, skb->csum)) - { - IP_MASQ_DEBUG(0, "Incoming failed %s checksum from %d.%d.%d.%d (size=%d)!\n", - masq_proto_name(iph->protocol), - NIPQUAD(iph->saddr), - size); - return -1; - } - default: - /* CHECKSUM_UNNECESSARY */ - } break; default: return 0; @@ -2624,8 +2642,11 @@ * masquerading entry. */ ms = ip_vs_schedule(svc, iph); - if (!ms) + if (!ms) { + if (ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) + return -1; return ip_vs_leave(svc, skb); + } ip_vs_conn_stats(ms, svc); } #endif /* CONFIG_IP_MASQUERADE_VS */ @@ -2633,6 +2654,12 @@ if (ms != NULL) { + if (ms->app && + ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } + /* * got reply, so clear flag */ @@ -2652,6 +2679,12 @@ } else { if ( ms->flags & IP_MASQ_F_NO_DPORT ) { /* && ms->protocol == IPPROTO_TCP ) { */ + if (!ms->app && + ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } + write_lock(&__ip_masq_lock); ip_masq_unhash(ms); @@ -2667,6 +2700,12 @@ } if (ms->flags & IP_MASQ_F_NO_DADDR ) { /* && ms->protocol == IPPROTO_TCP) { */ + if (!ms->app && + ip_masq_check_tcpudp(skb, iph, &h, size, doff) < 0) { + ip_masq_put(ms); + return -1; + } + write_lock(&__ip_masq_lock); ip_masq_unhash(ms); @@ -2715,16 +2754,17 @@ return -1; } + if (!ms->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { + /* Only port and addr are changed, make fast csum update */ + ip_masq_check_inc_update(&h, iph->daddr, ms->saddr, + h.portp[1], ms->sport, iph->protocol); + if (skb->ip_summed == CHECKSUM_HW) + skb->ip_summed = CHECKSUM_NONE; + } + iph->daddr = ms->saddr; h.portp[1] = ms->sport; - /* - * Invalidate csum saving if tunnel has masq helper - */ - - if (ms->app) - csum_ok = 0; - /* * Attempt ip_masq_app call. * will fix ip_masq and iph ack_seq stuff @@ -2742,37 +2782,10 @@ size = ntohs(iph->tot_len) - (iph->ihl * 4); } - /* - * Yug! adjust UDP/TCP checksums - */ - - /* - * Transport's payload partial csum - */ - - if (!csum_ok) { + if (ms->app) { csum = csum_partial(h.raw + doff, size - doff, 0); - } - skb->csum = csum; - - /* - * Protocol csum - */ - switch (iph->protocol) { - case IPPROTO_TCP: - h.th->check = 0; - h.th->check=csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, - csum_partial(h.raw , doff, csum)); - break; - case IPPROTO_UDP: - h.uh->check = 0; - h.uh->check=csum_tcpudp_magic(iph->saddr, iph->daddr, - size, iph->protocol, - csum_partial(h.raw , doff, csum)); - if (h.uh->check == 0) - h.uh->check = 0xFFFF; - break; + skb->csum = csum; + ip_masq_check_full_update(iph, &h, size, doff, csum); } ip_send_check(iph); @@ -2790,7 +2803,8 @@ * is found or created. Furthermore, send DEST_UNREACH icmp * packet to clients if it is not RST or it is not TCP. */ - if (!h.th->rst || iph->protocol != IPPROTO_TCP) { + if ((!h.th->rst || iph->protocol != IPPROTO_TCP) && + !ip_masq_check_tcpudp(skb, iph, &h, size, doff)) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); } return -1;