Linux网络编程- struct packet_mreq & setsockopt()

本文详细讲解了Linux中structpacket_mreq用于设置网络接口接收数据包类型的机制,以及如何使用setsockopt和getsockopt函数进行相关配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

struct packet_mreq

struct packet_mreq 是一个数据结构,用于 Linux 中的原始数据包套接字,当我们想改变套接字的行为以接收特定类型的数据包时,它与 setsockopt() 函数配合使用。

下面是 struct packet_mreq 的定义:

struct packet_mreq {
    int		mr_ifindex;   // Interface index of the network device
    unsigned short	mr_type;     // Type of membership (e.g., PACKET_MR_PROMISC, PACKET_MR_MULTICAST)
    unsigned short	mr_alen;     // Address length
    unsigned char	mr_address[8]; // Physical layer address
};

各字段的详细解释:

  1. mr_ifindex:

    • 这是网络接口的索引号。我们可以使用 if_nametoindex() 函数,将接口名称(如 “eth0”)转换为其索引。(注:if_nametoindex(iface_name) 函数用于将网络接口名称(如 “eth0”)转换为与该接口关联的接口索引。接口索引是内核用于唯一标识网络接口的整数。当 if_nametoindex 函数不能找到指定名称的网络接口时,它会返回0。因此,检查返回值是否为0可以告诉我们是否成功获取了接口索引。)
  2. mr_type:

    • 这是要修改的成员资格类型。例如:
      • PACKET_MR_PROMISC: 设置接口为混杂模式。
      • PACKET_MR_MULTICAST: 加入多播组。
      • PACKET_MR_UNICAST: 添加一个单播地址。
      • PACKET_MR_ALLMULTI: 接收所有多播数据包。
      • PACKET_MR_BROADCAST: 接收所有广播数据包。
  3. mr_alen:

    • 用于指定在 mr_address 字段中的物理地址长度。例如,对于以太网地址,这将是6。
  4. mr_address:

    • 物理层地址。这通常是以太网MAC地址,但长度取决于实际的物理层。例如,当 mr_typePACKET_MR_MULTICAST 时,我们将在此字段中指定要加入的多播地址。

例如,如果想将网络接口 “eth0” 设置为混杂模式,可以这样做:

struct packet_mreq mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = if_nametoindex("eth0");
mr.mr_type = PACKET_MR_PROMISC;
setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr));

而如果想加入一个特定的多播地址,则设置 mr_typePACKET_MR_MULTICAST,并提供相应的多播MAC地址。

示例

本例展示了如何为特定的网络接口(例如 “eth0”)设置混杂模式,并加入一个特定的多播地址。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <arpa/inet.h>

int main() {
    int sock;
    struct packet_mreq mr;
    const char* iface_name = "eth0";
    
    // 创建一个原始套接字
    sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock < 0) {
        perror("Error in socket creation");
        exit(EXIT_FAILURE);
    }

    // 设置网络接口为混杂模式
    memset(&mr, 0, sizeof(mr));
    mr.mr_ifindex = if_nametoindex(iface_name);
    if (mr.mr_ifindex == 0) {
        perror("Error getting interface index");
        exit(EXIT_FAILURE);
    }
    mr.mr_type = PACKET_MR_PROMISC;

    if (setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
        perror("Error setting socket to promiscuous mode");
        exit(EXIT_FAILURE);
    }

    // 假设我们要加入的多播MAC地址是 "01:00:5E:10:20:30"
    unsigned char multicast_address[6] = {0x01, 0x00, 0x5E, 0x10, 0x20, 0x30};
    mr.mr_type = PACKET_MR_MULTICAST;
    mr.mr_alen = 6;
    memcpy(mr.mr_address, multicast_address, 6);
    
    if (setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
        perror("Error adding multicast membership");
        exit(EXIT_FAILURE);
    }

    printf("Interface %s set to promiscuous mode and joined multicast address 01:00:5E:10:20:30.\n", iface_name);

    // ... 这里可以添加其他代码,例如数据包捕获和处理

    close(sock);
    return 0;
}

上述代码首先为 “eth0” 网络接口设置混杂模式,然后加入了特定的多播MAC地址 “01:00:5E:10:20:30”。需要有适当的权限来执行这个代码(通常需要root权限)。


加入特定的多播MAC地址允许网络接口接收发送到该特定多播地址的数据包。这与多播的基本工作方式有关。这里是一个简要的概述:

  1. 多播:多播是在IP网络上向多个接收者发送信息的方法,但不是向所有接收者发送信息(这称为广播)。多播发送的数据包被发送到一个特定的多播IP地址,并由加入该多播组的接收者接收。

  2. 多播MAC地址:由于数据链路层(例如以太网)并不了解IP地址,因此IP多播地址被映射到一个特定的MAC地址范围。这意味着,当一个多播数据包在以太网上发送时,它实际上是发送到一个特定的多播MAC地址。

  3. 加入多播组:如果应用程序对某个多播组感兴趣(即,它想要接收发送到该组的数据包),则需要告诉我们的网络接口加入该组。这样,当接口看到发送到相关多播MAC地址的数据包时,它就知道需要接收它们,而不是忽略它们。

  4. 用途

    • 多播视频和音频流:例如,一个服务器可以将实时视频流发送到多播地址,而所有希望查看该视频流的客户端只需加入相关的多播组即可。
    • 路由协议:一些路由协议使用多播来交换路由信息。
    • 服务发现:一些协议,如mDNS,使用多播来发现网络上的服务。
    • 其他:还有许多其他使用多播的场景,从股票交易到计算机游戏的实时多玩家数据。

总之,通过加入特定的多播MAC地址,我们的网络接口或应用程序可以选择性地接收发送到这些地址的数据包,这对于需要接收特定多播数据的应用程序来说是很有用的。

setsockopt()

setsockopt() 是一个系统调用,用于设置与某个套接字关联的选项。此函数允许应用程序在各种协议级别上设置或更改多种套接字行为。

以下是 setsockopt() 的基本形式:

int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);

参数解释:

  1. socket:它是要设置选项的套接字的文件描述符。
  2. level:这决定了哪个协议的选项应该被设置。例如,要设置TCP选项,可以使用 IPPROTO_TCP 作为级别。常见的级别包括 SOL_SOCKETIPPROTO_IPIPPROTO_TCP
  3. option_name:这是我们想要设置的具体选项的名称。例如,SO_REUSEADDRSO_KEEPALIVE 都是 SOL_SOCKET 级别的选项。
  4. option_value:这是一个指向要设置选项的新值的指针。
  5. option_len:这是 option_value 指向的数据的大小。

常见用途:

  • 端口重用:当套接字关闭后,端口可能会保持在 TIME_WAIT 状态一段时间。如果想立即重启服务器并重新绑定到相同的端口,可以使用 SO_REUSEADDR 选项。

    int optval = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    
  • 设置发送/接收缓冲区的大小:例如,我们可能希望增大套接字的发送或接收缓冲区。

    int bufsize = 1024 * 1024; // 1MB
    setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
    
  • TCP 选项:例如,TCP_NODELAY 选项可以用于禁用 Nagle’s 算法,使得小的数据包可以被更快地发送。

    int flag = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
    

返回值:

  • 成功时返回 0。
  • 失败时返回 -1 并设置 errno

错误:

  • EBADF:描述符不是一个有效的套接字。
  • ENOPROTOOPT:指定的协议级别不识别该选项。
  • EFAULToption_value 指向的内存不是一个有效的部分。

要获取套接字选项的当前值,可以使用 getsockopt() 函数。

注意:具体支持哪些选项和级别可能因操作系统而异,建议查阅特定操作系统的手册页或相关文档以获取详细和完整的信息。


setsockopt()leveloption_name 参数可以取多种值,具体取决于操作系统。以下是一些常见的选项及其解释:

1. level: SOL_SOCKET

这是通用的套接字选项级别。

  • SO_REUSEADDR

    • 说明:允许套接字和其他套接字绑定到相同的地址和端口。这对于在短时间内多次关闭、打开同一地址、端口的服务器应用程序很有用。
  • SO_KEEPALIVE

    • 说明:如果套接字没有接收到数据,在一段时间后将开始发送keepalive消息。这有助于确保连接仍然活动并确定远程主机是否还在线。
  • SO_RCVBUFSO_SNDBUF

    • 说明:设置或获取接收或发送缓冲区的大小。
  • SO_RCVTIMEOSO_SNDTIMEO

    • 说明:设置或获取接收或发送超时。
  • SO_ERROR

    • 说明:获取套接字上的待处理错误。
  • SO_TYPE

    • 说明:获取套接字的类型。

2. level: IPPROTO_IP

这是IP层的选项。

  • IP_TTL

    • 说明:设置或获取Time To Live字段的值。
  • IP_MULTICAST_IF

    • 说明:指定用于发送多播数据报的出口接口。
  • IP_ADD_MEMBERSHIPIP_DROP_MEMBERSHIP

    • 说明:加入或离开多播组。
  • IP_HDRINCL

    • 说明:指示应用程序将提供完整的IP头部。

3. level: IPPROTO_TCP

这是TCP层的选项。

  • TCP_NODELAY

    • 说明:控制Nagle算法是否用于套接字。这对于希望快速发送小的数据包的应用程序很有用。
  • TCP_MAXSEG

    • 说明:获取或设置TCP最大段大小。

这只是一个简要的概述,实际上有更多的选项和级别可供选择。这些选项的行为、可用性以及如何正确使用它们可能会因操作系统和其版本而异。因此,在使用特定的选项时,最好查阅所使用的操作系统的手册页或其他相关文档。

getsockopt()

getsockopt() 函数用于获取套接字选项。该函数允许应用程序查看或修改套接字的当前选项值。与setsockopt()类似,getsockopt()也可以在多种层次上获取选项,例如 SOL_SOCKET(通用套接字层)、IPPROTO_IP(IP层)和IPPROTO_TCP(TCP层)等。

函数原型

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

参数

  • sockfd:标识套接字的文件描述符。
  • level:选项定义的级别,例如:SOL_SOCKET、IPPROTO_IP。
  • optname:需要访问的选项名称,例如:SO_REUSEADDR、TCP_NODELAY。
  • optval:指向值的指针,该值将被获取。
  • optlen:作为输入时表示optval的最大长度,作为输出时表示optval的实际长度。

返回值
成功时返回0,失败时返回-1。

示例

以下是使用getsockopt()获取套接字发送缓冲区大小的示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>

int main(void) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    int sendbuff;
    socklen_t optlen = sizeof(sendbuff);
    
    // 获取SO_SNDBUF选项的值
    if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, &optlen) == -1) {
        perror("getsockopt");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Send buffer size = %d\n", sendbuff);
    close(sockfd);
    return 0;
}

setsockopt()相同,getsockopt()也有许多可用的leveloptname选项,并且它们的含义与setsockopt()函数中的相同。不同的操作系统和版本可能会支持不同的选项,所以建议查阅特定操作系统的手册页或其他文档来了解完整的详细信息。

/*** This file is part of avahi. avahi is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. avahi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with avahi; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include &lt;config.h&gt; #endif #include &lt;inttypes.h&gt; #include &lt;errno.h&gt; #include &lt;string.h&gt; #include &lt;stdio.h&gt; #include &lt;unistd.h&gt; #include &lt;fcntl.h&gt; #include &lt;sys/time.h&gt; #include &lt;sys/ioctl.h&gt; #ifdef HAVE_SYS_FILIO_H #include &lt;sys/filio.h&gt; #endif #include &lt;assert.h&gt; #include &lt;sys/types.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/in.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;net/if.h&gt; #include &lt;sys/uio.h&gt; #ifdef IP_RECVIF #include &lt;net/if_dl.h&gt; #endif #include &quot;dns.h&quot; #include &quot;fdutil.h&quot; #include &quot;socket.h&quot; #include &quot;log.h&quot; #include &quot;addr-util.h&quot; /* this is a portability hack */ #ifndef IPV6_ADD_MEMBERSHIP #ifdef IPV6_JOIN_GROUP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif #endif #ifndef IPV6_DROP_MEMBERSHIP #ifdef IPV6_LEAVE_GROUP #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif #endif static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { assert(ret_sa); memset(ret_sa, 0, sizeof(struct sockaddr_in)); ret_sa-&gt;sin_family = AF_INET; ret_sa-&gt;sin_port = htons(AVAHI_MDNS_PORT); inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &amp;ret_sa-&gt;sin_addr); } static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { assert(ret_sa); memset(ret_sa, 0, sizeof(struct sockaddr_in6)); ret_sa-&gt;sin6_family = AF_INET6; ret_sa-&gt;sin6_port = htons(AVAHI_MDNS_PORT); inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &amp;ret_sa-&gt;sin6_addr); } static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { assert(ret_sa); assert(a); assert(port &gt; 0); memset(ret_sa, 0, sizeof(struct sockaddr_in)); ret_sa-&gt;sin_family = AF_INET; ret_sa-&gt;sin_port = htons(port); memcpy(&amp;ret_sa-&gt;sin_addr, a, sizeof(AvahiIPv4Address)); } static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { assert(ret_sa); assert(a); assert(port &gt; 0); memset(ret_sa, 0, sizeof(struct sockaddr_in6)); ret_sa-&gt;sin6_family = AF_INET6; ret_sa-&gt;sin6_port = htons(port); memcpy(&amp;ret_sa-&gt;sin6_addr, a, sizeof(AvahiIPv6Address)); } int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { #ifdef HAVE_STRUCT_IP_MREQN struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif struct sockaddr_in sa; assert(fd &gt;= 0); assert(idx &gt;= 0); assert(a); memset(&amp;mreq, 0, sizeof(mreq)); #ifdef HAVE_STRUCT_IP_MREQN mreq.imr_ifindex = idx; mreq.imr_address.s_addr = a-&gt;address; #else mreq.imr_interface.s_addr = a-&gt;address; #endif mdns_mcast_group_ipv4(&amp;sa); mreq.imr_multiaddr = sa.sin_addr; /* Some network drivers have issues with dropping membership of * mcast groups when the iface is down, but don&#39;t allow rejoining * when it comes back up. This is an ugly workaround */ if (join) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &amp;mreq, sizeof(mreq)); if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &amp;mreq, sizeof(mreq)) &lt; 0) { avahi_log_warn(&quot;%s failed: %s&quot;, join ? &quot;IP_ADD_MEMBERSHIP&quot; : &quot;IP_DROP_MEMBERSHIP&quot;, strerror(errno)); return -1; } return 0; } int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { struct ipv6_mreq mreq6; struct sockaddr_in6 sa6; assert(fd &gt;= 0); assert(idx &gt;= 0); assert(a); memset(&amp;mreq6, 0, sizeof(mreq6)); mdns_mcast_group_ipv6 (&amp;sa6); mreq6.ipv6mr_multiaddr = sa6.sin6_addr; mreq6.ipv6mr_interface = idx; if (join) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &amp;mreq6, sizeof(mreq6)); if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &amp;mreq6, sizeof(mreq6)) &lt; 0) { avahi_log_warn(&quot;%s failed: %s&quot;, join ? &quot;IPV6_ADD_MEMBERSHIP&quot; : &quot;IPV6_DROP_MEMBERSHIP&quot;, strerror(errno)); return -1; } return 0; } static int reuseaddr(int fd) { int yes; yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;SO_REUSEADDR failed: %s&quot;, strerror(errno)); return -1; } #ifdef SO_REUSEPORT yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;SO_REUSEPORT failed: %s&quot;, strerror(errno)); if (errno != ENOPROTOOPT) return -1; } #endif return 0; } static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { assert(fd &gt;= 0); assert(sa); assert(l &gt; 0); if (bind(fd, sa, l) &lt; 0) { if (errno != EADDRINUSE) { avahi_log_warn(&quot;bind() failed: %s&quot;, strerror(errno)); return -1; } avahi_log_warn(&quot;*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***&quot;, sa-&gt;sa_family == AF_INET ? &quot;IPv4&quot; : &quot;IPv6&quot;); /* Try again, this time with SO_REUSEADDR set */ if (reuseaddr(fd) &lt; 0) return -1; if (bind(fd, sa, l) &lt; 0) { avahi_log_warn(&quot;bind() failed: %s&quot;, strerror(errno)); return -1; } } else { /* We enable SO_REUSEADDR afterwards, to make sure that the * user may run other mDNS implementations if he really * wants. */ if (reuseaddr(fd) &lt; 0) return -1; } return 0; } static int ipv4_pktinfo(int fd) { int yes; #ifdef IP_PKTINFO yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IP_PKTINFO failed: %s&quot;, strerror(errno)); return -1; } #else #ifdef IP_RECVINTERFACE yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVINTERFACE, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IP_RECVINTERFACE failed: %s&quot;, strerror(errno)); return -1; } #elif defined(IP_RECVIF) yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IP_RECVIF failed: %s&quot;, strerror(errno)); return -1; } #endif #ifdef IP_RECVDSTADDR yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IP_RECVDSTADDR failed: %s&quot;, strerror(errno)); return -1; } #endif #endif /* IP_PKTINFO */ #ifdef IP_RECVTTL yes = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IP_RECVTTL failed: %s&quot;, strerror(errno)); return -1; } #endif return 0; } static int ipv6_pktinfo(int fd) { int yes; #ifdef IPV6_RECVPKTINFO yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_RECVPKTINFO failed: %s&quot;, strerror(errno)); return -1; } #elif defined(IPV6_PKTINFO) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_PKTINFO failed: %s&quot;, strerror(errno)); return -1; } #endif #ifdef IPV6_RECVHOPS yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_RECVHOPS failed: %s&quot;, strerror(errno)); return -1; } #elif defined(IPV6_RECVHOPLIMIT) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_RECVHOPLIMIT failed: %s&quot;, strerror(errno)); return -1; } #elif defined(IPV6_HOPLIMIT) yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_HOPLIMIT failed: %s&quot;, strerror(errno)); return -1; } #endif return 0; } int avahi_open_socket_ipv4(int no_reuse) { struct sockaddr_in local; int fd = -1, r, ittl; uint8_t ttl, cyes; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) &lt; 0) { avahi_log_warn(&quot;socket() failed: %s&quot;, strerror(errno)); goto fail; } ttl = 255; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &amp;ttl, sizeof(ttl)) &lt; 0) { avahi_log_warn(&quot;IP_MULTICAST_TTL failed: %s&quot;, strerror(errno)); goto fail; } ittl = 255; if (setsockopt(fd, IPPROTO_IP, IP_TTL, &amp;ittl, sizeof(ittl)) &lt; 0) { avahi_log_warn(&quot;IP_TTL failed: %s&quot;, strerror(errno)); goto fail; } cyes = 1; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &amp;cyes, sizeof(cyes)) &lt; 0) { avahi_log_warn(&quot;IP_MULTICAST_LOOP failed: %s&quot;, strerror(errno)); goto fail; } memset(&amp;local, 0, sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(AVAHI_MDNS_PORT); if (no_reuse) r = bind(fd, (struct sockaddr*) &amp;local, sizeof(local)); else r = bind_with_warn(fd, (struct sockaddr*) &amp;local, sizeof(local)); if (r &lt; 0) goto fail; if (ipv4_pktinfo (fd) &lt; 0) goto fail; if (avahi_set_cloexec(fd) &lt; 0) { avahi_log_warn(&quot;FD_CLOEXEC failed: %s&quot;, strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) &lt; 0) { avahi_log_warn(&quot;O_NONBLOCK failed: %s&quot;, strerror(errno)); goto fail; } return fd; fail: if (fd &gt;= 0) close(fd); return -1; } int avahi_open_socket_ipv6(int no_reuse) { struct sockaddr_in6 sa, local; int fd = -1, yes, r; int ttl; mdns_mcast_group_ipv6(&amp;sa); if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) &lt; 0) { avahi_log_warn(&quot;socket() failed: %s&quot;, strerror(errno)); goto fail; } ttl = 255; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &amp;ttl, sizeof(ttl)) &lt; 0) { avahi_log_warn(&quot;IPV6_MULTICAST_HOPS failed: %s&quot;, strerror(errno)); goto fail; } ttl = 255; if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &amp;ttl, sizeof(ttl)) &lt; 0) { avahi_log_warn(&quot;IPV6_UNICAST_HOPS failed: %s&quot;, strerror(errno)); goto fail; } yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_V6ONLY failed: %s&quot;, strerror(errno)); goto fail; } yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_MULTICAST_LOOP failed: %s&quot;, strerror(errno)); goto fail; } memset(&amp;local, 0, sizeof(local)); local.sin6_family = AF_INET6; local.sin6_port = htons(AVAHI_MDNS_PORT); if (no_reuse) r = bind(fd, (struct sockaddr*) &amp;local, sizeof(local)); else r = bind_with_warn(fd, (struct sockaddr*) &amp;local, sizeof(local)); if (r &lt; 0) goto fail; if (ipv6_pktinfo(fd) &lt; 0) goto fail; if (avahi_set_cloexec(fd) &lt; 0) { avahi_log_warn(&quot;FD_CLOEXEC failed: %s&quot;, strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) &lt; 0) { avahi_log_warn(&quot;O_NONBLOCK failed: %s&quot;, strerror(errno)); goto fail; } return fd; fail: if (fd &gt;= 0) close(fd); return -1; } static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { assert(fd &gt;= 0); assert(msg); for (;;) { if (sendmsg(fd, msg, flags) &gt;= 0) break; if (errno == EINTR) continue; if (errno != EAGAIN) { char where[64]; struct sockaddr_storage *ss = msg-&gt;msg_name; if (ss-&gt;ss_family == PF_INET) { inet_ntop(ss-&gt;ss_family, &amp;((struct sockaddr_in*)ss)-&gt;sin_addr, where, sizeof(where)); } else if (ss-&gt;ss_family == PF_INET6) { inet_ntop(ss-&gt;ss_family, &amp;((struct sockaddr_in6*)ss)-&gt;sin6_addr, where, sizeof(where)); } else { where[0] = &#39;\0&#39;; } avahi_log_debug(&quot;sendmsg() to %s failed: %s&quot;, where, strerror(errno)); return -1; } if (avahi_wait_for_write(fd) &lt; 0) return -1; } return 0; } int avahi_send_dns_packet_ipv4( int fd, AvahiIfIndex interface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port) { struct sockaddr_in sa; struct msghdr msg; struct iovec io; #ifdef IP_PKTINFO struct cmsghdr *cmsg; size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; #elif !defined(IP_MULTICAST_IF) &amp;&amp; defined(IP_SENDSRCADDR) struct cmsghdr *cmsg; size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1]; #endif assert(fd &gt;= 0); assert(p); assert(avahi_dns_packet_check_valid(p) &gt;= 0); assert(!dst_address || dst_port &gt; 0); if (!dst_address) mdns_mcast_group_ipv4(&amp;sa); else ipv4_address_to_sockaddr(&amp;sa, dst_address, dst_port); memset(&amp;io, 0, sizeof(io)); io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p-&gt;size; memset(&amp;msg, 0, sizeof(msg)); msg.msg_name = &amp;sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &amp;io; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = NULL; msg.msg_controllen = 0; #ifdef IP_PKTINFO if (interface &gt; 0 || src_address) { struct in_pktinfo *pkti; memset(cmsg_data, 0, sizeof(cmsg_data)); msg.msg_control = cmsg_data; msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg-&gt;cmsg_len = msg.msg_controllen; cmsg-&gt;cmsg_level = IPPROTO_IP; cmsg-&gt;cmsg_type = IP_PKTINFO; pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); if (interface &gt; 0) pkti-&gt;ipi_ifindex = interface; if (src_address) pkti-&gt;ipi_spec_dst.s_addr = src_address-&gt;address; } #elif defined(IP_MULTICAST_IF) if (src_address) { struct in_addr any = { INADDR_ANY }; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &amp;src_address-&gt;address : &amp;any, sizeof(struct in_addr)) &lt; 0) { avahi_log_warn(&quot;IP_MULTICAST_IF failed: %s&quot;, strerror(errno)); return -1; } } #elif defined(IP_SENDSRCADDR) if (src_address) { struct in_addr *addr; memset(cmsg_data, 0, sizeof(cmsg_data)); msg.msg_control = cmsg_data; msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr)); cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg-&gt;cmsg_len = msg.msg_controllen; cmsg-&gt;cmsg_level = IPPROTO_IP; cmsg-&gt;cmsg_type = IP_SENDSRCADDR; addr = (struct in_addr *)CMSG_DATA(cmsg); addr-&gt;s_addr = src_address-&gt;address; } #elif defined(__GNUC__) #warning &quot;FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available&quot; #endif return sendmsg_loop(fd, &amp;msg, 0); } int avahi_send_dns_packet_ipv6( int fd, AvahiIfIndex interface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port) { struct sockaddr_in6 sa; struct msghdr msg; struct iovec io; struct cmsghdr *cmsg; size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1]; assert(fd &gt;= 0); assert(p); assert(avahi_dns_packet_check_valid(p) &gt;= 0); assert(!dst_address || dst_port &gt; 0); if (!dst_address) mdns_mcast_group_ipv6(&amp;sa); else ipv6_address_to_sockaddr(&amp;sa, dst_address, dst_port); memset(&amp;io, 0, sizeof(io)); io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p-&gt;size; memset(&amp;msg, 0, sizeof(msg)); msg.msg_name = &amp;sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &amp;io; msg.msg_iovlen = 1; msg.msg_flags = 0; if (interface &gt; 0 || src_address) { struct in6_pktinfo *pkti; memset(cmsg_data, 0, sizeof(cmsg_data)); msg.msg_control = cmsg_data; msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg-&gt;cmsg_len = msg.msg_controllen; cmsg-&gt;cmsg_level = IPPROTO_IPV6; cmsg-&gt;cmsg_type = IPV6_PKTINFO; pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg); if (interface &gt; 0) pkti-&gt;ipi6_ifindex = interface; if (src_address) memcpy(&amp;pkti-&gt;ipi6_addr, src_address-&gt;address, sizeof(src_address-&gt;address)); } else { msg.msg_control = NULL; msg.msg_controllen = 0; } return sendmsg_loop(fd, &amp;msg, 0); } AvahiDnsPacket *avahi_recv_dns_packet_ipv4( int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) { AvahiDnsPacket *p= NULL; struct msghdr msg; struct iovec io; size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */ ssize_t l; struct cmsghdr *cmsg; int found_addr = 0; int ms; struct sockaddr_in sa; assert(fd &gt;= 0); if (ioctl(fd, FIONREAD, &amp;ms) &lt; 0) { avahi_log_warn(&quot;ioctl(): %s&quot;, strerror(errno)); goto fail; } if (ms &lt; 0) { avahi_log_warn(&quot;FIONREAD returned negative value.&quot;); goto fail; } p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p-&gt;max_size; memset(&amp;msg, 0, sizeof(msg)); msg.msg_name = &amp;sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &amp;io; msg.msg_iovlen = 1; msg.msg_control = aux; msg.msg_controllen = sizeof(aux); msg.msg_flags = 0; if ((l = recvmsg(fd, &amp;msg, 0)) &lt; 0) { /* Linux returns EAGAIN when an invalid IP packet has been received. We suppress warnings in this case because this might create quite a bit of log traffic on machines with unstable links. (See #60) */ if (errno != EAGAIN) avahi_log_warn(&quot;recvmsg(): %s&quot;, strerror(errno)); goto fail; } /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So * fail after having read them. */ if (!ms) goto fail; if (sa.sin_addr.s_addr == INADDR_ANY) /* Linux 2.4 behaves very strangely sometimes! */ goto fail; assert(!(msg.msg_flags &amp; MSG_CTRUNC)); assert(!(msg.msg_flags &amp; MSG_TRUNC)); p-&gt;size = (size_t) l; if (ret_src_port) *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &amp;sa); if (ret_src_address) { AvahiAddress a; avahi_address_from_sockaddr((struct sockaddr*) &amp;sa, &amp;a); *ret_src_address = a.data.ipv4; } if (ret_ttl) *ret_ttl = 255; if (ret_iface) *ret_iface = AVAHI_IF_UNSPEC; for (cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&amp;msg, cmsg)) { if (cmsg-&gt;cmsg_level == IPPROTO_IP) { switch (cmsg-&gt;cmsg_type) { #ifdef IP_RECVTTL case IP_RECVTTL: #endif case IP_TTL: if (ret_ttl) *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); break; #ifdef IP_PKTINFO case IP_PKTINFO: { struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); if (ret_iface &amp;&amp; i-&gt;ipi_ifindex &gt; 0) *ret_iface = (int) i-&gt;ipi_ifindex; if (ret_dst_address) ret_dst_address-&gt;address = i-&gt;ipi_addr.s_addr; found_addr = 1; break; } #endif #ifdef IP_RECVIF case IP_RECVIF: { struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg); if (ret_iface) { #ifdef __sun if (*(uint_t*) sdl &gt; 0) *ret_iface = *(uint_t*) sdl; #else if (sdl-&gt;sdl_index &gt; 0) *ret_iface = (int) sdl-&gt;sdl_index; #endif } break; } #endif #ifdef IP_RECVDSTADDR case IP_RECVDSTADDR: if (ret_dst_address) memcpy(&amp;ret_dst_address-&gt;address, CMSG_DATA (cmsg), 4); found_addr = 1; break; #endif default: avahi_log_warn(&quot;Unhandled cmsg_type: %d&quot;, cmsg-&gt;cmsg_type); break; } } } assert(found_addr); return p; fail: if (p) avahi_dns_packet_free(p); return NULL; } AvahiDnsPacket *avahi_recv_dns_packet_ipv6( int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) { AvahiDnsPacket *p = NULL; struct msghdr msg; struct iovec io; size_t aux[1024 / sizeof(size_t)]; ssize_t l; int ms; struct cmsghdr *cmsg; int found_ttl = 0, found_iface = 0; struct sockaddr_in6 sa; assert(fd &gt;= 0); if (ioctl(fd, FIONREAD, &amp;ms) &lt; 0) { avahi_log_warn(&quot;ioctl(): %s&quot;, strerror(errno)); goto fail; } if (ms &lt; 0) { avahi_log_warn(&quot;FIONREAD returned negative value.&quot;); goto fail; } p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p-&gt;max_size; memset(&amp;msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr*) &amp;sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &amp;io; msg.msg_iovlen = 1; msg.msg_control = aux; msg.msg_controllen = sizeof(aux); msg.msg_flags = 0; if ((l = recvmsg(fd, &amp;msg, 0)) &lt; 0) { /* Linux returns EAGAIN when an invalid IP packet has been received. We suppress warnings in this case because this might create quite a bit of log traffic on machines with unstable links. (See #60) */ if (errno != EAGAIN) avahi_log_warn(&quot;recvmsg(): %s&quot;, strerror(errno)); goto fail; } /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So * fail after having read them. */ if (!ms) goto fail; assert(!(msg.msg_flags &amp; MSG_CTRUNC)); assert(!(msg.msg_flags &amp; MSG_TRUNC)); p-&gt;size = (size_t) l; if (ret_src_port) *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &amp;sa); if (ret_src_address) { AvahiAddress a; avahi_address_from_sockaddr((struct sockaddr*) &amp;sa, &amp;a); *ret_src_address = a.data.ipv6; } for (cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&amp;msg, cmsg)) { if (cmsg-&gt;cmsg_level == IPPROTO_IPV6) { switch (cmsg-&gt;cmsg_type) { case IPV6_HOPLIMIT: if (ret_ttl) *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); found_ttl = 1; break; case IPV6_PKTINFO: { struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); if (ret_iface &amp;&amp; i-&gt;ipi6_ifindex &gt; 0) *ret_iface = i-&gt;ipi6_ifindex; if (ret_dst_address) memcpy(ret_dst_address-&gt;address, i-&gt;ipi6_addr.s6_addr, 16); found_iface = 1; break; } default: avahi_log_warn(&quot;Unhandled cmsg_type: %d&quot;, cmsg-&gt;cmsg_type); break; } } } assert(found_iface); assert(found_ttl); return p; fail: if (p) avahi_dns_packet_free(p); return NULL; } int avahi_open_unicast_socket_ipv4(void) { struct sockaddr_in local; int fd = -1; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) &lt; 0) { avahi_log_warn(&quot;socket() failed: %s&quot;, strerror(errno)); goto fail; } memset(&amp;local, 0, sizeof(local)); local.sin_family = AF_INET; if (bind(fd, (struct sockaddr*) &amp;local, sizeof(local)) &lt; 0) { avahi_log_warn(&quot;bind() failed: %s&quot;, strerror(errno)); goto fail; } if (ipv4_pktinfo(fd) &lt; 0) { goto fail; } if (avahi_set_cloexec(fd) &lt; 0) { avahi_log_warn(&quot;FD_CLOEXEC failed: %s&quot;, strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) &lt; 0) { avahi_log_warn(&quot;O_NONBLOCK failed: %s&quot;, strerror(errno)); goto fail; } return fd; fail: if (fd &gt;= 0) close(fd); return -1; } int avahi_open_unicast_socket_ipv6(void) { struct sockaddr_in6 local; int fd = -1, yes; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) &lt; 0) { avahi_log_warn(&quot;socket() failed: %s&quot;, strerror(errno)); goto fail; } yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &amp;yes, sizeof(yes)) &lt; 0) { avahi_log_warn(&quot;IPV6_V6ONLY failed: %s&quot;, strerror(errno)); goto fail; } memset(&amp;local, 0, sizeof(local)); local.sin6_family = AF_INET6; if (bind(fd, (struct sockaddr*) &amp;local, sizeof(local)) &lt; 0) { avahi_log_warn(&quot;bind() failed: %s&quot;, strerror(errno)); goto fail; } if (ipv6_pktinfo(fd) &lt; 0) goto fail; if (avahi_set_cloexec(fd) &lt; 0) { avahi_log_warn(&quot;FD_CLOEXEC failed: %s&quot;, strerror(errno)); goto fail; } if (avahi_set_nonblock(fd) &lt; 0) { avahi_log_warn(&quot;O_NONBLOCK failed: %s&quot;, strerror(errno)); goto fail; } return fd; fail: if (fd &gt;= 0) close(fd); return -1; }
最新发布
08-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青衫客36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值