原始套接字的使用

参考资料:1:steven的unix网络编程

2:http://maguangzhi.bokee.com/5834192.html  IPv4、TCP和UDP的校验和计算

3:http://blog.csdn.net/zfrong/archive/2008/12/31/3670863.aspx 使用rawsocket构造UDP数据包方法

4:http://blog.csdn.net/bmywindy/archive/2009/08/05/4412143.aspx Raw Socket(原始套接字)实现Sniffer(嗅探)

 

试验:用两台机器,一台发送,一台接收

接收端:开两个程序:1个udp包的接受,1个链路层上的接收(便于调试,有时自己构造的udp包发到了接收端,但由于自己构造的udp有些参数设置错了,导致在传输层上收不到)

 

发送端:

 

代码:

接收端:1传输层的接受代码:

 

#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#define SERV_PORT 8888
void main()
{
    char buf[5000];
    int err=-1;
    int n=0;
    struct sockaddr_in remoteaddr;
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(SERV_PORT);
    int addlength = sizeof(struct sockaddr_in);
    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    err=bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(err<0){
        perror("bind failed");
        close(sockfd);
        exit(-1);
    }
    while(1){
        n=recvfrom(sockfd,buf,5000,0,(struct sockaddr*)&remoteaddr,&addlength);
        if(n<0){
            perror("recvfrom failed");
            close(sockfd);
            continue;
        }
        printf("the receive data /n");
        n=sendto(sockfd,buf,n,0,(struct sockaddr*)&remoteaddr, addlength);
        if(n<0){
            perror("sendto failed");
            continue;
        }
    }

    close(sockfd);
}

 

链路层上的接收:

 

 

 

 

/***************SimpelSniffer.c*************/
//auther:duanjigang@2006s
#include <stdio.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#define BUFFER_MAX 2000

int main(int argc, char *argv[])
{
    int fd;

    int sock, n_read, proto;       
    char buffer[BUFFER_MAX];
    char  *ethhead, *iphead, *tcphead,
          *udphead, *icmphead, *p;

    if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
    {
        fprintf(stdout, "create socket error/n");
        exit(0);
    }

    while(1)
    {
        n_read = recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
        /*
           14   6(dest)+6(source)+2(type or length)
           +
           20   ip header
           +
           8   icmp,tcp or udp header
           = 42
         */
        if(n_read < 42)
        {
            fprintf(stdout, "Incomplete header, packet corrupt/n");
            continue;
        }

        ethhead = buffer;
        p = ethhead;
        int n = 0XFF;
        printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
                "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X/n",
                p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,
                p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);

        iphead = ethhead + 14;
        fd=open("receive",O_RDWR);
        if(fd < 0)
            perror("open file failed/n");
        else{
            write(fd,iphead,28);
        }
        close(fd);
        p = iphead + 12;

        printf("IP: %d.%d.%d.%d => %d.%d.%d.%d/n",
                p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,
                p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);
        proto = (iphead + 9)[0];
        p =iphead;
        int a;
        a=p[0]&0X0F;
        printf("the iplenth is %d/n",a);  //自己想在ip可选项方面做个小试验。
        p = iphead+a*4;
        printf("Protocol: ");
        switch(proto)
        {
            case IPPROTO_ICMP: printf("ICMP/n");break;
            case IPPROTO_IGMP: printf("IGMP/n");break;
            case IPPROTO_IPIP: printf("IPIP/n");break;
            case IPPROTO_TCP :
            case IPPROTO_UDP :
                               printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP");
                               printf("source port: %u,",(p[0]<<8)&0XFF00 |  p[1]&0XFF);
                               printf("dest port: %u/n", (p[2]<<8)&0XFF00 | p[3]&0XFF);
                               break;
            case IPPROTO_RAW : printf("RAW/n");break;
            default:printf("Unkown, please query in include/linux/in.h/n");
        }
    }
}

发送端代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>

#define iplength 20
#define BUFLEN 2000
#define CLIENTADDR "192.168.18.175"
#define PORT 8888
#define SERVADDR "192.168.18.174"
typedef struct ip_hdr{//ipv4头部
 unsigned int ip_length:4; /*little-endian*/
 unsigned int ip_version:4;
 unsigned char ip_tos;
 unsigned short ip_total_length;
 unsigned short ip_id;
 unsigned short ip_flags;
 unsigned char ip_ttl;
 unsigned char ip_protocol;
 unsigned short ip_cksum;
 unsigned int ip_source;
 unsigned int ip_dest;
// unsigned int LSH;
}ip_hdr;

typedef struct udp_hdr{//udp头部
 unsigned short s_port;
 unsigned short d_port;
 unsigned short length;
 unsigned short cksum;
}udp_hdr;

typedef struct psd_header{//伪头部,用于计算校验和

 unsigned int s_ip;//source ip

 unsigned int d_ip;//dest ip

 unsigned char mbz;//0

 unsigned char proto;//proto type

 unsigned short plen;//length

}psd_header;

void swap(unsigned int *a, unsigned int *b)//交换
{
 *a = (*a)^(*b);
 *b = (*a)^(*b);
 *a = (*a)^(*b);
}

unsigned short checksum(unsigned short* buffer, int size)//校验和
{
 unsigned long cksum = 0;
 while(size>1)
 {
  cksum += *buffer++;
  size -= sizeof(unsigned short);
 }
 if(size)
 {
  cksum += *(unsigned char*)buffer;
 }
 cksum = (cksum>>16) + (cksum&0xffff); //将高16bit与低16bit相加

 cksum += (cksum>>16); //将进位到高位的16bit与低16bit 再相加

 return (unsigned short)(~cksum);
}
int main(int argc, char *argv[])
{
 char buf[BUFLEN];
 int sockfd = -1;
 memset(buf,0,BUFLEN);
 struct sockaddr_in host_addr;
 if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP))<0)
 {
  printf("socket() error!/n");
  exit(1);
 }
 memset(&host_addr, 0, sizeof(host_addr));
 host_addr.sin_family = AF_INET;
 host_addr.sin_port = htons(PORT);
 host_addr.sin_addr.s_addr = inet_addr(SERVADDR);

 const int on = 1;
 if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))<0)
 {
  printf("setsockopt() error!/n");
  exit(0);   
 }

 int addr_len = sizeof(host_addr);

// while(1)
// {   
  ip_hdr *iphdr;
  iphdr = (ip_hdr*)buf;
  udp_hdr *udphdr;
  udphdr = (udp_hdr*)(iphdr+1);
  iphdr->ip_length = (sizeof(iphdr)!=24)?5:6;
  iphdr->ip_version = 4;
  iphdr->ip_tos = 0;
  iphdr->ip_total_length = htons(sizeof(buf));
  iphdr->ip_id = 0;
  iphdr->ip_flags = 0;
  iphdr->ip_ttl = 0x40;
  iphdr->ip_protocol = 0x11;
  iphdr->ip_cksum = 0;
  iphdr->ip_source = inet_addr(CLIENTADDR);//源地址

  iphdr->ip_dest = inet_addr(SERVADDR);//目的地址
  iphdr->ip_cksum = checksum((unsigned short*)buf, 20);
  udphdr->s_port = htons(5000);//源端口
  udphdr->d_port = htons(PORT);//目的端口
  udphdr->length = htons(sizeof(buf)-iplength);
  udphdr->cksum = 0;
  psd_header psd;
  psd.s_ip = iphdr->ip_source;
  psd.d_ip = iphdr->ip_dest;
  psd.mbz = 0;
  psd.proto = 0x11;
  psd.plen = udphdr->length;
  char tmp[sizeof(psd)+ntohs(udphdr->length)];
  memcpy(tmp, &psd, sizeof(psd));
  memcpy(tmp+sizeof(psd), buf+iplength, sizeof(buf)-iplength);
  udphdr->cksum = checksum((unsigned short*)tmp, sizeof(tmp));
  int res =sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&host_addr, sizeof(host_addr));
  if(res == -1)
   perror("sendto failed");

  sleep(1);
// }
 return 0;

}
 

 

 

 注意:用rawsocket发送数据,在以太网上最多只能发1500byte,多了就报错,我猜底层应该不会再继续进行ip分片了,


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值