免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5362 | 回复: 10

[网络] linux下socket编程求助 [复制链接]

论坛徽章:
0
发表于 2012-11-15 17:33 |显示全部楼层
平台linux,网络IPV6。
在创建socket成功,在调用bind()函数的时候返回22,invalid argument。
google了半天,发现是sockaddr_in6中的sin6_scope_id没有设置导致,继续查,这个字段的含义好像是标示interface index,也就是使用的是ETH0,ETH1还是其他。
请教各位大侠,这个ifindex在C程序可以怎么取出来了?现在只是知道IP,devicename(eth0, eth1, etc)这些都不清楚。
或者有没有什么方法,可以不设置sin6_scope_id就能bind成功(我试了,把sin6_scope_id设置为相应的ifindex后bind能成功,但是不知道在C中怎么获取这个index)?
PS:使用system()系统调用,通过ip link |grep 这样的操作来获取的方法pass,因为很可能用户没有那么高的权限去使用这些shell命令,或者这些命令也可能被屏蔽掉

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
发表于 2012-11-15 20:10 |显示全部楼层
  1.    Address Format
  2.            struct sockaddr_in6 {
  3.                sa_family_t     sin6_family;   /* AF_INET6 */
  4.                in_port_t       sin6_port;     /* port number */
  5.                uint32_t        sin6_flowinfo; /* IPv6 flow information */
  6.                struct in6_addr sin6_addr;     /* IPv6 address */
  7.                uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
  8.            };

  9.            struct in6_addr {
  10.                unsigned char   s6_addr[16];   /* IPv6 address */
  11.            };

  12.        sin6_family is always set to AF_INET6; sin6_port is the protocol port (see sin_port in ip(7));  sin6_flowinfo  is  the  IPv6  flow  identifier;
  13.        sin6_addr is the 128-bit IPv6 address.  sin6_scope_id is an ID depending on the scope of the address.  It is new in Linux 2.4.  Linux only sup-
  14.        ports it for link-local addresses, in that case sin6_scope_id contains the interface index (see netdevice(7))
复制代码
我也不懂, 试着用getaddrinfo吧, 应该会帮你填好, 另外我觉得一般填0都没问题, 所以结构体先bzero一下.

论坛徽章:
0
发表于 2012-11-16 10:56 |显示全部楼层
linux_c_py_php 发表于 2012-11-15 20:10
我也不懂, 试着用getaddrinfo吧, 应该会帮你填好, 另外我觉得一般填0都没问题, 所以结构体先bzero一下.


现在使用的ipv6地址就是一个local link地址,FE08开头的,所以这个值应该是需要填写的吧,使用0的时候,bind就会失败。
关于getaddrinfo,这个函数是返回的一个列表,里面包含了多个地址相关信息,但是我这里需要指定是UDP,TCP或者SCTP,直接用这个函数的话,是不是还需要自己再做一次查找,找到list中的匹配项?

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
发表于 2012-11-16 11:38 |显示全部楼层
可以指定第三个参数hint进行过滤, 只返回希望得到的结果, 自己manpage一下.

如果是local link(应该是同一子网下的目标机吧?), inet6 addr: fe80::216:3eff:fe62:fc12/64 Scope:Link , 我ifconfig获得的这个地址是不是呢? getaddrinfo应该会填上吧, 除非你的网卡没有自动生成.

lwoods 发表于 2012-11-16 10:56
现在使用的ipv6地址就是一个local link地址,FE08开头的,所以这个值应该是需要填写的吧,使用0的时候, ...

论坛徽章:
0
发表于 2012-11-16 14:37 |显示全部楼层
linux_c_py_php 发表于 2012-11-16 11:38
可以指定第三个参数hint进行过滤, 只返回希望得到的结果, 自己manpage一下.

如果是local link(应该是同一 ...


hi
试了一下使用getaddrinfo这个函数,bind还是失败,解析了一下它的res出参,scope_id还是为0,如果是手动强制把这个值赋值,就没有问题:
写了个小的测试程序,代码如下:

#include <iostream>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <net/if.h>


using namespace std;

int main() {
        int res,sock,s,inInterfaces;
        struct sockaddr_in6 *ip6_addr;
        struct sockaddr *addr;
        struct in6_addr in6;
        char *addrStr = "fe80::20c:29ff:fe80:97d2";
        char *addrStr2 = "fe80::20c:29ff:fe80:97dc";
        int  port =5060;
        struct ifaddrs *ifaddr;
        int   ifindex = 0;

        sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
        if (sock < 0)
        {
                printf("creat sock err[%d]\r\n",errno);
        }

        ip6_addr = (sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
        memset(ip6_addr,0,sizeof(struct sockaddr_in6));

        inet_pton(AF_INET6, addrStr, &in6);
        ip6_addr->sin6_family = AF_INET6;
        addr = (sockaddr *)malloc(sizeof(struct sockaddr));
        memset(addr,0,sizeof(struct sockaddr));

        memcpy(&ip6_addr->sin6_addr,
                        &in6,sizeof(struct in6_addr));


  struct addrinfo hints, *result, *p;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET6;
        hints.ai_protocol = IPPROTO_UDP;
        hints.ai_flags    = AI_PASSIVE;

        res = getaddrinfo(addrStr, "5060", &hints,&result);
                if (res != 0)
                {
                        printf("getaddrinfo err[%d] res[%d]!!!\r\n",errno,res );
                }
                else
                {
                        printf("getaddrinfo ok!!!\r\n" );
                }
        p = result;
    while (p != NULL)
    {
            ifindex = ((sockaddr_in6 *)p->ai_addr)->sin6_scope_id;
            printf("ifindex[%d]\r\n",ifindex);            //这里输出始终为0            
                 printf("port[%d]\r\n",htons(((sockaddr_in6 *)p->ai_addr)->sin6_port));
            p = p->ai_next;
    }

        //ip6_addr->sin6_scope_id = 2;
        //ip6_addr->sin6_port     = htons(port);
        //addr =  (struct sockaddr *) ip6_addr;
        //res = bind(sock, addr, sizeof(struct sockaddr_in6));

        //((struct sockaddr_in6 *) result->ai_addr)->sin6_scope_id = 2;  //如果设置了这个,就能绑定成功       
        res = bind(sock, result->ai_addr, result->ai_addrlen);

        if (res != 0)
        {
                printf("bind socket err[%d] res[%d] ai_addrlen[%d]!!!\r\n",errno,res,result->ai_addrlen );
        }
        else
        {
                printf("bind socket ok!!!\r\n" );
        }

        return 0;
}

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
发表于 2012-11-16 15:02 |显示全部楼层
It is new in Linux 2.4.  Linux only sup-
       ports it for link-local addresses, in that case sin6_scope_id contains the interface index (see netdevice(7))

看了一下man 7 netdevice, 看样你需要用ioctl来获取ethx的int  ifr_ifindex并填到addr里了.
  1. SYNOPSIS
  2.        #include <sys/ioctl.h>
  3.        #include <net/if.h>

  4. DESCRIPTION
  5.        This man page describes the sockets interface which is used to configure network devices.

  6.        Linux supports some standard ioctls to configure network devices.  They can be used on any socket’s file descriptor regardless of the family or
  7.        type.  They pass an ifreq structure:

  8.            struct ifreq {
  9.                char ifr_name[IFNAMSIZ]; /* Interface name */
  10.                union {
  11.                    struct sockaddr ifr_addr;
  12.                    struct sockaddr ifr_dstaddr;
  13.                    struct sockaddr ifr_broadaddr;
  14.                    struct sockaddr ifr_netmask;
  15.                    struct sockaddr ifr_hwaddr;
  16.                    short           ifr_flags;
  17.                    int             ifr_ifindex;
  18.                    int             ifr_metric;
  19.                    int             ifr_mtu;
  20.                    struct ifmap    ifr_map;
  21.                    char            ifr_slave[IFNAMSIZ];
  22.                    char            ifr_newname[IFNAMSIZ];
  23.                    char           *ifr_data;
  24.                };
  25.            };

  26.            struct ifconf {
  27.                int                 ifc_len; /* size of buffer */
  28.                union {
  29.                    char           *ifc_buf; /* buffer address */
  30.                    struct ifreq   *ifc_req; /* array of structures */
  31.                };
  32.            };
复制代码
SIOCGIFINDEX
              Retrieve the interface index of the interface into ifr_ifindex.

用这个选项, 填写好网卡名称echx这样, 调用ioctl拿一下ifindex把。

论坛徽章:
0
发表于 2012-11-16 16:07 |显示全部楼层
linux_c_py_php 发表于 2012-11-16 15:02
It is new in Linux 2.4.  Linux only sup-
       ports it for link-local addresses, in that case si ...


是的,我现在就是在用ioctl获取ifindex,但是现在有个问题,ifq返回的数据中,ifindex,device name都是正确的,但是IP地址貌似不对啊,返回的IPV4的地址和网卡配置的相同,但是IPV6的地址不正确。
现在需要的是能通过IPV6地址,知道ifindex,然后填写scope_id;
部分代码如下:

        s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
        if(s < 0)
    {
        perror("socket";
        return 1;
    }
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = buf;
        if(ioctl(s, SIOCGIFCONF, &ifc) < 0)
        {
            perror("ioctl(SIOCGIFCONF)";
            return 1;
        }

        ifr          = ifc.ifc_req;
        inInterfaces = ifc.ifc_len / sizeof(struct ifreq);
        //res=ioctl(s,SIOCGIFNAME,&ifr[1]);
        for(int i=0; i<inInterfaces; i++)
        {
                res=ioctl(s,SIOCGIFINDEX,&ifr);
                if(res)
                {
                   perror("index error";
                }
                else
                {
                        inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)&(ifr.ifr_ifru.ifru_addr))->sin6_addr), dststr, 2;
                        printf("get ipv6 addr[%s]\r\n",dststr);
                        inet_ntop(AF_INET, &(((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr), dststr, 2;
                        //inet_ntop(AF_INET, &(((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr), dststr, 2;
                        printf(" ifindex[%d] interface[%s],addr[%s] \n",
                                        ifr.ifr_ifindex,
                                        ifr.ifr_name,
                                        dststr);
                }
        }

论坛徽章:
0
发表于 2012-11-16 17:32 |显示全部楼层
刚才查了一下,iotcl好像是取不到IPV6的地址,换了个函数getifaddr,能够取出保存IPV6在内的所以地址信息,貌似能够解决我现在的问题。写了一小段测试代码,能够符合我的需求,呵呵
int main() {
        int res,sock, ifindex=0;
        struct sockaddr_in6 *ip6_addr, *addr_v6;
        struct sockaddr *addr;
        struct in6_addr in6;
        char *addrStr = "fe80::20c:29ff:fe80:97d2";
        char *addrStr2 = "fe80::20c:29ff:fe80:97dc";
        int  port =5060;
        struct ifaddrs *ifaddr, *ifa;
        char *dststr;

        sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
        if (sock < 0)
        {
                cout << "create socket err!!!" << endl;
        }

        ip6_addr = (sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
        memset(ip6_addr,0,sizeof(struct sockaddr_in6));

        inet_pton(AF_INET6, addrStr2, &in6);
        ip6_addr->sin6_family = AF_INET6;
        addr = (sockaddr *)malloc(sizeof(struct sockaddr));
        memset(addr,0,sizeof(struct sockaddr));

        memcpy(&ip6_addr->sin6_addr,
                        &in6,sizeof(struct in6_addr));

        ip6_addr->sin6_port     = htons(port);

        dststr = (char *)malloc(2;

        if (getifaddrs(&ifaddr) == -1) {
                       perror("getifaddrs";
                       exit(EXIT_FAILURE);
        }
        for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
            if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6)
                continue;

            addr_v6=(struct sockaddr_in6*)(ifa->ifa_addr);
            inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr),dststr,2;

            for (int j=0;j < 4; j++)
            {
                if(addr_v6->sin6_addr.__in6_u.__u6_addr32[j] !=
                                ip6_addr->sin6_addr.__in6_u.__u6_addr32[j])
                {
                        break;
                }

                    if(j == 3)
                    {
                            ifindex = addr_v6->sin6_scope_id;
                    printf("ok ipv6 addr[%s] devicename[%s] ifindex[%d]\r\n",dststr, ifa->ifa_name,addr_v6->sin6_scope_id);
                    }
            }
            if (ifindex != 0)
            {
                    break;
            }
        }

        ip6_addr->sin6_scope_id = ifindex;
        addr =  (struct sockaddr *) ip6_addr;

        res = bind(sock, addr, sizeof(struct sockaddr_in6));

        if (res != 0)
        {
                printf("bind socket err[%d] res[%d]!!!\r\n",errno,res );
        }
        else
        {
                printf("bind socket ok!!!\r\n" );
        }

        return 0;
}

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
发表于 2012-11-16 18:05 |显示全部楼层
本帖最后由 linux_c_py_php 于 2012-11-16 18:06 编辑

  1.        SIOCGIFCONF
  2.               Return  a  list of interface (transport layer) addresses.  This currently means only addresses of the AF_INET (IPv4) family for compati-
  3.               bility.  The user passes a ifconf structure as argument to the ioctl.  It contains a pointer to an array of ifreq structures in  ifc_req
  4.               and its length in bytes in ifc_len.  The kernel fills the ifreqs with all current L3 interface addresses that are running: ifr_name con-
  5.               tains the interface name (eth0:1 etc.), ifr_addr the address.  The kernel returns with the actual length  in  ifc_len.   If  ifc_len  is
  6.               equal to the original length the buffer probably has overflowed and you should retry with a bigger buffer to get all addresses.  When no
  7.               error occurs the ioctl returns 0; otherwise -1.  Overflow is not an error.  
复制代码
  1.      Strictly speaking, SIOCGIFCONF and the other ioctls that only accept or return AF_INET socket addresses, are IP specific and belong in ip(7).

  2.        The names of interfaces with no addresses or that don’t have the IFF_RUNNING flag set can be found via /proc/net/dev.

  3.        Local IPv6 IP addresses can be found via /proc/net or via rtnetlink(7).
复制代码
的确获取不到IPV6的信息,

[root@vps616 lua-5.2.1]# cat  /proc/net/if_inet6
00000000000000000000000000000001 01 80 10 80       lo
fe8000000000000002163efffe62fc12 02 40 20 80     eth0

不知道后边几个字段的含义, 你这个接口应该是一个封装, 估计它多考虑了ipv6吧

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
发表于 2012-11-16 18:09 |显示全部楼层
  1. VERSIONS
  2.        The  getifaddrs()  function first appeared in glibc 2.3, but before glibc 2.3.3, the implementation only supported IPv4 addresses; IPv6 support
  3.        was added in glibc 2.3.3.  Support of address families other than IPv4 is only available on kernels that support netlink.
  4.        #include <arpa/inet.h>
  5.        #include <sys/socket.h>
  6.        #include <netdb.h>
  7.        #include <ifaddrs.h>
  8.        #include <stdio.h>
  9.        #include <stdlib.h>
  10.        #include <unistd.h>

  11.        int
  12.        main(int argc, char *argv[])
  13.        {
  14.            struct ifaddrs *ifaddr, *ifa;
  15.            int family, s;
  16.            char host[NI_MAXHOST];

  17.            if (getifaddrs(&ifaddr) == -1) {
  18.                perror("getifaddrs");
  19.                exit(EXIT_FAILURE);
  20.            }


  21.        int
  22.        main(int argc, char *argv[])
  23.        {
  24.            struct ifaddrs *ifaddr, *ifa;
  25.            int family, s;
  26.            char host[NI_MAXHOST];

  27.            if (getifaddrs(&ifaddr) == -1) {
  28.                perror("getifaddrs");
  29.                exit(EXIT_FAILURE);
  30.            }

  31.            /* Walk through linked list, maintaining head pointer so we
  32.               can free list later */

  33.            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  34.                if (ifa->ifa_addr == NULL)
  35.                    continue;

  36.                family = ifa->ifa_addr->sa_family;

  37.                /* Display interface name and family (including symbolic
  38.                   form of the latter for the common families) */

  39.                printf("%s  address family: %d%s\n",
  40.                        ifa->ifa_name, family,
  41.                        (family == AF_PACKET) ? " (AF_PACKET)" :
  42.                        (family == AF_INET) ?   " (AF_INET)" :
  43.                        (family == AF_INET6) ?  " (AF_INET6)" : "");

  44.                /* For an AF_INET* interface address, display the address */

  45.                if (family == AF_INET || family == AF_INET6) {
  46.                    s = getnameinfo(ifa->ifa_addr,
  47.                            (family == AF_INET) ? sizeof(struct sockaddr_in) :
  48.                                                  sizeof(struct sockaddr_in6),
  49.                            host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  50.                    if (s != 0) {
  51.                        printf("getnameinfo() failed: %s\n", gai_strerror(s));
  52.                        exit(EXIT_FAILURE);
  53.                    }
  54.                    printf("\taddress: <%s>\n", host);
  55.                }
  56.            }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP