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

Last change on this file since 5873 was 3690, checked in by mies, 16 years ago

Merged 20090512-mies-connectors changes r3472:r3689 into trunk.

File size: 11.0 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
41namespace ariba {
42namespace communication {
43
44use_logging_cpp(NetworkChangeDetection);
45SystemEventType NetworkChangeDetectionEventType("NetworkChangeDetectionEventType");
46
47NetworkChangeDetection::NetworkChangeDetection()
48 : running( false ), monitoringThread( NULL ), routingSocket( -1 ){
49 startMonitoring();
50}
51
52NetworkChangeDetection::~NetworkChangeDetection(){
53 stopMonitoring();
54}
55
56void NetworkChangeDetection::registerNotification(NetworkChangeInterface* callback){
57
58 RegistrationList::iterator i = find(
59 registrations.begin(), registrations.end(), callback );
60
61 if( i == registrations.end() )
62 registrations.push_back( callback );
63}
64
65void NetworkChangeDetection::unregisterNotification(NetworkChangeInterface* callback){
66
67 RegistrationList::iterator i = find(
68 registrations.begin(), registrations.end(), callback );
69
70 if( i != registrations.end() )
71 registrations.erase( i );
72}
73
74void NetworkChangeDetection::startMonitoring(){
75
76 logging_debug( "starting network change monitoring ..." );
77
78 running = true;
79 monitoringThread = new boost::thread(
80 boost::bind(&NetworkChangeDetection::monitoringThreadFunc, this) );
81}
82
83void NetworkChangeDetection::stopMonitoring(){
84
85 logging_debug( "stopping network change monitoring ..." );
86
87 // trigger the blocking read to end
88 running = false;
89
90 shutdown( routingSocket, SHUT_RDWR );
91 close( routingSocket );
92
93 // end the monitoring thread
94 monitoringThread->join();
95
96 delete monitoringThread;
97 monitoringThread = NULL;
98}
99
100void NetworkChangeDetection::monitoringThreadFunc(NetworkChangeDetection* obj){
101
102 //
103 // Most information about this routing socket interface is available
104 // when searching for NETLINK_ROUTE and in http://www.faqs.org/rfcs/rfc3549.html
105 // A small program that uses this stuff is ifplugd, nice reference for
106 // getting network interface notifications using routing sockets
107 // (e.g. http://ifplugd.sourcearchive.com/documentation/0.26/ifmonitor_8c-source.html)
108 // And the best resource is the linux code ... http://www.srcdoc.com/linux_2.2.26/rtnetlink_8h-source.html
109 // More:
110 // http://m.linuxjournal.com/article/8498
111 // http://lkml.indiana.edu/hypermail/linux/net/0508.2/0016.html
112 // http://infrahip.hiit.fi/hipl/release/1.0.1/hipl.1.0.1/hipd/netdev.c
113 // 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
114 // 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
115 // 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
116 //
117
118 //
119 // open the netlink routing socket, don't need raw sockets here!
120 //
121
122 obj->routingSocket = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
123 if( obj->routingSocket < 0 ){
124 logging_error("could not connect to routing socket: " +
125 string(strerror(errno)));
126 return;
127 }
128
129 //
130 // set a socket read timeout. this is actually bad, but closing
131 // the blocking socket using shutdown, close, or writing EOF to
132 // the socket did not work. this seems to be a bug related to
133 // routing sockets that will not close when in blocking mode
134 //
135
136 struct timeval tv;
137 tv.tv_sec = 1;
138 tv.tv_usec = 0;
139
140 setsockopt( obj->routingSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
141
142 //
143 // bind the netlink routing socket to our local address
144 // for the nl_groups, see http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/include/linux/rtnetlink.h#L520
145 // we listen on ipv4/6 new/delete address and link up/down notifications
146 //
147
148 struct sockaddr_nl addr;
149 memset( &addr, 0, sizeof(addr) );
150 addr.nl_family = AF_NETLINK;
151 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_LINK;
152 addr.nl_pid = getpid();
153
154 int ret = bind( obj->routingSocket, (struct sockaddr*)&addr, sizeof(addr) );
155 if( ret < 0 ){
156 close( obj->routingSocket );
157 logging_error( "could not bind routing socket: " + string(strerror(errno)) );
158 return;
159 }
160
161 //
162 // read from the netlink routing socket
163 //
164
165 logging_debug( "network change monitoring started" );
166
167 while( obj->running ){
168
169 char buffer[1024];
170 struct nlmsghdr* header = (struct nlmsghdr*)buffer;
171
172 int bytesRead = recv( obj->routingSocket, &buffer, sizeof(buffer), 0 );
173
174 if( bytesRead < 0 ){
175
176 // socket timeout, just continue
177 if( errno == EWOULDBLOCK ) continue;
178
179 // triggered by shutdown() function in stopMonitoring
180 if( errno == EBADF ) return;
181
182 // gracefull exit by signals
183 if( errno == EAGAIN || errno == EINTR ) return;
184
185 // all others are some kind of error
186 logging_error( "could not read from routing socket: " +
187 string(strerror(errno)) );
188 break;
189 }
190
191 for( ; bytesRead > 0; header = NLMSG_NEXT(header, bytesRead)) {
192 if ( !NLMSG_OK(header, bytesRead) ||
193 (size_t) bytesRead < sizeof(struct nlmsghdr) ||
194 (size_t) bytesRead < header->nlmsg_len) {
195 continue;
196 }
197
198 //
199 // here we evaluate the routing netlink message. for, the following
200 // messages are of interest:
201 // RTM_NEWLINK, RTM_DELLINK -> interface up/down
202 // RTM_NEWADDR, RTM_DELADDR -> new address on iface, address deleted from iface
203 // not interesting are:
204 // RTM_GETLINK, RTM_GETADDR
205 // RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE
206 //
207
208 NetworkChangeInterface::NetworkChangeInfo changeInfo;
209
210 switch( header->nlmsg_type ){
211
212 //
213 // handle network interface up/down notifications
214 // --> link layer notifications
215 //
216
217 case RTM_NEWLINK:
218 case RTM_DELLINK:
219
220 changeInfo = obj->extractInterfaceEvent( header );
221 break;
222
223 //
224 // handle network address configuration changes
225 // --> network layer notifications
226 //
227
228 case RTM_NEWADDR:
229 case RTM_DELADDR:
230
231 changeInfo = obj->extractAddressEvent( header );
232 break;
233
234 //
235 // other RTM message are ignored in case
236 // we did subscribe using nl_groups (see above)
237 //
238
239 default: break;
240
241 } // switch( header->nlmsg_type )
242
243 logging_info( "network change detected: [" << changeInfo << "]" );
244
245 // put noficiation event into the system queue for main
246 // thread synchronization. will be handled in NetworkChangeDetection::handleSystemEvent()
247 // that will run synchronized with the main thread
248
249 SystemQueue::instance().scheduleEvent(
250 SystemEvent( obj, NetworkChangeDetectionEventType,
251 new NetworkChangeInterface::NetworkChangeInfo(changeInfo)));
252
253 } // for( ; bytesRead > 0; header = NLMSG_NEXT(header, bytesRead))
254 } // while( running )
255
256 logging_debug( "network change monitoring stopped" );
257}
258
259NetworkChangeInterface::NetworkChangeInfo NetworkChangeDetection::extractInterfaceEvent(struct nlmsghdr* header){
260 NetworkChangeInterface::NetworkChangeInfo changeInfo;
261
262 //
263 // handle network interface up/down notifications
264 // the differentiation between up/down is _not_
265 // made using new/del-link in the message!
266 //
267
268 if( header->nlmsg_type == RTM_NEWLINK ){
269 logging_debug("network change message RTM_NEWLINK");
270 }else if( header->nlmsg_type == RTM_DELLINK ){
271 logging_debug("network change message RTM_DELLINK");
272 }
273
274 if( header->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)) ){
275 logging_error( "netlink packet soo small for ifinfomsg" );
276 return changeInfo;
277 }
278
279 //
280 // get the interface index and through this
281 // further information about the interface
282 //
283
284 struct ifinfomsg* detailIface = (struct ifinfomsg*)NLMSG_DATA(header);
285 changeInfo.interface = networkInformation.getInterface( detailIface->ifi_index );
286
287 //
288 // check whether device is now up or down
289 //
290
291 if( changeInfo.interface.isUp && changeInfo.interface.isRunning )
292 changeInfo.type = NetworkChangeInterface::EventTypeInterfaceUp;
293 else
294 changeInfo.type = NetworkChangeInterface::EventTypeInterfaceDown;
295
296
297 return changeInfo;
298}
299
300NetworkChangeInterface::NetworkChangeInfo NetworkChangeDetection::extractAddressEvent(struct nlmsghdr* header){
301 NetworkChangeInterface::NetworkChangeInfo changeInfo;
302
303 //
304 // handle network address configuration changes
305 // addresses are added/removed from an interface
306 //
307
308 if( header->nlmsg_type == RTM_NEWADDR ){
309 logging_debug("network change message RTM_NEWADDR");
310 changeInfo.type = NetworkChangeInterface::EventTypeAddressNew;
311 }else if( header->nlmsg_type == RTM_DELADDR ){
312 logging_debug("network change message RTM_DELADDR");
313 changeInfo.type = NetworkChangeInterface::EventTypeAddressDelete;
314 }
315
316 struct ifaddrmsg* detailAddr = (struct ifaddrmsg*)NLMSG_DATA(header);
317 changeInfo.interface = networkInformation.getInterface( detailAddr->ifa_index );
318
319 return changeInfo;
320}
321
322void NetworkChangeDetection::handleSystemEvent(const SystemEvent& event){
323
324 NetworkChangeInterface::NetworkChangeInfo* changeInfo = event.getData<NetworkChangeInterface::NetworkChangeInfo>();
325 const NetworkChangeDetection* obj = (const NetworkChangeDetection*)event.getListener();
326
327 RegistrationList::const_iterator i = obj->registrations.begin();
328 RegistrationList::const_iterator iend = obj->registrations.end();
329
330 for( ; i != iend; i++ )
331 (*i)->onNetworkChange(*changeInfo);
332
333 delete changeInfo;
334}
335
336}} // namespace ariba, communication
Note: See TracBrowser for help on using the repository browser.