An Overlay-based
Virtual Network Substrate
SpoVNet

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

Last change on this file since 3690 was 3690, checked in by mies, 14 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.