Initial patch proposed by Alexey Kuznetsov. Slightly adapted to support listen and automatic port selection. diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/include/linux/in.h linux-2.4.35-al20.2-geode-set6x86-transp/include/linux/in.h --- linux-2.4.35-al20.2-geode-set6x86/include/linux/in.h 2007-12-14 17:38:59 +0100 +++ linux-2.4.35-al20.2-geode-set6x86-transp/include/linux/in.h 2008-01-13 11:15:34 +0100 @@ -69,6 +69,7 @@ #define IP_RECVTOS 13 #define IP_MTU 14 #define IP_FREEBIND 15 +#define IP_TRANSPARENT 19 #define IP_RECVORIGADDRS 11273 #define IP_ORIGADDRS IP_RECVORIGADDRS diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/include/net/route.h linux-2.4.35-al20.2-geode-set6x86-transp/include/net/route.h --- linux-2.4.35-al20.2-geode-set6x86/include/net/route.h 2007-12-14 17:39:26 +0100 +++ linux-2.4.35-al20.2-geode-set6x86-transp/include/net/route.h 2008-01-13 11:16:02 +0100 @@ -38,12 +38,18 @@ #endif #define RTO_ONLINK 0x01 +#define RTO_TPROXY 0x8000 #define RTO_CONN 0 /* RTO_CONN is not used (being alias for 0), but preserved not to break * some modules referring to it. */ -#define RT_CONN_FLAGS(sk) (RT_TOS(sk->protinfo.af_inet.tos) | sk->localroute) +#ifdef CONFIG_IP_FOREIGN_BIND +#define RTO_SCONN(sk) ((sk)->reuse & 4 ? RTO_TPROXY : 0) +#else +#define RTO_SCONN(sk) 0 +#endif +#define RT_CONN_FLAGS(sk) (RT_TOS(sk->protinfo.af_inet.tos) | RTO_SCONN(sk) | sk->localroute) struct rt_key { @@ -56,7 +62,7 @@ #ifdef CONFIG_IP_ROUTE_FWMARK __u32 fwmark; #endif - __u8 tos; + __u16 tos; __u8 scope; }; diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/Config.in linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/Config.in --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/Config.in 2006-10-22 18:00:37 +0200 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/Config.in 2008-01-13 11:13:07 +0100 @@ -42,6 +42,7 @@ bool ' IP: TCP syncookie support (disabled per default)' CONFIG_SYN_COOKIES bool ' IP: Stealth Code (not enabled per default)' CONFIG_IP_STEALTH bool ' IP: TCP simultaneous connect support (disabled per default)' CONFIG_TCP_SIMULT_CONNECT +bool ' IP: allow binding to foreign addresses for connect and listen' CONFIG_IP_FOREIGN_BIND if [ "$CONFIG_NETFILTER" != "n" ]; then source net/ipv4/netfilter/Config.in fi diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/af_inet.c linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/af_inet.c --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/af_inet.c 2007-08-13 15:44:38 +0200 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/af_inet.c 2008-01-13 12:49:17 +0100 @@ -505,6 +505,21 @@ chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr); +#ifdef CONFIG_IP_FOREIGN_BIND + if (sk->reuse & 4) { + if (chk_addr_ret != RTN_UNICAST) { + /* Not a foreign address really, hence port is ours. */ + sk->reuse &= ~4; + } else { + sk->protinfo.af_inet.freebind = 1; + } + } else { + /* Not priviledged freebind option is not compatible with + * nonlocal connect. */ + sk->protinfo.af_inet.freebind = 0; + } +#endif + /* Not specified by any standard per-se, however it breaks too * many applications when removed. It is unfortunate since * allowing applications to make a non-local bind solves diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/ip_forward.c linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/ip_forward.c --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/ip_forward.c 2001-04-12 21:11:39 +0200 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/ip_forward.c 2008-01-13 10:38:50 +0100 @@ -84,6 +84,15 @@ if (skb->pkt_type != PACKET_HOST) goto drop; +#ifdef CONFIG_IP_FOREIGN_BIND + /* Could be done with a netfilter hook. Not clear how exactly. */ + if (skb->nh.iph->protocol == IPPROTO_TCP) { + if ((skb = tcp_v4_nonlocal_deliver(skb)) == NULL) + return NET_RX_SUCCESS; + opt = &(IPCB(skb)->opt); + } +#endif + skb->ip_summed = CHECKSUM_NONE; /* diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/ip_sockglue.c linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/ip_sockglue.c --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/ip_sockglue.c 2007-02-18 09:35:56 +0100 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/ip_sockglue.c 2008-01-13 10:42:48 +0100 @@ -413,7 +413,7 @@ (1<protinfo.af_inet.freebind = !!val; break; + case IP_TRANSPARENT: + if (optlen<1) + goto e_inval; + if (!capable(CAP_NET_ADMIN)) + goto e_inval; + if (val) + sk->reuse |= 4; + else + sk->reuse &= ~4; + break; default: #ifdef CONFIG_NETFILTER err = nf_setsockopt(sk, PF_INET, optname, optval, diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/route.c linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/route.c --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/route.c 2006-07-31 11:36:49 +0200 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/route.c 2008-01-13 10:47:18 +0100 @@ -1808,6 +1808,16 @@ /* * Major route resolver routine. + * + * NOTE: about CONFIG_IP_FOREIGN_BIND. Here it concides to + * CONFIG_IP_TRANSPARENT_PROXY used in linux-2.2. However, + * [ Also, I constructed new song: "Yesterday, all my troubles..." + * Whaat?! Well, words are the same, music may be the same too. Why not? + * But its sense is absolutely different! + * ] + * jokes apart, it is _not_ used to steal connections, only + * connections opened by us are nonlocal, hence we have + * no problems with TCP port shifting etc. */ int ip_route_output_slow(struct rtable **rp, const struct rt_key *oldkey) @@ -1848,8 +1858,19 @@ /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ dev_out = ip_dev_find(oldkey->src); +#ifdef CONFIG_IP_FOREIGN_BIND + /* If address is not local, test for nonlocal flag; + * if address is local --- clear the flag. + */ + if (dev_out == NULL) { + if (!(oldkey->tos & RTO_TPROXY) || inet_addr_type(oldkey->src) != RTN_UNICAST) + goto out; + flags |= RTCF_TPROXY; + } +#else if (dev_out == NULL) goto out; +#endif /* I removed check for oif == dev_out->oif here. It was wrong by three reasons: @@ -1860,6 +1881,9 @@ */ if (oldkey->oif == 0 +#ifdef CONFIG_IP_FOREIGN_BIND + && dev_out +#endif && (MULTICAST(oldkey->dst) || oldkey->dst == 0xFFFFFFFF)) { /* Special hack: user can direct multicasts and limited broadcast via necessary interface diff -urX dontdiff linux-2.4.35-al20.2-geode-set6x86/net/ipv4/tcp_ipv4.c linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/tcp_ipv4.c --- linux-2.4.35-al20.2-geode-set6x86/net/ipv4/tcp_ipv4.c 2007-08-13 15:44:38 +0200 +++ linux-2.4.35-al20.2-geode-set6x86-transp/net/ipv4/tcp_ipv4.c 2008-01-13 14:19:59 +0100 @@ -1852,6 +1852,43 @@ goto discard_it; } +#ifdef CONFIG_IP_FOREIGN_BIND +/* Could be done with netfilter hook. Not clear how to hook this in right place. */ + +struct sk_buff *tcp_v4_nonlocal_deliver(struct sk_buff *skb) +{ + struct sock *sk; + struct tcphdr *th; + int ihl; + + if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { + skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER); + if (!skb) + return NULL; + } + + ihl = skb->nh.iph->ihl*4; + + if (!pskb_may_pull(skb, ihl+8)) + goto out; + + th = (struct tcphdr*)(skb->nh.raw + ihl); + + sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source, + skb->nh.iph->daddr, ntohs(th->dest), + 0); + if (sk) { + sock_put(sk); + ip_local_deliver(skb); + return NULL; + } + +out: + return skb; +} +#endif + + /* With per-bucket locks this operation is not-atomic, so that * this version is not worse. */ @@ -1874,7 +1911,7 @@ /* Query new route. */ err = ip_route_connect(&rt, daddr, 0, - RT_TOS(sk->protinfo.af_inet.tos)|sk->localroute, + RT_CONN_FLAGS(sk), sk->bound_dev_if); if (err) return err;