ifcfg-rh: allow handling complex routing rules via dispatcher (rh #1160013)
[NetworkManager.git] / src / settings / plugins / ifcfg-rh / reader.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager system settings service
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 2008 - 2014 Red Hat, Inc.
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
28 #include <sys/wait.h>
29 #include <sys/inotify.h>
30 #include <errno.h>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33
34 #include <glib.h>
35 #include <glib/gi18n.h>
36 #include <nm-connection.h>
37 #include <nm-dbus-interface.h>
38 #include <nm-setting-connection.h>
39 #include <nm-setting-ip4-config.h>
40 #include <nm-setting-vlan.h>
41 #include <nm-setting-ip6-config.h>
42 #include <nm-setting-wired.h>
43 #include <nm-setting-wireless.h>
44 #include <nm-setting-8021x.h>
45 #include <nm-setting-bond.h>
46 #include <nm-setting-team.h>
47 #include <nm-setting-team-port.h>
48 #include <nm-setting-bridge.h>
49 #include <nm-setting-bridge-port.h>
50 #include <nm-setting-dcb.h>
51 #include <nm-setting-generic.h>
52 #include "nm-core-internal.h"
53 #include <nm-utils.h>
54
55 #include "nm-platform.h"
56 #include "nm-posix-signals.h"
57 #include "NetworkManagerUtils.h"
58 #include "nm-logging.h"
59
60 #include "common.h"
61 #include "shvar.h"
62 #include "utils.h"
63
64 #include "reader.h"
65
66 #define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, "    " msg)
67
68 static gboolean
69 get_int (const char *str, int *value)
70 {
71         char *e;
72         long int tmp;
73
74         errno = 0;
75         tmp = strtol (str, &e, 0);
76         if (errno || *e != '\0' || tmp > G_MAXINT || tmp < G_MININT)
77                 return FALSE;
78         *value = (int) tmp;
79         return TRUE;
80 }
81
82 static gboolean
83 get_uint (const char *str, guint32 *value)
84 {
85         char *e;
86         long unsigned int tmp;
87
88         errno = 0;
89         tmp = strtoul (str, &e, 0);
90         if (errno || *e != '\0')
91                 return FALSE;
92         *value = (guint32) tmp;
93         return TRUE;
94 }
95
96 static char *
97 make_connection_name (shvarFile *ifcfg,
98                       const char *ifcfg_name,
99                       const char *suggested,
100                       const char *prefix)
101 {
102         char *full_name = NULL, *name;
103
104         /* If the ifcfg file already has a NAME, always use that */
105         name = svGetValue (ifcfg, "NAME", FALSE);
106         if (name && strlen (name))
107                 return name;
108
109         /* Otherwise construct a new NAME */
110         g_free (name);
111         if (!prefix)
112                 prefix = _("System");
113
114         /* For cosmetic reasons, if the suggested name is the same as
115          * the ifcfg files name, don't use it.  Mainly for wifi so that
116          * the SSID is shown in the connection ID instead of just "wlan0".
117          */
118         if (suggested && strcmp (ifcfg_name, suggested))
119                 full_name = g_strdup_printf ("%s %s (%s)", prefix, suggested, ifcfg_name);
120         else
121                 full_name = g_strdup_printf ("%s %s", prefix, ifcfg_name);
122
123         return full_name;
124 }
125
126 static NMSetting *
127 make_connection_setting (const char *file,
128                          shvarFile *ifcfg,
129                          const char *type,
130                          const char *suggested,
131                          const char *prefix)
132 {
133         NMSettingConnection *s_con;
134         const char *ifcfg_name = NULL;
135         char *new_id, *uuid = NULL, *zone = NULL, *value;
136
137         ifcfg_name = utils_get_ifcfg_name (file, TRUE);
138         if (!ifcfg_name)
139                 return NULL;
140
141         s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
142
143         new_id = make_connection_name (ifcfg, ifcfg_name, suggested, prefix);
144         g_object_set (s_con, NM_SETTING_CONNECTION_ID, new_id, NULL);
145         g_free (new_id);
146
147         /* Try for a UUID key before falling back to hashing the file name */
148         uuid = svGetValue (ifcfg, "UUID", FALSE);
149         if (!uuid || !strlen (uuid)) {
150                 g_free (uuid);
151                 uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
152         }
153
154         g_object_set (s_con,
155                       NM_SETTING_CONNECTION_TYPE, type,
156                       NM_SETTING_CONNECTION_UUID, uuid,
157                       NULL);
158         g_free (uuid);
159
160         value = svGetValue (ifcfg, "DEVICE", FALSE);
161         if (value) {
162                 if (nm_utils_iface_valid_name (value)) {
163                         g_object_set (s_con,
164                                       NM_SETTING_CONNECTION_INTERFACE_NAME, value,
165                                       NULL);
166                 } else
167                         PARSE_WARNING ("invalid DEVICE name '%s'", value);
168                 g_free (value);
169         }
170
171         /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */
172         g_object_set (s_con,
173                       NM_SETTING_CONNECTION_AUTOCONNECT,
174                       svTrueValue (ifcfg, "ONBOOT", TRUE),
175                       NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
176                       (gint) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10,
177                                               NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
178                                               NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
179                                               NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT),
180                       NULL);
181
182         value = svGetValue (ifcfg, "USERS", FALSE);
183         if (value) {
184                 char **items, **iter;
185
186                 items = g_strsplit_set (value, " ", -1);
187                 for (iter = items; iter && *iter; iter++) {
188                         if (strlen (*iter)) {
189                                 if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL))
190                                         PARSE_WARNING ("invalid USERS item '%s'", *iter);
191                         }
192                 }
193                 g_free (value);
194                 g_strfreev (items);
195         }
196
197
198         zone = svGetValue(ifcfg, "ZONE", FALSE);
199         if (!zone || !strlen (zone)) {
200                 g_free (zone);
201                 zone = NULL;
202         }
203         g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, zone, NULL);
204         g_free (zone);
205
206         value = svGetValue (ifcfg, "SECONDARY_UUIDS", FALSE);
207         if (value) {
208                 char **items, **iter;
209
210                 items = g_strsplit_set (value, " \t", -1);
211                 for (iter = items; iter && *iter; iter++) {
212                         if (strlen (*iter)) {
213                                 if (!nm_setting_connection_add_secondary (s_con, *iter))
214                                         PARSE_WARNING ("secondary connection UUID '%s' already added", *iter);
215                         }
216                 }
217                 g_free (value);
218                 g_strfreev (items);
219         }
220
221         value = svGetValue (ifcfg, "BRIDGE", FALSE);
222         if (value) {
223                 const char *old_value;
224
225                 if ((old_value = nm_setting_connection_get_master (s_con))) {
226                         PARSE_WARNING ("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"",
227                                        old_value, value);
228                 } else {
229                         g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL);
230                         g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE,
231                                       NM_SETTING_BRIDGE_SETTING_NAME, NULL);
232                 }
233                 g_free (value);
234         }
235
236         value = svGetValue (ifcfg, "GATEWAY_PING_TIMEOUT", FALSE);
237         if (value) {
238                 long int tmp;
239                 guint32 timeout;
240
241                 errno = 0;
242                 tmp = strtol (value, NULL, 10);
243                 if (errno == 0 && tmp >= 0 && tmp < G_MAXINT32) {
244                         timeout = (guint32) tmp;
245                         g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, timeout, NULL);
246                 } else
247                         PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time");
248                 g_free (value);
249         }
250
251         return NM_SETTING (s_con);
252 }
253
254 /* Returns TRUE on missing address or valid address */
255 static gboolean
256 read_ip4_address (shvarFile *ifcfg,
257                   const char *tag,
258                   char **out_addr,
259                   GError **error)
260 {
261         char *value = NULL;
262
263         g_return_val_if_fail (ifcfg != NULL, FALSE);
264         g_return_val_if_fail (tag != NULL, FALSE);
265         g_return_val_if_fail (out_addr != NULL, FALSE);
266         g_return_val_if_fail (!error || !*error, FALSE);
267
268         *out_addr = NULL;
269
270         value = svGetValue (ifcfg, tag, FALSE);
271         if (!value)
272                 return TRUE;
273
274         if (nm_utils_ipaddr_valid (AF_INET, value)) {
275                 *out_addr = value;
276                 return TRUE;
277         } else {
278                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
279                              "Invalid %s IP4 address '%s'", tag, value);
280                 g_free (value);
281                 return FALSE;
282         }
283 }
284
285 static char *
286 get_numbered_tag (char *tag_name, int which)
287 {
288         if (which == -1)
289                 return g_strdup (tag_name);
290         return g_strdup_printf ("%s%u", tag_name, which);
291 }
292
293 static gboolean
294 is_any_ip4_address_defined (shvarFile *ifcfg)
295 {
296         int i;
297
298         for (i = -1; i <= 2; i++) {
299                 char *tag;
300                 char *value;
301
302                 tag = get_numbered_tag ("IPADDR", i);
303                 value = svGetValue (ifcfg, tag, FALSE);
304                 g_free (tag);
305                 if (value) {
306                         g_free (value);
307                         return TRUE;
308                 }
309
310                 tag = get_numbered_tag ("PREFIX", i);
311                 value = svGetValue (ifcfg, tag, FALSE);
312                 g_free(tag);
313                 if (value) {
314                         g_free (value);
315                         return TRUE;
316                 }
317
318                 tag = get_numbered_tag ("NETMASK", i);
319                 value = svGetValue (ifcfg, tag, FALSE);
320                 g_free(tag);
321                 if (value) {
322                         g_free (value);
323                         return TRUE;
324                 }
325         }
326         return FALSE;
327 }
328
329 /* Returns TRUE on missing address or valid address */
330 static gboolean
331 read_full_ip4_address (shvarFile *ifcfg,
332                        const char *network_file,
333                        gint32 which,
334                        NMIPAddress *base_addr,
335                        NMIPAddress **out_address,
336                        char **out_gateway,
337                        GError **error)
338 {
339         char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag;
340         char *ip = NULL;
341         long prefix = 0;
342         gboolean success = FALSE;
343         char *value;
344         guint32 tmp;
345
346         g_return_val_if_fail (which >= -1, FALSE);
347         g_return_val_if_fail (ifcfg != NULL, FALSE);
348         g_return_val_if_fail (network_file != NULL, FALSE);
349         g_return_val_if_fail (out_address != NULL, FALSE);
350         g_return_val_if_fail (*out_address == NULL, FALSE);
351         g_return_val_if_fail (!error || !*error, FALSE);
352
353         ip_tag = get_numbered_tag ("IPADDR", which);
354         prefix_tag = get_numbered_tag ("PREFIX", which);
355         netmask_tag = get_numbered_tag ("NETMASK", which);
356         gw_tag = get_numbered_tag ("GATEWAY", which);
357
358         /* IP address */
359         if (!read_ip4_address (ifcfg, ip_tag, &ip, error))
360                 goto done;
361         if (!ip) {
362                 if (base_addr)
363                         ip = g_strdup (nm_ip_address_get_address (base_addr));
364                 else {
365                         success = TRUE;
366                         goto done;
367                 }
368         }
369
370         /* Gateway */
371         if (out_gateway && !*out_gateway) {
372                 if (!read_ip4_address (ifcfg, gw_tag, out_gateway, error))
373                         goto done;
374         }
375
376         /* Prefix */
377         value = svGetValue (ifcfg, prefix_tag, FALSE);
378         if (value) {
379                 errno = 0;
380                 prefix = strtol (value, NULL, 10);
381                 if (errno || prefix < 0) {
382                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
383                                      "Invalid IP4 prefix '%s'", value);
384                         g_free (value);
385                         goto done;
386                 }
387                 g_free (value);
388         }
389
390         /* Fall back to NETMASK if no PREFIX was specified */
391         if (prefix == 0) {
392                 if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
393                         goto done;
394                 if (value) {
395                         inet_pton (AF_INET, value, &tmp);
396                         prefix = nm_utils_ip4_netmask_to_prefix (tmp);
397                         g_free (value);
398                 }
399         }
400
401         if (prefix == 0 && base_addr)
402                 prefix = nm_ip_address_get_prefix (base_addr);
403
404         /* Try to autodetermine the prefix for the address' class */
405         if (prefix == 0) {
406                 if (inet_pton (AF_INET, ip, &tmp) == 1) {
407                         prefix = nm_utils_ip4_get_default_prefix (tmp);
408
409                         PARSE_WARNING ("missing %s, assuming %s/%ld", prefix_tag, ip, prefix);
410                 }
411         }
412
413         /* Validate the prefix */
414         if (prefix == 0) {
415                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
416                              "Missing IP4 prefix");
417                 goto done;
418         }
419
420         *out_address = nm_ip_address_new (AF_INET, ip, prefix, error);
421         if (*out_address)
422                 success = TRUE;
423
424 done:
425         g_free (ip);
426         g_free (ip_tag);
427         g_free (prefix_tag);
428         g_free (netmask_tag);
429         g_free (gw_tag);
430
431         return success;
432 }
433
434 /* Returns TRUE on missing route or valid route */
435 static gboolean
436 read_one_ip4_route (shvarFile *ifcfg,
437                     const char *network_file,
438                     guint32 which,
439                     NMIPRoute **out_route,
440                     GError **error)
441 {
442         char *ip_tag, *netmask_tag, *gw_tag, *metric_tag, *value;
443         char *dest = NULL, *next_hop = NULL;
444         gint64 prefix, metric;
445         gboolean success = FALSE;
446
447         g_return_val_if_fail (ifcfg != NULL, FALSE);
448         g_return_val_if_fail (network_file != NULL, FALSE);
449         g_return_val_if_fail (out_route != NULL, FALSE);
450         g_return_val_if_fail (*out_route == NULL, FALSE);
451         g_return_val_if_fail (!error || !*error, FALSE);
452
453         ip_tag = g_strdup_printf ("ADDRESS%u", which);
454         netmask_tag = g_strdup_printf ("NETMASK%u", which);
455         gw_tag = g_strdup_printf ("GATEWAY%u", which);
456         metric_tag = g_strdup_printf ("METRIC%u", which);
457
458         /* Destination */
459         if (!read_ip4_address (ifcfg, ip_tag, &dest, error))
460                 goto out;
461         if (!dest) {
462                 /* Check whether IP is missing or 0.0.0.0 */
463                 char *val;
464                 val = svGetValue (ifcfg, ip_tag, FALSE);
465                 if (!val) {
466                         *out_route = NULL;
467                         success = TRUE;  /* missing route = success */
468                         goto out;
469                 }
470                 g_free (val);
471         }
472
473         /* Next hop */
474         if (!read_ip4_address (ifcfg, gw_tag, &next_hop, error))
475                 goto out;
476         /* We don't make distinction between missing GATEWAY IP and 0.0.0.0 */
477
478         /* Prefix */
479         if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
480                 goto out;
481         if (value) {
482                 guint32 netmask;
483
484                 inet_pton (AF_INET, value, &netmask);
485                 prefix = nm_utils_ip4_netmask_to_prefix (netmask);
486                 g_free (value);
487                 if (prefix == 0 || netmask != nm_utils_ip4_prefix_to_netmask (prefix)) {
488                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
489                                      "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, NULL));
490                         goto out;
491                 }
492         } else {
493                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
494                              "Missing IP4 route element '%s'", netmask_tag);
495                 goto out;
496         }
497
498         /* Metric */
499         value = svGetValue (ifcfg, metric_tag, FALSE);
500         if (value) {
501                 metric = nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
502                 if (metric < 0) {
503                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
504                                      "Invalid IP4 route metric '%s'", value);
505                         g_free (value);
506                         goto out;
507                 }
508                 g_free (value);
509         } else
510                 metric = -1;
511
512         *out_route = nm_ip_route_new (AF_INET, dest, prefix, next_hop, metric, error);
513         if (*out_route)
514                 success = TRUE;
515
516 out:
517         g_free (dest);
518         g_free (next_hop);
519         g_free (ip_tag);
520         g_free (netmask_tag);
521         g_free (gw_tag);
522         g_free (metric_tag);
523         return success;
524 }
525
526 static gboolean
527 read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error)
528 {
529         char *contents = NULL;
530         gsize len = 0;
531         char **lines = NULL, **iter;
532         GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
533         GMatchInfo *match_info;
534         NMIPRoute *route = NULL;
535         char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
536         gint64 prefix_int, metric_int;
537         gboolean success = FALSE;
538
539         const char *pattern_empty = "^\\s*(\\#.*)?$";
540         const char *pattern_to1 = "^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)"  /* IP or 'default' keyword */
541                                   "(?:/(\\d{1,2}))?";                                         /* optional prefix */
542         const char *pattern_to2 = "to\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|default)" /* IP or 'default' keyword */
543                                   "(?:/(\\d{1,2}))?";                                         /* optional prefix */
544         const char *pattern_via = "via\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";       /* IP of gateway */
545         const char *pattern_metric = "metric\\s+(\\d+)";                                      /* metric */
546
547         g_return_val_if_fail (filename != NULL, FALSE);
548         g_return_val_if_fail (s_ip4 != NULL, FALSE);
549         g_return_val_if_fail (!error || !*error, FALSE);
550
551         /* Read the route file */
552         if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) {
553                 g_free (contents);
554                 return TRUE;  /* missing/empty = success */
555         }
556
557         /* Create regexes for pieces to be matched */
558         regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL);
559         regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL);
560         regex_via = g_regex_new (pattern_via, 0, 0, NULL);
561         regex_metric = g_regex_new (pattern_metric, 0, 0, NULL);
562
563         /* Iterate through file lines */
564         lines = g_strsplit_set (contents, "\n\r", -1);
565         for (iter = lines; iter && *iter; iter++) {
566
567                 /* Skip empty lines */
568                 if (g_regex_match_simple (pattern_empty, *iter, 0, 0))
569                         continue;
570
571                 /* Destination */
572                 g_regex_match (regex_to1, *iter, 0, &match_info);
573                 if (!g_match_info_matches (match_info)) {
574                         g_match_info_free (match_info);
575                         g_regex_match (regex_to2, *iter, 0, &match_info);
576                         if (!g_match_info_matches (match_info)) {
577                                 g_match_info_free (match_info);
578                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
579                                              "Missing IP4 route destination address in record: '%s'", *iter);
580                                 goto error;
581                         }
582                 }
583                 dest = g_match_info_fetch (match_info, 1);
584                 if (!strcmp (dest, "default"))
585                         strcpy (dest, "0.0.0.0");
586                 if (!nm_utils_ipaddr_valid (AF_INET, dest)) {
587                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
588                                      "Invalid IP4 route destination address '%s'", dest);
589                         g_free (dest);
590                         g_match_info_free (match_info);
591                         goto error;
592                 }
593
594                 /* Prefix - is optional; 32 if missing */
595                 prefix = g_match_info_fetch (match_info, 2);
596                 g_match_info_free (match_info);
597                 prefix_int = 32;
598                 if (prefix) {
599                         errno = 0;
600                         prefix_int = strtol (prefix, NULL, 10);
601                         if (errno || prefix_int <= 0 || prefix_int > 32) {
602                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
603                                              "Invalid IP4 route destination prefix '%s'", prefix);
604                                 g_free (dest);
605                                 g_free (prefix);
606                                 goto error;
607                         }
608                 }
609                 g_free (prefix);
610
611                 /* Next hop */
612                 g_regex_match (regex_via, *iter, 0, &match_info);
613                 if (g_match_info_matches (match_info)) {
614                         next_hop = g_match_info_fetch (match_info, 1);
615                         if (!nm_utils_ipaddr_valid (AF_INET, next_hop)) {
616                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
617                                              "Invalid IP4 route gateway address '%s'",
618                                              next_hop);
619                                 g_match_info_free (match_info);
620                                 g_free (dest);
621                                 g_free (next_hop);
622                                 goto error;
623                         }
624                 } else {
625                         /* we don't make distinction between missing GATEWAY IP and 0.0.0.0 */
626                         next_hop = NULL;
627                 }
628                 g_match_info_free (match_info);
629
630                 /* Metric */
631                 g_regex_match (regex_metric, *iter, 0, &match_info);
632                 metric_int = -1;
633                 if (g_match_info_matches (match_info)) {
634                         metric = g_match_info_fetch (match_info, 1);
635                         errno = 0;
636                         metric_int = strtol (metric, NULL, 10);
637                         if (errno || metric_int < 0) {
638                                 g_match_info_free (match_info);
639                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
640                                              "Invalid IP4 route metric '%s'", metric);
641                                 g_free (dest);
642                                 g_free (next_hop);
643                                 g_free (metric);
644                                 goto error;
645                         }
646                         g_free (metric);
647                 }
648                 g_match_info_free (match_info);
649
650                 route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error);
651                 if (!route) {
652                         g_free (dest);
653                         g_free (next_hop);
654                         goto error;
655                 }
656                 if (!nm_setting_ip_config_add_route (s_ip4, route))
657                         PARSE_WARNING ("duplicate IP4 route");
658         }
659
660         success = TRUE;
661
662 error:
663         g_free (contents);
664         g_strfreev (lines);
665         if (route)
666                 nm_ip_route_unref (route);
667         g_regex_unref (regex_to1);
668         g_regex_unref (regex_to2);
669         g_regex_unref (regex_via);
670         g_regex_unref (regex_metric);
671
672         return success;
673 }
674
675 static gboolean
676 parse_full_ip6_address (shvarFile *ifcfg,
677                         const char *network_file,
678                         const char *addr_str,
679                         int i,
680                         NMIPAddress **out_address,
681                         GError **error)
682 {
683         char **list;
684         char *ip_val, *prefix_val;
685         long prefix;
686         gboolean success = FALSE;
687
688         g_return_val_if_fail (addr_str != NULL, FALSE);
689         g_return_val_if_fail (out_address != NULL, FALSE);
690         g_return_val_if_fail (*out_address == NULL, FALSE);
691         g_return_val_if_fail (!error || !*error, FALSE);
692
693         /* Split the address and prefix */
694         list = g_strsplit_set (addr_str, "/", 2);
695         if (g_strv_length (list) < 1) {
696                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
697                              "Invalid IP6 address '%s'", addr_str);
698                 goto error;
699         }
700
701         ip_val = list[0];
702
703         prefix_val = list[1];
704         if (prefix_val) {
705                 errno = 0;
706                 prefix = strtol (prefix_val, NULL, 10);
707                 if (errno || prefix <= 0 || prefix > 128) {
708                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
709                                      "Invalid IP6 prefix '%s'", prefix_val);
710                         goto error;
711                 }
712         } else {
713                 /* Missing prefix is treated as prefix of 64 */
714                 prefix = 64;
715         }
716
717         *out_address = nm_ip_address_new (AF_INET6, ip_val, prefix, error);
718         if (*out_address)
719                 success = TRUE;
720
721 error:
722         g_strfreev (list);
723         return success;
724 }
725
726 /* IPv6 address is very complex to describe completely by a regular expression,
727  * so don't try to, rather use looser syntax to comprise all possibilities
728  * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
729  * namely destination IP without 'to' keyword is recognized just at line start.
730  */
731 #define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
732
733 static gboolean
734 read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
735 {
736         char *contents = NULL;
737         gsize len = 0;
738         char **lines = NULL, **iter;
739         GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
740         GMatchInfo *match_info;
741         NMIPRoute *route = NULL;
742         char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
743         gint64 prefix_int, metric_int;
744         gboolean success = FALSE;
745
746         const char *pattern_empty = "^\\s*(\\#.*)?$";
747         const char *pattern_to1 = "^\\s*(default|" IPV6_ADDR_REGEX ")"  /* IPv6 or 'default' keyword */
748                                   "(?:/(\\d{1,3}))?";                   /* optional prefix */
749         const char *pattern_to2 = "to\\s+(default|" IPV6_ADDR_REGEX ")" /* IPv6 or 'default' keyword */
750                                   "(?:/(\\d{1,3}))?";                   /* optional prefix */
751         const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")";       /* IPv6 of gateway */
752         const char *pattern_metric = "metric\\s+(\\d+)";                /* metric */
753
754         g_return_val_if_fail (filename != NULL, FALSE);
755         g_return_val_if_fail (s_ip6 != NULL, FALSE);
756         g_return_val_if_fail (!error || !*error, FALSE);
757
758         /* Read the route file */
759         if (!g_file_get_contents (filename, &contents, &len, NULL) || !len) {
760                 g_free (contents);
761                 return TRUE;  /* missing/empty = success */
762         }
763
764         /* Create regexes for pieces to be matched */
765         regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL);
766         regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL);
767         regex_via = g_regex_new (pattern_via, 0, 0, NULL);
768         regex_metric = g_regex_new (pattern_metric, 0, 0, NULL);
769
770         /* Iterate through file lines */
771         lines = g_strsplit_set (contents, "\n\r", -1);
772         for (iter = lines; iter && *iter; iter++) {
773
774                 /* Skip empty lines */
775                 if (g_regex_match_simple (pattern_empty, *iter, 0, 0))
776                         continue;
777
778                 /* Destination */
779                 g_regex_match (regex_to1, *iter, 0, &match_info);
780                 if (!g_match_info_matches (match_info)) {
781                         g_match_info_free (match_info);
782                         g_regex_match (regex_to2, *iter, 0, &match_info);
783                         if (!g_match_info_matches (match_info)) {
784                                 g_match_info_free (match_info);
785                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
786                                              "Missing IP6 route destination address in record: '%s'", *iter);
787                                 goto error;
788                         }
789                 }
790                 dest = g_match_info_fetch (match_info, 1);
791                 if (!g_strcmp0 (dest, "default")) {
792                         /* Ignore default route - NM handles it internally */
793                         g_clear_pointer (&dest, g_free);
794                         g_match_info_free (match_info);
795                         PARSE_WARNING ("ignoring manual default route: '%s' (%s)", *iter, filename);
796                         continue;
797                 }
798
799                 /* Prefix - is optional; 128 if missing */
800                 prefix = g_match_info_fetch (match_info, 2);
801                 g_match_info_free (match_info);
802                 prefix_int = 128;
803                 if (prefix) {
804                         errno = 0;
805                         prefix_int = strtol (prefix, NULL, 10);
806                         if (errno || prefix_int <= 0 || prefix_int > 128) {
807                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
808                                              "Invalid IP6 route destination prefix '%s'", prefix);
809                                 g_free (dest);
810                                 g_free (prefix);
811                                 goto error;
812                         }
813                 }
814                 g_free (prefix);
815
816                 /* Next hop */
817                 g_regex_match (regex_via, *iter, 0, &match_info);
818                 if (g_match_info_matches (match_info)) {
819                         next_hop = g_match_info_fetch (match_info, 1);
820                         if (!nm_utils_ipaddr_valid (AF_INET6, next_hop)) {
821                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
822                                              "Invalid IPv6 route nexthop address '%s'",
823                                              next_hop);
824                                 g_match_info_free (match_info);
825                                 g_free (dest);
826                                 g_free (next_hop);
827                                 goto error;
828                         }
829                 } else {
830                         /* Missing "via" is taken as :: */
831                         next_hop = NULL;
832                 }
833                 g_match_info_free (match_info);
834
835                 /* Metric */
836                 g_regex_match (regex_metric, *iter, 0, &match_info);
837                 metric_int = -1;
838                 if (g_match_info_matches (match_info)) {
839                         metric = g_match_info_fetch (match_info, 1);
840                         errno = 0;
841                         metric_int = strtol (metric, NULL, 10);
842                         if (errno || metric_int < 0 || metric_int > G_MAXUINT32) {
843                                 g_match_info_free (match_info);
844                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
845                                              "Invalid IP6 route metric '%s'", metric);
846                                 g_free (dest);
847                                 g_free (next_hop);
848                                 g_free (metric);
849                                 goto error;
850                         }
851                         g_free (metric);
852                 }
853                 g_match_info_free (match_info);
854
855                 route = nm_ip_route_new (AF_INET6, dest, prefix_int, next_hop, metric_int, error);
856                 g_free (dest);
857                 g_free (next_hop);
858                 if (!route)
859                         goto error;
860                 if (!nm_setting_ip_config_add_route (s_ip6, route))
861                         PARSE_WARNING ("duplicate IP6 route");
862         }
863
864         success = TRUE;
865
866 error:
867         g_free (contents);
868         g_strfreev (lines);
869         if (route)
870                 nm_ip_route_unref (route);
871         g_regex_unref (regex_to1);
872         g_regex_unref (regex_to2);
873         g_regex_unref (regex_via);
874         g_regex_unref (regex_metric);
875
876         return success;
877 }
878
879
880 static NMSetting *
881 make_ip4_setting (shvarFile *ifcfg,
882                   const char *network_file,
883                   GError **error)
884 {
885         NMSettingIPConfig *s_ip4 = NULL;
886         char *value = NULL;
887         char *route_path = NULL;
888         char *method;
889         char *gateway = NULL;
890         gint32 i;
891         shvarFile *network_ifcfg;
892         shvarFile *route_ifcfg;
893         gboolean never_default = FALSE;
894
895         s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
896
897         /* First check if DEFROUTE is set for this device; DEFROUTE has the
898          * opposite meaning from never-default. The default if DEFROUTE is not
899          * specified is DEFROUTE=yes which means that this connection can be used
900          * as a default route
901          */
902         never_default = !svTrueValue (ifcfg, "DEFROUTE", TRUE);
903
904         /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
905         network_ifcfg = svOpenFile (network_file, NULL);
906         if (network_ifcfg) {
907                 char *gatewaydev;
908
909                 /* Get the connection ifcfg device name and the global gateway device */
910                 value = svGetValue (ifcfg, "DEVICE", FALSE);
911                 gatewaydev = svGetValue (network_ifcfg, "GATEWAYDEV", FALSE);
912
913                 /* If there was a global gateway device specified, then only connections
914                  * for that device can be the default connection.
915                  */
916                 if (gatewaydev && value)
917                         never_default = !!strcmp (value, gatewaydev);
918
919                 g_free (gatewaydev);
920                 g_free (value);
921                 svCloseFile (network_ifcfg);
922         }
923
924         value = svGetValue (ifcfg, "BOOTPROTO", FALSE);
925
926         if (!value || !*value || !g_ascii_strcasecmp (value, "none")) {
927                 if (is_any_ip4_address_defined (ifcfg))
928                         method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
929                 else
930                         method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
931         } else if (!g_ascii_strcasecmp (value, "bootp") || !g_ascii_strcasecmp (value, "dhcp")) {
932                 method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
933         } else if (!g_ascii_strcasecmp (value, "static")) {
934                 method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
935         } else if (!g_ascii_strcasecmp (value, "autoip")) {
936                 g_free (value);
937                 g_object_set (s_ip4,
938                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
939                               NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
940                               NULL);
941                 return NM_SETTING (s_ip4);
942         } else if (!g_ascii_strcasecmp (value, "shared")) {
943                 g_free (value);
944                 g_object_set (s_ip4,
945                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED,
946                               NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
947                               NULL);
948                 return NM_SETTING (s_ip4);
949         } else {
950                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
951                              "Unknown BOOTPROTO '%s'", value);
952                 g_free (value);
953                 goto done;
954         }
955         g_free (value);
956
957         g_object_set (s_ip4,
958                       NM_SETTING_IP_CONFIG_METHOD, method,
959                       NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "PEERDNS", TRUE),
960                       NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "PEERROUTES", TRUE),
961                       NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
962                       NM_SETTING_IP_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV4_FAILURE_FATAL", FALSE),
963                       NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV4_ROUTE_METRIC", 10,
964                                                                           -1, G_MAXUINT32, -1),
965                       NULL);
966
967         if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
968                 return NM_SETTING (s_ip4);
969
970         /* Handle DHCP settings */
971         if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
972                 value = svGetValue (ifcfg, "DHCP_HOSTNAME", FALSE);
973                 if (value && strlen (value))
974                         g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL);
975                 g_free (value);
976
977                 g_object_set (s_ip4,
978                               NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
979                               svTrueValue (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
980                               NULL);
981
982                 value = svGetValue (ifcfg, "DHCP_CLIENT_ID", FALSE);
983                 if (value && strlen (value))
984                         g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL);
985                 g_free (value);
986         }
987
988         /* Read static IP addresses.
989          * Read them even for AUTO method - in this case the addresses are
990          * added to the automatic ones. Note that this is not currently supported by
991          * the legacy 'network' service (ifup-eth).
992          */
993         for (i = -1; i < 256; i++) {
994                 NMIPAddress *addr = NULL;
995
996                 if (!read_full_ip4_address (ifcfg, network_file, i, NULL, &addr, &gateway, error))
997                         goto done;
998
999                 if (!addr) {
1000                         /* The first mandatory variable is 2-indexed (IPADDR2)
1001                          * Variables IPADDR, IPADDR0 and IPADDR1 are optional */
1002                         if (i > 1)
1003                                 break;
1004                         continue;
1005                 }
1006
1007                 if (!nm_setting_ip_config_add_address (s_ip4, addr))
1008                         PARSE_WARNING ("duplicate IP4 address");
1009                 nm_ip_address_unref (addr);
1010         }
1011
1012         /* Gateway */
1013         if (!gateway) {
1014                 network_ifcfg = svOpenFile (network_file, NULL);
1015                 if (network_ifcfg) {
1016                         gboolean read_success;
1017
1018                         read_success = read_ip4_address (network_ifcfg, "GATEWAY", &gateway, error);
1019                         svCloseFile (network_ifcfg);
1020                         if (!read_success)
1021                                 goto done;
1022                 }
1023         }
1024         g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
1025
1026         /* DNS servers
1027          * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
1028          */
1029         for (i = 1; i <= 10; i++) {
1030                 char *tag;
1031
1032                 tag = g_strdup_printf ("DNS%u", i);
1033                 value = svGetValue (ifcfg, tag, FALSE);
1034                 if (value) {
1035                         if (nm_utils_ipaddr_valid (AF_INET, value)) {
1036                                 if (!nm_setting_ip_config_add_dns (s_ip4, value))
1037                                         PARSE_WARNING ("duplicate DNS server %s", tag);
1038                         } else if (nm_utils_ipaddr_valid (AF_INET6, value)) {
1039                                 /* Ignore IPv6 addresses */
1040                         } else {
1041                                 PARSE_WARNING ("invalid DNS server address %s", value);
1042                                 g_free (tag);
1043                                 g_free (value);
1044                                 goto done;
1045                         }
1046
1047                         g_free (value);
1048                 }
1049
1050                 g_free (tag);
1051         }
1052
1053         /* DNS searches */
1054         value = svGetValue (ifcfg, "DOMAIN", FALSE);
1055         if (value) {
1056                 char **searches = NULL;
1057
1058                 searches = g_strsplit (value, " ", 0);
1059                 if (searches) {
1060                         char **item;
1061                         for (item = searches; *item; item++) {
1062                                 if (strlen (*item)) {
1063                                         if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
1064                                                 PARSE_WARNING ("duplicate DNS domain '%s'", *item);
1065                                 }
1066                         }
1067                         g_strfreev (searches);
1068                 }
1069                 g_free (value);
1070         }
1071
1072         /* Static routes  - route-<name> file */
1073         route_path = utils_get_route_path (ifcfg->fileName);
1074
1075         if (utils_has_complex_routes (route_path)) {
1076                 PARSE_WARNING ("'rule-' or 'rule6-' file is present; you will need to use a dispatcher script to apply these routes");
1077         } else if (utils_has_route_file_new_syntax (route_path)) {
1078                 /* Parse route file in new syntax */
1079                 route_ifcfg = utils_get_route_ifcfg (ifcfg->fileName, FALSE);
1080                 if (route_ifcfg) {
1081                         for (i = 0; i < 256; i++) {
1082                                 NMIPRoute *route = NULL;
1083
1084                                 if (!read_one_ip4_route (route_ifcfg, network_file, i, &route, error)) {
1085                                         svCloseFile (route_ifcfg);
1086                                         goto done;
1087                                 }
1088
1089                                 if (!route)
1090                                         break;
1091
1092                                 if (!nm_setting_ip_config_add_route (s_ip4, route))
1093                                         PARSE_WARNING ("duplicate IP4 route");
1094                                 nm_ip_route_unref (route);
1095                         }
1096                         svCloseFile (route_ifcfg);
1097                 }
1098         } else {
1099                 if (!read_route_file_legacy (route_path, s_ip4, error))
1100                         goto done;
1101         }
1102
1103         /* Legacy value NM used for a while but is incorrect (rh #459370) */
1104         if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
1105                 value = svGetValue (ifcfg, "SEARCH", FALSE);
1106                 if (value) {
1107                         char **searches = NULL;
1108
1109                         searches = g_strsplit (value, " ", 0);
1110                         if (searches) {
1111                                 char **item;
1112                                 for (item = searches; *item; item++) {
1113                                         if (strlen (*item)) {
1114                                                 if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
1115                                                         PARSE_WARNING ("duplicate DNS search '%s'", *item);
1116                                         }
1117                                 }
1118                                 g_strfreev (searches);
1119                         }
1120                         g_free (value);
1121                 }
1122         }
1123
1124         return NM_SETTING (s_ip4);
1125
1126 done:
1127         g_free (gateway);
1128         g_free (route_path);
1129         g_object_unref (s_ip4);
1130         return NULL;
1131 }
1132
1133 static void
1134 read_aliases (NMSettingIPConfig *s_ip4, const char *filename, const char *network_file)
1135 {
1136         GDir *dir;
1137         char *dirname, *base;
1138         shvarFile *parsed;
1139         NMIPAddress *base_addr;
1140         GError *err = NULL;
1141
1142         g_return_if_fail (s_ip4 != NULL);
1143         g_return_if_fail (filename != NULL);
1144
1145         if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0)
1146                 return;
1147
1148         base_addr = nm_setting_ip_config_get_address (s_ip4, 0);
1149
1150         dirname = g_path_get_dirname (filename);
1151         g_return_if_fail (dirname != NULL);
1152         base = g_path_get_basename (filename);
1153         g_return_if_fail (base != NULL);
1154
1155         dir = g_dir_open (dirname, 0, &err);
1156         if (dir) {
1157                 const char *item;
1158                 NMIPAddress *addr;
1159                 gboolean ok;
1160
1161                 while ((item = g_dir_read_name (dir))) {
1162                         char *full_path, *device;
1163                         const char *p;
1164
1165                         if (!utils_is_ifcfg_alias_file (item, base))
1166                                 continue;
1167
1168                         full_path = g_build_filename (dirname, item, NULL);
1169
1170                         p = strchr (item, ':');
1171                         g_assert (p != NULL); /* we know this is true from utils_is_ifcfg_alias_file() */
1172                         for (p++; *p; p++) {
1173                                 if (!g_ascii_isalnum (*p) && *p != '_')
1174                                         break;
1175                         }
1176                         if (*p) {
1177                                 PARSE_WARNING ("ignoring alias file '%s' with invalid name", full_path);
1178                                 g_free (full_path);
1179                                 continue;
1180                         }
1181
1182                         parsed = svOpenFile (full_path, &err);
1183                         if (!parsed) {
1184                                 PARSE_WARNING ("couldn't parse alias file '%s': %s", full_path, err->message);
1185                                 g_free (full_path);
1186                                 g_clear_error (&err);
1187                                 continue;
1188                         }
1189
1190                         device = svGetValue (parsed, "DEVICE", FALSE);
1191                         if (!device) {
1192                                 PARSE_WARNING ("alias file '%s' has no DEVICE", full_path);
1193                                 svCloseFile (parsed);
1194                                 g_free (full_path);
1195                                 continue;
1196                         }
1197                         /* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */
1198                         if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) {
1199                                 PARSE_WARNING ("alias file '%s' has invalid DEVICE (%s) for filename",
1200                                                full_path, device);
1201                                 g_free (device);
1202                                 svCloseFile (parsed);
1203                                 g_free (full_path);
1204                                 continue;
1205                         }
1206
1207                         addr = NULL;
1208                         ok = read_full_ip4_address (parsed, network_file, -1, base_addr, &addr, NULL, &err);
1209                         svCloseFile (parsed);
1210                         if (ok) {
1211                                 nm_ip_address_set_attribute (addr, "label", g_variant_new_string (device));
1212                                 if (!nm_setting_ip_config_add_address (s_ip4, addr))
1213                                         PARSE_WARNING ("duplicate IP4 address in alias file %s", item);
1214                         } else {
1215                                 PARSE_WARNING ("error reading IP4 address from alias file '%s': %s",
1216                                                full_path, err ? err->message : "no address");
1217                                 g_clear_error (&err);
1218                         }
1219                         nm_ip_address_unref (addr);
1220
1221                         g_free (device);
1222                         g_free (full_path);
1223                 }
1224
1225                 g_dir_close (dir);
1226         } else {
1227                 PARSE_WARNING ("can not read directory '%s': %s", dirname, err->message);
1228                 g_error_free (err);
1229         }
1230
1231         g_free (base);
1232         g_free (dirname);
1233 }
1234
1235 static NMSetting *
1236 make_ip6_setting (shvarFile *ifcfg,
1237                   const char *network_file,
1238                   GError **error)
1239 {
1240         NMSettingIPConfig *s_ip6 = NULL;
1241         char *value = NULL;
1242         char *str_value;
1243         char *route6_path = NULL;
1244         gboolean ipv6init, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE;
1245         char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
1246         char *ipv6addr, *ipv6addr_secondaries;
1247         char **list = NULL, **iter;
1248         guint32 i;
1249         shvarFile *network_ifcfg;
1250         gboolean never_default = FALSE;
1251         gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
1252         char *ip6_privacy_str;
1253         NMSettingIP6ConfigPrivacy ip6_privacy_val;
1254
1255         s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
1256
1257         /* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the
1258          * opposite meaning from never-default. The default if IPV6_DEFROUTE is not
1259          * specified is IPV6_DEFROUTE=yes which means that this connection can be used
1260          * as a default route
1261          */
1262         never_default = !svTrueValue (ifcfg, "IPV6_DEFROUTE", TRUE);
1263
1264         /* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified;
1265          * they are global and override IPV6_DEFROUTE
1266          * When both are set, the device specified in IPV6_DEFAULTGW takes preference.
1267          */
1268         network_ifcfg = svOpenFile (network_file, NULL);
1269         if (network_ifcfg) {
1270                 char *ipv6_defaultgw, *ipv6_defaultdev;
1271                 char *default_dev = NULL;
1272
1273                 /* Get the connection ifcfg device name and the global default route device */
1274                 value = svGetValue (ifcfg, "DEVICE", FALSE);
1275                 ipv6_defaultgw = svGetValue (network_ifcfg, "IPV6_DEFAULTGW", FALSE);
1276                 ipv6_defaultdev = svGetValue (network_ifcfg, "IPV6_DEFAULTDEV", FALSE);
1277
1278                 if (ipv6_defaultgw) {
1279                         default_dev = strchr (ipv6_defaultgw, '%');
1280                         if (default_dev)
1281                                 default_dev++;
1282                 }
1283                 if (!default_dev)
1284                         default_dev = ipv6_defaultdev;
1285
1286                 /* If there was a global default route device specified, then only connections
1287                  * for that device can be the default connection.
1288                  */
1289                 if (default_dev && value)
1290                         never_default = !!strcmp (value, default_dev);
1291
1292                 g_free (ipv6_defaultgw);
1293                 g_free (ipv6_defaultdev);
1294                 g_free (value);
1295                 svCloseFile (network_ifcfg);
1296         }
1297
1298         /* Find out method property */
1299         /* Is IPV6 enabled? Set method to "ignored", when not enabled */
1300         str_value = svGetValue (ifcfg, "IPV6INIT", FALSE);
1301         ipv6init = svTrueValue (ifcfg, "IPV6INIT", FALSE);
1302         if (!str_value) {
1303                 network_ifcfg = svOpenFile (network_file, NULL);
1304                 if (network_ifcfg) {
1305                         ipv6init = svTrueValue (network_ifcfg, "IPV6INIT", FALSE);
1306                         svCloseFile (network_ifcfg);
1307                 }
1308         }
1309         g_free (str_value);
1310
1311         if (!ipv6init)
1312                 method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;  /* IPv6 is disabled */
1313         else {
1314                 ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
1315                 ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
1316                 dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE);
1317
1318                 if (ipv6_autoconf)
1319                         method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
1320                 else if (dhcp6)
1321                         method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
1322                 else {
1323                         /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
1324                         str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
1325                         if (!str_value)
1326                                 str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
1327
1328                         if (!str_value)
1329                                 method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
1330                         g_free (str_value);
1331                 }
1332         }
1333         /* TODO - handle other methods */
1334
1335         /* Read IPv6 Privacy Extensions configuration */
1336         ip6_privacy_str = svGetValue (ifcfg, "IPV6_PRIVACY", FALSE);
1337         if (ip6_privacy_str) {
1338                 ip6_privacy = svTrueValue (ifcfg, "IPV6_PRIVACY", FALSE);
1339                 if (!ip6_privacy)
1340                         ip6_privacy = g_strcmp0 (ip6_privacy_str, "rfc4941") == 0 ||
1341                                       g_strcmp0 (ip6_privacy_str, "rfc3041") == 0;
1342         }
1343         ip6_privacy_prefer_public_ip = svTrueValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE);
1344         ip6_privacy_val = ip6_privacy_str ?
1345                               (ip6_privacy ?
1346                                   (ip6_privacy_prefer_public_ip ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) :
1347                                   NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED) :
1348                               NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
1349         g_free (ip6_privacy_str);
1350
1351         g_object_set (s_ip6,
1352                       NM_SETTING_IP_CONFIG_METHOD, method,
1353                       NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "IPV6_PEERDNS", TRUE),
1354                       NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "IPV6_PEERROUTES", TRUE),
1355                       NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
1356                       NM_SETTING_IP_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV6_FAILURE_FATAL", FALSE),
1357                       NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV6_ROUTE_METRIC", 10,
1358                                                                           -1, G_MAXUINT32, -1),
1359                       NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val,
1360                       NULL);
1361
1362         /* Don't bother to read IP, DNS and routes when IPv6 is disabled */
1363         if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
1364                 return NM_SETTING (s_ip6);
1365
1366         if (   !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
1367             || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
1368                 /* METHOD_AUTO may trigger DHCPv6, so save the hostname to send to DHCP */
1369                 value = svGetValue (ifcfg, "DHCP_HOSTNAME", FALSE);
1370                 if (value && value[0])
1371                         g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL);
1372                 g_free (value);
1373         }
1374
1375         /* Read static IP addresses.
1376          * Read them even for AUTO and DHCP methods - in this case the addresses are
1377          * added to the automatic ones. Note that this is not currently supported by
1378          * the legacy 'network' service (ifup-eth).
1379          */
1380         ipv6addr = svGetValue (ifcfg, "IPV6ADDR", FALSE);
1381         ipv6addr_secondaries = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
1382
1383         value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
1384                            ipv6addr ? ipv6addr : "",
1385                            ipv6addr_secondaries ? ipv6addr_secondaries : "",
1386                            NULL);
1387         g_free (ipv6addr);
1388         g_free (ipv6addr_secondaries);
1389
1390         list = g_strsplit_set (value, " ", 0);
1391         g_free (value);
1392         for (iter = list, i = 0; iter && *iter; iter++, i++) {
1393                 NMIPAddress *addr = NULL;
1394
1395                 if (!parse_full_ip6_address (ifcfg, network_file, *iter, i, &addr, error)) {
1396                         g_strfreev (list);
1397                         goto error;
1398                 }
1399
1400                 if (!nm_setting_ip_config_add_address (s_ip6, addr))
1401                         PARSE_WARNING ("duplicate IP6 address");
1402                 nm_ip_address_unref (addr);
1403         }
1404         g_strfreev (list);
1405
1406         /* Gateway */
1407         if (nm_setting_ip_config_get_num_addresses (s_ip6)) {
1408                 value = svGetValue (ifcfg, "IPV6_DEFAULTGW", FALSE);
1409                 if (!value) {
1410                         /* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */
1411                         network_ifcfg = svOpenFile (network_file, NULL);
1412                         if (network_ifcfg) {
1413                                 value = svGetValue (network_ifcfg, "IPV6_DEFAULTGW", FALSE);
1414                                 svCloseFile (network_ifcfg);
1415                         }
1416                 }
1417                 if (value) {
1418                         char *ptr;
1419                         if ((ptr = strchr (value, '%')) != NULL)
1420                                 *ptr = '\0';  /* remove %interface prefix if present */
1421                         if (!nm_utils_ipaddr_valid (AF_INET6, value)) {
1422                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1423                                              "Invalid IP6 address '%s'", value);
1424                                 g_free (value);
1425                                 goto error;
1426                         }
1427
1428                         g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, value, NULL);
1429                         g_free (value);
1430                 }
1431         }
1432
1433         /* DNS servers
1434          * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
1435          */
1436         for (i = 1; i <= 10; i++) {
1437                 char *tag;
1438
1439                 tag = g_strdup_printf ("DNS%u", i);
1440                 value = svGetValue (ifcfg, tag, FALSE);
1441                 if (!value) {
1442                         g_free (tag);
1443                         break; /* all done */
1444                 }
1445
1446                 if (nm_utils_ipaddr_valid (AF_INET6, value)) {
1447                         if (!nm_setting_ip_config_add_dns (s_ip6, value))
1448                                 PARSE_WARNING ("duplicate DNS server %s", tag);
1449                 } else if (nm_utils_ipaddr_valid (AF_INET, value)) {
1450                         /* Ignore IPv4 addresses */
1451                 } else {
1452                         PARSE_WARNING ("invalid DNS server address %s", value);
1453                         g_free (tag);
1454                         g_free (value);
1455                         goto error;
1456                 }
1457
1458                 g_free (tag);
1459                 g_free (value);
1460         }
1461
1462         /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */
1463
1464         if (!utils_has_complex_routes (ifcfg->fileName)) {
1465                 /* Read static routes from route6-<interface> file */
1466                 route6_path = utils_get_route6_path (ifcfg->fileName);
1467                 if (!read_route6_file (route6_path, s_ip6, error))
1468                         goto error;
1469
1470                 g_free (route6_path);
1471         }
1472
1473         return NM_SETTING (s_ip6);
1474
1475 error:
1476         g_free (route6_path);
1477         g_object_unref (s_ip6);
1478         return NULL;
1479 }
1480
1481 static void
1482 check_if_bond_slave (shvarFile *ifcfg,
1483                      NMSettingConnection *s_con)
1484 {
1485         char *value;
1486
1487         value = svGetValue (ifcfg, "MASTER", FALSE);
1488         if (value) {
1489                 g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL);
1490                 g_object_set (s_con,
1491                               NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
1492                               NULL);
1493                 g_free (value);
1494         }
1495
1496         /* We should be checking for SLAVE=yes as well, but NM used to not set that,
1497          * so for backward-compatibility, we don't check.
1498          */
1499 }
1500
1501 static void
1502 check_if_team_slave (shvarFile *ifcfg,
1503                      NMSettingConnection *s_con)
1504 {
1505         char *value;
1506
1507         value = svGetValue (ifcfg, "DEVICETYPE", FALSE);
1508         if (!value)
1509                 return;
1510         if (strcasecmp (value, TYPE_TEAM_PORT)) {
1511                 g_free (value);
1512                 return;
1513         }
1514         g_free (value);
1515         value = svGetValue (ifcfg, "TEAM_MASTER", FALSE);
1516         if (!value)
1517                 return;
1518         g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL);
1519         g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL);
1520         g_free (value);
1521 }
1522
1523 typedef struct {
1524         const char *enable_key;
1525         const char *advertise_key;
1526         const char *willing_key;
1527         const char *flags_prop;
1528 } DcbFlagsProperty;
1529
1530 enum {
1531         DCB_APP_FCOE_FLAGS = 0,
1532         DCB_APP_ISCSI_FLAGS = 1,
1533         DCB_APP_FIP_FLAGS = 2,
1534         DCB_PFC_FLAGS = 3,
1535         DCB_PG_FLAGS = 4,
1536 };
1537
1538 static DcbFlagsProperty dcb_flags_props[] = {
1539         { KEY_DCB_APP_FCOE_ENABLE,  KEY_DCB_APP_FCOE_ADVERTISE,  KEY_DCB_APP_FCOE_WILLING,  NM_SETTING_DCB_APP_FCOE_FLAGS },
1540         { KEY_DCB_APP_ISCSI_ENABLE, KEY_DCB_APP_ISCSI_ADVERTISE, KEY_DCB_APP_ISCSI_WILLING, NM_SETTING_DCB_APP_ISCSI_FLAGS },
1541         { KEY_DCB_APP_FIP_ENABLE,   KEY_DCB_APP_FIP_ADVERTISE,   KEY_DCB_APP_FIP_WILLING,   NM_SETTING_DCB_APP_FIP_FLAGS },
1542         { KEY_DCB_PFC_ENABLE,       KEY_DCB_PFC_ADVERTISE,       KEY_DCB_PFC_WILLING,       NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS },
1543         { KEY_DCB_PG_ENABLE,        KEY_DCB_PG_ADVERTISE,        KEY_DCB_PG_WILLING,        NM_SETTING_DCB_PRIORITY_GROUP_FLAGS },
1544         { NULL },
1545 };
1546
1547 static NMSettingDcbFlags
1548 read_dcb_flags (shvarFile *ifcfg, DcbFlagsProperty *property)
1549 {
1550         NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
1551
1552         if (svTrueValue (ifcfg, property->enable_key, FALSE))
1553                 flags |= NM_SETTING_DCB_FLAG_ENABLE;
1554         if (svTrueValue (ifcfg, property->advertise_key, FALSE))
1555                 flags |= NM_SETTING_DCB_FLAG_ADVERTISE;
1556         if (svTrueValue (ifcfg, property->willing_key, FALSE))
1557                 flags |= NM_SETTING_DCB_FLAG_WILLING;
1558
1559         return flags;
1560 }
1561
1562 static gboolean
1563 read_dcb_app (shvarFile *ifcfg,
1564               NMSettingDcb *s_dcb,
1565               const char *app,
1566               DcbFlagsProperty *flags_prop,
1567               const char *priority_prop,
1568               GError **error)
1569 {
1570         NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
1571         char *tmp, *val;
1572         gboolean success = TRUE;
1573         int priority = -1;
1574
1575         flags = read_dcb_flags (ifcfg, flags_prop);
1576
1577         /* Priority */
1578         tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app);
1579         val = svGetValue (ifcfg, tmp, FALSE);
1580         if (val) {
1581                 success = get_int (val, &priority);
1582                 if (success)
1583                         success = (priority >= 0 && priority <= 7);
1584                 if (!success) {
1585                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1586                                      "Invalid %s value '%s' (expected 0 - 7)",
1587                                      tmp, val);
1588                 }
1589                 g_free (val);
1590
1591                 if (!(flags & NM_SETTING_DCB_FLAG_ENABLE))
1592                         PARSE_WARNING ("ignoring DCB %s priority; app not enabled", app);
1593         }
1594         g_free (tmp);
1595
1596         if (success) {
1597                 g_object_set (G_OBJECT (s_dcb),
1598                               flags_prop->flags_prop, flags,
1599                               priority_prop, (guint) priority,
1600                               NULL);
1601         }
1602
1603         return success;
1604 }
1605
1606 typedef void (*DcbSetBoolFunc) (NMSettingDcb *, guint, gboolean);
1607
1608 static gboolean
1609 read_dcb_bool_array (shvarFile *ifcfg,
1610                      NMSettingDcb *s_dcb,
1611                      NMSettingDcbFlags flags,
1612                      const char *prop,
1613                      const char *desc,
1614                      DcbSetBoolFunc set_func,
1615                      GError **error)
1616 {
1617         char *val;
1618         gboolean success = FALSE;
1619         guint i;
1620
1621         val = svGetValue (ifcfg, prop, FALSE);
1622         if (!val)
1623                 return TRUE;
1624
1625         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1626                 PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
1627                 success = TRUE;
1628                 goto out;
1629         }
1630
1631         val = g_strstrip (val);
1632         if (strlen (val) != 8) {
1633                 PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
1634                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1635                                      "boolean array must be 8 characters");
1636                 goto out;
1637         }
1638
1639         /* All characters must be either 0 or 1 */
1640         for (i = 0; i < 8; i++) {
1641                 if (val[i] != '0' && val[i] != '1') {
1642                         PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, val);
1643                         g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1644                                              "invalid boolean digit");
1645                         goto out;
1646                 }
1647                 set_func (s_dcb, i, (val[i] == '1'));
1648         }
1649         success = TRUE;
1650
1651 out:
1652         g_free (val);
1653         return success;
1654 }
1655
1656 typedef void (*DcbSetUintFunc) (NMSettingDcb *, guint, guint);
1657
1658 static gboolean
1659 read_dcb_uint_array (shvarFile *ifcfg,
1660                      NMSettingDcb *s_dcb,
1661                      NMSettingDcbFlags flags,
1662                      const char *prop,
1663                      const char *desc,
1664                      gboolean f_allowed,
1665                      DcbSetUintFunc set_func,
1666                      GError **error)
1667 {
1668         char *val;
1669         gboolean success = FALSE;
1670         guint i;
1671
1672         val = svGetValue (ifcfg, prop, FALSE);
1673         if (!val)
1674                 return TRUE;
1675
1676         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1677                 PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
1678                 success = TRUE;
1679                 goto out;
1680         }
1681
1682         val = g_strstrip (val);
1683         if (strlen (val) != 8) {
1684                 PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
1685                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1686                                      "uint array must be 8 characters");
1687                 goto out;
1688         }
1689
1690         /* All characters must be either 0 - 7 or (optionally) f */
1691         for (i = 0; i < 8; i++) {
1692                 if (val[i] >= '0' && val[i] <= '7')
1693                         set_func (s_dcb, i, val[i] - '0');
1694                 else if (f_allowed && (val[i] == 'f' || val[i] == 'F'))
1695                         set_func (s_dcb, i, 15);
1696                 else {
1697                         PARSE_WARNING ("invalid %s value '%s': not 0 - 7%s",
1698                                        prop, val, f_allowed ? " or 'f'" : "");
1699                         g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1700                                              "invalid uint digit");
1701                         goto out;
1702                 }
1703         }
1704         success = TRUE;
1705
1706 out:
1707         g_free (val);
1708         return success;
1709 }
1710
1711 static gboolean
1712 read_dcb_percent_array (shvarFile *ifcfg,
1713                         NMSettingDcb *s_dcb,
1714                         NMSettingDcbFlags flags,
1715                         const char *prop,
1716                         const char *desc,
1717                         gboolean sum_pct,
1718                         DcbSetUintFunc set_func,
1719                         GError **error)
1720 {
1721         char *val;
1722         gboolean success = FALSE;
1723         char **split = NULL, **iter;
1724         int tmp;
1725         guint i, sum = 0;
1726
1727         val = svGetValue (ifcfg, prop, FALSE);
1728         if (!val)
1729                 return TRUE;
1730
1731         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1732                 PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
1733                 success = TRUE;
1734                 goto out;
1735         }
1736
1737         val = g_strstrip (val);
1738         split = g_strsplit_set (val, ",", 0);
1739         if (!split || (g_strv_length (split) != 8)) {
1740                 PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val);
1741                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1742                                      "percent array must be 8 elements");
1743                 goto out;
1744         }
1745
1746         for (iter = split, i = 0; iter && *iter; iter++, i++) {
1747                 if (!get_int (*iter, &tmp) || tmp < 0 || tmp > 100) {
1748                         PARSE_WARNING ("invalid %s percentage value '%s'", prop, *iter);
1749                         g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1750                                              "invalid percent element");
1751                         goto out;
1752                 }
1753                 set_func (s_dcb, i, (guint) tmp);
1754                 sum += (guint) tmp;
1755         }
1756
1757         if (sum_pct && (sum != 100)) {
1758                 PARSE_WARNING ("%s percentages do not equal 100%%", prop);
1759                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1760                                      "invalid percentage sum");
1761                 goto out;
1762         }
1763
1764         success = TRUE;
1765
1766 out:
1767         if (split)
1768                 g_strfreev (split);
1769         g_free (val);
1770         return success;
1771 }
1772
1773 static gboolean
1774 make_dcb_setting (shvarFile *ifcfg,
1775                   const char *network_file,
1776                   NMSetting **out_setting,
1777                   GError **error)
1778 {
1779         NMSettingDcb *s_dcb = NULL;
1780         gboolean dcb_on;
1781         NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
1782         char *val;
1783
1784         g_return_val_if_fail (out_setting != NULL, FALSE);
1785
1786         dcb_on = !!svTrueValue (ifcfg, "DCB", FALSE);
1787         if (!dcb_on)
1788                 return TRUE;
1789
1790         s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
1791         g_assert (s_dcb);
1792
1793         /* FCOE */
1794         if (!read_dcb_app (ifcfg, s_dcb, "FCOE",
1795                            &dcb_flags_props[DCB_APP_FCOE_FLAGS],
1796                            NM_SETTING_DCB_APP_FCOE_PRIORITY,
1797                            error)) {
1798                 g_object_unref (s_dcb);
1799                 return FALSE;
1800         }
1801         if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) {
1802                 val = svGetValue (ifcfg, KEY_DCB_APP_FCOE_MODE, FALSE);
1803                 if (val) {
1804                         if (strcmp (val, NM_SETTING_DCB_FCOE_MODE_FABRIC) == 0 ||
1805                             strcmp (val, NM_SETTING_DCB_FCOE_MODE_VN2VN) == 0)
1806                                 g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_FCOE_MODE, val, NULL);
1807                         else {
1808                                 PARSE_WARNING ("invalid FCoE mode '%s'", val);
1809                                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1810                                                      "invalid FCoE mode");
1811                                 g_free (val);
1812                                 g_object_unref (s_dcb);
1813                                 return FALSE;
1814                         }
1815                         g_free (val);
1816                 }
1817         }
1818
1819         /* iSCSI */
1820         if (!read_dcb_app (ifcfg, s_dcb, "ISCSI",
1821                            &dcb_flags_props[DCB_APP_ISCSI_FLAGS],
1822                            NM_SETTING_DCB_APP_ISCSI_PRIORITY,
1823                            error)) {
1824                 g_object_unref (s_dcb);
1825                 return FALSE;
1826         }
1827
1828         /* FIP */
1829         if (!read_dcb_app (ifcfg, s_dcb, "FIP",
1830                            &dcb_flags_props[DCB_APP_FIP_FLAGS],
1831                            NM_SETTING_DCB_APP_FIP_PRIORITY,
1832                            error)) {
1833                 g_object_unref (s_dcb);
1834                 return FALSE;
1835         }
1836
1837         /* Priority Flow Control */
1838         flags = read_dcb_flags (ifcfg, &dcb_flags_props[DCB_PFC_FLAGS]);
1839         g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, flags, NULL);
1840
1841         if (!read_dcb_bool_array (ifcfg,
1842                                   s_dcb,
1843                                   flags,
1844                                   KEY_DCB_PFC_UP,
1845                                   "PFC",
1846                                   nm_setting_dcb_set_priority_flow_control,
1847                                   error)) {
1848                 g_object_unref (s_dcb);
1849                 return FALSE;
1850         }
1851
1852         /* Priority Groups */
1853         flags = read_dcb_flags (ifcfg, &dcb_flags_props[DCB_PG_FLAGS]);
1854         g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, flags, NULL);
1855
1856         if (!read_dcb_uint_array (ifcfg,
1857                                   s_dcb,
1858                                   flags,
1859                                   KEY_DCB_PG_ID,
1860                                   "PGID",
1861                                   TRUE,
1862                                   nm_setting_dcb_set_priority_group_id,
1863                                   error)) {
1864                 g_object_unref (s_dcb);
1865                 return FALSE;
1866         }
1867
1868         /* Group bandwidth */
1869         if (!read_dcb_percent_array (ifcfg,
1870                                      s_dcb,
1871                                      flags,
1872                                      KEY_DCB_PG_PCT,
1873                                      "PGPCT",
1874                                      TRUE,
1875                                      nm_setting_dcb_set_priority_group_bandwidth,
1876                                      error)) {
1877                 g_object_unref (s_dcb);
1878                 return FALSE;
1879         }
1880
1881         /* Priority bandwidth */
1882         if (!read_dcb_percent_array (ifcfg,
1883                                      s_dcb,
1884                                      flags,
1885                                      KEY_DCB_PG_UPPCT,
1886                                      "UPPCT",
1887                                      FALSE,
1888                                      nm_setting_dcb_set_priority_bandwidth,
1889                                      error)) {
1890                 g_object_unref (s_dcb);
1891                 return FALSE;
1892         }
1893
1894         /* Strict Bandwidth */
1895         if (!read_dcb_bool_array (ifcfg,
1896                                   s_dcb,
1897                                   flags,
1898                                   KEY_DCB_PG_STRICT,
1899                                   "STRICT",
1900                                   nm_setting_dcb_set_priority_strict_bandwidth,
1901                                   error)) {
1902                 g_object_unref (s_dcb);
1903                 return FALSE;
1904         }
1905
1906         if (!read_dcb_uint_array (ifcfg,
1907                                   s_dcb,
1908                                   flags,
1909                                   KEY_DCB_PG_UP2TC,
1910                                   "UP2TC",
1911                                   FALSE,
1912                                   nm_setting_dcb_set_priority_traffic_class,
1913                                   error)) {
1914                 g_object_unref (s_dcb);
1915                 return FALSE;
1916         }
1917
1918         *out_setting = NM_SETTING (s_dcb);
1919         return TRUE;
1920 }
1921
1922 static gboolean
1923 add_one_wep_key (shvarFile *ifcfg,
1924                  const char *shvar_key,
1925                  guint8 key_idx,
1926                  gboolean passphrase,
1927                  NMSettingWirelessSecurity *s_wsec,
1928                  GError **error)
1929 {
1930         char *key = NULL;
1931         char *value = NULL;
1932         gboolean success = FALSE;
1933
1934         g_return_val_if_fail (ifcfg != NULL, FALSE);
1935         g_return_val_if_fail (shvar_key != NULL, FALSE);
1936         g_return_val_if_fail (key_idx <= 3, FALSE);
1937         g_return_val_if_fail (s_wsec != NULL, FALSE);
1938
1939         value = svGetValue (ifcfg, shvar_key, FALSE);
1940         if (!value || !strlen (value)) {
1941                 g_free (value);
1942                 return TRUE;
1943         }
1944
1945         /* Validate keys */
1946         if (passphrase) {
1947                 if (strlen (value) && strlen (value) < 64) {
1948                         key = g_strdup (value);
1949                         g_object_set (G_OBJECT (s_wsec),
1950                                       NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
1951                                       NM_WEP_KEY_TYPE_PASSPHRASE,
1952                                       NULL);
1953                 }
1954         } else {
1955                 if (strlen (value) == 10 || strlen (value) == 26) {
1956                         /* Hexadecimal WEP key */
1957                         char *p = value;
1958
1959                         while (*p) {
1960                                 if (!g_ascii_isxdigit (*p)) {
1961                                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1962                                                      "Invalid hexadecimal WEP key.");
1963                                         goto out;
1964                                 }
1965                                 p++;
1966                         }
1967                         key = g_strdup (value);
1968                 } else if (   !strncmp (value, "s:", 2)
1969                            && (strlen (value) == 7 || strlen (value) == 15)) {
1970                         /* ASCII key */
1971                         char *p = value + 2;
1972
1973                         while (*p) {
1974                                 if (!g_ascii_isprint ((int) (*p))) {
1975                                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1976                                                      "Invalid ASCII WEP key.");
1977                                         goto out;
1978                                 }
1979                                 p++;
1980                         }
1981
1982                         /* Remove 's:' prefix.
1983                          * Don't convert to hex string. wpa_supplicant takes 'wep_key0' option over D-Bus as byte array
1984                          * and converts it to hex string itself. Even though we convert hex string keys into a bin string
1985                          * before passing to wpa_supplicant, this prevents two unnecessary conversions. And mainly,
1986                          * ASCII WEP key doesn't change to HEX WEP key in UI, which could confuse users.
1987                          */
1988                         key = g_strdup (value + 2);
1989                 }
1990         }
1991
1992         if (key) {
1993                 nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
1994                 g_free (key);
1995                 success = TRUE;
1996         } else
1997                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1998                              "Invalid WEP key length.");
1999
2000 out:
2001         g_free (value);
2002         return success;
2003 }
2004
2005 static gboolean
2006 read_wep_keys (shvarFile *ifcfg,
2007                guint8 def_idx,
2008                NMSettingWirelessSecurity *s_wsec,
2009                GError **error)
2010 {
2011         /* Try hex/ascii keys first */
2012         if (!add_one_wep_key (ifcfg, "KEY1", 0, FALSE, s_wsec, error))
2013                 return FALSE;
2014         if (!add_one_wep_key (ifcfg, "KEY2", 1, FALSE, s_wsec, error))
2015                 return FALSE;
2016         if (!add_one_wep_key (ifcfg, "KEY3", 2, FALSE, s_wsec, error))
2017                 return FALSE;
2018         if (!add_one_wep_key (ifcfg, "KEY4", 3, FALSE, s_wsec, error))
2019                 return FALSE;
2020         if (!add_one_wep_key (ifcfg, "KEY", def_idx, FALSE, s_wsec, error))
2021                 return FALSE;
2022
2023         /* And then passphrases */
2024         if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE1", 0, TRUE, s_wsec, error))
2025                 return FALSE;
2026         if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE2", 1, TRUE, s_wsec, error))
2027                 return FALSE;
2028         if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE3", 2, TRUE, s_wsec, error))
2029                 return FALSE;
2030         if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE4", 3, TRUE, s_wsec, error))
2031                 return FALSE;
2032
2033         return TRUE;
2034 }
2035
2036 static NMSettingSecretFlags
2037 read_secret_flags (shvarFile *ifcfg, const char *flags_key)
2038 {
2039         NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
2040         char *val;
2041
2042         g_return_val_if_fail (flags_key != NULL, NM_SETTING_SECRET_FLAG_NONE);
2043         g_return_val_if_fail (flags_key[0] != '\0', NM_SETTING_SECRET_FLAG_NONE);
2044         g_return_val_if_fail (g_str_has_suffix (flags_key, "_FLAGS"), NM_SETTING_SECRET_FLAG_NONE);
2045
2046         val = svGetValue (ifcfg, flags_key, FALSE);
2047         if (val) {
2048                 if (strstr (val, SECRET_FLAG_AGENT))
2049                         flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
2050                 if (strstr (val, SECRET_FLAG_NOT_SAVED))
2051                         flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
2052                 if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
2053                         flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
2054
2055                 g_free (val);
2056         }
2057         return flags;
2058 }
2059
2060 static NMSetting *
2061 make_wep_setting (shvarFile *ifcfg,
2062                   const char *file,
2063                   GError **error)
2064 {
2065         NMSettingWirelessSecurity *s_wsec;
2066         char *value;
2067         shvarFile *keys_ifcfg = NULL;
2068         int default_key_idx = 0;
2069         gboolean has_default_key = FALSE, success;
2070         NMSettingSecretFlags key_flags;
2071
2072         s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
2073         g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL);
2074
2075         value = svGetValue (ifcfg, "DEFAULTKEY", FALSE);
2076         if (value) {
2077                 success = get_int (value, &default_key_idx);
2078                 if (success && (default_key_idx >= 1) && (default_key_idx <= 4)) {
2079                         has_default_key = TRUE;
2080                         default_key_idx--;  /* convert to [0...3] */
2081                         g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, default_key_idx, NULL);
2082                 } else {
2083                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2084                                      "Invalid default WEP key '%s'", value);
2085                         g_free (value);
2086                         goto error;
2087                 }
2088                 g_free (value);
2089         }
2090
2091         /* Read WEP key flags */
2092         key_flags = read_secret_flags (ifcfg, "WEP_KEY_FLAGS");
2093         g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL);
2094
2095         /* Read keys in the ifcfg file if they are system-owned */
2096         if (key_flags == NM_SETTING_SECRET_FLAG_NONE) {
2097                 if (!read_wep_keys (ifcfg, default_key_idx, s_wsec, error))
2098                         goto error;
2099
2100                 /* Try to get keys from the "shadow" key file */
2101                 keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
2102                 if (keys_ifcfg) {
2103                         if (!read_wep_keys (keys_ifcfg, default_key_idx, s_wsec, error)) {
2104                                 svCloseFile (keys_ifcfg);
2105                                 goto error;
2106                         }
2107                         svCloseFile (keys_ifcfg);
2108                         g_assert (error == NULL || *error == NULL);
2109                 }
2110         }
2111
2112         value = svGetValue (ifcfg, "SECURITYMODE", FALSE);
2113         if (value) {
2114                 char *lcase;
2115
2116                 lcase = g_ascii_strdown (value, -1);
2117                 g_free (value);
2118
2119                 if (!strcmp (lcase, "open")) {
2120                         g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL);
2121                 } else if (!strcmp (lcase, "restricted")) {
2122                         g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL);
2123                 } else {
2124                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2125                                      "Invalid WEP authentication algorithm '%s'",
2126                                      lcase);
2127                         g_free (lcase);
2128                         goto error;
2129                 }
2130                 g_free (lcase);
2131         }
2132
2133         /* If no WEP keys were given, and the keys are not agent-owned, and no
2134          * default WEP key index was given, then the connection is unencrypted.
2135          */
2136         if (   !nm_setting_wireless_security_get_wep_key (s_wsec, 0)
2137             && !nm_setting_wireless_security_get_wep_key (s_wsec, 1)
2138             && !nm_setting_wireless_security_get_wep_key (s_wsec, 2)
2139             && !nm_setting_wireless_security_get_wep_key (s_wsec, 3)
2140             && (has_default_key == FALSE)
2141             && (key_flags == NM_SETTING_SECRET_FLAG_NONE)) {
2142                 const char *auth_alg;
2143
2144                 auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
2145                 if (auth_alg && !strcmp (auth_alg, "shared")) {
2146                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2147                                      "WEP Shared Key authentication is invalid for "
2148                                      "unencrypted connections.");
2149                         goto error;
2150                 }
2151
2152                 /* Unencrypted */
2153                 g_object_unref (s_wsec);
2154                 s_wsec = NULL;
2155         }
2156
2157         return (NMSetting *) s_wsec;
2158
2159 error:
2160         if (s_wsec)
2161                 g_object_unref (s_wsec);
2162         return NULL;
2163 }
2164
2165 static gboolean
2166 fill_wpa_ciphers (shvarFile *ifcfg,
2167                   NMSettingWirelessSecurity *wsec,
2168                   gboolean group,
2169                   gboolean adhoc)
2170 {
2171         char *value = NULL, *p;
2172         char **list = NULL, **iter;
2173         int i = 0;
2174
2175         p = value = svGetValue (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", TRUE);
2176         if (!value)
2177                 return TRUE;
2178
2179         /* Strip quotes */
2180         if (p[0] == '"')
2181                 p++;
2182         if (p[strlen (p) - 1] == '"')
2183                 p[strlen (p) - 1] = '\0';
2184
2185         list = g_strsplit_set (p, " ", 0);
2186         for (iter = list; iter && *iter; iter++, i++) {
2187                 /* Ad-Hoc configurations cannot have pairwise ciphers, and can only
2188                  * have one group cipher.  Ignore any additional group ciphers and
2189                  * any pairwise ciphers specified.
2190                  */
2191                 if (adhoc) {
2192                         if (group && (i > 0)) {
2193                                 PARSE_WARNING ("ignoring group cipher '%s' (only one group cipher allowed "
2194                                                "in Ad-Hoc mode)", *iter);
2195                                 continue;
2196                         } else if (!group) {
2197                                 PARSE_WARNING ("ignoring pairwise cipher '%s' (pairwise not used "
2198                                                "in Ad-Hoc mode)", *iter);
2199                                 continue;
2200                         }
2201                 }
2202
2203                 if (!strcmp (*iter, "CCMP")) {
2204                         if (group)
2205                                 nm_setting_wireless_security_add_group (wsec, "ccmp");
2206                         else
2207                                 nm_setting_wireless_security_add_pairwise (wsec, "ccmp");
2208                 } else if (!strcmp (*iter, "TKIP")) {
2209                         if (group)
2210                                 nm_setting_wireless_security_add_group (wsec, "tkip");
2211                         else
2212                                 nm_setting_wireless_security_add_pairwise (wsec, "tkip");
2213                 } else if (group && !strcmp (*iter, "WEP104"))
2214                         nm_setting_wireless_security_add_group (wsec, "wep104");
2215                 else if (group && !strcmp (*iter, "WEP40"))
2216                         nm_setting_wireless_security_add_group (wsec, "wep40");
2217                 else {
2218                         PARSE_WARNING ("ignoring invalid %s cipher '%s'",
2219                                        group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE",
2220                                        *iter);
2221                 }
2222         }
2223
2224         if (list)
2225                 g_strfreev (list);
2226         g_free (value);
2227         return TRUE;
2228 }
2229
2230 #define WPA_PMK_LEN 32
2231
2232 static char *
2233 parse_wpa_psk (shvarFile *ifcfg,
2234                const char *file,
2235                GBytes *ssid,
2236                GError **error)
2237 {
2238         shvarFile *keys_ifcfg;
2239         char *psk = NULL, *p, *hashed = NULL;
2240         size_t plen;
2241         gboolean quoted = FALSE;
2242
2243         /* Passphrase must be between 10 and 66 characters in length because WPA
2244          * hex keys are exactly 64 characters (no quoting), and WPA passphrases
2245          * are between 8 and 63 characters (inclusive), plus optional quoting if
2246          * the passphrase contains spaces.
2247          */
2248
2249         /* Try to get keys from the "shadow" key file */
2250         keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
2251         if (keys_ifcfg) {
2252                 psk = svGetValue (keys_ifcfg, "WPA_PSK", TRUE);
2253                 svCloseFile (keys_ifcfg);
2254         }
2255
2256         /* Fall back to the original ifcfg */
2257         if (!psk)
2258                 psk = svGetValue (ifcfg, "WPA_PSK", TRUE);
2259
2260         if (!psk)
2261                 return NULL;
2262
2263         p = psk;
2264         plen = strlen (p);
2265
2266         if (   (plen >= 2 && (p[0] == '"' || p[0] == '\'') && p[0] == p[plen - 1])
2267             || (plen >= 3 && p[0] == '$' && p[1] == '\'' && p[1] == p[plen - 1]))
2268                 quoted = TRUE;
2269
2270         if (!quoted && (strlen (psk) == 64)) {
2271                 /* Verify the hex PSK; 64 digits */
2272                 while (*p) {
2273                         if (!g_ascii_isxdigit (*p++)) {
2274                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2275                                              "Invalid WPA_PSK (contains non-hexadecimal characters)");
2276                                 goto out;
2277                         }
2278                 }
2279                 hashed = g_strdup (psk);
2280         } else {
2281                 /* Prior to 4f6eef9e77265484555663cf666cde4fa8323469 and
2282                  * 28e2e446868b94b92edc4a82aa0bf1e3eda8ec54 the writer may not have
2283                  * properly quoted passphrases, so just handle anything that's unquoted
2284                  * and between 8 and 63 characters as a passphrase.
2285                  */
2286
2287                 /* Get rid of the quotes */
2288                 hashed = utils_single_unquote_string (p);
2289
2290                 /* Length check */
2291                 if (strlen (hashed) < 8 || strlen (hashed) > 63) {
2292                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2293                                      "Invalid WPA_PSK (passphrases must be between "
2294                                      "8 and 63 characters long (inclusive))");
2295                         g_free (hashed);
2296                         hashed = NULL;
2297                         goto out;
2298                 }
2299         }
2300
2301         if (!hashed) {
2302                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2303                              "Invalid WPA_PSK (doesn't look like a passphrase or hex key)");
2304                 goto out;
2305         }
2306
2307 out:
2308         g_free (psk);
2309         return hashed;
2310 }
2311
2312 static gboolean
2313 eap_simple_reader (const char *eap_method,
2314                    shvarFile *ifcfg,
2315                    shvarFile *keys,
2316                    NMSetting8021x *s_8021x,
2317                    gboolean phase2,
2318                    GError **error)
2319 {
2320         NMSettingSecretFlags flags;
2321         char *value;
2322
2323         value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE);
2324         if (!value) {
2325                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2326                              "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
2327                              eap_method);
2328                 return FALSE;
2329         }
2330         g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
2331         g_free (value);
2332
2333         flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
2334         g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL);
2335
2336         /* Only read the password if it's system-owned */
2337         if (flags == NM_SETTING_SECRET_FLAG_NONE) {
2338                 value = svGetValue (ifcfg, "IEEE_8021X_PASSWORD", FALSE);
2339                 if (!value && keys) {
2340                         /* Try the lookaside keys file */
2341                         value = svGetValue (keys, "IEEE_8021X_PASSWORD", FALSE);
2342                 }
2343
2344                 if (!value) {
2345                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2346                                      "Missing IEEE_8021X_PASSWORD for EAP method '%s'.",
2347                                      eap_method);
2348                         return FALSE;
2349                 }
2350
2351                 g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL);
2352                 g_free (value);
2353         }
2354
2355         return TRUE;
2356 }
2357
2358 static char *
2359 get_full_file_path (const char *ifcfg_path, const char *file_path)
2360 {
2361         const char *base = file_path;
2362         char *p, *ret, *dirname;
2363
2364         g_return_val_if_fail (ifcfg_path != NULL, NULL);
2365         g_return_val_if_fail (file_path != NULL, NULL);
2366
2367         if (file_path[0] == '/')
2368                 return g_strdup (file_path);
2369
2370         p = strrchr (file_path, '/');
2371         if (p)
2372                 base = p + 1;
2373
2374         dirname = g_path_get_dirname (ifcfg_path);
2375         ret = g_build_path ("/", dirname, base, NULL);
2376         g_free (dirname);
2377         return ret;
2378 }
2379
2380 static gboolean
2381 eap_tls_reader (const char *eap_method,
2382                 shvarFile *ifcfg,
2383                 shvarFile *keys,
2384                 NMSetting8021x *s_8021x,
2385                 gboolean phase2,
2386                 GError **error)
2387 {
2388         char *value;
2389         char *ca_cert = NULL;
2390         char *real_path = NULL;
2391         char *client_cert = NULL;
2392         char *privkey = NULL;
2393         char *privkey_password = NULL;
2394         gboolean success = FALSE;
2395         NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
2396         const char *ca_cert_key = phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT";
2397         const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
2398         const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
2399         const char *cli_cert_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT";
2400         const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS": "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
2401         const char *pk_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS;
2402         NMSettingSecretFlags flags;
2403
2404         value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE);
2405         if (!value) {
2406                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2407                              "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
2408                              eap_method);
2409                 return FALSE;
2410         }
2411         g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
2412         g_free (value);
2413
2414         ca_cert = svGetValue (ifcfg, ca_cert_key, FALSE);
2415         if (ca_cert) {
2416                 real_path = get_full_file_path (ifcfg->fileName, ca_cert);
2417                 if (phase2) {
2418                         if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
2419                                                                    real_path,
2420                                                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
2421                                                                    NULL,
2422                                                                    error))
2423                                 goto done;
2424                 } else {
2425                         if (!nm_setting_802_1x_set_ca_cert (s_8021x,
2426                                                             real_path,
2427                                                             NM_SETTING_802_1X_CK_SCHEME_PATH,
2428                                                             NULL,
2429                                                             error))
2430                                 goto done;
2431                 }
2432                 g_free (real_path);
2433                 real_path = NULL;
2434         } else {
2435                 PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!",
2436                                ca_cert_key, eap_method);
2437         }
2438
2439         /* Read and set private key password flags */
2440         flags = read_secret_flags (ifcfg, pk_pw_flags_key);
2441         g_object_set (s_8021x, pk_pw_flags_prop, flags, NULL);
2442
2443         /* Read the private key password if it's system-owned */
2444         if (flags == NM_SETTING_SECRET_FLAG_NONE) {
2445                 /* Private key password */
2446                 privkey_password = svGetValue (ifcfg, pk_pw_key, FALSE);
2447                 if (!privkey_password && keys) {
2448                         /* Try the lookaside keys file */
2449                         privkey_password = svGetValue (keys, pk_pw_key, FALSE);
2450                 }
2451
2452                 if (!privkey_password) {
2453                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2454                                      "Missing %s for EAP method '%s'.",
2455                                      pk_pw_key,
2456                                      eap_method);
2457                         goto done;
2458                 }
2459         }
2460
2461         /* The private key itself */
2462         privkey = svGetValue (ifcfg, pk_key, FALSE);
2463         if (!privkey) {
2464                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2465                              "Missing %s for EAP method '%s'.",
2466                              pk_key,
2467                              eap_method);
2468                 goto done;
2469         }
2470
2471         real_path = get_full_file_path (ifcfg->fileName, privkey);
2472         if (phase2) {
2473                 if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
2474                                                                real_path,
2475                                                                privkey_password,
2476                                                                NM_SETTING_802_1X_CK_SCHEME_PATH,
2477                                                                &privkey_format,
2478                                                                error))
2479                         goto done;
2480         } else {
2481                 if (!nm_setting_802_1x_set_private_key (s_8021x,
2482                                                         real_path,
2483                                                         privkey_password,
2484                                                         NM_SETTING_802_1X_CK_SCHEME_PATH,
2485                                                         &privkey_format,
2486                                                         error))
2487                         goto done;
2488         }
2489         g_free (real_path);
2490         real_path = NULL;
2491
2492         /* Only set the client certificate if the private key is not PKCS#12 format,
2493          * as NM (due to supplicant restrictions) requires.  If the key was PKCS#12,
2494          * then nm_setting_802_1x_set_private_key() already set the client certificate
2495          * to the same value as the private key.
2496          */
2497         if (   privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
2498             || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) {
2499                 client_cert = svGetValue (ifcfg, cli_cert_key, FALSE);
2500                 if (!client_cert) {
2501                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2502                                      "Missing %s for EAP method '%s'.",
2503                                      cli_cert_key,
2504                                      eap_method);
2505                         goto done;
2506                 }
2507
2508                 real_path = get_full_file_path (ifcfg->fileName, client_cert);
2509                 if (phase2) {
2510                         if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x,
2511                                                                        real_path,
2512                                                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
2513                                                                        NULL,
2514                                                                        error))
2515                                 goto done;
2516                 } else {
2517                         if (!nm_setting_802_1x_set_client_cert (s_8021x,
2518                                                                 real_path,
2519                                                                 NM_SETTING_802_1X_CK_SCHEME_PATH,
2520                                                                 NULL,
2521                                                                 error))
2522                                 goto done;
2523                 }
2524                 g_free (real_path);
2525                 real_path = NULL;
2526         }
2527
2528         success = TRUE;
2529
2530 done:
2531         g_free (real_path);
2532         g_free (ca_cert);
2533         g_free (client_cert);
2534         g_free (privkey);
2535         g_free (privkey_password);
2536         return success;
2537 }
2538
2539 static gboolean
2540 eap_peap_reader (const char *eap_method,
2541                  shvarFile *ifcfg,
2542                  shvarFile *keys,
2543                  NMSetting8021x *s_8021x,
2544                  gboolean phase2,
2545                  GError **error)
2546 {
2547         char *anon_ident = NULL;
2548         char *ca_cert = NULL;
2549         char *real_cert_path = NULL;
2550         char *inner_auth = NULL;
2551         char *peapver = NULL;
2552         char *lower;
2553         char **list = NULL, **iter;
2554         gboolean success = FALSE;
2555
2556         ca_cert = svGetValue (ifcfg, "IEEE_8021X_CA_CERT", FALSE);
2557         if (ca_cert) {
2558                 real_cert_path = get_full_file_path (ifcfg->fileName, ca_cert);
2559                 if (!nm_setting_802_1x_set_ca_cert (s_8021x,
2560                                                     real_cert_path,
2561                                                     NM_SETTING_802_1X_CK_SCHEME_PATH,
2562                                                     NULL,
2563                                                     error))
2564                         goto done;
2565         } else {
2566                 PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
2567                                eap_method);
2568         }
2569
2570         peapver = svGetValue (ifcfg, "IEEE_8021X_PEAP_VERSION", FALSE);
2571         if (peapver) {
2572                 if (!strcmp (peapver, "0"))
2573                         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL);
2574                 else if (!strcmp (peapver, "1"))
2575                         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL);
2576                 else {
2577                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2578                                      "Unknown IEEE_8021X_PEAP_VERSION value '%s'",
2579                                      peapver);
2580                         goto done;
2581                 }
2582         }
2583
2584         if (svTrueValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE))
2585                 g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
2586
2587         anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE);
2588         if (anon_ident && strlen (anon_ident))
2589                 g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
2590
2591         inner_auth = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE);
2592         if (!inner_auth) {
2593                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2594                              "Missing IEEE_8021X_INNER_AUTH_METHODS.");
2595                 goto done;
2596         }
2597
2598         /* Handle options for the inner auth method */
2599         list = g_strsplit (inner_auth, " ", 0);
2600         for (iter = list; iter && *iter; iter++) {
2601                 if (!strlen (*iter))
2602                         continue;
2603
2604                 if (   !strcmp (*iter, "MSCHAPV2")
2605                     || !strcmp (*iter, "MD5")
2606                     || !strcmp (*iter, "GTC")) {
2607                         if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2608                                 goto done;
2609                 } else if (!strcmp (*iter, "TLS")) {
2610                         if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2611                                 goto done;
2612                 } else {
2613                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2614                                      "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
2615                                      *iter);
2616                         goto done;
2617                 }
2618
2619                 lower = g_ascii_strdown (*iter, -1);
2620                 g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
2621                 g_free (lower);
2622                 break;
2623         }
2624
2625         if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
2626                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2627                              "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
2628                 goto done;
2629         }
2630
2631         success = TRUE;
2632
2633 done:
2634         if (list)
2635                 g_strfreev (list);
2636         g_free (inner_auth);
2637         g_free (peapver);
2638         g_free (real_cert_path);
2639         g_free (ca_cert);
2640         g_free (anon_ident);
2641         return success;
2642 }
2643
2644 static gboolean
2645 eap_ttls_reader (const char *eap_method,
2646                  shvarFile *ifcfg,
2647                  shvarFile *keys,
2648                  NMSetting8021x *s_8021x,
2649                  gboolean phase2,
2650                  GError **error)
2651 {
2652         gboolean success = FALSE;
2653         char *anon_ident = NULL;
2654         char *ca_cert = NULL;
2655         char *real_cert_path = NULL;
2656         char *inner_auth = NULL;
2657         char *tmp;
2658         char **list = NULL, **iter;
2659
2660         ca_cert = svGetValue (ifcfg, "IEEE_8021X_CA_CERT", FALSE);
2661         if (ca_cert) {
2662                 real_cert_path = get_full_file_path (ifcfg->fileName, ca_cert);
2663                 if (!nm_setting_802_1x_set_ca_cert (s_8021x,
2664                                                     real_cert_path,
2665                                                     NM_SETTING_802_1X_CK_SCHEME_PATH,
2666                                                     NULL,
2667                                                     error))
2668                         goto done;
2669         } else {
2670                 PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
2671                                eap_method);
2672         }
2673
2674         anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE);
2675         if (anon_ident && strlen (anon_ident))
2676                 g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
2677
2678         tmp = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE);
2679         if (!tmp) {
2680                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2681                              "Missing IEEE_8021X_INNER_AUTH_METHODS.");
2682                 goto done;
2683         }
2684
2685         inner_auth = g_ascii_strdown (tmp, -1);
2686         g_free (tmp);
2687
2688         /* Handle options for the inner auth method */
2689         list = g_strsplit (inner_auth, " ", 0);
2690         for (iter = list; iter && *iter; iter++) {
2691                 if (!strlen (*iter))
2692                         continue;
2693
2694                 if (   !strcmp (*iter, "mschapv2")
2695                     || !strcmp (*iter, "mschap")
2696                     || !strcmp (*iter, "pap")
2697                     || !strcmp (*iter, "chap")) {
2698                         if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2699                                 goto done;
2700                         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
2701                 } else if (!strcmp (*iter, "eap-tls")) {
2702                         if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2703                                 goto done;
2704                         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
2705                 } else if (   !strcmp (*iter, "eap-mschapv2")
2706                            || !strcmp (*iter, "eap-md5")
2707                            || !strcmp (*iter, "eap-gtc")) {
2708                         if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2709                                 goto done;
2710                         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + STRLEN ("eap-")), NULL);
2711                 } else {
2712                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2713                                      "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
2714                                      *iter);
2715                         goto done;
2716                 }
2717                 break;
2718         }
2719
2720         success = TRUE;
2721
2722 done:
2723         if (list)
2724                 g_strfreev (list);
2725         g_free (inner_auth);
2726         g_free (real_cert_path);
2727         g_free (ca_cert);
2728         g_free (anon_ident);
2729         return success;
2730 }
2731
2732 static gboolean
2733 eap_fast_reader (const char *eap_method,
2734                  shvarFile *ifcfg,
2735                  shvarFile *keys,
2736                  NMSetting8021x *s_8021x,
2737                  gboolean phase2,
2738                  GError **error)
2739 {
2740         char *anon_ident = NULL;
2741         char *pac_file = NULL;
2742         char *real_pac_path = NULL;
2743         char *inner_auth = NULL;
2744         char *fast_provisioning = NULL;
2745         char *lower;
2746         char **list = NULL, **iter;
2747         const char* pac_prov_str;
2748         gboolean allow_unauth = FALSE, allow_auth = FALSE;
2749         gboolean success = FALSE;
2750
2751         pac_file = svGetValue (ifcfg, "IEEE_8021X_PAC_FILE", FALSE);
2752         if (pac_file) {
2753                 real_pac_path = get_full_file_path (ifcfg->fileName, pac_file);
2754                 g_object_set (s_8021x, NM_SETTING_802_1X_PAC_FILE, real_pac_path, NULL);
2755         }
2756
2757         fast_provisioning = svGetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING", FALSE);
2758         if (fast_provisioning) {
2759                 list = g_strsplit_set (fast_provisioning, " \t", 0);
2760                 for (iter = list; iter && *iter; iter++) {
2761                         if (**iter == '\0')
2762                                 continue;
2763                         if (strcmp (*iter, "allow-unauth") == 0)
2764                                 allow_unauth = TRUE;
2765                         else if (strcmp (*iter, "allow-auth") == 0)
2766                                 allow_auth = TRUE;
2767                         else {
2768                                 PARSE_WARNING ("invalid IEEE_8021X_FAST_PROVISIONING '%s' "
2769                                                "(space-separated list of these values [allow-auth, allow-unauth] expected)",
2770                                                *iter);
2771                         }
2772                 }
2773                 g_strfreev (list);
2774                 list = NULL;
2775         }
2776         pac_prov_str = allow_unauth ? (allow_auth ? "3" : "1") : (allow_auth ? "2" : "0");
2777         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, pac_prov_str, NULL);
2778
2779         if (!pac_file && !(allow_unauth || allow_auth)) {
2780                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2781                              "IEEE_8021X_PAC_FILE not provided and EAP-FAST automatic PAC provisioning disabled.");
2782                 goto done;
2783         }
2784
2785         anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE);
2786         if (anon_ident && strlen (anon_ident))
2787                 g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
2788
2789         inner_auth = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE);
2790         if (!inner_auth) {
2791                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2792                              "Missing IEEE_8021X_INNER_AUTH_METHODS.");
2793                 goto done;
2794         }
2795
2796         /* Handle options for the inner auth method */
2797         list = g_strsplit (inner_auth, " ", 0);
2798         for (iter = list; iter && *iter; iter++) {
2799                 if (!strlen (*iter))
2800                         continue;
2801
2802                 if (   !strcmp (*iter, "MSCHAPV2")
2803                     || !strcmp (*iter, "GTC")) {
2804                         if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
2805                                 goto done;
2806                 } else {
2807                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2808                                      "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
2809                                      *iter);
2810                         goto done;
2811                 }
2812
2813                 lower = g_ascii_strdown (*iter, -1);
2814                 g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
2815                 g_free (lower);
2816                 break;
2817         }
2818
2819         if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
2820                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2821                              "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
2822                 goto done;
2823         }
2824
2825         success = TRUE;
2826
2827 done:
2828         g_strfreev (list);
2829         g_free (inner_auth);
2830         g_free (fast_provisioning);
2831         g_free (real_pac_path);
2832         g_free (pac_file);
2833         g_free (anon_ident);
2834         return success;
2835 }
2836
2837 typedef struct {
2838         const char *method;
2839         gboolean (*reader)(const char *eap_method,
2840                            shvarFile *ifcfg,
2841                            shvarFile *keys,
2842                            NMSetting8021x *s_8021x,
2843                            gboolean phase2,
2844                            GError **error);
2845         gboolean wifi_phase2_only;
2846 } EAPReader;
2847
2848 static EAPReader eap_readers[] = {
2849         { "md5", eap_simple_reader, TRUE },
2850         { "pap", eap_simple_reader, TRUE },
2851         { "chap", eap_simple_reader, TRUE },
2852         { "mschap", eap_simple_reader, TRUE },
2853         { "mschapv2", eap_simple_reader, TRUE },
2854         { "leap", eap_simple_reader, FALSE },
2855         { "pwd", eap_simple_reader, FALSE },
2856         { "tls", eap_tls_reader, FALSE },
2857         { "peap", eap_peap_reader, FALSE },
2858         { "ttls", eap_ttls_reader, FALSE },
2859         { "fast", eap_fast_reader, FALSE },
2860         { NULL, NULL }
2861 };
2862
2863 static void
2864 read_8021x_list_value (shvarFile *ifcfg,
2865                        const char *ifcfg_var_name,
2866                        NMSetting8021x *setting,
2867                        const char *prop_name)
2868 {
2869         char *value;
2870         char **strv;
2871
2872         g_return_if_fail (ifcfg != NULL);
2873         g_return_if_fail (ifcfg_var_name != NULL);
2874         g_return_if_fail (prop_name != NULL);
2875
2876         value = svGetValue (ifcfg, ifcfg_var_name, FALSE);
2877         if (!value)
2878                 return;
2879
2880         strv = g_strsplit_set (value, " \t", 0);
2881         if (strv && strv[0])
2882                 g_object_set (setting, prop_name, strv, NULL);
2883         g_strfreev (strv);
2884         g_free (value);
2885 }
2886
2887 static NMSetting8021x *
2888 fill_8021x (shvarFile *ifcfg,
2889             const char *file,
2890             const char *key_mgmt,
2891             gboolean wifi,
2892             GError **error)
2893 {
2894         NMSetting8021x *s_8021x;
2895         shvarFile *keys = NULL;
2896         char *value;
2897         char **list = NULL, **iter;
2898
2899         value = svGetValue (ifcfg, "IEEE_8021X_EAP_METHODS", FALSE);
2900         if (!value) {
2901                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2902                              "Missing IEEE_8021X_EAP_METHODS for key management '%s'",
2903                              key_mgmt);
2904                 return NULL;
2905         }
2906
2907         list = g_strsplit (value, " ", 0);
2908         g_free (value);
2909
2910         s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
2911
2912         /* Read in the lookaside keys file, if present */
2913         keys = utils_get_keys_ifcfg (file, FALSE);
2914
2915         /* Validate and handle each EAP method */
2916         for (iter = list; iter && *iter; iter++) {
2917                 EAPReader *eap = &eap_readers[0];
2918                 gboolean found = FALSE;
2919                 char *lower = NULL;
2920
2921                 lower = g_ascii_strdown (*iter, -1);
2922                 while (eap->method) {
2923                         if (strcmp (eap->method, lower))
2924                                 goto next;
2925
2926                         /* Some EAP methods don't provide keying material, thus they
2927                          * cannot be used with WiFi unless they are an inner method
2928                          * used with TTLS or PEAP or whatever.
2929                          */
2930                         if (wifi && eap->wifi_phase2_only) {
2931                                 PARSE_WARNING ("ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.",
2932                                                lower);
2933                                 goto next;
2934                         }
2935
2936                         /* Parse EAP method specific options */
2937                         if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) {
2938                                 g_free (lower);
2939                                 goto error;
2940                         }
2941                         nm_setting_802_1x_add_eap_method (s_8021x, lower);
2942                         found = TRUE;
2943                         break;
2944
2945                 next:
2946                         eap++;
2947                 }
2948
2949                 if (!found)
2950                         PARSE_WARNING ("ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower);
2951                 g_free (lower);
2952         }
2953
2954         if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) {
2955                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2956                              "No valid EAP methods found in IEEE_8021X_EAP_METHODS.");
2957                 goto error;
2958         }
2959
2960         value = svGetValue (ifcfg, "IEEE_8021X_SUBJECT_MATCH", FALSE);
2961         g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, value, NULL);
2962         g_free (value);
2963
2964         value = svGetValue (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH", FALSE);
2965         g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, value, NULL);
2966         g_free (value);
2967
2968         read_8021x_list_value (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES",
2969                                s_8021x, NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
2970         read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES",
2971                                s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
2972
2973         if (list)
2974                 g_strfreev (list);
2975         if (keys)
2976                 svCloseFile (keys);
2977         return s_8021x;
2978
2979 error:
2980         if (list)
2981                 g_strfreev (list);
2982         if (keys)
2983                 svCloseFile (keys);
2984         g_object_unref (s_8021x);
2985         return NULL;
2986 }
2987
2988 static NMSetting *
2989 make_wpa_setting (shvarFile *ifcfg,
2990                   const char *file,
2991                   GBytes *ssid,
2992                   gboolean adhoc,
2993                   NMSetting8021x **s_8021x,
2994                   GError **error)
2995 {
2996         NMSettingWirelessSecurity *wsec;
2997         char *value, *psk, *lower;
2998         gboolean wpa_psk = FALSE, wpa_eap = FALSE, ieee8021x = FALSE;
2999
3000         wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
3001
3002         value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
3003         wpa_psk = !g_strcmp0 (value, "WPA-PSK");
3004         wpa_eap = !g_strcmp0 (value, "WPA-EAP");
3005         ieee8021x = !g_strcmp0 (value, "IEEE8021X");
3006         if (!wpa_psk && !wpa_eap && !ieee8021x)
3007                 goto error; /* Not WPA or Dynamic WEP */
3008
3009         /* Pairwise and Group ciphers (only relevant for WPA/RSN) */
3010         if (wpa_psk || wpa_eap) {
3011                 fill_wpa_ciphers (ifcfg, wsec, FALSE, adhoc);
3012                 fill_wpa_ciphers (ifcfg, wsec, TRUE, adhoc);
3013         }
3014
3015         /* WPA and/or RSN */
3016         if (adhoc) {
3017                 /* Ad-Hoc mode only supports WPA proto for now */
3018                 nm_setting_wireless_security_add_proto (wsec, "wpa");
3019         } else {
3020                 char *allow_wpa, *allow_rsn;
3021
3022                 allow_wpa = svGetValue (ifcfg, "WPA_ALLOW_WPA", FALSE);
3023                 allow_rsn = svGetValue (ifcfg, "WPA_ALLOW_WPA2", FALSE);
3024
3025                 if (allow_wpa && svTrueValue (ifcfg, "WPA_ALLOW_WPA", TRUE))
3026                         nm_setting_wireless_security_add_proto (wsec, "wpa");
3027                 if (allow_rsn && svTrueValue (ifcfg, "WPA_ALLOW_WPA2", TRUE))
3028                         nm_setting_wireless_security_add_proto (wsec, "rsn");
3029
3030                 /* If neither WPA_ALLOW_WPA or WPA_ALLOW_WPA2 were present, default
3031                  * to both WPA and RSN allowed.
3032                  */
3033                 if (!allow_wpa && !allow_rsn && !ieee8021x) {
3034                         nm_setting_wireless_security_add_proto (wsec, "wpa");
3035                         nm_setting_wireless_security_add_proto (wsec, "rsn");
3036                 }
3037
3038                 g_free (allow_wpa);
3039                 g_free (allow_rsn);
3040         }
3041
3042         /* coverity[dereference] */
3043         if (!strcmp (value, "WPA-PSK")) {
3044                 NMSettingSecretFlags psk_flags;
3045
3046                 psk_flags = read_secret_flags (ifcfg, "WPA_PSK_FLAGS");
3047                 g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, psk_flags, NULL);
3048
3049                 /* Read PSK if it's system-owned */
3050                 if (psk_flags == NM_SETTING_SECRET_FLAG_NONE) {
3051                         psk = parse_wpa_psk (ifcfg, file, ssid, error);
3052                         if (psk) {
3053                                 g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL);
3054                                 g_free (psk);
3055                         }
3056                 }
3057
3058                 if (adhoc)
3059                         g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL);
3060                 else
3061                         g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NULL);
3062         } else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) {
3063                 /* Adhoc mode is mutually exclusive with any 802.1x-based authentication */
3064                 if (adhoc) {
3065                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3066                                      "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", value);
3067                         goto error;
3068                 }
3069
3070                 *s_8021x = fill_8021x (ifcfg, file, value, TRUE, error);
3071                 if (!*s_8021x)
3072                         goto error;
3073
3074                 lower = g_ascii_strdown (value, -1);
3075                 g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL);
3076                 g_free (lower);
3077         } else {
3078                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3079                              "Unknown wireless KEY_MGMT type '%s'", value);
3080                 goto error;
3081         }
3082
3083         g_free (value);
3084         return (NMSetting *) wsec;
3085
3086 error:
3087         g_free (value);
3088         if (wsec)
3089                 g_object_unref (wsec);
3090         return NULL;
3091 }
3092
3093 static NMSetting *
3094 make_leap_setting (shvarFile *ifcfg,
3095                    const char *file,
3096                    GError **error)
3097 {
3098         NMSettingWirelessSecurity *wsec;
3099         shvarFile *keys_ifcfg;
3100         char *value;
3101         NMSettingSecretFlags flags;
3102
3103         wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
3104
3105         value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
3106         if (!value || strcmp (value, "IEEE8021X"))
3107                 goto error; /* Not LEAP */
3108
3109         g_free (value);
3110         value = svGetValue (ifcfg, "SECURITYMODE", FALSE);
3111         if (!value || strcasecmp (value, "leap"))
3112                 goto error; /* Not LEAP */
3113
3114         g_free (value);
3115
3116         flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
3117         g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL);
3118
3119         /* Read LEAP password if it's system-owned */
3120         if (flags == NM_SETTING_SECRET_FLAG_NONE) {
3121                 value = svGetValue (ifcfg, "IEEE_8021X_PASSWORD", FALSE);
3122                 if (!value) {
3123                         /* Try to get keys from the "shadow" key file */
3124                         keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
3125                         if (keys_ifcfg) {
3126                                 value = svGetValue (keys_ifcfg, "IEEE_8021X_PASSWORD", FALSE);
3127                                 svCloseFile (keys_ifcfg);
3128                         }
3129                 }
3130                 if (value && strlen (value))
3131                         g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL);
3132                 g_free (value);
3133         }
3134
3135         value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE);
3136         if (!value || !strlen (value)) {
3137                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3138                              "Missing LEAP identity");
3139                 goto error;
3140         }
3141         g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL);
3142         g_free (value);
3143
3144         g_object_set (wsec,
3145                       NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
3146                       NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap",
3147                       NULL);
3148
3149         return (NMSetting *) wsec;
3150
3151 error:
3152         g_free (value);
3153         if (wsec)
3154                 g_object_unref (wsec);
3155         return NULL;
3156 }
3157
3158 static NMSetting *
3159 make_wireless_security_setting (shvarFile *ifcfg,
3160                                 const char *file,
3161                                 GBytes *ssid,
3162                                 gboolean adhoc,
3163                                 NMSetting8021x **s_8021x,
3164                                 GError **error)
3165 {
3166         NMSetting *wsec;
3167
3168         g_return_val_if_fail (error && !*error, NULL);
3169
3170         if (!adhoc) {
3171                 wsec = make_leap_setting (ifcfg, file, error);
3172                 if (wsec)
3173                         return wsec;
3174                 else if (*error)
3175                         return NULL;
3176         }
3177
3178         wsec = make_wpa_setting (ifcfg, file, ssid, adhoc, s_8021x, error);
3179         if (wsec)
3180                 return wsec;
3181         else if (*error)
3182                 return NULL;
3183
3184         wsec = make_wep_setting (ifcfg, file, error);
3185         if (wsec)
3186                 return wsec;
3187         else if (*error)
3188                 return NULL;
3189
3190         return NULL; /* unencrypted */
3191 }
3192
3193 static char **
3194 transform_hwaddr_blacklist (const char *blacklist)
3195 {
3196         char **strv, **iter;
3197         int shift = 0;
3198
3199         strv = _nm_utils_strsplit_set (blacklist, " \t", 0);
3200         for (iter = strv; iter && *iter; iter++) {
3201                 if (shift) {
3202                         *(iter - shift) = *iter;
3203                         *iter = NULL;
3204                 }
3205                 if (!nm_utils_hwaddr_valid (*(iter - shift), ETH_ALEN)) {
3206                         PARSE_WARNING ("invalid MAC in HWADDR_BLACKLIST '%s'", *(iter - shift));
3207                         g_free (*(iter - shift));
3208                         *(iter - shift) = NULL;
3209                         shift++;
3210                 }
3211         }
3212         return strv;
3213 }
3214
3215 static NMSetting *
3216 make_wireless_setting (shvarFile *ifcfg,
3217                        GError **error)
3218 {
3219         NMSettingWireless *s_wireless;
3220         char *value = NULL;
3221         gint64 chan = 0;
3222
3223         s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
3224
3225         value = svGetValue (ifcfg, "HWADDR", FALSE);
3226         if (value) {
3227                 value = g_strstrip (value);
3228                 g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, value, NULL);
3229                 g_free (value);
3230         }
3231
3232         value = svGetValue (ifcfg, "MACADDR", FALSE);
3233         if (value) {
3234                 value = g_strstrip (value);
3235                 g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, value, NULL);
3236                 g_free (value);
3237         }
3238
3239         value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
3240         if (value) {
3241                 char **strv;
3242
3243                 strv = transform_hwaddr_blacklist (value);
3244                 g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, strv, NULL);
3245                 g_strfreev (strv);
3246                 g_free (value);
3247         }
3248
3249         value = svGetValue (ifcfg, "ESSID", TRUE);
3250         if (value) {
3251                 GBytes *bytes = NULL;
3252                 gsize ssid_len = 0;
3253                 gsize value_len = strlen (value);
3254
3255                 if (   (value_len >= 2)
3256                     && (value[0] == '"')
3257                     && (value[value_len - 1] == '"')) {
3258                         /* Strip the quotes and unescape */
3259                         char *p = value + 1;
3260
3261                         value[value_len - 1] = '\0';
3262                         svUnescape (p);
3263                         bytes = g_bytes_new (p, strlen (p));
3264                 } else if ((value_len > 2) && (strncmp (value, "0x", 2) == 0)) {
3265                         /* Hex representation */
3266                         if (value_len % 2) {
3267                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3268                                              "Invalid SSID '%s' size (looks like hex but length not multiple of 2)",
3269                                              value);
3270                                 g_free (value);
3271                                 goto error;
3272                         }
3273
3274                         bytes = nm_utils_hexstr2bin (value);
3275                         if (!bytes) {
3276                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3277                                              "Invalid SSID '%s' (looks like hex SSID but isn't)",
3278                                              value);
3279                                 g_free (value);
3280                                 goto error;
3281                         }
3282                 } else
3283                         bytes = g_bytes_new (value, value_len);
3284
3285                 ssid_len = g_bytes_get_size (bytes);
3286                 if (ssid_len > 32 || ssid_len == 0) {
3287                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3288                                      "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
3289                                      value, ssid_len);
3290                         g_bytes_unref (bytes);
3291                         g_free (value);
3292                         goto error;
3293                 }
3294
3295                 g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, bytes, NULL);
3296                 g_bytes_unref (bytes);
3297                 g_free (value);
3298         }
3299
3300         value = svGetValue (ifcfg, "MODE", FALSE);
3301         if (value) {
3302                 char *lcase;
3303                 const char *mode = NULL;
3304
3305                 lcase = g_ascii_strdown (value, -1);
3306                 g_free (value);
3307
3308                 if (!strcmp (lcase, "ad-hoc")) {
3309                         mode = "adhoc";
3310                 } else if (!strcmp (lcase, "ap")) {
3311                         mode = "ap";
3312                 } else if (!strcmp (lcase, "managed") || !strcmp (lcase, "auto")) {
3313                         mode = "infrastructure";
3314                 } else {
3315                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3316                                      "Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')",
3317                                      lcase);
3318                         g_free (lcase);
3319                         goto error;
3320                 }
3321                 g_free (lcase);
3322
3323                 g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL);
3324         }
3325
3326         value = svGetValue (ifcfg, "BSSID", FALSE);
3327         if (value) {
3328                 value = g_strstrip (value);
3329                 g_object_set (s_wireless, NM_SETTING_WIRELESS_BSSID, value, NULL);
3330                 g_free (value);
3331         }
3332
3333         value = svGetValue (ifcfg, "CHANNEL", FALSE);
3334         if (value) {
3335                 errno = 0;
3336                 chan = nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
3337                 if (errno || (chan == 0)) {
3338                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3339                                      "Invalid wireless channel '%s'", value);
3340                         g_free (value);
3341                         goto error;
3342                 }
3343                 g_object_set (s_wireless, NM_SETTING_WIRELESS_CHANNEL, (guint32) chan, NULL);
3344                 g_free (value);
3345         }
3346
3347         value = svGetValue (ifcfg, "BAND", FALSE);
3348         if (value) {
3349                 if (!strcmp (value, "a")) {
3350                         if (chan && chan <= 14) {
3351                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3352                                              "Band '%s' invalid for channel %u", value, (guint32) chan);
3353                                 g_free (value);
3354                                 goto error;
3355                         }
3356                 } else if (!strcmp (value, "bg")) {
3357                         if (chan && chan > 14) {
3358                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3359                                              "Band '%s' invalid for channel %u", value, (guint32) chan);
3360                                 g_free (value);
3361                                 goto error;
3362                         }
3363                 } else {
3364                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3365                                      "Invalid wireless band '%s'", value);
3366                         g_free (value);
3367                         goto error;
3368                 }
3369                 g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, value, NULL);
3370                 g_free (value);
3371         } else if (chan > 0) {
3372                 if (chan > 14)
3373                         g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "a", NULL);
3374                 else
3375                         g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL);
3376         }
3377
3378         value = svGetValue (ifcfg, "MTU", FALSE);
3379         if (value) {
3380                 long int mtu;
3381
3382                 errno = 0;
3383                 mtu = strtol (value, NULL, 10);
3384                 if (errno || mtu < 0 || mtu > 50000) {
3385                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3386                                      "Invalid wireless MTU '%s'", value);
3387                         g_free (value);
3388                         goto error;
3389                 }
3390                 g_object_set (s_wireless, NM_SETTING_WIRELESS_MTU, (guint32) mtu, NULL);
3391                 g_free (value);
3392         }
3393
3394         g_object_set (s_wireless,
3395                       NM_SETTING_WIRELESS_HIDDEN,
3396                       svTrueValue (ifcfg, "SSID_HIDDEN", FALSE),
3397                       NULL);
3398
3399         return NM_SETTING (s_wireless);
3400
3401 error:
3402         if (s_wireless)
3403                 g_object_unref (s_wireless);
3404         return NULL;
3405 }
3406
3407 static NMConnection *
3408 wireless_connection_from_ifcfg (const char *file,
3409                                 shvarFile *ifcfg,
3410                                 GError **error)
3411 {
3412         NMConnection *connection = NULL;
3413         NMSetting *con_setting = NULL;
3414         NMSetting *wireless_setting = NULL;
3415         NMSetting8021x *s_8021x = NULL;
3416         GBytes *ssid;
3417         NMSetting *security_setting = NULL;
3418         char *printable_ssid = NULL;
3419         const char *mode;
3420         gboolean adhoc = FALSE;
3421         GError *local = NULL;
3422
3423         g_return_val_if_fail (file != NULL, NULL);
3424         g_return_val_if_fail (ifcfg != NULL, NULL);
3425         g_return_val_if_fail (!error || !*error, NULL);
3426
3427         connection = nm_simple_connection_new ();
3428
3429         /* Wireless */
3430         wireless_setting = make_wireless_setting (ifcfg, error);
3431         if (!wireless_setting) {
3432                 g_object_unref (connection);
3433                 return NULL;
3434         }
3435         nm_connection_add_setting (connection, wireless_setting);
3436
3437         ssid = nm_setting_wireless_get_ssid (NM_SETTING_WIRELESS (wireless_setting));
3438         if (ssid) {
3439                 printable_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
3440                                                         g_bytes_get_size (ssid));
3441         } else
3442                 printable_ssid = g_strdup_printf ("unmanaged");
3443
3444         mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (wireless_setting));
3445         if (mode && !strcmp (mode, "adhoc"))
3446                 adhoc = TRUE;
3447
3448         /* Wireless security */
3449         security_setting = make_wireless_security_setting (ifcfg, file, ssid, adhoc, &s_8021x, &local);
3450         if (local) {
3451                 g_free (printable_ssid);
3452                 g_object_unref (connection);
3453                 g_propagate_error (error, local);
3454                 return NULL;
3455         }
3456         if (security_setting) {
3457                 nm_connection_add_setting (connection, security_setting);
3458                 if (s_8021x)
3459                         nm_connection_add_setting (connection, NM_SETTING (s_8021x));
3460         }
3461
3462         /* Connection */
3463         con_setting = make_connection_setting (file, ifcfg,
3464                                                NM_SETTING_WIRELESS_SETTING_NAME,
3465                                                printable_ssid, NULL);
3466         g_free (printable_ssid);
3467         if (!con_setting) {
3468                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3469                              "Failed to create connection setting.");
3470                 g_object_unref (connection);
3471                 return NULL;
3472         }
3473         nm_connection_add_setting (connection, con_setting);
3474
3475         return connection;
3476 }
3477
3478 static NMSetting *
3479 make_wired_setting (shvarFile *ifcfg,
3480                     const char *file,
3481                     NMSetting8021x **s_8021x,
3482                     GError **error)
3483 {
3484         NMSettingWired *s_wired;
3485         char *value = NULL;
3486         int mtu;
3487         char *nettype;
3488
3489         s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
3490
3491         value = svGetValue (ifcfg, "MTU", FALSE);
3492         if (value) {
3493                 if (get_int (value, &mtu)) {
3494                         if (mtu >= 0 && mtu < 65536)
3495                                 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu, NULL);
3496                 } else {
3497                         /* Shouldn't be fatal... */
3498                         PARSE_WARNING ("invalid MTU '%s'", value);
3499                 }
3500                 g_free (value);
3501         }
3502
3503         value = svGetValue (ifcfg, "HWADDR", FALSE);
3504         if (value) {
3505                 value = g_strstrip (value);
3506                 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL);
3507                 g_free (value);
3508         }
3509
3510         value = svGetValue (ifcfg, "SUBCHANNELS", FALSE);
3511         if (value) {
3512                 const char *p = value;
3513                 gboolean success = TRUE;
3514                 char **chans = NULL;
3515
3516                 /* basic sanity checks */
3517                 while (*p) {
3518                         if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) {
3519                                 PARSE_WARNING ("invalid SUBCHANNELS '%s'", value);
3520                                 success = FALSE;
3521                                 break;
3522                         }
3523                         p++;
3524                 }
3525
3526                 if (success) {
3527                         guint32 num_chans;
3528
3529                         chans = g_strsplit_set (value, ",", 0);
3530                         num_chans = g_strv_length (chans);
3531                         if (num_chans < 2 || num_chans > 3) {
3532                                 PARSE_WARNING ("invalid SUBCHANNELS '%s' (%d channels, 2 or 3 expected)",
3533                                                value, g_strv_length (chans));
3534                         } else
3535                                 g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL);
3536                         g_strfreev (chans);
3537                 }
3538                 g_free (value);
3539         }
3540
3541         value = svGetValue (ifcfg, "PORTNAME", FALSE);
3542         if (value && strlen (value)) {
3543                 nm_setting_wired_add_s390_option (s_wired, "portname", value);
3544         }
3545         g_free (value);
3546
3547         value = svGetValue (ifcfg, "CTCPROT", FALSE);
3548         if (value && strlen (value))
3549                 nm_setting_wired_add_s390_option (s_wired, "ctcprot", value);
3550         g_free (value);
3551
3552         nettype = svGetValue (ifcfg, "NETTYPE", FALSE);
3553         if (nettype && strlen (nettype)) {
3554                 if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc"))
3555                         g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL);
3556                 else
3557                         PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype);
3558         }
3559         g_free (nettype);
3560
3561         value = svGetValue (ifcfg, "OPTIONS", FALSE);
3562         if (value && strlen (value)) {
3563                 char **options, **iter;
3564
3565                 iter = options = g_strsplit_set (value, " ", 0);
3566                 while (iter && *iter) {
3567                         char *equals = strchr (*iter, '=');
3568                         gboolean valid = FALSE;
3569
3570                         if (equals) {
3571                                 *equals = '\0';
3572                                 valid = nm_setting_wired_add_s390_option (s_wired, *iter, equals + 1);
3573                         }
3574                         if (!valid)
3575                                 PARSE_WARNING ("invalid s390 OPTION '%s'", *iter);
3576                         iter++;
3577                 }
3578                 g_strfreev (options);
3579         }
3580         g_free (value);
3581
3582         value = svGetValue (ifcfg, "MACADDR", FALSE);
3583         if (value) {
3584                 value = g_strstrip (value);
3585                 g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, value, NULL);
3586                 g_free (value);
3587         }
3588
3589         value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
3590         if (value) {
3591                 char **strv;
3592
3593                 strv = transform_hwaddr_blacklist (value);
3594                 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL);
3595                 g_strfreev (strv);
3596                 g_free (value);
3597         }
3598
3599         value = svGetValue (ifcfg, "KEY_MGMT", FALSE);
3600         if (value) {
3601                 if (!strcmp (value, "IEEE8021X")) {
3602                         *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error);
3603                         if (!*s_8021x)
3604                                 goto error;
3605                 } else {
3606                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3607                                      "Unknown wired KEY_MGMT type '%s'", value);
3608                         goto error;
3609                 }
3610                 g_free (value);
3611         }
3612
3613         return (NMSetting *) s_wired;
3614
3615 error:
3616         g_free (value);
3617         g_object_unref (s_wired);
3618         return NULL;
3619 }
3620
3621 static NMConnection *
3622 wired_connection_from_ifcfg (const char *file,
3623                              shvarFile *ifcfg,
3624                              GError **error)
3625 {
3626         NMConnection *connection = NULL;
3627         NMSetting *con_setting = NULL;
3628         NMSetting *wired_setting = NULL;
3629         NMSetting8021x *s_8021x = NULL;
3630
3631         g_return_val_if_fail (file != NULL, NULL);
3632         g_return_val_if_fail (ifcfg != NULL, NULL);
3633
3634         connection = nm_simple_connection_new ();
3635
3636         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL, NULL);
3637         if (!con_setting) {
3638                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3639                              "Failed to create connection setting.");
3640                 g_object_unref (connection);
3641                 return NULL;
3642         }
3643         check_if_bond_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
3644         check_if_team_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
3645         nm_connection_add_setting (connection, con_setting);
3646
3647         wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error);
3648         if (!wired_setting) {
3649                 g_object_unref (connection);
3650                 return NULL;
3651         }
3652         nm_connection_add_setting (connection, wired_setting);
3653
3654         if (s_8021x)
3655                 nm_connection_add_setting (connection, NM_SETTING (s_8021x));
3656
3657         return connection;
3658 }
3659
3660 static gboolean
3661 parse_infiniband_p_key (shvarFile *ifcfg,
3662                         int *out_p_key,
3663                         char **out_parent,
3664                         GError **error)
3665 {
3666         char *device = NULL, *physdev = NULL, *pkey_id = NULL, *end;
3667         char *ifname = NULL;
3668         guint32 id = G_MAXUINT32;
3669         gboolean ret = FALSE;
3670
3671         device = svGetValue (ifcfg, "DEVICE", FALSE);
3672         if (!device) {
3673                 PARSE_WARNING ("InfiniBand connection specified PKEY but not DEVICE");
3674                 goto done;
3675         }
3676
3677         physdev = svGetValue (ifcfg, "PHYSDEV", FALSE);
3678         if (!physdev) {
3679                 PARSE_WARNING ("InfiniBand connection specified PKEY but not PHYSDEV");
3680                 goto done;
3681         }
3682
3683         pkey_id = svGetValue (ifcfg, "PKEY_ID", FALSE);
3684         if (!pkey_id) {
3685                 PARSE_WARNING ("InfiniBand connection specified PKEY but not PKEY_ID");
3686                 goto done;
3687         }
3688
3689         if (g_str_has_prefix (pkey_id, "0x"))
3690                 id = strtoul (pkey_id, &end, 16);
3691         else if (!g_str_has_prefix (pkey_id, "0"))
3692                 id = strtoul (pkey_id, &end, 10);
3693         else
3694                 end = pkey_id;
3695         if (end == pkey_id || *end || id > 0xFFFF) {
3696                 PARSE_WARNING ("invalid InfiniBand PKEY_ID '%s'", pkey_id);
3697                 goto done;
3698         }
3699         id = (id | 0x8000);
3700
3701         ifname = g_strdup_printf ("%s.%04x", physdev, id);
3702         if (strcmp (device, ifname) != 0) {
3703                 PARSE_WARNING ("InfiniBand DEVICE (%s) does not match PHYSDEV+PKEY_ID (%s)",
3704                                device, ifname);
3705                 goto done;
3706         }
3707
3708         *out_p_key = id;
3709         *out_parent = g_strdup (physdev);
3710         ret = TRUE;
3711
3712  done:
3713         g_free (device);
3714         g_free (physdev);
3715         g_free (pkey_id);
3716         g_free (ifname);
3717
3718         if (!ret) {
3719                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3720                              "Failed to create InfiniBand setting.");
3721         }
3722         return ret;
3723 }
3724
3725
3726 static NMSetting *
3727 make_infiniband_setting (shvarFile *ifcfg,
3728                          const char *file,
3729                          GError **error)
3730 {
3731         NMSettingInfiniband *s_infiniband;
3732         char *value = NULL;
3733         int mtu;
3734
3735         s_infiniband = NM_SETTING_INFINIBAND (nm_setting_infiniband_new ());
3736
3737         value = svGetValue (ifcfg, "MTU", FALSE);
3738         if (value) {
3739                 if (get_int (value, &mtu)) {
3740                         if (mtu >= 0 && mtu < 65536)
3741                                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu, NULL);
3742                 } else {
3743                         /* Shouldn't be fatal... */
3744                         PARSE_WARNING ("invalid MTU '%s'", value);
3745                 }
3746                 g_free (value);
3747         }
3748
3749         value = svGetValue (ifcfg, "HWADDR", FALSE);
3750         if (value) {
3751                 value = g_strstrip (value);
3752                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, value, NULL);
3753                 g_free (value);
3754         }
3755
3756         if (svTrueValue (ifcfg, "CONNECTED_MODE", FALSE))
3757                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "connected", NULL);
3758         else
3759                 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL);
3760
3761         if (svTrueValue (ifcfg, "PKEY", FALSE)) {
3762                 int p_key;
3763                 char *parent;
3764
3765                 if (!parse_infiniband_p_key (ifcfg, &p_key, &parent, error)) {
3766                         g_object_unref (s_infiniband);
3767                         return NULL;
3768                 }
3769
3770                 g_object_set (s_infiniband,
3771                               NM_SETTING_INFINIBAND_P_KEY, p_key,
3772                               NM_SETTING_INFINIBAND_PARENT, parent,
3773                               NULL);
3774         }
3775
3776         return (NMSetting *) s_infiniband;
3777 }
3778
3779 static NMConnection *
3780 infiniband_connection_from_ifcfg (const char *file,
3781                                   shvarFile *ifcfg,
3782                                   GError **error)
3783 {
3784         NMConnection *connection = NULL;
3785         NMSetting *con_setting = NULL;
3786         NMSetting *infiniband_setting = NULL;
3787
3788         g_return_val_if_fail (file != NULL, NULL);
3789         g_return_val_if_fail (ifcfg != NULL, NULL);
3790
3791         connection = nm_simple_connection_new ();
3792
3793         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL);
3794         if (!con_setting) {
3795                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3796                              "Failed to create connection setting.");
3797                 g_object_unref (connection);
3798                 return NULL;
3799         }
3800         check_if_bond_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
3801         check_if_team_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
3802         nm_connection_add_setting (connection, con_setting);
3803
3804         infiniband_setting = make_infiniband_setting (ifcfg, file, error);
3805         if (!infiniband_setting) {
3806                 g_object_unref (connection);
3807                 return NULL;
3808         }
3809         nm_connection_add_setting (connection, infiniband_setting);
3810
3811         return connection;
3812 }
3813
3814 static void
3815 handle_bond_option (NMSettingBond *s_bond,
3816                     const char *key,
3817                     const char *value)
3818 {
3819         char *sanitized = NULL, *j;
3820         const char *p = value;
3821
3822         /* Remove any quotes or +/- from arp_ip_target */
3823         if (!g_strcmp0 (key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET) && value && value[0]) {
3824                 if (*p == '\'' || *p == '"')
3825                         p++;
3826                 j = sanitized = g_malloc0 (strlen (p) + 1);
3827                 while (*p) {
3828                         if (*p != '+' && *p != '-' && *p != '\'' && *p != '"')
3829                                 *j++ = *p;
3830                         p++;
3831                 }
3832         }
3833
3834         if (!nm_setting_bond_add_option (s_bond, key, sanitized ? sanitized : value))
3835                 PARSE_WARNING ("invalid bonding option '%s'", key);
3836         g_free (sanitized);
3837 }
3838
3839 static NMSetting *
3840 make_bond_setting (shvarFile *ifcfg,
3841                    const char *file,
3842                    GError **error)
3843 {
3844         NMSettingBond *s_bond;
3845         char *value;
3846
3847         s_bond = NM_SETTING_BOND (nm_setting_bond_new ());
3848
3849         value = svGetValue (ifcfg, "DEVICE", FALSE);
3850         if (!value || !strlen (value)) {
3851                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3852                              "mandatory DEVICE keyword missing");
3853                 goto error;
3854         }
3855         g_free (value);
3856
3857         value = svGetValue (ifcfg, "BONDING_OPTS", FALSE);
3858         if (value) {
3859                 char **items, **iter;
3860
3861                 items = g_strsplit_set (value, " ", -1);
3862                 for (iter = items; iter && *iter; iter++) {
3863                         if (strlen (*iter)) {
3864                                 char **keys, *key, *val;
3865
3866                                 keys = g_strsplit_set (*iter, "=", 2);
3867                                 if (keys && *keys) {
3868                                         key = *keys;
3869                                         val = *(keys + 1);
3870                                         if (val && strlen(key) && strlen(val))
3871                                                 handle_bond_option (s_bond, key, val);
3872                                 }
3873
3874                                 g_strfreev (keys);
3875                         }
3876                 }
3877                 g_free (value);
3878                 g_strfreev (items);
3879         }
3880
3881         return (NMSetting *) s_bond;
3882
3883 error:
3884         g_object_unref (s_bond);
3885         return NULL;
3886 }
3887
3888 static NMConnection *
3889 bond_connection_from_ifcfg (const char *file,
3890                             shvarFile *ifcfg,
3891                             GError **error)
3892 {
3893         NMConnection *connection = NULL;
3894         NMSetting *con_setting = NULL;
3895         NMSetting *bond_setting = NULL;
3896         NMSetting *wired_setting = NULL;
3897         NMSetting8021x *s_8021x = NULL;
3898
3899         g_return_val_if_fail (file != NULL, NULL);
3900         g_return_val_if_fail (ifcfg != NULL, NULL);
3901
3902         connection = nm_simple_connection_new ();
3903
3904         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BOND_SETTING_NAME, NULL, _("Bond"));
3905         if (!con_setting) {
3906                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3907                              "Failed to create connection setting.");
3908                 g_object_unref (connection);
3909                 return NULL;
3910         }
3911         nm_connection_add_setting (connection, con_setting);
3912
3913         bond_setting = make_bond_setting (ifcfg, file, error);
3914         if (!bond_setting) {
3915                 g_object_unref (connection);
3916                 return NULL;
3917         }
3918         nm_connection_add_setting (connection, bond_setting);
3919
3920         wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error);
3921         if (!wired_setting) {
3922                 g_object_unref (connection);
3923                 return NULL;
3924         }
3925         nm_connection_add_setting (connection, wired_setting);
3926
3927         if (s_8021x)
3928                 nm_connection_add_setting (connection, NM_SETTING (s_8021x));
3929
3930         return connection;
3931 }
3932
3933 /* Check 'error' for errors. Missing config (NULL return value) is a valid case. */
3934 static char *
3935 read_team_config (shvarFile *ifcfg, const char *key, GError **error)
3936 {
3937         char *value;
3938         size_t l;
3939
3940         /* FIXME: validate the JSON at some point */
3941         value = svGetValue (ifcfg, key, TRUE);
3942         if (!value)
3943                 return NULL;
3944
3945         /* No reason Team config should be over 20k.  The config is read
3946          * verbatim, length-checked, then unescaped.  svUnescape() does not
3947          * deal well with extremely long strings.
3948          */
3949         l = strlen (value);
3950         if (l > 20000) {
3951                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3952                              "%s too long (size %zd)", key, l);
3953                 g_free (value);
3954                 return NULL;
3955         }
3956         svUnescape (value);
3957         return value;
3958 }
3959
3960 static NMSetting *
3961 make_team_setting (shvarFile *ifcfg,
3962                    const char *file,
3963                    GError **error)
3964 {
3965         NMSettingTeam *s_team;
3966         char *value;
3967         GError *local_err = NULL;
3968
3969         s_team = NM_SETTING_TEAM (nm_setting_team_new ());
3970
3971         value = svGetValue (ifcfg, "DEVICE", FALSE);
3972         if (!value || !strlen (value)) {
3973                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3974                              "mandatory DEVICE keyword missing");
3975                 goto error;
3976         }
3977         g_free (value);
3978
3979         value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err);
3980         if (local_err) {
3981                 g_propagate_error (error, local_err);
3982                 goto error;
3983         }
3984         g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL);
3985         g_free (value);
3986
3987         return (NMSetting *) s_team;
3988
3989 error:
3990         g_object_unref (s_team);
3991         return NULL;
3992 }
3993
3994 static NMConnection *
3995 team_connection_from_ifcfg (const char *file,
3996                             shvarFile *ifcfg,
3997                             GError **error)
3998 {
3999         NMConnection *connection = NULL;
4000         NMSetting *con_setting = NULL;
4001         NMSetting *team_setting = NULL;
4002         NMSetting *wired_setting = NULL;
4003         NMSetting8021x *s_8021x = NULL;
4004
4005         g_return_val_if_fail (file != NULL, NULL);
4006         g_return_val_if_fail (ifcfg != NULL, NULL);
4007
4008         connection = nm_simple_connection_new ();
4009
4010         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_TEAM_SETTING_NAME, NULL, _("Team"));
4011         if (!con_setting) {
4012                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4013                              "Failed to create connection setting.");
4014                 g_object_unref (connection);
4015                 return NULL;
4016         }
4017         nm_connection_add_setting (connection, con_setting);
4018
4019         team_setting = make_team_setting (ifcfg, file, error);
4020         if (!team_setting) {
4021                 g_object_unref (connection);
4022                 return NULL;
4023         }
4024         nm_connection_add_setting (connection, team_setting);
4025
4026         wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error);
4027         if (!wired_setting) {
4028                 g_object_unref (connection);
4029                 return NULL;
4030         }
4031         nm_connection_add_setting (connection, wired_setting);
4032
4033         if (s_8021x)
4034                 nm_connection_add_setting (connection, NM_SETTING (s_8021x));
4035
4036         return connection;
4037 }
4038
4039 typedef void (*BridgeOptFunc) (NMSetting *setting,
4040                                gboolean stp,
4041                                const char *key,
4042                                const char *value);
4043
4044 static void
4045 handle_bridge_option (NMSetting *setting,
4046                       gboolean stp,
4047                       const char *key,
4048                       const char *value)
4049 {
4050         guint32 u = 0;
4051
4052         if (!strcmp (key, "priority")) {
4053                 if (stp == FALSE) {
4054                         PARSE_WARNING ("'priority' invalid when STP is disabled");
4055                 } else if (get_uint (value, &u))
4056                         g_object_set (setting, NM_SETTING_BRIDGE_PRIORITY, u, NULL);
4057                 else
4058                         PARSE_WARNING ("invalid priority value '%s'", value);
4059         } else if (!strcmp (key, "hello_time")) {
4060                 if (stp == FALSE) {
4061                         PARSE_WARNING ("'hello_time' invalid when STP is disabled");
4062                 } else if (get_uint (value, &u))
4063                         g_object_set (setting, NM_SETTING_BRIDGE_HELLO_TIME, u, NULL);
4064                 else
4065                         PARSE_WARNING ("invalid hello_time value '%s'", value);
4066         } else if (!strcmp (key, "max_age")) {
4067                 if (stp == FALSE) {
4068                         PARSE_WARNING ("'max_age' invalid when STP is disabled");
4069                 } else if (get_uint (value, &u))
4070                         g_object_set (setting, NM_SETTING_BRIDGE_MAX_AGE, u, NULL);
4071                 else
4072                         PARSE_WARNING ("invalid max_age value '%s'", value);
4073         } else if (!strcmp (key, "ageing_time")) {
4074                 if (get_uint (value, &u))
4075                         g_object_set (setting, NM_SETTING_BRIDGE_AGEING_TIME, u, NULL);
4076                 else
4077                         PARSE_WARNING ("invalid ageing_time value '%s'", value);
4078         } else
4079                         PARSE_WARNING ("unhandled bridge option '%s'", key);
4080 }
4081
4082 static void
4083 handle_bridging_opts (NMSetting *setting,
4084                       gboolean stp,
4085                       const char *value,
4086                       BridgeOptFunc func)
4087 {
4088         char **items, **iter;
4089
4090         items = g_strsplit_set (value, " ", -1);
4091         for (iter = items; iter && *iter; iter++) {
4092                 if (strlen (*iter)) {
4093                         char **keys, *key, *val;
4094
4095                         keys = g_strsplit_set (*iter, "=", 2);
4096                         if (keys && *keys) {
4097                                 key = *keys;
4098                                 val = *(keys + 1);
4099                                 if (val && strlen(key) && strlen(val))
4100                                         func (setting, stp, key, val);
4101                         }
4102
4103                         g_strfreev (keys);
4104                 }
4105         }
4106         g_strfreev (items);
4107 }
4108
4109 static NMSetting *
4110 make_bridge_setting (shvarFile *ifcfg,
4111                      const char *file,
4112                      GError **error)
4113 {
4114         NMSettingBridge *s_bridge;
4115         char *value;
4116         guint32 u;
4117         gboolean stp = FALSE;
4118         gboolean stp_set = FALSE;
4119
4120         s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
4121
4122         value = svGetValue (ifcfg, "DEVICE", FALSE);
4123         if (!value || !strlen (value)) {
4124                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4125                              "mandatory DEVICE keyword missing");
4126                 goto error;
4127         }
4128         g_free (value);
4129
4130         value = svGetValue (ifcfg, "MACADDR", FALSE);
4131         if (value) {
4132                 value = g_strstrip (value);
4133                 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, value, NULL);
4134                 g_free (value);
4135         }
4136
4137         value = svGetValue (ifcfg, "STP", FALSE);
4138         if (value) {
4139                 if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) {
4140                         g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL);
4141                         stp = TRUE;
4142                         stp_set = TRUE;
4143                 } else if (!strcasecmp (value, "off") || !strcasecmp (value, "no")) {
4144                         g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
4145                         stp_set = TRUE;
4146                 } else
4147                         PARSE_WARNING ("invalid STP value '%s'", value);
4148                 g_free (value);
4149         }
4150
4151         if (!stp_set) {
4152                 /* Missing or invalid STP property means "no" */
4153                 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
4154         }
4155
4156         value = svGetValue (ifcfg, "DELAY", FALSE);
4157         if (value) {
4158                 if (stp) {
4159                         if (get_uint (value, &u))
4160                                 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, u, NULL);
4161                         else
4162                                 PARSE_WARNING ("invalid forward delay value '%s'", value);
4163                 } else
4164                         PARSE_WARNING ("DELAY invalid when STP is disabled");
4165                 g_free (value);
4166         }
4167
4168         value = svGetValue (ifcfg, "BRIDGING_OPTS", FALSE);
4169         if (value) {
4170                 handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option);
4171                 g_free (value);
4172         }
4173
4174         return (NMSetting *) s_bridge;
4175
4176 error:
4177         g_object_unref (s_bridge);
4178         return NULL;
4179 }
4180
4181 static NMConnection *
4182 bridge_connection_from_ifcfg (const char *file,
4183                               shvarFile *ifcfg,
4184                               GError **error)
4185 {
4186         NMConnection *connection = NULL;
4187         NMSetting *con_setting = NULL;
4188         NMSetting *bridge_setting = NULL;
4189
4190         g_return_val_if_fail (file != NULL, NULL);
4191         g_return_val_if_fail (ifcfg != NULL, NULL);
4192
4193         connection = nm_simple_connection_new ();
4194
4195         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BRIDGE_SETTING_NAME, NULL, _("Bridge"));
4196         if (!con_setting) {
4197                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4198                              "Failed to create connection setting.");
4199                 g_object_unref (connection);
4200                 return NULL;
4201         }
4202         nm_connection_add_setting (connection, con_setting);
4203
4204         bridge_setting = make_bridge_setting (ifcfg, file, error);
4205         if (!bridge_setting) {
4206                 g_object_unref (connection);
4207                 return NULL;
4208         }
4209         nm_connection_add_setting (connection, bridge_setting); 
4210
4211         return connection;
4212 }
4213
4214 static void
4215 handle_bridge_port_option (NMSetting *setting,
4216                            gboolean stp,
4217                            const char *key,
4218                            const char *value)
4219 {
4220         guint32 u = 0;
4221
4222         if (!strcmp (key, "priority")) {
4223                 if (get_uint (value, &u))
4224                         g_object_set (setting, NM_SETTING_BRIDGE_PORT_PRIORITY, u, NULL);
4225                 else
4226                         PARSE_WARNING ("invalid priority value '%s'", value);
4227         } else if (!strcmp (key, "path_cost")) {
4228                 if (get_uint (value, &u))
4229                         g_object_set (setting, NM_SETTING_BRIDGE_PORT_PATH_COST, u, NULL);
4230                 else
4231                         PARSE_WARNING ("invalid path_cost value '%s'", value);
4232         } else if (!strcmp (key, "hairpin_mode")) {
4233                 if (!strcasecmp (value, "on") || !strcasecmp (value, "yes") || !strcmp (value, "1"))
4234                         g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, TRUE, NULL);
4235                 else if (!strcasecmp (value, "off") || !strcasecmp (value, "no"))
4236                         g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, FALSE, NULL);
4237                 else
4238                         PARSE_WARNING ("invalid hairpin_mode value '%s'", value);
4239         } else
4240                         PARSE_WARNING ("unhandled bridge port option '%s'", key);
4241 }
4242
4243 static NMSetting *
4244 make_bridge_port_setting (shvarFile *ifcfg)
4245 {
4246         NMSetting *s_port = NULL;
4247         char *value;
4248
4249         g_return_val_if_fail (ifcfg != NULL, FALSE);
4250
4251         value = svGetValue (ifcfg, "BRIDGE", FALSE);
4252         if (value) {
4253                 g_free (value);
4254
4255                 s_port = nm_setting_bridge_port_new ();
4256                 value = svGetValue (ifcfg, "BRIDGING_OPTS", FALSE);
4257                 if (value)
4258                         handle_bridging_opts (s_port, FALSE, value, handle_bridge_port_option);
4259                 g_free (value);
4260         }
4261
4262         return s_port;
4263 }
4264
4265 static NMSetting *
4266 make_team_port_setting (shvarFile *ifcfg)
4267 {
4268         NMSetting *s_port = NULL;
4269         char *value;
4270         GError *error = NULL;
4271
4272         value = read_team_config (ifcfg, "TEAM_PORT_CONFIG", &error);
4273         if (value) {
4274                 s_port = nm_setting_team_port_new ();
4275                 g_object_set (s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL);
4276                 g_free (value);
4277         } else if (error) {
4278                 PARSE_WARNING ("%s", error->message);
4279                 g_error_free (error);
4280         }
4281
4282         return s_port;
4283 }
4284
4285 static gboolean
4286 is_bond_device (const char *name, shvarFile *parsed)
4287 {
4288         g_return_val_if_fail (name != NULL, FALSE);
4289         g_return_val_if_fail (parsed != NULL, FALSE);
4290
4291         if (svTrueValue (parsed, "BONDING_MASTER", FALSE))
4292                 return TRUE;
4293         
4294         /* XXX: Check for "bond[\d]+"? */
4295
4296         return FALSE;
4297 }
4298
4299 static gboolean
4300 is_vlan_device (const char *name, shvarFile *parsed)
4301 {
4302         g_return_val_if_fail (name != NULL, FALSE);
4303         g_return_val_if_fail (parsed != NULL, FALSE);
4304
4305         if (svTrueValue (parsed, "VLAN", FALSE))
4306                 return TRUE;
4307
4308         return FALSE;
4309 }
4310
4311 static gboolean
4312 is_wifi_device (const char *name, shvarFile *parsed)
4313 {
4314         int ifindex;
4315
4316         g_return_val_if_fail (name != NULL, FALSE);
4317         g_return_val_if_fail (parsed != NULL, FALSE);
4318
4319         ifindex = nm_platform_link_get_ifindex (name);
4320         if (ifindex == 0)
4321                 return FALSE;
4322
4323         return nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_WIFI;
4324 }
4325
4326 static void
4327 parse_prio_map_list (NMSettingVlan *s_vlan,
4328                      shvarFile *ifcfg,
4329                      const char *key,
4330                      NMVlanPriorityMap map)
4331 {
4332         char *value;
4333         gchar **list = NULL, **iter;
4334
4335         value = svGetValue (ifcfg, key, FALSE);
4336         if (!value)
4337                 return;
4338
4339         list = g_strsplit_set (value, ",", -1);
4340         g_free (value);
4341
4342         for (iter = list; iter && *iter; iter++) {
4343                 if (!*iter || !strchr (*iter, ':'))
4344                         continue;
4345
4346                 if (!nm_setting_vlan_add_priority_str (s_vlan, map, *iter))
4347                         PARSE_WARNING ("invalid %s priority map item '%s'", key, *iter);
4348         }
4349         g_strfreev (list);
4350 }
4351
4352 static NMSetting *
4353 make_vlan_setting (shvarFile *ifcfg,
4354                    const char *file,
4355                    GError **error)
4356 {
4357         NMSettingVlan *s_vlan = NULL;
4358         char *value = NULL;
4359         char *iface_name = NULL;
4360         char *parent = NULL;
4361         const char *p = NULL;
4362         char *end = NULL;
4363         gint vlan_id = -1;
4364         guint32 vlan_flags = 0;
4365
4366         value = svGetValue (ifcfg, "VLAN_ID", FALSE);
4367         if (value) {
4368                 errno = 0;
4369                 vlan_id = (gint) g_ascii_strtoll (value, NULL, 10);
4370                 if (vlan_id < 0 || vlan_id > 4096 || errno) {
4371                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4372                                      "Invalid VLAN_ID '%s'", value);
4373                         g_free (value);
4374                         return NULL;
4375                 }
4376                 g_free (value);
4377         }
4378
4379         /* Need DEVICE if we don't have a separate VLAN_ID property */
4380         iface_name = svGetValue (ifcfg, "DEVICE", FALSE);
4381         if (!iface_name && vlan_id < 0) {
4382                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4383                                      "Missing DEVICE property; cannot determine VLAN ID.");
4384                 return NULL;
4385         }
4386
4387         s_vlan = NM_SETTING_VLAN (nm_setting_vlan_new ());
4388
4389         /* Parent interface from PHYSDEV takes precedence if it exists */
4390         parent = svGetValue (ifcfg, "PHYSDEV", FALSE);
4391
4392         if (iface_name) {
4393                 p = strchr (iface_name, '.');
4394                 if (p) {
4395                         /* eth0.43; PHYSDEV is assumed from it if unknown */
4396                         if (!parent) {
4397                                 parent = g_strndup (iface_name, p - iface_name);
4398                                 if (g_str_has_prefix (parent, "vlan")) {
4399                                         /* Like initscripts, if no PHYSDEV and we get an obviously
4400                                          * invalid parent interface from DEVICE, fail.
4401                                          */
4402                                         g_free (parent);
4403                                         parent = NULL;
4404                                 }
4405                         }
4406                         p++;
4407                 } else {
4408                         /* format like vlan43; PHYSDEV must be set */
4409                         if (g_str_has_prefix (iface_name, "vlan"))
4410                                 p = iface_name + 4;
4411                 }
4412
4413                 if (p) {
4414                         /* Grab VLAN ID from interface name; this takes precedence over the
4415                          * separate VLAN_ID property for backwards compat.
4416                          */
4417                         vlan_id = (gint) g_ascii_strtoll (p, &end, 10);
4418                         if (vlan_id < 0 || vlan_id > 4095 || end == p || *end) {
4419                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4420                                              "Failed to determine VLAN ID from DEVICE '%s'",
4421                                              iface_name);
4422                                 goto error;
4423                         }
4424                 }
4425         }
4426
4427         if (vlan_id < 0) {
4428                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4429                                      "Failed to determine VLAN ID from DEVICE or VLAN_ID.");
4430                 goto error;
4431         }
4432         g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
4433
4434         if (parent == NULL) {
4435                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4436                                      "Failed to determine VLAN parent from DEVICE or PHYSDEV");
4437                 goto error;
4438         }
4439         g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
4440
4441         if (svTrueValue (ifcfg, "REORDER_HDR", FALSE))
4442                 vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS;
4443
4444         value = svGetValue (ifcfg, "VLAN_FLAGS", FALSE);
4445         if (value) {
4446                 if (g_strstr_len (value, -1, "GVRP"))
4447                         vlan_flags |= NM_VLAN_FLAG_GVRP;
4448                 if (g_strstr_len (value, -1, "LOOSE_BINDING"))
4449                         vlan_flags |= NM_VLAN_FLAG_LOOSE_BINDING;
4450         }
4451
4452         g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, vlan_flags, NULL);
4453         g_free (value);
4454
4455         parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP);
4456         parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP);
4457
4458         return (NMSetting *) s_vlan;
4459
4460 error:
4461         g_free (parent);
4462         g_free (iface_name);
4463         g_object_unref (s_vlan);
4464         return NULL;
4465 }
4466
4467 static NMConnection *
4468 vlan_connection_from_ifcfg (const char *file,
4469                             shvarFile *ifcfg,
4470                             GError **error)
4471 {
4472         NMConnection *connection = NULL;
4473         NMSetting *con_setting = NULL;
4474         NMSetting *wired_setting = NULL;
4475         NMSetting *vlan_setting = NULL;
4476         NMSetting8021x *s_8021x = NULL;
4477
4478         g_return_val_if_fail (file != NULL, NULL);
4479         g_return_val_if_fail (ifcfg != NULL, NULL);
4480
4481         connection = nm_simple_connection_new ();
4482
4483         con_setting = make_connection_setting (file, ifcfg, NM_SETTING_VLAN_SETTING_NAME, NULL, "Vlan");
4484         if (!con_setting) {
4485                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4486                              "Failed to create connection setting.");
4487                 g_object_unref (connection);
4488                 return NULL;
4489         }
4490         check_if_bond_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
4491         check_if_team_slave (ifcfg, NM_SETTING_CONNECTION (con_setting));
4492         nm_connection_add_setting (connection, con_setting);
4493
4494         vlan_setting = make_vlan_setting (ifcfg, file, error);
4495         if (!vlan_setting) {
4496                 g_object_unref (connection);
4497                 return NULL;
4498         }
4499         nm_connection_add_setting (connection, vlan_setting);
4500
4501         wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error);
4502         if (!wired_setting) {
4503                 g_object_unref (connection);
4504                 return NULL;
4505         }
4506         nm_connection_add_setting (connection, wired_setting);
4507
4508         if (s_8021x)
4509                 nm_connection_add_setting (connection, NM_SETTING (s_8021x));
4510
4511         return connection;
4512 }
4513
4514 static NMConnection *
4515 create_unhandled_connection (const char *filename, shvarFile *ifcfg,
4516                              const char *type, char **out_spec)
4517 {
4518         NMConnection *connection;
4519         NMSetting *s_con;
4520         char *value;
4521
4522         g_assert (out_spec != NULL);
4523
4524         connection = nm_simple_connection_new ();
4525
4526         /* Get NAME, UUID, etc. We need to set a connection type (generic) and add
4527          * an empty type-specific setting as well, to make sure it passes
4528          * nm_connection_verify() later.
4529          */
4530         s_con = make_connection_setting (filename, ifcfg, NM_SETTING_GENERIC_SETTING_NAME,
4531                                          NULL, NULL);
4532         nm_connection_add_setting (connection, s_con);
4533
4534         nm_connection_add_setting (connection, nm_setting_generic_new ());
4535
4536         /* Get a spec */
4537         value = svGetValue (ifcfg, "HWADDR", FALSE);
4538         if (value) {
4539                 char *lower = g_ascii_strdown (value, -1);
4540                 *out_spec = g_strdup_printf ("%s:mac:%s", type, lower);
4541                 g_free (lower);
4542                 g_free (value);
4543                 return connection;
4544         }
4545
4546         value = svGetValue (ifcfg, "SUBCHANNELS", FALSE);
4547         if (value) {
4548                 *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, value);
4549                 g_free (value);
4550                 return connection;
4551         }
4552
4553         value = svGetValue (ifcfg, "DEVICE", FALSE);
4554         if (value) {
4555                 *out_spec = g_strdup_printf ("%s:interface-name:%s", type, value);
4556                 g_free (value);
4557                 return connection;
4558         }
4559
4560         g_object_unref (connection);
4561         return NULL;
4562 }
4563
4564 char *
4565 uuid_from_file (const char *filename)
4566 {
4567         const char *ifcfg_name = NULL;
4568         shvarFile *ifcfg;
4569         char *uuid;
4570
4571         g_return_val_if_fail (filename != NULL, NULL);
4572
4573         ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
4574         if (!ifcfg_name)
4575                 return NULL;
4576
4577         ifcfg = svOpenFile (filename, NULL);
4578         if (!ifcfg)
4579                 return NULL;
4580
4581         /* Try for a UUID key before falling back to hashing the file name */
4582         uuid = svGetValue (ifcfg, "UUID", FALSE);
4583         if (!uuid || !strlen (uuid)) {
4584                 g_free (uuid);
4585                 uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
4586         }
4587
4588         svCloseFile (ifcfg);
4589         return uuid;
4590 }
4591
4592 static void
4593 check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6)
4594 {
4595         if (!s_ip6)
4596                 return;
4597
4598         /* If there is no IPv4 config or it doesn't contain DNS searches,
4599          * read DOMAIN and put the domains into IPv6.
4600          */
4601         if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) {
4602                 /* DNS searches */
4603                 char *value = svGetValue (ifcfg, "DOMAIN", FALSE);
4604                 if (value) {
4605                         char **searches = g_strsplit (value, " ", 0);
4606                         if (searches) {
4607                                 char **item;
4608                                 for (item = searches; *item; item++) {
4609                                         if (strlen (*item)) {
4610                                                 if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item))
4611                                                         PARSE_WARNING ("duplicate DNS domain '%s'", *item);
4612                                         }
4613                                 }
4614                                 g_strfreev (searches);
4615                         }
4616                         g_free (value);
4617                 }
4618         }
4619 }
4620
4621 static NMConnection *
4622 connection_from_file_full (const char *filename,
4623                            const char *network_file,  /* for unit tests only */
4624                            const char *test_type,     /* for unit tests only */
4625                            char **out_unhandled,
4626                            GError **error,
4627                            gboolean *out_ignore_error)
4628 {
4629         NMConnection *connection = NULL;
4630         shvarFile *parsed;
4631         char *type, *devtype, *bootproto;
4632         NMSetting *s_ip4, *s_ip6, *s_port, *s_dcb = NULL;
4633         const char *ifcfg_name = NULL;
4634
4635         g_return_val_if_fail (filename != NULL, NULL);
4636         if (out_unhandled)
4637                 g_return_val_if_fail (*out_unhandled == NULL, NULL);
4638
4639         /* Non-NULL only for unit tests; normally use /etc/sysconfig/network */
4640         if (!network_file)
4641                 network_file = SYSCONFDIR "/sysconfig/network";
4642
4643         ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
4644         if (!ifcfg_name) {
4645                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4646                              "Ignoring connection '%s' because it's not an ifcfg file.", filename);
4647                 return NULL;
4648         }
4649
4650         parsed = svOpenFile (filename, error);
4651         if (!parsed)
4652                 return NULL;
4653
4654         if (!svTrueValue (parsed, "NM_CONTROLLED", TRUE)) {
4655                 g_assert (out_unhandled != NULL);
4656
4657                 connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
4658                 if (!connection)
4659                         PARSE_WARNING ("NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
4660                 goto done;
4661         }
4662
4663         /* iBFT is handled by the iBFT settings plugin */
4664         bootproto = svGetValue (parsed, "BOOTPROTO", FALSE);
4665         if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
4666                 if (out_ignore_error)
4667                         *out_ignore_error = TRUE;
4668                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4669                              "Ignoring iBFT configuration");
4670                 g_free (bootproto);
4671                 goto done;
4672         }
4673
4674         type = NULL;
4675
4676         devtype = svGetValue (parsed, "DEVICETYPE", FALSE);
4677         if (devtype) {
4678                 if (!strcasecmp (devtype, TYPE_TEAM))
4679                         type = g_strdup (TYPE_TEAM);
4680                 else if (!strcasecmp (devtype, TYPE_TEAM_PORT))
4681                         type = g_strdup (TYPE_ETHERNET);
4682                 g_free (devtype);
4683         }
4684
4685         if (!type)
4686                 type = svGetValue (parsed, "TYPE", FALSE);
4687
4688         if (!type) {
4689                 char *device;
4690
4691                 device = svGetValue (parsed, "DEVICE", FALSE);
4692                 if (!device) {
4693                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4694                                      "File '%s' had neither TYPE nor DEVICE keys.", filename);
4695                         goto done;
4696                 }
4697
4698                 if (!strcmp (device, "lo")) {
4699                         if (out_ignore_error)
4700                                 *out_ignore_error = TRUE;
4701                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4702                                      "Ignoring loopback device config.");
4703                         g_free (device);
4704                         goto done;
4705                 }
4706
4707                 if (!test_type) {
4708                         if (is_bond_device (device, parsed))
4709                                 type = g_strdup (TYPE_BOND);
4710                         else if (is_vlan_device (device, parsed))
4711                                 type = g_strdup (TYPE_VLAN);
4712                         else if (is_wifi_device (device, parsed))
4713                                 type = g_strdup (TYPE_WIRELESS);
4714                         else
4715                                 type = g_strdup (TYPE_ETHERNET);
4716                 } else {
4717                         /* For the unit tests, there won't necessarily be any
4718                          * adapters of the connection's type in the system so the
4719                          * type can't be tested with ioctls.
4720                          */
4721                         type = g_strdup (test_type);
4722                 }
4723
4724                 g_free (device);
4725         } else {
4726                 /* Check for IBM s390 CTC devices and call them Ethernet */
4727                 if (g_strcmp0 (type, "CTC") == 0) {
4728                         g_free (type);
4729                         type = g_strdup (TYPE_ETHERNET);
4730                 }
4731         }
4732
4733         if (svTrueValue (parsed, "BONDING_MASTER", FALSE) &&
4734             strcasecmp (type, TYPE_BOND)) {
4735                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4736                              "BONDING_MASTER=yes key only allowed in TYPE=bond connections");
4737                 goto done;
4738         }
4739
4740         /* Construct the connection */
4741         if (!strcasecmp (type, TYPE_ETHERNET))
4742                 connection = wired_connection_from_ifcfg (filename, parsed, error);
4743         else if (!strcasecmp (type, TYPE_WIRELESS))
4744                 connection = wireless_connection_from_ifcfg (filename, parsed, error);
4745         else if (!strcasecmp (type, TYPE_INFINIBAND))
4746                 connection = infiniband_connection_from_ifcfg (filename, parsed, error);
4747         else if (!strcasecmp (type, TYPE_BOND))
4748                 connection = bond_connection_from_ifcfg (filename, parsed, error);
4749         else if (!strcasecmp (type, TYPE_TEAM))
4750                 connection = team_connection_from_ifcfg (filename, parsed, error);
4751         else if (!strcasecmp (type, TYPE_VLAN))
4752                 connection = vlan_connection_from_ifcfg (filename, parsed, error);
4753         else if (!strcasecmp (type, TYPE_BRIDGE))
4754                 connection = bridge_connection_from_ifcfg (filename, parsed, error);
4755         else {
4756                 g_assert (out_unhandled != NULL);
4757
4758                 connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled);
4759                 if (!connection)
4760                         PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed");
4761                 goto done;
4762         }
4763         g_free (type);
4764
4765         if (!connection)
4766                 goto done;
4767
4768         s_ip6 = make_ip6_setting (parsed, network_file, error);
4769         if (!s_ip6) {
4770                 g_object_unref (connection);
4771                 connection = NULL;
4772                 goto done;
4773         } else
4774                 nm_connection_add_setting (connection, s_ip6);
4775
4776         s_ip4 = make_ip4_setting (parsed, network_file, error);
4777         if (!s_ip4) {
4778                 g_object_unref (connection);
4779                 connection = NULL;
4780                 goto done;
4781         } else {
4782                 read_aliases (NM_SETTING_IP_CONFIG (s_ip4), filename, network_file);
4783                 nm_connection_add_setting (connection, s_ip4);
4784         }
4785
4786         /* There is only one DOMAIN variable and it is read and put to IPv4 config
4787          * But if IPv4 is disabled or the config fails for some reason, we read
4788          * DOMAIN and put the values into IPv6 config instead.
4789          */
4790         check_dns_search_domains (parsed, s_ip4, s_ip6);
4791
4792         /* Bridge port? */
4793         s_port = make_bridge_port_setting (parsed);
4794         if (s_port)
4795                 nm_connection_add_setting (connection, s_port);
4796
4797         /* Team port? */
4798         s_port = make_team_port_setting (parsed);
4799         if (s_port)
4800                 nm_connection_add_setting (connection, s_port);
4801
4802         if (!make_dcb_setting (parsed, network_file, &s_dcb, error)) {
4803                 g_object_unref (connection);
4804                 connection = NULL;
4805                 goto done;
4806         }
4807         if (s_dcb)
4808                 nm_connection_add_setting (connection, s_dcb);
4809
4810         if (!nm_connection_normalize (connection, NULL, NULL, error)) {
4811                 g_object_unref (connection);
4812                 connection = NULL;
4813         }
4814
4815 done:
4816         svCloseFile (parsed);
4817         return connection;
4818 }
4819
4820 NMConnection *
4821 connection_from_file (const char *filename,
4822                       char **out_unhandled,
4823                       GError **error)
4824 {
4825         gboolean ignore_error = FALSE;
4826         NMConnection *conn;
4827
4828         conn = connection_from_file_full (filename, NULL, NULL,
4829                                           out_unhandled,
4830                                           error,
4831                                           &ignore_error);
4832         if (error && *error && !ignore_error)
4833                 PARSE_WARNING ("%s", (*error)->message);
4834         return conn;
4835 }
4836
4837 NMConnection *
4838 connection_from_file_test (const char *filename,
4839                            const char *network_file,
4840                            const char *test_type,
4841                            char **out_unhandled,
4842                            GError **error)
4843 {
4844         return connection_from_file_full (filename,
4845                                           network_file,
4846                                           test_type,
4847                                           out_unhandled,
4848                                           error,
4849                                           NULL);
4850 }
4851
4852 guint
4853 devtimeout_from_file (const char *filename)
4854 {
4855         shvarFile *ifcfg;
4856         char *devtimeout_str;
4857         guint devtimeout;
4858
4859         g_return_val_if_fail (filename != NULL, 0);
4860
4861         ifcfg = svOpenFile (filename, NULL);
4862         if (!ifcfg)
4863                 return 0;
4864
4865         devtimeout_str = svGetValue (ifcfg, "DEVTIMEOUT", FALSE);
4866         if (devtimeout_str) {
4867                 devtimeout = nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
4868                 g_free (devtimeout_str);
4869         } else
4870                 devtimeout = 0;
4871
4872         return devtimeout;
4873 }