如何收听IGMPv3帧

时间:2016-05-16 15:22:17

标签: c linux sockets igmp

我需要为此获取IGMPv3帧我正在使用如下的套接字:

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);

问题是我的程序是filtring IGMPv3 Frames,我不知道为什么!!我没有得到IGMP帧,虽然我在使用wireshark时得到它们。我也尝试使用:

sockfd = socket(PF_PACKET, SOCK_RAW, htons(0x0800));

但我只能获得ICMP帧,而不能获得IGMP帧。 PS:我在另一台机器上尝试了我的程序并且它工作正常,所以我认为问题在于我的内核,是否有人知道是否有任何与套接字有关的配置?

以下是整个代码:

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>

#define DEST_MAC0   0x01
#define DEST_MAC1   0x00
#define DEST_MAC2   0x5e
#define DEST_MAC3   0x00
#define DEST_MAC4   0x00
#define DEST_MAC5   0x16

#define ETHER_TYPE  0x0800

#define DEFAULT_IF  "eth1"
#define BUF_SIZ     1024

int main(int argc, char *argv[])
{
    char sender[INET6_ADDRSTRLEN];
    int sockfd, ret, i, counter;
    int sockopt;
    ssize_t numbytes;
    struct ifreq ifopts;    /* set promiscuous mode */
    struct ifreq if_ip; /* get ip addr */
    struct sockaddr_storage their_addr;
    uint8_t buf[BUF_SIZ];
    char ifName[IFNAMSIZ];

    /* Get interface name */
    if (argc > 1)
        strcpy(ifName, argv[1]);
    else
        strcpy(ifName, DEFAULT_IF);

    /* Header structures */
    struct ether_header *eh = (struct ether_header *) buf;
    struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
    struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct iphdr) + sizeof(struct ether_header));

    memset(&if_ip, 0, sizeof(struct ifreq));

    /* Open PF_PACKET socket, listening for EtherType ETHER_TYPE */
//  if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1) {
//      perror("listener: socket");
//      return -1;
//  }
    sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE));
    if (sockfd == -1) {
        perror("listener: socket");
        return -1;
    }

    /* Set interface to promiscuous mode - do we need to do this every time? */
    strncpy(ifopts.ifr_name, ifName, IFNAMSIZ-1);
    ioctl(sockfd, SIOCGIFFLAGS, &ifopts);
    ifopts.ifr_flags |= IFF_PROMISC;
    ioctl(sockfd, SIOCSIFFLAGS, &ifopts);
    /* Allow the socket to be reused - incase connection is closed prematurely */
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) {
        perror("setsockopt");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    /* Bind to device */
    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ-1) == -1)  {
        perror("SO_BINDTODEVICE");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

repeat: printf("listener: Waiting to recvfrom...\n");
    numbytes = recvfrom(sockfd, buf, BUF_SIZ, 0, NULL, NULL);
    printf("listener: got packet %lu bytes\n Frame = {",(unsigned long int) numbytes);
    for( counter = 0; counter < numbytes; counter++)
        printf( "%02X ", buf[counter]);
    printf( "}");

    /* Check the packet is for me */
    if (eh->ether_dhost[0] == DEST_MAC0 &&
            eh->ether_dhost[1] == DEST_MAC1 &&
            eh->ether_dhost[2] == DEST_MAC2 &&
            eh->ether_dhost[3] == DEST_MAC3 &&
            eh->ether_dhost[4] == DEST_MAC4 &&
            eh->ether_dhost[5] == DEST_MAC5) {
        printf("Correct destination MAC address\n");
    } else {
        printf("Wrong destination MAC: %x:%x:%x:%x:%x:%x\n",
                        eh->ether_dhost[0],
                        eh->ether_dhost[1],
                        eh->ether_dhost[2],
                        eh->ether_dhost[3],
                        eh->ether_dhost[4],
                        eh->ether_dhost[5]);
        ret = -1;
        goto done;
    }

    /* Get source IP */
    ((struct sockaddr_in *)&their_addr)->sin_addr.s_addr = iph->saddr;
    inet_ntop(AF_INET, &((struct sockaddr_in*)&their_addr)->sin_addr, sender, sizeof sender);

    /* Look up my device IP addr if possible */
    strncpy(if_ip.ifr_name, ifName, IFNAMSIZ-1);
    if (ioctl(sockfd, SIOCGIFADDR, &if_ip) >= 0) { /* if we can't check then don't */
        printf("Source IP: %s\n My IP: %s\n", sender,
                inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
        /* ignore if I sent it */
        if (strcmp(sender, inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr)) == 0)  {
            printf("but I sent it :(\n");
            ret = -1;
            goto done;
        }
    }

    /* UDP payload length */
    ret = ntohs(udph->len) - sizeof(struct udphdr);

    /* Print packet */
    printf("\tData:");
    for (i=0; i<numbytes; i++) printf("%02x:", buf[i]);
    printf("\n");

done:   goto repeat;

    close(sockfd);
    return ret;
}

请注意,我正在初始化的地址mac是我从wireshark上的IGMP帧获得的地址。这段代码实际上允许我只检测ICMP帧。

使用时:

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);

而不是:

sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE);

我什么都没得到! 在wireshark上,我得到了我想要的所有帧,包括IGMP和ICMP !!

0 个答案:

没有答案