1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
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 of the License, or
7    	 * (at your option) 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 2011 Red Hat, Inc.
19   	 */
20   	
21   	#include "config.h"
22   	
23   	#include <glib.h>
24   	#include <glib/gi18n.h>
25   	
26   	#include <linux/if_infiniband.h>
27   	#include <netinet/ether.h>
28   	
29   	#include "nm-device-infiniband.h"
30   	#include "nm-logging.h"
31   	#include "nm-utils.h"
32   	#include "NetworkManagerUtils.h"
33   	#include "nm-device-private.h"
34   	#include "nm-enum-types.h"
35   	#include "nm-dbus-manager.h"
36   	
37   	#include "nm-device-infiniband-glue.h"
38   	
39   	
40   	G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
41   	
42   	#define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate))
43   	
44   	#define NM_INFINIBAND_ERROR (nm_infiniband_error_quark ())
45   	
46   	typedef struct {
47   		int dummy;
48   	} NMDeviceInfinibandPrivate;
49   	
50   	enum {
51   		PROP_0,
52   	
53   		LAST_PROP
54   	};
55   	
56   	static GQuark
57   	nm_infiniband_error_quark (void)
58   	{
59   		static GQuark quark = 0;
60   		if (!quark)
61   			quark = g_quark_from_static_string ("nm-infiniband-error");
62   		return quark;
63   	}
64   	
65   	static GObject*
66   	constructor (GType type,
67   				 guint n_construct_params,
68   				 GObjectConstructParam *construct_params)
69   	{
70   		GObject *object;
71   		NMDeviceInfinibandPrivate *priv;
72   		NMDevice *self;
73   	
74   		object = G_OBJECT_CLASS (nm_device_infiniband_parent_class)->constructor (type,
75   		                                                                        n_construct_params,
76   		                                                                        construct_params);
77   		if (!object)
78   			return NULL;
79   	
80   		self = NM_DEVICE (object);
(1) Event returned_pointer: Pointer "priv" returned by "g_type_instance_get_private((GTypeInstance *)self, nm_device_infiniband_get_type())" is never used.
81   		priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (self);
82   	
83   		nm_log_dbg (LOGD_HW | LOGD_INFINIBAND, "(%s): kernel ifindex %d",
84   		            nm_device_get_iface (NM_DEVICE (self)),
85   		            nm_device_get_ifindex (NM_DEVICE (self)));
86   	
87   		return object;
88   	}
89   	
90   	static void
91   	nm_device_infiniband_init (NMDeviceInfiniband * self)
92   	{
93   	}
94   	
95   	NMDevice *
96   	nm_device_infiniband_new (NMPlatformLink *platform_device)
97   	{
98   		g_return_val_if_fail (platform_device != NULL, NULL);
99   	
100  		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
101  		                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
102  		                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
103  		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
104  		                                  NULL);
105  	}
106  	
107  	NMDevice *
108  	nm_device_infiniband_new_partition (NMConnection *connection,
109  	                                    NMDevice     *parent)
110  	{
111  		NMSettingInfiniband *s_infiniband;
112  		int p_key, parent_ifindex;
113  		const char *iface;
114  	
115  		g_return_val_if_fail (connection != NULL, NULL);
116  		g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (parent), NULL);
117  	
118  		iface = nm_connection_get_virtual_iface_name (connection);
119  		g_return_val_if_fail (iface != NULL, NULL);
120  	
121  		parent_ifindex = nm_device_get_ifindex (parent);
122  		s_infiniband = nm_connection_get_setting_infiniband (connection);
123  		p_key = nm_setting_infiniband_get_p_key (s_infiniband);
124  	
125  		if (   !nm_platform_infiniband_partition_add (parent_ifindex, p_key)
126  		    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
127  			nm_log_warn (LOGD_DEVICE | LOGD_INFINIBAND, "(%s): failed to add InfiniBand P_Key interface for '%s': %s",
128  			             iface, nm_connection_get_id (connection),
129  			             nm_platform_get_error_msg ());
130  			return NULL;
131  		}
132  	
133  		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
134  		                                  NM_DEVICE_IFACE, iface,
135  		                                  NM_DEVICE_DRIVER, nm_device_get_driver (parent),
136  		                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
137  		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
138  		                                  NULL);
139  	}
140  	
141  	static guint32
142  	get_generic_capabilities (NMDevice *dev)
143  	{
144  		return NM_DEVICE_CAP_CARRIER_DETECT;
145  	}
146  	
147  	static NMActStageReturn
148  	act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
149  	{
150  		NMActRequest *req;
151  		NMConnection *connection;
152  		NMSettingInfiniband *s_infiniband;
153  		const char *transport_mode;
154  		char *mode_path;
155  		gboolean ok;
156  	
157  		g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
158  	
159  		req = nm_device_get_act_request (dev);
160  		g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
161  	
162  		connection = nm_act_request_get_connection (req);
163  		g_assert (connection);
164  		s_infiniband = nm_connection_get_setting_infiniband (connection);
165  		g_assert (s_infiniband);
166  	
167  		transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband);
168  	
169  		mode_path = g_strdup_printf ("/sys/class/net/%s/mode", nm_device_get_iface (dev));
170  		if (!g_file_test (mode_path, G_FILE_TEST_EXISTS)) {
171  			g_free (mode_path);
172  	
173  			if (!strcmp (transport_mode, "datagram"))
174  				return NM_ACT_STAGE_RETURN_SUCCESS;
175  			else {
176  				*reason = NM_DEVICE_STATE_REASON_INFINIBAND_MODE;
177  				return NM_ACT_STAGE_RETURN_FAILURE;
178  			}
179  		}
180  	
181  		ok = nm_utils_do_sysctl (mode_path, transport_mode);
182  		g_free (mode_path);
183  	
184  		if (!ok) {
185  			*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
186  			return NM_ACT_STAGE_RETURN_FAILURE;
187  		}
188  	
189  		return NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (dev, reason);
190  	}
191  	
192  	static void
193  	ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
194  	{
195  		NMConnection *connection;
196  		NMSettingInfiniband *s_infiniband;
197  		guint32 mtu;
198  	
199  		connection = nm_device_get_connection (self);
200  		g_assert (connection);
201  		s_infiniband = nm_connection_get_setting_infiniband (connection);
202  		g_assert (s_infiniband);
203  	
204  		/* MTU override */
205  		mtu = nm_setting_infiniband_get_mtu (s_infiniband);
206  		if (mtu)
207  			nm_ip4_config_set_mtu (config, mtu);
208  	}
209  	
210  	static gboolean
211  	check_connection_compatible (NMDevice *device,
212  	                             NMConnection *connection,
213  	                             GError **error)
214  	{
215  		NMSettingInfiniband *s_infiniband;
216  		const GByteArray *mac;
217  	
218  		if (!NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->check_connection_compatible (device, connection, error))
219  			return FALSE;
220  	
221  		if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
222  			g_set_error (error,
223  			             NM_INFINIBAND_ERROR,
224  						 NM_INFINIBAND_ERROR_CONNECTION_NOT_INFINIBAND,
225  			             "The connection was not an InfiniBand connection.");
226  			return FALSE;
227  		}
228  	
229  		s_infiniband = nm_connection_get_setting_infiniband (connection);
230  		if (!s_infiniband) {
231  			g_set_error (error,
232  			             NM_INFINIBAND_ERROR, NM_INFINIBAND_ERROR_CONNECTION_INVALID,
233  			             "The connection was not a valid infiniband connection.");
234  			return FALSE;
235  		}
236  	
237  		if (s_infiniband) {
238  			mac = nm_setting_infiniband_get_mac_address (s_infiniband);
239  			/* We only compare the last 8 bytes */
240  			if (mac && memcmp (mac->data + INFINIBAND_ALEN - 8,
241  			                   nm_device_get_hw_address (device, NULL) + INFINIBAND_ALEN - 8,
242  			                   8)) {
243  				g_set_error (error,
244  				             NM_INFINIBAND_ERROR,
245  				             NM_INFINIBAND_ERROR_CONNECTION_INCOMPATIBLE,
246  				             "The connection's MAC address did not match this device.");
247  				return FALSE;
248  			}
249  		}
250  	
251  		return TRUE;
252  	}
253  	
254  	static gboolean
255  	complete_connection (NMDevice *device,
256  	                     NMConnection *connection,
257  	                     const char *specific_object,
258  	                     const GSList *existing_connections,
259  	                     GError **error)
260  	{
261  		NMSettingInfiniband *s_infiniband;
262  		const GByteArray *setting_mac;
263  		const guint8 *hw_address;
264  	
265  		nm_utils_complete_generic (connection,
266  		                           NM_SETTING_INFINIBAND_SETTING_NAME,
267  		                           existing_connections,
268  		                           _("InfiniBand connection %d"),
269  		                           NULL,
270  		                           TRUE);
271  	
272  		s_infiniband = nm_connection_get_setting_infiniband (connection);
273  		if (!s_infiniband) {
274  			s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
275  			nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
276  		}
277  	
278  		setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
279  		hw_address = nm_device_get_hw_address (device, NULL);
280  		if (setting_mac) {
281  			/* Make sure the setting MAC (if any) matches the device's MAC */
282  			if (memcmp (setting_mac->data, hw_address, INFINIBAND_ALEN)) {
283  				g_set_error_literal (error,
284  				                     NM_SETTING_INFINIBAND_ERROR,
285  				                     NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
286  				                     NM_SETTING_INFINIBAND_MAC_ADDRESS);
287  				return FALSE;
288  			}
289  		} else {
290  			GByteArray *mac;
291  	
292  			/* Lock the connection to this device by default */
293  			mac = g_byte_array_sized_new (INFINIBAND_ALEN);
294  			g_byte_array_append (mac, hw_address, INFINIBAND_ALEN);
295  			g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
296  			g_byte_array_free (mac, TRUE);
297  		}
298  	
299  		if (!nm_setting_infiniband_get_transport_mode (s_infiniband))
300  			g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL);
301  	
302  		return TRUE;
303  	}
304  	
305  	static gboolean
306  	match_l2_config (NMDevice *self, NMConnection *connection)
307  	{
308  		/* FIXME */
309  		return TRUE;
310  	}
311  	
312  	static gboolean
313  	spec_match_list (NMDevice *device, const GSList *specs)
314  	{
315  		char *hwaddr_str, *spec_str;
316  		const GSList *iter;
317  	
318  		if (NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->spec_match_list (device, specs))
319  			return TRUE;
320  	
321  		hwaddr_str = nm_utils_hwaddr_ntoa (nm_device_get_hw_address (device, NULL),
322  		                                   ARPHRD_INFINIBAND);
323  	
324  		/* InfiniBand hardware address matches only need to match the last
325  		 * 8 bytes. In string format, that means we skip the first 36
326  		 * characters of hwaddr_str, and the first 40 of the spec (to skip
327  		 * "mac:" too).
328  		 */
329  		for (iter = specs; iter; iter = g_slist_next (iter)) {
330  			spec_str = iter->data;
331  	
332  			if (   !g_ascii_strncasecmp (spec_str, "mac:", 4)
333  			    && strlen (spec_str) > 40
334  			    && !g_ascii_strcasecmp (spec_str + 40, hwaddr_str + 36)) {
335  				g_free (hwaddr_str);
336  				return TRUE;
337  			}
338  		}
339  	
340  		g_free (hwaddr_str);
341  		return FALSE;
342  	}
343  	
344  	static void
345  	get_property (GObject *object, guint prop_id,
346  	              GValue *value, GParamSpec *pspec)
347  	{
348  		switch (prop_id) {
349  		default:
350  			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
351  			break;
352  		}
353  	}
354  	
355  	static void
356  	set_property (GObject *object, guint prop_id,
357  				  const GValue *value, GParamSpec *pspec)
358  	{
359  		switch (prop_id) {
360  		default:
361  			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362  			break;
363  		}
364  	}
365  	
366  	static void
367  	nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
368  	{
369  		GObjectClass *object_class = G_OBJECT_CLASS (klass);
370  		NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
371  	
372  		g_type_class_add_private (object_class, sizeof (NMDeviceInfinibandPrivate));
373  	
374  		/* virtual methods */
375  		object_class->constructor = constructor;
376  		object_class->get_property = get_property;
377  		object_class->set_property = set_property;
378  	
379  		parent_class->get_generic_capabilities = get_generic_capabilities;
380  		parent_class->check_connection_compatible = check_connection_compatible;
381  		parent_class->complete_connection = complete_connection;
382  		parent_class->spec_match_list = spec_match_list;
383  	
384  		parent_class->act_stage1_prepare = act_stage1_prepare;
385  		parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
386  		parent_class->match_l2_config = match_l2_config;
387  	
388  		/* properties */
389  	
390  		nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
391  		                                        G_TYPE_FROM_CLASS (klass),
392  		                                        &dbus_glib_nm_device_infiniband_object_info);
393  	
394  		dbus_g_error_domain_register (NM_INFINIBAND_ERROR, NULL, NM_TYPE_INFINIBAND_ERROR);
395  	}
396