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,出错。

 

posted on 2012-08-28 15:32  hcu5555  阅读(1004)  评论(0编辑  收藏  举报