Line data Source code
1 : // Network Address -- route class functions
2 : // Copyright (c) 2018 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // https://snapwebsites.org/project/libaddr
5 : //
6 : // Permission is hereby granted, free of charge, to any person obtaining a
7 : // copy of this software and associated documentation files (the
8 : // "Software"), to deal in the Software without restriction, including
9 : // without limitation the rights to use, copy, modify, merge, publish,
10 : // distribute, sublicense, and/or sell copies of the Software, and to
11 : // permit persons to whom the Software is furnished to do so, subject to
12 : // the following conditions:
13 : //
14 : // The above copyright notice and this permission notice shall be included
15 : // in all copies or substantial portions of the Software.
16 : //
17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 : //
25 :
26 : /** \file
27 : * \brief The implementation of the route class.
28 : *
29 : * This file includes the implementation of the route class.
30 : *
31 : * The route class registers information about a route. It is created
32 : * by using the get_routes() function.
33 : */
34 :
35 : // self
36 : //
37 : #include "libaddr/route.h"
38 :
39 : // C++ library
40 : //
41 : #include <algorithm>
42 : #include <fstream>
43 : #include <iostream>
44 :
45 : // C library
46 : //
47 : #include <net/route.h>
48 :
49 :
50 :
51 : namespace addr
52 : {
53 :
54 :
55 : typedef std::vector<std::string> words_t;
56 :
57 :
58 : /** \brief Details used by the route class implementation.
59 : *
60 : * The following handles the route class and gathering of the routes.
61 : */
62 : namespace
63 : {
64 :
65 :
66 : /** \brief Read one line of content from a file.
67 : *
68 : * This function reads one line of content up to the next '\n'.
69 : *
70 : * \param[in,out] in The input stream.
71 : * \param[out] line The line just read.
72 : *
73 : * \return 0 on success, -1 on error (generally EOF)
74 : */
75 27 : int readwords(std::ifstream & in, words_t & words)
76 : {
77 27 : words.clear();
78 54 : std::string w;
79 3048 : for(;;)
80 : {
81 3075 : int const c(in.get());
82 3075 : if(c < 0)
83 : {
84 3 : return -1;
85 : }
86 3072 : if(c == '\n')
87 : {
88 24 : return 0;
89 : }
90 3048 : if(c == '\t' || c == ' ')
91 : {
92 : // there should always be a first word, however, there can
93 : // be multiple '\t' or ' ' after one another
94 4122 : if(!w.empty())
95 : {
96 264 : words.push_back(w);
97 264 : w.clear();
98 : }
99 : }
100 : else
101 : {
102 987 : w += c;
103 : }
104 : }
105 : }
106 :
107 :
108 33 : int get_position(words_t const & headers, std::string const & column_name)
109 : {
110 : auto it(std::find(
111 : headers.cbegin()
112 : , headers.cend()
113 33 : , column_name));
114 :
115 33 : if(it == headers.end())
116 : {
117 : return -1; // LCOV_EXCL_LINE
118 : }
119 :
120 33 : return it - headers.begin();
121 : }
122 :
123 :
124 231 : std::string const & get_value(words_t const & entries, int pos)
125 : {
126 231 : static std::string const not_found;
127 :
128 231 : if(pos < static_cast<int>(entries.size()))
129 : {
130 231 : return entries[pos];
131 : }
132 :
133 : return not_found; // LCOV_EXCL_LINE
134 : }
135 :
136 :
137 504 : int hex_to_number(char c)
138 : {
139 504 : if(c >= '0' && c <= '9')
140 : {
141 351 : return c - '0';
142 : }
143 153 : if(c >= 'a' && c <= 'f')
144 : {
145 : return c - 'a' + 10; // LCOV_EXCL_LINE
146 : }
147 153 : if(c >= 'A' && c <= 'F')
148 : {
149 153 : return c - 'A' + 10;
150 : }
151 : throw std::runtime_error("invalid hexadecimal digit"); // LCOV_EXCL_LINE
152 : }
153 :
154 :
155 63 : addr hex_to_addr(std::string const & address)
156 : {
157 63 : if(address.length() != 8)
158 : {
159 : throw std::runtime_error("invalid length for an hex address"); // LCOV_EXCL_LINE
160 : }
161 :
162 63 : struct sockaddr_in in = sockaddr_in();
163 63 : in.sin_family = AF_INET;
164 63 : in.sin_port = 0;
165 : in.sin_addr.s_addr =
166 63 : (hex_to_number(address[7]) << 0)
167 63 : | (hex_to_number(address[6]) << 4)
168 63 : | (hex_to_number(address[5]) << 8)
169 63 : | (hex_to_number(address[4]) << 12)
170 63 : | (hex_to_number(address[3]) << 16)
171 63 : | (hex_to_number(address[2]) << 20)
172 63 : | (hex_to_number(address[1]) << 24)
173 63 : | (hex_to_number(address[0]) << 28)
174 63 : ;
175 :
176 63 : return addr(in);
177 : }
178 :
179 :
180 : struct flag_name_t
181 : {
182 : uint32_t const f_flag;
183 : char const f_name;
184 : };
185 :
186 : /** \brief Flags used by route tables.
187 : *
188 : * \note
189 : * The list of flags presented here includes IPv4 and IPv6 flags.
190 : *
191 : * \note
192 : * Some of the flags are not defined in Ubuntu 16.04.
193 : */
194 : flag_name_t const g_rtf_flag_names[] =
195 : {
196 : { RTF_UP, 'U' },
197 : { RTF_GATEWAY, 'G' },
198 : { RTF_REJECT, '!' }, // may not be defined
199 : { RTF_HOST, 'H' },
200 : { RTF_REINSTATE, 'R' },
201 : { RTF_DYNAMIC, 'D' },
202 : { RTF_MODIFIED, 'M' },
203 : { RTF_DEFAULT, 'd' },
204 : { RTF_ALLONLINK, 'a' },
205 : { RTF_ADDRCONF, 'c' },
206 : { RTF_NONEXTHOP, 'o' },
207 : //{ RTF_EXPIRES, 'e' },
208 : { RTF_CACHE, 'C' },
209 : { RTF_FLOW, 'f' },
210 : { RTF_POLICY, 'p' },
211 : { RTF_LOCAL, 'l' },
212 : { RTF_MTU, 'u' },
213 : { RTF_WINDOW, 'w' },
214 : { RTF_IRTT, 'i' },
215 : //{ RTF_NOTCACHED, 'n' },
216 : };
217 :
218 :
219 :
220 : }
221 : // no name namespace
222 :
223 :
224 :
225 : /** \brief Read the list of routes.
226 : *
227 : * This function reads the list of routes using the /proc/net/routes
228 : * file. It returns a vector of easy to use route objects.
229 : *
230 : * The content of the route table is scanned using the column names
231 : * so it makes sure that it does not use the wrong column (i.e.
232 : * expect that the columns never change over time.)
233 : *
234 : * \note
235 : * If an error occurs, the reurned vector is empty and errno is
236 : * set to the error that happened. The ENODATA error is used
237 : * if some mandatory columns are missing and thus this function
238 : * cannot properly load the columns.
239 : *
240 : * \todo
241 : * Write the IPv6 function. It's similar only there are no headers
242 : * and (obviously?!) the IPs are IPv6 instead of IPv4.
243 : *
244 : * \return A vector of the routes found in the file.
245 : */
246 3 : route::vector_t route::get_ipv4_routes()
247 : {
248 : // the 'route' tool uses '/proc/net/route' so we do that too here
249 : //
250 3 : route::vector_t routes;
251 :
252 6 : std::ifstream in("/proc/net/route");
253 :
254 : // the first line is a set of headers, we use that to make sure that
255 : // we know what each column is
256 : //
257 6 : words_t headers;
258 3 : int e(readwords(in, headers));
259 3 : if(e < 0)
260 : {
261 : return routes; // LCOV_EXCL_LINE
262 : }
263 :
264 : // TODO: we may want to remove case although I don't think it will
265 : // change over time, it could be one more thing that could...
266 :
267 3 : int const pos_iface (get_position(headers, "Iface"));
268 3 : int const pos_destination(get_position(headers, "Destination"));
269 3 : int const pos_gateway (get_position(headers, "Gateway"));
270 3 : int const pos_flags (get_position(headers, "Flags"));
271 3 : int const pos_refcnt (get_position(headers, "RefCnt"));
272 3 : int const pos_use (get_position(headers, "Use"));
273 3 : int const pos_metric (get_position(headers, "Metric"));
274 3 : int const pos_mask (get_position(headers, "Mask"));
275 3 : int const pos_mtu (get_position(headers, "MTU"));
276 3 : int const pos_window (get_position(headers, "Window"));
277 3 : int const pos_irtt (get_position(headers, "IRTT"));
278 :
279 3 : if(pos_iface == -1
280 3 : || pos_destination == -1
281 3 : || pos_gateway == -1)
282 : {
283 : errno = ENODATA; // LCOV_EXCL_LINE
284 : return routes; // LCOV_EXCL_LINE
285 : }
286 :
287 21 : for(;;)
288 : {
289 : // read one entry
290 : //
291 45 : words_t entries;
292 24 : e = readwords(in, entries);
293 24 : if(e < 0)
294 : {
295 3 : break;
296 : }
297 :
298 : // convert each column to data in a 'route' object
299 : //
300 42 : route r;
301 :
302 21 : r.f_interface_name = get_value(entries, pos_iface);
303 21 : r.f_destination_address = hex_to_addr(get_value(entries, pos_destination));
304 21 : r.f_gateway_address = hex_to_addr(get_value(entries, pos_gateway));
305 21 : r.f_flags = std::stol(get_value(entries, pos_flags));
306 21 : r.f_reference_count = std::stol(get_value(entries, pos_refcnt));
307 21 : r.f_use = std::stol(get_value(entries, pos_use));
308 21 : r.f_metric = std::stol(get_value(entries, pos_metric));
309 21 : r.f_mtu = std::stol(get_value(entries, pos_mtu));
310 21 : r.f_window = std::stol(get_value(entries, pos_window));
311 21 : r.f_irtt = std::stol(get_value(entries, pos_irtt));
312 :
313 : // the mask is handled specially
314 : //
315 42 : std::string mask_str(get_value(entries, pos_mask));
316 21 : if(!mask_str.empty())
317 : {
318 21 : addr mask = hex_to_addr(mask_str);
319 21 : struct sockaddr_in ipv4 = sockaddr_in();
320 21 : mask.get_ipv4(ipv4);
321 21 : uint8_t m[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
322 21 : m[12] = ipv4.sin_addr.s_addr >> 0;
323 21 : m[13] = ipv4.sin_addr.s_addr >> 8;
324 21 : m[14] = ipv4.sin_addr.s_addr >> 16;
325 21 : m[15] = ipv4.sin_addr.s_addr >> 24;
326 21 : r.f_destination_address.set_mask(m);
327 : }
328 :
329 21 : routes.push_back(pointer_t(new route(r)));
330 : }
331 :
332 3 : return routes;
333 : }
334 :
335 :
336 15 : std::string const & route::get_interface_name() const
337 : {
338 15 : return f_interface_name;
339 : }
340 :
341 :
342 8 : addr const & route::get_destination_address() const
343 : {
344 8 : return f_destination_address;
345 : }
346 :
347 :
348 7 : addr const & route::get_gateway_address() const
349 : {
350 7 : return f_gateway_address;
351 : }
352 :
353 :
354 7 : int route::get_flags() const
355 : {
356 7 : return f_flags;
357 : }
358 :
359 :
360 7 : std::string route::flags_to_string() const
361 : {
362 7 : std::string result;
363 :
364 : std::for_each(
365 : g_rtf_flag_names
366 7 : , g_rtf_flag_names + sizeof(g_rtf_flag_names) / sizeof(g_rtf_flag_names[0])
367 126 : , [&result, this](auto const & fn)
368 134 : {
369 126 : if((f_flags & fn.f_flag) != 0)
370 : {
371 16 : result += fn.f_name;
372 : }
373 140 : });
374 :
375 7 : return result;
376 : }
377 :
378 :
379 7 : int route::get_reference_count() const
380 : {
381 7 : return f_reference_count;
382 : }
383 :
384 :
385 7 : int route::get_use() const
386 : {
387 7 : return f_use;
388 : }
389 :
390 :
391 7 : int route::get_metric() const
392 : {
393 7 : return f_metric;
394 : }
395 :
396 :
397 7 : int route::get_mtu() const
398 : {
399 7 : return f_mtu;
400 : }
401 :
402 :
403 7 : int route::get_window() const
404 : {
405 7 : return f_window;
406 : }
407 :
408 :
409 7 : int route::get_irtt() const
410 : {
411 7 : return f_irtt;
412 : }
413 :
414 :
415 2 : route::pointer_t find_default_route(route::vector_t const & routes)
416 : {
417 : auto it(std::find_if(
418 : routes.cbegin()
419 : , routes.cend()
420 1 : , [](auto const & r)
421 : {
422 : return r->get_destination_address().is_default();
423 3 : }));
424 :
425 2 : if(it == routes.cend())
426 : {
427 1 : return nullptr;
428 : }
429 :
430 1 : return *it;
431 : }
432 :
433 :
434 :
435 6 : }
436 : // addr namespace
437 : // vim: ts=4 sw=4 et
|