一,使用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,出錯。