From 04f524d203b78d4767873e14c062beb54c3c38e1 Mon Sep 17 00:00:00 2001
From: Glenn <glenux@glenux.net>
Date: Fri, 25 Sep 2009 21:18:43 +0200
Subject: [PATCH] igmpv3gen: Initial import.

---
 LICENSE.txt   | 165 +++++++++++++++++++++++++
 src/Makefile  |   9 ++
 src/igmpgen.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 510 insertions(+)
 create mode 100644 LICENSE.txt
 create mode 100644 src/Makefile
 create mode 100644 src/igmpgen.c

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..0a82915
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,9 @@
+IGMPPROG=igmpgen
+CFLAGS=-Werror -Wall `libnet-config --defines`
+LDFLAGS=`libnet-config --libs`
+
+all:
+	gcc $(CFLAGS) $(IGMPPROG).c -o $(IGMPPROG) $(LDFLAGS)
+
+clean:
+	rm -f $(IGMPPROG)
diff --git a/src/igmpgen.c b/src/igmpgen.c
new file mode 100644
index 0000000..27eb7ee
--- /dev/null
+++ b/src/igmpgen.c
@@ -0,0 +1,336 @@
+#include <libnet.h>
+#include <netinet/igmp.h>
+
+#define IGMP_V3_MEMBERSHIP_REPORT 0x22
+
+struct igmp_extra {
+    u_int8_t igmp_version;
+    char * igmp_tag;
+    u_int8_t igmp_type;             /* IGMP type */
+    u_int8_t igmp_code;             /* routing code */
+    char * igmp_dst;
+    int group_override_dst;
+} g_igmp_pkts[] = {
+    // name,  type (or version+type), code
+    { 1, "query",  0x11, 0, "224.0.0.1", 0 },
+    { 1, "report", 0x12, 0, "224.0.0.1", 1 },
+    { 1, "dvmrp",  0x13, 0, "224.0.0.1", 0 },
+
+    { 2, "query",  0x11, 1, "224.0.0.1", 0 },
+    { 2, "report", 0x16, 1, "224.0.0.2", 1 },
+    { 2, "leave",  0x17, 1, "224.0.0.2", 0 },
+
+    { 3, "report", 0x22, 1, "224.0.0.22", 0 },
+
+    { 0, 0, 0, 0 },
+};
+
+void usage(char *);
+
+int main(int argc, char **argv)
+{
+    /* ip addresses */
+    u_int32_t ip_src = 0;
+    u_int32_t ip_dst = 0;
+    u_int32_t igmp_group = 0;      
+    char *igmp_group_str = NULL;
+    char *ip_src_str = NULL;
+    char *ip_dst_str = NULL;
+
+    /* ports */
+    u_short src_prt = 3141;
+    u_short dst_prt = 5926;   
+
+
+    u_char igmp_type = 0;
+    u_char igmp_code = 0;
+    u_int8_t igmp_version = 0;
+    int igmp_override;
+    struct igmp_extra *pkt_ptr;
+
+    int ip_src_test = 1;
+    int ip_dst_test = 1;
+    int igmp_group_test =1;
+
+    /* libnet stuff */
+    char neterr[LIBNET_ERRBUF_SIZE];
+    libnet_ptag_t ptag;
+    libnet_t * netcontext = NULL;
+    char *device = "eth0";
+
+    /* misc */
+    int c;                      
+    char *cp = NULL; 
+    int found=0;
+
+    printf("IGMP packet generator\n\n");
+
+    printf("Parsing command line...\n");
+    while((c = getopt(argc, argv, "i:t:g:d:s:")) != EOF)
+    {
+        switch (c)
+        {
+            case 'i':
+                printf("  Net interface = [%s]\n",optarg);
+                device = optarg;
+                break;
+
+            case 't':
+                if (!(cp = strrchr(optarg, '.'))) {
+                    usage(argv[0]);
+                    exit(1);
+                }
+                *cp++ = 0;
+
+                igmp_version = (u_short)atoi(cp);
+
+                pkt_ptr = g_igmp_pkts;
+                while(pkt_ptr->igmp_version || pkt_ptr->igmp_tag){
+                    if ((strcasecmp(pkt_ptr->igmp_tag, optarg) == 0) 
+                            && (igmp_version == pkt_ptr->igmp_version)){
+                        found = 1;
+                        igmp_type = pkt_ptr->igmp_type;
+                        igmp_code = pkt_ptr->igmp_code;
+                        ip_dst_str = pkt_ptr->igmp_dst;
+                        igmp_override = pkt_ptr->group_override_dst;
+
+                        break;
+                    }
+                    pkt_ptr++; 
+                }
+                if (found){
+                    printf("  Packet = [%s] version [%d]\n", pkt_ptr->igmp_tag, igmp_version);
+                } else {
+                    usage(argv[0]);
+                    exit(1);
+                }
+
+                break;
+            case 'g':
+                printf("  Group = [%s]\n", optarg);
+                igmp_group_str = optarg;
+                break;
+
+            case 'n':
+                // delay between packets in sec
+                break;
+
+                /*
+                 *  We expect the input to be of the form `ip.ip.ip.ip.port`.  We
+                 *  point cp to the last dot of the IP address/port string and
+                 *  then seperate them with a NULL byte.  The optarg now points to
+                 *  just the IP address, and cp points to the port.
+                 */
+            case 'd':
+                if ((cp = strrchr(optarg, ':')))
+                {
+                    *cp++ = 0;
+                    dst_prt = (u_short)atoi(cp);
+                } else {
+                    dst_prt = 0;
+                }
+                ip_dst_str = strdup(optarg);
+                printf("  Destination = ip [%s] port [%d]\n", ip_dst_str, dst_prt);
+                break;
+
+            case 's':
+                if ((cp = strrchr(optarg, ':')))
+                {
+                    *cp++ = 0;
+                    src_prt = (u_short)atoi(cp);
+                } else {
+                    src_prt = 0;
+                }
+                ip_src_str = strdup(optarg);
+                printf("  Source = ip [%s] port [%d]\n", ip_src_str, src_prt);
+                break;
+        }
+    }
+    if (!igmp_group_str || !device)
+    {
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+    printf("done\n");
+
+    /*
+     * Memory initialization
+     */
+    printf("Initializing libnet context...");
+    netcontext = libnet_init(LIBNET_RAW4, device, neterr);
+    if (!netcontext){
+        fprintf(stderr,neterr);
+        exit(1);
+    }
+    libnet_clear_packet(netcontext);
+    if (!ip_src_str)
+    {
+        ip_src = libnet_get_ipaddr4(netcontext);
+        ip_src_str = libnet_addr2name4(ip_src, LIBNET_DONT_RESOLVE);
+    }
+
+    printf("done\n");
+
+
+    printf("Packet construction...\n");
+
+    /*
+     *  Packet construction : IGMP
+     */
+    printf("  Building IGMP content...\n");
+
+    // Override dest with group info when needed
+    if (igmp_override) {
+        ip_dst_str = igmp_group_str;
+    }
+
+    switch ( igmp_type ) {
+        case 0x11: // IGMP_MEMBERSHIP_QUERY
+            if (igmp_version == 2){
+                // group specific
+            } else if (igmp_version == 1) {
+                // general
+                igmp_group = 0;
+                igmp_group_str = "0.0.0.0";
+                igmp_group_test = 0;
+            }
+            // DST = 224.0.0.1
+            //igmp_group = 0;
+            //igmp_group_test = 0;
+            break;
+        case 0x12: //
+        default:
+            break;
+    }
+
+    if (ip_src_test
+            && !(ip_src = libnet_name2addr4(netcontext, ip_src_str, LIBNET_RESOLVE)))
+    {
+        fprintf(stderr,"Bad source IP address: %s\n", ip_src_str);
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+        exit(1);
+    }
+
+    if (ip_dst_test 
+            && !(ip_dst = libnet_name2addr4(netcontext, ip_dst_str, LIBNET_RESOLVE)))
+    {
+        fprintf(stderr,"Bad destination IP address: %s\n", ip_dst_str);
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+        exit(1);
+    }
+
+    if (igmp_group_test && !(igmp_group = libnet_name2addr4(netcontext, igmp_group_str, LIBNET_RESOLVE)))
+    {
+        fprintf(stderr,"Bad group IP address: %s\n", optarg);
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+        exit(1);
+    }
+
+    printf("    IP SRC IP  = %s\n", ip_src_str);
+    printf("    IP DEST IP = %s\n", ip_dst_str);
+    printf("    IGMP TYPE  = 0x%02x\n", igmp_type);
+    printf("    IGMP CODE  = 0x%02x\n", igmp_code);
+    printf("    IGMP GROUP = %s\n", igmp_group_str);
+    printf("    GROUP TEST = %d\n", igmp_group_test);
+
+    /*
+       int test = 1;
+       if ( *(char *) test == 1) {
+    // little endian
+    printf("little endian\n");
+    } else {
+    // big endian
+    printf("big endian\n");
+    }
+    */
+
+    ptag = libnet_build_igmp(
+            igmp_type,              // IGMP type 
+            igmp_code,              // IGMP code or TTL 
+            0,                      // checksum
+            htonl(igmp_group),                // ip addr 
+            NULL,                      // Ptr to packet data (or null) 
+            0,                      // Packet payload length 
+            netcontext,             // Ptr to libnet context
+            0);                     // Build a new packet header
+    if (ptag < 0)
+    { 
+        fprintf(stderr, "Error while building IGMP header data (err %d)\n", ptag);
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+        exit(1);
+    }
+
+
+    u_int8_t ipopt[2];
+    ipopt[0] = IPOPT_RA;
+    ipopt[1] = 4;
+    ptag = libnet_build_ipv4_options(
+            ipopt,       // options
+            2,           // options_s (length of option string)
+            netcontext,  // libnet context
+            0         // ptag
+            );                
+    if (ptag < 0)
+    { 
+        fprintf(stderr, "Error while building IPv4 options data (err %d)\n", ptag);
+    }
+
+    ptag = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_IGMP_H,  // pkg size + data
+            0,         // IP tos
+            getpid(),  // IP ID
+            IP_DF,         // frag flags and offset
+            1,         // TTL
+            IPPROTO_IGMP,           // transport protocol
+            0,                      // checksum
+            ip_src,                 // source IP
+            ip_dst,                 // destination IP
+            NULL,                   // payload (none)
+            0,                      // payload length
+            netcontext,             // libnet context
+            0                       // build a new header
+            );                
+    if (ptag < 0)
+    { 
+        fprintf(stderr, "Error while building IPv4 header data (err %d)\n", ptag);
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+        exit(1);
+    }
+
+    printf("  done\n"); //IGMP content
+
+    c = libnet_write(netcontext);
+    if (c < 0)
+    {
+        fprintf(stderr,"%s\n", libnet_geterror(netcontext));
+    }
+    else
+    {
+        printf("construction and injection completed, wrote all %d bytes\n", c);
+    } 
+
+    /*
+     *  Free packet memory.
+     */
+    libnet_diag_dump_pblock(netcontext);
+    libnet_destroy(netcontext);
+    exit(0);
+
+    return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+void usage(char *name)
+{
+    struct igmp_extra *pkt_ptr;
+    fprintf(stderr, "usage: %s -i ethdevice -g group -t packet.version [-s ip:port] [-d ip:port]\n", name);
+
+    pkt_ptr = g_igmp_pkts;
+    fprintf(stderr,"\nAvailable packet types:\n");
+    while(pkt_ptr->igmp_version || pkt_ptr->igmp_tag){
+        fprintf(stderr," - %s.%d\n", pkt_ptr->igmp_tag, pkt_ptr->igmp_version);
+        pkt_ptr++; 
+    }
+}
+
+
+// vim: ts=4 sts=4 sw=4 et