SADP主要使用的是链路层多播及UDP多播的原理进行实现的。
1.链路层多播
socket(PF_PACKET, SOCK_RAW, htons(0x8033))
PF_PACKET接口可以操作链路层的数据
使用SOCK_RAW发送的数据必须包含链路层的协议头,接受得到的数据包,包含链路层协议头。而使用 SOCK_DGRAM则都不含链路层的协议头。
0x8033指的是socket以广播协议发送
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)
第一个参数socket是套接字描述符。 第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为SOL_SOCKET。 第三个参数 option_name指定准备设置的选项,option_name可以有哪些常用取值,这取决于level,以linux 2.6内核为例(在不同的平台上,这种关系可能会有不同),在套接字级别上(SOL_SOCKET): SO_ATTTACH_FILTER选项发送到内核,并且你的filter代码能通过内核的检查,这样你就可以立即过滤socket上面的数据了
Filter结构介绍 其中 *filter 指向结构为 struct sock_filter 的 BPF过滤代码(code)。
struct sock_fprog {
unsigned short len;
struct sock_filter _user *filter;
};
这样的结构被组装成一个4元数组,包含:code、jt、jf和K值。jt和jf是跳转偏移量,k是一个通用值提供给code使用。
struct sock_filter {
__u16 code;
__u8 jt;
__u8 jf;
__u32 k;
};
struct sock_filter BPF_code[]= {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00008033 },
{ 0x6, 0, 0, 0x00000200 },
{ 0x6, 0, 0, 0x00000000 }
};
BPF(Berkeley Packet Filter)伯克利包过滤器。 BPF允许用户空间程序将一个过滤(filter)附加到任何的套接字(socket)上面用来允许或不允许某些类型的数据通过socket
tcpdump 是linux 中调试网络的一个工具, 实际上 tcpdump 就是用利用BPF原理编写的一个工具,所以tcpdump 提供了一个生成 bpf code 的选项 -dd 。 使用 tcpdump 命令加上 -dd 选项来生成 bpf code,命令格式如下: tcpdump -dd -i eth0 port 22 注意:用tcpdump生成的BPF代码只能用于SOCK_RAW的socket,这类socket是可以直接操作数据链路层的。如果你打算将BPF用于ip层等较高层次的socket,那么你需要手工修改部分行的code.k,也就是修改如ldh [12]当中的[12]这个数值,因为这个数值的偏移量是按照从链路层开始计算得到的,在没有链路层之后,这个值就发生了变化,这个是需要注意的。 实现了让所有IPv4/IPv6 port 22的包通过,这个socket上所有其他的包将会被丢弃 tcpdump -dd -i eth0 port 22
链路层多播的发包实现方式 原理是利用AF_PACKET 套接字发送一个任意的以太网帧
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_ifindex = get_iface_index(p->fd, (const int8_t *)p->device[ifarr_idx]);
sa.sll_protocol = htons(ETH_P_ALL);
sendto(p->fd, packet, len, 0, (struct sockaddr *)&sa, sizeof(sa));
数据链路层的头信息通常定义在 sockaddr_ll 的结构体中,protocol是按照网络字节顺序(network byte order),大部分定义在头文件中,设置协议时,例如 htons(ETH_P_ALL)来接收所有的数据包;
UDP多播
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
AF_INET:监听udp协议的239.255.255.250,端口37020的报文 SOCK_DGRAM:socket设置为UDP数据包 IPPROTO_UDP :socket走 UDP协议进行数据收发 局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址, 路由器并不转发属于此范围的IP包。
预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网 络协议。
管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私 有IP地址,不能用于Internet,可限制多播范围
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr, sizeof(so_reuseaddr)
SO_REUSEADDR套接字选项能起到以下4个不同的功用。 (1)SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作他们的本地端口的连接仍存在。这个条件通常是这样碰到的:
(a)启动一个监听服务器; (b)连接请求到达,派生一个子进程来处理这个客户; (c)监听服务器终止,但子进程继续为现有连接上的客户提供服务; (d)重启监听服务器。
默认情况下,当监听服务器在步骤d通过调用socket,bind和listen重新启动时,由于他试图捆绑一个现有连接(即正由早先派生的那个子进程处理着的连接)上的端口,从而bind调用会失败。但是如果该服务器在socket和bind两个调用之间设置了SO_REUSEADDR套接字选项,那么将成功。所有TCP服务器都应该指定本套接字选项,以允许服务器在这种情况下被重新启动。
(2)SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们绝对不可能启动捆绑相同IP地址和相同端口号的多个服务器:这是完全重复的捆绑,即使我们给第二个服务器设置了SO_REUSEADDR套接字也不管用。
(3)SO_REUSEADDR 允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。
(4)SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口号已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说本特性仅支持UDP套接字。
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))
setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop))
setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))
setsockopt(iSockFd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface))
1.选项IP_MULTICASE_TTL 选项IP_MULTICAST_TTL允许设置超时TTL,范围为0~255之间的任何值,例如:
unsigned char ttl=255; setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
2.选项IP_MULTICAST_LOOP IP_MULTICAST_LOOP 应用到接收端.在接收端启用IP_MULTICAST_LOOP. loop设置为1,表示接收自身发送出去的数据,设置为0表示不接收
3.选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP 加入或者退出一个组播组,通过选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBER- SHIP,对一个结构struct ip_mreq类型的变量进行控制,struct ip_mreq原型如下:
struct ip_mreq
{
struct in_addr imn_multiaddr;
struct in_addr imr_interface;
};
选项IP_ADD_MEMBERSHIP用于加入某个广播组,之后就可以向这个广播组发送数据或者从广播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的广播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。例如:
struct ip_mreq mreq;
setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
使用IP_ADD_MEMBERSHIP选项每次只能加入一个网络接口的IP地址到多播组,但并不是一个多播组仅允许一个主机IP地址加入,可以多次调用IP_ADD_MEMBERSHIP选项来实现多个IP地址加入同一个广播组,或者同一个IP地址加入多个广播组。当imr_ interface为INADDR_ANY时,选择的是默认组播接口。 4.选项SO_BINDTODEVICE 当套接口被绑定到指定的网络设备接口之后,只有来自该设备的数据包才会被套接口处理
如果有多个接口,例如eth0, eth1, ethx…,就可以在创建套接字的时候绑定相应的接口发送数据,网关端目前使用的eth2
int c;
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(pClientAddr);
sa.sin_port = htons(port);
c = sendto(p->fd, packet, len, 0, (struct sockaddr *)&sa, sizeof(sa));
|