From defe6d19541ec095099cf41c411ce6daff70a176 Mon Sep 17 00:00:00 2001 From: razb Date: Sat, 9 Apr 2005 16:30:15 +0000 Subject: *** empty log message *** --- tests/GNUmakefile.SOCK_Netlink_Test | 136 +++++ tests/SOCK_Netlink_Test.cpp | 980 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1116 insertions(+) create mode 100644 tests/GNUmakefile.SOCK_Netlink_Test create mode 100644 tests/SOCK_Netlink_Test.cpp 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 +#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)) */ -- cgit v1.2.1