Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / ACE / tests / SOCK_Netlink_Test.cpp
blobdecba0fbc08f3ead884a88ba0d506a05827e054c
2 //=============================================================================
3 /**
4 * @file SOCK_Netlink_Test.cpp
6 * Tests adding of a secondary IP-address, using linux netlink
7 * sockets.
9 * @author Robert Iakobashvili
10 * @author coroberti@gmail.com
12 //=============================================================================
15 #include "test_config.h"
17 #ifdef ACE_HAS_NETLINK
19 #include "ace/Event_Handler.h"
20 #include "ace/Reactor.h"
21 #include "ace/Log_Msg.h"
22 #include "ace/Get_Opt.h"
24 #include "ace/Netlink_Addr.h"
25 #include "ace/SOCK_Netlink.h"
27 #include "ace/OS_NS_sys_socket.h"
28 #include "ace/OS_NS_time.h"
30 #include <linux/rtnetlink.h>
33 /**
34 * NETLINK SOCKET INTERFACE is a socket API communication
35 * between linux kernel and userland.
37 * Main usage of netlink communication is for communication between
38 * kernel/custom modules/drivers and userspace;
40 * In order not to force ACE-tests runners to install some driver for testing
41 * purposes, we are using here a build-in netlink interface used by linux for
42 * "routing" purposes (rtnetlink).
44 * This test is hopefully a useful example of how via netlink to add a new
45 * secondary IPv4 address to an interface and to delete it further. The
46 * example is integrated within reactive framework for IO demultiplexing.
48 * A one button test adds a new secondary IP-address to a loopback
49 * interface and deletes it. Even if the address added remains, this shall no
50 * cause any damage. In any case the lifetime of a secondary ip is limited
51 * till the next reboot. You may wish to run the test with -d option (not to
52 * make an address cleanup after addtion) and see it by 'ip addr' command.
53 * Further re-run of the test without -d option will cleanup the address.
55 * Please, note, that it is ok that the message "rtnetlink error message:
56 * Cannot assign requested address" will appear on your console, because first
57 * the test is trying to remove the secondary-ip and errors, if it was not before.
59 * The one-button test fails, of there is no a loopback device named "lo" on a
60 * host or the device is disabled.
62 * The same rtnetlink interface may be used to get/add/delete ip-addresses,
63 * get/add/delete routing rules, control ARP entires, control Qdisk disciplines,
64 * traffic classes and traffic filters, manage network interface configuration
66 * For more information, please, read man pages:
67 * netlink (3), netlink (7), rtnetlink (3), rtnetlink (3), rtnetlink (7) and ip (8).
69 * Some ideas for the test were borrowed from the code of iprouted2
70 * written by Alexey Kuznetsov.
72 * Command line options:
74 * -d do not cleanup the ip-address after addition (so that you can see it by the
75 * 'ip addr' command)
77 * -i the name of interface to add the address
79 * -a ipv4 slash netmask bits, like "192.168.1.1/24"
82 From Linux headers:
84 // Generic structure for encapsulation of optional route information.
85 // It is reminiscent of sockaddr, but with sa_family replaced with attribute type.
86 struct rtattr
88 unsigned short rta_len;
89 unsigned short rta_type;
92 //Interface address.
93 struct ifaddrmsg
95 unsigned char ifa_family;
96 unsigned char ifa_prefixlen; // The prefix length is the length of the address mask
97 unsigned char ifa_flags; // Flags: IFA_F_SECONDARY for secondary
98 // address (old alias interface), IFA_F_PERMANENT
99 // for a permanent address
100 unsigned char ifa_scope; // locality
101 int ifa_index; // Link index is the interface index in the table of interfaces.
104 struct nlmsghdr
106 __u32 nlmsg_len; // Length of message including header
107 __u16 nlmsg_type; // Message content
108 __u16 nlmsg_flags; // Additional flags
109 __u32 nlmsg_seq; // Sequence number
110 __u32 nlmsg_pid; // Sending process PID
115 // The global config params
117 static int one_button_test = 0;
118 static char ip_slash_mask[32];
119 static char net_dev_name[16];
120 static int dont_cleanup_added_ip = 0;
122 // The function returns index of an interface by its name
125 get_if_index (const char*const interface,
126 int &if_index)
128 if_index = -1;
130 struct ifreq if_req;
131 ACE_OS::memset (&if_req, 0, sizeof (struct ifreq));
133 ACE_OS::strncpy (if_req.ifr_name,
134 static_cast<const char*> (interface),
135 sizeof (if_req.ifr_name));
137 ACE_HANDLE s = ACE_OS::socket (AF_INET,SOCK_DGRAM,0);
139 if (s == ACE_INVALID_HANDLE)
140 ACE_ERROR_RETURN ((LM_ERROR,
141 ACE_TEXT ("%p\n"),
142 ACE_TEXT ("get_if_index - failed on\n")
143 ACE_TEXT ("ACE_OS::socket")),
144 -1);
146 int result = ACE_OS::ioctl (s,
147 SIOCGIFINDEX,
148 reinterpret_cast<char*> (&if_req));
150 if (result == -1)
152 ACE_ERROR ((LM_ERROR,
153 ACE_TEXT ("%p\n"),
154 ACE_TEXT ("get_if_index:")
155 ACE_TEXT ("ioctl (get interface index)")));
157 else
159 if_index = if_req.ifr_ifindex;
161 return result;
165 An assisting structure for passing data via routing netlink socket.
166 The idea borrowed from a great iprouted2 utility,
167 written by Alexey Kuznetsov.
169 struct Inet_Prefix
171 u_char family;
172 u_char bytelen;
173 ACE_INT16 bitlen;
174 ACE_UINT32 data[4];
179 * Contains header netlink message header of a type nlmsghdr with a
180 * following request type dependent controlling structure, which is for
181 * adding/deleting IP-addresses is of type ifaddrmsg with a following
182 * buffer, containing the data: ip-address, its length, number of netmask
183 * bits, etc.
185 struct Netlink_Request
187 struct nlmsghdr nhdr_; // message header
188 struct ifaddrmsg ifa_; // interface
189 char buf_[256];
195 * The handler first is trying to delete an ip-address, further
196 * to add the ip and, if successful to cleanup the address.
198 class Secondary_Ipaddr_Handler : public ACE_Event_Handler
200 public:
202 // Default constructor
203 Secondary_Ipaddr_Handler (void);
205 // Destructor
206 virtual ~Secondary_Ipaddr_Handler (void);
208 //FUZZ: disable check_for_lack_ACE_OS
209 // Initialization. Schedules a timer to run start the business.
211 ///FUZZ: enable check_for_lack_ACE_OS
212 int open (ACE_Reactor *const reactor,
213 char* const ip_slash_mask,
214 const char *const if_name);
216 // Returns reference to netlink socket. Necessary for reactor.
217 virtual ACE_HANDLE get_handle (void) const;
220 * Takes care of the input. Reads the incoming messages,
221 * makes their processing.
223 virtual int handle_input (ACE_HANDLE handle);
225 // Makes clean-up
226 virtual int handle_close (ACE_HANDLE handle,
227 ACE_Reactor_Mask close_mask);
229 // Runs a state machine. Controls adding/deleting of ip-address.
230 int handle_timeout (ACE_Time_Value const & tv,
231 void const * arg = 0);
233 // Sends to kernel a request to add secondary ip/mask to an
234 // interface.
235 int add_ip (char* const ip_slash_mask,
236 const char *const if_name);
238 // Sends to kernel a request to delete secondary ip/mask
239 // from an interface.
240 int delete_ip (char* const ip_slash_mask,
241 const char *const if_name);
244 * 1. We are trying to delete the ip-address, if exists.
245 * Shall be either successful, or fail, when no-ip yet.
246 * 2. Adding the ip-address, shall be successful;
247 * 3. Cleaning up the ip-address. Shall be successful as well.
249 enum
251 START = 0,
252 IP_DELETED,
253 IP_ADDED,
254 IP_CLEANUPED,
255 SUCCESS,
256 FAILED
259 // Returns the currect state
260 int get_state () const { return this->state_;}
262 protected:
264 //FUZZ: disable check_for_lack_ACE_OS
265 // De-registers the handler from the reactor,
266 // other cleanup jobs
267 virtual int close ();
269 ///FUZZ: enable check_for_lack_ACE_OS
270 ACE_SOCK_Netlink& socket ();
272 private:
274 // Schedule two sec timer.
275 int schedule_one_sec_timer ();
277 // A workhorse for add_ip () and delete_ip ()
278 int dispatch_ip_operation (char* const ip_slash_mask,
279 const char *const if_name,
280 bool action);
283 * Initializes netlink request for adding (action = true) or
284 * deleting (action = false) of a secondary ip-address/mask.
286 int init_netlink_request (char* const ip_slash_mask,
287 const char *const if_name,
288 Netlink_Request& net_req,
289 bool action);
291 // Fills data part of Netlink_Request
292 int fill_inet_prefix (Inet_Prefix &inet_prefix,
293 const char*const ip_slash_netmask);
296 * Fills routing request (operations with ip-addresses are
297 * a part of netlink routing interface).
299 void fill_rtnetlink_request (nlmsghdr &hdr,
300 int type,
301 void *data,
302 int data_length);
304 enum
306 COMMAND_TIMEOUT = -3,
307 COMMAND_RECV_ERROR = -2,
308 COMMAND_ERROR = -1,
309 COMMAND_SUCCESS = 0
312 // Mark command status in handle_input ().
313 void on_recv_error () { this->command_status_ = COMMAND_RECV_ERROR; }
314 void on_command_success () { this->command_status_ = COMMAND_SUCCESS; }
315 void on_command_error () { this->command_status_ = COMMAND_ERROR; }
317 // The socket.
318 ACE_SOCK_Netlink socket_ ;
320 // The address of the socket.
321 ACE_Netlink_Addr address_ ;
323 // Message sequence number.
324 ACE_UINT32 seq_ ;
326 // The request structure passed to kernel.
327 Netlink_Request netlink_request_;
329 // ip-addr-slash-mask
330 char ip_buff_[32];
332 // interface
333 char if_buff_[16];
335 // Buffer to receive messages. Not too large?
336 char recv_buff_[1024];
338 // The state of the handler.
339 int state_;
341 // The status of the command.
342 int command_status_;
345 Secondary_Ipaddr_Handler::Secondary_Ipaddr_Handler ()
347 socket_ (),
348 address_ (),
349 seq_ (0),
350 state_ (START),
351 command_status_ (COMMAND_SUCCESS)
355 Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler ()
357 ACE_DEBUG ((LM_DEBUG,
358 ACE_TEXT ("(%P|%t) Secondary_Ipaddr_Handler::~Secondary_Ipaddr_Handler\n")));
359 this->close ();
362 Secondary_Ipaddr_Handler::open (ACE_Reactor *const reactor,
363 char* const ip_slash_mask,
364 const char *const if_name)
366 if (!reactor ||
367 !ip_slash_mask || !ACE_OS::strlen (ip_slash_mask) ||
368 !if_name || !ACE_OS::strlen (if_name))
369 ACE_ERROR_RETURN ((LM_ERROR,
370 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::open: error ")
371 ACE_TEXT("zero pointers or zero length strings used as input.\n")),
372 -1);
374 this->reactor (reactor);
376 // Another option is to pass a zero pid to the address, to call open () on socket
377 // performing binding and after bind () to call getsockbyname to fill the address
379 this->address_.set (ACE_OS::getpid (), 0);
381 if (this->socket ().open (this->address_,
382 ACE_PROTOCOL_FAMILY_NETLINK,
383 NETLINK_ROUTE) == -1)
384 //FUZZ: disable check_for_lack_ACE_OS
385 ACE_ERROR_RETURN ((LM_ERROR,
386 ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::open: - failed\n")
387 ACE_TEXT("to initialize netlink socket bu open ().\n")),
388 -1);
389 //FUZZ: enable check_for_lack_ACE_OS
391 // register with the reactor for input
392 if (this->reactor ()->register_handler (this,
393 ACE_Event_Handler::READ_MASK) == -1)
394 ACE_ERROR_RETURN ((LM_ERROR,
395 ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::open - ")
396 ACE_TEXT("can't register with reactor for handling input.\n")),
397 -1);
399 if (this->reactor ()->schedule_timer (this,
401 ACE_Time_Value::zero) == -1)
402 ACE_ERROR_RETURN ((LM_ERROR,
403 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::open - ")
404 ACE_TEXT("can't schedule timer with reactor.\n")),
405 -1);
407 this->seq_ = ACE_OS::time (0);
409 ACE_OS::strncpy (this->ip_buff_,
410 ip_slash_mask,
411 sizeof this->ip_buff_);
413 ACE_OS::strncpy (this->if_buff_,
414 if_name,
415 sizeof this->if_buff_);
417 return 0;
420 ACE_HANDLE
421 Secondary_Ipaddr_Handler::get_handle (void) const
423 return this->socket_.get_handle ();
427 Secondary_Ipaddr_Handler::handle_input (ACE_HANDLE)
429 ACE_DEBUG ((LM_DEBUG,
430 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - entered\n")));
432 nlmsghdr *hdr = 0;
433 iovec iov;
435 iov.iov_base = this->recv_buff_;
436 iov.iov_len = sizeof (this->recv_buff_);
438 int rval_bytes = -1;
439 ACE_Netlink_Addr raddr;
440 raddr.set (0, 0);
442 rval_bytes = this->socket ().recv (&iov, 1, raddr);
443 switch (rval_bytes)
445 case -1: // Complain and leave
446 ACE_ERROR_RETURN ((LM_ERROR,
447 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ")
448 ACE_TEXT("%p bad read\n"), ACE_TEXT("client")),
449 -1);
451 case 0: // Complain and leave
452 ACE_ERROR_RETURN ((LM_ERROR,
453 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - "),
454 ACE_TEXT("eof, closing daemon (fd = %d)\n"),
455 this->get_handle ()),
456 -1);
458 default:
459 if (raddr.get_size () != sizeof (sockaddr_nl))
461 this->on_recv_error ();
462 ACE_ERROR_RETURN ((LM_ERROR,
463 ACE_TEXT("(%n %P) Secondary_Ipaddr_Handler::handle_input - ")
464 ACE_TEXT("address length not equal sockaddr_nl\n")),
465 -1);
468 hdr = reinterpret_cast <nlmsghdr*> (this->recv_buff_);
470 if (static_cast <int> (hdr->nlmsg_len) != rval_bytes)
472 this->on_recv_error ();
473 ACE_ERROR_RETURN ((LM_ERROR,
474 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ")
475 ACE_TEXT("size of nlmsg_len not equal received bytes\n")),
476 -1);
479 if (static_cast <int> (hdr->nlmsg_pid) != this->address_.get_pid () || hdr->nlmsg_seq != this->seq_)
480 ACE_ERROR_RETURN ((LM_ERROR,
481 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - ")
482 ACE_TEXT("process id or message sequence is different\n")),
483 -1);
485 if (hdr->nlmsg_type == NLMSG_ERROR)
487 struct nlmsgerr *err = static_cast <struct nlmsgerr*> (NLMSG_DATA(hdr));
489 errno = -err->error;
490 if (errno == 0)
492 this->on_command_success ();
493 ACE_DEBUG ((LM_DEBUG,
494 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - command success\n")));
495 return 0;
498 this->on_command_error ();
499 ACE_DEBUG ((LM_DEBUG,
500 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_input - command informs about error\n")));
502 // some error message
503 ACE_OS::perror("rtnetlink error message: ");
505 return 0;
508 return -1;
512 Secondary_Ipaddr_Handler::handle_timeout (ACE_Time_Value const &,
513 void const *)
515 ACE_DEBUG ((LM_DEBUG,
516 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - entered\n")));
518 if (this->command_status_ != COMMAND_SUCCESS &&
519 (this->command_status_ != COMMAND_ERROR &&
520 this->state_ == IP_DELETED))
522 ACE_ERROR_RETURN ((LM_ERROR,
523 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ")
524 ACE_TEXT("previous command failed\n")),
525 -1);
527 else
528 this->command_status_ = COMMAND_TIMEOUT; //invalidate command status
530 switch (this->state_)
532 case START: //delete the ip, if it presents
533 if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1)
535 this->state_ = FAILED;
536 ACE_ERROR_RETURN ((LM_ERROR,
537 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ")
538 ACE_TEXT("delete_ip failed\n")),
539 -1);
541 break;
543 case IP_DELETED: // add the ip
544 if (this->add_ip (this->ip_buff_, this->if_buff_) == -1)
546 this->state_ = FAILED;
547 ACE_ERROR_RETURN ((LM_ERROR,
548 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ")
549 ACE_TEXT("add_ip failed\n")),
550 -1);
552 break;
554 case IP_ADDED: //delete added ip to make cleanup
555 if (dont_cleanup_added_ip)
557 this->state_ = SUCCESS;
558 return 0;
560 else
562 if (this->delete_ip (this->ip_buff_, this->if_buff_) == -1)
564 this->state_ = FAILED;
565 ACE_ERROR_RETURN ((LM_ERROR,
566 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::handle_timeout - ")
567 ACE_TEXT("delete_ip failed\n")),
568 -1);
571 break;
573 case IP_CLEANUPED:
574 this->state_ = SUCCESS;
575 return 0;
577 default:
578 return -1;
581 this->schedule_one_sec_timer ();
582 this->state_++;
583 return 0;
587 Secondary_Ipaddr_Handler::handle_close (ACE_HANDLE,
588 ACE_Reactor_Mask)
590 ACE_DEBUG ((LM_DEBUG,
591 ACE_TEXT("(%P|%t) Secondary_Ipaddr_Handler::handle_close\n")));
592 this->close ();
593 return 0;
597 Secondary_Ipaddr_Handler::close ()
599 ACE_DEBUG ((LM_DEBUG,
600 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::close\n")));
602 if (this->reactor ())
604 this->reactor ()->remove_handler (this,
605 ACE_Event_Handler::ALL_EVENTS_MASK |
606 ACE_Event_Handler::DONT_CALL);
608 this->reactor ()->cancel_timer (this);
610 this->reactor (0);
612 return 0;
616 Secondary_Ipaddr_Handler::schedule_one_sec_timer ()
618 const ACE_Time_Value one_sec (1, 0);
620 if (this->reactor ()->schedule_timer (this,
622 one_sec) == -1)
623 ACE_ERROR_RETURN ((LM_ERROR,
624 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::schedule_one_sec_timer - ")
625 ACE_TEXT("can't schedule timer with reactor.\n")),
626 -1);
627 return 0;
630 ACE_SOCK_Netlink&
631 Secondary_Ipaddr_Handler::socket ()
633 return this->socket_;
637 Secondary_Ipaddr_Handler::fill_inet_prefix (
638 Inet_Prefix &inet_prefix,
639 const char*const ip_slash_netmask)
641 ACE_OS::memset (&inet_prefix, 0, sizeof inet_prefix);
642 char* slash = const_cast <char *> (ACE_OS::strchr (ip_slash_netmask, '/'));
643 if (slash)
644 *slash = '\0'; // The idea from Igor Potulnitsky
646 inet_prefix.family = AF_INET; // another option is AF_UNSPEC
647 inet_prefix.bytelen = 4;
648 inet_prefix.bitlen = -1;
650 char ip_buff[32];
651 ACE_OS::strncpy (ip_buff, ip_slash_netmask, sizeof (ip_buff));
653 char* to_search = ip_buff, *dot = 0;
655 for (int i = 0; i < 4; i++)
657 if (i < 3)
659 dot = ACE_OS::strchr (to_search, '.');
660 if (!dot || !ACE_OS::strlen (to_search))
661 return -1;
662 else
663 *dot = '\0';
665 int num = ACE_OS::atoi (to_search);
666 if (num < 0 || num > 255)
667 return -1;
668 else
669 ((u_char *) &inet_prefix.data)[i] = (u_char)num;
671 if (i < 3)
672 to_search = dot + 1;
675 inet_prefix.bitlen = 32; // AF_INET: 32
677 if (slash)
679 int mask_len = 0;
680 mask_len = ACE_OS::atoi (slash + 1);
681 if (mask_len >= 0)
682 inet_prefix.bitlen = mask_len;
683 *slash = '/';
685 return 0;
688 void
689 Secondary_Ipaddr_Handler::fill_rtnetlink_request (
690 nlmsghdr &hdr,
691 int type,
692 void *data,
693 int data_length)
695 // points to the end of the aligned header
696 struct rtattr *rta = reinterpret_cast <struct rtattr*> (((reinterpret_cast <char*>(&hdr)) + NLMSG_ALIGN (hdr.nlmsg_len)));
698 rta->rta_type = type;
699 rta->rta_len = RTA_LENGTH (data_length);
700 ACE_OS::memcpy (RTA_DATA(rta), data, data_length);
701 hdr.nlmsg_len = NLMSG_ALIGN (hdr.nlmsg_len) + RTA_LENGTH (data_length);
705 Secondary_Ipaddr_Handler::dispatch_ip_operation (
706 char* const ip_slash_mask,
707 const char *const if_name,
708 bool action)
710 if (this->init_netlink_request (ip_slash_mask,
711 if_name,
712 this->netlink_request_,
713 action) == -1)
714 ACE_ERROR_RETURN ((LM_ERROR,
715 "Secondary_Ipaddr_Handler::ip_operation - "
716 "init_netlink_request () failed.\n"),
717 -1);
719 this->netlink_request_.nhdr_.nlmsg_seq = ++this->seq_;
720 this->netlink_request_.nhdr_.nlmsg_flags |= NLM_F_ACK;
722 iovec iov_send =
724 static_cast <void*> (&this->netlink_request_.nhdr_),
725 this->netlink_request_.nhdr_.nlmsg_len
728 ACE_Netlink_Addr addr_send;
729 addr_send.set (0, 0);
731 if (this->socket ().send (&iov_send,
733 addr_send) == -1)
734 ACE_ERROR_RETURN ((LM_ERROR,
735 ACE_TEXT("Secondary_Ipaddr_Handler::ip_operation - ")
736 ACE_TEXT("send of request failed with errno %d.\n"),
737 errno),
738 -1);
739 return 0;
743 Secondary_Ipaddr_Handler::delete_ip (
744 char* const ip_slash_mask,
745 const char *const if_name)
747 if (this->dispatch_ip_operation (
748 ip_slash_mask,
749 if_name,
750 false) == -1)
751 ACE_ERROR_RETURN ((LM_ERROR,
752 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::delete_ip - ")
753 ACE_TEXT("dispatch_ip_operation() failed.\n")),
754 -1);
755 return 0;
759 Secondary_Ipaddr_Handler::add_ip (
760 char* const ip_slash_mask,
761 const char *const if_name)
763 if (this->dispatch_ip_operation (ip_slash_mask,
764 if_name,
765 true) == -1)
766 ACE_ERROR_RETURN ((LM_ERROR,
767 ACE_TEXT("(%P) Secondary_Ipaddr_Handler::add_ip - ")
768 ACE_TEXT("dispatch_ip_operation() failed.\n")),
769 -1);
770 return 0;
774 Secondary_Ipaddr_Handler::init_netlink_request (
775 char* const ip_slash_netmask,
776 const char *const if_name,
777 Netlink_Request& net_req,
778 bool action)
780 ACE_OS::memset (&net_req, 0, sizeof(net_req));
782 // fill the request header
783 net_req.nhdr_.nlmsg_len =
784 NLMSG_LENGTH (sizeof(struct ifaddrmsg));
785 net_req.nhdr_.nlmsg_flags = NLM_F_REQUEST;
786 net_req.nhdr_.nlmsg_type = action ? RTM_NEWADDR : RTM_DELADDR;
787 net_req.ifa_.ifa_family = AF_INET;
789 int interface_index = -1;
790 if (get_if_index (if_name,
791 interface_index) == -1 || interface_index < 0)
793 ACE_OS::fprintf (stderr, "get_if_index () - failed\n");
794 return -1;
796 net_req.ifa_.ifa_index = interface_index;
798 Inet_Prefix local_prefix;
800 if (fill_inet_prefix (local_prefix,
801 ip_slash_netmask) == -1)
803 ACE_OS::fprintf (stderr, "fill_inet_prefix () - failed\n");
804 return -1;
807 fill_rtnetlink_request (net_req.nhdr_,
808 IFA_LOCAL,
809 &local_prefix.data,
810 local_prefix.bytelen);
812 net_req.ifa_.ifa_prefixlen = local_prefix.bitlen; // number of bits in netmask
813 net_req.ifa_.ifa_scope = 0;
815 return 0;
818 static int run_test (char*const ip_slash_netmask,
819 const char*const if_name)
821 Secondary_Ipaddr_Handler sec_ip_handler;
823 if (sec_ip_handler.open (ACE_Reactor::instance (),
824 ip_slash_netmask,
825 if_name)
826 == -1)
827 ACE_ERROR_RETURN ((LM_ERROR,
828 ACE_TEXT("(%P) SOCK_Netlink_Test - run_test () failed ")
829 ACE_TEXT("due to sec_ip_handler.open () error.\n")),
830 -1);
832 ACE_Time_Value wait_time (4, 0);
834 ACE_Reactor::instance()->run_reactor_event_loop (wait_time);
836 if (sec_ip_handler.get_state () != Secondary_Ipaddr_Handler::SUCCESS)
837 return -1;
839 return 0;
842 static int
843 parse_args (int argc, ACE_TCHAR *argv[])
845 if (argc == 1) // one button test
847 one_button_test = 1;
848 return 0;
851 ACE_OS::memset (ip_slash_mask, 0, sizeof ip_slash_mask);
852 ACE_OS::memset (net_dev_name, 0, sizeof net_dev_name);
854 ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("a:di:"));
855 int c = 0, ip_len = 0, if_len = 0;
856 while ((c = get_opt ()) != EOF)
858 switch (c)
860 case 'a': // ip_slash_netmask_bits
861 ACE_OS::strcpy (ip_slash_mask,
862 reinterpret_cast <const char*> (get_opt.opt_arg ()));
864 if (! (ip_len = ACE_OS::strlen (ip_slash_mask)))
866 ACE_ERROR ((LM_ERROR,
867 ACE_TEXT("%s, -a should be followed by a dotted ipv4 addr slash netmask bits.\n")
868 ACE_TEXT("Example: \"-a 192.168.1.1/24\" .\n"),
869 ACE_TEXT("SOCK_Netlink_Test")));
870 return -1;
872 break;
874 case 'd':
875 dont_cleanup_added_ip = 1;
876 break;
878 case 'i': // name of the interface
879 ACE_OS::strcpy (net_dev_name,
880 reinterpret_cast <const char*> (get_opt.opt_arg ()));
881 if (! (if_len = ACE_OS::strlen (net_dev_name)))
883 ACE_ERROR ((LM_ERROR,
884 ACE_TEXT("%s, -i should be followed by a valid name of network interface.\n"),
885 ACE_TEXT("SOCK_Netlink_Test")));
886 return -1;
888 break;
890 default:
891 break;
895 if ((ip_len == 0 && if_len) || (ip_len && if_len == 0))
897 ACE_ERROR_RETURN ((LM_ERROR,
898 ACE_TEXT("%s - error: both options -a and -i should be provided.\n"),
899 ACE_TEXT("SOCK_Netlink_Test")),
900 -1);
902 else if (ip_len == 0 && if_len == 0)
904 one_button_test = 1;
907 return 0;
910 #define DEFAULT_IP_SLASH_MASK "192.168.1.100/24"
911 #define DEFAULT_NET_DEVICE_NAME "lo"
914 run_main (int argc, ACE_TCHAR *argv[])
916 ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test"));
918 if (ACE_OS::geteuid ())
920 ACE_DEBUG ((LM_INFO,
921 ACE_TEXT ("Process has no superuser priveleges. ")
922 ACE_TEXT ("Unable to run this test.\n")));
924 else
926 if (::parse_args (argc, argv) == -1)
928 return -1;
931 if (one_button_test)
933 ACE_OS::strcpy (ip_slash_mask,
934 DEFAULT_IP_SLASH_MASK);
935 ACE_OS::strcpy (net_dev_name,
936 DEFAULT_NET_DEVICE_NAME);
939 int rval = -1;
940 if ((rval = run_test (ip_slash_mask,
941 net_dev_name)) < 0)
943 ACE_DEBUG ((LM_ERROR,
944 ACE_TEXT ("run_test() failed with rval returned %d. "),
945 rval));
946 return -1;
950 ACE_END_TEST;
952 return 0;
955 #else /* ACE_HAS_NETLINK*/
958 run_main (int, ACE_TCHAR *[])
960 ACE_START_TEST (ACE_TEXT ("SOCK_Netlink_Test"));
962 ACE_DEBUG ((LM_INFO,
963 ACE_TEXT("(%P|%t|%T) \"SOCK_Netlink_Test\" main() - ")
964 ACE_TEXT("Linux netlink socket support not configured.\n")
965 ACE_TEXT("#define ACE_HAS_NETLINK = 1 in your config.h ")
966 ACE_TEXT("file to enable and run the process as a superuser.\n")));
968 ACE_END_TEST;
970 return 0;
973 #endif /* ACE_HAS_NETLINK*/