source: source/ariba/communication/networkinfo/NetworkChangeDetection.cpp@ 10767

Last change on this file since 10767 was 10767, checked in by Michael Tänzer, 12 years ago

Clean up NetworkChangeDetection

File size: 9.9 KB
Line 
1// [License]
2// The Ariba-Underlay Copyright
3//
4// Copyright (c) 2008-2009, Institute of Telematics, UniversitÀt Karlsruhe (TH)
5//
6// Institute of Telematics
7// UniversitÀt Karlsruhe (TH)
8// Zirkel 2, 76128 Karlsruhe
9// Germany
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17// 2. Redistributions in binary form must reproduce the above copyright
18// notice, this list of conditions and the following disclaimer in the
19// documentation and/or other materials provided with the distribution.
20//
21// THIS SOFTWARE IS PROVIDED BY THE INSTITUTE OF TELEMATICS ``AS IS'' AND
22// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARIBA PROJECT OR
25// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//
33// The views and conclusions contained in the software and documentation
34// are those of the authors and should not be interpreted as representing
35// official policies, either expressed or implied, of the Institute of
36// Telematics.
37// [License]
38
39#include "NetworkChangeDetection.h"
40
41#include <string>
42#include <algorithm>
43#include <cerrno>
44#include <string.h>
45#include <sys/types.h>
46#include <unistd.h>
47#include <boost/bind.hpp>
48#include <sys/socket.h>
49
50extern "C" {
51 #include <linux/netlink.h>
52 #include <linux/rtnetlink.h>
53}
54
55#include "ariba/utility/system/SystemQueue.h"
56
57using std::find;
58using ariba::utility::SystemQueue;
59using ariba::utility::SystemEventType;
60
61namespace ariba {
62namespace communication {
63
64use_logging_cpp(NetworkChangeDetection);
65SystemEventType NetworkChangeDetectionEventType("NetworkChangeDetectionEventType");
66
67NetworkChangeDetection::NetworkChangeDetection()
68 : running( false ), monitoringThread( NULL ), routingSocket( -1 ){
69 startMonitoring();
70}
71
72NetworkChangeDetection::~NetworkChangeDetection(){
73 stopMonitoring();
74}
75
76void NetworkChangeDetection::registerNotification(NetworkChangeInterface* callback){
77
78 RegistrationList::iterator i = find(
79 registrations.begin(), registrations.end(), callback );
80
81 if( i == registrations.end() )
82 registrations.push_back( callback );
83}
84
85void NetworkChangeDetection::unregisterNotification(NetworkChangeInterface* callback){
86
87 RegistrationList::iterator i = find(
88 registrations.begin(), registrations.end(), callback );
89
90 if( i != registrations.end() )
91 registrations.erase( i );
92}
93
94void NetworkChangeDetection::startMonitoring(){
95
96 logging_debug( "starting network change monitoring ..." );
97
98 running = true;
99 monitoringThread = new boost::thread(
100 boost::bind(&NetworkChangeDetection::monitoringThreadFunc, this) );
101}
102
103void NetworkChangeDetection::stopMonitoring(){
104
105 logging_debug( "stopping network change monitoring ..." );
106
107 // trigger the blocking read to end
108 running = false;
109
110 shutdown( routingSocket, SHUT_RDWR );
111 close( routingSocket );
112
113 // end the monitoring thread
114 monitoringThread->join();
115
116 delete monitoringThread;
117 monitoringThread = NULL;
118}
119
120void NetworkChangeDetection::monitoringThreadFunc(NetworkChangeDetection* obj){
121
122 //
123 // Most information about this routing socket interface is available
124 // when searching for NETLINK_ROUTE and in http://www.faqs.org/rfcs/rfc3549.html
125 // A small program that uses this stuff is ifplugd, nice reference for
126 // getting network interface notifications using routing sockets
127 // (e.g. http://ifplugd.sourcearchive.com/documentation/0.26/ifmonitor_8c-source.html)
128 // And the best resource is the linux code ... http://www.srcdoc.com/linux_2.2.26/rtnetlink_8h-source.html
129 // More:
130 // http://m.linuxjournal.com/article/8498
131 // http://lkml.indiana.edu/hypermail/linux/net/0508.2/0016.html
132 // http://infrahip.hiit.fi/hipl/release/1.0.1/hipl.1.0.1/hipd/netdev.c
133 // http://www.google.de/codesearch?hl=de&q=rtmsg_ifinfo+show:Ndy7Mq7IdCw:aylASy5mtF0:ClfaxlLp-PU&source=universal&cs_p=ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-2.4.18.tar.gz&cs_f=linux/net/core/rtnetlink.c#l490
134 // http://www.google.com/codesearch?hl=en&q=RTM_NEWADDR+NETLINK_ROUTE+show:eUekvGbF94M:iCwmLwtnVbU:38qoeYp--0A&sa=N&cd=1&ct=rc&cs_p=ftp://ftp.stacken.kth.se/pub/arla/arla-0.42.tar.gz&cs_f=arla-0.42/lib/roken/getifaddrs.c
135 // http://www.google.com/codesearch?hl=en&q=RTMGRP_LINK+rtm_newaddr+nl_groups+show:sUV8mV--Lb4:gk0jx_xv0Yg:ZB9MN7Fgkgg&sa=N&cd=1&ct=rc&cs_p=http://freshmeat.net/redir/strongswan/60428/url_bz2/strongswan-4.1.2.tar.bz2&cs_f=strongswan-4.1.4/src/charon/kernel/kernel_interface.c#l689
136 //
137
138 //
139 // open the netlink routing socket, don't need raw sockets here!
140 //
141
142 obj->routingSocket = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
143 if( obj->routingSocket < 0 ){
144 logging_error("could not connect to routing socket: " +
145 std::string(strerror(errno)));
146 return;
147 }
148
149 //
150 // set a socket read timeout. this is actually bad, but closing
151 // the blocking socket using shutdown, close, or writing EOF to
152 // the socket did not work. this seems to be a bug related to
153 // routing sockets that will not close when in blocking mode
154 //
155
156 struct timeval tv;
157 tv.tv_sec = 1;
158 tv.tv_usec = 0;
159
160 setsockopt( obj->routingSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
161
162 //
163 // bind the netlink routing socket to our local address
164 // for the nl_groups, see http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/include/linux/rtnetlink.h#L520
165 // we listen on ipv4/6 new/delete address and link up/down notifications
166 //
167
168 struct sockaddr_nl addr;
169 memset( &addr, 0, sizeof(addr) );
170 addr.nl_family = AF_NETLINK;
171 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_LINK;
172 addr.nl_pid = getpid();
173
174 int ret = bind( obj->routingSocket, (struct sockaddr*)&addr, sizeof(addr) );
175 if( ret < 0 ){
176 close( obj->routingSocket );
177 logging_error( "could not bind routing socket: " + std::string(strerror(errno)) );
178 return;
179 }
180
181 //
182 // read from the netlink routing socket
183 //
184
185 logging_debug( "network change monitoring started" );
186
187 while( obj->running ){
188
189 char buffer[1024];
190 struct nlmsghdr* header = (struct nlmsghdr*)buffer;
191
192 int bytesRead = recv( obj->routingSocket, &buffer, sizeof(buffer), 0 );
193
194 if( bytesRead < 0 ){
195
196 // socket timeout, just continue
197 if( errno == EWOULDBLOCK ) continue;
198
199 // triggered by shutdown() function in stopMonitoring
200 if( errno == EBADF ) return;
201
202 // gracefull exit by signals
203 if( errno == EAGAIN || errno == EINTR ) return;
204
205 // all others are some kind of error
206 logging_error( "could not read from routing socket: " +
207 std::string(strerror(errno)) );
208 break;
209 }
210
211 for( ; bytesRead > 0; header = NLMSG_NEXT(header, bytesRead)) {
212 if (!NLMSG_OK(header, (size_t)bytesRead) ||
213 (size_t) bytesRead < sizeof(struct nlmsghdr) ||
214 (size_t) bytesRead < (size_t)header->nlmsg_len) {
215 continue;
216 }
217
218 // somehow all notifications with pid=0 are
219 // invalid and occur much too often
220 if(header->nlmsg_pid == 0) continue;
221
222 //
223 // here we evaluate the routing netlink message. for, the following
224 // messages are of interest:
225 // RTM_NEWADDR, RTM_DELADDR -> new address on iface, address deleted from iface
226 //
227
228 NetworkChangeInterface::NetworkChangeInfo changeInfo;
229
230 switch( header->nlmsg_type ){
231
232 //
233 // handle network address configuration changes
234 // --> network layer notifications
235 //
236
237 case RTM_NEWADDR:
238 case RTM_DELADDR:
239
240 changeInfo = obj->extractAddressEvent( header );
241 break;
242
243 //
244 // other RTM message are ignored in case
245 // we did subscribe using nl_groups (see above)
246 //
247
248 default: break;
249
250 } // switch( header->nlmsg_type )
251
252 logging_info( "network change detected: [" << changeInfo << "]" );
253
254 // put noficiation event into the system queue for main
255 // thread synchronization. will be handled in NetworkChangeDetection::handleSystemEvent()
256 // that will run synchronized with the main thread
257
258 SystemQueue::instance().scheduleEvent(
259 SystemEvent( obj, NetworkChangeDetectionEventType,
260 new NetworkChangeInterface::NetworkChangeInfo(changeInfo)));
261
262 } // for( ; bytesRead > 0; header = NLMSG_NEXT(header, bytesRead))
263 } // while( running )
264
265 logging_debug( "network change monitoring stopped" );
266}
267
268NetworkChangeInterface::NetworkChangeInfo NetworkChangeDetection::extractAddressEvent(struct nlmsghdr* header){
269 NetworkChangeInterface::NetworkChangeInfo changeInfo;
270
271 //
272 // handle network address configuration changes
273 // addresses are added/removed from an interface
274 //
275
276 if( header->nlmsg_type == RTM_NEWADDR ){
277 logging_debug("network change message RTM_NEWADDR");
278 changeInfo.type = NetworkChangeInterface::EventTypeInterfaceUp;
279 }else if( header->nlmsg_type == RTM_DELADDR ){
280 logging_debug("network change message RTM_DELADDR");
281 changeInfo.type = NetworkChangeInterface::EventTypeInterfaceDown;
282 }
283
284 struct ifaddrmsg* detailAddr = (struct ifaddrmsg*)NLMSG_DATA(header);
285 changeInfo.interface = networkInformation.getInterface( detailAddr->ifa_index );
286
287 return changeInfo;
288}
289
290void NetworkChangeDetection::handleSystemEvent(const SystemEvent& event){
291
292 NetworkChangeInterface::NetworkChangeInfo* changeInfo = event.getData<NetworkChangeInterface::NetworkChangeInfo>();
293 const NetworkChangeDetection* obj = (const NetworkChangeDetection*)event.getListener();
294
295 RegistrationList::const_iterator i = obj->registrations.begin();
296 RegistrationList::const_iterator iend = obj->registrations.end();
297
298 for( ; i != iend; i++ )
299 (*i)->onNetworkChange(*changeInfo);
300
301 delete changeInfo;
302}
303
304}} // namespace ariba, communication
Note: See TracBrowser for help on using the repository browser.