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