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