📄 ip_masq.c
字号:
write_lock(&__ip_masq_lock);#ifdef CONFIG_IP_MASQ_NREUSE mst = __ip_masq_getbym(proto, maddr, mport);#else mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport);#endif if (mst == NULL) { if (atomic_read(free_ports_p) == 0) { if (mflags & IP_MASQ_F_USER) write_unlock_bh(&__ip_masq_lock); else write_unlock(&__ip_masq_lock); break; } atomic_dec(free_ports_p); ip_masq_hash(ms); if (mflags & IP_MASQ_F_USER) write_unlock_bh(&__ip_masq_lock); else write_unlock(&__ip_masq_lock); ip_masq_bind_app(ms); n_fails = 0; atomic_inc(&ms->refcnt); masq_set_state_timeout(ms, IP_MASQ_S_NONE); return ms; } if (mflags & IP_MASQ_F_USER) write_unlock_bh(&__ip_masq_lock); else write_unlock(&__ip_masq_lock); __ip_masq_put(mst); } if (++n_fails < 5) IP_MASQ_ERR( "ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n", masq_proto_name(ms->protocol), atomic_read(free_ports_p));mport_nono: kfree_s(ms, sizeof(*ms)); sysctl_ip_always_defrag--; MOD_DEC_USE_COUNT; return NULL;}/* * Get transport protocol data offset, check against size * return: * 0 if other IP proto * -1 if error */static __inline__ int proto_doff(unsigned proto, char *th, unsigned size){ int ret = -1; switch (proto) { case IPPROTO_ICMP: if (size >= sizeof(struct icmphdr)) ret = sizeof(struct icmphdr); break; case IPPROTO_UDP: if (size >= sizeof(struct udphdr)) ret = sizeof(struct udphdr); break; case IPPROTO_TCP: /* * Is this case, this check _also_ avoids * touching an invalid pointer if * size is invalid */ if (size >= sizeof(struct tcphdr)) { ret = ((struct tcphdr*)th)->doff << 2; if (ret > size) { ret = -1 ; } } break; default: /* Other proto: nothing to say, by now :) */ ret = 0; } if (ret < 0) IP_MASQ_DEBUG(0, "mess proto_doff for proto=%d, size =%d\n", proto, size); return ret;}int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr){ struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->nh.iph; union ip_masq_tphdr h; struct ip_masq *ms; int size; /* * 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 */ 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) { /* * Output path: do not pass other IP protos nor * invalid packets. */ return -1; } /* Lets determine our maddr now, shall we? */ if (maddr == 0) { struct rtable *rt; struct rtable *skb_rt = (struct rtable*)skb->dst; struct device *skb_dev = skb_rt->u.dst.dev; if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos)|RTO_CONN, skb_dev?skb_dev->ifindex:0)) { /* Fallback on old method */ /* This really shouldn't happen... */ maddr = inet_select_addr(skb_dev, skb_rt->rt_gateway, RT_SCOPE_UNIVERSE); } else { /* Route lookup succeeded */ maddr = rt->rt_src; ip_rt_put(rt); } } switch (iph->protocol) { case IPPROTO_ICMP: 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 */ /* h.raw = (char*) iph + iph->ihl * 4; */ IP_MASQ_DEBUG(2, "Outgoing %s %08lX:%04X -> %08lX:%04X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(h.portp[0]), ntohl(iph->daddr), ntohs(h.portp[1])); ms = ip_masq_out_get_iph(iph); if (ms!=NULL) { /* * If sysctl !=0 and no pkt has been received yet * in this tunnel and routing iface address has changed... * "You are welcome, diald". */ if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) { 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)); } write_lock(&__ip_masq_lock); ip_masq_unhash(ms); ms->maddr = maddr; ip_masq_hash(ms); write_unlock(&__ip_masq_lock); } /* * Set sport if not defined yet (e.g. ftp PASV). Because * masq entries are hashed on sport, unhash with old value * and hash with new. */ if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) { write_lock(&__ip_masq_lock); ip_masq_unhash(ms); ms->flags &= ~IP_MASQ_F_NO_SPORT; ms->sport = h.portp[0]; ip_masq_hash(ms); /* hash on new sport */ write_unlock(&__ip_masq_lock); IP_MASQ_DEBUG(1, "ip_fw_masquerade(): filled sport=%d\n", ntohs(ms->sport)); } if (ms->flags & IP_MASQ_F_DLOOSE) { /* * update dest loose values */ ms->dport = h.portp[1]; ms->daddr = iph->daddr; } } else { /* * Nope, not found, create a new entry for it */#ifdef CONFIG_IP_MASQUERADE_MOD if (!(ms = ip_masq_mod_out_create(skb, iph, maddr))) #endif ms = ip_masq_new(iph->protocol, maddr, 0, iph->saddr, h.portp[0], iph->daddr, h.portp[1], 0); if (ms == NULL) return -1; } /* * Call module's output update hook */#ifdef CONFIG_IP_MASQUERADE_MOD ip_masq_mod_out_update(skb, iph, ms);#endif /* * Change the fragments origin */ size = skb->len - (h.raw - skb->nh.raw); /* * 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 */ if (ip_masq_app_pkt_out(ms, skb_p, maddr) != 0) { /* * skb has possibly changed, update pointers. */ skb = *skb_p; iph = skb->nh.iph; h.raw = (char*) iph + iph->ihl *4; size = skb->len - (h.raw - skb->nh.raw); /* doff should have not changed */ } /* * Adjust packet accordingly to protocol */ /* * Transport's payload partial csum */ if (!csum_ok) { 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); 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_send_check(iph); IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n", ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr)); masq_set_state(ms, 1, iph, h.portp); ip_masq_put(ms); return 0; }/* * Restore original addresses and ports in the original IP * datagram if the failing packet has been [de]masqueraded. * This is ugly in the extreme. We no longer have the original * packet so we have to reconstruct it from the failing packet * plus data in the masq tables. The resulting "original data" * should be good enough to tell the sender which session to * throttle. Relies on far too much knowledge of masq internals, * there ought to be a better way - KAO 990303. * * Moved here from icmp.c - JJC. * Already known: type == ICMP_DEST_UNREACH, IPSKB_MASQUERADED * skb->nh.iph points to original header. * * Must try both OUT and IN tables; we could add a flag * ala IPSKB_MASQUERADED to avoid 2nd tables lookup, but this is VERY * unlike because routing makes mtu decision before reaching * ip_fw_masquerade(). * */int ip_fw_unmasq_icmp(struct sk_buff *skb) { struct ip_masq *ms; struct iphdr *iph = skb->nh.iph; __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]); /* * Always called from _bh context: use read_[un]lock() */ /* * Peek "out" table, this packet has bounced: * out->in(frag_needed!)->OUT[icmp] * * iph->daddr is IN host * iph->saddr is OUT host */ read_lock(&__ip_masq_lock); ms = __ip_masq_out_get(iph->protocol, iph->daddr, portp[1], iph->saddr, portp[0]); read_unlock(&__ip_masq_lock); if (ms) { IP_MASQ_DEBUG(1, "Incoming frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n", NIPQUAD(iph->daddr), NIPQUAD(ms->maddr)); iph->daddr = ms->maddr; portp[1] = ms->mport; __ip_masq_put(ms); return 1; } /* * Peek "in" table * in->out(frag_needed!)->IN[icmp] * * iph->daddr is OUT host * iph->saddr is MASQ host * */ read_lock(&__ip_masq_lock); ms = __ip_masq_in_get(iph->protocol, iph->daddr, portp[1], iph->saddr, portp[0]); read_unlock(&__ip_masq_lock); if (ms) { IP_MASQ_DEBUG(1, "Outgoing frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n", NIPQUAD(iph->saddr), NIPQUAD(ms->saddr)); iph->saddr = ms->saddr; portp[0] = ms->sport; __ip_masq_put(ms); return 1; } return 0;}/* * Handle ICMP messages in forward direction. * Find any that might be relevant, check against existing connections, * forward to masqueraded host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded */int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr){ struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->nh.iph; struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %lX -> %lX\n", icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr));#ifdef CONFIG_IP_MASQUERADE_ICMP if ((icmph->type == ICMP_ECHO ) || (icmph->type == ICMP_TIMESTAMP ) || (icmph->type == ICMP_INFO_REQUEST ) || (icmph->type == ICMP_ADDRESS )) { IP_MASQ_DEBUG(2, "icmp request rcv %lX->%lX id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), icmph->type); ms = ip_masq_out_get(iph->protocol, iph->saddr, icmp_id(icmph), iph->daddr, icmp_hv_req(icmph)); if (ms == NULL) { ms = ip_masq_new(iph->protocol, maddr, 0, iph->saddr, icmp_id(icmph), iph->daddr, icmp_hv_req(icmph), 0); if (ms == NULL) return (-1); IP_MASQ_DEBUG(1, "Created new icmp entry\n"); } /* Rewrite source address */ /* * If sysctl !=0 and no pkt has been received yet * in this tunnel and routing iface address has changed... * "You are welcome, diald". */ if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) { if (sysctl_ip_dynaddr > 1) { IP_MASQ_INFO( "ip_fw_masq_icmp(): change masq.addr %d.%d.%d.%d to %d.%d.%d.%d", NIPQUAD(ms->maddr), NIPQUAD(maddr)); } write_lock(&__ip_masq_lock); ip_masq_unhash(ms); ms->maddr = maddr; ip_masq_hash(ms);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -