linux 怎样加入一个多播组

linux 怎样加入一个多播组,第1张

应用程序通过命令字IP_ADD_MEMBERSHIP把一个socket加入到一个多播组,IP_ADD_MEMBERSHIP是一个IP层的命令字,其调用使用的参数是结构体struct ip_mreq,其定义如下:struct ip_mreq{struct in_addr imr_multiaddr struct in_addr imr_interface } 该结构体的两个成员分别用于指定所加入的多播组的组IP地址,和所要加入组的那个本地接口的IP地址。该命令字没有源过滤的功能,它相当于实现IGMPv1的多播加入服务接口。ip_setsockopt实现了该命令字,它通过调用ip_mc_join_group把socket加入到多播组。表示socket的结构体struct inet_sock有一个成员mc_list,它是一个结构体struct ip_mc_socklist的指针,实际上一个该结构体的链表,该结构体的定义如下:struct ip_mc_socklist{struct ip_mc_socklist *next struct ip_mreqn multi unsigned intsfmode struct ip_sf_socklist *sflist }next指向链表的下一个节点;multi表示组信息,即在哪一个本地接口上,加入到哪一个多播组;sfmode是过滤模式,取值为MCAST_INCLUDE或MCAST_EXCLUDE,分别表示只接收sflist所列出的那些源的多播数据报,和不接收sflist所列出的那些源的多播数据报;sflist是源列表,结构体struct ip_sf_socklist的定义如下:struct ip_sf_socklist{unsigned intsl_max unsigned intsl_count __u32 sl_addr[0] }sl_addr是源地址列表,sl_count应该是源地址列表中源地址的数量,sl_max应该是当前sl_addr数组的最大可容纳量(不确定)。对于通过调用IP_ADD_MEMBERSHIP加入的多播组,它会在struct inet_sock的mc_list的链表头添加如下一个节点:struct ip_mc_socklist{.next = 原来的链表头 .multi = 所加入的多播组,和接口信息 .sfmode = MCAST_EXCLUDE .sflist = NULL即不排除任何源地址,也就是不存在源过滤。}另外,一个socket所允许加入的多播组的最大数量也是有限制的,mc_list中节点的数量不允许超过sysctl_igmp_max_memberships(缺省为20)。ip_mc_join_group还需要通过ip_mreq.imr_interface的指定值找到要加入多播组的那个接口,并为接口设置状态(即该接口要加入哪个多播组,过滤哪些源,也就是为该接口增加一个组,如果要增加的组已存在,则增加该组的引用计数)。代表网络设备接口的结构体struct in_device有一个成员mc_list,这是一个结构体struct ip_mc_list的链表,该结构体的定义如下:struct ip_mc_list{struct in_device*interface unsigned long multiaddr struct ip_sf_list *sources struct ip_sf_list *tomb unsigned intsfmode unsigned long sfcount[2] struct ip_mc_list *next struct timer_list timer int users atomic_trefcnt spinlock_t lock chartm_running charreporter charunsolicit_count charloaded unsigned char gsquery unsigned char crcount }interface指向网络设备接口,multicast即为加入的组的多播地址,users记录当前有几个socket在该接口上加入了该多播组。sfcount是一个有两个元素的数组,分别记录在该接口上加入多播组的socket的过滤模式为EXCLUDE和INCLUDE的数量,sfmode为该接口本身的过滤模式。sources为源地址列表,该结构体具体内容稍后再分析。timer为主动报告定时器,当一个接口(注意:不是socket)新加入到一个多播组,需要向多播路由器发送一个igmp报告,以通知多播路由器需要向本地网络转发该组的数据报。tm_running是一个标志,如果timer当前正在运行,则置1,否则置0。reporter也是一个标志,如果当前正要开始发送igmp报告,则置该标志为1,否则为0。unsolicit_count是当一个接口新加入到一个多播组时,发送主动报告的次数,值赋为IGMP_Unsolicited_Report_Count(缺省值为2)。loaded也是一个标志,当该接口上的该多播组被加入时,需要通知硬件过滤器,通知完成即置该标志为1,否则为0。该结构体比较复杂,先看通过IP_ADD_MEMBERSHIP命令字把一个socket加入到一个新的多播组,会使struct in_device的mc_list中增加一个什么样的节点。下面是生成的节点的情况:struct ip_mc_list{.interface = in_dev .multiaddr = 多播组地址 .source = NULL//源过滤列表为空。.tomb = NULL .sfmode = MCAST_EXCLUDE //EXCLUDE模式,即不过滤任何源。.sfcount[MCAST_EXCLUDE] = 1 .sfcount[MCAST_INCLUDE] = 0//即该节点上该多播组有一个socket加入,过滤模式为EXCLUDE。.users = 1 //有一个用户。.refcnt = 1//引用计数为1.tm_running = 0 .unsolicit_count = 2 ... ...}新生成的节点加入到mc_list链表中后,要通知网络设备接口的硬件,以使它的过滤机制可以接收进该多播组的数据报,同时也要通知多播路由器。首先要把多播地址映射成以太网地址,映射规则是把多播IP地址的低23位放到以太网多播地址01-00-5E-00-00-00(16进制)的低23位。因为一个IP组地址有28位有效位(除去高位的1110),所以有可能出现多个组地址被映射成同一个以太网多播地址,具体实现见ip_eth_mc_map。然后把这个mac地址加到硬件的过滤机制中。具体的实现在函数dev_mc_add中。代表网络设备接口的结构体struct net_device也有一个成员mc_list,它是一个结构体struct dev_mc_list的链表,该结构体的定义如下:struct dev_mc_list{struct dev_mc_list *next __u8dmi_addr[MAX_ADDR_LEN] unsigned char dmi_addrlen int dmi_users int dmi_gusers }next指向链表下一个节点,dmi_addr是多播mac地址,dmi_addrlen为多播mac地址的长度,dmi_users是在节点被重复到加入到设备上的次数,struct net_device还有一个成员mc_count,用于记录链表中节点的数量。dev_mc_add创建一个新的struct dev_mc_list节点,加入到链表中,并通过调用网络设备接口的成员函数set_multicast_list来启用设备的过滤机制。最后一步发送主动成员报告,这里,首先忽略IGMPv1和IGMPv2存在的情况。如果要加入的多播组是IGMP_ALL_HOSTS(224.0.0.1),则不需要发送成员报告。否则启用定时器struct in_device->mr_ifc_timer(接口状态改变定时器),该定时器在设备初始化的时候被建立,其超时处理函数是igmp_ifc_timer_expire,它发送一个IGMPv3的报告,然后再次启用定时器。也就是说,第一个主动成员报告立即发出,然后在一个0到IGMP_Unsolicited_Report_Interval(缺省为10秒)之间的一个时间后,发出第二个主动成员报告,连续发出IGMP_Unsolicited_Report_Count(缺省值为2)个。测试环境中要加入的多播组是224.0.1.1,发出的IGMPv3报告如下:数据含义22 第3版成员关系报告00 8bit保留,必须为0f8 fc 校验和00 00 16bit保留,必须为000 01 组记录的数量,为1下面为一条组记录:04 类型为CHANGE_TO_EXCLUDE_MODE,改变到EXCLUDE过滤模式00 辅助数据长度00 00 源地址的数量e0 00 01 01 组地址224.0.1.1

arp_ignore和arp_announce参数都和ARP协议相关,主要用于控制系统返回arp响应和发送arp请求时的动作。这两个参数很重要,特别是在LVS的DR场景下,它们的配置直接影响到DR转发是否正常。

首先看一下Linux内核文档中对于它们的描述:

arp_ignore - INTEGER

Define different modes for sending replies in response to

received ARP requests that resolve local target IP addresses:

0 - (default): reply for any local target IP address, configured

on any interface

1 - reply only if the target IP address is local address

configured on the incoming interface

2 - reply only if the target IP address is local address

configured on the incoming interface and both with the

sender's IP address are part from same subnet on this interface

3 - do not reply for local addresses configured with scope host,

only resolutions for global and link addresses are replied

4-7 - reserved

8 - do not reply for all local addresses

The max value from conf/{all,interface}/arp_ignore is used

when ARP request is received on the {interface}

arp_ignore参数的作用是控制系统在收到外部的arp请求时,是否要返回arp响应。

arp_ignore参数常用的取值主要有0,1,2,3~8较少用到:

0:响应任意网卡上接收到的对本机IP地址的arp请求(包括环回网卡上的地址),而不管该目的IP是否在接收网卡上。

1:只响应目的IP地址为接收网卡上的本地地址的arp请求。

2:只响应目的IP地址为接收网卡上的本地地址的arp请求,并且arp请求的源IP必须和接收网卡同网段。

3:如果ARP请求数据包所请求的IP地址对应的本地地址其作用域(scope)为主机(host),则不回应ARP响应数据包,如果作用域为全局(global)或链路(link),则回应ARP响应数据包。

4~7:保留未使用

8:不回应所有的arp请求

sysctl.conf中包含all和eth/lo(具体网卡)的arp_ignore参数,取其中较大的值生效。

arp_announce - INTEGER

Define different restriction levels for announcing the local

source IP address from IP packets in ARP requests sent on

interface:

0 - (default) Use any local address, configured on any interface

1 - Try to avoid local addresses that are not in the target's

subnet for this interface. This mode is useful when target

hosts reachable via this interface require the source IP

address in ARP requests to be part of their logical network

configured on the receiving interface. When we generate the

request we will check all our subnets that include the

target IP and will preserve the source address if it is from

such subnet. If there is no such subnet we select source

address according to the rules for level 2.

2 - Always use the best local address for this target.

In this mode we ignore the source address in the IP packet

and try to select local address that we prefer for talks with

the target host. Such local address is selected by looking

for primary IP addresses on all our subnets on the outgoing

interface that include the target IP address. If no suitable

local address is found we select the first local address

we have on the outgoing interface or on all other interfaces,

with the hope we will receive reply for our request and

even sometimes no matter the source IP address we announce.

The max value from conf/{all,interface}/arp_announce is used.

arp_announce的作用是控制系统在对外发送arp请求时,如何选择arp请求数据包的源IP地址。(比如系统准备通过网卡发送一个数据包a,这时数据包a的源IP和目的IP一般都是知道的,而根据目的IP查询路由表,发送网卡也是确定的,故源MAC地址也是知道的,这时就差确定目的MAC地址了。而想要获取目的IP对应的目的MAC地址,就需要发送arp请求。arp请求的目的IP自然就是想要获取其MAC地址的IP,而arp请求的源IP是什么呢? 可能第一反应会以为肯定是数据包a的源IP地址,但是这个也不是一定的,arp请求的源IP是可以选择的,控制这个地址如何选择就是arp_announce的作用)

arp_announce参数常用的取值有0,1,2。

0:允许使用任意网卡上的IP地址作为arp请求的源IP,通常就是使用数据包a的源IP。

1:尽量避免使用不属于该发送网卡子网的本地地址作为发送arp请求的源IP地址。

2:忽略IP数据包的源IP地址,选择该发送网卡上最合适的本地地址作为arp请求的源IP地址。

sysctl.conf中包含all和eth/lo(具体网卡)的arp_ignore参数,取其中较大的值生效。

(1)当arp_ignore参数配置为0时,eth1网卡上收到目的IP为环回网卡IP的arp请求,但是eth1也会返回arp响应,把自己的mac地址告诉对端。

(2)当arp_ignore参数配置为1时,eth1网卡上收到目的IP为环回网卡IP的arp请求,发现请求的IP不是自己网卡上的IP,不会回arp响应。

(3)当arp_announce参数配置为0时,系统要发送的IP包源地址为eth1的地址,IP包目的地址根据路由表查询判断需要从eth2网卡发出,这时会先从eth2网卡发起一个arp请求,用于获取目的IP地址的MAC地址。该arp请求的源MAC自然是eth2网卡的MAC地址,但是源IP地址会选择eth1网卡的地址。

(4)当arp_announce参数配置为2时,eth2网卡发起arp请求时,源IP地址会选择eth2网卡自身的IP地址。

因为DR模式下,每个真实服务器节点都要在环回网卡上绑定虚拟服务IP。这时候,如果客户端对于虚拟服务IP的arp请求广播到了各个真实服务器节点,如果arp_ignore参数配置为0,则各个真实服务器节点都会响应该arp请求,此时客户端就无法正确获取LVS节点上正确的虚拟服务IP所在网卡的MAC地址。假如某个真实服务器节点A的网卡eth1响应了该arp请求,客户端把A节点的eth1网卡的MAC地址误认为是LVS节点的虚拟服务IP所在网卡的MAC,从而将业务请求消息直接发到了A节点的eth1网卡。这时候虽然因为A节点在环回网卡上也绑定了虚拟服务IP,所以A节点也能正常处理请求,业务暂时不会受到影响。但时此时由于客户端请求没有发到LVS的虚拟服务IP上,所以LVS的负载均衡能力没有生效。造成的后果就是,A节点一直在单节点运行,业务量过大时可能会出现性能瓶颈。

所以DR模式下要求arp_ignore参数要求配置为1。

每个机器或者交换机中都有一张arp表,该表用于存储对端通信节点IP地址和MAC地址的对应关系。当收到一个未知IP地址的arp请求,就会再本机的arp表中新增对端的IP和MAC记录;当收到一个已知IP地址(arp表中已有记录的地址)的arp请求,则会根据arp请求中的源MAC刷新自己的arp表。

如果arp_announce参数配置为0,则网卡在发送arp请求时,可能选择的源IP地址并不是该网卡自身的IP地址,这时候收到该arp请求的其他节点或者交换机上的arp表中记录的该网卡IP和MAC的对应关系就不正确,可能会引发一些未知的网络问题,存在安全隐患。

所以DR模式下要求arp_announce参数要求配置为2。

arp_ignore和arp_announce参数分别有all,default,lo,eth1,eth2...等对应不同网卡的具体参数。当all和具体网卡的参数值不一致时,取较大值生效。

一般只需修改all和某个具体网卡的参数即可(取决于你需要修改哪个网卡)。下面以修改lo网卡为例:

net.ipv4.conf.all.arp_ignore=1

net.ipv4.conf.lo.arp_ignore=1

net.ipv4.conf.all.arp_announce=2

net.ipv4.conf.lo.arp_announce=2

sysctl -w net.ipv4.conf.all.arp_ignore=1

sysctl -w net.ipv4.conf.lo.arp_ignore=1

sysctl -w net.ipv4.conf.all.arp_announce=2

sysctl -w net.ipv4.conf.lo.arp_announce=2

echo "1">/proc/sys/net/ipv4/conf/all/arp_ignore

echo "1">/proc/sys/net/ipv4/conf/lo/arp_ignore

echo "2">/proc/sys/net/ipv4/conf/all/arp_announce

echo "2">/proc/sys/net/ipv4/conf/lo/arp_announce

linux系统第二块网卡收到组播包的原因可能是:1、网卡配置错误,导致网卡接收到了组播包;2、网络中有其他设备发送了组播包,网卡接收到了组播包;3、网络中有其他设备发送了组播包,但是网卡没有正确过滤,导致网卡接收到了组播包。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8750846.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-20
下一篇2023-04-20

发表评论

登录后才能评论

评论列表(0条)

    保存