From: Radek Pazdera rpazdera@redhat.com
This test integratest multicast test tools into LNST and makes it possible to run and evaluate them from the recipes.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- Tests/TestMulticast.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 128 insertions(+), 0 deletions(-) create mode 100644 Tests/TestMulticast.py
diff --git a/Tests/TestMulticast.py b/Tests/TestMulticast.py new file mode 100644 index 0000000..c757bca --- /dev/null +++ b/Tests/TestMulticast.py @@ -0,0 +1,128 @@ +""" +Wrapper for executing the multicast test tools in LNST + +Copyright 2012 Red Hat, Inc. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +rpazdera@redhat.com (Radek Pazdera) +""" + +import logging +import re +from Common.TestsCommon import TestGeneric +from NetTest.NetTestCommand import CommandException +from Common.ExecCmd import exec_cmd + +class TestMulticast(TestGeneric): + """ Wrapper for executing the multicast test tools in LNST + + Running this test executes specified test setup from + multicast tools and evaluates the results. The behavior + is defined entirely by behavior of the selected setup. + + """ + + _tools_location = "test_tools/multicast/" + _conditions = {} + + @staticmethod + def _remove_mask(address_in_string): + """ Remove mask suffix from the IP address """ + if address_in_string != None: + return address_in_string.split('/')[0] + else: + return address_in_string + + def _compose_cmd(self): + """ Setup a command from the recipe options """ + cmd = "" + opts = {} + + setup = self._tools_location + self.get_mopt("setup") + opts["multicast_address"] = self._remove_mask(self.get_opt("address")) + opts["port"] = self.get_opt("port") + opts["interface"] = self._remove_mask(self.get_opt("interface")) + opts["duration"] = self.get_opt("duration") + + # sender-specific + opts["loop"] = self.get_opt("loop") + opts["ttl"] = self.get_opt("ttl") + opts["delay"] = self.get_opt("delay") + + # receiver-specific + opts["source"] = self._remove_mask(self.get_opt("source")) + + cmd = "./{0} ".format(setup) + + for optname, optval in opts.iteritems(): + if optval != None: + cmd += "--{0} "{1}" ".format(optname, optval) + + return cmd + + def _evaluate_result(self, name, value): + """ Check if the result meets required conditions """ + if name in self._conditions: + try: + float(value) + except ValueError: + value = '"' + value + '"' + + result = eval(value + self._conditions[name]) + logging.info("Condition evaluated {2}: {0}{1}".format(name, + self._conditions[name], str(result))) + + return result + else: + return True + + def _prepare_conditions(self): + """ Search for var names in conditions """ + varname_r = r"[a-zA-Z_][a-zA-Z0-9_]*" + + conds = self.get_multi_opt("condition") + logging.debug(conds) + for cond in conds: + if cond == None: + continue + logging.debug(cond) + match = re.match(varname_r, cond) + if match: + name = match.group(0) + self._conditions[name] = cond.replace(name, "") + else: + raise CommandException(self) + + + def run(self): + self._prepare_conditions() + + setup_name = self.get_mopt("setup") + logging.info("Started Multicast test setup {0}".format(setup_name)) + + exec_cmd("cd " + self._tools_location + " && make") + + cmd = self._compose_cmd() + data_stdout = exec_cmd(cmd, die_on_err=False)[0] + + res = {} + + # line format matches name=value pairs with optional + # double quotes around the value + line_format_r = r"([a-zA-Z0-9_ ]+)="?([a-zA-Z0-9_ ]*)"?" + + for line in data_stdout.split("\n"): + match = re.search(line_format_r, line) + if match: + name = match.group(1).strip() + value = match.group(2).strip() + + res[name] = value + logging.info("Test result: {0} = {1}".format(name, value)) + if not self._evaluate_result(name, value): + return self.set_fail("Conditions not met!", res) + + return self.set_pass(res)
From: Radek Pazdera rpazdera@redhat.com
The `test_tools/' directory is a place for storing various non-python tools that will later be used for testing, such as programs in C, shell scripts etc.
This commit also adds first test tools - a set of utilities, test setups and cases for validating implementation of multicast/igmp in kernel.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- example_recipes/cmd_sequences/multicast/if.xml | 77 ++++++++ example_recipes/cmd_sequences/multicast/loop.xml | 70 +++++++ example_recipes/cmd_sequences/multicast/simple.xml | 33 ++++ example_recipes/cmd_sequences/multicast/ttl.xml | 112 ++++++++++++ test_tools/multicast/Makefile | 21 ++ test_tools/multicast/client/send_simple.c | 59 ++++++ test_tools/multicast/multicast_utils.h | 192 ++++++++++++++++++++ test_tools/multicast/offline/sockopt_if.c | 70 +++++++ test_tools/multicast/offline/sockopt_loop.c | 52 ++++++ test_tools/multicast/offline/sockopt_ttl.c | 79 ++++++++ test_tools/multicast/parameters.h | 152 ++++++++++++++++ test_tools/multicast/server/recv_simple.c | 50 +++++ test_tools/multicast/sockopt_utils.h | 175 ++++++++++++++++++ 13 files changed, 1142 insertions(+), 0 deletions(-) create mode 100644 example_recipes/cmd_sequences/multicast/if.xml create mode 100644 example_recipes/cmd_sequences/multicast/loop.xml create mode 100644 example_recipes/cmd_sequences/multicast/simple.xml create mode 100644 example_recipes/cmd_sequences/multicast/ttl.xml create mode 100644 test_tools/multicast/Makefile create mode 100644 test_tools/multicast/client/send_simple.c create mode 100644 test_tools/multicast/multicast_utils.h create mode 100644 test_tools/multicast/offline/sockopt_if.c create mode 100644 test_tools/multicast/offline/sockopt_loop.c create mode 100644 test_tools/multicast/offline/sockopt_ttl.c create mode 100644 test_tools/multicast/parameters.h create mode 100644 test_tools/multicast/server/recv_simple.c create mode 100644 test_tools/multicast/sockopt_utils.h
diff --git a/example_recipes/cmd_sequences/multicast/if.xml b/example_recipes/cmd_sequences/multicast/if.xml new file mode 100644 index 0000000..c7fec3f --- /dev/null +++ b/example_recipes/cmd_sequences/multicast/if.xml @@ -0,0 +1,77 @@ +<!-- Offline IP_MULTICAST_IF test --> +<!-- Requires: 2 hosts + - [1] with one interface + - [2] with one interface + - --> + +<command_sequence> + <!-- IP_MULTICAST_IF sockopt conformance test --> + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="sockopt_if" /> + + <!-- This condition doesn't pass yet because of a + bug in the syscal. #803202 on RedHat Bugzilla --> + <!-- <option name="condition" value="status == 'pass'" /> --> + </options> + </command> + + <!-- IP_MULTICAST_IF correct interfaces set --> + <command type="exec" machine_id="1" value="sleep 1" /> + <command type="exec" machine_id="2" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="ttl" value="0" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="2" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][2]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> + + <!-- IP_MULTICAST_IF incorrect interfaces set --> + <command type="exec" machine_id="1" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="ttl" value="1" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="2" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="interface" value="127.0.0.1" /> + + <option name="condition" value="packets_received == 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> +</command_sequence> diff --git a/example_recipes/cmd_sequences/multicast/loop.xml b/example_recipes/cmd_sequences/multicast/loop.xml new file mode 100644 index 0000000..7b155e1 --- /dev/null +++ b/example_recipes/cmd_sequences/multicast/loop.xml @@ -0,0 +1,70 @@ +<!-- Offline IP_MULTICAST_LOOP test --> +<!-- Requires: 1 hosts with at least two interfaces --> + +<command_sequence> + <!-- IP_MULTICAST_LOOP sockopt conformance test --> + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="sockopt_loop" /> + <option name="condition" value="status == 'pass'" /> + </options> + </command> + + <!-- IP_MULTICAST_LOOP enabled --> + <command type="exec" machine_id="1" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="loop" value="1" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> + + <!-- IP_MULTICAST_LOOP disabled --> + <command type="exec" machine_id="1" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="loop" value="0" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received == 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> +</command_sequence> diff --git a/example_recipes/cmd_sequences/multicast/simple.xml b/example_recipes/cmd_sequences/multicast/simple.xml new file mode 100644 index 0000000..236558b --- /dev/null +++ b/example_recipes/cmd_sequences/multicast/simple.xml @@ -0,0 +1,33 @@ +<!-- Requires: 2 hosts with at least one interface --> +<!-- ['machines'][2]['netconfig'][1]['addresses'][0] --> + +<command_sequence> + <command type="exec" machine_id="1" value="sleep 1" /> + <command type="exec" machine_id="2" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="2" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <!-- <option name="interface" value="192.168.122.179" /> --> + <option type="recipe_eval" name="interface" value="['machines'][2]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> +</command_sequence> diff --git a/example_recipes/cmd_sequences/multicast/ttl.xml b/example_recipes/cmd_sequences/multicast/ttl.xml new file mode 100644 index 0000000..ae55228 --- /dev/null +++ b/example_recipes/cmd_sequences/multicast/ttl.xml @@ -0,0 +1,112 @@ +<!-- Offline IP_MULTICAST_TTL test --> +<!-- Requires: 2 hosts + - [1] with one interface + - [2] with one interface + - --> + +<command_sequence> + <!-- IP_MULTICAST_TTL sockopt conformance test --> + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="sockopt_ttl" /> + <option name="condition" value="status == 'pass'" /> + </options> + </command> + + <!-- IP_MULTICAST_TTL = 0, looped on one host --> + <command type="exec" machine_id="1" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="ttl" value="0" /> + <option name="loop" value="1" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="1" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> + + <!-- IP_MULTICAST_TTL = 0 between 2 hosts --> + <!-- KNOWN BUG: according to the specs, packets + with TTL=0 should not leave the host. + + Well it does, it's an intentional hack for some + apps [1]. + + http://www.spinics.net/lists/netdev/msg183704.html + --> + <command type="exec" machine_id="1" value="sleep 1" /> + <command type="exec" machine_id="2" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="ttl" value="0" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="2" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][2]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> + + <!-- IP_MULTICAST_TTL = 1 between 2 hosts --> + <command type="exec" machine_id="1" value="sleep 1" /> + + <command type="test" value="Multicast" machine_id="1" timeout="30" bg_id="1"> + <options> + <option name="setup" value="send_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option name="delay" value="0.1" /> + <option name="ttl" value="1" /> + <option type="recipe_eval" name="interface" value="['machines'][1]['netconfig'][1]['addresses'][0]" /> + </options> + </command> + + <command type="test" value="Multicast" machine_id="2" timeout="30"> + <options> + <option name="setup" value="recv_simple" /> + <option name="address" value="238.0.0.1" /> + <option name="port" value="1337" /> + <option name="duration" value="10" /> + <option type="recipe_eval" name="interface" value="['machines'][2]['netconfig'][1]['addresses'][0]" /> + + <option name="condition" value="packets_received > 0" /> + </options> + </command> + + <command type="wait" machine_id="1" value="1" /> +</command_sequence> diff --git a/test_tools/multicast/Makefile b/test_tools/multicast/Makefile new file mode 100644 index 0000000..47af8f5 --- /dev/null +++ b/test_tools/multicast/Makefile @@ -0,0 +1,21 @@ +CC=gcc +TOOLS_DIR=. +CFLAGS=-Wall -Wextra -I$(TOOLS_DIR) + +SENDERS=send_simple +RECEIVERS=recv_simple +OFFLINE=sockopt_loop sockopt_ttl sockopt_if + +all: $(SENDERS) $(RECEIVERS) $(OFFLINE) + +$(SENDERS): + $(CC) $(CFLAGS) -o $@ client/$@.c + +$(RECEIVERS): + $(CC) $(CFLAGS) -o $@ server/$@.c + +$(OFFLINE): + $(CC) $(CFLAGS) -o $@ offline/$@.c + +clean: + rm -f $(SENDERS) $(RECEIVERS) $(OFFLINE) diff --git a/test_tools/multicast/client/send_simple.c b/test_tools/multicast/client/send_simple.c new file mode 100644 index 0000000..2651c36 --- /dev/null +++ b/test_tools/multicast/client/send_simple.c @@ -0,0 +1,59 @@ +/* + * send_simple.c - simple sender setup for multicast tests + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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. + */ + +#define SEND +#include "multicast_utils.h" + +int main(int argc, char** argv) +{ + struct parameters params; + parse_args(argc, argv, ¶ms); + + int sockfd = init_out_socket(¶ms); + + /* Sockopts setup */ + if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, + &(params.loop), sizeof(params.loop)) < 0) { + perror("setsockopt"); + return EXIT_FAILURE; + } + + if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, + &(params.ttl), sizeof(params.ttl)) < 0) { + perror("setsockopt"); + return EXIT_FAILURE; + } + + if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, + &(params.interface), sizeof(params.interface)) < 0) { + perror("setsockopt"); + return EXIT_FAILURE; + } + + int num_sent = 0; + num_sent = send_data(sockfd, params.multiaddr, params.port, + params.duration, params.delay); + + printf("packets_sent=%d\n", num_sent); + + return EXIT_SUCCESS; +} diff --git a/test_tools/multicast/multicast_utils.h b/test_tools/multicast/multicast_utils.h new file mode 100644 index 0000000..25d0c38 --- /dev/null +++ b/test_tools/multicast/multicast_utils.h @@ -0,0 +1,192 @@ +/* + * multicast_utils.h - common tools for kernel multicast tests + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 __MULTICAST_UTILS_H__ +#define __MULTICAST_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 <sys/select.h> + +#include <signal.h> +#include <time.h> + +#include <stdlib.h> +#include <unistd.h> + +#if !defined(SEND) && !defined(RECEIVE) +#error "At least one of SEND/RECEIVE macros must be defined!" +#endif + +#include "parameters.h" + +#define MESSAGE "Hello world!" + +int __verbosity = 0; + +/* Verbose print */ +#define printv(args...) \ + if (__verbosity > 0) \ + { \ + printf(args); \ + fflush(stdout); \ + } + +/** Initiailze socket for receiving multicast data */ +int init_in_socket(struct in_addr multiaddr, short port) +{ + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("socket()"); + exit(EXIT_FAILURE); + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr = multiaddr; + memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero)); + + if (bind(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + perror("bind()"); + exit(EXIT_FAILURE); + } + + return sockfd; +} + +/** Initialize socket for sending multicast data */ +int init_out_socket() +{ + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("socket()"); + exit(EXIT_FAILURE); + } + + return sockfd; +} + +/** Close a socket */ +void free_socket(int sockfd) +{ + close(sockfd); +} + +/** Wait for data up to `duration' seconds */ +int wait_for_data(int sockfd, int duration, int packet_limit) +{ + const char message[] = MESSAGE; + char buffer[] = MESSAGE; + memset(buffer, 0, sizeof(buffer)); + + int num_received = 0; + + fd_set receive_fd_set; + struct timeval timeout; + + time_t deadline = time(NULL) + duration; + + printv("Receiving\n"); + + while (1) { + FD_ZERO(&receive_fd_set); + FD_SET(sockfd, &receive_fd_set); + + if (duration == 0) { + timeout.tv_sec = 5; + timeout.tv_usec = 0; + } else { + time_t now = time(NULL); + if ((deadline - now) <= 0) + break; + + timeout.tv_sec = deadline - now; + timeout.tv_usec = 0; + } + + + if (select(sockfd + 1, &receive_fd_set, NULL, NULL, &timeout) > 0) { + recv(sockfd, buffer, sizeof(buffer), 0); + if (strncmp(message, buffer, sizeof(buffer)) == 0) { + num_received++; + + printv("."); + if (!(num_received % 10)) + printv("\n"); + + if (packet_limit > 0 && num_received > packet_limit) + break; + } + } + } + + printv("\n"); + + return num_received; +} + +/** Send data for specified amount of time */ +int send_data(int sockfd, struct in_addr multiaddr, short port, + int duration, double delay) +{ + const char message[] = MESSAGE; + int i = 0; + + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_addr = multiaddr; + addr.sin_port = htons(port); + memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero)); + + struct timespec delay_value; + delay_value.tv_sec = 0; + delay_value.tv_nsec = delay * 999999999; + + printv("Sending...\n"); + + time_t started_at = time(NULL); + while (duration == 0 || (time(NULL) - started_at) < duration) { + i++; + sendto(sockfd, message, strlen(message), 0, + (struct sockaddr*) &addr, sizeof(addr)); + + printv("."); + if (!(i % 10)) + printv("\n"); + + nanosleep(&delay_value, NULL); + } + + printv("\n"); + + return i; +} + +#endif diff --git a/test_tools/multicast/offline/sockopt_if.c b/test_tools/multicast/offline/sockopt_if.c new file mode 100644 index 0000000..ff5bd51 --- /dev/null +++ b/test_tools/multicast/offline/sockopt_if.c @@ -0,0 +1,70 @@ +/* + * sockopt_if.c - IP_MULTICAST_IF socket option test + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 "sockopt_utils.h" + + +void test_if() +{ + struct in_addr address; + size_t size = sizeof(address); + + address.s_addr = INADDR_ANY; + test_getsockopt("IP_MULTICAST_IF default value", + IP_MULTICAST_IF, &address, size); + + inet_pton(AF_INET, "127.0.0.1", &address); + test_sockopt_value("IP_MULTICAST_IF set to 127.0.0.1", + IP_MULTICAST_IF, &address, size); + + struct ip_mreqn mreqn; + mreqn.imr_multiaddr.s_addr = 0xdeadbeef; + mreqn.imr_address.s_addr = INADDR_ANY; + mreqn.imr_ifindex = 0; + + test_sockopt_value("IP_MULTICAST_IF set to INADDR_ANY", + IP_MULTICAST_IF, &mreqn, sizeof(mreqn)); + + mreqn.imr_address.s_addr = 0x0100007f; + test_sockopt_value("IP_MULTICAST_IF set to 127.0.0.1", + IP_MULTICAST_IF, &mreqn, sizeof(mreqn)); + + + /* Errors */ + test_setsockopt_error("IP_MULTICAST_IF bad optlen", + IP_MULTICAST_IF, &address, 3, EINVAL); + + inet_pton(AF_INET, "238.0.10.0", &address); + test_setsockopt_error("IP_MULTICAST_IF address 238.0.10.0", + IP_MULTICAST_IF, &address, + sizeof(address), EADDRNOTAVAIL); +} + +int main() +{ + initialize(); + + test_if(); + + report_and_exit(); + return 0; +} diff --git a/test_tools/multicast/offline/sockopt_loop.c b/test_tools/multicast/offline/sockopt_loop.c new file mode 100644 index 0000000..fdf7478 --- /dev/null +++ b/test_tools/multicast/offline/sockopt_loop.c @@ -0,0 +1,52 @@ +/* + * sockopt_loop.c - IP_MULTICAST_LOOP socket option test + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 "sockopt_utils.h" + + +void test_loop() +{ + int value; + size_t size = sizeof(value); + + value = 1; + test_getsockopt("IP_MULTICAST_LOOP default value", + IP_MULTICAST_LOOP, &value, size); + + value = 0; + test_sockopt_value("IP_MULTICAST_LOOP set to zero", + IP_MULTICAST_LOOP, &value, size); + + /* Errors */ + test_setsockopt_error("IP_MULTICAST_LOOP bad optlen", + IP_MULTICAST_LOOP, &value, 0, EINVAL); +} + +int main() +{ + initialize(); + + test_loop(); + + report_and_exit(); + return 0; +} diff --git a/test_tools/multicast/offline/sockopt_ttl.c b/test_tools/multicast/offline/sockopt_ttl.c new file mode 100644 index 0000000..293bbe5 --- /dev/null +++ b/test_tools/multicast/offline/sockopt_ttl.c @@ -0,0 +1,79 @@ +/* + * sockopt_ttl.c - IP_MULTICAST_TTL socket option test + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 "sockopt_utils.h" + + +void test_ttl() +{ + int value; + size_t size = sizeof(value); + + value = 1; + test_getsockopt("IP_MULTICAST_TTL default value", + IP_MULTICAST_TTL, &value, size); + + value = 0; + test_sockopt_value("IP_MULTICAST_TTL set to zero", + IP_MULTICAST_TTL, &value, size); + + value = 64; + test_sockopt_value("IP_MULTICAST_TTL set to 64", + IP_MULTICAST_TTL, &value, size); + + value = 255; + test_sockopt_value("IP_MULTICAST_TTL set to 255", + IP_MULTICAST_TTL, &value, size); + + + /* + * Special case: + * For some reason kernel accepts + * TTL = -1 and takes it as if it were 1 + */ + value = -1; + test_setsockopt("IP_MULTICAST_TTL set to -1", + IP_MULTICAST_TTL, &value, size); + + value = 1; + test_getsockopt("IP_MULTICAST_TTL set to 1", + IP_MULTICAST_TTL, &value, size); + + + /* Errors */ + value = 500; + test_setsockopt_error("IP_MULTICAST_TTL set to 500", + IP_MULTICAST_TTL, &value, size, EINVAL); + + test_setsockopt_error("IP_MULTICAST_TTL bad optlen", + IP_MULTICAST_TTL, &value, 0, EINVAL); +} + +int main() +{ + initialize(); + + test_ttl(); + + report_and_exit(); + return 0; +} diff --git a/test_tools/multicast/parameters.h b/test_tools/multicast/parameters.h new file mode 100644 index 0000000..6a313ec --- /dev/null +++ b/test_tools/multicast/parameters.h @@ -0,0 +1,152 @@ +/* + * parameters.h - common code for parsing sender/receiver parameters + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 <stdio.h> +#include <string.h> +#include <errno.h> + +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <signal.h> +#include <time.h> + +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> + +extern int __verbosity; + +/** Structure that carries test parameters */ +struct parameters +{ + int duration; /* seconds */ + + struct in_addr multiaddr; + short port; + struct in_addr interface; + +#ifdef RECEIVE + struct in_addr sourceaddr; +#endif + +#ifdef SEND + double delay; + int ttl; + int loop; +#endif +}; + +/** Initialize parameters struct with default values. */ +void default_parameters(struct parameters* params) +{ + params->duration = 10; + params->port = 0; + memset(¶ms->multiaddr, 0, sizeof(struct in_addr)); + memset(¶ms->interface, 0, sizeof(struct in_addr)); +#ifdef RECEIVE + memset(¶ms->sourceaddr, 0, sizeof(struct in_addr)); +#endif + +#ifdef SEND + params->delay = 0.1; + params->ttl = 1; + params->loop = 1; +#endif +} + +/** 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"; +#endif + +#ifdef RECEIVE + static const char* opts = "d:a:p:s:i:v"; +#endif + + 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'}, +#endif + +#ifdef SEND + {"delay", required_argument, NULL, 'f'}, + {"ttl", required_argument, NULL, 't'}, + {"loop", required_argument, NULL, 'l'}, +#endif + {0, 0, NULL, 0} + }; + + default_parameters(args); + + int opt; + int option_index = 0; + while((opt = getopt_long(argc, argv, opts, long_options, + &option_index)) != -1) { + switch (opt) { + case 'd': + args->duration = atoi(optarg); + break; + case 'a': + inet_pton(AF_INET, optarg, &(args->multiaddr)); + break; + case 'p': + args->port = atoi(optarg); + break; + case 'i': + inet_pton(AF_INET, optarg, &(args->interface)); + break; + case 'v': + __verbosity = 1; + break; +#ifdef RECEIVE + case 's': + inet_pton(AF_INET, optarg, &(args->sourceaddr)); + break; +#endif + +#ifdef SEND + case 'f': + args->delay = atof(optarg); + break; + case 't': + args->ttl = atoi(optarg); + break; + case 'l': + args->loop = atoi(optarg); + break; +#endif + default: /* '?' */ + printf("%s: invalid test options\n", argv[0]); + exit(EXIT_FAILURE); + } + } +} diff --git a/test_tools/multicast/server/recv_simple.c b/test_tools/multicast/server/recv_simple.c new file mode 100644 index 0000000..c4d045c --- /dev/null +++ b/test_tools/multicast/server/recv_simple.c @@ -0,0 +1,50 @@ +/* + * recv_simple.c - simple receiver setup for multicast tests + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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. + */ + +#define RECEIVE +#include "multicast_utils.h" + +int main(int argc, char** argv) +{ + struct parameters params; + parse_args(argc, argv, ¶ms); + + int sockfd = init_in_socket(params.multiaddr, params.port); + + struct ip_mreq mreq; + mreq.imr_multiaddr = params.multiaddr; + mreq.imr_interface = params.interface; + + int num_recv = 0; + + if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &(mreq), sizeof(mreq)) < 0) { + perror("setsockopt"); + return EXIT_FAILURE; + } + + num_recv = wait_for_data(sockfd, params.duration, 0); + + printf("packets_received=%d\n", num_recv); + + return EXIT_SUCCESS; +} diff --git a/test_tools/multicast/sockopt_utils.h b/test_tools/multicast/sockopt_utils.h new file mode 100644 index 0000000..6486f71 --- /dev/null +++ b/test_tools/multicast/sockopt_utils.h @@ -0,0 +1,175 @@ +/* + * sockopt_utils.h - common tools for writing sockopt conformance tests + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Radek Pazdera (rpazdera@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 __SOCKOPT_UTILS_H__ +#define __SOCKOPT_UTILS_H__ + +#define __SUCCESS_CODE 0 +#define __FAILURE_CODE 1 + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +int __test_status = 1; +int __sockfd; + +void fail() +{ + __test_status = 0; +} + +void report_and_exit() +{ + close(__sockfd); + + if (__test_status) { + printf("status=pass\n"); + exit(__SUCCESS_CODE); + } else { + printf("status=fail\n"); + exit(__FAILURE_CODE); + } +} + +void error_exit(char* what) +{ + int code = errno; + printf("error_message=%s %s\n", what, strerror(code)); + fail(); + report_and_exit(); +} + +void initialize() +{ + __sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (__sockfd < 0) + error_exit("socket()"); +} + +void test_getsockopt(char* test_name, int optname, void *expected_optval, + socklen_t expected_optlen) +{ + int status; + socklen_t size = expected_optlen; + void* value = malloc(size); + + if (value == NULL) + error_exit("malloc()"); + + memset(value, 0, size); + + status = getsockopt(__sockfd, IPPROTO_IP, optname, value, &size); + if (status < 0) + error_exit("getsockopt()"); + + if (expected_optlen != size) { + printf("%s=fail: size of the returned struct differ\n", test_name); + fail(); + } else { + socklen_t i; + for (i = 0; i < size; i++) + if (((char*) value)[i] != ((char*) expected_optval)[i]) + { + free(value); + printf("%s=fail: received value of the option " + "differs from expected one\n", test_name); + fail(); + return; + } + + printf("%s=pass\n", test_name); + } + + free(value); +} + + +void test_setsockopt(char* test_name, int optname, void *optval, + socklen_t optlen) +{ + int status; + + status = setsockopt(__sockfd, IPPROTO_IP, optname, optval, optlen); + if (status < 0) + error_exit("setsockopt()"); + + printf("%s=pass\n", test_name); +} + +void test_sockopt_value(char* test_name, int optname, + void *optval, socklen_t optlen) +{ + test_setsockopt(test_name, optname, optval, optlen); + test_getsockopt(test_name, optname, optval, optlen); +} + +void test_setsockopt_error(char* test_name, int optname, void *optval, + socklen_t optlen, int expected_errorcode) +{ + int status; + + status = setsockopt(__sockfd, IPPROTO_IP, optname, optval, optlen); + if (status < 0) { + if (errno != expected_errorcode) { + printf("%s=fail: error codes don't " + "match (expected %d, got %d)\n", test_name, + expected_errorcode, errno); + fail(); + } else { + printf("%s=pass\n", test_name); + } + } else { + printf("%s=fail: no error occured\n", test_name); + fail(); + } +} + +void test_getsockopt_error(char* test_name, int optname, void *optval, + socklen_t optlen, int expected_errorcode) +{ + int status; + socklen_t size = optlen; + + status = getsockopt(__sockfd, IPPROTO_IP, optname, optval, &size); + if (status < 0) { + if (errno != expected_errorcode) { + printf("%s=fail: error codes don't " + "match (expected %d, got %d)\n", test_name, + expected_errorcode, errno); + fail(); + } else { + printf("%s=pass\n", test_name); + } + } else { + printf("%s=fail: no error occured\n", test_name); + fail(); + } +} + +#endif
From: Radek Pazdera rpazdera@redhat.com
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- example_recipes/rpazdera-multicast.xml | 38 ++++++++++++++++++++++++++++++++ 1 files changed, 38 insertions(+), 0 deletions(-) create mode 100644 example_recipes/rpazdera-multicast.xml
diff --git a/example_recipes/rpazdera-multicast.xml b/example_recipes/rpazdera-multicast.xml new file mode 100644 index 0000000..9a90168 --- /dev/null +++ b/example_recipes/rpazdera-multicast.xml @@ -0,0 +1,38 @@ +<!-- Example test scenario using multicast test tools --> +<nettestrecipe> + <machines> + <machine id="1"> + <netmachineconfig> + <info hostname="10.34.1.120" rootpass="redhat" /> + <netdevice id="1" type="eth" phys_id="1" hwaddr="52:54:00:2E:D5:A8"/> + <netdevice id="2" type="eth" phys_id="2" hwaddr="52:54:00:90:23:0B"/> + </netmachineconfig> + <netconfig> + <netdevice id="1" type="eth" phys_id="1"> + <addresses> + <address value="192.168.122.237/24" /> + </addresses> + </netdevice> + <netdevice id="2" type="eth" phys_id="2" /> + </netconfig> + </machine> + <machine id="2"> + <netmachineconfig> + <info hostname="10.34.1.171" rootpass="redhat" /> + <netdevice id="1" type="eth" phys_id="1" hwaddr="52:54:00:12:DF:5A"/> + </netmachineconfig> + <netconfig> + <netdevice id="1" type="eth" phys_id="1"> + <addresses> + <address value="192.168.122.179/24" /> + </addresses> + </netdevice> + </netconfig> + </machine> + </machines> + + <command_sequence source="cmd_sequences/multicast/if.xml" /> + <command_sequence source="cmd_sequences/multicast/ttl.xml" /> + <command_sequence source="cmd_sequences/multicast/loop.xml" /> + <command_sequence source="cmd_sequences/multicast/simple.xml" /> +</nettestrecipe>
Patchset applied, Thank you very much Radek!
Thu, Apr 05, 2012 at 02:56:38PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This test integratest multicast test tools into LNST and makes it possible to run and evaluate them from the recipes.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
Tests/TestMulticast.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 128 insertions(+), 0 deletions(-) create mode 100644 Tests/TestMulticast.py
diff --git a/Tests/TestMulticast.py b/Tests/TestMulticast.py new file mode 100644 index 0000000..c757bca --- /dev/null +++ b/Tests/TestMulticast.py @@ -0,0 +1,128 @@ +""" +Wrapper for executing the multicast test tools in LNST
+Copyright 2012 Red Hat, Inc. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +"""
+__author__ = """ +rpazdera@redhat.com (Radek Pazdera) +"""
+import logging +import re +from Common.TestsCommon import TestGeneric +from NetTest.NetTestCommand import CommandException +from Common.ExecCmd import exec_cmd
+class TestMulticast(TestGeneric):
- """ Wrapper for executing the multicast test tools in LNST
- Running this test executes specified test setup from
- multicast tools and evaluates the results. The behavior
- is defined entirely by behavior of the selected setup.
- """
- _tools_location = "test_tools/multicast/"
- _conditions = {}
- @staticmethod
- def _remove_mask(address_in_string):
""" Remove mask suffix from the IP address """if address_in_string != None:return address_in_string.split('/')[0]else:return address_in_string- def _compose_cmd(self):
""" Setup a command from the recipe options """cmd = ""opts = {}setup = self._tools_location + self.get_mopt("setup")opts["multicast_address"] = self._remove_mask(self.get_opt("address"))opts["port"] = self.get_opt("port")opts["interface"] = self._remove_mask(self.get_opt("interface"))opts["duration"] = self.get_opt("duration")# sender-specificopts["loop"] = self.get_opt("loop")opts["ttl"] = self.get_opt("ttl")opts["delay"] = self.get_opt("delay")# receiver-specificopts["source"] = self._remove_mask(self.get_opt("source"))cmd = "./{0} ".format(setup)for optname, optval in opts.iteritems():if optval != None:cmd += "--{0} \"{1}\" ".format(optname, optval)return cmd- def _evaluate_result(self, name, value):
""" Check if the result meets required conditions """if name in self._conditions:try:float(value)except ValueError:value = '"' + value + '"'result = eval(value + self._conditions[name])logging.info("Condition evaluated {2}: {0}{1}".format(name,self._conditions[name], str(result)))return resultelse:return True- def _prepare_conditions(self):
""" Search for var names in conditions """varname_r = r"[a-zA-Z_][a-zA-Z0-9_]*"conds = self.get_multi_opt("condition")logging.debug(conds)for cond in conds:if cond == None:continuelogging.debug(cond)match = re.match(varname_r, cond)if match:name = match.group(0)self._conditions[name] = cond.replace(name, "")else:raise CommandException(self)- def run(self):
self._prepare_conditions()setup_name = self.get_mopt("setup")logging.info("Started Multicast test setup {0}".format(setup_name))exec_cmd("cd " + self._tools_location + " && make")cmd = self._compose_cmd()data_stdout = exec_cmd(cmd, die_on_err=False)[0]res = {}# line format matches name=value pairs with optional# double quotes around the valueline_format_r = r"([a-zA-Z0-9_ ]+)=\"?([a-zA-Z0-9_ ]*)\"?"for line in data_stdout.split("\n"):match = re.search(line_format_r, line)if match:name = match.group(1).strip()value = match.group(2).strip()res[name] = valuelogging.info("Test result: {0} = {1}".format(name, value))if not self._evaluate_result(name, value):return self.set_fail("Conditions not met!", res)return self.set_pass(res)-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/lnst-developers
lnst-developers@lists.fedorahosted.org