E-MailRelay

ginterfaces_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file ginterfaces_unix.cpp
19///
20// Linux:
21// ip address add 127.0.0.2/8 dev lo
22// ip address del 127.0.0.2/8 dev lo
23// ip address add fe80::dead:beef/64 dev eth0
24// ip address del fe80::dead:beef/64 dev eth0
25//
26// BSD...
27// ifconfig lo0 inet 127.0.0.2 alias netmask 255.0.0.0
28// ifconfig lo0 inet 127.0.0.2 -alias
29// ifconfig em0 inet6 fe80::dead:beef/64 alias
30// ifconfig em0 inet6 fe80::dead:beef/64 -alias
31//
32
33#include "gdef.h"
34#include "ginterfaces.h"
35#include "gassert.h"
36#include "gexception.h"
37#include "geventloop.h"
38#include "gprocess.h"
39#include "gbuffer.h"
40#include "gstr.h"
41#include "groot.h"
42#include <ifaddrs.h>
43#include <functional>
44#include <memory>
45#include <utility>
46#include <sstream>
47
48 namespace GNet
49{
50 class InterfacesNotifierImp ;
51}
52
53//| \class GNet::InterfacesNotifierImp
54/// Handles read events on a routing netlink socket.
55///
56 class GNet::InterfacesNotifierImp : public InterfacesNotifier
57{
58 public:
59 static bool active() ;
60 InterfacesNotifierImp( Interfaces * , ExceptionSink es ) ;
61 std::string readEvent() override ; // unix
62 std::string onFutureEvent() override ; // windows
63 template <typename T> std::pair<T*,std::size_t> readSocket() ;
64
65 public:
66 G::Buffer<char> m_buffer ;
67 std::unique_ptr<RawSocket> m_socket ;
68} ;
69
70// ==
71
72 bool GNet::Interfaces::active()
73{
74 return InterfacesNotifierImp::active() ;
75}
76
77 void GNet::Interfaces::loadImp( ExceptionSink es , std::vector<Item> & list )
78{
79 if( !m_notifier )
80 m_notifier = std::make_unique<InterfacesNotifierImp>( this , es ) ;
81
82 ifaddrs * info_p = nullptr ;
83 int rc = getifaddrs( &info_p ) ;
84 if( rc < 0 )
85 {
86 int e = G::Process::errno_() ;
87 throw G::Exception( "getifaddrs error" , G::Process::strerror(e) ) ;
88 }
89
90 using deleter_fn_t = std::function<void(ifaddrs*)> ;
91 using deleter_t = std::unique_ptr<ifaddrs,deleter_fn_t> ;
92 deleter_t deleter( info_p , deleter_fn_t(freeifaddrs) ) ;
93
94 const std::size_t nmax = AddressStorage().n() ;
95 const bool scope_id_fixup = G::is_bsd() ;
96 for( ; info_p != nullptr ; info_p = info_p->ifa_next )
97 {
98 G_ASSERT( info_p->ifa_name && info_p->ifa_name[0] ) ;
99 if( info_p->ifa_name == nullptr )
100 continue ;
101
102 G_ASSERT( info_p->ifa_addr ) ;
103 if( info_p->ifa_addr == nullptr )
104 continue ;
105
106 if( !Address::supports(info_p->ifa_addr->sa_family,0) )
107 continue ;
108
109 Item item ;
110 item.name = std::string( info_p->ifa_name ) ;
111 item.address_family = info_p->ifa_addr->sa_family ;
112 item.address = Address( info_p->ifa_addr , nmax , scope_id_fixup ) ;
113 item.valid_address = !item.address.isAny() ; // just in case
114 item.up = !!( info_p->ifa_flags & IFF_UP ) ;
115 item.loopback = !!( info_p->ifa_flags & IFF_LOOPBACK ) ;
116 item.has_netmask = info_p->ifa_netmask != nullptr ;
117
118 if( item.has_netmask )
119 {
120 if( info_p->ifa_netmask->sa_family == AF_UNSPEC ) // openbsd
121 info_p->ifa_netmask->sa_family = info_p->ifa_addr->sa_family ;
122
123 Address netmask( info_p->ifa_netmask , nmax ) ;
124 item.netmask_bits = netmask.bits() ;
125 }
126
127 list.push_back( item ) ;
128 }
129}
130
131// ==
132
133 template <typename T>
134std::pair<T*,std::size_t> GNet::InterfacesNotifierImp::readSocket()
135{
136 static_assert( sizeof(T) <= 4096U , "" ) ;
137 m_buffer.resize( 4096U ) ;
138
139 ssize_t rc = m_socket->read( &m_buffer[0] , m_buffer.size() ) ;
140 if( rc < 0 )
141 {
142 GDEF_UNUSED int e = G::Process::errno_() ;
143 G_DEBUG( "GNet::InterfacesNotifierImp: read error: " << G::Process::strerror(e) ) ;
144 }
145
146 T * p = G::buffer_cast<T*>( m_buffer , std::nothrow ) ;
147 std::size_t n = static_cast<std::size_t>( rc >= 0 && p ? rc : 0 ) ;
148 return { p , n } ;
149}
150
151std::string GNet::InterfacesNotifierImp::onFutureEvent()
152{
153 return std::string() ;
154}
155
156#if GCONFIG_HAVE_RTNETLINK
157
158#include <asm/types.h>
159#include <sys/socket.h>
160#include <linux/netlink.h>
161#include <linux/rtnetlink.h>
162
163 bool GNet::InterfacesNotifierImp::active()
164{
165 return true ;
166}
167
168GNet::InterfacesNotifierImp::InterfacesNotifierImp( Interfaces * outer , ExceptionSink es )
169{
170 if( EventLoop::exists() )
171 {
172 union netlink_address_union
173 {
174 struct sockaddr_nl specific ;
175 struct sockaddr generic ;
176 } ;
177 netlink_address_union address {} ;
178 address.specific.nl_family = AF_NETLINK ;
179 #if GCONFIG_HAVE_IPV6
180 address.specific.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR ;
181 #else
182 address.specific.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR ;
183 #endif
184 {
185 G::Root claim_root ;
186 m_socket = std::make_unique<RawSocket>( AF_NETLINK , SOCK_RAW , NETLINK_ROUTE ) ;
187 int rc = ::bind( m_socket->fd() , &address.generic , sizeof(address.specific) ) ;
188 int e = G::Process::errno_() ;
189 if( rc < 0 )
190 throw G::Exception( "netlink socket bind error" , G::Process::strerror(e) ) ;
191 }
192 m_socket->addReadHandler( *outer , es ) ;
193 }
194}
195
196std::string GNet::InterfacesNotifierImp::readEvent()
197{
198 auto buffer_pair = readSocket<nlmsghdr>() ;
199
200 const nlmsghdr * hdr = buffer_pair.first ;
201 std::size_t size = buffer_pair.second ;
202 if( hdr == nullptr || size == 0U )
203 return std::string() ;
204
205 const char * sep = "" ;
206 std::ostringstream ss ;
207 for( ; NLMSG_OK(hdr,size) ; hdr = NLMSG_NEXT(hdr,size) , sep = ", " )
208 {
209 if( hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR )
210 break ;
211
212 if( hdr->nlmsg_type == RTM_NEWLINK ||
213 hdr->nlmsg_type == RTM_DELLINK ||
214 hdr->nlmsg_type == RTM_GETLINK )
215 {
216 GDEF_UNUSED ifinfomsg * p = static_cast<ifinfomsg*>( NLMSG_DATA(hdr) ) ;
217 GDEF_UNUSED int n = NLMSG_PAYLOAD( hdr , size ) ;
218 ss << sep << "link" ;
219 if( hdr->nlmsg_type == RTM_NEWLINK ) ss << " new" ;
220 if( hdr->nlmsg_type == RTM_DELLINK ) ss << " deleted" ;
221 }
222 else if( hdr->nlmsg_type == RTM_NEWADDR ||
223 hdr->nlmsg_type == RTM_DELADDR ||
224 hdr->nlmsg_type == RTM_GETADDR )
225 {
226 GDEF_UNUSED ifaddrmsg * p = static_cast<ifaddrmsg*>( NLMSG_DATA(hdr) ) ;
227 GDEF_UNUSED int n = NLMSG_PAYLOAD( hdr , size ) ;
228 ss << sep << "address" ;
229 if( hdr->nlmsg_type == RTM_NEWADDR ) ss << " new" ;
230 if( hdr->nlmsg_type == RTM_DELADDR ) ss << " deleted" ;
231 }
232 }
233 return ss.str() ;
234}
235
236#else
237
238#if GCONFIG_HAVE_NETROUTE
239
240// see route(4)
241#include <net/route.h>
242
243 bool GNet::InterfacesNotifierImp::active()
244{
245 return true ;
246}
247
248GNet::InterfacesNotifierImp::InterfacesNotifierImp( Interfaces * outer , ExceptionSink es )
249{
250 if( EventLoop::exists() )
251 {
252 {
253 G::Root claim_root ;
254 m_socket = std::make_unique<RawSocket>( PF_ROUTE , SOCK_RAW , AF_UNSPEC ) ;
255 }
256 m_socket->addReadHandler( *outer , es ) ;
257 }
258}
259
260std::string GNet::InterfacesNotifierImp::readEvent()
261{
262 using Header = struct rt_msghdr ;
263 std::string result ;
264 auto buffer_pair = readSocket<Header>() ;
265 if( buffer_pair.second >= 4U )
266 {
267 Header * p = buffer_pair.first ;
268 if( p->rtm_msglen != buffer_pair.second )
269 G_DEBUG( "GNet::InterfacesNotifierImp::readEvent: invalid message length" ) ;
270 if( p->rtm_type == RTM_NEWADDR )
271 result = "address new" ;
272 else if( p->rtm_type == RTM_DELADDR )
273 result = "address deleted" ;
274 else if( p->rtm_type == RTM_IFINFO )
275 result = "interface change" ;
276 }
277 return result ;
278}
279
280#else
281
282 bool GNet::InterfacesNotifierImp::active()
283{
284 return false ;
285}
286
287GNet::InterfacesNotifierImp::InterfacesNotifierImp( Interfaces * , ExceptionSink )
288{
289}
290
291std::string GNet::InterfacesNotifierImp::readEvent()
292{
293 return std::string() ;
294}
295
296#endif
297#endif
GNet::Address::supports
static bool supports(Family)
Returns true if the implementation supports the given address family.
Definition: gaddress.cpp:33
GNet::EventLoop::exists
static bool exists()
Returns true if an instance exists.
Definition: geventloop.cpp:54
GNet::ExceptionSink
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
Definition: gexceptionsink.h:100
GNet::InterfacesNotifierImp
Handles read events on a routing netlink socket.
Definition: ginterfaces_unix.cpp:57
GNet::InterfacesNotifierImp::readEvent
std::string readEvent() override
Called by GNet::Interfaces to handle a read event.
Definition: ginterfaces_unix.cpp:291
GNet::InterfacesNotifierImp::onFutureEvent
std::string onFutureEvent() override
Called by GNet::Interfaces to handle a future event.
Definition: ginterfaces_unix.cpp:151
GNet::InterfacesNotifier
A pimple base-class used by GNet::Interfaces.
Definition: ginterfaces.h:157
GNet::Interfaces
A class for getting a list of network interfaces and their addresses.
Definition: ginterfaces.h:45
GNet::Interfaces::active
static bool active()
Returns true if the implementation can raise InterfacesHandler events.
Definition: ginterfaces_unix.cpp:72
G::Exception
A general-purpose exception class derived from std::exception and containing an error message.
Definition: gexception.h:45
G::Process::strerror
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
Definition: gprocess_unix.cpp:127
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:115
G::Root
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:52
gassert.h
gbuffer.h
geventloop.h
gexception.h
ginterfaces.h
gprocess.h
groot.h
gstr.h
GNet
Network classes.
Definition: gdef.h:1115
G::Buffer< char >