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