Pergunta

I'm trying to forge an IGMPv2 Membership Request packet and send it on a RAW socket.

The RFC 3376 states:

IGMP messages are encapsulated in IPv4 datagrams, with an IP protocol number of 2. Every IGMP message described in this document is sent with an IP Time-to-Live of 1, IP Precedence of Internetwork Control (e.g., Type of Service 0xc0), and carries an IP Router Alert option [RFC-2113] in its IP header

So the IP_ROUTER_ALERT flag must be set.

I'm trying to forge the strict necessary of the packet (e.g. only the IGMP header & payload), so i'm using the setsockopt to edit the IP options.

some useful variables:

#define C_IP_MULTICAST_TTL 1
#define C_IP_ROUTER_ALERT 1

int sockfd = 0;
int ecsockopt = 0;
int bytes_num = 0;

int ip_multicast_ttl = C_IP_MULTICAST_TTL;
int ip_router_alert = C_IP_ROUTER_ALERT;

Here's how I open the RAW socket:

sock_domain = AF_INET;
sock_type = SOCK_RAW;
sock_proto = IPPROTO_IGMP;

if ((ecsockopt = socket(sock_domain,sock_type,sock_proto)) < 0) {
  printf("Error %d: Can't open socket.\n", errno);
  return 1;
} else {
  printf("** Socket opened.\n");
}
sockfd = ecsockopt;

Then I set the TTL and Router Alert option:

// Set the sent packets TTL
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ip_multicast_ttl, sizeof(ip_multicast_ttl))) < 0) {
  printf("Error %d: Can't set TTL.\n", ecsockopt);
  return 1;
} else {
  printf("** TTL set.\n");
}

// Set the Router Alert
if((ecsockopt = setsockopt(sockfd, IPPROTO_IP, IP_ROUTER_ALERT, &ip_router_alert, sizeof(ip_router_alert))) < 0) {
  printf("Error %d: Can't set Router Alert.\n", ecsockopt);
  return 1;
} else {
  printf("** Router Alert set.\n");
}

The setsockopt of IP_ROUTER_ALERT returns 0. After forging the packet, i send it with sendto in this way:

// Send the packet
if((bytes_num = sendto(sockfd, packet, packet_size, 0, (struct sockaddr*) &mgroup1_addr, sizeof(mgroup1_addr))) < 0) {
  printf("Error %d: Can't send Membership report message.\n", bytes_num);
  return 1;
} else {
  printf("** Membership report message sent. (bytes=%d)\n",bytes_num);
}

The packet is sent, but the IP_ROUTER_ALERT option (checked with wireshark) is missing. Am i doing something wrong? is there some other methods to set the IP_ROUTER_ALERT option?

Thanks in advance.

Foi útil?

Solução

Finally i've found out that the IP_ROUTER_ALERT has to be set by the Linux Kernel. IGMP membership requests are sent after a IP_ADD_MEMBERSHIP is done and the Kernel takes charge of setting the IP_ROUTER_ALERT flag.

Outras dicas

I do not know why your code is not working (it looks fine to me), but I can suggest a workaround: Drop one more layer down on your raw socket and build Ethernet frames. You may also want to take a look at Libnet, which handles building packets like this for you.

The documentation states:

Pass all to-be forwarded packets with the IP Router Alert option set to this socket. Only valid for raw sockets. This is useful, for instance, for user space RSVP daemons. The tapped packets are not forwarded by the kernel, it is the users responsibility to send them out again. Socket binding is ignored, such packets are only filtered by protocol. Expects an integer flag.

This sounds as if the option only matters when receiving packets on the socket, not when sending them. If you're sending raw packets, can't you just set the required option in the IP header yourself?

As a reference I would recommend one of the many IGMP aware programs out there. One example is igmpproxy: https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L54

/*
 * Open and initialize the igmp socket, and fill in the non-changing
 * IP header fields in the output packet buffer.
 */
void initIgmp(void) {
    struct ip *ip;

    recv_buf = malloc(RECV_BUF_SIZE);
    send_buf = malloc(RECV_BUF_SIZE);

    k_hdr_include(true);    /* include IP header when sending */
    k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering        */
    k_set_ttl(1);       /* restrict multicasts to one hop */
    k_set_loop(false);      /* disable multicast loopback     */

    ip         = (struct ip *)send_buf;
    memset(ip, 0, sizeof(struct ip));
    /*
     * Fields zeroed that aren't filled in later:
     * - IP ID (let the kernel fill it in)
     * - Offset (we don't send fragments)
     * - Checksum (let the kernel fill it in)
     */
    ip->ip_v   = IPVERSION;
    ip->ip_hl  = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */
    ip->ip_tos = 0xc0;      /* Internet Control */
    ip->ip_ttl = MAXTTL;    /* applies to unicasts only */
    ip->ip_p   = IPPROTO_IGMP;

    allhosts_group   = htonl(INADDR_ALLHOSTS_GROUP);
    allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
    alligmp3_group   = htonl(INADDR_ALLIGMPV3_GROUP);
}

and https://github.com/ViToni/igmpproxy/blob/logging/src/igmp.c#L271

/*
 * Construct an IGMP message in the output packet buffer.  The caller may
 * have already placed data in that buffer, of length 'datalen'.
 */
static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) {
    struct ip *ip;
    struct igmp *igmp;
    extern int curttl;

    ip                      = (struct ip *)send_buf;
    ip->ip_src.s_addr       = src;
    ip->ip_dst.s_addr       = dst;
    ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen);

    if (IN_MULTICAST(ntohl(dst))) {
        ip->ip_ttl = curttl;
    } else {
        ip->ip_ttl = MAXTTL;
    }

    /* Add Router Alert option */
    ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[0] = IPOPT_RA;
    ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[1] = 0x04;
    ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00;
    ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00;

    igmp                    = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN);
    igmp->igmp_type         = type;
    igmp->igmp_code         = code;
    igmp->igmp_group.s_addr = group;
    igmp->igmp_cksum        = 0;
    igmp->igmp_cksum        = inetChksum((unsigned short *)igmp,
                                         IP_HEADER_RAOPT_LEN + datalen);

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top