博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

多网卡ip

Posted on 2016-04-06 14:16  bw_0927  阅读(643)  评论(0)    收藏  举报

http://blog.chinaunix.net/uid-7377299-id-112976.html

http://blog.chinaunix.net/uid-7377299-id-112985.html

http://blog.csdn.net/coolingcoding/article/details/7392107

 

系统中存在两个网卡,如何根据socket号知道哪个连接(tcp)在哪个网卡上? 根据getpeername好像有点行不通。或者通过SIOCGARP?netlink?
   将问题进行分解,分为主动方式和被动方式两种。主动方式即是系统调用socket创建接口得到的socket号,被动方式即是通过类似accept得到的socket号。
   主动方式创建之后,可以利用setsockopt的SO_BINDTODEVICE。
 

在socket(7)中对该套接口选项的说明如下:

SO_BINDTODEVICE

      Bind this socket to a particular device like "eth0", as specified in the passed interface name. If the name is an empty string or the option length is zero, the socket  device  binding is  removed.  The passed option is a variable-length null terminated interface name string with the maximum size  of  IFNAMSIZ. If a socket is bound to an interface, only packets received from that particular interface are processed by the socket. Note that this only  works  for  some  socket types, particularly AF_INET sockets. It is not supported  for  packet  sockets  (use  normal bind(8) there).

 

 

strncpy(interface.ifr_ifrn.ifrn_name, "eth0", \
  strlen("eth0"));
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, \
  (char *)&interface, sizeof(interface)) 0) {
       perror("SO_BINDTODEVICE failed");
      /* Deal with error... */
}

 

 

==============================

 

这几天在看Linux下多网卡的资料,因为服务器要工作在多网卡模式下,会涉及到分流的策略。当然,我还没有到研究这个问题的时候,因为距离这个课题还比较远,先要扫清周围的障碍才行。
    多网卡环境下,首先涉及到服务器的监听,设置socket地址为INADDR_ANY可以将所有的网卡的具体端口纳入监听的范围之内,但是,accept到的socket是连接到哪个网卡的,需要在应用层加以区分,因为上层软件支持过代理之类的设置,查了一下尝试用ioctl获取,发现获取的时候需要指定网卡设备名(ifname),显然不可取,因为如果知道了连接哪个网卡,问题会相对简单一些,现在可以根据socket获取某个连接使用的哪个网卡设备。
   accept函数的参数addr可以获取对方的IP信息,但需要填充好sin_family类型。使用getsockname()和getpeername()可以得到本地连接和远程对方的IP,在多网卡环境下测试能够区分开不同的网卡上的连接。因此,由应用程序管理不同的网卡上的连接以及做相关的均衡, 是一种思路。
    对不同的网卡,采用不同的监听线程,即监听线程与网卡绑定,会绕过上面的问题,但那样会打破原先的程序架构,不到不得已,先不这样做。
    另一个问题是网关的设置,现在的问题是似乎只一个网卡的网关生效,系统默认按照default的路由,另一个网卡上的数据包也会走默认的网关,很多时候出不去。route设置可以针对不同的网卡设备添加相应的default网关,需要进一步实验,查资料。
    跟踪了一下netstat的代码,发现其统计连接的方式是通过读取/proc/net/下的tcp,udp等文件分析得来的,也就是内核最清楚具体连接的双方以及对应的socket。对tcp连接,内核中的实现是通过tcp4_seq_show(版本2.6.11)将相关的信息输出到proc中的。也就是说,如果将这个函数hack,netstat将得不到tcp连接的信息了。ifconfig得到网卡设备的信息是通过读取/proc/net/dev实现的。
    部分测试代码:
 

 

/**
  *This program is used for testing the network ip.
  */
#include <sys/types.h> /* See NOTES */
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <time.h>
#include <asm/ioctls.h>
#include <signal.h>
#include <linux/if.h>
#include <linux/sockios.h>

#define MAX_LISTENERS 30
#define DBG_SERVER
#ifdef DBG_SERVER
#define DBG(x) x 
#else 
#define DBG(x) 
#endif

int get_src_bindip(int _iSocket)
{
    struct ifreq if_req;
    int iIP = -1;
    int iRet = -1;
    struct sockaddr_in sin_a; 

    memset(&if_req, 0, sizeof(struct ifreq));
    strncpy(if_req.ifr_name, "eth0", 6);
    if_req.ifr_name[4] = 0;
    if(_iSocket > 0)
    {
       iRet = ioctl(_iSocket, SIOCGIFADDR, &if_req);
     if(iRet < 0)
     {
         printf("Error %d %s\n", errno, strerror(errno));
         return iRet;
     }
    }
    memcpy(&sin_a, &if_req.ifr_addr, sizeof(sin_a));
    printf("ip is %s \n", inet_ntoa(sin_a.sin_addr));
    return 0;
}

int get_dst_ip(int _iSocket)
{
    struct ifreq if_req;
    int iIP = -1;
    int iRet = -1;
    struct sockaddr_in sin_a; 
    
    memset(&if_req, 0, sizeof(struct ifreq));
    strncpy(if_req.ifr_name, "eth0", 6);
    if(_iSocket > 0)
    {
            iRet = ioctl(_iSocket, SIOCGIFDSTADDR, &if_req);
     if(iRet < 0)
     {
         printf("Error %d %s\n", errno, strerror(errno));
         return iRet;
     }
    }
    memcpy(&sin_a, &if_req.ifr_addr, sizeof(sin_a));
    printf("ip is %s \n", inet_ntoa(sin_a.sin_addr));
    
    return 0;
}

int main(int argc, char * argv[])
{
    
    struct sockaddr_in sin1;
    int size;
    struct timeval nTimeout; 
    int iServerFD = -1;
    int iConnectFD = -1;
    int iSrcAddr = -1;
    int iDstAddr = -1;
    char cAddr[16] = {0};
    char cMask[16] = {0};
        
    iServerFD = socket(AF_INET, SOCK_STREAM, 0);
    if(iServerFD < 0)
    {
        DBG(printf("Create socket error!\n"));
    return iServerFD;
    }
    
    
    sin1.sin_family = AF_INET;
    sin1.sin_port = htons(8000);
    sin1.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(iServerFD, (struct sockaddr *)&sin1, sizeof(struct sockaddr_in)) < 0)
    {
    DBG(printf("Bind error!\n"));
    return -1;
    }

    if( listen(iServerFD, MAX_LISTENERS) < 0)
    {
    DBG(printf("Listen failed!\n"));
    return -1;
    }
    
    printf("Start to listen....\n");

    if((iConnectFD = accept(iServerFD, (struct sockaddr *)&sin1, &size)) < 0)
    {
    DBG(printf("Accept failed!\n"));
    return -1;
    }
    DBG(printf("got the client connected %s!\n", inet_ntoa(sin1.sin_addr)));
    getpeername(iConnectFD, (struct sockaddr *)&sin1, &size);
    DBG(printf("got the client connected %s!\n", inet_ntoa(sin1.sin_addr)));
    getsockname(iConnectFD, (struct sockaddr *)&sin1, &size);
    DBG(printf("The IP %s!\n", inet_ntoa(sin1.sin_addr)));

    iSrcAddr = get_src_bindip(iConnectFD);
    iDstAddr = get_dst_ip(iConnectFD);

    printf("The src is 0x%x dst is 0x%x\n", iSrcAddr, iDstAddr);

    close(iServerFD);
    close(iConnectFD);
    
    return 0;
}