TPROXY: implemented IP_RECVORIGDSTADDR socket option
authorBalazs Scheidler <bazsi@balabit.hu>
Mon, 17 Nov 2008 03:32:39 +0000 (19:32 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Nov 2008 03:32:39 +0000 (19:32 -0800)
In case UDP traffic is redirected to a local UDP socket,
the originally addressed destination address/port
cannot be recovered with the in-kernel tproxy.

This patch adds an IP_RECVORIGDSTADDR sockopt that enables
a IP_ORIGDSTADDR ancillary message in recvmsg(). This
ancillary message contains the original destination address/port
of the packet being received.

Signed-off-by: Balazs Scheidler <bazsi@balabit.hu>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/in.h
net/ipv4/ip_sockglue.c

index db458beef19dabe156b52eecab7bd30e11a7e5f9..d60122a3a0886310e9221405ad1c022f9d3d2fa7 100644 (file)
@@ -80,6 +80,10 @@ struct in_addr {
 /* BSD compatibility */
 #define IP_RECVRETOPTS IP_RETOPTS
 
+/* TProxy original addresses */
+#define IP_ORIGDSTADDR       20
+#define IP_RECVORIGDSTADDR   IP_ORIGDSTADDR
+
 /* IP_MTU_DISCOVER values */
 #define IP_PMTUDISC_DONT               0       /* Never send DF frames */
 #define IP_PMTUDISC_WANT               1       /* Use per route hints  */
index e976efeb14563608ef7367638fc62969053ee76a..624b534201e2633a79519f8a8980a7130a356ac8 100644 (file)
@@ -48,6 +48,7 @@
 #define IP_CMSG_RECVOPTS       8
 #define IP_CMSG_RETOPTS                16
 #define IP_CMSG_PASSSEC                32
+#define IP_CMSG_ORIGDSTADDR     64
 
 /*
  *     SOL_IP control messages.
@@ -126,6 +127,27 @@ static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
        security_release_secctx(secdata, seclen);
 }
 
+void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
+{
+       struct sockaddr_in sin;
+       struct iphdr *iph = ip_hdr(skb);
+       u16 *ports = (u16 *) skb_transport_header(skb);
+
+       if (skb_transport_offset(skb) + 4 > skb->len)
+               return;
+
+       /* All current transport protocols have the port numbers in the
+        * first four bytes of the transport header and this function is
+        * written with this assumption in mind.
+        */
+
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = iph->daddr;
+       sin.sin_port = ports[1];
+       memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+       put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
+}
 
 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 {
@@ -160,6 +182,12 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 
        if (flags & 1)
                ip_cmsg_recv_security(msg, skb);
+
+       if ((flags>>=1) == 0)
+               return;
+       if (flags & 1)
+               ip_cmsg_recv_dstaddr(msg, skb);
+
 }
 
 int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
@@ -421,7 +449,8 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                             (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
                             (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT))) ||
            optname == IP_MULTICAST_TTL ||
-           optname == IP_MULTICAST_LOOP) {
+           optname == IP_MULTICAST_LOOP ||
+           optname == IP_RECVORIGDSTADDR) {
                if (optlen >= sizeof(int)) {
                        if (get_user(val, (int __user *) optval))
                                return -EFAULT;
@@ -509,6 +538,12 @@ static int do_ip_setsockopt(struct sock *sk, int level,
                else
                        inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
                break;
+       case IP_RECVORIGDSTADDR:
+               if (val)
+                       inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
+               else
+                       inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
+               break;
        case IP_TOS:    /* This sets both TOS and Precedence */
                if (sk->sk_type == SOCK_STREAM) {
                        val &= ~3;
@@ -1022,6 +1057,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_PASSSEC:
                val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
                break;
+       case IP_RECVORIGDSTADDR:
+               val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
+               break;
        case IP_TOS:
                val = inet->tos;
                break;