最近在实际的项目中遇到了组播通信,其中最主要的问题是组播如何跨网络路由的问题,起初在网络找资料,找到了PIMD来实现组播数包的转发。
借助pimd也能实现组包数据包的转发。但是,pimd是个应用程序,如果组播数据平凡的话,会影响到系统的效率。同时考虑到实际的项目中,组播数据源和组播的接受者是
在同一个局域网之内,只要交换机支持组播,通过交换机将组播的发送者和接受者连接起来,之后通过更改驱动程序直接实现数据包的转发,即eth0->eth1或者eth1->eth0,。eth0和eth1不在同一个网段。
数据交互框图如下:
单播数据处理如下:
B中的DSP只发送单播给A中的DSP,单播数据的转发可以根据iptable规则来实现。
A上面的单播数据转发配置如下:
WAN IP:10.10.104.221
LAN IP:192.168.119.7
DSP IP:192.168.119.3
iptables -t nat -A PREROUTING -d 10.10.104.221 -p udp --dport 5001 -j DNAT --to 192.168.119.3
iptable规则表示目前地址为:10.10.104.221 时,将此数据包的目的地址更改为:192.168.119.3。即数据转发给A中的DSP。
B上面的单播数据转发配置如下:
WAN IP:10.10.104.219 LAN IP:192.168.197.1 DSP IP:192.168.197.244 iptables -t nat -A PREROUTING -d 192.168.197.1 -p udp --dport 5001 -j DNAT --to 10.10.104.221 iptables -t nat -A POSTROUTING -o eth0 -s 192.168.197.244 -p udp --dport 5001 -j SNAT --to 10.10.104.219 insmod snull.ko ifconfig virt_net0 up brctl addif br0 virt_net0 echo 192.168.197.1 > /proc/LanIpAddr cat /proc/LanIpAddr 上面的命令中,俩条iptables命令意思是,第一条:当B中的DSP发送单播数据时如果目的地址为:192.168.197.1就将目的地址改为:10.10.104.221,即表示发送给:A的eth0接口。第二条:当从eth0出去的数据包如果源地址为:192.168.197.244(即DSP的ip地址),将源地址改为B的eth0的IP地址。即首先做DNAT转换,紧接着做SNAT转化。 ############################################################################
组播数据的处理如下:
组播数据:
数据流向是A中DSP是组播的数据源,B中的DSP是组播的接受者。A中的DSP和eth1是同一个网段,A中DSP的组播数据首先发送eth1,之后再转发到eth0,之后数据通过网络发送给B设备的eth0接口。同理当组播数据到达B设备的eth0接口之后,通过驱动程序将数据转发到eth1接口,eth1和DSP为通一个网段(192.168.100.xx)。
现在对上面的代码进行说明:
应该在linux ethernet驱动程序中,每一个ethN(eth0,eth1....)对应驱动中的一个mac_unit。对于eth0来时mac_unit-=0,对于eth1来说mac_unit=1.故可在驱动的接受函数中判断:一个特定的数据包是来自那个mac_unit或者在特定的mac_unit中处理特定的数据包。此处是:在mac_unit=0上面即eth0上面对特定的组播数据进行处理,首先判断:if((mac->mac_unit == 0)&&(0==memcmp(dest_mac, MUTICAST_MAC, ETH_ALEN))&&(iph->daddr == *(unsigned int *)MUTICAST_DEST_IP))
即目的IP为:224.1.2.3 ,目的MAC地址为:0100 5e01 0203 。当收到的数据包满足此条件时可以知道此数据包为:组播数据。将此数据包的目的地址更改为:单播目的地址:
memcpy(ethh->h_dest,DSP_VX_MAC, ETH_ALEN); /* 为:192.168.197.244 对于的MAC地址*/
iph->daddr = *(unsigned int *)DSP_VX_IP; /* 192.168.197.244 */
之后:
if(virt_dev != NULL))
{
is_muticast_package_flag =0;
skb->dev =virt_dev;
retskb =netif_receive_skb(skb);
}
通过虚拟接口将skb的dev更改为:virt_dev。此后数据包就会通过协议栈进行处理。同时有下面的桥接。
insmod snull.ko //为虚拟网卡,虚拟出来virt_net0设备。
ifconfig virt_net0 up
brctl addif br0 virt_net0
有了上面的处理,B设备中的DSP:192.168.197.244 就会收到一个组播数据(此时只是,目的地址为单播的目的地址,数据内容没有变化)
其代码类似如下:
#if DP_VERSION //DP
//* muticast IP */ //30:22:7B:CA:CC:DB
struct net_device* virt_dev;
EXPORT_SYMBOL(virt_dev);
#define LAN_DSP_UDP_PORT 6735
#define LAN_LOCAL_PORT 2849
static int muticast_dest_ipaddr = 0xE0010203; /* 224.1.2.3 */
static int LAN_ipaddr = 0xC0A8C541; /* 192.168.197.7 */
static int DSP_VX_ipaddr = 0xC0A8C5F4; /* 192.1689.197.244 */
static int CP_ipaddr = 0x0; /* 0.0.0.0 */
static unsigned char *LAN_IP = (unsigned char *)&LAN_ipaddr;/* 192.168.197.7 */
static unsigned char *DSP_VX_IP = (unsigned char *)&DSP_VX_ipaddr;/* 192.1689.197.244 */
static unsigned char *CP_SRC_IP = (unsigned char *)&CP_ipaddr;/* CP端,及组播源的IP地址 */
static unsigned char *MUTICAST_DEST_IP = (unsigned char *)&muticast_dest_ipaddr; /* 224.1.2.3 */
static unsigned char MUTICAST_MAC[ETH_ALEN] = {0x01,0x00,0x5e,0x01,0x02,0x03}; /* 0100 5e01 0203 */
static unsigned char LAN_MAC[ETH_ALEN] = {0x30,0x22,0x7B,0xCA,0xCC,0xDB }; /*HWaddr 00:27:1D:10:00:00 */
static unsigned char DSP_VX_MAC[ETH_ALEN] = {0xAA,0xBB,0xCC,0x01,0x19,0x1C }; /*aa:bb:cc:01:19:1c */
static unsigned char TEMP_MAC[ETH_ALEN] = {0x00,0x03,0x7F,0xFF,0xFF,0xFF }; /* 00:03:7F:FF:FF:FF */
static unsigned char CP_SRC_MAC[ETH_ALEN] = {0xAA,0xBB,0xCC,0x01,0x19,0x1C }; /* CP端,及组播源的MAC地址 */
static int Lan_Ipaddr_read (char *page, char **start, off_t off,int count, int *eof, void *data)
{
count = sprintf(page, "%d.%d.%d.%d\n", \
(LAN_ipaddr>>24)&0x000000ff,(LAN_ipaddr>>16)&0x000000ff,\
(LAN_ipaddr>>8)&0x000000ff,(LAN_ipaddr)&0x000000ff);
count = sprintf(page, "%s%02x:%02x:%02x:%02x:%02x:%02x\n", page,\
LAN_MAC[0],LAN_MAC[1],LAN_MAC[2],LAN_MAC[3],LAN_MAC[4],LAN_MAC[5]);
*eof = 1; /* Indicate completion */
return count;
}
static int dp_muticastsrouce_Ipaddr_read (char *page, char **start, off_t off,int count, int *eof, void *data)
{
count = sprintf(page,"cp_wan_ip\ncp_wan_ip=%d.%d.%d.%d\n", \
(CP_ipaddr>>24)&0x000000ff,(CP_ipaddr>>16)&0x000000ff,\
(CP_ipaddr>>8)&0x000000ff,(CP_ipaddr)&0x000000ff);
count = sprintf(page, "%s%02x:%02x:%02x:%02x:%02x:%02x\n", page,\
CP_SRC_MAC[0],CP_SRC_MAC[1],CP_SRC_MAC[2],CP_SRC_MAC[3],CP_SRC_MAC[4],CP_SRC_MAC[5]);
*eof = 1; /* Indicate completion */
return count;
}
static int Lan_Ipaddr_write (struct file *file, const char *buf,unsigned long count, void *data)
{
char *end;
unsigned int u32temp,u32temp1;
u32temp = (int)simple_strtoul(buf,&end,10);
u32temp1 = (u32temp&0x000000ff) <<24;
if(*end!='.') return 0;
buf = end + 1;
u32temp = (int)simple_strtoul(buf,&end,10);
u32temp1 += (u32temp&0x000000ff) <<16;
if(*end!='.') return 0;
buf = end + 1;
u32temp = (int)simple_strtoul(buf,&end,10);
u32temp1 += (u32temp&0x000000ff) <<8;
if(*end!='.') return 0;
buf = end + 1;
u32temp = (int)simple_strtoul(buf,&end,10);
u32temp1 += (u32temp&0x000000ff);
LAN_ipaddr = u32temp1;
return count;
}
void init_dp_ip_addr()
{
static struct proc_dir_entry * dp_lan_proc_entry;
static struct proc_dir_entry * dpside_cp_ip_proc_entry;
mode_t mode =0444;
dp_lan_proc_entry = create_proc_entry("dp_lanIpAddr",mode, NULL);
dp_lan_proc_entry->read_proc = Lan_Ipaddr_read;
dp_lan_proc_entry->write_proc = Lan_Ipaddr_write;
dpside_cp_ip_proc_entry = create_proc_entry("cp_wan_ip",mode, NULL);
dpside_cp_ip_proc_entry->read_proc = dp_muticastsrouce_Ipaddr_read;
}
#endif
在函数int athr_gmac_hard_start(struct sk_buff *skb, struct net_device *dev)
{//中自动学习MAC地址
#if DP_VERSION
{
/* 修改目的,源MAC地址*/
struct ethhdr *ethh;
struct iphdr * iph;
unsigned char *dest_mac;
ethh =(struct ethhdr*)skb->data;
iph = (struct iphdr *)(skb->data + ETH_HLEN);
dest_mac = ethh->h_dest;
if(mac->mac_unit == 1)
{
if((ethh->h_proto == htons(ETH_P_ARP))||(ethh->h_proto == htons(ETH_P_RARP)))
{
// printk("arp ,rap .....\n")
if(0 == memcmp(skb->data + 28,LAN_IP, 4))
{
if(0 != memcmp(skb->data + 22,LAN_MAC, 6))
{
memcpy(LAN_MAC, skb->data + 22, ETH_ALEN);
printk(" LAN_MAC MAC changed by ARP, the new MAC is: %02X:%02X:%02X:%02X:%02X:%02X\n", \
LAN_MAC[0],LAN_MAC[1],LAN_MAC[2],LAN_MAC[3],LAN_MAC[4],LAN_MAC[5]);
}
}
}
}
}
#endif
}
在函数有线口驱动接受函数
athr_receive_pkt()
{
#if DP_VERSION
{
/* 修改目的,源MAC地址*/
struct ethhdr *ethh;
struct iphdr * iph;
struct udp_hdr *udph;
unsigned char *dest_mac,*src_mac;
ethh =(struct ethhdr*)skb->data;
iph = (struct iphdr *)(skb->data + ETH_HLEN);
dest_mac = ethh->h_dest;
src_mac = ethh->h_source;
if(virt_dev != NULL)
{
if((mac->mac_unit == 0)&&(0==memcmp(dest_mac, MUTICAST_MAC, ETH_ALEN))&&(iph->daddr == *(unsigned int *)MUTICAST_DEST_IP))
{
//printk("**********muticast data package**************************************************\n");
memcpy(CP_SRC_MAC,ethh->h_source, ETH_ALEN);
*(unsigned int *)CP_SRC_IP =iph->saddr;
memcpy(ethh->h_dest,DSP_VX_MAC, ETH_ALEN);
iph->daddr = *(unsigned int *)DSP_VX_IP;
ip_send_check(iph);
udph = (struct udp_hdr *)(skb->data + ETH_HLEN + 20);
udph->check = 0;
is_muticast_package_flag =1;
}
}
if((mac->mac_unit == 1)&&(0==memcmp(src_mac, DSP_VX_MAC, ETH_ALEN))&&(iph->saddr == *(unsigned int *)DSP_VX_IP))
{
memcpy(ethh->h_dest,LAN_MAC, ETH_ALEN);
iph->daddr = *(unsigned int *)LAN_IP;
ip_send_check(iph);
udph = (struct udp_hdr *)(skb->data + ETH_HLEN + 20); //udp 8 bytes
udph->check = 0;
}
}
#endif
{
#if DP_VERSION
if((mac->mac_unit == 0)&&(is_muticast_package_flag ==1)&&(virt_dev != NULL))
{
// print_ip_buffer1(skb, (char *)"umtical", __LINE__);
// printk("----------------------------------------------------------------------\n");
is_muticast_package_flag =0;
skb->dev =virt_dev;
retskb =netif_receive_skb(skb);
}
else
{
retskb =netif_receive_skb(skb);
}
#endif
}
------------------------------------------------------------------------
#if CP_7240_ON_OFF_VERSION2
if((mac->mac_unit == 1)&&(0==memcmp(dest_mac, MUTICAST_MAC, ETH_ALEN))&&(iph->daddr == *(unsigned int *)MUTICAST_DEST_IP))
//if((mac->mac_unit == 1)&&(is_multicast_ether_addr(dest_mac)))
{
//athr_gmacs[i]->mac_dev
skb->dev =athr_gmacs[0]->mac_dev;
printk("----------------------------------------------------------------------\n");
memcpy(ethh->h_source, WAN_MAC, ETH_ALEN);
iph->saddr = *(unsigned int *)WAN_IP_ADDR;
ip_send_check(iph);
udph = (struct udp_hdr *)(skb->data + ETH_HLEN + 20);
udph->check = 0;
if(athr_gmacs[0]->mac_ifup) //athr_gmac_hard_start(struct sk_buff *skb, struct net_device *dev)
{
athr_gmacs[0]->mac_dev->netdev_ops->ndo_start_xmit(skb, athr_gmacs[0]->mac_dev);
}
}
else
{
netif_receive_skb(skb); //这个地方注意。
}
#else
{
netif_receive_skb(skb); //这个地方注意。
}
#endif