一,使用socket发送广播包的方法:
1,sock = socket(AF_INET,SOCK_DGRAM,0);构建upd套接字
setsockopt(sock, SOL_SOCKET, SO_BROADCAST,(char*)&bbroadcast,sizeof(int));设置套接字属性,使之能发送广播包:4个255或
如192.168.136.255的包。也可以接受广播包,但是得保证广播包的端口号是自己绑定的端口号。
在windows下测试结果如上,而在Ubuntu下,有时候能发送4个255的广播包,有时候就只能发送192.168.136.255的包。原因不明。
注:Ubuntu下这样也可以发送4个255的广播包,前提是ip地址是自动获取或是手动填写的,而不能用ifconfig临时分配。
这样设置,也可以接受网络上的广播包,只能接受发往套接字bind的端口的广播包。
2,fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)),构建链路层套接字,就可直接发送广播包。
源码:1,
#include <netinet/in.h> // for sockaddr_in
#include <sys/types.h> // for socket
#include <sys/socket.h> // for socket
#include <stdio.h> // for printf
#include <stdlib.h> // for exit
#include <string.h> // for bzero
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define HELLO_WORLD_SERVER_PORT 6660
#define BUFFER_SIZE 1024
int main(int argc, char **argv)
{
int err = 0;
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
client_addr.sin_port = htons(0);
int client_socket = socket(AF_INET,SOCK_DGRAM,0);
if( client_socket < 0)
{
printf("Create Socket Failed!/n");
exit(1);
}
if(bind(client_socket, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1)
perror("bind");
int bbroadcast=1;
err=setsockopt(client_socket, SOL_SOCKET, SO_BROADCAST,(char*)&bbroadcast,sizeof(int));
if(err<0)
{
close(client_socket);
exit(1);
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
// server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
server_addr.sin_port = htons(6166);
// server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
strcpy(buffer, "hello world");
socklen_t n = sizeof(server_addr) ;
while(1)
sendto(client_socket,buffer,BUFFER_SIZE,0,(struct sockaddr*)&server_addr,n);
return 0;
}
源码2:
raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
{
int fd;
int result;
struct sockaddr_ll dest;
struct udp_dhcp_packet packet;
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
DEBUG(LOG_ERR, "socket call failed: %m");
return -1;
}
memset(&dest, 0, sizeof(dest));
memset(&packet, 0, sizeof(packet));
dest.sll_family = AF_PACKET;
dest.sll_protocol = htons(ETH_P_IP);
dest.sll_ifindex = ifindex;
dest.sll_halen = 6;
memcpy(dest.sll_addr, dest_arp, 6);
if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
DEBUG(LOG_ERR, "bind call failed: %m");
close(fd);
return -1;
}
packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = source_ip;
packet.ip.daddr = dest_ip;
packet.udp.source = htons(source_port);
packet.udp.dest = htons(dest_port);
packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
packet.ip.tot_len = packet.udp.len;
memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
packet.ip.ihl = sizeof(packet.ip) >> 2;
packet.ip.version = IPVERSION;
packet.ip.ttl = IPDEFTTL;
packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
if (result <= 0) {
DEBUG(LOG_ERR, "write on socket failed: %m");
}
close(fd);
return result;
}
3,fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)。
bind(fd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr))
这样可以接受广播包,只接收那些广播包的upd目的端口号是自己绑定的端口号的广播包。
前提是保证双方的ip地址在同一网段内。
二,原始数据包分析:
1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.
不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
不能:不能收到从本机发送出去的数据包.
发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部
这种套接字用来写个ping程序比较适合
注:不能接受ip地址是非同一网段的广播包。能接受同一网段的主机发送的广播包。
可定也能接受发往本机ip地址的单播包。
2. socket(PF_PACKET, SOCK_RAW, htons(x));
这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.
能: 接收发往本地mac的数据帧
能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
协议类型一共有四个
ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧,
接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct
sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll.
这种socket大小通吃,强悍
注:非混杂模式下,可以接收同一局域网内任意主机发的广播包,肯定也接受发往本机的单播包
混杂模式下,可以接收非发往本地的数据包。
三, int client_socket = socket(AF_INET,SOCK_DGRAM,0);
使用udp创建的套接字。也可以使用connect函数绑定一个远端的主机地址,这样就可以直接使用write/read了。
int client_socket = socket(AF_INET,SOCK_STREAM,0);
使用tcp创建的套接字,使用connect绑定远端主机地址时,如果远端主机的相应端口的套接字没有设置成listen状态的话,
会返回-1,出错。