socket_淺談發送廣播包雜記

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

 

發佈了20 篇原創文章 · 獲贊 4 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章