1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* nm-platform.c - Handle runtime kernel networking configuration
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 2, or (at your option)
7 * 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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Copyright (C) 2012 Red Hat, Inc.
19 */
20
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <string.h>
27
28 #include "nm-platform.h"
29 #include "nm-logging.h"
30 #include "nm-enum-types.h"
31
32 #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
33
34 #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
35
36 G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
37
38 /* NMPlatform signals */
39 enum {
40 LINK_ADDED,
41 LINK_CHANGED,
42 LINK_REMOVED,
43 IP4_ADDRESS_ADDED,
44 IP4_ADDRESS_CHANGED,
45 IP4_ADDRESS_REMOVED,
46 IP6_ADDRESS_ADDED,
47 IP6_ADDRESS_CHANGED,
48 IP6_ADDRESS_REMOVED,
49 IP4_ROUTE_ADDED,
50 IP4_ROUTE_CHANGED,
51 IP4_ROUTE_REMOVED,
52 IP6_ROUTE_ADDED,
53 IP6_ROUTE_CHANGED,
54 IP6_ROUTE_REMOVED,
55 LAST_SIGNAL
56 };
57
58 static guint signals[LAST_SIGNAL] = { 0 };
59
60 /******************************************************************/
61
62 /* Singleton NMPlatform subclass instance and cached class object */
63 static NMPlatform *platform = NULL;
64 static NMPlatformClass *klass = NULL;
65
66 /**
67 * nm_platform_setup:
68 * @type: The #GType for a subclass of #NMPlatform
69 *
70 * Do not use this function directly, it is intended to be called by
71 * NMPlatform subclasses. For the linux platform initialization use
72 * nm_linux_platform_setup() instead.
73 *
74 * Failing to set up #NMPlatform singleton results in a fatal error,
75 * as well as trying to initialize it multiple times without freeing
76 * it.
77 *
78 * NetworkManager will typically use only one platform object during
79 * its run. Test programs might want to switch platform implementations,
80 * though. This is done with a combination of nm_platform_free() and
81 * nm_*_platform_setup().
82 */
83 void
84 nm_platform_setup (GType type)
85 {
86 gboolean status;
87
88 g_assert (platform == NULL);
89
90 platform = g_object_new (type, NULL);
91 g_assert (NM_IS_PLATFORM (platform));
92
93 klass = NM_PLATFORM_GET_CLASS (platform);
94 g_assert (klass->setup);
95
96 status = klass->setup (platform);
97 g_assert (status);
98 }
99
100 /**
101 * nm_platform_free:
102 *
103 * Free #NMPlatform singleton created by nm_*_platform_setup().
104 */
105 void
106 nm_platform_free (void)
107 {
108 g_assert (platform);
109
110 g_object_unref (platform);
111 platform = NULL;
112 }
113
114 /**
115 * nm_platform_get:
116 *
117 * Retrieve #NMPlatform singleton. Use this whenever you want to connect to
118 * #NMPlatform signals. It is an error to call it before nm_*_platform_setup()
119 * or after nm_platform_free().
120 *
121 * Returns: (transfer none): The #NMPlatform singleton reference.
122 */
123 NMPlatform *
124 nm_platform_get (void)
125 {
126 g_assert (platform);
127
128 return platform;
129 }
130
131 /******************************************************************/
132
133 /**
134 * nm_platform_set_error:
135 * @error: The error code
136 *
137 * Convenience function to falsify platform->error. It can be used for example
138 * by functions that want to save the error, execute some operations and
139 * restore it.
140 */
141 void nm_platform_set_error (NMPlatformError error)
142 {
143 platform->error = error;
144 }
145
146 /**
147 * nm_platform_get_error:
148 *
149 * Convenience function to quickly retrieve the error code of the last
150 * operation.
151 *
152 * Returns: Integer error code.
153 */
154 NMPlatformError
155 nm_platform_get_error (void)
156 {
157 g_assert (platform);
158
159 return platform->error;
160 }
161
162 /**
163 * nm_platform_get_error_message:
164 *
165 * Returns: Static human-readable string for the error. Don't free.
166 */
167 const char *
168 nm_platform_get_error_msg (void)
169 {
170 g_assert (platform);
171
172 switch (platform->error) {
173 case NM_PLATFORM_ERROR_NONE:
174 return "unknown error";
175 case NM_PLATFORM_ERROR_NOT_FOUND:
176 return "object not found";
177 case NM_PLATFORM_ERROR_EXISTS:
178 return "object already exists";
179 case NM_PLATFORM_ERROR_WRONG_TYPE:
180 return "object is wrong type";
181 case NM_PLATFORM_ERROR_NOT_SLAVE:
182 return "link not a slave";
183 case NM_PLATFORM_ERROR_NO_FIRMWARE:
184 return "firmware not found";
185 default:
186 return "invalid error number";
187 }
188 }
189
190 static void
191 reset_error (void)
192 {
193 g_assert (platform);
194 platform->error = NM_PLATFORM_ERROR_NONE;
195 }
196
197 /******************************************************************/
198
199 /**
200 * nm_platform_sysctl_set:
201 * @path: Absolute option path
202 * @value: Value to write
203 *
204 * This function is intended to be used for writing values to sysctl-style
205 * virtual runtime configuration files. This includes not only /proc/sys
206 * but also for example /sys/class.
207 *
208 * Returns: %TRUE on success.
209 */
210 gboolean
211 nm_platform_sysctl_set (const char *path, const char *value)
212 {
213 reset_error ();
214
215 g_return_val_if_fail (path, FALSE);
216 g_return_val_if_fail (value, FALSE);
217 g_return_val_if_fail (klass->sysctl_set, FALSE);
218
219 /* Don't write outside known locations */
220 g_assert (g_str_has_prefix (path, "/proc/sys")
221 || g_str_has_prefix (path, "/sys"));
222 /* Don't write to suspicious locations */
223 g_assert (!strstr (path, ".."));
224
225 return klass->sysctl_set (platform, path, value);
226 }
227
228 /**
229 * nm_platform_sysctl_get:
230 * @path: Absolute path to sysctl
231 *
232 * Returns: (transfer full): Contents of the virtual sysctl file.
233 */
234 char *
235 nm_platform_sysctl_get (const char *path)
236 {
237 reset_error ();
238
239 g_return_val_if_fail (path, NULL);
240 g_return_val_if_fail (klass->sysctl_get, NULL);
241
242 return klass->sysctl_get (platform, path);
243 }
244
245 /******************************************************************/
246
247 /**
248 * nm_platform_query_devices:
249 *
250 * Emit #NMPlatform:link-added signals for all currently-known links.
251 * Should only be called at startup.
252 */
253 void
254 nm_platform_query_devices (void)
255 {
256 GArray *links_array;
257 NMPlatformLink *links;
258 int i;
259
260 links_array = nm_platform_link_get_all ();
261 links = (NMPlatformLink *) links_array->data;
262 for (i = 0; i < links_array->len; i++) {
263 g_signal_emit (platform, signals[LINK_ADDED], 0,
264 links[i].ifindex, &links[i], NM_PLATFORM_REASON_INTERNAL);
265 }
266 g_array_unref (links_array);
267 }
268
269 static int
270 compare_links (gconstpointer a, gconstpointer b)
271 {
272 NMPlatformLink *link_a = (NMPlatformLink *) a;
273 NMPlatformLink *link_b = (NMPlatformLink *) b;
274 int sortindex_a, sortindex_b;
275
276 /* We mostly want to sort by ifindex. However, slaves should sort
277 * before their masters, and children (eg, VLANs) should sort after
278 * their parents.
279 */
280 if (link_a->master)
281 sortindex_a = link_a->master * 3 - 1;
282 else if (link_a->parent)
283 sortindex_a = link_a->parent * 3 + 1;
284 else
285 sortindex_a = link_a->ifindex * 3;
286
287 if (link_b->master)
288 sortindex_b = link_b->master * 3 - 1;
289 else if (link_b->parent)
290 sortindex_b = link_b->parent * 3 + 1;
291 else
292 sortindex_b = link_b->ifindex * 3;
293
294 if (sortindex_a == sortindex_b)
295 return link_a->ifindex - link_b->ifindex;
296 else
297 return sortindex_a - sortindex_b;
298 }
299
300 /**
301 * nm_platform_link_get_all:
302 *
303 * Retrieve a snapshot of configuration for all links at once. The result is
304 * owned by the caller and should be freed with g_array_unref().
305 */
306 GArray *
307 nm_platform_link_get_all (void)
308 {
309 GArray *links;
310
311 reset_error ();
312
313 g_return_val_if_fail (klass->link_get_all, NULL);
314
315 links = klass->link_get_all (platform);
316 g_array_sort (links, compare_links);
317 return links;
318 }
319
320 /**
321 * nm_platform_link_add:
322 * @name: Interface name
323 * @type: Interface type
324 *
325 * Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS
326 * if interface is already already exists. Any link-added signal will be
327 * emitted from an idle handler and not within this function.
328 */
329 static gboolean
330 nm_platform_link_add (const char *name, NMLinkType type)
331 {
332 reset_error ();
333
334 g_return_val_if_fail (name, FALSE);
335 g_return_val_if_fail (klass->link_add, FALSE);
336
337 if (nm_platform_link_exists (name)) {
338 debug ("link: already exists");
339 platform->error = NM_PLATFORM_ERROR_EXISTS;
340 return FALSE;
341 }
342
343 return klass->link_add (platform, name, type);
344 }
345
346 /**
347 * nm_platform_dummy_add:
348 * @name: New interface name
349 *
350 * Create a software ethernet-like interface
351 */
352 gboolean
353 nm_platform_dummy_add (const char *name)
354 {
355 g_return_val_if_fail (name, FALSE);
356
357 debug ("link: adding dummy '%s'", name);
358 return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY);
359 }
360
361 /**
362 * nm_platform_link_exists:
363 * @name: Interface name
364 *
365 * Returns: %TRUE if an interface of this name exists, %FALSE otherwise.
366 */
367 gboolean
368 nm_platform_link_exists (const char *name)
369 {
370 int ifindex = nm_platform_link_get_ifindex (name);
371
372 reset_error();
373 return ifindex > 0;
374 }
375
376 /**
377 * nm_platform_link_delete:
378 * @ifindex: Interface index
379 *
380 * Delete a software interface. Sets platform->error to
381 * NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available.
382 */
383 gboolean
384 nm_platform_link_delete (int ifindex)
385 {
386 const char *name;
387
388 reset_error ();
389
390 g_return_val_if_fail (klass->link_delete, FALSE);
391
392 name = nm_platform_link_get_name (ifindex);
393
394 if (!name)
395 return FALSE;
396
397 debug ("link: deleting '%s' (%d)", name, ifindex);
398 return klass->link_delete (platform, ifindex);
399 }
400
401 /**
402 * nm_platform_link_get_index:
403 * @name: Interface name
404 *
405 * Returns: The interface index corresponding to the given interface name
406 * or 0. Inteface name is owned by #NMPlatform, don't free it.
407 */
408 int
409 nm_platform_link_get_ifindex (const char *name)
410 {
411 int ifindex;
412
413 reset_error ();
414
415 g_return_val_if_fail (name, 0);
416 g_return_val_if_fail (klass->link_get_ifindex, 0);
417
418 ifindex = klass->link_get_ifindex (platform, name);
419
420 if (!ifindex) {
421 debug ("link not found: %s", name);
422 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
423 }
424
425 return ifindex;
426 }
427
428 /**
429 * nm_platform_link_get_name:
430 * @name: Interface name
431 *
432 * Returns: The interface name corresponding to the given interface index
433 * or %NULL.
434 */
435 const char *
436 nm_platform_link_get_name (int ifindex)
437 {
438 const char *name;
439
440 reset_error ();
441
442 g_return_val_if_fail (klass->link_get_name, NULL);
443
444 name = klass->link_get_name (platform, ifindex);
445
446 if (!name) {
447 debug ("link not found: %d", ifindex);
448 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
449 return FALSE;
450 }
451
452 return name;
453 }
454
455 /**
456 * nm_platform_link_get_type:
457 * @ifindex: Interface index.
458 *
459 * Returns: Link type constant as defined in nm-platform.h. On error,
460 * NM_LINK_TYPE_NONE is returned.
461 */
462 NMLinkType
463 nm_platform_link_get_type (int ifindex)
464 {
465 reset_error ();
466
467 g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE);
468
469 return klass->link_get_type (platform, ifindex);
470 }
471
472 /**
473 * nm_platform_link_get_type_name:
474 * @ifindex: Interface index.
475 *
476 * Returns: A string describing the type of link. In some cases this
477 * may be more specific than nm_platform_link_get_type(), but in
478 * other cases it may not. On error, %NULL is returned.
479 */
480 const char *
481 nm_platform_link_get_type_name (int ifindex)
482 {
483 reset_error ();
484
485 g_return_val_if_fail (klass->link_get_type_name, NULL);
486
487 return klass->link_get_type_name (platform, ifindex);
488 }
489
490 /**
491 * nm_platform_link_is_software:
492 * @ifindex: Interface index.
493 *
494 * Returns: %TRUE if ifindex belongs to a software interface, not backed by
495 * a physical device.
496 */
497 gboolean
498 nm_platform_link_is_software (int ifindex)
499 {
500 return (nm_platform_link_get_type (ifindex) & 0x10000);
501 }
502
503 /**
504 * nm_platform_link_supports_slaves:
505 * @ifindex: Interface index.
506 *
507 * Returns: %TRUE if ifindex belongs to an interface capable of enslaving
508 * other interfaces.
509 */
510 gboolean
511 nm_platform_link_supports_slaves (int ifindex)
512 {
513 return (nm_platform_link_get_type (ifindex) & 0x20000);
514 }
515
516 /**
517 * nm_platform_link_is_up:
518 * @ifindex: Interface index
519 *
520 * Check if the interface is up.
521 */
522 gboolean
523 nm_platform_link_is_up (int ifindex)
524 {
525 reset_error ();
526
527 g_return_val_if_fail (ifindex >= 0, FALSE);
528 g_return_val_if_fail (klass->link_is_up, FALSE);
529
530 return klass->link_is_up (platform, ifindex);
531 }
532
533 /**
534 * nm_platform_link_is_connected:
535 * @ifindex: Interface index
536 *
537 * Check if the interface is connected.
538 */
539 gboolean
540 nm_platform_link_is_connected (int ifindex)
541 {
542 reset_error ();
543
544 g_return_val_if_fail (ifindex >= 0, FALSE);
545 g_return_val_if_fail (klass->link_is_connected, FALSE);
546
547 return klass->link_is_connected (platform, ifindex);
548 }
549
550 /**
551 * nm_platform_link_uses_arp:
552 * @ifindex: Interface index
553 *
554 * Check if the interface is configured to use ARP.
555 */
556 gboolean
557 nm_platform_link_uses_arp (int ifindex)
558 {
559 reset_error ();
560
561 g_return_val_if_fail (ifindex >= 0, FALSE);
562 g_return_val_if_fail (klass->link_uses_arp, FALSE);
563
564 return klass->link_uses_arp (platform, ifindex);
565 }
566
567 /**
568 * nm_platform_link_set_address:
569 * @ifindex: Interface index
570 * @address: The new MAC address
571 *
572 * Set interface MAC address.
573 */
574 gboolean
575 nm_platform_link_set_address (int ifindex, gconstpointer address, size_t length)
576 {
577 reset_error ();
578
579 g_return_val_if_fail (ifindex > 0, FALSE);
580 g_return_val_if_fail (address, FALSE);
581 g_return_val_if_fail (length > 0, FALSE);
582 g_return_val_if_fail (klass->link_set_address, FALSE);
583
584 debug ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (ifindex), ifindex);
585 return klass->link_set_address (platform, ifindex, address, length);
586 }
587
588 /**
589 * nm_platform_link_get_address:
590 * @ifindex: Interface index
591 * @length: Pointer to a variable to store address length
592 *
593 * Saves interface hardware address to @address.
594 */
595 gconstpointer
596 nm_platform_link_get_address (int ifindex, size_t *length)
597 {
598 reset_error ();
599
600 if (length)
601 *length = 0;
602
603 g_return_val_if_fail (ifindex > 0, NULL);
604 g_return_val_if_fail (klass->link_get_address, NULL);
605
606 return klass->link_get_address (platform, ifindex, length);
607 }
608
609 gboolean
610 nm_platform_link_supports_carrier_detect (int ifindex)
611 {
612 g_return_val_if_fail (ifindex >= 0, FALSE);
613 g_return_val_if_fail (klass->link_supports_carrier_detect, FALSE);
614
615 return klass->link_supports_carrier_detect (platform, ifindex);
616 }
617
618 gboolean
619 nm_platform_link_supports_vlans (int ifindex)
620 {
621 g_return_val_if_fail (ifindex >= 0, FALSE);
622 g_return_val_if_fail (klass->link_supports_vlans, FALSE);
623
624 return klass->link_supports_vlans (platform, ifindex);
625 }
626
627 /**
628 * nm_platform_link_set_up:
629 * @ifindex: Interface index
630 *
631 * Bring the interface up.
632 */
633 gboolean
634 nm_platform_link_set_up (int ifindex)
635 {
636 reset_error ();
637
638 g_return_val_if_fail (ifindex > 0, FALSE);
639 g_return_val_if_fail (klass->link_set_up, FALSE);
640
641 debug ("link: setting up '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
642 return klass->link_set_up (platform, ifindex);
643 }
644
645 /**
646 * nm_platform_link_set_down:
647 * @ifindex: Interface index
648 *
649 * Take the interface down.
650 */
651 gboolean
652 nm_platform_link_set_down (int ifindex)
653 {
654 reset_error ();
655
656 g_return_val_if_fail (ifindex > 0, FALSE);
657 g_return_val_if_fail (klass->link_set_down, FALSE);
658
659 debug ("link: setting down '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
660 return klass->link_set_down (platform, ifindex);
661 }
662
663 /**
664 * nm_platform_link_set_arp:
665 * @ifindex: Interface index
666 *
667 * Enable ARP on the interface.
668 */
669 gboolean
670 nm_platform_link_set_arp (int ifindex)
671 {
672 reset_error ();
673
674 g_return_val_if_fail (ifindex >= 0, FALSE);
675 g_return_val_if_fail (klass->link_set_arp, FALSE);
676
677 debug ("link: setting arp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
678 return klass->link_set_arp (platform, ifindex);
679 }
680
681 /**
682 * nm_platform_link_set_noarp:
683 * @ifindex: Interface index
684 *
685 * Disable ARP on the interface.
686 */
687 gboolean
688 nm_platform_link_set_noarp (int ifindex)
689 {
690 reset_error ();
691
692 g_return_val_if_fail (ifindex >= 0, FALSE);
693 g_return_val_if_fail (klass->link_set_noarp, FALSE);
694
695 debug ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex);
696 return klass->link_set_noarp (platform, ifindex);
697 }
698
699 /**
700 * nm_platform_link_set_mtu:
701 * @ifindex: Interface index
702 * @mtu: The new MTU value
703 *
704 * Set interface MTU.
705 */
706 gboolean
707 nm_platform_link_set_mtu (int ifindex, guint32 mtu)
708 {
709 reset_error ();
710
711 g_return_val_if_fail (ifindex >= 0, FALSE);
712 g_return_val_if_fail (mtu > 0, FALSE);
713 g_return_val_if_fail (klass->link_set_mtu, FALSE);
714
715 debug ("link: setting '%s' (%d) mtu %d", nm_platform_link_get_name (ifindex), ifindex, mtu);
716 return klass->link_set_mtu (platform, ifindex, mtu);
717 }
718
719 /**
720 * nm_platform_link_get_mtu:
721 * @ifindex: Interface index
722 *
723 * Returns: MTU value for the interface or 0 on error.
724 */
725 guint32
726 nm_platform_link_get_mtu (int ifindex)
727 {
728 reset_error ();
729
730 g_return_val_if_fail (ifindex >= 0, 0);
731 g_return_val_if_fail (klass->link_get_mtu, 0);
732
733 return klass->link_get_mtu (platform, ifindex);
734 }
735
736 /**
737 * nm_platform_link_enslave:
738 * @master: Interface index of the master
739 * @slave: Interface index of the slave
740 *
741 * Enslave @slave to @master.
742 */
743 gboolean
744 nm_platform_link_enslave (int master, int slave)
745 {
746 reset_error ();
747
748 g_assert (platform);
749 g_return_val_if_fail (master > 0, FALSE);
750 g_return_val_if_fail (slave> 0, FALSE);
751 g_return_val_if_fail (klass->link_enslave, FALSE);
752
753 debug ("link: enslaving '%s' (%d) to master '%s' (%d)",
754 nm_platform_link_get_name (slave), slave,
755 nm_platform_link_get_name (master), master);
756 return klass->link_enslave (platform, master, slave);
757 }
758
759 /**
760 * nm_platform_link_release:
761 * @master: Interface index of the master
762 * @slave: Interface index of the slave
763 *
764 * Release @slave from @master.
765 */
766 gboolean
767 nm_platform_link_release (int master, int slave)
768 {
769 reset_error ();
770
771 g_assert (platform);
772 g_return_val_if_fail (master > 0, FALSE);
773 g_return_val_if_fail (slave > 0, FALSE);
774 g_return_val_if_fail (klass->link_release, FALSE);
775
776 if (nm_platform_link_get_master (slave) != master) {
777 platform->error = NM_PLATFORM_ERROR_NOT_SLAVE;
778 return FALSE;
779 }
780
781 debug ("link: releasing '%s' (%d) from master '%s' (%d)",
782 nm_platform_link_get_name (slave), slave,
783 nm_platform_link_get_name (master), master);
784 return klass->link_release (platform, master, slave);
785 }
786
787 /**
788 * nm_platform_link_get_master:
789 * @slave: Interface index of the slave.
790 *
791 * Returns: Interfase index of the slave's master.
792 */
793 int
794 nm_platform_link_get_master (int slave)
795 {
796 reset_error ();
797
798 g_assert (platform);
799 g_return_val_if_fail (slave >= 0, FALSE);
800 g_return_val_if_fail (klass->link_get_master, FALSE);
801
802 if (!nm_platform_link_get_name (slave)) {
803 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
804 return 0;
805 }
806 return klass->link_get_master (platform, slave);
807 }
808
809 /**
810 * nm_platform_bridge_add:
811 * @name: New interface name
812 *
813 * Create a software bridge.
814 */
815 gboolean
816 nm_platform_bridge_add (const char *name)
817 {
818 debug ("link: adding bridge '%s'", name);
819 return nm_platform_link_add (name, NM_LINK_TYPE_BRIDGE);
820 }
821
822 /**
823 * nm_platform_bond_add:
824 * @name: New interface name
825 *
826 * Create a software bonding device.
827 */
828 gboolean
829 nm_platform_bond_add (const char *name)
830 {
831 debug ("link: adding bond '%s'", name);
832 return nm_platform_link_add (name, NM_LINK_TYPE_BOND);
833 }
834
835 /**
836 * nm_platform_team_add:
837 * @name: New interface name
838 *
839 * Create a software teaming device.
840 */
841 gboolean
842 nm_platform_team_add (const char *name)
843 {
844 debug ("link: adding team '%s'", name);
845 return nm_platform_link_add (name, NM_LINK_TYPE_TEAM);
846 }
847
848 /**
849 * nm_platform_vlan_add:
850 * @name: New interface name
851 * @vlanid: VLAN identifier
852 * @vlanflags: VLAN flags from libnm-util
853 *
854 * Create a software VLAN device.
855 */
856 gboolean
857 nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags)
858 {
859 reset_error ();
860
861 g_assert (platform);
862 g_return_val_if_fail (parent >= 0, FALSE);
863 g_return_val_if_fail (vlanid >= 0, FALSE);
864 g_return_val_if_fail (name, FALSE);
865 g_return_val_if_fail (klass->vlan_add, FALSE);
866
867 if (nm_platform_link_exists (name)) {
868 debug ("link already exists: %s", name);
869 platform->error = NM_PLATFORM_ERROR_EXISTS;
870 return FALSE;
871 }
872
873 debug ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x",
874 name, parent, vlanid, vlanflags);
875 return klass->vlan_add (platform, name, parent, vlanid, vlanflags);
876 }
877
878 gboolean
879 nm_platform_master_set_option (int ifindex, const char *option, const char *value)
880 {
881 reset_error ();
882
883 g_return_val_if_fail (ifindex > 0, FALSE);
884 g_return_val_if_fail (option, FALSE);
885 g_return_val_if_fail (value, FALSE);
886 g_return_val_if_fail (klass->master_set_option, FALSE);
887
888 return klass->master_set_option (platform, ifindex, option, value);
889 }
890
891 char *
892 nm_platform_master_get_option (int ifindex, const char *option)
893 {
894 reset_error ();
895
896 g_return_val_if_fail (ifindex > 0, FALSE);
897 g_return_val_if_fail (option, FALSE);
898 g_return_val_if_fail (klass->master_set_option, FALSE);
899
900 return klass->master_get_option (platform, ifindex, option);
901 }
902
903 gboolean
904 nm_platform_slave_set_option (int ifindex, const char *option, const char *value)
905 {
906 reset_error ();
907
908 g_return_val_if_fail (ifindex > 0, FALSE);
909 g_return_val_if_fail (option, FALSE);
910 g_return_val_if_fail (value, FALSE);
911 g_return_val_if_fail (klass->slave_set_option, FALSE);
912
913 return klass->slave_set_option (platform, ifindex, option, value);
914 }
915
916 char *
917 nm_platform_slave_get_option (int ifindex, const char *option)
918 {
919 reset_error ();
920
921 g_return_val_if_fail (ifindex > 0, FALSE);
922 g_return_val_if_fail (option, FALSE);
923 g_return_val_if_fail (klass->slave_set_option, FALSE);
924
925 return klass->slave_get_option (platform, ifindex, option);
926 }
927
928 gboolean
929 nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid)
930 {
931 reset_error ();
932
933 g_assert (platform);
934 g_return_val_if_fail (klass->vlan_get_info, FALSE);
935
936 if (parent)
937 *parent = 0;
938 if (vlanid)
939 *vlanid = 0;
940
941 if (nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_VLAN)
942 return FALSE;
943
944 return klass->vlan_get_info (platform, ifindex, parent, vlanid);
945 }
946
947 gboolean
948 nm_platform_vlan_set_ingress_map (int ifindex, int from, int to)
949 {
950 reset_error ();
951
952 g_assert (platform);
953 g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE);
954
955 debug ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to);
956 return klass->vlan_set_ingress_map (platform, ifindex, from, to);
957 }
958
959 gboolean
960 nm_platform_vlan_set_egress_map (int ifindex, int from, int to)
961 {
962 reset_error ();
963
964 g_assert (platform);
965 g_return_val_if_fail (klass->vlan_set_egress_map, FALSE);
966
967 debug ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to);
968 return klass->vlan_set_egress_map (platform, ifindex, from, to);
969 }
970
971 gboolean
972 nm_platform_infiniband_partition_add (int parent, int p_key)
973 {
974 const char *parent_name;
975 char *name;
976
977 reset_error ();
978
979 g_return_val_if_fail (parent >= 0, FALSE);
980 g_return_val_if_fail (p_key >= 0, FALSE);
981 g_return_val_if_fail (klass->infiniband_partition_add, FALSE);
982
983 if (nm_platform_link_get_type (parent) != NM_LINK_TYPE_INFINIBAND) {
984 platform->error = NM_PLATFORM_ERROR_WRONG_TYPE;
985 return FALSE;
986 }
987
988 parent_name = nm_platform_link_get_name (parent);
989 name = g_strdup_printf ("%s.%04x", parent_name, p_key);
990 if (nm_platform_link_exists (name)) {
991 debug ("infiniband: already exists");
992 platform->error = NM_PLATFORM_ERROR_EXISTS;
993 g_free (name);
994 return FALSE;
995 }
996 g_free (name);
997
998 return klass->infiniband_partition_add (platform, parent, p_key);
999 }
1000
1001 gboolean
1002 nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *props)
1003 {
1004 reset_error ();
1005
1006 g_return_val_if_fail (ifindex > 0, FALSE);
1007 g_return_val_if_fail (props != NULL, FALSE);
1008
1009 return klass->veth_get_properties (platform, ifindex, props);
1010 }
1011
1012 gboolean
1013 nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *props)
1014 {
1015 reset_error ();
1016
1017 g_return_val_if_fail (ifindex > 0, FALSE);
1018 g_return_val_if_fail (props != NULL, FALSE);
1019
1020 return klass->tun_get_properties (platform, ifindex, props);
1021 }
1022
1023 gboolean
1024 nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props)
1025 {
1026 reset_error ();
1027
1028 g_return_val_if_fail (ifindex > 0, FALSE);
1029 g_return_val_if_fail (props != NULL, FALSE);
1030
1031 return klass->macvlan_get_properties (platform, ifindex, props);
1032 }
1033
1034 gboolean
1035 nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props)
1036 {
1037 reset_error ();
1038
1039 g_return_val_if_fail (ifindex > 0, FALSE);
1040 g_return_val_if_fail (props != NULL, FALSE);
1041
1042 return klass->gre_get_properties (platform, ifindex, props);
1043 }
1044
1045 /******************************************************************/
1046
1047 GArray *
1048 nm_platform_ip4_address_get_all (int ifindex)
1049 {
1050 reset_error ();
1051
1052 g_return_val_if_fail (ifindex > 0, NULL);
1053 g_return_val_if_fail (klass->ip4_address_get_all, NULL);
1054
1055 return klass->ip4_address_get_all (platform, ifindex);
1056 }
1057
1058 GArray *
1059 nm_platform_ip6_address_get_all (int ifindex)
1060 {
1061 reset_error ();
1062
1063 g_return_val_if_fail (ifindex > 0, NULL);
1064 g_return_val_if_fail (klass->ip6_address_get_all, NULL);
1065
1066 return klass->ip6_address_get_all (platform, ifindex);
1067 }
1068
1069 gboolean
1070 nm_platform_ip4_address_add (int ifindex, in_addr_t address, int plen, guint32 lifetime, guint32 preferred)
1071 {
1072 reset_error ();
1073
1074 g_return_val_if_fail (ifindex > 0, FALSE);
1075 g_return_val_if_fail (plen > 0, FALSE);
1076 g_return_val_if_fail (lifetime > 0, FALSE);
1077 g_return_val_if_fail (preferred >= 0, FALSE);
1078 g_return_val_if_fail (klass->ip4_address_add, FALSE);
1079
1080 debug ("address: adding or updating IPv4 address");
1081 return klass->ip4_address_add (platform, ifindex, address, plen, lifetime, preferred);
1082 }
1083
1084 gboolean
1085 nm_platform_ip6_address_add (int ifindex, struct in6_addr address, int plen, guint32 lifetime, guint32 preferred)
1086 {
1087 reset_error ();
1088
1089 g_return_val_if_fail (ifindex > 0, FALSE);
1090 g_return_val_if_fail (plen > 0, FALSE);
1091 g_return_val_if_fail (lifetime > 0, FALSE);
1092 g_return_val_if_fail (preferred >= 0, FALSE);
1093 g_return_val_if_fail (klass->ip6_address_add, FALSE);
1094
1095 debug ("address: adding or updating IPv6 address");
1096 return klass->ip6_address_add (platform, ifindex, address, plen, lifetime, preferred);
1097 }
1098
1099 gboolean
1100 nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen)
1101 {
1102 reset_error ();
1103
1104 g_return_val_if_fail (ifindex > 0, FALSE);
1105 g_return_val_if_fail (plen > 0, FALSE);
1106 g_return_val_if_fail (klass->ip4_address_delete, FALSE);
1107
1108 if (!nm_platform_ip4_address_exists (ifindex, address, plen)) {
1109 debug ("address doesn't exists");
1110 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
1111 return FALSE;
1112 }
1113
1114 debug ("address: deleting IPv4 address");
1115 return klass->ip4_address_delete (platform, ifindex, address, plen);
1116 }
1117
1118 gboolean
1119 nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen)
1120 {
1121 reset_error ();
1122
1123 g_return_val_if_fail (ifindex > 0, FALSE);
1124 g_return_val_if_fail (plen > 0, FALSE);
1125 g_return_val_if_fail (klass->ip6_address_delete, FALSE);
1126
1127 if (!nm_platform_ip6_address_exists (ifindex, address, plen)) {
1128 debug ("address doesn't exists");
1129 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
1130 return FALSE;
1131 }
1132
1133 debug ("address: deleting IPv6 address");
1134 return klass->ip6_address_delete (platform, ifindex, address, plen);
1135 }
1136
1137 gboolean
1138 nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen)
1139 {
1140 reset_error ();
1141
1142 g_return_val_if_fail (plen > 0, FALSE);
1143 g_return_val_if_fail (klass->ip4_address_exists, FALSE);
1144
1145 return klass->ip4_address_exists (platform, ifindex, address, plen);
1146 }
1147
1148 gboolean
1149 nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen)
1150 {
1151 reset_error ();
1152
1153 g_return_val_if_fail (plen > 0, FALSE);
1154 g_return_val_if_fail (klass->ip6_address_exists, FALSE);
1155
1156 return klass->ip6_address_exists (platform, ifindex, address, plen);
1157 }
1158
1159 static gboolean
1160 array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address)
1161 {
1162 guint len = addresses ? addresses->len : 0;
1163 guint i;
1164
1165 for (i = 0; i < len; i++) {
1166 NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i);
1167
1168 if (candidate->address == address->address && candidate->plen == address->plen)
1169 return TRUE;
1170 }
1171
1172 return FALSE;
1173 }
1174
1175 static gboolean
1176 array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address)
1177 {
1178 guint len = addresses ? addresses->len : 0;
1179 guint i;
1180
1181 for (i = 0; i < len; i++) {
1182 NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i);
1183
1184 if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen)
1185 return TRUE;
1186 }
1187
1188 return FALSE;
1189 }
1190
1191 static guint32
1192 get_time (void)
1193 {
1194 struct timespec tp;
1195
1196 clock_gettime (CLOCK_MONOTONIC, &tp);
1197
1198 return tp.tv_sec;
1199 }
1200
1201 /* Compute (a - b) in an overflow-safe manner. */
1202 static guint32
1203 subtract_guint32 (guint32 a, guint32 b)
1204 {
1205 if (a == G_MAXUINT32)
1206 return G_MAXUINT32;
1207
1208 return a > b ? a - b : 0;
1209 }
1210
1211 /**
1212 * nm_platform_ip4_address_sync:
1213 * @ifindex: Interface index
1214 * @known_addresses: List of addresses
1215 *
1216 * A convenience function to synchronize addresses for a specific interface
1217 * with the least possible disturbance. It simply removes addresses that are
1218 * not listed and adds addresses that are.
1219 *
1220 * Returns: %TRUE on success.
1221 */
1222 gboolean
1223 nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses)
1224 {
1225 GArray *addresses;
1226 NMPlatformIP4Address *address;
1227 guint32 now = get_time ();
1228 int i;
1229
1230 /* Delete unknown addresses */
1231 addresses = nm_platform_ip4_address_get_all (ifindex);
1232 for (i = 0; i < addresses->len; i++) {
1233 address = &g_array_index (addresses, NMPlatformIP4Address, i);
1234
1235 if (!array_contains_ip4_address (known_addresses, address))
1236 nm_platform_ip4_address_delete (ifindex, address->address, address->plen);
1237 }
1238 g_array_free (addresses, TRUE);
1239
1240 if (!known_addresses)
1241 return TRUE;
1242
1243 /* Add missing addresses */
1244 for (i = 0; i < known_addresses->len; i++) {
1245 const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i);
1246 guint32 lifetime, preferred;
1247
1248 if (known_address->lifetime) {
1249 /* Pad the timestamp by 5 seconds to avoid potential races. */
1250 guint32 shift = subtract_guint32 (now, known_address->timestamp + 5);
1251
1252 lifetime = subtract_guint32 (known_address->lifetime, shift);
1253 preferred = subtract_guint32 (known_address->lifetime, shift);
1254 } else
1255 lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT;
1256
1257 if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->plen, lifetime, preferred))
1258 return FALSE;
1259 }
1260
1261 return TRUE;
1262 }
1263
1264 /**
1265 * nm_platform_ip6_address_sync:
1266 * @ifindex: Interface index
1267 * @known_addresses: List of addresses
1268 *
1269 * A convenience function to synchronize addresses for a specific interface
1270 * with the least possible disturbance. It simply removes addresses that are
1271 * not listed and adds addresses that are.
1272 *
1273 * Returns: %TRUE on success.
1274 */
1275 gboolean
1276 nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses)
1277 {
1278 GArray *addresses;
1279 NMPlatformIP6Address *address;
1280 guint32 now = get_time ();
1281 int i;
1282
1283 /* Delete unknown addresses */
1284 addresses = nm_platform_ip6_address_get_all (ifindex);
1285 for (i = 0; i < addresses->len; i++) {
1286 address = &g_array_index (addresses, NMPlatformIP6Address, i);
1287
1288 /* Leave link local address management to the kernel */
1289 if (IN6_IS_ADDR_LINKLOCAL (&address->address))
1290 continue;
1291
1292 if (!array_contains_ip6_address (known_addresses, address))
1293 nm_platform_ip6_address_delete (ifindex, address->address, address->plen);
1294 }
1295 g_array_free (addresses, TRUE);
1296
1297 if (!known_addresses)
1298 return TRUE;
1299
1300 /* Add missing addresses */
1301 for (i = 0; i < known_addresses->len; i++) {
1302 const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i);
1303 guint32 lifetime, preferred;
1304
1305 if (known_address->lifetime) {
1306 /* Pad the timestamp by 5 seconds to avoid potential races. */
1307 guint32 shift = subtract_guint32 (now, known_address->timestamp + 5);
1308
1309 lifetime = subtract_guint32 (known_address->lifetime, shift);
1310 preferred = subtract_guint32 (known_address->lifetime, shift);
1311 } else
1312 lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT;
1313
1314 if (!nm_platform_ip6_address_add (ifindex, known_address->address, known_address->plen, lifetime, preferred))
1315 return FALSE;
1316 }
1317
1318 return TRUE;
1319 }
1320
1321 gboolean
1322 nm_platform_address_flush (int ifindex)
1323 {
1324 return nm_platform_ip4_address_sync (ifindex, NULL)
1325 && nm_platform_ip6_address_sync (ifindex, NULL);
1326 }
1327
1328 /******************************************************************/
1329
1330 GArray *
1331 nm_platform_ip4_route_get_all (int ifindex, gboolean include_default)
1332 {
1333 reset_error ();
1334
1335 g_return_val_if_fail (ifindex > 0, NULL);
1336 g_return_val_if_fail (klass->ip4_route_get_all, NULL);
1337
1338 return klass->ip4_route_get_all (platform, ifindex, include_default);
1339 }
1340
1341 GArray *
1342 nm_platform_ip6_route_get_all (int ifindex, gboolean include_default)
1343 {
1344 reset_error ();
1345
1346 g_return_val_if_fail (ifindex > 0, NULL);
1347 g_return_val_if_fail (klass->ip6_route_get_all, NULL);
1348
1349 return klass->ip6_route_get_all (platform, ifindex, include_default);
1350 }
1351
1352 gboolean
1353 nm_platform_ip4_route_add (int ifindex,
1354 in_addr_t network, int plen,
1355 in_addr_t gateway, int metric, int mss)
1356 {
1357 reset_error ();
1358
1359 g_return_val_if_fail (platform, FALSE);
1360 g_return_val_if_fail (0 <= plen && plen <= 32, FALSE);
1361 g_return_val_if_fail (metric >= 0, FALSE);
1362 g_return_val_if_fail (mss >= 0, FALSE);
1363 g_return_val_if_fail (klass->ip4_route_add, FALSE);
1364
1365 if (!metric)
1366 metric = 1024;
1367
1368 return klass->ip4_route_add (platform, ifindex, network, plen, gateway, metric, mss);
1369 }
1370
1371 gboolean
1372 nm_platform_ip6_route_add (int ifindex,
1373 struct in6_addr network, int plen, struct in6_addr gateway, int metric, int mss)
1374 {
1375 g_return_val_if_fail (platform, FALSE);
1376 g_return_val_if_fail (0 <= plen && plen <= 128, FALSE);
1377 g_return_val_if_fail (metric >= 0, FALSE);
1378 g_return_val_if_fail (mss >= 0, FALSE);
1379 g_return_val_if_fail (klass->ip6_route_add, FALSE);
1380
1381 if (!metric)
1382 metric = 1024;
1383
1384 return klass->ip6_route_add (platform, ifindex, network, plen, gateway, metric, mss);
1385 }
1386
1387 gboolean
1388 nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, int metric)
1389 {
1390 reset_error ();
1391
1392 g_return_val_if_fail (platform, FALSE);
1393 g_return_val_if_fail (klass->ip4_route_delete, FALSE);
1394
1395 if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric)) {
1396 debug ("route not found");
1397 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
1398 return FALSE;
1399 }
1400
1401 return klass->ip4_route_delete (platform, ifindex, network, plen, metric);
1402 }
1403
1404 gboolean
1405 nm_platform_ip6_route_delete (int ifindex,
1406 struct in6_addr network, int plen, int metric)
1407 {
1408 reset_error ();
1409
1410 g_return_val_if_fail (platform, FALSE);
1411 g_return_val_if_fail (klass->ip6_route_delete, FALSE);
1412
1413 if (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)) {
1414 debug ("route not found");
1415 platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
1416 return FALSE;
1417 }
1418
1419 return klass->ip6_route_delete (platform, ifindex, network, plen, metric);
1420 }
1421
1422 gboolean
1423 nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, int metric)
1424 {
1425 reset_error ();
1426
1427 g_return_val_if_fail (platform, FALSE);
1428 g_return_val_if_fail (klass->ip4_route_exists, FALSE);
1429
1430 return klass->ip4_route_exists (platform,ifindex, network, plen, metric);
1431 }
1432
1433 gboolean
1434 nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, int metric)
1435 {
1436 reset_error ();
1437
1438 g_return_val_if_fail (platform, FALSE);
1439 g_return_val_if_fail (klass->ip6_route_exists, FALSE);
1440
1441 return klass->ip6_route_exists (platform, ifindex, network, plen, metric);
1442 }
1443
1444 static gboolean
1445 array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route)
1446 {
1447 guint len = routes ? routes->len : 0;
1448 guint i;
1449
1450 for (i = 0; i < len; i++)
1451 if (!memcmp (&g_array_index (routes, NMPlatformIP4Route, i), route, sizeof (*route)))
1452 return TRUE;
1453
1454 return FALSE;
1455 }
1456
1457 static gboolean
1458 array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
1459 {
1460 guint len = routes ? routes->len : 0;
1461 guint i;
1462
1463 for (i = 0; i < len; i++)
1464 if (!memcmp (&g_array_index (routes, NMPlatformIP6Route, i), route, sizeof (*route)))
1465 return TRUE;
1466
1467 return FALSE;
1468 }
1469
1470 /**
1471 * nm_platform_ip4_route_sync:
1472 * @ifindex: Interface index
1473 * @known_routes: List of routes
1474 *
1475 * A convenience function to synchronize routes for a specific interface
1476 * with the least possible disturbance. It simply removes routes that are
1477 * not listed and adds routes that are.
1478 *
1479 * Returns: %TRUE on success.
1480 */
1481 gboolean
1482 nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes)
1483 {
1484 GArray *routes;
1485 NMPlatformIP4Route *route;
1486 const NMPlatformIP4Route *known_route;
1487 int i;
1488
1489 /* Delete unknown routes */
1490 routes = nm_platform_ip4_route_get_all (ifindex, FALSE);
1491 for (i = 0; i < routes->len; i++) {
1492 route = &g_array_index (routes, NMPlatformIP4Route, i);
1493 route->ifindex = 0;
1494
1495 if (!array_contains_ip4_route (known_routes, route))
1496 nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric);
1497 }
1498 g_array_free (routes, TRUE);
1499
1500 if (!known_routes)
1501 return TRUE;
1502
1503 /* Add missing routes */
1504 for (i = 0; i < known_routes->len; i++) {
1505 known_route = &g_array_index (known_routes, NMPlatformIP4Route, i);
1506
(21) Event example_checked: |
Example5: "nm_platform_ip4_route_add(ifindex, known_route->network, known_route->plen, known_route->gateway, known_route->metric, known_route->mss)" has its value checked in "nm_platform_ip4_route_add(ifindex, known_route->network, known_route->plen, known_route->gateway, known_route->metric, known_route->mss)". |
Also see events: |
[check_return][example_checked][example_checked][example_checked][example_checked][unchecked_value] |
1507 if (!nm_platform_ip4_route_add (ifindex,
1508 known_route->network, known_route->plen, known_route->gateway,
1509 known_route->metric, known_route->mss))
1510 return FALSE;
1511 }
1512
1513 return TRUE;
1514 }
1515
1516 /**
1517 * nm_platform_ip6_route_sync:
1518 * @ifindex: Interface index
1519 * @known_routes: List of routes
1520 *
1521 * A convenience function to synchronize routes for a specific interface
1522 * with the least possible disturbance. It simply removes routes that are
1523 * not listed and adds routes that are.
1524 *
1525 * Returns: %TRUE on success.
1526 */
1527 gboolean
1528 nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes)
1529 {
1530 GArray *routes;
1531 NMPlatformIP6Route *route;
1532 const NMPlatformIP6Route *known_route;
1533 int i;
1534
1535 /* Delete unknown routes */
1536 routes = nm_platform_ip6_route_get_all (ifindex, FALSE);
1537 for (i = 0; i < routes->len; i++) {
1538 route = &g_array_index (routes, NMPlatformIP6Route, i);
1539 route->ifindex = 0;
1540
1541 if (!array_contains_ip6_route (known_routes, route))
1542 nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric);
1543 }
1544 g_array_free (routes, TRUE);
1545
1546 if (!known_routes)
1547 return TRUE;
1548
1549 /* Add missing routes */
1550 for (i = 0; i < known_routes->len; i++) {
1551 known_route = &g_array_index (known_routes, NMPlatformIP6Route, i);
1552
1553 if (!nm_platform_ip6_route_add (ifindex,
1554 known_route->network, known_route->plen, known_route->gateway,
1555 known_route->metric, known_route->mss))
1556 return FALSE;
1557 }
1558
1559 return TRUE;
1560 }
1561
1562 gboolean
1563 nm_platform_route_flush (int ifindex)
1564 {
1565 return nm_platform_ip4_route_sync (ifindex, NULL)
1566 && nm_platform_ip6_route_sync (ifindex, NULL);
1567 }
1568
1569 /******************************************************************/
1570
1571 /**
1572 * nm_platform_ip4_address_to_string:
1573 * @route: pointer to NMPlatformIP4Address address structure
1574 *
1575 * A method for converting an address struct into a string representation.
1576 *
1577 * Example output: ""
1578 *
1579 * Returns: a string representation of the address. The returned string
1580 * is an internal buffer, so do not keep or free the returned string.
1581 * Also, this function is not thread safe.
1582 */
1583 const char *
1584 nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address)
1585 {
1586 static char buffer[256];
1587 char s_address[INET_ADDRSTRLEN];
1588 const char *s_dev;
1589
1590 g_return_val_if_fail (address, "(unknown)");
1591
1592 inet_ntop (AF_INET, &address->address, s_address, sizeof (s_address));
1593 s_dev = address->ifindex > 0 ? nm_platform_link_get_name (address->ifindex) : NULL;
1594
1595 g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u dev %s",
1596 s_address, address->plen, (guint)address->lifetime, (guint)address->preferred,
1597 (guint)address->timestamp, s_dev ? s_dev : "-");
1598 return buffer;
1599 }
1600
1601 /**
1602 * nm_platform_ip6_address_to_string:
1603 * @route: pointer to NMPlatformIP6Address address structure
1604 *
1605 * A method for converting an address struct into a string representation.
1606 *
1607 * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1"
1608 *
1609 * Returns: a string representation of the address. The returned string
1610 * is an internal buffer, so do not keep or free the returned string.
1611 * Also, this function is not thread safe.
1612 */
1613 const char *
1614 nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address)
1615 {
1616 static char buffer[256];
1617 char s_address[INET6_ADDRSTRLEN];
1618 const char *s_dev;
1619
1620 g_return_val_if_fail (address, "(unknown)");
1621
1622 inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address));
1623 s_dev = address->ifindex > 0 ? nm_platform_link_get_name (address->ifindex) : NULL;
1624
1625 g_snprintf (buffer, sizeof (buffer), "%s/%d lft %u pref %u time %u dev %s",
1626 s_address, address->plen, (guint)address->lifetime, (guint)address->preferred,
1627 (guint)address->timestamp, s_dev ? s_dev : "-");
1628 return buffer;
1629 }
1630
1631 /**
1632 * nm_platform_ip4_route_to_string:
1633 * @route: pointer to NMPlatformIP4Route route structure
1634 *
1635 * A method for converting a route struct into a string representation.
1636 *
1637 * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0"
1638 *
1639 * Returns: a string representation of the route. The returned string
1640 * is an internal buffer, so do not keep or free the returned string.
1641 * Also, this function is not thread safe.
1642 */
1643 const char *
1644 nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route)
1645 {
1646 static char buffer[256];
1647 char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN];
1648 const char *s_dev;
1649
1650 g_return_val_if_fail (route, "(unknown)");
1651
1652 inet_ntop (AF_INET, &route->network, s_network, sizeof(s_network));
1653 inet_ntop (AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
1654 s_dev = route->ifindex > 0 ? nm_platform_link_get_name (route->ifindex) : NULL;
1655
1656 g_snprintf (buffer, sizeof (buffer), "%s/%d via %s dev %s metric %u mss %u",
1657 s_network, route->plen, s_gateway, s_dev ? s_dev : "-",
1658 route->metric, route->mss);
1659 return buffer;
1660 }
1661
1662 /**
1663 * nm_platform_ip6_route_to_string:
1664 * @route: pointer to NMPlatformIP6Route route structure
1665 *
1666 * A method for converting a route struct into a string representation.
1667 *
1668 * Example output: "ff02::fb/128 via :: dev em1 metric 0"
1669 *
1670 * Returns: a string representation of the route. The returned string
1671 * is an internal buffer, so do not keep or free the returned string.
1672 * Also, this function is not thread safe.
1673 */
1674 const char *
1675 nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route)
1676 {
1677 static char buffer[256];
1678 char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN];
1679 const char *s_dev;
1680
1681 g_return_val_if_fail (route, "(unknown)");
1682
1683 inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network));
1684 inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway));
1685 s_dev = route->ifindex > 0 ? nm_platform_link_get_name (route->ifindex) : NULL;
1686
1687 g_snprintf (buffer, sizeof (buffer), "%s/%d via %s dev %s metric %u mss %u",
1688 s_network, route->plen, s_gateway, s_dev ? s_dev : "-",
1689 route->metric, route->mss);
1690 return buffer;
1691 }
1692
1693 #define _CMP_POINTER(a, b) \
1694 G_STMT_START { \
1695 if ((a) == (b)) \
1696 return 0; \
1697 if (!(a)) \
1698 return -1; \
1699 if (!(b)) \
1700 return 1; \
1701 } G_STMT_END
1702
1703 #define _CMP_FIELD(a, b, field) \
1704 G_STMT_START { \
1705 if (((a)->field) != ((b)->field)) \
1706 return (((a)->field) < ((b)->field)) ? -1 : 1; \
1707 } G_STMT_END
1708
1709 #define _CMP_FIELD_MEMCMP(a, b, field) \
1710 G_STMT_START { \
1711 int c = memcmp (&((a)->field), &((b)->field), \
1712 sizeof ((a)->field)); \
1713 if (c != 0) \
1714 return c < 0 ? -1 : 1; \
1715 } G_STMT_END
1716
1717 int
1718 nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
1719 {
1720 _CMP_POINTER (a, b);
1721 _CMP_FIELD_MEMCMP (a, b, address);
1722 _CMP_FIELD (a, b, ifindex);
1723 _CMP_FIELD (a, b, plen);
1724 _CMP_FIELD (a, b, timestamp);
1725 _CMP_FIELD (a, b, lifetime);
1726 _CMP_FIELD (a, b, preferred);
1727 return 0;
1728 }
1729
1730 int
1731 nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
1732 {
1733 _CMP_POINTER (a, b);
1734 _CMP_FIELD (a, b, ifindex);
1735 _CMP_FIELD_MEMCMP (a, b, address);
1736 _CMP_FIELD (a, b, plen);
1737 _CMP_FIELD (a, b, timestamp);
1738 _CMP_FIELD (a, b, lifetime);
1739 _CMP_FIELD (a, b, preferred);
1740 return 0;
1741 }
1742
1743 int
1744 nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
1745 {
1746 _CMP_POINTER (a, b);
1747 _CMP_FIELD (a, b, ifindex);
1748 _CMP_FIELD_MEMCMP (a, b, network);
1749 _CMP_FIELD (a, b, plen);
1750 _CMP_FIELD_MEMCMP (a, b, gateway);
1751 _CMP_FIELD (a, b, metric);
1752 _CMP_FIELD (a, b, mss);
1753 return 0;
1754 }
1755
1756 int
1757 nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
1758 {
1759 _CMP_POINTER (a, b);
1760 _CMP_FIELD (a, b, ifindex);
1761 _CMP_FIELD_MEMCMP (a, b, network);
1762 _CMP_FIELD (a, b, plen);
1763 _CMP_FIELD_MEMCMP (a, b, gateway);
1764 _CMP_FIELD (a, b, metric);
1765 _CMP_FIELD (a, b, mss);
1766 return 0;
1767 }
1768
1769 #undef _CMP_POINTER
1770 #undef _CMP_FIELD
1771 #undef _CMP_FIELD_MEMCMP
1772
1773
1774 static void
1775 log_link (NMPlatformLink *device, const char *change_type)
1776 {
1777 debug ("signal: link %s: %s (%d)", change_type, device->name, device->ifindex);
1778 }
1779
1780 static void
1781 log_link_added (NMPlatform *p, int ifindex, NMPlatformLink *device, gpointer user_data)
1782 {
1783 log_link (device, "added");
1784 }
1785
1786 static void
1787 log_link_changed (NMPlatform *p, int ifindex, NMPlatformLink *device, gpointer user_data)
1788 {
1789 log_link (device, "changed");
1790 }
1791
1792 static void
1793 log_link_removed (NMPlatform *p, int ifindex, NMPlatformLink *device, gpointer user_data)
1794 {
1795 log_link (device, "removed");
1796 }
1797
1798 static void
1799 log_ip4_address (NMPlatformIP4Address *address, const char *change_type)
1800 {
1801 const char *name = nm_platform_link_get_name (address->ifindex);
1802
1803 debug ("(%s) signal: address %s: %s", name, change_type, nm_platform_ip4_address_to_string (address));
1804 }
1805
1806 static void
1807 log_ip4_address_added (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, gpointer user_data)
1808 {
1809 log_ip4_address (address, "added");
1810 }
1811
1812 static void
1813 log_ip4_address_changed (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, gpointer user_data)
1814 {
1815 log_ip4_address (address, "changed");
1816 }
1817
1818 static void
1819 log_ip4_address_removed (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, gpointer user_data)
1820 {
1821 log_ip4_address (address, "removed");
1822 }
1823
1824 static void
1825 log_ip6_address (NMPlatformIP6Address *address, const char *change_type)
1826 {
1827 const char *name = nm_platform_link_get_name (address->ifindex);
1828
1829 debug ("(%s) signal: address %s: %s", name, change_type, nm_platform_ip6_address_to_string (address));
1830 }
1831
1832 static void
1833 log_ip6_address_added (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, gpointer user_data)
1834 {
1835 log_ip6_address (address, "added");
1836 }
1837
1838 static void
1839 log_ip6_address_changed (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, gpointer user_data)
1840 {
1841 log_ip6_address (address, "changed");
1842 }
1843
1844 static void
1845 log_ip6_address_removed (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, gpointer user_data)
1846 {
1847 log_ip6_address (address, "removed");
1848 }
1849
1850 static void
1851 log_ip4_route (NMPlatformIP4Route *route, const char *change_type)
1852 {
1853 debug ("signal: route %s: %s", change_type, nm_platform_ip4_route_to_string (route));
1854 }
1855
1856 static void
1857 log_ip4_route_added (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, gpointer user_data)
1858 {
1859 log_ip4_route (route, "added");
1860 }
1861
1862 static void
1863 log_ip4_route_changed (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, gpointer user_data)
1864 {
1865 log_ip4_route (route, "changed");
1866 }
1867
1868 static void
1869 log_ip4_route_removed (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, gpointer user_data)
1870 {
1871 log_ip4_route (route, "removed");
1872 }
1873
1874 static void
1875 log_ip6_route (NMPlatformIP6Route *route, const char *change_type)
1876 {
1877 debug ("signal: route %s: %s", change_type, nm_platform_ip6_route_to_string (route));
1878 }
1879
1880 static void
1881 log_ip6_route_added (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, gpointer user_data)
1882 {
1883 log_ip6_route (route, "added");
1884 }
1885
1886 static void
1887 log_ip6_route_changed (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, gpointer user_data)
1888 {
1889 log_ip6_route (route, "changed");
1890 }
1891
1892 static void
1893 log_ip6_route_removed (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, gpointer user_data)
1894 {
1895 log_ip6_route (route, "removed");
1896 }
1897
1898 /******************************************************************/
1899
1900 static void
1901 nm_platform_init (NMPlatform *object)
1902 {
1903 }
1904
1905 #define SIGNAL(signal_id, method) signals[signal_id] = \
1906 g_signal_new_class_handler (NM_PLATFORM_ ## signal_id, \
1907 G_OBJECT_CLASS_TYPE (object_class), \
1908 G_SIGNAL_RUN_FIRST, \
1909 G_CALLBACK (method), \
1910 NULL, NULL, NULL, \
1911 G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_REASON);
1912
1913 static void
1914 nm_platform_class_init (NMPlatformClass *platform_class)
1915 {
1916 GObjectClass *object_class = G_OBJECT_CLASS (platform_class);
1917
1918 /* Signals */
1919 SIGNAL (LINK_ADDED, log_link_added)
1920 SIGNAL (LINK_CHANGED, log_link_changed)
1921 SIGNAL (LINK_REMOVED, log_link_removed)
1922 SIGNAL (IP4_ADDRESS_ADDED, log_ip4_address_added)
1923 SIGNAL (IP4_ADDRESS_CHANGED, log_ip4_address_changed)
1924 SIGNAL (IP4_ADDRESS_REMOVED, log_ip4_address_removed)
1925 SIGNAL (IP6_ADDRESS_ADDED, log_ip6_address_added)
1926 SIGNAL (IP6_ADDRESS_CHANGED, log_ip6_address_changed)
1927 SIGNAL (IP6_ADDRESS_REMOVED, log_ip6_address_removed)
1928 SIGNAL (IP4_ROUTE_ADDED, log_ip4_route_added)
1929 SIGNAL (IP4_ROUTE_CHANGED, log_ip4_route_changed)
1930 SIGNAL (IP4_ROUTE_REMOVED, log_ip4_route_removed)
1931 SIGNAL (IP6_ROUTE_ADDED, log_ip6_route_added)
1932 SIGNAL (IP6_ROUTE_CHANGED, log_ip6_route_changed)
1933 SIGNAL (IP6_ROUTE_REMOVED, log_ip6_route_removed)
1934 }
1935