Linux igmp snooping 学习笔记 之一 相应的数据结构与初始化

来源:互联网 发布:csol网络加速器 编辑:程序博客网 时间:2024/06/14 00:46


一、igmpsnooping相关的数据结构

组播相关的数据结构主要有三个,下面分别分析:

 

1、struct net_bridge_mdb_htable

 

 

/*组播组数据库转发表,该结构体将所有的组播组数据库转发项通过hash数组连接到一起*/

struct net_bridge_mdb_htable

{

       structhlist_head            *mhash; //hash数组,将所有的net_bridge_mdb_entry链接到一起

       structrcu_head                     rcu;

       structnet_bridge_mdb_htable       *old;

       u32                       size; //hash数组中所有hash链表中存在的所有net_bridge_mdb_entry项的总数

       u32                       max;//hash数组的最大值

       u32                       secret;

       u32                       ver;

};

 

2、net_bridge_mdb_entry

 

/*一个组播组数据库转发项,描述一个组播组的详细信息*/

struct net_bridge_mdb_entry

{

       structhlist_node            hlist[2];

       structhlist_node            mglist;

       structnet_bridge           *br;//桥

       structnet_bridge_port_group *ports;//

       structrcu_head                     rcu;

       structtimer_list             timer;//组播组数据库项失效定时器,若超时,则会将该组播端口从组播组数据库项的组播端口列表中删除

       structtimer_list             query_timer;//查询定时

       __be32                         addr;//组播组地址

       u32                       queries_sent;

};

其中port指向所有加入到组播组addr的组播端口

mglist用于将通过桥br接收到的igmp 加入报文创建的组播组数据库转发项连接起来

 

3、struct net_bridge_port_group

/*加入一个组播组的组播端口信息结构体*/

struct net_bridge_port_group {

       structnet_bridge_port           *port;//加入该组播组的桥端口

       structnet_bridge_port_group *next;//下一个组播组详细参数结构体(可以有多个端口加入到同一个组播组)

       structhlist_node            mglist;

       structrcu_head                     rcu;//rcu表头

       structtimer_list             timer;//组播端口失效定时器,若超时,则会将该组播端口从组播组数据库项的组播端口列表中删除

       structtimer_list             query_timer;//查询计时器

       __be32                         addr;//组播组地址,每一个组播端口也要一个组播组的原因是,通过这个值可以快速查找到其关联的组播组数据库项

       u32                       queries_sent;//已发送查询包的次数

};

其中,port链表用于将所有加入到组播组addr的对应桥端

Next指向下一个组播端口,该下一个组播组地址也为addr,仅仅是port值不同。

其中mglist用于将该组播端口加入到port的mglist链表中,通过桥端口的mglist链表,能够查找到该桥端口加入的所有组播组。

下面是这3个数据结构之间的关系

 

 

二、Igmpsnooping的初始化

Igmp snooping的初始化是使用函数br_multicast_init来实现的,在该函数里,主要组播组转发数据库表的hash数组的最大值进行设置,设置发送查询报文的间隔时间,以及发送查询报文的次数,并初始化桥的igmp查询的定时器。

void br_multicast_init(struct net_bridge*br)

{

       br->hash_elasticity= 4;//每个组播组 ip中所能关联的端口个数

       br->hash_max= 512; //mdb中hash数组的最大值

 

       br->multicast_router= 1;

       br->multicast_last_member_count= 2;

       br->multicast_startup_query_count= 2;

 

       br->multicast_last_member_interval= HZ;

       br->multicast_query_response_interval= 10 * HZ; //组播查询最大回复时间

       br->multicast_startup_query_interval= 125 * HZ / 4;  //开启发送查询报文的间隔时间

       br->multicast_query_interval= 125 * HZ; //查询包的发送间隔时间

       br->multicast_querier_interval= 255 * HZ;

       br->multicast_membership_interval= 260 * HZ;

 

       spin_lock_init(&br->multicast_lock);

       setup_timer(&br->multicast_router_timer,

                  br_multicast_local_router_expired, 0);

       setup_timer(&br->multicast_querier_timer,

                  br_multicast_local_router_expired, 0);

       setup_timer(&br->multicast_query_timer,br_multicast_query_expired,

                  (unsigned long)br);

}

 

2、桥与桥端口的查询定时器

对于桥与桥端口的查询定时器,主要是用来实现igmp snooping组播数据库转发表的持续更新的。

 

a)Br桥的查询定时器

对于定时器br->multicast_query_timer,在开启igmp snooping功能后就会开启桥端口的组播查询功能,主要分为如下情况:

i)当br设备up且开启igmp snooping时,就会调用函数br_multicast_open,开启一个立即超时的查询定时器

ii) 重新开始igmp snooping时,也会调用函数br_multicast_open,开启一个立即超时的查询定时器。

/*

开启一个桥的igmpsnooping功能

1、重置发送查询数据包计数

2、修改桥的查询定时器

*/

void br_multicast_open(struct net_bridge*br)

{

       br->multicast_startup_queries_sent= 0;

 

       if(br->multicast_disabled)

              return;

 

       mod_timer(&br->multicast_query_timer,jiffies);

}

 

 

当桥的查询定时器超时,就会调用函数br_multicast_query_expired进行处理,对于桥设备,该函数会发送一个组播通用查询包给上层进行处理,这时如果上层协议栈开启了igmp proxy功能,就会触发上层协议栈对lan侧的设备通用组播查询功能。对于上层开启igmp proxy功能,通过桥的查询定时器就能对lan侧设备进行周期的通用查询,然后就可以间接实现igmp snooping组播转发数据库的更新。

 

       对于桥的查询定时器,因为其需要上层开启igmp proxy功能,且当上层开启igmp proxy功能后,如果开启了igmp snooping功能,就能通过发送通用的组播查询报文,实现快速的建立组播组数据库转发表。

       从上面的程序可以看出,其是依赖上层是否开启igmp proxy,而不是直接发送通用组播组查询报文,这样也满足了igmp协议的要求,即只有组播组路由器才能发送组播查询报文。

 

B)桥端口的查询定时器

那如果上层协议栈不支持igmpproxy功能,上层协议栈收到通用查询报文就会丢弃掉,就不会触发上层协议栈对lan侧设备的通用查询,那不就没有办法更新igmp snooping的组播组转发数据库了?

       代码编写者显然也注意到了这个问题,所以又增加了桥端口的组播查询定时器功能,在开启igmp snooping功能后就会开启桥端口的组播查询功能,分为下面两种情况:

1、  在一个桥端口up起来且开启igmp snooping时,通过间接调用__br_multicast_enable_port,启动一个立即到期的桥端口的查询定时器

2、  在重新开启igmp snooping功能时,通过调用__br_multicast_enable_port,启动一个立即到期的桥端口的查询定时器。

下面我们分析一下函数__br_multicast_enable_port。

static void__br_multicast_enable_port(struct net_bridge_port *port)

{

       port->multicast_startup_queries_sent= 0;

 

       if(try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||

          del_timer(&port->multicast_query_timer))

              mod_timer(&port->multicast_query_timer,jiffies);

}

该函数还是比较简单的,主要是开启一个立即超时的桥端口定时器,超时后即会执行超时处理函数br_multicast_port_query_expired。

 

 

static voidbr_multicast_port_query_expired(unsigned long data)

{

       structnet_bridge_port *port = (void *)data;

       structnet_bridge *br = port->br;

 

       spin_lock(&br->multicast_lock);

       if(port->state == BR_STATE_DISABLED ||

           port->state == BR_STATE_BLOCKING)

              gotoout;

 

       if(port->multicast_startup_queries_sent <

           br->multicast_startup_query_count)

              port->multicast_startup_queries_sent++;

 

       br_multicast_send_query(port->br,port,

                            port->multicast_startup_queries_sent);

 

out:

       spin_unlock(&br->multicast_lock);

}

该函数的结构还是比较简单的,主要是判断桥端口是否处于forward状态,接着就会调用函数br_multicast_send_query发送一个通用查询报文。

 

       对于函数br_multicast_send_query,其既可以向本地上层协议栈发送组播组查询报文,也可以将组播组通用查询报文从指定的桥端口发送出去。

       下面分析一下函数br_multicast_send_query

/*

1、  调用函数br_multicast_alloc_query构造一个通用组播组查询报文

2、  对于桥端口不为空时,则将该查询报文从相应的端口发送出去

3、  对于桥端口为空时,则将该查询报文发送给上层协议栈

4、  更新桥或者桥端口的查询定时器。

*/

static void br_multicast_send_query(structnet_bridge *br,

                                struct net_bridge_port *port, u32 sent)

{

       unsignedlong time;

       structsk_buff *skb;

 

       if(!netif_running(br->dev) || br->multicast_disabled ||

          timer_pending(&br->multicast_querier_timer))

              return;

       /*构造一个通用查询数据包*/

       skb= br_multicast_alloc_query(br, 0);

       if(!skb)

              gototimer;

 

       if(port) {

              __skb_push(skb,sizeof(struct ethhdr));

              skb->dev= port->dev;

              NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_OUT, skb, NULL, skb->dev,

                     dev_queue_xmit);

       }else

              netif_rx(skb);

 

timer:

       time= jiffies;

       time+= sent < br->multicast_startup_query_count ?

              br->multicast_startup_query_interval:

              br->multicast_query_interval;

       mod_timer(port? &port->multicast_query_timer :

                      &br->multicast_query_timer, time);

}

以上就是桥与桥端口的组播查询定时器的工作原理。

 

三、igmpsnooping 的开启与关闭

Igmp snooping的开启与关闭是通过函数br_multicast_toggle实现

对于igmp snooping的开启与关闭,目前是通过sysfs实现igmpsnooping的开启与关闭。

我们可以通过添加ioctl接口,实现通过brctl来开启或者关闭igmp SNOOPING

 

/*

功能:开启或者关闭igmpSNOOPING的函数。

只有当要设置的值与br->multicast_disabled的当前值不同时才允许修改。

当开启igmpsnooping功能时,则重新创建组播组转发数据库表,并调用br_multicast_open与__br_multicast_enable_port启动桥与桥端口的组播查询定时器。

*/

int br_multicast_toggle(struct net_bridge*br, unsigned long val)

{

       structnet_bridge_port *port;

       interr = -ENOENT;

 

       spin_lock(&br->multicast_lock);

       if(!netif_running(br->dev))

              gotounlock;

 

       err= 0;

       /*如果要设置的值与当前的值相同,则直接返回*/

       if(br->multicast_disabled == !val)

              gotounlock;

 

       br->multicast_disabled= !val;

       /*当关闭igmp SNOOPING时,并没有释放br->mdb*/

       if(br->multicast_disabled)

              gotounlock;

   /*

 

    1、当开启IGMP SNOOPING时,如果br->mdb、br->mdb->old均不为0,

          则说明br->mdb值有问题,此时则会关闭IGMP SNOOPING功能

    2、当开启IGMP SNOOPING时,如果br->mdb不为0,且br->mdb->old为0,

           则调用br_mdb_rehash,将原br->mdb中的组播组项重新计算hash值

           并存放在新的br->mdb中,释放原br->mdb占用的空间*/

       if(br->mdb) {

              if(br->mdb->old) {

                     err= -EEXIST;

rollback:

                     br->multicast_disabled= !!val;

                     gotounlock;

              }

 

              err= br_mdb_rehash(&br->mdb, br->mdb->max,

                                br->hash_elasticity);

              if(err)

                     gotorollback;

       }

       /*

     1、reset br的发送查询包统计计数

     2、修改br的组播查询定时器multicast_query_timer的值

       */

       br_multicast_open(br);

       /*

       对于br桥组上的每一个端口

     1、reset port的发送查询包统计计数

     2、删除端口的查询定时器,并重新调度

       */

       list_for_each_entry(port,&br->port_list, list) {

              if(port->state == BR_STATE_DISABLED ||

                  port->state == BR_STATE_BLOCKING)

                     continue;

 

              __br_multicast_enable_port(port);

       }

 

unlock:

       spin_unlock(&br->multicast_lock);

 

       returnerr;

}

至此完成了igmpsnooping的数据结构与初始化的介绍,下节分析igmp snooping的接收处理函数。

0 0
原创粉丝点击