网卡驱动如何设置组播MAC地址

最近关注了一些IP组播的知识,IP的组播需要以太网的支持。在这边文章内我们就主要讨论以太网如果支持IP组播。

首先看当前的interface是否支持multi-cast,如下面的命令红色部分标注,则说明当前的网卡支持组播,可以通过下面两个相关的设置选项进行设置。

      allmulti

              Enableor disable all-multicast mode.  Ifselected,  all  multi‐

              castpackets on the network will be received by the interface.

       multicast

              Set  the multicast  flag on the interface.This should not nor‐

              mally beneeded as the drivers  set  the flag  correctly  them‐

              selves.

 [ansen@localhostwork]$ ifconfig

ens33: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500

        inet6fe80::250:56ff:fe33:bef  prefixlen64  scopeid 0x20<link>

        ether00:50:56:33:0b:ef  txqueuelen 1000  (Ethernet)

        RX packets37116  bytes 5195493 (4.9 MiB)

        RX errors0  dropped 0  overruns 0 frame 0

        TX packets183  bytes 17209 (16.8 KiB)

        TX errors0  dropped 0 overruns 0  carrier 0 collisions 0

        deviceinterrupt 19  base 0x2000 

通过if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof (structip_mreq)) == -1)

使得当前网卡加入某个组。然后通过netstat–g 查看当前的网卡是否已经加入了哪些组。

默认如果当前网卡支持multi-cast,那么在网卡启动时已经默认加入224.0.0.1这个组。这个组代表当前网络上所有支持组播的主机。

 

 

[ansen@localhost work]$ netstat -ng

IPv6/IPv4 Group Memberships

Interface       RefCntGroup

--------------- ------ ---------------------

lo              1      224.0.0.1

ens33           1      224.0.0.1

lo              1      ff02::1

lo              1      ff01::1

ens33           1      ff02::1:ff33:bef

ens33           1      ff02::1

ens33           1      ff01::1

 

在系统级别我们可以利用setsockopt使网卡加入某个组播组。那么在网卡驱动的层面是如何实现的呢。下面我们利用一份网卡驱动程序,详细解读以下在网卡驱动的实现。

 

下面两个地址代表了整个组播地址MAC地址的空间,对应到组播IP地址为224.0.0.0~239.255.255.255.

u_char  ether_ipmulticast_min[6]= { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };

u_char  ether_ipmulticast_max[6]= { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };

#ifdef INET6

u_char  ether_ipv6multicast_min[6]= { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };

u_char  ether_ipv6multicast_max[6]= { 0x33, 0x33, 0xff, 0xff, 0xff, 0xff };

#endif /* INET6 */

下面的宏实现了IP组播地址到MAC组播地址的转换。说的非常清楚。高25位固定,低23位是从IP组播地址拿过来的。

/*

 * Macro to map an IPmulticast address to an Ethernet multicast address.

 * The high-order 25bits of the Ethernet address are statically assigned,

 * and the low-order23 bits are taken from the low end of the IP address.

 */

#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr)                                                   \

                /*struct in_addr *ipaddr; */                                                                      \

                /*u_int8_t enaddr[ETHER_ADDR_LEN]; */                                                          \

{                                                                                                                                              \

                (enaddr)[0]= 0x01;                                                                                         \

                (enaddr)[1]= 0x00;                                                                                         \

                (enaddr)[2]= 0x5e;                                                                                         \

                (enaddr)[3]= ((u_int8_t *)ipaddr)[1] & 0x7f;                                      \

                (enaddr)[4]= ((u_int8_t *)ipaddr)[2];                                                    \

                (enaddr)[5]= ((u_int8_t *)ipaddr)[3];                                                    \

 

 

 

下面的函数就是就是网卡驱动中设置加组播MAC地址的函数。这个函数会作为函数指针赋给if.ioctl这函数,供上层应用调用。

/*

 * Add an Ethernetmulticast address or range of addresses to the list for a

 * given interface.

 */

int

ether_addmulti(ifr, ac)

                structifreq *ifr;   //参数1是IP相关的参数

                registerstruct arpcom *ac; //参数2大家从名字可以看出是ARP相关的参数

{

                registerstruct ether_multi *enm;

                structsockaddr_in *sin;

#ifdef INET6

                structsockaddr_in6 *sin6;

#endif /* INET6 */

                u_charaddrlo[6];

                u_charaddrhi[6];

                int s =splimp();

 

                switch(ifr->ifr_addr.sa_family) {

 

                caseAF_UNSPEC:

                                bcopy(ifr->ifr_addr.sa_data,addrlo, 6);

                                bcopy(addrlo,addrhi, 6);

                                break;

 

#ifdef INET

                caseAF_INET:  //查看参数中的IP地址族,如果是INET4则进行以下操作,我们关心的分支

                                sin= (struct sockaddr_in *)&(ifr->ifr_addr);

                                if(sin->sin_addr.s_addr == INADDR_ANY) {

                                                /*  //这里设置网卡加入整个组播区间,就是用ifconfig设置almulticast

                                                 * An IP address of INADDR_ANY means listen toall

                                                 * of the Ethernet multicast addresses used forIP.

                                                 * (This is for the sake of IP multicast routers.)

                                                 */

                                                bcopy(ether_ipmulticast_min,addrlo, 6);

                                                bcopy(ether_ipmulticast_max,addrhi, 6);

                                }

                                else{

                                        //这里的两行的意思就是转化MAC地址,并设置组播区间,由于我们只有一个地址所以上下边界是同一个地址。

                                                ETHER_MAP_IP_MULTICAST(&sin->sin_addr,addrlo);

                                                bcopy(addrlo,addrhi, 6);

                                }

                                break;

#endif

#ifdef INET6

                caseAF_INET6:

                                sin6= (struct sockaddr_in6 *)&(ifr->ifr_addr);

                                if(IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {

                                                /*

                                                 * An unspecified IPv6 address means listen toall

                                                 * of the IPv6 multicast addresses on thisEthernet.

                                                 * (Multicast routers like this.)

                                                 */

                                                bcopy(ether_ipv6multicast_min,addrlo, ETHER_ADDR_LEN);

                                                bcopy(ether_ipv6multicast_max,addrhi, ETHER_ADDR_LEN);

                                }else {

                                                ETHER_MAP_IN6_MULTICAST(sin6->sin6_addr,addrlo);

                                                bcopy(addrlo,addrhi, ETHER_ADDR_LEN);

                                }

                                break;

#endif /* INET6 */

 

                default:

                                splx(s);

                                return(EAFNOSUPPORT);

                }

 

                /*

                 * Verify that we have valid Ethernet multicastaddresses.

                 */

//这里我们可以知道组播和单播MAC地址的区别就是低位第一个字节是否为1.

                if((addrlo[0] & 0x01) != 1 || (addrhi[0] & 0x01) != 1) {

                                splx(s);

                                return(EINVAL);

                }

                /*

                 * See if the address range is already in thelist.

                 */

                ETHER_LOOKUP_MULTI(addrlo,addrhi, ac, enm);

                if (enm!= NULL) {

                                /*

                                 * Found it; just increment the referencecount.

                                 */

                                ++enm->enm_refcount;

                                splx(s);

                                return(0);

                }

                /*

                 * New address or range; malloc a new multicastrecord

                 * and link it into the interface's multicastlist.

                 */

    最后这个组播地址或者区间会添加到当前网卡的组播列表。而这个列表会被网卡驱动设置到multi-castfilter 相关的寄存器。具体的函数请看最后。

                enm =(struct ether_multi *)malloc(sizeof(*enm), M_IFMADDR, M_NOWAIT);

                if (enm== NULL) {

                                splx(s);

                                return(ENOBUFS);

                }

                bcopy(addrlo,enm->enm_addrlo, 6);

                bcopy(addrhi,enm->enm_addrhi, 6);

                enm->enm_ac= ac;

                enm->enm_refcount= 1;

                LIST_INSERT_HEAD(&ac->ac_multiaddrs,enm, enm_list);

                ac->ac_multicnt++;

                splx(s);

                /*

                 * Return ENETRESET to inform the driver thatthe list has changed

                 * and its reception filter should be adjustedaccordingly.

                 */

                return(ENETRESET);

}

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值