commit b1c8521214090b55c89332bcc020d42227f4a544
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Thu May 3 11:23:57 2012 +0200
igmp: Adding setup for sending IGMP queries
This commit adds a completely new setup into multicast test tools:
send_igmp_query
It's capable of sending IGMP queries of any version with various
parameters (even malformed ones).
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
test_tools/multicast/Makefile | 2 +-
test_tools/multicast/client/send_igmp_query.c | 168 ++++++++++++++++++++++++
test_tools/multicast/igmp_utils.h | 167 ++++++++++++++++++++++++
test_tools/multicast/parameters.h | 173 ++++++++++++++++++++++---
4 files changed, 490 insertions(+), 20 deletions(-)
---
diff --git a/test_tools/multicast/Makefile b/test_tools/multicast/Makefile
index e09746c..90b0a83 100644
--- a/test_tools/multicast/Makefile
+++ b/test_tools/multicast/Makefile
@@ -2,7 +2,7 @@ CC=gcc
TOOLS_DIR=.
CFLAGS=-Wall -Wextra -I$(TOOLS_DIR)
-SENDERS=send_simple
+SENDERS=send_simple send_igmp_query
RECEIVERS=recv_simple recv_membership recv_source_membership recv_block_source
OFFLINE=sockopt_loop sockopt_ttl sockopt_if sockopt_membership sockopt_source_membership sockopt_block_source max_groups
diff --git a/test_tools/multicast/client/send_igmp_query.c b/test_tools/multicast/client/send_igmp_query.c
new file mode 100644
index 0000000..de5e608
--- /dev/null
+++ b/test_tools/multicast/client/send_igmp_query.c
@@ -0,0 +1,168 @@
+/*
+ * send_igmp_query.c - igmp querier simulator
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Author: Radek Pazdera (rpazdera(a)redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "igmp_utils.h"
+
+void general_query(int sockfd, struct in_addr saddr)
+{
+ int len = sizeof(struct iphdr) + sizeof(struct ipopts)
+ + sizeof(struct igmphdr);
+
+ unsigned char *buffer = malloc(len);
+ if (buffer == NULL) {
+ perror("malloc()");
+ exit(EXIT_FAILURE);
+ }
+
+ struct iphdr *ip_header = (struct iphdr *) buffer;
+
+ struct ipopts *ip_options = (struct ipopts *)(buffer + sizeof(struct iphdr));
+
+ struct igmphdr *igmp_header;
+ igmp_header = (struct igmphdr *) (buffer + sizeof(struct iphdr)
+ + sizeof(struct ipopts));
+
+ struct in_addr daddr;
+ inet_aton("224.0.0.1", &daddr);
+ ip_header_init(ip_header, saddr, daddr);
+ ip_options_init(ip_options);
+
+ igmp_header->type = IGMP_HOST_MEMBERSHIP_QUERY;
+ igmp_header->code = 0;
+
+ /* IGMPv1 general query */
+ igmp_header->group = 0;
+
+ igmp_header->csum = 0;
+ igmp_header->csum = checksum((unsigned short *) igmp_header,
+ sizeof(struct igmphdr));
+ send_ip_frame(sockfd, daddr, buffer, len);
+ free(buffer);
+}
+
+void group_specific_query(int sockfd, struct in_addr saddr,
+ struct in_addr daddr,struct in_addr group,
+ int max_resp_time)
+{
+ int len = sizeof(struct iphdr) + sizeof(struct ipopts)
+ + sizeof(struct igmphdr);
+
+ unsigned char *buffer = malloc(len);
+ if (buffer == NULL) {
+ perror("malloc()");
+ exit(EXIT_FAILURE);
+ }
+
+ struct iphdr *ip_header = (struct iphdr *) buffer;
+
+ struct ipopts *ip_options = (struct ipopts *)(buffer + sizeof(struct iphdr));
+
+ struct igmphdr *igmp_header;
+ igmp_header = (struct igmphdr *) (buffer + sizeof(struct iphdr)
+ + sizeof(struct ipopts));
+
+ ip_header_init(ip_header, saddr, daddr);
+ ip_options_init(ip_options);
+
+ igmp_header->type = IGMP_HOST_MEMBERSHIP_QUERY;
+ igmp_header->code = max_resp_time;
+
+ igmp_header->group = group.s_addr;
+
+ igmp_header->csum = 0;
+ igmp_header->csum = checksum((unsigned short *) igmp_header,
+ sizeof(struct igmphdr));
+
+ send_ip_frame(sockfd, daddr, buffer, len);
+ free(buffer);
+}
+
+void group_and_source_specific_query(int sockfd, struct in_addr saddr,
+ struct in_addr daddr, struct in_addr group,
+ struct in_addr *sources, int num_sources,
+ int max_resp_time)
+{
+ int len = sizeof(struct iphdr) + sizeof(struct ipopts)
+ + sizeof(struct igmpv3_query) + num_sources*sizeof(__be32);
+
+ unsigned char *buffer = malloc(len);
+ if (buffer == NULL) {
+ perror("malloc()");
+ exit(EXIT_FAILURE);
+ }
+
+ struct iphdr *ip_header = (struct iphdr *) buffer;
+
+ struct ipopts *ip_options = (struct ipopts *)(buffer + sizeof(struct iphdr));
+
+ struct igmpv3_query *igmp_header;
+ igmp_header = (struct igmpv3_query *) (buffer + sizeof(struct iphdr)
+ + sizeof(struct ipopts));
+
+ ip_header_init(ip_header, saddr, daddr);
+ ip_options_init(ip_options);
+
+ igmp_header->type = IGMP_HOST_MEMBERSHIP_QUERY;
+ igmp_header->code = max_resp_time;
+
+ igmp_header->group = group.s_addr;
+
+ int i;
+ for (i = 0; i < num_sources; i++)
+ igmp_header->srcs[i] = sources[i].s_addr;
+
+ igmp_header->csum = 0;
+ igmp_header->csum = checksum((unsigned short *) igmp_header,
+ sizeof(struct igmpv3_query) +
+ num_sources*sizeof(__be32));
+
+ send_ip_frame(sockfd, daddr, buffer, len);
+ free(buffer);
+}
+
+int main(int argc, char** argv)
+{
+ struct parameters params;
+ parse_args(argc, argv, ¶ms);
+
+ int sockfd = init_raw_socket(params.interface);
+
+ switch(params.query_type) {
+ case IGMP_GENERAL_QUERY:
+ general_query(sockfd, params.interface);
+ break;
+ case IGMP_GROUP_SPECIFIC_QUERY:
+ group_specific_query(sockfd, params.interface,
+ params.destaddr, params.multiaddr,
+ params.max_resp_time);
+ break;
+ case IGMP_GROUP_AND_SOURCE_SPECIFIC_QUERY:
+ group_and_source_specific_query(sockfd,params.interface,
+ params.destaddr, params.multiaddr,
+ ¶ms.sourceaddr, 1,
+ params.max_resp_time);
+
+ break;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/test_tools/multicast/igmp_utils.h b/test_tools/multicast/igmp_utils.h
new file mode 100644
index 0000000..901d01f
--- /dev/null
+++ b/test_tools/multicast/igmp_utils.h
@@ -0,0 +1,167 @@
+/*
+ * igmp_utils.h - tools for sending/receiving IGMP packets
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Author: Radek Pazdera (rpazdera(a)redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __IGMP_UTILS_H__
+#define __IGMP_UTILS_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <time.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <netinet/ip.h>
+#include <linux/igmp.h>
+
+#define IGMP
+#include "parameters.h"
+
+int __verbosity = 0;
+
+/* Verbose print */
+#ifndef printv
+#define printv(args...) \
+ if (__verbosity > 0) \
+ { \
+ printf(args); \
+ fflush(stdout); \
+ }
+#endif
+
+struct ipopt_ra {
+ u_char type;
+ u_char length;
+ uint16_t data;
+};
+
+struct ipopts {
+ struct ipopt_ra ra;
+};
+
+unsigned short checksum(unsigned short *addr, int len)
+{
+ register int nleft = len;
+ register int sum = 0;
+ u_short answer = 0;
+
+ while (nleft > 1) {
+ sum += *addr++;
+ nleft -= 2;
+ }
+
+
+ if (nleft == 1) {
+ *(u_char *)(&answer) = *(u_char *)addr;
+ sum += answer;
+ }
+
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ answer = ~sum;
+ return(answer);
+}
+
+void ip_header_init(struct iphdr *iph, struct in_addr saddr,
+ struct in_addr daddr)
+{
+ iph->version = 4;
+ iph->ihl = 6;
+ iph->tos = 0xc0;
+ iph->id = htons(0);
+ iph->frag_off = htons(0b0100000000000000); /* DF */
+ iph->ttl = 1;
+ iph->protocol = IPPROTO_IGMP;
+
+ iph->saddr = saddr.s_addr;
+ iph->daddr = daddr.s_addr;
+}
+
+void ip_options_init(struct ipopts *options)
+{
+ options->ra.type = 0x94;
+ options->ra.length = 4;
+ options->ra.data = 0;
+}
+
+void send_ip_frame(int sockfd, struct in_addr daddr,
+ unsigned char* buffer, size_t len)
+{
+ struct sockaddr_in servaddr;
+ bzero(&servaddr, sizeof(struct sockaddr_in));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr = daddr;
+
+ int bytes_sent = sendto(sockfd, buffer, len, 0, (struct sockaddr *)&servaddr,
+ sizeof(struct sockaddr_in));
+ if (bytes_sent < 0) {
+ perror("sendto()");
+ exit(EXIT_FAILURE);
+ }
+
+ FILE *fp;
+ fp=fopen("deeebg", "wb");
+ fwrite(buffer, sizeof(char), len, fp);
+}
+
+int init_raw_socket(struct in_addr interface)
+{
+ int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (sockfd < 0) {
+ perror("socket()");
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in addr;
+ bzero(&addr, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr = interface;
+
+ int result = bind(sockfd, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+
+ if (result < 0) {
+ perror("bind()");
+ exit(EXIT_FAILURE);
+ }
+
+ return sockfd;
+}
+
+/** Close a socket */
+void free_socket(int sockfd)
+{
+ close(sockfd);
+}
+
+
+#endif
diff --git a/test_tools/multicast/parameters.h b/test_tools/multicast/parameters.h
index 6a313ec..a9fda9d 100644
--- a/test_tools/multicast/parameters.h
+++ b/test_tools/multicast/parameters.h
@@ -20,6 +20,9 @@
* 02110-1301, USA.
*/
+#ifndef __PARAMETERS_H__
+#define __PARAMETERS_H__
+
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -37,6 +40,14 @@
extern int __verbosity;
+#ifdef IGMP
+enum __igmp_query_types {
+ IGMP_GENERAL_QUERY = 1,
+ IGMP_GROUP_SPECIFIC_QUERY = 2,
+ IGMP_GROUP_AND_SOURCE_SPECIFIC_QUERY = 3
+};
+#endif
+
/** Structure that carries test parameters */
struct parameters
{
@@ -46,7 +57,7 @@ struct parameters
short port;
struct in_addr interface;
-#ifdef RECEIVE
+#if defined(RECEIVE) || defined(IGMP)
struct in_addr sourceaddr;
#endif
@@ -55,6 +66,12 @@ struct parameters
int ttl;
int loop;
#endif
+
+#ifdef IGMP
+ short query_type;
+ struct in_addr destaddr;
+ int max_resp_time;
+#endif
};
/** Initialize parameters struct with default values. */
@@ -64,7 +81,8 @@ void default_parameters(struct parameters* params)
params->port = 0;
memset(¶ms->multiaddr, 0, sizeof(struct in_addr));
memset(¶ms->interface, 0, sizeof(struct in_addr));
-#ifdef RECEIVE
+
+#if defined(RECEIVE) || defined(IGMP)
memset(¶ms->sourceaddr, 0, sizeof(struct in_addr));
#endif
@@ -73,36 +91,118 @@ void default_parameters(struct parameters* params)
params->ttl = 1;
params->loop = 1;
#endif
+
+#ifdef IGMP
+ params->query_type = IGMP_GENERAL_QUERY;
+ memset(¶ms->destaddr, 0, sizeof(struct in_addr));
+ params->max_resp_time = 10;
+#endif
+}
+
+void usage(char *program_name, int retval)
+{
+ printf("usage: %s\n", program_name);
+ printf(" -h | --help print this\n");
+ printf(" -i | --interface a.b.c.d local interface to use for communication\n");
+ printf(" -v | --verbose print additional information during the runtime\n");
+
+ printf("\n");
+
+ printf(" -d | --duration x test duration\n");
+
+#ifdef SEND
+ printf(" -f | --delay x delay between messages\n");
+#endif
+
+ printf("\n");
+
+ printf(" -a | --address a.b.c.d multicast group address\n");
+
+#if defined(SEND) || defined(IGMP)
+ printf(" -s | --source_address a.b.c.d source address\n");
+#endif
+
+#ifdef IGMP
+ printf(" -z | --dest_address a.b.c.d destination address\n");
+#endif
+
+#if defined(SEND) || defined(RECEIVE)
+ printf(" -p | --port x port number\n");
+#endif
+
+ printf("\n");
+
+#ifdef IGMP
+ printf(" -q | --query_type query type\n");
+ printf(" -r | --max_resp_time x maximum response time\n");
+#endif
+
+#ifdef SEND
+ printf(" -t | --ttl x time to live for IP packet\n");
+ printf(" -l | --loop x loopback multicast communication\n");
+#endif
+
+ exit(retval);
}
/** Generic function for parsing arguments */
void parse_args(int argc, char** argv, struct parameters* args)
{
#ifdef SEND
- static const char* opts = "d:a:p:i:f:l:t:v";
+ #define __send_opts "f:t:l:p:"
+#else
+ #define __send_opts ""
+#endif
+
+#if defined(RECEIVE) || defined(IGMP)
+ #define __recv_opts "s:p:"
+#else
+ #define __recv_opts ""
+#endif
+
+#ifdef IGMP
+ #define __igmp_opts "q:z:r:"
+#else
+ #define __igmp_opts ""
#endif
-#ifdef RECEIVE
- static const char* opts = "d:a:p:s:i:v";
+#ifdef IGMP
+ int dest_was_set = 0;
#endif
+ static const char* opts = "d:a:i:v" __send_opts __recv_opts
+ __igmp_opts;
+
+
static struct option long_options[] =
{
- {"duration", required_argument, NULL, 'd'},
- {"multicast_address", required_argument, NULL, 'a'},
- {"port", required_argument, NULL, 'p'},
- {"interface", required_argument, NULL, 'i'},
- {"verbose", no_argument, NULL, 'v'},
-#ifdef RECEIVE
- {"source_address", required_argument, NULL, 's'},
+ {"help", required_argument, NULL, 'h'},
+ {"interface", required_argument, NULL, 'i'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"duration", required_argument, NULL, 'd'},
+ {"multicast_address", required_argument, NULL, 'a'},
+
+#if defined(RECEIVE) || defined(IGMP)
+ {"source_address", required_argument, NULL, 's'},
#endif
#ifdef SEND
- {"delay", required_argument, NULL, 'f'},
- {"ttl", required_argument, NULL, 't'},
- {"loop", required_argument, NULL, 'l'},
+ {"delay", required_argument, NULL, 'f'},
+ {"ttl", required_argument, NULL, 't'},
+ {"loop", required_argument, NULL, 'l'},
#endif
- {0, 0, NULL, 0}
+
+#if defined(SEND) || defined(RECEIVE)
+ {"port", required_argument, NULL, 'p'},
+#endif
+
+#ifdef IGMP
+ {"query_type", required_argument, NULL, 'q'},
+ {"dest_address", required_argument, NULL, 'z'},
+ {"max_resp_time", required_argument, NULL, 'r'},
+
+#endif
+ {0, 0, NULL, 0}
};
default_parameters(args);
@@ -121,13 +221,16 @@ void parse_args(int argc, char** argv, struct parameters* args)
case 'p':
args->port = atoi(optarg);
break;
+ case 'h':
+ usage(argv[1], EXIT_SUCCESS);
+ break;
case 'i':
inet_pton(AF_INET, optarg, &(args->interface));
break;
case 'v':
__verbosity = 1;
break;
-#ifdef RECEIVE
+#if defined(RECEIVE) || defined(IGMP)
case 's':
inet_pton(AF_INET, optarg, &(args->sourceaddr));
break;
@@ -144,9 +247,41 @@ void parse_args(int argc, char** argv, struct parameters* args)
args->loop = atoi(optarg);
break;
#endif
+
+#ifdef IGMP
+ case 'q':
+ args->query_type = atoi(optarg);
+ /*if (strcmp(optarg, "general") == 0) {
+ args->query_type = IGMP_GENERAL_QUERY;
+ } else if (strcmp(optarg, "group_specific") == 0) {
+ args->query_type = IGMP_GROUP_SPECIFIC_QUERY;
+ } else if (strcmp(optarg, "group_and_source_specific") == 0) {
+ args->query_type = IGMP_GROUP_AND_SOURCE_SPECIFIC_QUERY;
+ } else {
+ fprintf(stderr, "%s: undefined query type\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }*/
+ break;
+ case 'z':
+ inet_pton(AF_INET, optarg, &(args->destaddr));
+ dest_was_set = 1;
+ break;
+ case 'r':
+ args->max_resp_time = atoi(optarg);
+ break;
+#endif
default: /* '?' */
- printf("%s: invalid test options\n", argv[0]);
- exit(EXIT_FAILURE);
+ fprintf(stderr, "%s: invalid test options\n", argv[0]);
+ usage(argv[0], EXIT_FAILURE);
}
}
+
+#ifdef IGMP
+ if (!dest_was_set)
+ args->destaddr = args->multiaddr;
+#endif
+
}
+
+#endif