Linux下模擬驗證NAT功能

Linux下模擬驗證NAT功能

NAT(網絡地址轉換)功能能夠方便內網與外網(通常是Internet)連接,既減少了有效IP的使用量又保護了內網主機的信息。

Linux下的NAT功能嵌入在Linux最新一代防火牆Netfilter中,並提供給用戶操作接口Iptables,相關知識可以查看網上資料。

最近我通過兩臺安裝了Linux的電腦搭建環境,驗證了NAT功能的正確性。(我也是有夠無聊的….)


環境搭建

這裏寫圖片描述

正常的NAT的流程如上圖所示,處於內網的主機PC2(10.0.0.2)要給外網的PC1發送數據,傳包的時候一開始源地址爲(10.0.0.2),經過NAT主機的時候服務器會把源地址改爲(172.16.93.129)這樣PC1收到數據的時候將按照目的地址爲(172.16.93.129)來發送包(目的IP地址爲局部內網的包是無法正確傳輸的)。

我現在稍微修改了上圖的結構,變爲(文章中IP爲虛構):

這裏寫圖片描述

因此相當於一臺電腦裏面包含了一個內部網址(相當於內部服務器),現在我需要通過外網主機(166.111.33.21)的訪問主機(166.111.33.25)的內網服務器(192.168.10.10),但是對於外網主機A來說只知道B的主機外網IP,想要訪問主機B的內網服務的話就是不行的,因此需要NAT的功能來進行地址轉換。

添加虛擬網卡

這裏順便提一下Linux中如何添加一塊虛擬網卡

  1. 首先修改網卡配置文件,Ubuntun下,網卡的配置文件是/etc/network/interfaces
    在這個文件中增加如下內容並保存:

    auto eth1
    iface eth1 inet static
    address 192.168.10.10
    netmask 255.255.255.0
  2. 重啓網卡(重新加載配置文件)纔會生效,使用如下命令重啓:

    sudo /etc/init.d/networking restart

配置NAT

搭建好環境之後需要就開始配置NAT功能,在主機B中需要用iptables來實現NAT。

爲了保證發送個主機B(166.111.33.25)的數據包能夠轉發給內網的服務器(192.168.10.10),我們首先配置如下命令:

DNAT:iptables -t nat -A PREROUTING -p tcp -j DNAT –to-destination 192.168.10.10

上述命令表示在發送到主機B的數據包若是屬於tcp連接的進入目的地址轉換,目的地址要變成192.168.10.10

同時我們還可以實現SNAT,即服務器發出數據包的時候將源地址變爲主機B的166.111.33.25,命令如下:

iptables -t nat -A POSTROUTING -s 192.168.10.10/255.255.255.0 -o eth0 -j MASQUERADE

-o eth0 表示數據轉發到eth0網卡也就是IP地址166.111.33.25對應的網卡。

NAT功能驗證

我們將利用Socket通信發送數據來驗證NAT功能。
首先在主機B中開啓服務端等待接受數據,代碼如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAX_LINE 100
void my_fun(char *p)
{
        if(p==NULL)
                return ;
        for(;*p!='\0';p++)
                if((*p)>='A' && (*p)<='Z')
                        *p=*p - 'A' +'a';
}
int main(void)
{
        struct sockaddr_in sin;
        struct sockaddr_in cin;
        int l_fd;
        int c_fd;
        socklen_t   len= sizeof ( struct sockaddr_in );
        char buf[MAX_LINE];
        char addr_p[INET_ADDRSTRLEN];
        int port=8000;
        int n;
        bzero(&sin,sizeof(sin));
        bzero(&cin,sizeof(cin));
        sin.sin_family=AF_INET;
        sin.sin_addr.s_addr=inet_addr("192.168.10.10");
        sin.sin_port=htons(port);
        l_fd=socket(AF_INET,SOCK_STREAM,0);
        bind(l_fd,(struct sockaddr *)&sin,sizeof(sin ));
        listen(l_fd,10);
        printf("waiting...\n");

        while(1)
        {
                c_fd=accept(l_fd,(struct sockaddr *)&cin,&len);
                n=read(c_fd,buf,MAX_LINE);
                inet_ntop(AF_INET,&cin.sin_addr,addr_p,sizeof(addr_p));
                printf("client IP is %s,port is %d\n",addr_p,ntohs(cin.sin_port)); 
                printf("content is %s\n",buf);
                my_fun(buf);
                write(c_fd,buf,n);
                close(c_fd);
        }
          if(colse(l_fd)==-1)
        {
                perror("fail to close");
                exit(1);
        }
        return 0;
}

主機A開啓客戶端來向主機B發送數據,代碼如下:

#include <stdio.h>/*客戶端,發送一串大寫字符,如CHINA*/
#include <sys/socket.h>
#include <arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include <net/if.h> 
#include<netinet/in.h>

#define MAX_LINE 100
int main(int argc,char *argv[])
{
        struct sockaddr_in sin;
        char buf[MAX_LINE];
        int sfd;
        int port=8000;
        char *str="test string";
        if(argc>1)
                str=argv[1];
        bzero(&sin,sizeof(sin));
        sin.sin_family=AF_INET;
        inet_pton(AF_INET,argv[1],&sin.sin_addr);
        sin.sin_port=htons(port);
        sfd=socket(AF_INET,SOCK_STREAM,0);

    //connect(sfd,(struct sockaddr *)&sin,sizeof(sin));
    if( connect(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        {
           printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
           exit(0);
        }


    char    recvline[4096], sendline[4096];
    printf("send msg to server: \n");
    fgets(sendline, 4096, stdin);
    if( send(sfd, sendline, strlen(sendline), 0) < 0)
    {
      printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
      exit(0);
    }

        //write(sfd,str,strlen(str)+1);

        read(sfd,buf,MAX_LINE);
        printf("recive from server:%s\n",buf);
        read(sfd,buf,MAX_LINE);
        printf("recive from server:%s\n",buf);
        close(sfd);
        return 0;
}

主機B開啓服務,並設定服務端IP爲192.168.10.10.

客戶端(若編譯好之後的文件爲client),在命令行中敲入:
./client 166.111.33.25
將會顯示結果。

實驗結果表明,開啓NAT功能,主機A可以發送數據到主機B;若不開啓NAT功能,主機A就無法發送數據到主機B,從而驗證了Linux下的NAT功能….

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章