设为首页 收藏本站
查看: 1863|回复: 0

[经验分享] 虚拟网卡TUN/TAP设备使用实例

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-11-24 07:07:54 | 显示全部楼层 |阅读模式
  文章出处:http://blog.csdn.net/solstice/article/details/6579232
  


  转载渊源:这篇文章源自陈硕老师的博客,原文讨论的主题是在绕开操作系统协议栈的情况下,对tcp并发连接数的支持情况;因为其中对TUN / TAP设备的使用非常典型,而且讲解清晰,所以特部分转载过来作为资料留存;
  


  IBM developerworks上有一篇文章对tun / tap设备进行了详细的介绍,也是一篇非常好的参考资料,另附连接如下:http://www.ibm.com/developerworks/cn/linux/l-tuntap/
  


  背景:在一台PC机上模拟TCP客户端程序发起连接请求,同时在该PC上创建虚拟网卡tun0,接收连接请求并送至faketcp应用程序,用于模拟TCP服务器端进行响应;
  


  拓扑结构如下:
DSC0000.jpg
  


  具体做法是:在atom上通过打开/dev/net/tun 设备来创建一个tun0虚拟网卡,然后把这个网卡的地址设为192.168.0.1/24,这样faketcp程序就扮演了192.168.0.0/24这个网段上的所有机器。atom发给192.168.0.2 ~ 192.168.0.254的IP packet都会发给faketcp程序,faketcp程序可以模拟其中任何一个IP给atom发IP packet;
  程序分成几步来实现。
  第一步:实现icmp echo协议,这样就能ping通faketcp了;
  icmpecho.cc#include "faketcp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <linux/if_ether.h>
int main()
{
char ifname[IFNAMSIZ] = &quot;tun%d&quot;;
int fd = tun_alloc(ifname);
if (fd < 0)
{
fprintf(stderr, &quot;tunnel interface allocation failed\n&quot;);
exit(1);
}
printf(&quot;allocted tunnel interface %s\n&quot;, ifname);
sleep(1);
for (;;)
{
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
const int iphdr_len = iphdr.ihl*4;
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr_len >= iphdr_size
&& iphdr_len <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr_len) == 0)
{
const void* payload = buf + iphdr_len;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
}
else
{
printf(&quot;bad packet\n&quot;);
for (int i = 0; i < nread; ++i)
{
if (i % 4 == 0) printf(&quot;\n&quot;);
printf(&quot;%02x &quot;, buf);
}
printf(&quot;\n&quot;);
}
}
return 0;
}




  faketcp.cc


  #include &quot;faketcp.h&quot;
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/if_tun.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <sys/ioctl.h>
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
if ((fd = open(&quot;/dev/net/tun&quot;, O_RDWR)) < 0)
{
perror(&quot;open&quot;);
return -1;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (*dev)
{
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0)
{
perror(&quot;ioctl&quot;);
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
uint16_t in_checksum(const void* buf, int len)
{
assert(len % 2 == 0);
const uint16_t* data = static_cast<const uint16_t*>(buf);
int sum = 0;
for (int i = 0; i < len; i+=2)
{
sum += *data++;
}
// while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
assert(sum <= 0xFFFF);
return ~sum;
}
void icmp_input(int fd, const void* input, const void* payload, int len)
{
const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
const struct icmphdr* icmphdr = static_cast<const struct icmphdr*>(payload);
// const int icmphdr_size = sizeof(*icmphdr);
const int iphdr_len = iphdr->ihl*4;
if (icmphdr->type == ICMP_ECHO)
{
char source[INET_ADDRSTRLEN];
char dest[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
printf(&quot;%s > %s: &quot;, source, dest);
printf(&quot;ICMP echo request, id %d, seq %d, length %d\n&quot;,
ntohs(icmphdr->un.echo.id),
ntohs(icmphdr->un.echo.sequence),
len - iphdr_len);
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct icmphdr icmphdr;
} out;
};
memcpy(output, input, len);
out.icmphdr.type = ICMP_ECHOREPLY;
out.icmphdr.checksum += ICMP_ECHO; // FIXME: not portable
std::swap(out.iphdr.saddr, out.iphdr.daddr);
write(fd, output, len);
}
}
  


  运行方法,打开3个命令行窗口:
  1. 在第1个窗口运行 sudo ./icmpecho,程序显示:
  allocted tunnel interface tun0
  


  2.  在第2个窗口运行:
  $ sudo ifconfig tun0 192.168.0.1/24
  $ sudo tcpdump -i tun0
  


  3. 在第3个窗口运行:
  $ ping 192.168.0.2
  $ ping 192.168.0.3
  $ ping 192.168.0.234
  发现每个192.168.0.X 的IP都能ping通;
  


  第二步:实现拒接TCP连接的功能,即在收到SYN TCP segment的时候发送RST segment。
  


  rejectall.cc


  #include &quot;faketcp.h&quot;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>
void tcp_input(int fd, const void* input, const void* payload, int tot_len)
{
const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);
const int iphdr_len = iphdr->ihl*4;
const int tcp_seg_len = tot_len - iphdr_len;
const int tcphdr_size = sizeof(*tcphdr);
if (tcp_seg_len >= tcphdr_size
&& tcp_seg_len >= tcphdr->doff*4)
{
const int tcphdr_len = tcphdr->doff*4;
if (tcphdr->syn)
{
char source[INET_ADDRSTRLEN];
char dest[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
printf(&quot;IP %s.%d > %s.%d: &quot;,
source, ntohs(tcphdr->source), dest, ntohs(tcphdr->dest));
printf(&quot;Flags [S], seq %u, win %d, length %d\n&quot;,
ntohl(tcphdr->seq),
ntohs(tcphdr->window),
tot_len - iphdr_len - tcphdr_len);
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct tcphdr tcphdr;
} out;
};
assert(sizeof(out) == sizeof(struct iphdr) + sizeof(struct tcphdr));
int output_len = sizeof(out);
bzero(&out, output_len + 4);
memcpy(output, input, sizeof(struct iphdr));
out.iphdr.tot_len = htons(output_len);
std::swap(out.iphdr.saddr, out.iphdr.daddr);
out.iphdr.check = 0;
out.iphdr.check = in_checksum(output, sizeof(struct iphdr));
out.tcphdr.source = tcphdr->dest;
out.tcphdr.dest = tcphdr->source;
out.tcphdr.seq = 0;
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
out.tcphdr.doff = sizeof(struct tcphdr) / 4;
out.tcphdr.ack = 1;
out.tcphdr.rst = 1;
out.tcphdr.window = 0;
unsigned char* pseudo = output + output_len;
pseudo[0] = 0;
pseudo[1] = IPPROTO_TCP;
pseudo[2] = 0;
pseudo[3] = sizeof(struct tcphdr);
out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
write(fd, output, output_len);
}
}
}
int main()
{
char ifname[IFNAMSIZ] = &quot;tun%d&quot;;
int fd = tun_alloc(ifname);
if (fd < 0)
{
fprintf(stderr, &quot;tunnel interface allocation failed\n&quot;);
exit(1);
}
printf(&quot;allocted tunnel interface %s\n&quot;, ifname);
sleep(1);
for (;;)
{
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
const int iphdr_len = iphdr.ihl*4;
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr_len >= iphdr_size
&& iphdr_len <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr_len) == 0)
{
const void* payload = buf + iphdr_len;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
else if (iphdr.protocol == IPPROTO_TCP)
{
tcp_input(fd, buf, payload, nread);
}
}
else
{
printf(&quot;bad packet\n&quot;);
for (int i = 0; i < nread; ++i)
{
if (i % 4 == 0) printf(&quot;\n&quot;);
printf(&quot;%02x &quot;, buf);
}
printf(&quot;\n&quot;);
}
}
return 0;
}

运行方法,打开3个命令行窗口,头两个窗口的操作与前面相同,运行的faketcp程序是 ./rejectall
  


  3. 在第3个窗口运行
  $ nc 192.168.0.2  2000
  $ nc 192.168.0.2  3333
  $ nc 192.168.0.7  5555
  发现向其中任意一个IP发起的TCP连接都被拒接了。
  


  第三步:实现接受TCP连接的功能,即在接收到SYN TCP segment的时候发回 SYN &#43; ACK。这个程序同时处理了连接断开的情况,即在收到FIN segment的时候发回 FIN &#43; ACK。
  acceptall.cc
  #include &quot;faketcp.h&quot;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>
void tcp_input(int fd, const void* input, const void* payload, int tot_len)
{
const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);
const int iphdr_len = iphdr->ihl*4;
const int tcp_seg_len = tot_len - iphdr_len;
const int tcphdr_size = sizeof(*tcphdr);
if (tcp_seg_len >= tcphdr_size
&& tcp_seg_len >= tcphdr->doff*4)
{
const int tcphdr_len = tcphdr->doff*4;
char source[INET_ADDRSTRLEN];
char dest[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
printf(&quot;IP %s.%d > %s.%d: &quot;,
source, ntohs(tcphdr->source), dest, ntohs(tcphdr->dest));
printf(&quot;Flags [%c], seq %u, win %d, length %d\n&quot;,
tcphdr->syn ? 'S' : (tcphdr->fin ? 'F' : '.'),
ntohl(tcphdr->seq),
ntohs(tcphdr->window),
tot_len - iphdr_len - tcphdr_len);
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct tcphdr tcphdr;
} out;
};
assert(sizeof(out) == sizeof(struct iphdr) + sizeof(struct tcphdr));
int output_len = sizeof(out);
bzero(&out, output_len + 4);
memcpy(output, input, sizeof(struct iphdr));
out.iphdr.tot_len = htons(output_len);
std::swap(out.iphdr.saddr, out.iphdr.daddr);
out.iphdr.check = 0;
out.iphdr.check = in_checksum(output, sizeof(struct iphdr));
out.tcphdr.source = tcphdr->dest;
out.tcphdr.dest = tcphdr->source;
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
out.tcphdr.doff = sizeof(struct tcphdr) / 4;
out.tcphdr.window = htons(5000);
bool response = false;
if (tcphdr->syn)
{
out.tcphdr.seq = htonl(123456);
out.tcphdr.syn = 1;
out.tcphdr.ack = 1;
response = true;
}
else if (tcphdr->fin)
{
out.tcphdr.seq = htonl(123457);
out.tcphdr.fin = 1;
out.tcphdr.ack = 1;
response = true;
}
unsigned char* pseudo = output + output_len;
pseudo[0] = 0;
pseudo[1] = IPPROTO_TCP;
pseudo[2] = 0;
pseudo[3] = sizeof(struct tcphdr);
out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
if (response)
{
write(fd, output, output_len);
}
}
}
int main()
{
char ifname[IFNAMSIZ] = &quot;tun%d&quot;;
int fd = tun_alloc(ifname);
if (fd < 0)
{
fprintf(stderr, &quot;tunnel interface allocation failed\n&quot;);
exit(1);
}
printf(&quot;allocted tunnel interface %s\n&quot;, ifname);
sleep(1);
for (;;)
{
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
const int iphdr_len = iphdr.ihl*4;
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr_len >= iphdr_size
&& iphdr_len <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr_len) == 0)
{
const void* payload = buf + iphdr_len;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
else if (iphdr.protocol == IPPROTO_TCP)
{
tcp_input(fd, buf, payload, nread);
}
}
else
{
printf(&quot;bad packet\n&quot;);
for (int i = 0; i < nread; ++i)
{
if (i % 4 == 0) printf(&quot;\n&quot;);
printf(&quot;%02x &quot;, buf);
}
printf(&quot;\n&quot;);
}
}
return 0;
}

运行方法,打开3个命令行窗口,步骤与前面相同,运行的faketcp程序是 ./acceptall。这次会发现 nc 能和192.168.0.X中的每一个IP 每一个PORT都能连通。还可以在第4个窗口中运行 netstat -tpn,以确认连接确实建立起来了。如果在nc中输入数据,数据会堆积在操作系统中,表现为netstat 显示的发送队列 (Send-Q)的长度增加
  


  第四步:在第三步接受TCP连接的基础上,实现接收数据,即在收到包含 payload 数据的 TCP segment时发回ACK
  discardall.cc
  #include &quot;faketcp.h&quot;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>
void tcp_input(int fd, const void* input, const void* payload, int tot_len)
{
const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);
const int iphdr_len = iphdr->ihl*4;
const int tcp_seg_len = tot_len - iphdr_len;
const int tcphdr_size = sizeof(*tcphdr);
if (tcp_seg_len >= tcphdr_size
&& tcp_seg_len >= tcphdr->doff*4)
{
const int tcphdr_len = tcphdr->doff*4;
const int payload_len = tot_len - iphdr_len - tcphdr_len;
char source[INET_ADDRSTRLEN];
char dest[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
printf(&quot;IP %s.%d > %s.%d: &quot;,
source, ntohs(tcphdr->source), dest, ntohs(tcphdr->dest));
printf(&quot;Flags [%c], seq %u, win %d, length %d\n&quot;,
tcphdr->syn ? 'S' : (tcphdr->fin ? 'F' : '.'),
ntohl(tcphdr->seq),
ntohs(tcphdr->window),
payload_len);
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct tcphdr tcphdr;
} out;
};
assert(sizeof(out) == sizeof(struct iphdr) + sizeof(struct tcphdr));
int output_len = sizeof(out);
bzero(&out, output_len + 4);
memcpy(output, input, sizeof(struct iphdr));
out.iphdr.tot_len = htons(output_len);
std::swap(out.iphdr.saddr, out.iphdr.daddr);
out.iphdr.check = 0;
out.iphdr.check = in_checksum(output, sizeof(struct iphdr));
out.tcphdr.source = tcphdr->dest;
out.tcphdr.dest = tcphdr->source;
out.tcphdr.doff = sizeof(struct tcphdr) / 4;
out.tcphdr.window = htons(5000);
bool response = false;
if (tcphdr->syn)
{
out.tcphdr.seq = htonl(123456);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
out.tcphdr.syn = 1;
out.tcphdr.ack = 1;
response = true;
}
else if (tcphdr->fin)
{
out.tcphdr.seq = htonl(123457);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
out.tcphdr.fin = 1;
out.tcphdr.ack = 1;
response = true;
}
else if (payload_len > 0)
{
out.tcphdr.seq = htonl(123457);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+payload_len);
out.tcphdr.ack = 1;
response = true;
}
unsigned char* pseudo = output + output_len;
pseudo[0] = 0;
pseudo[1] = IPPROTO_TCP;
pseudo[2] = 0;
pseudo[3] = sizeof(struct tcphdr);
out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
if (response)
{
write(fd, output, output_len);
}
}
}
int main()
{
char ifname[IFNAMSIZ] = &quot;tun%d&quot;;
int fd = tun_alloc(ifname);
if (fd < 0)
{
fprintf(stderr, &quot;tunnel interface allocation failed\n&quot;);
exit(1);
}
printf(&quot;allocted tunnel interface %s\n&quot;, ifname);
sleep(1);
for (;;)
{
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
const int iphdr_len = iphdr.ihl*4;
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr_len >= iphdr_size
&& iphdr_len <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr_len) == 0)
{
const void* payload = buf + iphdr_len;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
else if (iphdr.protocol == IPPROTO_TCP)
{
tcp_input(fd, buf, payload, nread);
}
}
else
{
printf(&quot;bad packet\n&quot;);
for (int i = 0; i < nread; ++i)
{
if (i % 4 == 0) printf(&quot;\n&quot;);
printf(&quot;%02x &quot;, buf);
}
printf(&quot;\n&quot;);
}
}
return 0;
}


  运行方法,打开3个命令行窗口,步骤与前面相同,运行的faketcp程序是./acceptall。这次会发现nc 能和192.168.0.X中的每一个IP 每一个PORT都能连通,数据也能发出去。还可以在第4个窗口中运行netstat -tpn,以确认连接确实建立起来了,并且发送队列的长度为0
  这一步已经解决了前面的问题2,扮演任意TCP服务端。
  


  第五步:解决前面的问题1,扮演客户端向atom发起任意多的连接。
  connectmany.cc
  #include &quot;faketcp.h&quot;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>
void tcp_input(int fd, const void* input, const void* payload, int tot_len, bool passive)
{
const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);
const int iphdr_len = iphdr->ihl*4;
const int tcp_seg_len = tot_len - iphdr_len;
const int tcphdr_size = sizeof(*tcphdr);
if (tcp_seg_len >= tcphdr_size
&& tcp_seg_len >= tcphdr->doff*4)
{
const int tcphdr_len = tcphdr->doff*4;
const int payload_len = tot_len - iphdr_len - tcphdr_len;
char source[INET_ADDRSTRLEN];
char dest[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
printf(&quot;IP %s.%d > %s.%d: &quot;,
source, ntohs(tcphdr->source), dest, ntohs(tcphdr->dest));
printf(&quot;Flags [%c], seq %u, win %d, length %d\n&quot;,
tcphdr->syn ? 'S' : (tcphdr->fin ? 'F' : '.'),
ntohl(tcphdr->seq),
ntohs(tcphdr->window),
payload_len);
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct tcphdr tcphdr;
} out;
};
assert(sizeof(out) == sizeof(struct iphdr) + sizeof(struct tcphdr));
int output_len = sizeof(out);
bzero(&out, output_len + 4);
memcpy(output, input, sizeof(struct iphdr));
out.iphdr.tot_len = htons(output_len);
std::swap(out.iphdr.saddr, out.iphdr.daddr);
out.iphdr.check = 0;
out.iphdr.check = in_checksum(output, sizeof(struct iphdr));
out.tcphdr.source = tcphdr->dest;
out.tcphdr.dest = tcphdr->source;
out.tcphdr.doff = sizeof(struct tcphdr) / 4;
out.tcphdr.window = htons(5000);
bool response = false;
if (tcphdr->syn)
{
out.tcphdr.seq = htonl(passive ? 123456 : 123457);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
if (passive)
{
out.tcphdr.syn = 1;
}
out.tcphdr.ack = 1;
response = true;
}
else if (tcphdr->fin)
{
out.tcphdr.seq = htonl(123457);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
out.tcphdr.fin = 1;
out.tcphdr.ack = 1;
response = true;
}
else if (payload_len > 0)
{
out.tcphdr.seq = htonl(123457);
out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+payload_len);
out.tcphdr.ack = 1;
response = true;
}
unsigned char* pseudo = output + output_len;
pseudo[0] = 0;
pseudo[1] = IPPROTO_TCP;
pseudo[2] = 0;
pseudo[3] = sizeof(struct tcphdr);
out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
if (response)
{
write(fd, output, output_len);
}
}
}

bool connect_one(int fd, uint32_t daddr, int dport, uint32_t saddr, int sport)
{
{
union
{
unsigned char output[ETH_FRAME_LEN];
struct
{
struct iphdr iphdr;
struct tcphdr tcphdr;
} out;
};
bzero(&out, (sizeof out)+4);
out.iphdr.version = IPVERSION;
out.iphdr.ihl = sizeof(out.iphdr)/4;
out.iphdr.tos = 0;
out.iphdr.tot_len = htons(sizeof(out));
out.iphdr.id = 55564;
out.iphdr.frag_off |= htons(IP_DF);
out.iphdr.ttl = IPDEFTTL;
out.iphdr.protocol = IPPROTO_TCP;
out.iphdr.saddr = saddr;
out.iphdr.daddr = daddr;
out.iphdr.check = in_checksum(output, sizeof(struct iphdr));
out.tcphdr.source = sport;
out.tcphdr.dest = dport;
out.tcphdr.seq = htonl(123456);
out.tcphdr.ack_seq = 0;
out.tcphdr.doff = sizeof(out.tcphdr)/4;
out.tcphdr.syn = 1;
out.tcphdr.window = htons(4096);
unsigned char* pseudo = output + sizeof out;
pseudo[0] = 0;
pseudo[1] = IPPROTO_TCP;
pseudo[2] = 0;
pseudo[3] = sizeof(struct tcphdr);
out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
write(fd, output, sizeof out);
}
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
// printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr.ihl*4 >= iphdr_size
&& iphdr.ihl*4 <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr.ihl*4) == 0)
{
const void* payload = buf + iphdr.ihl*4;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
else if (iphdr.protocol == IPPROTO_TCP)
{
tcp_input(fd, buf, payload, nread, false);
}
}
return true;
}
void connect_many(int fd, const char* ipstr, int port, int count)
{
uint32_t destip;
inet_pton(AF_INET, ipstr, &destip);
uint32_t srcip = ntohl(destip)+1;
int srcport = 1024;
for (int i = 0; i < count; ++i)
{
connect_one(fd, destip, htons(port), htonl(srcip), htons(srcport));
srcport++;
if (srcport > 0xFFFF)
{
srcport = 1024;
srcip++;
}
}
}
void usage()
{
}
int main(int argc, char* argv[])
{
if (argc < 4)
{
usage();
return 0;
}
char ifname[IFNAMSIZ] = &quot;tun%d&quot;;
int fd = tun_alloc(ifname);
if (fd < 0)
{
fprintf(stderr, &quot;tunnel interface allocation failed\n&quot;);
exit(1);
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int count = atoi(argv[3]);
printf(&quot;allocted tunnel interface %s\n&quot;, ifname);
printf(&quot;press enter key to start connecting %s:%d\n&quot;, ip, port);
getchar();
connect_many(fd, ip, port, count);
for (;;)
{
union
{
unsigned char buf[ETH_FRAME_LEN];
struct iphdr iphdr;
};
const int iphdr_size = sizeof iphdr;
int nread = read(fd, buf, sizeof(buf));
if (nread < 0)
{
perror(&quot;read&quot;);
close(fd);
exit(1);
}
printf(&quot;read %d bytes from tunnel interface %s.\n&quot;, nread, ifname);
const int iphdr_len = iphdr.ihl*4;
if (nread >= iphdr_size
&& iphdr.version == 4
&& iphdr_len >= iphdr_size
&& iphdr_len <= nread
&& iphdr.tot_len == htons(nread)
&& in_checksum(buf, iphdr_len) == 0)
{
const void* payload = buf + iphdr_len;
if (iphdr.protocol == IPPROTO_ICMP)
{
icmp_input(fd, buf, payload, nread);
}
else if (iphdr.protocol == IPPROTO_TCP)
{
tcp_input(fd, buf, payload, nread, true);
}
}
else
{
printf(&quot;bad packet\n&quot;);
for (int i = 0; i < nread; ++i)
{
if (i % 4 == 0) printf(&quot;\n&quot;);
printf(&quot;%02x &quot;, buf);
}
printf(&quot;\n&quot;);
}
}
return 0;
}


  这一步的运行方法与前面不同,打开4个命令行窗口。
  1.  在第1个窗口运行sudo ./connectmany 192.168.0.1  2007  1000,表示将向192.168.0.1:2007 发起1000个并发连接。
  程序显示
  allocated tunnel interface tun0
  press enter key to start connecting 192.168.0.1  2007
  


  2. 在第二个窗口运行
  
  $ sudo ifconfig tun0 192.168.0.1/24
  $ sudo tcpdump -i tun0
  


  3. 在第3个窗口运行一个能接收并发TCP连接的服务程序,可以是httpd, 也可以是muduo的echo 或discard示例,程序应listen 2007端口。
  


  4. 回到第1个窗口敲回车,然后在第4个窗口中用netstat -tpn来观察并发连接。
  


  


  


  文中代码目录连接:https://github.com/chenshuo/recipes/tree/master/faketcp


  


  


  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-142795-1-1.html 上篇帖子: Unix网络编程卷一: 网络地址转换函数 (inet_pton和inet_ntop) 下篇帖子: 套接字API(九)inet_pton函数和inet_ntop函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表