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);
(1) Event unsigned_compare: This greater-than-or-equal-to-zero comparison of an unsigned value is always true. "preferred >= 0U".
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 	
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