summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrazb <razb@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>2005-04-09 16:30:15 +0000
committerrazb <razb@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>2005-04-09 16:30:15 +0000
commitdefe6d19541ec095099cf41c411ce6daff70a176 (patch)
tree88b5b45ad82f7e7a7f1d9f3f6c4abc2192bac217
parentefbee1258c904d741f9bebe0934d4d315d163471 (diff)
downloadATCD-NetlinkSockets.tar.gz
*** empty log message ***NetlinkSockets
-rw-r--r--tests/GNUmakefile.SOCK_Netlink_Test136
-rw-r--r--tests/SOCK_Netlink_Test.cpp980
2 files changed, 1116 insertions, 0 deletions
diff --git a/tests/GNUmakefile.SOCK_Netlink_Test b/tests/GNUmakefile.SOCK_Netlink_Test
new file mode 100644
index 00000000000..77646ec457a
--- /dev/null
+++ b/tests/GNUmakefile.SOCK_Netlink_Test
@@ -0,0 +1,136 @@
+# -*- Makefile -*-
+#----------------------------------------------------------------------------
+# GNU Makefile
+#
+# @file GNUmakefile.SOCK_Netlink_Test
+#
+# gnu.mpd,v 1.119 2004/12/16 16:07:21 elliott_c Exp
+#
+# This file was automatically generated by MPC. Any changes made directly to
+# this file will be lost the next time it is generated.
+#
+#----------------------------------------------------------------------------
+MAKEFILE = GNUmakefile.SOCK_Netlink_Test
+DEPENDENCY_FILE = .depend.SOCK_Netlink_Test
+BIN_UNCHECKED = SOCK_Netlink_Test
+
+FILES = \
+ $(ACE_ROOT)/tests/Main \
+ SOCK_Netlink_Test
+
+VPATH = .:$(ACE_ROOT)/tests
+
+#----------------------------------------------------------------------------
+# Include macros and targets
+#----------------------------------------------------------------------------
+LDLIBS = -lTest_Output -lACE
+
+ifeq ($(INSBIN),.)
+ ifeq ($(PWD),)
+ PWD=$(shell pwd)
+ endif
+ INSBIN = $(PWD)
+endif
+OUTPUT_DIRECTORY = $(INSBIN)
+
+include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU
+## We don't need the ACELIB setting from wrapper_macros.GNU
+ACELIB =
+
+ifeq ($(versioned_so),1)
+SOVERSION = .5.4.3
+endif
+
+# To build multiple targets in the same directory on AIX, it works
+# best to have a template directory per project.
+# The compiler/linker isn't too smart about instantiating templates...
+ifdef TEMPINCDIR
+TEMPINCDIR := $(TEMPINCDIR)/SOCK_Netlink_Test
+
+all: $(TEMPINCDIR)
+
+endif
+
+ifneq ($(OUTPUT_DIRECTORY),)
+all: $(OUTPUT_DIRECTORY)
+
+$(OUTPUT_DIRECTORY):
+ -@$(MKDIR) "$(OUTPUT_DIRECTORY)"
+endif
+
+# turn off libcheck if doing a dry run
+ifeq ($(findstring n, $(MAKEFLAGS)),n)
+ LIBCHECK = 1
+else
+ # turn off libcheck if keep going was passed too
+ ifeq ($(findstring k, $(MAKEFLAGS)),k)
+ LIBCHECK = 1
+ else
+ LIBCHECK ?= $(filter-out $(foreach lib,Test_Output ACE,$(findstring $(lib),$(foreach libpath,$(ACE_ROOT)/lib /usr/lib $(INSLIB),$(wildcard $(libpath)/lib$(lib).* $(libpath)/$(lib).lib)))),Test_Output ACE)
+ ifeq ($(LIBCHECK),)
+ LIBCHECK = 1
+ endif
+ endif
+endif
+ifeq ($(LIBCHECK), 1)
+BIN = $(BIN_UNCHECKED)$(EXEEXT)
+else
+ all: lib_warning
+endif
+
+OBJS = $(addsuffix .$(OBJEXT), $(notdir $(FILES)))
+SRC = $(addsuffix .cpp, $(FILES))
+ifneq (,$(RC))
+ FILES += $(RESOURCES)
+endif
+
+
+include $(ACE_ROOT)/include/makeinclude/macros.GNU
+include $(ACE_ROOT)/include/makeinclude/rules.common.GNU
+include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU
+
+ifneq ($(OUTPUT_DIRECTORY),)
+ifneq ($(OUTPUT_DIRECTORY),.)
+ INSTALL = $(VBIN:%=$(INSBIN)/%)
+ CLEANUP_INSTALL += $(CLEANUP_BIN:%=$(INSBIN)/%$(VAR)$(EXEEXT))
+endif
+endif
+
+include $(ACE_ROOT)/include/makeinclude/rules.local.GNU
+ifeq ($(VXWORKSLINK),true)
+include $(TGT_DIR)/h/make/rules.$(PRJ_TYPE)
+endif
+
+ifeq ($(VXWORKSLINK),true)
+LDLIBPATH = -L$(ACE_ROOT)/lib
+else
+LDFLAGS += -L$(ACE_ROOT)/lib
+endif
+CPPFLAGS += -I$(ACE_ROOT)
+ifeq ($(static_libs),1)
+ CPPFLAGS += -DACE_AS_STATIC_LIBS
+endif
+
+#----------------------------------------------------------------------------
+# Local targets
+#----------------------------------------------------------------------------
+lib_warning:
+ @echo This project will not be built due to the following missing library:
+ @echo $(LIBCHECK)
+
+
+ifneq ($(VXWORKSLINK),true)
+ifndef kylix
+$(BIN): $(addprefix $(VDIR), $(OBJS))
+ $(LINK.cc) $(LDFLAGS) $(CC_OUTPUT_FLAG) $@ $^ $(VLDLIBS) $(POSTLINK)
+else
+$(BIN): $(addprefix $(VDIR), $(OBJS))
+ $(LINK.cc) $(LDFLAGS) $(CC_OUTPUT_FLAG) $(VLDLIBS) $(BORINITEXEOBJ) $(POSTLINK) $^, $@,,
+endif
+endif
+
+realclean: clean
+ifneq ($(GENERATED_DIRTY),)
+ -$(RM) -r $(GENERATED_DIRTY)
+endif
+
diff --git a/tests/SOCK_Netlink_Test.cpp b/tests/SOCK_Netlink_Test.cpp
new file mode 100644
index 00000000000..dfeac0ade01
--- /dev/null
+++ b/tests/SOCK_Netlink_Test.cpp
@@ -0,0 +1,980 @@
+// $Id$
+// SOCK_Netlink_Test.cpp,v 4.4.4 2004/08/24
+// ========================================================//
+//
+// = LIBRARY
+// tests
+//
+// = FILENAME
+// SOCK_Netlink_Test.cpp
+//
+// = DESCRIPTION
+// Tests adding of a secondary IP-address, using linux netlink
+// sockets.
+//
+// = AUTHOR
+// Robert Iakobashvili (roberti@gonetworks.com, roberti@walla.co.il)
+//
+// =========================================================//
+
+#include "test_config.h"
+
+#if defined (ACE_HAS_LINUX_NETLINK) && (ACE_HAS_LINUX_NETLINK == 1)
+
+#include "ace/Event_Handler.h"
+#include "ace/Reactor.h"
+#include "ace/Log_Msg.h"
+#include "ace/Get_Opt.h"
+
+#include "ace/Netlink_Addr.h"
+#include "ace/SOCK_Netlink.h"
+
+#ifdef __linux__
+#include <linux/rtnetlink.h>
+#endif /* __linux__ */
+
+/**
+ * NETLINK SOCKET INTERFACE is a socket API communication
+ * between linux kernel and userland.
+ *
+ * Main usage of netlink communication is for communication between
+ * kernel/custom modules/drivers and userspace;
+ *
+ * In order not to force ACE-tests runners to install some driver for testing
+ * purposes, we are using here a build-in netlink interface used by linux for
+ * "routing" purposes (rtnetlink).
+ *
+ * This test is hopefully a useful example of how via netlink to add a new
+ * secondary IPv4 address to an interface and to delete it further. The
+ * example is integrated within reactive framework for IO demultiplexing.
+ *
+ * A one button test adds a new secondary IP-address to a loopback
+ * interface and deletes it. Even if the address added remains, this shall no
+ * cause any damage. In any case the lifetime of a secondary ip is limited
+ * till the next reboot. You may wish to run the test with -d option (not to
+ * make an address cleanup after addtion) and see it by 'ip addr' command.
+ * Further re-run of the test without -d option will cleanup the address.
+ *
+ * The one-button test fails, of there is no a loopback device named "lo" on a
+ * host or the device is disabled.
+ *
+ * The same rtnetlink interface may be used to get/add/delete ip-addresses,
+ * get/add/delete routing rules, control ARP entires, control Qdisk disciplines,
+ * traffic classes and traffic filters, manage network interface configuration
+ *
+ * For more information, please, read man pages:
+ * netlink (3), netlink (7), rtnetlink (3), rtnetlink (3), rtnetlink (7) and ip (8).
+ *
+ * Some ideas for the test were borrowed from the code of iprouted2,
+ * written by Alexey Kuznetsov.
+ *
+ * Command line options:
+ *
+ * -d do not cleanup the ip-address after addition (so that you can see it by the
+ * 'ip addr' command)
+ *
+ * -i the name of interface to add the address
+ *
+ * -a ipv4 slash netmask bits, like "192.168.1.1/24"
+ *
+
+From Linux headers:
+
+// Generic structure for encapsulation of optional route information.
+// It is reminiscent of sockaddr, but with sa_family replaced with attribute type.
+struct rtattr
+{
+ unsigned short rta_len;
+ unsigned short rta_type;
+};
+
+//Interface address.
+struct ifaddrmsg
+{
+unsigned char ifa_family;
+unsigned char ifa_prefixlen; // The prefix length is the length of the address mask
+unsigned char ifa_flags; // Flags: IFA_F_SECONDARY for secondary
+ // address (old alias interface), IFA_F_PERMANENT
+ // for a permanent address
+unsigned char ifa_scope; // locality
+int ifa_index; // Link index is the interface index in the table of interfaces.
+};
+
+struct nlmsghdr
+{
+__u32 nlmsg_len; // Length of message including header
+__u16 nlmsg_type; // Message content
+__u16 nlmsg_flags; // Additional flags
+__u32 nlmsg_seq; // Sequence number
+__u32 nlmsg_pid; // Sending process PID
+};
+*/
+
+
+// The global config params
+//
+static int one_button_test = 0;
+static char ip_slash_mask[32];
+static char net_dev_name[16];
+static int dont_cleanup_added_ip = 0;
+
+
+// The function returns index of an interface by its name
+//
+int
+get_if_index (const char*const interface,
+ int &if_index)
+{
+ if_index = -1;
+
+ struct ifreq if_req;
+ ACE_OS::memset (&if_req, 0, sizeof (struct ifreq));
+
+ ACE_OS::strncpy (if_req.ifr_name,
+ (const char*) interface,
+ sizeof (if_req.ifr_name));
+
+ ACE_HANDLE s = ACE_OS::socket (AF_INET,SOCK_DGRAM,0);
+
+ if (s == ACE_INVALID_HANDLE)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ ACE_LIB_TEXT ("%p\n"),
+ ACE_LIB_TEXT ("get_if_index - failed on\n")
+ ACE_LIB_TEXT ("ACE_OS::socket")),
+ -1);
+
+ int result = ACE_OS::ioctl (s,
+ SIOCGIFINDEX,
+ (char*)&if_req);
+ if (result == -1)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_LIB_TEXT ("%p\n"),
+ ACE_LIB_TEXT ("get_if_index:")
+ ACE_LIB_TEXT ("ioctl (get interface index)")));
+ }
+ else
+ {
+ if_index = if_req.ifr_ifindex;
+ }
+ return result;
+}
+
+/**
+ An assisting structure for passing data via routing netlink socket.
+ The idea borrowed from a great iprouted2 utility,
+ written by Alexey Kuznetsov.
+*/
+struct Inet_Prefix
+{
+ u_char family;
+ u_char bytelen;
+ ACE_INT16 bitlen;
+ ACE_UINT32 data[4];
+};
+
+
+/**
+ * Contains header netlink message header of a type nlmsghdr with a
+ * following request type dependent controlling structure, which is for
+ * adding/deleting IP-addresses is of type ifaddrmsg with a following
+ * buffer, containing the data: ip-address, its length, number of netmask
+ * bits, etc.
+ */
+struct Netlink_Request
+{
+ struct nlmsghdr nhdr_; // message header
+ struct ifaddrmsg ifa_; // interface
+ char buf_[256];
+};
+
+
+
+/**
+ * The handler first is trying to delete an ip-address, further
+ * to add the ip and, if successful to cleanup the address.
+*/
+class Secondary_Ipaddr_Handler : public ACE_Event_Handler
+{
+public:
+
+ // Default constructor
+ Secondary_Ipaddr_Handler (void);
+
+ // Destructor
+ virtual ~Secondary_Ipaddr_Handler (void);
+
+ // Initialization. Schedules a timer to run start the business.
+ //
+ int open (ACE_Reactor *const reactor,
+ char* const ip_slash_mask,
+ const char *const if_name);
+
+ // Returns reference to netlink socket. Necessary for reactor.
+ virtual ACE_HANDLE get_handle (void) const;
+
+ /**
+ * Takes care of the input. Reads the incoming messages,
+ * makes their processing.
+ */
+ virtual int handle_input (ACE_HANDLE handle);
+
+ // Makes clean-up
+ virtual int handle_close (ACE_HANDLE handle,
+ ACE_Reactor_Mask close_mask);
+
+ // Runs a state machine. Controls adding/deleting of ip-address.
+ int handle_timeout (ACE_Time_Value const & tv,
+ void const * arg = 0);
+
+ // Sends to kernel a request to add secondary ip/mask to an
+ // interface.
+ int add_ip (char* const ip_slash_mask,
+ const char *const if_name);
+
+ // Sends to kernel a request to delete secondary ip/mask
+ // from an interface.
+ int delete_ip (char* const ip_slash_mask,
+ const char *const if_name);
+
+ /*
+ * 1. We are trying to delete the ip-address, if exists.
+ * Shall be either successful, or fail, when no-ip yet.
+ * 2. Adding the ip-address, shall be successful;
+ * 3. Cleaning up the ip-address. Shall be successful as well.
+ */
+ enum
+ {
+ START = 0,
+ IP_DELETED,
+ IP_ADDED,
+ IP_CLEANUPED,
+ SUCCESS,
+ FAILED
+ };
+
+ // Returns the currect state
+ int get_state () const { return this->state_;}
+
+protected:
+
+ // De-registers the handler from the reactor,
+ // other cleanup jobs
+ virtual int close ();
+
+ ACE_SOCK_Netlink& socket ();
+
+private:
+
+ // Schedule two sec timer.
+ int schedule_one_sec_timer ();
+
+ // A workhorse for add_ip () and delete_ip ()
+ int dispatch_ip_operation (char* const ip_slash_mask,
+ const char *const if_name,
+ bool action);
+
+ /**
+ * Initializes netlink request for adding (action = true) or
+ * deleting (action = false) of a secondary ip-address/mask.
+ */
+ int init_netlink_request (char* const ip_slash_mask,
+ const char *const if_name,
+ Netlink_Request& net_req,
+ bool action);
+
+ // Fills data part of Netlink_Request.
+ int fill_inet_prefix (Inet_Prefix &inet_prefix,
+ const char*const ip_slash_netmask);
+
+ /**
+ * Fills routing request (operations with ip-addresses are
+ * a part of netlink routing interface).
+ */
+ void fill_rtnetlink_request (nlmsghdr &hdr,
+ int type,
+ void *data,
+ int data_length);
+
+ enum
+ {
+ COMMAND_TIMEOUT = -3,
+ COMMAND_RECV_ERROR = -2,
+ COMMAND_ERROR = -1,
+ COMMAND_SUCCESS = 0
+ };
+
+ // Mark command status in handle_input ().
+ void on_recv_error () { this->command_status_ = COMMAND_RECV_ERROR; }
+ void on_command_success () { this->command_status_ = COMMAND_SUCCESS; }
+ void on_command_error () { this->command_status_ = COMMAND_ERROR; }
+
+ // The socket.
+ ACE_SOCK_Netlink socket_ ;
+
+ // The address of the socket.
+ ACE_Netlink_Addr address_ ;
+
+ // Message sequence number.
+ ACE_UINT32 seq_ ;
+
+ // The request structure passed to kernel.
+ Netlink_Request netlink_request_;
+
+ // ip-addr-slash-mask
+ char ip_buff_[32];
+
+ // interface
+ char if_buff_[16];
+
+ // Buffer to receive messages. Not too large?
+ char recv_buff_[1024];
+
+ // The state of the handler.
+ int state_;
+
+ // The status of the command.
+ int command_status_;
+};
+
+Secondary_Ipaddr_Handler::Secondary_Ipaddr_Handler ()
+ :
+ socket_ (),
+ address_ (),
+ seq_ (0),
+ state_ (START),
+ command_status_ (COMMAND_SUCCESS)
+{
+}
+
+Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler ()
+{
+ ACE_DEBUG ((LM_DEBUG,
+ "(%P|%t) Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler \n"));
+ this->close ();
+}
+int
+Secondary_Ipaddr_Handler::open (ACE_Reactor *const reactor,
+ char* const ip_slash_mask,
+ const char *const if_name)
+{
+ if (!reactor ||
+ !ip_slash_mask || !ACE_OS::strlen (ip_slash_mask) ||
+ !if_name || !ACE_OS::strlen (if_name))
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::open: error "
+ "zero pointers or zero length strings used as input. \n"),
+ -1);
+
+ this->reactor (reactor);
+
+ // Another option is to pass a zero pid to the address, to call open () on socket
+ // performing binding and after bind () to call getsockbyname to fill the address
+
+ this->address_.set (ACE_OS::getpid (), 0);
+
+ if (this->socket ().open (this->address_,
+ ACE_PROTOCOL_FAMILY_NETLINK,
+ NETLINK_ROUTE) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P|%t) Secondary_Ipaddr_Handler::open: - failed \n"
+ "to initialize netlink socket bu open (). \n"),
+ -1);
+
+ // register with the reactor for input
+ if (this->reactor ()->register_handler (this,
+ ACE_Event_Handler::READ_MASK) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P|%t) Secondary_Ipaddr_Handler::open - "
+ "can't register with reactor for handling input.\n"),
+ -1);
+
+ if (this->reactor ()->schedule_timer (this,
+ 0,
+ ACE_Time_Value::zero) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::open - "
+ "can't schedule timer with reactor.\n"),
+ -1);
+
+ this->seq_ = time (NULL);
+
+ ACE_OS::strncpy (this->ip_buff_,
+ ip_slash_mask,
+ sizeof this->ip_buff_);
+
+ ACE_OS::strncpy (this->if_buff_,
+ if_name,
+ sizeof this->if_buff_);
+
+ return 0;
+}
+
+ACE_HANDLE
+Secondary_Ipaddr_Handler::get_handle (void) const
+{
+ return this->socket_.get_handle ();
+}
+
+int
+Secondary_Ipaddr_Handler::handle_input (ACE_HANDLE handle)
+{
+ ACE_UNUSED_ARG (handle);
+
+ ACE_DEBUG ((LM_DEBUG,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - entered \n"));
+
+ nlmsghdr *hdr = 0;
+ iovec iov;
+
+ iov.iov_base = this->recv_buff_;
+ iov.iov_len = sizeof (this->recv_buff_);
+
+ int rval_bytes = -1;
+ ACE_Netlink_Addr raddr;
+ raddr.set (0, 0);
+
+ rval_bytes = this->socket ().recv (&iov, 1, raddr);
+ switch (rval_bytes)
+ {
+ case -1: // Complain and leave
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - "
+ "%p bad read\n", "client"),
+ -1);
+
+ case 0: // Complain and leave
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - "
+ "eof, closing daemon (fd = %d)\n", this->get_handle ()),
+ -1);
+
+ default:
+ if (raddr.get_size () != sizeof (sockaddr_nl))
+ {
+ this->on_recv_error ();
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%n %P) Secondary_Ipaddr_Handler::handle_input - "
+ "address length not equal sockaddr_nl\n"),
+ -1);
+ }
+
+ hdr = (nlmsghdr*) this->recv_buff_;
+
+ if ((int)hdr->nlmsg_len != rval_bytes)
+ {
+ this->on_recv_error ();
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - "
+ "size of nlmsg_len not equal received bytes\n"),
+ -1);
+ }
+ if ((int)hdr->nlmsg_pid != this->address_.get_pid () || hdr->nlmsg_seq != this->seq_)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - "
+ "process id or message sequence is different \n"),
+ -1);
+ if (hdr->nlmsg_type == NLMSG_ERROR)
+ {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(hdr);
+
+ errno = -err->error;
+ if (errno == 0)
+ {
+ this->on_command_success ();
+ ACE_DEBUG ((LM_DEBUG,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - command success \n"));
+ return 0;
+ }
+
+ this->on_command_error ();
+ ACE_DEBUG ((LM_DEBUG,
+ "(%P) Secondary_Ipaddr_Handler::handle_input - command informs about error \n"));
+
+ // some error message
+ perror("rtnetlink error message: ");
+
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+Secondary_Ipaddr_Handler::handle_timeout (
+ ACE_Time_Value const & tv,
+ void const * arg)
+{
+ ACE_UNUSED_ARG (tv);
+ ACE_UNUSED_ARG (arg);
+
+ ACE_DEBUG ((LM_DEBUG,
+ "(%P) Secondary_Ipaddr_Handler::handle_timeout - entered \n"));
+
+ if (this->command_status_ != COMMAND_SUCCESS &&
+ (this->command_status_ != COMMAND_ERROR &&
+ this->state_ == IP_DELETED))
+ {
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_timeout - "
+ "previous command failed\n"),
+ -1);
+ }
+ else
+ this->command_status_ = COMMAND_TIMEOUT; //invalidate command status
+
+ switch (this->state_)
+ {
+ case START: //delete the ip, if it presents
+ if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1)
+ {
+ this->state_ = FAILED;
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_timeout - "
+ "delete_ip failed\n"),
+ -1);
+ }
+ break;
+
+ case IP_DELETED: // add the ip
+ if (this->add_ip (this->ip_buff_, this->if_buff_) == -1)
+ {
+ this->state_ = FAILED;
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_timeout - "
+ "add_ip failed\n"),
+ -1);
+ }
+ break;
+
+ case IP_ADDED: //delete added ip to make cleanup
+ if (dont_cleanup_added_ip)
+ {
+ this->state_ = SUCCESS;
+ return 0;
+ }
+ else
+ {
+ if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1)
+ {
+ this->state_ = FAILED;
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::handle_timeout - "
+ "delete_ip failed\n"),
+ -1);
+ }
+ }
+ break;
+
+ case IP_CLEANUPED:
+ this->state_ = SUCCESS;
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ this->schedule_one_sec_timer ();
+ this->state_++;
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::handle_close (ACE_HANDLE handle,
+ ACE_Reactor_Mask close_mask)
+{
+ ACE_UNUSED_ARG (handle);
+ ACE_UNUSED_ARG (close_mask);
+
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) Secondary_Ipaddr_Handler::handle_close \n"));
+ this->close ();
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::close ()
+{
+ ACE_DEBUG ((LM_DEBUG, "(%P) Secondary_Ipaddr_Handler::close \n"));
+
+ if (this->reactor ())
+ {
+ this->reactor ()->remove_handler (this,
+ ACE_Event_Handler::ALL_EVENTS_MASK |
+ ACE_Event_Handler::DONT_CALL);
+
+ this->reactor ()->cancel_timer (this);
+
+ this->reactor (0);
+ }
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::schedule_one_sec_timer ()
+{
+ const ACE_Time_Value one_sec (1, 0);
+
+ if (this->reactor ()->schedule_timer (this,
+ 0,
+ one_sec) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::schedule_one_sec_timer - "
+ "can't schedule timer with reactor.\n"),
+ -1);
+ return 0;
+}
+
+ACE_SOCK_Netlink&
+Secondary_Ipaddr_Handler::socket ()
+{
+ return this->socket_;
+}
+
+int
+Secondary_Ipaddr_Handler::fill_inet_prefix (
+ Inet_Prefix &inet_prefix,
+ const char*const ip_slash_netmask)
+{
+ ACE_OS::memset (&inet_prefix, 0, sizeof inet_prefix);
+ char* slash = (char *) ACE_OS::strchr (ip_slash_netmask, '/');
+ if (slash)
+ *slash = '\0'; // The idea from Igor Potulnitsky
+
+ inet_prefix.family = AF_INET; // another option is AF_UNSPEC
+ inet_prefix.bytelen = 4;
+ inet_prefix.bitlen = -1;
+
+ char ip_buff[32];
+ ACE_OS::strncpy (ip_buff, ip_slash_netmask, sizeof (ip_buff));
+
+ char* to_search = ip_buff, *dot = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (i < 3)
+ {
+ dot = ACE_OS::strchr (to_search, '.');
+ if (!dot || !ACE_OS::strlen (to_search))
+ return -1;
+ else
+ *dot = '\0';
+ }
+ int num = atoi (to_search);
+ if (num < 0 || num > 255)
+ return -1;
+ else
+ ((u_char *) &inet_prefix.data)[i] = (u_char)num;
+
+ if (i < 3)
+ to_search = dot + 1;
+ }
+
+ inet_prefix.bitlen = 32; // AF_INET: 32
+
+ if (slash)
+ {
+ int mask_len = 0;
+ mask_len = ACE_OS::atoi (slash + 1);
+ if (mask_len >= 0)
+ inet_prefix.bitlen = mask_len;
+ *slash = '/';
+ }
+ return 0;
+}
+
+void
+Secondary_Ipaddr_Handler::fill_rtnetlink_request (
+ nlmsghdr &hdr,
+ int type,
+ void *data,
+ int data_length)
+{
+ // points to the end of the aligned header
+ struct rtattr *rta = (struct rtattr*) (((char *) &hdr) + NLMSG_ALIGN (hdr.nlmsg_len));
+
+ rta->rta_type = type;
+ rta->rta_len = RTA_LENGTH (data_length);
+ ACE_OS::memcpy (RTA_DATA(rta), data, data_length);
+ hdr.nlmsg_len = NLMSG_ALIGN (hdr.nlmsg_len) + RTA_LENGTH (data_length);
+}
+
+int
+Secondary_Ipaddr_Handler::dispatch_ip_operation (
+ char* const ip_slash_mask,
+ const char *const if_name,
+ bool action)
+{
+ if (this->init_netlink_request (ip_slash_mask,
+ if_name,
+ this->netlink_request_,
+ action) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "Secondary_Ipaddr_Handler::ip_operation - "
+ "init_netlink_request () failed.\n"),
+ -1);
+
+ this->netlink_request_.nhdr_.nlmsg_seq = ++this->seq_;
+ this->netlink_request_.nhdr_.nlmsg_flags |= NLM_F_ACK;
+
+ iovec iov_send =
+ {
+ (void*) &this->netlink_request_.nhdr_,
+ this->netlink_request_.nhdr_.nlmsg_len
+ };
+
+ ACE_Netlink_Addr addr_send;
+ addr_send.set (0, 0);
+
+ if (this->socket ().send (&iov_send,
+ 1,
+ addr_send) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "Secondary_Ipaddr_Handler::ip_operation - "
+ "send of request failed with errno %d.\n",
+ errno),
+ -1);
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::delete_ip (
+ char* const ip_slash_mask,
+ const char *const if_name)
+{
+ if (this->dispatch_ip_operation (
+ ip_slash_mask,
+ if_name,
+ false) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::delete_ip - "
+ "dispatch_ip_operation() failed.\n"),
+ -1);
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::add_ip (
+ char* const ip_slash_mask,
+ const char *const if_name)
+{
+ if (this->dispatch_ip_operation (ip_slash_mask,
+ if_name,
+ true) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) Secondary_Ipaddr_Handler::add_ip - "
+ "dispatch_ip_operation() failed.\n"),
+ -1);
+ return 0;
+}
+
+int
+Secondary_Ipaddr_Handler::init_netlink_request (
+ char* const ip_slash_netmask,
+ const char *const if_name,
+ Netlink_Request& net_req,
+ bool action)
+{
+ ACE_OS::memset (&net_req, 0, sizeof(net_req));
+
+ // fill the request header
+ net_req.nhdr_.nlmsg_len =
+ NLMSG_LENGTH (sizeof(struct ifaddrmsg));
+ net_req.nhdr_.nlmsg_flags = NLM_F_REQUEST;
+ net_req.nhdr_.nlmsg_type = action ? RTM_NEWADDR : RTM_DELADDR;
+ net_req.ifa_.ifa_family = AF_INET;
+
+ int interface_index = -1;
+ if (get_if_index (if_name,
+ interface_index) == -1 || interface_index < 0)
+ {
+ fprintf (stderr, "get_if_index () - failed\n");
+ return -1;
+ }
+ net_req.ifa_.ifa_index = interface_index;
+
+ Inet_Prefix local_prefix;
+
+ if (fill_inet_prefix (local_prefix,
+ ip_slash_netmask) == -1)
+ {
+ fprintf (stderr, "fill_inet_prefix () - failed\n");
+ return -1;
+ }
+
+ fill_rtnetlink_request (net_req.nhdr_,
+ IFA_LOCAL,
+ &local_prefix.data,
+ local_prefix.bytelen);
+
+ net_req.ifa_.ifa_prefixlen = local_prefix.bitlen; // number of bits in netmask
+ net_req.ifa_.ifa_scope = 0;
+
+ return 0;
+}
+
+static int run_test (char*const ip_slash_netmask,
+ const char*const if_name)
+{
+ Secondary_Ipaddr_Handler sec_ip_handler;
+
+ if (sec_ip_handler.open (ACE_Reactor::instance (),
+ ip_slash_netmask,
+ if_name)
+ == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "(%P) SOCK_Netlink_Test - run_test () failed "
+ "due to sec_ip_handler.open () error.\n"),
+ -1);
+
+ ACE_Time_Value wait_time (4, 0);
+
+ ACE_Reactor::instance()->run_reactor_event_loop (wait_time);
+
+ if (sec_ip_handler.get_state () != Secondary_Ipaddr_Handler::SUCCESS)
+ return -1;
+
+ return 0;
+}
+
+static int
+parse_args (int argc, ACE_TCHAR *argv[])
+{
+ if (argc == 1) // one button test
+ {
+ one_button_test = 1;
+ return 0;
+ }
+
+ ACE_OS::memset (ip_slash_mask, 0, sizeof ip_slash_mask);
+ ACE_OS::memset (net_dev_name, 0, sizeof net_dev_name);
+
+ ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:di:"));
+ int c = 0, ip_len = 0, if_len = 0;
+
+ while ((c = get_opt ()) != EOF)
+ {
+ switch (c)
+ {
+ case 'a': // ip_slash_netmask_bits
+ ACE_OS::strncpy (ip_slash_mask,
+ get_opt.optarg,
+ sizeof ip_slash_mask);
+
+ if (! (ip_len = ACE_OS::strlen (ip_slash_mask)))
+ {
+ ACE_ERROR ((LM_ERROR,
+ "%s, -a should be followed by a dotted ipv4 addr slash netmask bits.\n"
+ "Example: \"-a 192.168.1.1/24\" .\n",
+ "SOCK_Netlink_Test"));
+ return -1;
+ }
+ break;
+
+ case 'd':
+ dont_cleanup_added_ip = 1;
+ break;
+
+ case 'i': // name of the interface
+ ACE_OS::strncpy (net_dev_name,
+ get_opt.optarg,
+ sizeof net_dev_name);
+
+ if (! (if_len = ACE_OS::strlen (net_dev_name)))
+ {
+ ACE_ERROR ((LM_ERROR,
+ "%s, -i should be followed by a valid name of network interface.\n",
+ "SOCK_Netlink_Test"));
+ return -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ((ip_len == 0 && if_len) || (ip_len && if_len == 0))
+ {
+ ACE_ERROR_RETURN ((LM_ERROR,
+ "%s - error: both options -a and -i should be provided. \n",
+ "SOCK_Netlink_Test"),
+ -1);
+ }
+ else if (ip_len == 0 && if_len == 0)
+ {
+ one_button_test = 1;
+ }
+
+ return 0;
+}
+
+#define DEFAULT_IP_SLASH_MASK "192.168.1.100/24"
+#define DEFAULT_NET_DEVICE_NAME "lo"
+
+int
+run_main (int argc, ACE_TCHAR *argv[])
+{
+ ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test"));
+
+ if (::geteuid ())
+ {
+ ACE_DEBUG ((LM_ERROR,
+ ACE_TEXT ("(%P) \"SOCK_Netlink_Test\" run_main() - \n")
+ ACE_TEXT ("\tProcess has no superuser preveledges. ")
+ ACE_TEXT ("Unable to run this test.\n")));
+ return -1;
+ }
+
+ if (::parse_args (argc, argv) == -1)
+ {
+ return -1;
+ }
+
+ if (one_button_test)
+ {
+ ACE_OS::strncpy (ip_slash_mask,
+ DEFAULT_IP_SLASH_MASK,
+ sizeof ip_slash_mask);
+ ACE_OS::strncpy (net_dev_name,
+ DEFAULT_NET_DEVICE_NAME,
+ sizeof net_dev_name);
+ }
+
+ int rval = -1;
+ if ((rval = run_test (ip_slash_mask,
+ net_dev_name)) < 0)
+ {
+ ACE_DEBUG ((LM_ERROR,
+ ACE_TEXT ("(%P) \"SOCK_Netlink_Test\" run_main() - \n")
+ ACE_TEXT ("\trun_test () failed with rval returned %d. "),
+ rval));
+ return -1;
+ }
+
+ ACE_END_TEST;
+
+ return 0;
+}
+
+#else /* ! ((ACE_HAS_LINUX_NETLINK) && (ACE_HAS_LINUX_NETLINK == 1)) */
+
+int
+run_main (int argc, ACE_TCHAR *argv[])
+{
+ ACE_UNUSED_ARG (argc);
+ ACE_UNUSED_ARG (argv);
+
+ ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test"));
+
+ ACE_DEBUG ((LM_INFO,
+ "(%P|%t|%T) \"SOCK_Netlink_Test\" main() - "
+ "Linux netlink socket support not configured.\n"
+ "#define ACE_HAS_LINUX_NETLINK = 1 in your config.h "
+ "file to enable and run the process as a superuser.\n"));
+
+ ACE_END_TEST;
+
+ return 0;
+}
+
+#endif /* ((ACE_HAS_LINUX_NETLINK) && (ACE_HAS_LINUX_NETLINK == 1)) */