• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            不會飛的鳥

            2010年12月10日 ... 不鳥他們!!! 我要用自己開發的分布式文件系統、分布式調度系統、分布式檢索系統, 做自己的搜索引擎!!!大魚有大志!!! ---楊書童

            linux sock_raw原始套接字編程

            sock_raw原始套接字編程可以接收到本機網卡上的數據幀或者數據包,對與監聽網絡的流量和分析是很有作用的.一共可以有3種方式創建這種socket
             
            1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)發送接收ip數據包
            2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))發送接收以太網數據幀
            3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))過時了,不要用啊
             
            理解一下SOCK_RAW的原理, 比如網卡收到了一個 14+20+8+100+4 的udp的以太網數據幀.
             
            首先,網卡對該數據幀進行硬過濾(根據網卡的模式不同會有不同的動作,如果設置了promisc混雜模式的話,則不做任何過濾直接交給下一層輸入例程,否則非本機mac或者廣播mac會被直接丟棄).按照上面的例子,如果成功的話,會進入ip輸入例程.但是在進入ip輸入例程之前,系統會檢查系統中是否有通過socket(AF_PACKET, SOCK_RAW, ..)創建的套接字.如果有的話并且協議相符,在這個例子中就是需要ETH_P_IP或者ETH_P_ALL類型.系統就給每個這樣的socket接收緩沖區發送一個數據幀拷貝.然后進入下一步.
             
            其次,進入了ip輸入例程(ip層會對該數據包進行軟過濾,就是檢查校驗或者丟棄非本機ip或者廣播ip的數據包等,具體要參考源代碼),例子中就是如果成功的話會進入udp輸入例程.但是在交給udp輸入例程之前,系統會檢查系統中是否有通過socket(AF_INET, SOCK_RAW, ..)創建的套接字.如果有的話并且協議相符,在這個例子中就是需要IPPROTO_UDP類型.系統就給每個這樣的socket接收緩沖區發送一個數據幀拷貝.然后進入下一步.
             
            最后,進入udp輸入例程 ...
             
            ps:如果校驗和出錯的話,內核會直接丟棄該數據包的.而不會拷貝給sock_raw的套接字,因為校驗和都出錯了,數據肯定有問題的包括所有信息都沒有意義了.
             
            進一步分析他們的能力.
            1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
            能:該套接字可以接收協議類型為(tcp udp icmp等)發往本機的ip數據包,從上面看的就是20+8+100.
            不能:不能收到非發往本地ip的數據包(ip軟過濾會丟棄這些不是發往本機ip的數據包).
            不能:不能收到從本機發送出去的數據包.
            發送的話需要自己組織tcp udp icmp等頭部.可以setsockopt來自己包裝ip頭部
            這種套接字用來寫個ping程序比較適合
                 
            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_ARP 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 sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
                struct sockaddr_ll sll;
                memset( &sll, 0, sizeof(sll) );
                sll.sll_family = AF_PACKET;
                struct ifreq ifstruct;
                strcpy(ifstruct.ifr_name, "eth0");
                ioctl(sockfd, SIOCGIFINDEX, &ifstruct);
                sll.sll_ifindex = ifstruct.ifr_ifindex;
                sll.sll_protocol = htons(ETH_P_ALL);
                if(bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1 ) {
                   perror("bind()");
                ...

             

            int set_promisc(char *interface, int fd) {
                    struct ifreq ifr;
                    strcpy(ifr.ifr_name, interface);
                    if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                            perror("iotcl()");
                            return -1;
                    }
                    ifr.ifr_flags |= IFF_PROMISC;
                    if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                            perror("iotcl()");
                            return -1;
                    }
                    return 0;
            }
             
            int unset_promisc(char *interface, int fd) {
                    struct ifreq ifr;
                    strcpy(ifr.ifr_name, interface);
                    if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                            perror("iotcl()");
                            return -1;
                    }
                    ifr.ifr_flags &= ~IFF_PROMISC;
                    if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                            perror("iotcl()");
                            return -1;
                    }
                    return 0;
            }

             

            3. socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))這個最好不要用,反正我不用...
             
            總結使用方法: 1.只想收到發往本機某種協議的ip數據包的話用第一種就足夠了
                        2. 更多的詳細的內容請使用第二種.包括ETH_P_ALL參數和混雜模式都可以使它的能力不斷的加強.
            ps:很多自己的想法.虛擬機測試環境.有錯歡迎指出交流
            qq:110024218
             
             
            我寫的ping
             

            #include "stdio.h"
            #include "stdlib.h"
            #include "string.h"
             
            #include "unistd.h"
            #include "sys/types.h"
            #include "sys/socket.h"
            #include "netinet/in.h"
            #include "netinet/ip.h"
            #include "netinet/ip_icmp.h"
            #include "netdb.h"
            #include "errno.h"
            #include "arpa/inet.h"
            #include "signal.h"
            #include "sys/time.h"
             
            extern int errno;
             
            int sockfd;
            struct sockaddr_in addr; //peer addr
            char straddr[128]; //peer addr ip(char*)
            char sendbuf[2048];
            char recvbuf[2048];
            int sendnum;
            int recvnum;
            int datalen = 30;
             
             
            unsigned short my_cksum(unsigned short *data, int len) {
                    int result = 0;
                    for(int i=0; i<len/2; i++) {
                            result += *data;
                            data++;
                    }
                    while(result >> 16)result = (result&0xffff) + (result>>16);
                    return ~result;
            }
            void tv_sub(struct timeval* recvtime, const struct timeval* sendtime) {
                    int sec = recvtime->tv_sec - sendtime->tv_sec;
                    int usec = recvtime->tv_usec - sendtime->tv_usec;
                    if(usec >= 0) {
                            recvtime->tv_sec = sec;
                            recvtime->tv_usec = usec;
                    } else {
                            recvtime->tv_sec = sec-1;
                            recvtime->tv_usec = -usec;
                    }
            }
             
            void send_icmp() {
                    struct icmp* icmp = (struct icmp*)sendbuf;
                    icmp->icmp_type = ICMP_ECHO;
                    icmp->icmp_code = 0;
                    icmp->icmp_cksum = 0;
                    icmp->icmp_id = getpid(); //needn't use htons() call, because peer networking kernel didn't handle this data and won't make different meanings(bigdian litteldian)
                    icmp->icmp_seq = ++sendnum; //needn'
            t use hotns() call too.
                    gettimeofday((struct timeval*)icmp->icmp_data, NULL);
                    int len = 8+datalen;
                    icmp->icmp_cksum = my_cksum((unsigned short*)icmp, len);
                    int retval = sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&addr, sizeof(addr));
                    if(retval == -1){
                            perror("sendto()");
                            exit(-1);
                    } else {
            // printf("send icmp request to %s(%d) bytes\n", straddr, len);
                    }
            }
            void recv_icmp() {
                    struct timeval *sendtime;
                    struct timeval recvtime;
             
                    for(;;) {
                            int n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, 0, 0);
                            if(n == -1) {
                                    if(errno == EINTR)continue;
                                    else {
                                            perror("recvfrom()");
                                            exit(-1);
                                    }
                            } else {
                                    gettimeofday(&recvtime, NULL);
                                    struct ip *ip = (struct ip*)recvbuf;
                                    if(ip->ip_src.s_addr != addr.sin_addr.s_addr) {
            // printf("ip_src is not : %s\n", straddr);
                                            continue;
                                    }
                                    struct icmp *icmp = (struct icmp*)(recvbuf + ((ip->ip_hl)<<2));
                                    if(icmp->icmp_id != getpid()) {
            // printf("icmp_id is not :%d\n", getpid());
                                            continue;
                                    }
                                    recvnum++;
                                    sendtime = (struct timeval*)icmp->icmp_data;
                                    tv_sub(&recvtime, sendtime);
                                    printf("imcp echo from %s(%dbytes)\tttl=%d\tseq=%d\ttime=%d.%06d s\n", straddr, n, ip->ip_ttl, icmp->icmp_seq, recvtime.tv_sec, recvtime.tv_usec);
                            }
                    }
            }
            void catch_sigalrm(int signum) {
                    send_icmp();
                    alarm(1);
            }
            void catch_sigint(int signum) {
                    printf("\nPing statics:send %d packets, recv %d packets, %d%% lost...\n", sendnum, recvnum, (int)((float)(sendnum-recvnum)/sendnum)*100);
                    exit(0);
            }
             
            int main(int argc, char **argv) {
                    if(argc != 2) {
                            printf("please use format: ping hostname\n");
                            exit(-1);
                    }
             
                    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
                    if(sockfd == -1) {
                            perror("socket()");
                            return -1;
                    }
             
            /*
                    int sendbufsize = 180;
                    socklen_t sendbufsizelen = sizeof(sendbufsize);
                    if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sendbufsize, sendbufsizelen) == -1)perror("setsockopt()");
                    int recvbufsize;
                    socklen_t recvbufsizelen;
                    if(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbufsize, &recvbufsizelen) == -1)perror("getsockopt()");
            */
             
                    memset(&addr, 0, sizeof(addr));
                    addr.sin_family = AF_INET;
                    int retval = inet_pton(AF_INET, argv[1], &addr.sin_addr);
                    if(retval == -1 || retval == 0) {
                            struct hostent* host = gethostbyname(argv[1]);
                            if(host == NULL) {
                                    fprintf(stderr, "gethostbyname(%s):%s\n", argv[1], strerror(errno));
                                    exit(-1);
                            }
            /*
                            if(host->h_name != NULL)printf("hostent.h_name:%s\n", host->h_name);
                            if(host->h_aliases != NULL && *(host->h_aliases) != NULL)printf("hostent.h_aliases:%s\n", *(host->h_aliases));
                            printf("hostent.h_addrtype:%d\n", host->h_addrtype);
                            printf("hostent.h_length:%d\n", host->h_length);
            */
                            if(host->h_addr_list != NULL && *(host->h_addr_list) != NULL) {
                                    strncpy((char*)&addr.sin_addr, *(host->h_addr_list), 4);
                                    inet_ntop(AF_INET, *(host->h_addr_list), straddr, sizeof(straddr));
                            }
                            printf("Ping address:%s(%s)\n\n", host->h_name, straddr);
                    } else {
                            strcpy(straddr, argv[1]);
                            printf("Ping address:%s(%s)\n\n", straddr, straddr);
                    }
             
                    struct sigaction sa1;
                    memset(&sa1, 0, sizeof(sa1));
                    sa1.sa_handler = catch_sigalrm;
                    sigemptyset(&sa1.sa_mask);
                    sa1.sa_flags = 0;
                    if(sigaction(SIGALRM, &sa1, NULL) == -1)perror("sigaction()");
                    struct sigaction sa2;
                    memset(&sa2, 0, sizeof(sa2));
                    sa2.sa_handler = catch_sigint;
                    sigemptyset(&sa2.sa_mask);
                    sa2.sa_flags = 0;
                    if(sigaction(SIGINT, &sa2, NULL) == -1)perror("sigaction()");
             
                    alarm(1);
                    recv_icmp();
             
                    return 0;
            }

            posted on 2011-06-27 09:16 不會飛的鳥 閱讀(675) 評論(0)  編輯 收藏 引用

            少妇久久久久久被弄到高潮| 97久久天天综合色天天综合色hd| 午夜不卡888久久| 精品国产热久久久福利| 成人综合久久精品色婷婷| 久久综合九色综合网站| 久久国产视屏| 久久综合给久久狠狠97色| 久久国产午夜精品一区二区三区| 久久久久亚洲AV成人网人人网站| 久久国产精品国产自线拍免费| 色天使久久综合网天天| 久久精品国产精品国产精品污 | 人妻无码αv中文字幕久久琪琪布| 久久最新精品国产| 99久久做夜夜爱天天做精品| 久久国产精品国产自线拍免费| 2019久久久高清456| 麻豆国内精品久久久久久| 久久青青草原综合伊人| 久久人人爽人人爽人人片AV不| 四虎国产精品成人免费久久| 青青青国产成人久久111网站| 欧美丰满熟妇BBB久久久| 国产精品久久久久久久人人看 | 日韩精品久久久久久久电影蜜臀 | 99久久免费国产特黄| 国内精品人妻无码久久久影院导航 | 久久久久亚洲精品日久生情 | 99久久99久久精品免费看蜜桃| 久久这里的只有是精品23| 久久久久女教师免费一区| 精品久久久久中文字幕一区| 99久久精品免费看国产免费| 日本福利片国产午夜久久| 国内精品久久久久久99蜜桃| 99精品久久精品一区二区| 浪潮AV色综合久久天堂| 久久精品无码一区二区无码| 国内精品久久人妻互换 | 久久亚洲精品无码aⅴ大香|