使用辅助IP地址连接套接字

6
不久之前,我能够通过以下步骤从给定接口的主IP地址或次IP地址连接到指定目标地址:
  1. 使用 "ip addr add ..." 命令将次要IP地址添加到给定接口,
  2. 根据需要将套接字绑定到主IP地址或次IP地址,
  3. 使用在步骤#2中绑定的主IP地址或次IP地址连接到目标IP地址。
我最近将一台机器升级到Linux 3.3.6,这种方法不再起作用,但我不记得我最后一次尝试它时实际上是哪个版本。有人知道如何在更新的内核中进行同样的操作吗?我刚刚在另一台机器上验证了相同的代码,它的内核是2.6.23。
引用: 更新2: 这似乎与NIC驱动程序有关。使用3.3.6和8139too,一切正常。使用不同的NIC和via-rhine,则会出现问题。
当我在3.3.6上运行代码(源代码在末尾)时,会发生以下情况(通过在后台运行的tshark捕获数据包输出):
$ ./bind_connect 10.0.1.124 10.0.1.120
Bound socket: 10.0.1.120 [10.0.1.120->10.0.1.124]

#           wrong source IP (should use 10.0.1.120 from bind)
#                      |
#                      v
$ 121.051052   10.0.1.220 -> 10.0.1.124   50418 80 TCP 74 50418 > 80 [SYN] Seq=3358582895 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=30589624 TSecr=0 WS=128
121.051428   10.0.1.124 -> 10.0.1.220   80 50418 TCP 74 80 > 50418 [SYN, ACK] Seq=1815118993 Ack=3358582896 Win=5792 Len=0 MSS=1460 SACK_PERM=1 TSval=8157158 TSecr=30589624 WS=2
121.051475   10.0.1.220 -> 10.0.1.124   50418 80 TCP 66 50418 > 80 [ACK] Seq=3358582896 Ack=1815118994 Win=14720 Len=0 TSval=30589624 TSecr=8157158
121.051504   10.0.1.220 -> 10.0.1.124   50418 80 TCP 66 50418 > 80 [FIN, ACK] Seq=3358582896 Ack=1815118994 Win=14720 Len=0 TSval=30589624 TSecr=8157158
121.051768   10.0.1.124 -> 10.0.1.220   80 50418 TCP 66 80 > 50418 [ACK] Seq=1815118994 Ack=3358582897 Win=5792 Len=0 TSval=8157158 TSecr=30589624
121.051913   10.0.1.124 -> 10.0.1.220   80 50418 TCP 66 80 > 50418 [FIN, ACK] Seq=1815118994 Ack=3358582897 Win=5792 Len=0 TSval=8157158 TSecr=30589624
121.051941   10.0.1.220 -> 10.0.1.124   50418 80 TCP 66 50418 > 80 [ACK] Seq=3358582897 Ack=1815118995 Win=14720 Len=0 TSval=30589625 TSecr=8157158


$ ./bind_connect 10.0.1.124 10.0.1.220
Bound socket: 10.0.1.220 [10.0.1.220->10.0.1.124]
$ 124.139966   10.0.1.220 -> 10.0.1.124   41470 80 TCP 74 41470 > 80 [SYN] Seq=1404133303 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=30590551 TSecr=0 WS=128
124.140421   10.0.1.124 -> 10.0.1.220   80 41470 TCP 74 80 > 41470 [SYN, ACK] Seq=1918490481 Ack=1404133304 Win=5792 Len=0 MSS=1460 SACK_PERM=1 TSval=8157467 TSecr=30590551 WS=2
124.140478   10.0.1.220 -> 10.0.1.124   41470 80 TCP 66 41470 > 80 [ACK] Seq=1404133304 Ack=1918490482 Win=14720 Len=0 TSval=30590551 TSecr=8157467
124.140553   10.0.1.220 -> 10.0.1.124   41470 80 TCP 66 41470 > 80 [FIN, ACK] Seq=1404133304 Ack=1918490482 Win=14720 Len=0 TSval=30590551 TSecr=8157467
124.140934   10.0.1.124 -> 10.0.1.220   80 41470 TCP 66 80 > 41470 [ACK] Seq=1918490482 Ack=1404133305 Win=5792 Len=0 TSval=8157467 TSecr=30590551
124.140976   10.0.1.124 -> 10.0.1.220   80 41470 TCP 66 80 > 41470 [FIN, ACK] Seq=1918490482 Ack=1404133305 Win=5792 Len=0 TSval=8157467 TSecr=30590551
124.140995   10.0.1.220 -> 10.0.1.124   41470 80 TCP 66 41470 > 80 [ACK] Seq=1404133305 Ack=1918490483 Win=14720 Len=0 TSval=30590551 TSecr=8157467

$ uname -a
Linux erebus.mn.ca 3.3.6-1-ARCH #1 SMP PREEMPT Sun May 13 09:59:18 UTC 2012 i686 GNU/Linux
$ ip route get 10.0.1.124
10.0.1.124 dev eth0.894  src 10.0.1.220 
    cache  ipid 0x61ca rtt 3ms rttvar 3ms cwnd 10

然后在2.6.23上也是同样的情况:

$ ./bind_connect 10.0.0.123 10.0.0.226                                                                                                                                           >
Bound socket: 10.0.0.226 [10.0.0.226->10.0.0.123]
$ 231.566278   10.0.0.226 -> 10.0.0.123   TCP 54109 > http [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=99922052 TSER=0 WS=5
231.566448   10.0.0.123 -> 10.0.0.226   TCP http > 54109 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=7060755 TSER=99922052 WS=1
231.566463   10.0.0.226 -> 10.0.0.123   TCP 54109 > http [ACK] Seq=1 Ack=1 Win=5856 Len=0 TSV=99922052 TSER=7060755
231.566510   10.0.0.226 -> 10.0.0.123   TCP 54109 > http [FIN, ACK] Seq=1 Ack=1 Win=5856 Len=0 TSV=99922052 TSER=7060755
231.566593   10.0.0.123 -> 10.0.0.226   TCP http > 54109 [ACK] Seq=1 Ack=2 Win=5792 Len=0 TSV=7060755 TSER=99922052
231.566704   10.0.0.123 -> 10.0.0.226   TCP http > 54109 [FIN, ACK] Seq=1 Ack=2 Win=5792 Len=0 TSV=7060755 TSER=99922052
231.566737   10.0.0.226 -> 10.0.0.123   TCP 54109 > http [ACK] Seq=2 Ack=2 Win=5856 Len=0 TSV=99922052 TSER=7060755

$ ./bind_connect 10.0.0.123 10.0.0.126                                                                                                                                           >
Bound socket: 10.0.0.126 [10.0.0.126->10.0.0.123]
$ 235.824867   10.0.0.126 -> 10.0.0.123   TCP 34228 > http [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=99926310 TSER=0 WS=5
235.825185   10.0.0.123 -> 10.0.0.126   TCP http > 34228 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=7061180 TSER=99926310 WS=1
235.825236   10.0.0.126 -> 10.0.0.123   TCP 34228 > http [ACK] Seq=1 Ack=1 Win=5856 Len=0 TSV=99926311 TSER=7061180
235.825273   10.0.0.126 -> 10.0.0.123   TCP 34228 > http [FIN, ACK] Seq=1 Ack=1 Win=5856 Len=0 TSV=99926311 TSER=7061180
235.825721   10.0.0.123 -> 10.0.0.126   TCP http > 34228 [ACK] Seq=1 Ack=2 Win=5792 Len=0 TSV=7061180 TSER=99926311
235.825722   10.0.0.123 -> 10.0.0.126   TCP http > 34228 [FIN, ACK] Seq=1 Ack=2 Win=5792 Len=0 TSV=7061180 TSER=99926311
235.825756   10.0.0.126 -> 10.0.0.123   TCP 34228 > http [ACK] Seq=2 Ack=2 Win=5856 Len=0 TSV=99926311 TSER=7061180

$ uname -a
Linux gaia.mn.ca 2.6.23.17-88.fc7 #1 SMP Thu May 15 00:35:10 EDT 2008 i686 i686 i386 GNU/Linux
$ ip route get 10.0.0.123
10.0.0.123 dev eth0  src 10.0.0.126
    cache  mtu 1500 advmss 1460 hoplimit 64

bind_connect的代码:

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    struct sockaddr_in sn;
    socklen_t sn_len;
    struct sockaddr_in src;
    struct sockaddr_in dst;
    int sock;
    char b1[INET_ADDRSTRLEN];
    char b2[INET_ADDRSTRLEN];
    char b3[INET_ADDRSTRLEN];

    src.sin_family = dst.sin_family = AF_INET;
    src.sin_port = 0;
    dst.sin_port = htons(80);

    if (argc < 3) {
        printf("missing argument\n");
        return 1;
    }

    if (inet_pton(AF_INET, argv[1], &dst.sin_addr) != 1) {
        perror("pton");
        return -errno;
    }

    if (inet_pton(AF_INET, argv[2], &src.sin_addr) != 1) {
        perror("pton");
        return -errno;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket");
        return -errno;
    }

    if (bind(sock, (struct sockaddr*)&src, sizeof src) != 0) {
        perror("bind");
        return -errno;
    }

    sn_len = sizeof sn;
    if (getsockname(sock, (struct sockaddr*)&sn, &sn_len) != 0) {
        perror("getsockname");
        return -errno;
    }

    printf("Bound socket: %s [%s->%s]\n",
           inet_ntop(AF_INET, &sn.sin_addr, b1, sizeof b1),
           inet_ntop(AF_INET, &src.sin_addr, b2, sizeof b2),
           inet_ntop(AF_INET, &dst.sin_addr, b3, sizeof b3));

    if (connect(sock, (struct sockaddr*)&dst, sizeof dst) != 0) {
        perror("connect");
        return -errno;
    }

    close(sock);

    return 0;
}

2
你能解释一下“不再工作”的意思吗?在你上面的每个不同运行中,你的程序都没有打印任何错误——这意味着它实际上连接并关闭了。打印的“绑定套接字”行似乎与相应的命令行匹配。数据包跟踪似乎是一个三次握手后跟着正常的关闭序列。换句话说,我没有看到任何问题。但我可能会错过一些微妙的东西。 - selbie
抱歉,我应该使用一个与 .120 / .220 不同的源 IP。 "不工作" 的情况是从错误的源 IP 发送,例如绑定到 .120,但流量从 .220 发送。 - Wade
你是在虚拟机上运行吗?如果是,你的数据包跟踪是在虚拟机内部还是在主机上运行的? - selbie
@selbie - 感谢您的查看。结果发现我在使用iptables nat规则时做错了事情。 - Wade
1个回答

3
问题的原因是我设置了一个iptables的nat POSTROUTING规则,即:
Chain POSTROUTING (policy ACCEPT 11 packets, 637 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  any    eth0.894  anywhere             anywhere            

我删除了以下规则:

$ sudo iptables -t nat -F

问题得到了解决。之前我为了测试另一个问题,设置了nat规则,但后来忘记删除它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接