ifcfg-rh: allow handling complex routing rules via dispatcher (rh #1160013)
[NetworkManager.git] / src / settings / plugins / ifcfg-rh / writer.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager system settings service - keyfile plugin
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 2009 - 2014 Red Hat, Inc.
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <arpa/inet.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <stdio.h>
32
33 #include <nm-setting-connection.h>
34 #include <nm-setting-wired.h>
35 #include <nm-setting-wireless.h>
36 #include <nm-setting-8021x.h>
37 #include <nm-setting-ip4-config.h>
38 #include <nm-setting-ip6-config.h>
39 #include <nm-setting-pppoe.h>
40 #include <nm-setting-vlan.h>
41 #include <nm-setting-team.h>
42 #include <nm-setting-team-port.h>
43 #include "nm-core-internal.h"
44 #include <nm-utils.h>
45
46 #include "nm-logging.h"
47 #include "common.h"
48 #include "shvar.h"
49 #include "reader.h"
50 #include "writer.h"
51 #include "utils.h"
52 #include "crypto.h"
53
54
55 static void
56 save_secret_flags (shvarFile *ifcfg,
57                    const char *key,
58                    NMSettingSecretFlags flags)
59 {
60         GString *str;
61
62         g_return_if_fail (ifcfg != NULL);
63         g_return_if_fail (key != NULL);
64
65         if (flags == NM_SETTING_SECRET_FLAG_NONE) {
66                 svSetValue (ifcfg, key, NULL, FALSE);
67                 return;
68         }
69
70         /* Convert flags bitfield into string representation */
71         str = g_string_sized_new (20);
72         if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED)
73                 g_string_append (str, SECRET_FLAG_AGENT);
74
75         if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) {
76                 if (str->len)
77                         g_string_append_c (str, ' ');
78                 g_string_append (str, SECRET_FLAG_NOT_SAVED);
79         }
80
81         if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) {
82                 if (str->len)
83                         g_string_append_c (str, ' ');
84                 g_string_append (str, SECRET_FLAG_NOT_REQUIRED);
85         }
86
87         svSetValue (ifcfg, key, str->len ? str->str : NULL, FALSE);
88         g_string_free (str, TRUE);
89 }
90
91 static void
92 set_secret (shvarFile *ifcfg,
93             const char *key,
94             const char *value,
95             const char *flags_key,
96             NMSettingSecretFlags flags,
97             gboolean verbatim)
98 {
99         shvarFile *keyfile;
100         GError *error = NULL;
101         
102         /* Clear the secret from the ifcfg and the associated "keys" file */
103         svSetValue (ifcfg, key, NULL, FALSE);
104
105         /* Save secret flags */
106         save_secret_flags (ifcfg, flags_key, flags);
107
108         keyfile = utils_get_keys_ifcfg (ifcfg->fileName, TRUE);
109         if (!keyfile) {
110                 nm_log_warn (LOGD_SETTINGS, "    could not create ifcfg file for '%s'", ifcfg->fileName);
111                 goto error;
112         }
113
114         /* Clear the secret from the associated "keys" file */
115         svSetValue (keyfile, key, NULL, FALSE);
116
117         /* Only write the secret if it's system owned and supposed to be saved */
118         if (flags == NM_SETTING_SECRET_FLAG_NONE)
119                 svSetValue (keyfile, key, value, verbatim);
120
121         if (!svWriteFile (keyfile, 0600, &error)) {
122                 nm_log_warn (LOGD_SETTINGS, "    could not update ifcfg file '%s': %s",
123                              keyfile->fileName, error->message);
124                 g_clear_error (&error);
125                 svCloseFile (keyfile);
126                 goto error;
127         }
128         svCloseFile (keyfile);
129         return;
130
131 error:
132         /* Try setting the secret in the actual ifcfg */
133         svSetValue (ifcfg, key, value, FALSE);
134 }
135
136 static gboolean
137 write_secret_file (const char *path,
138                    const char *data,
139                    gsize len,
140                    GError **error)
141 {
142         char *tmppath;
143         int fd = -1, written;
144         gboolean success = FALSE;
145
146         tmppath = g_malloc0 (strlen (path) + 10);
147         memcpy (tmppath, path, strlen (path));
148         strcat (tmppath, ".XXXXXX");
149
150         errno = 0;
151         fd = mkstemp (tmppath);
152         if (fd < 0) {
153                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
154                              "Could not create temporary file for '%s': %d",
155                              path, errno);
156                 goto out;
157         }
158
159         /* Only readable by root */
160         errno = 0;
161         if (fchmod (fd, S_IRUSR | S_IWUSR)) {
162                 close (fd);
163                 unlink (tmppath);
164                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
165                              "Could not set permissions for temporary file '%s': %d",
166                              path, errno);
167                 goto out;
168         }
169
170         errno = 0;
171         written = write (fd, data, len);
172         if (written != len) {
173                 close (fd);
174                 unlink (tmppath);
175                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
176                              "Could not write temporary file for '%s': %d",
177                              path, errno);
178                 goto out;
179         }
180         close (fd);
181
182         /* Try to rename */
183         errno = 0;
184         if (rename (tmppath, path)) {
185                 unlink (tmppath);
186                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
187                              "Could not rename temporary file to '%s': %d",
188                              path, errno);
189                 goto out;
190         }
191         success = TRUE;
192
193 out:
194         g_free (tmppath);
195         return success;
196 }
197
198 typedef struct ObjectType {
199         const char *setting_key;
200         NMSetting8021xCKScheme (*scheme_func)(NMSetting8021x *setting);
201         const char *           (*path_func)  (NMSetting8021x *setting);
202         GBytes *               (*blob_func)  (NMSetting8021x *setting);
203         const char *ifcfg_key;
204         const char *suffix;
205 } ObjectType;
206
207 static const ObjectType ca_type = {
208         NM_SETTING_802_1X_CA_CERT,
209         nm_setting_802_1x_get_ca_cert_scheme,
210         nm_setting_802_1x_get_ca_cert_path,
211         nm_setting_802_1x_get_ca_cert_blob,
212         "IEEE_8021X_CA_CERT",
213         "ca-cert.der"
214 };
215
216 static const ObjectType phase2_ca_type = {
217         NM_SETTING_802_1X_PHASE2_CA_CERT,
218         nm_setting_802_1x_get_phase2_ca_cert_scheme,
219         nm_setting_802_1x_get_phase2_ca_cert_path,
220         nm_setting_802_1x_get_phase2_ca_cert_blob,
221         "IEEE_8021X_INNER_CA_CERT",
222         "inner-ca-cert.der"
223 };
224
225 static const ObjectType client_type = {
226         NM_SETTING_802_1X_CLIENT_CERT,
227         nm_setting_802_1x_get_client_cert_scheme,
228         nm_setting_802_1x_get_client_cert_path,
229         nm_setting_802_1x_get_client_cert_blob,
230         "IEEE_8021X_CLIENT_CERT",
231         "client-cert.der"
232 };
233
234 static const ObjectType phase2_client_type = {
235         NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
236         nm_setting_802_1x_get_phase2_client_cert_scheme,
237         nm_setting_802_1x_get_phase2_client_cert_path,
238         nm_setting_802_1x_get_phase2_client_cert_blob,
239         "IEEE_8021X_INNER_CLIENT_CERT",
240         "inner-client-cert.der"
241 };
242
243 static const ObjectType pk_type = {
244         NM_SETTING_802_1X_PRIVATE_KEY,
245         nm_setting_802_1x_get_private_key_scheme,
246         nm_setting_802_1x_get_private_key_path,
247         nm_setting_802_1x_get_private_key_blob,
248         "IEEE_8021X_PRIVATE_KEY",
249         "private-key.pem"
250 };
251
252 static const ObjectType phase2_pk_type = {
253         NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
254         nm_setting_802_1x_get_phase2_private_key_scheme,
255         nm_setting_802_1x_get_phase2_private_key_path,
256         nm_setting_802_1x_get_phase2_private_key_blob,
257         "IEEE_8021X_INNER_PRIVATE_KEY",
258         "inner-private-key.pem"
259 };
260
261 static const ObjectType p12_type = {
262         NM_SETTING_802_1X_PRIVATE_KEY,
263         nm_setting_802_1x_get_private_key_scheme,
264         nm_setting_802_1x_get_private_key_path,
265         nm_setting_802_1x_get_private_key_blob,
266         "IEEE_8021X_PRIVATE_KEY",
267         "private-key.p12"
268 };
269
270 static const ObjectType phase2_p12_type = {
271         NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
272         nm_setting_802_1x_get_phase2_private_key_scheme,
273         nm_setting_802_1x_get_phase2_private_key_path,
274         nm_setting_802_1x_get_phase2_private_key_blob,
275         "IEEE_8021X_INNER_PRIVATE_KEY",
276         "inner-private-key.p12"
277 };
278
279 static gboolean
280 write_object (NMSetting8021x *s_8021x,
281               shvarFile *ifcfg,
282               const ObjectType *objtype,
283               GError **error)
284 {
285         NMSetting8021xCKScheme scheme;
286         const char *path = NULL;
287         GBytes *blob = NULL;
288
289         g_return_val_if_fail (ifcfg != NULL, FALSE);
290         g_return_val_if_fail (objtype != NULL, FALSE);
291
292         scheme = (*(objtype->scheme_func))(s_8021x);
293         switch (scheme) {
294         case NM_SETTING_802_1X_CK_SCHEME_BLOB:
295                 blob = (*(objtype->blob_func))(s_8021x);
296                 break;
297         case NM_SETTING_802_1X_CK_SCHEME_PATH:
298                 path = (*(objtype->path_func))(s_8021x);
299                 break;
300         default:
301                 break;
302         }
303
304         /* If certificate/private key wasn't sent, the connection may no longer be
305          * 802.1x and thus we clear out the paths and certs.
306          */
307         if (!path && !blob) {
308                 char *standard_file;
309                 int ignored;
310
311                 /* Since no cert/private key is now being used, delete any standard file
312                  * that was created for this connection, but leave other files alone.
313                  * Thus, for example,
314                  * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der
315                  * will be deleted, but /etc/pki/tls/cert.pem will not.
316                  */
317                 standard_file = utils_cert_path (ifcfg->fileName, objtype->suffix);
318                 if (g_file_test (standard_file, G_FILE_TEST_EXISTS))
319                         ignored = unlink (standard_file);
320                 g_free (standard_file);
321
322                 svSetValue (ifcfg, objtype->ifcfg_key, NULL, FALSE);
323                 return TRUE;
324         }
325
326         /* If the object path was specified, prefer that over any raw cert data that
327          * may have been sent.
328          */
329         if (path) {
330                 svSetValue (ifcfg, objtype->ifcfg_key, path, FALSE);
331                 return TRUE;
332         }
333
334         /* If it's raw certificate data, write the data out to the standard file */
335         if (blob) {
336                 gboolean success;
337                 char *new_file;
338                 GError *write_error = NULL;
339
340                 new_file = utils_cert_path (ifcfg->fileName, objtype->suffix);
341                 if (!new_file) {
342                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
343                                      "Could not create file path for %s / %s",
344                                      NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key);
345                         return FALSE;
346                 }
347
348                 /* Write the raw certificate data out to the standard file so that we
349                  * can use paths from now on instead of pushing around the certificate
350                  * data itself.
351                  */
352                 success = write_secret_file (new_file,
353                                              (const char *) g_bytes_get_data (blob, NULL),
354                                              g_bytes_get_size (blob),
355                                              &write_error);
356                 if (success) {
357                         svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE);
358                         g_free (new_file);
359                         return TRUE;
360                 } else {
361                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
362                                      "Could not write certificate/key for %s / %s: %s",
363                                      NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key,
364                                      (write_error && write_error->message) ? write_error->message : "(unknown)");
365                         g_clear_error (&write_error);
366                 }
367                 g_free (new_file);
368         }
369
370         return FALSE;
371 }
372
373 static gboolean
374 write_8021x_certs (NMSetting8021x *s_8021x,
375                    gboolean phase2,
376                    shvarFile *ifcfg,
377                    GError **error)
378 {
379         const char *password = NULL;
380         gboolean success = FALSE, is_pkcs12 = FALSE;
381         const ObjectType *otype = NULL;
382         NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
383
384         /* CA certificate */
385         if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_ca_type : &ca_type, error))
386                 return FALSE;
387
388         /* Private key */
389         if (phase2) {
390                 otype = &phase2_pk_type;
391                 if (nm_setting_802_1x_get_phase2_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
392                         otype = &phase2_p12_type;
393                         is_pkcs12 = TRUE;
394                 }
395                 password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
396                 flags = nm_setting_802_1x_get_phase2_private_key_password_flags (s_8021x);
397         } else {
398                 otype = &pk_type;
399                 if (nm_setting_802_1x_get_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
400                         otype = &p12_type;
401                         is_pkcs12 = TRUE;
402                 }
403                 password = nm_setting_802_1x_get_private_key_password (s_8021x);
404                 flags = nm_setting_802_1x_get_private_key_password_flags (s_8021x);
405         }
406
407         /* Save the private key */
408         if (!write_object (s_8021x, ifcfg, otype, error))
409                 goto out;
410
411         /* Private key password */
412         if (phase2) {
413                 set_secret (ifcfg,
414                             "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD",
415                             password,
416                             "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS",
417                             flags,
418                             FALSE);
419         } else {
420                 set_secret (ifcfg,
421                             "IEEE_8021X_PRIVATE_KEY_PASSWORD",
422                             password,
423                             "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS",
424                             flags,
425                             FALSE);
426         }
427
428         /* Client certificate */
429         if (is_pkcs12) {
430                 /* Don't need a client certificate with PKCS#12 since the file is both
431                  * the client certificate and the private key in one file.
432                  */
433                 svSetValue (ifcfg,
434                             phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
435                             NULL, FALSE);
436         } else {
437                 /* Save the client certificate */
438                 if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_client_type : &client_type, error))
439                         goto out;
440         }
441
442         success = TRUE;
443
444 out:
445         return success;
446 }
447
448 static gboolean
449 write_8021x_setting (NMConnection *connection,
450                      shvarFile *ifcfg,
451                      gboolean wired,
452                      GError **error)
453 {
454         NMSetting8021x *s_8021x;
455         const char *value, *match;
456         char *tmp = NULL;
457         gboolean success = FALSE;
458         GString *phase2_auth;
459         GString *str;
460         guint32 i, num;
461
462         s_8021x = nm_connection_get_setting_802_1x (connection);
463         if (!s_8021x) {
464                 /* If wired, clear KEY_MGMT */
465                 if (wired)
466                         svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE);
467                 return TRUE;
468         }
469
470         /* If wired, write KEY_MGMT */
471         if (wired)
472                 svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE);
473
474         /* EAP method */
475         if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) {
476                 value = nm_setting_802_1x_get_eap_method (s_8021x, 0);
477                 if (value)
478                         tmp = g_ascii_strup (value, -1);
479         }
480         svSetValue (ifcfg, "IEEE_8021X_EAP_METHODS", tmp ? tmp : NULL, FALSE);
481         g_free (tmp);
482
483         svSetValue (ifcfg, "IEEE_8021X_IDENTITY",
484                     nm_setting_802_1x_get_identity (s_8021x),
485                     FALSE);
486
487         svSetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY",
488                     nm_setting_802_1x_get_anonymous_identity (s_8021x),
489                     FALSE);
490
491         set_secret (ifcfg,
492                     "IEEE_8021X_PASSWORD",
493                     nm_setting_802_1x_get_password (s_8021x),
494                     "IEEE_8021X_PASSWORD_FLAGS",
495                     nm_setting_802_1x_get_password_flags (s_8021x),
496                     FALSE);
497
498         /* PEAP version */
499         value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
500         svSetValue (ifcfg, "IEEE_8021X_PEAP_VERSION", NULL, FALSE);
501         if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
502                 svSetValue (ifcfg, "IEEE_8021X_PEAP_VERSION", value, FALSE);
503
504         /* Force new PEAP label */
505         value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
506         svSetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", NULL, FALSE);
507         if (value && !strcmp (value, "1"))
508                 svSetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes", FALSE);
509
510         /* PAC file */
511         value = nm_setting_802_1x_get_pac_file (s_8021x);
512         svSetValue (ifcfg, "IEEE_8021X_PAC_FILE", NULL, FALSE);
513         if (value)
514                 svSetValue (ifcfg, "IEEE_8021X_PAC_FILE", value, FALSE);
515
516         /* FAST PAC provisioning */
517         value = nm_setting_802_1x_get_phase1_fast_provisioning (s_8021x);
518         svSetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING", NULL, FALSE);
519         if (value) {
520                 if (strcmp (value, "1") == 0)
521                         svSetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth", FALSE);
522                 else if (strcmp (value, "2") == 0)
523                         svSetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth", FALSE);
524                 else if (strcmp (value, "3") == 0)
525                         svSetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth", FALSE);
526         }
527
528         /* Phase2 auth methods */
529         svSetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", NULL, FALSE);
530         phase2_auth = g_string_new (NULL);
531
532         value = nm_setting_802_1x_get_phase2_auth (s_8021x);
533         if (value) {
534                 tmp = g_ascii_strup (value, -1);
535                 g_string_append (phase2_auth, tmp);
536                 g_free (tmp);
537         }
538
539         value = nm_setting_802_1x_get_phase2_autheap (s_8021x);
540         if (value) {
541                 if (phase2_auth->len)
542                         g_string_append_c (phase2_auth, ' ');
543
544                 tmp = g_ascii_strup (value, -1);
545                 g_string_append_printf (phase2_auth, "EAP-%s", tmp);
546                 g_free (tmp);
547         }
548
549         svSetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS",
550                     phase2_auth->len ? phase2_auth->str : NULL,
551                     FALSE);
552
553         g_string_free (phase2_auth, TRUE);
554
555         svSetValue (ifcfg, "IEEE_8021X_SUBJECT_MATCH",
556                     nm_setting_802_1x_get_subject_match (s_8021x),
557                     FALSE);
558
559         svSetValue (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
560                     nm_setting_802_1x_get_phase2_subject_match (s_8021x),
561                     FALSE);
562
563         svSetValue (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", NULL, FALSE);
564         str = g_string_new (NULL);
565         num = nm_setting_802_1x_get_num_altsubject_matches (s_8021x);
566         for (i = 0; i < num; i++) {
567                 if (i > 0)
568                         g_string_append_c (str, ' ');
569                 match = nm_setting_802_1x_get_altsubject_match (s_8021x, i);
570                 g_string_append (str, match);
571         }
572         if (str->len > 0)
573                 svSetValue (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str, FALSE);
574         g_string_free (str, TRUE);
575
576         svSetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", NULL, FALSE);
577         str = g_string_new (NULL);
578         num = nm_setting_802_1x_get_num_phase2_altsubject_matches (s_8021x);
579         for (i = 0; i < num; i++) {
580                 if (i > 0)
581                         g_string_append_c (str, ' ');
582                 match = nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, i);
583                 g_string_append (str, match);
584         }
585         if (str->len > 0)
586                 svSetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str, FALSE);
587         g_string_free (str, TRUE);
588
589         success = write_8021x_certs (s_8021x, FALSE, ifcfg, error);
590         if (success) {
591                 /* phase2/inner certs */
592                 success = write_8021x_certs (s_8021x, TRUE, ifcfg, error);
593         }
594
595         return success;
596 }
597
598 static gboolean
599 write_wireless_security_setting (NMConnection *connection,
600                                  shvarFile *ifcfg,
601                                  gboolean adhoc,
602                                  gboolean *no_8021x,
603                                  GError **error)
604 {
605         NMSettingWirelessSecurity *s_wsec;
606         const char *key_mgmt, *auth_alg, *key, *proto, *cipher, *psk;
607         gboolean wep = FALSE, wpa = FALSE, dynamic_wep = FALSE;
608         char *tmp;
609         guint32 i, num;
610         GString *str;
611
612         s_wsec = nm_connection_get_setting_wireless_security (connection);
613         if (!s_wsec) {
614                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
615                              "Missing '%s' setting", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
616                 return FALSE;
617         }
618
619         key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
620         g_assert (key_mgmt);
621
622         auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
623
624         svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE);
625
626         if (!strcmp (key_mgmt, "none")) {
627                 svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE);
628                 wep = TRUE;
629                 *no_8021x = TRUE;
630         } else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
631                 svSetValue (ifcfg, "KEY_MGMT", "WPA-PSK", FALSE);
632                 wpa = TRUE;
633                 *no_8021x = TRUE;
634         } else if (!strcmp (key_mgmt, "ieee8021x")) {
635                 svSetValue (ifcfg, "KEY_MGMT", "IEEE8021X", FALSE);
636                 dynamic_wep = TRUE;
637         } else if (!strcmp (key_mgmt, "wpa-eap")) {
638                 svSetValue (ifcfg, "KEY_MGMT", "WPA-EAP", FALSE);
639                 wpa = TRUE;
640         }
641
642         svSetValue (ifcfg, "SECURITYMODE", NULL, FALSE);
643         if (auth_alg) {
644                 if (!strcmp (auth_alg, "shared"))
645                         svSetValue (ifcfg, "SECURITYMODE", "restricted", FALSE);
646                 else if (!strcmp (auth_alg, "open"))
647                         svSetValue (ifcfg, "SECURITYMODE", "open", FALSE);
648                 else if (!strcmp (auth_alg, "leap")) {
649                         svSetValue (ifcfg, "SECURITYMODE", "leap", FALSE);
650                         svSetValue (ifcfg, "IEEE_8021X_IDENTITY",
651                                     nm_setting_wireless_security_get_leap_username (s_wsec),
652                                     FALSE);
653                         set_secret (ifcfg,
654                                     "IEEE_8021X_PASSWORD",
655                                     nm_setting_wireless_security_get_leap_password (s_wsec),
656                                     "IEEE_8021X_PASSWORD_FLAGS",
657                                     nm_setting_wireless_security_get_leap_password_flags (s_wsec),
658                                     FALSE);
659                         *no_8021x = TRUE;
660                 }
661         }
662
663         /* WEP keys */
664
665         /* Clear any default key */
666         set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
667
668         /* Clear existing keys */
669         for (i = 0; i < 4; i++) {
670                 tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1);
671                 set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
672                 g_free (tmp);
673
674                 tmp = g_strdup_printf ("KEY%d", i + 1);
675                 set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
676                 g_free (tmp);
677         }
678
679         /* And write the new ones out */
680         if (wep) {
681                 /* Default WEP TX key index */
682                 tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1);
683                 svSetValue (ifcfg, "DEFAULTKEY", tmp, FALSE);
684                 g_free (tmp);
685
686                 for (i = 0; i < 4; i++) {
687                         NMWepKeyType key_type;
688
689                         key = nm_setting_wireless_security_get_wep_key (s_wsec, i);
690                         if (key) {
691                                 char *ascii_key = NULL;
692
693                                 /* Passphrase needs a different ifcfg key since with WEP, there
694                                  * are some passphrases that are indistinguishable from WEP hex
695                                  * keys.
696                                  */
697                                 key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec);
698                                 if (key_type == NM_WEP_KEY_TYPE_PASSPHRASE)
699                                         tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1);
700                                 else {
701                                         tmp = g_strdup_printf ("KEY%d", i + 1);
702
703                                         /* Add 's:' prefix for ASCII keys */
704                                         if (strlen (key) == 5 || strlen (key) == 13) {
705                                                 ascii_key = g_strdup_printf ("s:%s", key);
706                                                 key = ascii_key;
707                                         }
708                                 }
709
710                                 set_secret (ifcfg,
711                                             tmp,
712                                             key,
713                                             "WEP_KEY_FLAGS",
714                                             nm_setting_wireless_security_get_wep_key_flags (s_wsec),
715                                             FALSE);
716                                 g_free (tmp);
717                                 g_free (ascii_key);
718                         }
719                 }
720         }
721
722         /* WPA protos */
723         svSetValue (ifcfg, "WPA_ALLOW_WPA", NULL, FALSE);
724         svSetValue (ifcfg, "WPA_ALLOW_WPA2", NULL, FALSE);
725         num = nm_setting_wireless_security_get_num_protos (s_wsec);
726         for (i = 0; i < num; i++) {
727                 proto = nm_setting_wireless_security_get_proto (s_wsec, i);
728                 if (proto && !strcmp (proto, "wpa"))
729                         svSetValue (ifcfg, "WPA_ALLOW_WPA", "yes", FALSE);
730                 else if (proto && !strcmp (proto, "rsn"))
731                         svSetValue (ifcfg, "WPA_ALLOW_WPA2", "yes", FALSE);
732         }
733
734         /* WPA Pairwise ciphers */
735         svSetValue (ifcfg, "CIPHER_PAIRWISE", NULL, FALSE);
736         str = g_string_new (NULL);
737         num = nm_setting_wireless_security_get_num_pairwise (s_wsec);
738         for (i = 0; i < num; i++) {
739                 if (i > 0)
740                         g_string_append_c (str, ' ');
741                 cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i);
742
743                 /* Don't write out WEP40 or WEP104 if for some reason they are set; they
744                  * are not valid pairwise ciphers.
745                  */
746                 if (strcmp (cipher, "wep40") && strcmp (cipher, "wep104")) {
747                         tmp = g_ascii_strup (cipher, -1);
748                         g_string_append (str, tmp);
749                         g_free (tmp);
750                 }
751         }
752         if (strlen (str->str) && (dynamic_wep == FALSE))
753                 svSetValue (ifcfg, "CIPHER_PAIRWISE", str->str, FALSE);
754         g_string_free (str, TRUE);
755
756         /* WPA Group ciphers */
757         svSetValue (ifcfg, "CIPHER_GROUP", NULL, FALSE);
758         str = g_string_new (NULL);
759         num = nm_setting_wireless_security_get_num_groups (s_wsec);
760         for (i = 0; i < num; i++) {
761                 if (i > 0)
762                         g_string_append_c (str, ' ');
763                 cipher = nm_setting_wireless_security_get_group (s_wsec, i);
764                 tmp = g_ascii_strup (cipher, -1);
765                 g_string_append (str, tmp);
766                 g_free (tmp);
767         }
768         if (strlen (str->str) && (dynamic_wep == FALSE))
769                 svSetValue (ifcfg, "CIPHER_GROUP", str->str, FALSE);
770         g_string_free (str, TRUE);
771
772         /* WPA Passphrase */
773         if (wpa) {
774                 char *quoted = NULL;
775
776                 psk = nm_setting_wireless_security_get_psk (s_wsec);
777                 if (psk && (strlen (psk) != 64)) {
778                         /* Quote the PSK since it's a passphrase */
779                         quoted = utils_single_quote_string (psk);
780                 }
781                 set_secret (ifcfg,
782                             "WPA_PSK",
783                             quoted ? quoted : psk,
784                             "WPA_PSK_FLAGS",
785                             nm_setting_wireless_security_get_psk_flags (s_wsec),
786                             TRUE);
787                 g_free (quoted);
788         } else {
789                 set_secret (ifcfg,
790                             "WPA_PSK",
791                             NULL,
792                             "WPA_PSK_FLAGS",
793                             NM_SETTING_SECRET_FLAG_NONE,
794                             FALSE);
795         }
796
797         return TRUE;
798 }
799
800 static gboolean
801 write_wireless_setting (NMConnection *connection,
802                         shvarFile *ifcfg,
803                         gboolean *no_8021x,
804                         GError **error)
805 {
806         NMSettingWireless *s_wireless;
807         char *tmp, *tmp2;
808         GBytes *ssid;
809         const guint8 *ssid_data;
810         gsize ssid_len;
811         const char *mode, *bssid;
812         const char *device_mac, *cloned_mac;
813         char buf[33];
814         guint32 mtu, chan, i;
815         gboolean adhoc = FALSE, hex_ssid = FALSE;
816         const char * const *macaddr_blacklist;
817
818         s_wireless = nm_connection_get_setting_wireless (connection);
819         if (!s_wireless) {
820                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
821                              "Missing '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
822                 return FALSE;
823         }
824
825         device_mac = nm_setting_wireless_get_mac_address (s_wireless);
826         svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
827
828         cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
829         svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
830
831         svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
832         macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
833         if (macaddr_blacklist[0]) {
834                 char *blacklist_str;
835
836                 blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
837                 svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str, FALSE);
838                 g_free (blacklist_str);
839         }
840
841         svSetValue (ifcfg, "MTU", NULL, FALSE);
842         mtu = nm_setting_wireless_get_mtu (s_wireless);
843         if (mtu) {
844                 tmp = g_strdup_printf ("%u", mtu);
845                 svSetValue (ifcfg, "MTU", tmp, FALSE);
846                 g_free (tmp);
847         }
848
849         ssid = nm_setting_wireless_get_ssid (s_wireless);
850         if (!ssid) {
851                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
852                              "Missing SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
853                 return FALSE;
854         }
855         ssid_data = g_bytes_get_data (ssid, &ssid_len);
856         if (!ssid_len || ssid_len > 32) {
857                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
858                              "Invalid SSID in '%s' setting", NM_SETTING_WIRELESS_SETTING_NAME);
859                 return FALSE;
860         }
861
862         /* If the SSID contains any non-printable characters, we need to use the
863          * hex notation of the SSID instead.
864          */
865         for (i = 0; i < ssid_len; i++) {
866                 if (!g_ascii_isprint (ssid_data[i])) {
867                         hex_ssid = TRUE;
868                         break;
869                 }
870         }
871
872         if (hex_ssid) {
873                 GString *str;
874
875                 /* Hex SSIDs don't get quoted */
876                 str = g_string_sized_new (ssid_len * 2 + 3);
877                 g_string_append (str, "0x");
878                 for (i = 0; i < ssid_len; i++)
879                         g_string_append_printf (str, "%02X", ssid_data[i]);
880                 svSetValue (ifcfg, "ESSID", str->str, TRUE);
881                 g_string_free (str, TRUE);
882         } else {
883                 /* Printable SSIDs always get quoted */
884                 memset (buf, 0, sizeof (buf));
885                 memcpy (buf, ssid_data, ssid_len);
886                 tmp = svEscape (buf);
887
888                 /* svEscape will usually quote the string, but just for consistency,
889                  * if svEscape doesn't quote the ESSID, we quote it ourselves.
890                  */
891                 if (tmp[0] != '"' && tmp[strlen (tmp) - 1] != '"') {
892                         tmp2 = g_strdup_printf ("\"%s\"", tmp);
893                         svSetValue (ifcfg, "ESSID", tmp2, TRUE);
894                         g_free (tmp2);
895                 } else
896                         svSetValue (ifcfg, "ESSID", tmp, TRUE);
897                 g_free (tmp);
898         }
899
900         mode = nm_setting_wireless_get_mode (s_wireless);
901         if (!mode || !strcmp (mode, "infrastructure")) {
902                 svSetValue (ifcfg, "MODE", "Managed", FALSE);
903         } else if (!strcmp (mode, "adhoc")) {
904                 svSetValue (ifcfg, "MODE", "Ad-Hoc", FALSE);
905                 adhoc = TRUE;
906         } else if (!strcmp (mode, "ap")) {
907                 svSetValue (ifcfg, "MODE", "Ap", FALSE);
908         } else {
909                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
910                              "Invalid mode '%s' in '%s' setting",
911                              mode, NM_SETTING_WIRELESS_SETTING_NAME);
912                 return FALSE;
913         }
914
915         svSetValue (ifcfg, "CHANNEL", NULL, FALSE);
916         svSetValue (ifcfg, "BAND", NULL, FALSE);
917         chan = nm_setting_wireless_get_channel (s_wireless);
918         if (chan) {
919                 tmp = g_strdup_printf ("%u", chan);
920                 svSetValue (ifcfg, "CHANNEL", tmp, FALSE);
921                 g_free (tmp);
922         } else {
923                 /* Band only set if channel is not, since channel implies band */
924                 svSetValue (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless), FALSE);
925         }
926
927         bssid = nm_setting_wireless_get_bssid (s_wireless);
928         svSetValue (ifcfg, "BSSID", bssid, FALSE);
929
930         /* Ensure DEFAULTKEY and SECURITYMODE are cleared unless there's security;
931          * otherwise there's no way to detect WEP vs. open when WEP keys aren't
932          * saved.
933          */
934         svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE);
935         svSetValue (ifcfg, "SECURITYMODE", NULL, FALSE);
936
937         if (nm_connection_get_setting_wireless_security (connection)) {
938                 if (!write_wireless_security_setting (connection, ifcfg, adhoc, no_8021x, error))
939                         return FALSE;
940         } else {
941                 char *keys_path;
942
943                 /* Clear out wifi security keys */
944                 svSetValue (ifcfg, "KEY_MGMT", NULL, FALSE);
945                 svSetValue (ifcfg, "IEEE_8021X_IDENTITY", NULL, FALSE);
946                 set_secret (ifcfg, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
947                 svSetValue (ifcfg, "SECURITYMODE", NULL, FALSE);
948
949                 /* Clear existing keys */
950                 set_secret (ifcfg, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
951                 for (i = 0; i < 4; i++) {
952                         tmp = g_strdup_printf ("KEY_PASSPHRASE%d", i + 1);
953                         set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
954                         g_free (tmp);
955         
956                         tmp = g_strdup_printf ("KEY%d", i + 1);
957                         set_secret (ifcfg, tmp, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
958                         g_free (tmp);
959                 }
960
961                 svSetValue (ifcfg, "DEFAULTKEY", NULL, FALSE);
962                 svSetValue (ifcfg, "WPA_ALLOW_WPA", NULL, FALSE);
963                 svSetValue (ifcfg, "WPA_ALLOW_WPA2", NULL, FALSE);
964                 svSetValue (ifcfg, "CIPHER_PAIRWISE", NULL, FALSE);
965                 svSetValue (ifcfg, "CIPHER_GROUP", NULL, FALSE);
966                 set_secret (ifcfg, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE, FALSE);
967
968                 /* Kill any old keys file */
969                 keys_path = utils_get_keys_path (ifcfg->fileName);
970                 (void) unlink (keys_path);
971                 g_free (keys_path);
972         }
973
974         svSetValue (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL, TRUE);
975
976         svSetValue (ifcfg, "TYPE", TYPE_WIRELESS, FALSE);
977
978         return TRUE;
979 }
980
981 static gboolean
982 write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
983 {
984         NMSettingInfiniband *s_infiniband;
985         char *tmp;
986         const char *mac, *transport_mode, *parent;
987         guint32 mtu;
988         int p_key;
989
990         s_infiniband = nm_connection_get_setting_infiniband (connection);
991         if (!s_infiniband) {
992                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
993                              "Missing '%s' setting", NM_SETTING_INFINIBAND_SETTING_NAME);
994                 return FALSE;
995         }
996
997         mac = nm_setting_infiniband_get_mac_address (s_infiniband);
998         svSetValue (ifcfg, "HWADDR", mac, FALSE);
999
1000         svSetValue (ifcfg, "MTU", NULL, FALSE);
1001         mtu = nm_setting_infiniband_get_mtu (s_infiniband);
1002         if (mtu) {
1003                 tmp = g_strdup_printf ("%u", mtu);
1004                 svSetValue (ifcfg, "MTU", tmp, FALSE);
1005                 g_free (tmp);
1006         }
1007
1008         transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband);
1009         svSetValue (ifcfg, "CONNECTED_MODE",
1010                     strcmp (transport_mode, "connected") == 0 ? "yes" : "no",
1011                     FALSE);
1012
1013         p_key = nm_setting_infiniband_get_p_key (s_infiniband);
1014         if (p_key != -1) {
1015                 svSetValue (ifcfg, "PKEY", "yes", FALSE);
1016                 tmp = g_strdup_printf ("%u", p_key);
1017                 svSetValue (ifcfg, "PKEY_ID", tmp, FALSE);
1018                 g_free (tmp);
1019
1020                 parent = nm_setting_infiniband_get_parent (s_infiniband);
1021                 if (parent)
1022                         svSetValue (ifcfg, "PHYSDEV", parent, FALSE);
1023         }
1024
1025         svSetValue (ifcfg, "TYPE", TYPE_INFINIBAND, FALSE);
1026
1027         return TRUE;
1028 }
1029
1030 static gboolean
1031 write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1032 {
1033         NMSettingWired *s_wired;
1034         const char *device_mac, *cloned_mac;
1035         char *tmp;
1036         const char *nettype, *portname, *ctcprot, *s390_key, *s390_val;
1037         guint32 mtu, num_opts, i;
1038         const char *const *s390_subchannels;
1039         GString *str;
1040         const char * const *macaddr_blacklist;
1041
1042         s_wired = nm_connection_get_setting_wired (connection);
1043         if (!s_wired) {
1044                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1045                              "Missing '%s' setting", NM_SETTING_WIRED_SETTING_NAME);
1046                 return FALSE;
1047         }
1048
1049         device_mac = nm_setting_wired_get_mac_address (s_wired);
1050         svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
1051
1052         cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
1053         svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
1054
1055         svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
1056         macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
1057         if (macaddr_blacklist[0]) {
1058                 char *blacklist_str;
1059
1060                 blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
1061                 svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str, FALSE);
1062                 g_free (blacklist_str);
1063         }
1064
1065         svSetValue (ifcfg, "MTU", NULL, FALSE);
1066         mtu = nm_setting_wired_get_mtu (s_wired);
1067         if (mtu) {
1068                 tmp = g_strdup_printf ("%u", mtu);
1069                 svSetValue (ifcfg, "MTU", tmp, FALSE);
1070                 g_free (tmp);
1071         }
1072
1073         svSetValue (ifcfg, "SUBCHANNELS", NULL, FALSE);
1074         s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired);
1075         if (s390_subchannels) {
1076                 int len = g_strv_length ((char **)s390_subchannels);
1077
1078                 tmp = NULL;
1079             if (len == 2) {
1080                     tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
1081             } else if (len == 3) {
1082                     tmp = g_strdup_printf ("%s,%s,%s", s390_subchannels[0], s390_subchannels[1],
1083                                            s390_subchannels[2]);
1084                 }
1085                 svSetValue (ifcfg, "SUBCHANNELS", tmp, FALSE);
1086                 g_free (tmp);
1087         }
1088
1089         svSetValue (ifcfg, "NETTYPE", NULL, FALSE);
1090         nettype = nm_setting_wired_get_s390_nettype (s_wired);
1091         if (nettype)
1092                 svSetValue (ifcfg, "NETTYPE", nettype, FALSE);
1093
1094         svSetValue (ifcfg, "PORTNAME", NULL, FALSE);
1095         portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname");
1096         if (portname)
1097                 svSetValue (ifcfg, "PORTNAME", portname, FALSE);
1098
1099         svSetValue (ifcfg, "CTCPROT", NULL, FALSE);
1100         ctcprot = nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot");
1101         if (ctcprot)
1102                 svSetValue (ifcfg, "CTCPROT", ctcprot, FALSE);
1103
1104         svSetValue (ifcfg, "OPTIONS", NULL, FALSE);
1105         num_opts = nm_setting_wired_get_num_s390_options (s_wired);
1106         if (s390_subchannels && num_opts) {
1107                 str = g_string_sized_new (30);
1108                 for (i = 0; i < num_opts; i++) {
1109                         nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val);
1110
1111                         /* portname is handled separately */
1112                         if (!strcmp (s390_key, "portname") || !strcmp (s390_key, "ctcprot"))
1113                                 continue;
1114
1115                         if (str->len)
1116                                 g_string_append_c (str, ' ');
1117                         g_string_append_printf (str, "%s=%s", s390_key, s390_val);
1118                 }
1119                 if (str->len)
1120                         svSetValue (ifcfg, "OPTIONS", str->str, FALSE);
1121                 g_string_free (str, TRUE);
1122         }
1123
1124         svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE);
1125
1126         return TRUE;
1127 }
1128
1129 static char *
1130 vlan_priority_maplist_to_stringlist (NMSettingVlan *s_vlan, NMVlanPriorityMap map)
1131 {
1132         char **strlist;
1133         char *value;
1134
1135         if (map == NM_VLAN_INGRESS_MAP)
1136                 g_object_get (G_OBJECT (s_vlan), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, &strlist, NULL);
1137         else if (map == NM_VLAN_EGRESS_MAP)
1138                 g_object_get (G_OBJECT (s_vlan), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, &strlist, NULL);
1139         else
1140                 return NULL;
1141
1142         if (strlist[0])
1143                 value = g_strjoinv (",", strlist);
1144         else
1145                 value = NULL;
1146         g_strfreev (strlist);
1147
1148         return value;
1149 }
1150
1151 static gboolean
1152 write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
1153 {
1154         NMSettingVlan *s_vlan;
1155         NMSettingConnection *s_con;
1156         NMSettingWired *s_wired;
1157         char *tmp;
1158         guint32 vlan_flags = 0;
1159
1160         s_con = nm_connection_get_setting_connection (connection);
1161         if (!s_con) {
1162                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1163                                      "Missing connection setting");
1164                 return FALSE;
1165         }
1166
1167         s_vlan = nm_connection_get_setting_vlan (connection);
1168         if (!s_vlan) {
1169                 g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1170                                      "Missing VLAN setting");
1171                 return FALSE;
1172         }
1173
1174         svSetValue (ifcfg, "VLAN", "yes", FALSE);
1175         svSetValue (ifcfg, "TYPE", TYPE_VLAN, FALSE);
1176         svSetValue (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con), FALSE);
1177         svSetValue (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan), FALSE);
1178
1179         tmp = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan));
1180         svSetValue (ifcfg, "VLAN_ID", tmp, FALSE);
1181         g_free (tmp);
1182
1183         vlan_flags = nm_setting_vlan_get_flags (s_vlan);
1184         if (vlan_flags & NM_VLAN_FLAG_REORDER_HEADERS)
1185                 svSetValue (ifcfg, "REORDER_HDR", "1", FALSE);
1186         else
1187                 svSetValue (ifcfg, "REORDER_HDR", "0", FALSE);
1188
1189         svSetValue (ifcfg, "VLAN_FLAGS", NULL, FALSE);
1190         if (vlan_flags & NM_VLAN_FLAG_GVRP) {
1191                 if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING)
1192                         svSetValue (ifcfg, "VLAN_FLAGS", "GVRP,LOOSE_BINDING", FALSE);
1193                 else
1194                         svSetValue (ifcfg, "VLAN_FLAGS", "GVRP", FALSE);
1195         } else if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING)
1196                 svSetValue (ifcfg, "VLAN_FLAGS", "LOOSE_BINDING", FALSE);
1197
1198         tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_INGRESS_MAP);
1199         svSetValue (ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp, FALSE);
1200         g_free (tmp);
1201
1202         tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_EGRESS_MAP);
1203         svSetValue (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp, FALSE);
1204         g_free (tmp);
1205
1206         svSetValue (ifcfg, "HWADDR", NULL, FALSE);
1207         svSetValue (ifcfg, "MACADDR", NULL, FALSE);
1208         svSetValue (ifcfg, "MTU", NULL, FALSE);
1209
1210         s_wired = nm_connection_get_setting_wired (connection);
1211         if (s_wired) {
1212                 const char *device_mac, *cloned_mac;
1213                 guint32 mtu;
1214
1215                 *wired = TRUE;
1216
1217                 device_mac = nm_setting_wired_get_mac_address (s_wired);
1218                 if (device_mac)
1219                         svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
1220
1221                 cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
1222                 if (cloned_mac)
1223                         svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
1224
1225                 mtu = nm_setting_wired_get_mtu (s_wired);
1226                 if (mtu) {
1227                         tmp = g_strdup_printf ("%u", mtu);
1228                         svSetValue (ifcfg, "MTU", tmp, FALSE);
1229                         g_free (tmp);
1230                 }
1231         }
1232
1233         return TRUE;
1234 }
1235
1236 static gboolean
1237 write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1238 {
1239         NMSettingBond *s_bond;
1240         const char *iface;
1241         guint32 i, num_opts;
1242
1243         s_bond = nm_connection_get_setting_bond (connection);
1244         if (!s_bond) {
1245                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1246                              "Missing '%s' setting", NM_SETTING_BOND_SETTING_NAME);
1247                 return FALSE;
1248         }
1249
1250         iface = nm_connection_get_interface_name (connection);
1251         if (!iface) {
1252                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1253                              "Missing interface name");
1254                 return FALSE;
1255         }
1256
1257         svSetValue (ifcfg, "DEVICE", iface, FALSE);
1258         svSetValue (ifcfg, "BONDING_OPTS", NULL, FALSE);
1259
1260         num_opts = nm_setting_bond_get_num_options (s_bond);
1261         if (num_opts > 0) {
1262                 GString *str = g_string_sized_new (64);
1263
1264                 for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) {
1265                         const char *key, *value;
1266
1267                         if (!nm_setting_bond_get_option (s_bond, i, &key, &value))
1268                                 continue;
1269
1270                         if (str->len)
1271                                 g_string_append_c (str, ' ');
1272
1273                         g_string_append_printf (str, "%s=%s", key, value);
1274                 }
1275
1276                 if (str->len)
1277                         svSetValue (ifcfg, "BONDING_OPTS", str->str, FALSE);
1278
1279                 g_string_free (str, TRUE);
1280         }
1281
1282         svSetValue (ifcfg, "TYPE", TYPE_BOND, FALSE);
1283         svSetValue (ifcfg, "BONDING_MASTER", "yes", FALSE);
1284
1285         return TRUE;
1286 }
1287
1288 static gboolean
1289 write_team_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1290 {
1291         NMSettingTeam *s_team;
1292         const char *iface;
1293         const char *config;
1294
1295         s_team = nm_connection_get_setting_team (connection);
1296         if (!s_team) {
1297                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1298                              "Missing '%s' setting", NM_SETTING_TEAM_SETTING_NAME);
1299                 return FALSE;
1300         }
1301
1302         iface = nm_connection_get_interface_name (connection);
1303         if (!iface) {
1304                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1305                              "Missing interface name");
1306                 return FALSE;
1307         }
1308
1309         svSetValue (ifcfg, "DEVICE", iface, FALSE);
1310         config = nm_setting_team_get_config (s_team);
1311         svSetValue (ifcfg, "TEAM_CONFIG", config, FALSE);
1312         svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM, FALSE);
1313
1314         return TRUE;
1315 }
1316
1317 static guint32
1318 get_setting_default (NMSetting *setting, const char *prop)
1319 {
1320         GParamSpec *pspec;
1321         GValue val = G_VALUE_INIT;
1322         guint32 ret = 0;
1323
1324         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), prop);
1325         g_assert (pspec);
1326         g_value_init (&val, pspec->value_type);
1327         g_param_value_set_default (pspec, &val);
1328         g_assert (G_VALUE_HOLDS_UINT (&val));
1329         ret = g_value_get_uint (&val);
1330         g_value_unset (&val);
1331         return ret;
1332 }
1333
1334 static gboolean
1335 write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1336 {
1337         NMSettingBridge *s_bridge;
1338         const char *iface;
1339         guint32 i;
1340         GString *opts;
1341         const char *mac;
1342         char *s;
1343
1344         s_bridge = nm_connection_get_setting_bridge (connection);
1345         if (!s_bridge) {
1346                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1347                              "Missing '%s' setting", NM_SETTING_BRIDGE_SETTING_NAME);
1348                 return FALSE;
1349         }
1350
1351         iface = nm_connection_get_interface_name (connection);
1352         if (!iface) {
1353                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1354                              "Missing interface name");
1355                 return FALSE;
1356         }
1357
1358         svSetValue (ifcfg, "DEVICE", iface, FALSE);
1359         svSetValue (ifcfg, "BRIDGING_OPTS", NULL, FALSE);
1360         svSetValue (ifcfg, "STP", "no", FALSE);
1361         svSetValue (ifcfg, "DELAY", NULL, FALSE);
1362
1363         mac = nm_setting_bridge_get_mac_address (s_bridge);
1364         svSetValue (ifcfg, "MACADDR", mac, FALSE);
1365
1366         /* Bridge options */
1367         opts = g_string_sized_new (32);
1368
1369         if (nm_setting_bridge_get_stp (s_bridge)) {
1370                 svSetValue (ifcfg, "STP", "yes", FALSE);
1371
1372                 i = nm_setting_bridge_get_forward_delay (s_bridge);
1373                 if (i != get_setting_default (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) {
1374                         s = g_strdup_printf ("%u", i);
1375                         svSetValue (ifcfg, "DELAY", s, FALSE);
1376                         g_free (s);
1377                 }
1378
1379                 g_string_append_printf (opts, "priority=%u", nm_setting_bridge_get_priority (s_bridge));
1380
1381                 i = nm_setting_bridge_get_hello_time (s_bridge);
1382                 if (i != get_setting_default (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_HELLO_TIME)) {
1383                         if (opts->len)
1384                                 g_string_append_c (opts, ' ');
1385                         g_string_append_printf (opts, "hello_time=%u", i);
1386                 }
1387
1388                 i = nm_setting_bridge_get_max_age (s_bridge);
1389                 if (i != get_setting_default (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MAX_AGE)) {
1390                         if (opts->len)
1391                                 g_string_append_c (opts, ' ');
1392                         g_string_append_printf (opts, "max_age=%u", i);
1393                 }
1394         }
1395
1396         i = nm_setting_bridge_get_ageing_time (s_bridge);
1397         if (i != get_setting_default (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_AGEING_TIME)) {
1398                 if (opts->len)
1399                         g_string_append_c (opts, ' ');
1400                 g_string_append_printf (opts, "ageing_time=%u", i);
1401         }
1402
1403         if (opts->len)
1404                 svSetValue (ifcfg, "BRIDGING_OPTS", opts->str, FALSE);
1405         g_string_free (opts, TRUE);
1406
1407         svSetValue (ifcfg, "TYPE", TYPE_BRIDGE, FALSE);
1408
1409         return TRUE;
1410 }
1411
1412 static gboolean
1413 write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1414 {
1415         NMSettingBridgePort *s_port;
1416         guint32 i;
1417         GString *opts;
1418
1419         s_port = nm_connection_get_setting_bridge_port (connection);
1420         if (!s_port)
1421                 return TRUE;
1422
1423         svSetValue (ifcfg, "BRIDGING_OPTS", NULL, FALSE);
1424
1425         /* Bridge options */
1426         opts = g_string_sized_new (32);
1427
1428         i = nm_setting_bridge_port_get_priority (s_port);
1429         if (i != get_setting_default (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PRIORITY))
1430                 g_string_append_printf (opts, "priority=%u", i);
1431
1432         i = nm_setting_bridge_port_get_path_cost (s_port);
1433         if (i != get_setting_default (NM_SETTING (s_port), NM_SETTING_BRIDGE_PORT_PATH_COST)) {
1434                 if (opts->len)
1435                         g_string_append_c (opts, ' ');
1436                 g_string_append_printf (opts, "path_cost=%u", i);
1437         }
1438
1439         if (nm_setting_bridge_port_get_hairpin_mode (s_port)) {
1440                 if (opts->len)
1441                         g_string_append_c (opts, ' ');
1442                 g_string_append_printf (opts, "hairpin_mode=1");
1443         }
1444
1445         if (opts->len)
1446                 svSetValue (ifcfg, "BRIDGING_OPTS", opts->str, FALSE);
1447         g_string_free (opts, TRUE);
1448
1449         return TRUE;
1450 }
1451
1452 static gboolean
1453 write_team_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1454 {
1455         NMSettingTeamPort *s_port;
1456         const char *config;
1457
1458         s_port = nm_connection_get_setting_team_port (connection);
1459         if (!s_port)
1460                 return TRUE;
1461
1462         config = nm_setting_team_port_get_config (s_port);
1463         svSetValue (ifcfg, "TEAM_PORT_CONFIG", config, FALSE);
1464
1465         return TRUE;
1466 }
1467
1468 static void
1469 write_dcb_flags (shvarFile *ifcfg, const char *tag, NMSettingDcbFlags flags)
1470 {
1471         char *prop;
1472
1473         prop = g_strdup_printf ("DCB_%s_ENABLE", tag);
1474         svSetValue (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL, FALSE);
1475         g_free (prop);
1476
1477         prop = g_strdup_printf ("DCB_%s_ADVERTISE", tag);
1478         svSetValue (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL, FALSE);
1479         g_free (prop);
1480
1481         prop = g_strdup_printf ("DCB_%s_WILLING", tag);
1482         svSetValue (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL, FALSE);
1483         g_free (prop);
1484 }
1485
1486 static void
1487 write_dcb_app (shvarFile *ifcfg,
1488                const char *tag,
1489                NMSettingDcbFlags flags,
1490                gint priority)
1491 {
1492         char *prop, *tmp = NULL;
1493
1494         write_dcb_flags (ifcfg, tag, flags);
1495
1496         if ((flags & NM_SETTING_DCB_FLAG_ENABLE) && (priority >= 0))
1497                 tmp = g_strdup_printf ("%d", priority);
1498         prop = g_strdup_printf ("DCB_%s_PRIORITY", tag);
1499         svSetValue (ifcfg, prop, tmp, FALSE);
1500         g_free (prop);
1501         g_free (tmp);
1502 }
1503
1504 typedef gboolean (*DcbGetBoolFunc) (NMSettingDcb *, guint);
1505
1506 static void
1507 write_dcb_bool_array (shvarFile *ifcfg,
1508                       const char *key,
1509                       NMSettingDcb *s_dcb,
1510                       NMSettingDcbFlags flags,
1511                       DcbGetBoolFunc get_func)
1512 {
1513         char str[9];
1514         guint i;
1515
1516         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1517                 svSetValue (ifcfg, key, NULL, FALSE);
1518                 return;
1519         }
1520
1521         str[8] = 0;
1522         for (i = 0; i < 8; i++)
1523                 str[i] = get_func (s_dcb, i) ? '1' : '0';
1524         svSetValue (ifcfg, key, str, FALSE);
1525 }
1526
1527 typedef guint (*DcbGetUintFunc) (NMSettingDcb *, guint);
1528
1529 static void
1530 write_dcb_uint_array (shvarFile *ifcfg,
1531                       const char *key,
1532                       NMSettingDcb *s_dcb,
1533                       NMSettingDcbFlags flags,
1534                       DcbGetUintFunc get_func)
1535 {
1536         char str[9];
1537         guint i, num;
1538
1539         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1540                 svSetValue (ifcfg, key, NULL, FALSE);
1541                 return;
1542         }
1543
1544         str[8] = 0;
1545         for (i = 0; i < 8; i++) {
1546                 num = get_func (s_dcb, i);
1547                 if (num < 10)
1548                         str[i] = '0' + num;
1549                 else if (num == 15)
1550                         str[i] = 'f';
1551                 else
1552                         g_assert_not_reached ();
1553         }
1554         svSetValue (ifcfg, key, str, FALSE);
1555 }
1556
1557 static void
1558 write_dcb_percent_array (shvarFile *ifcfg,
1559                          const char *key,
1560                          NMSettingDcb *s_dcb,
1561                          NMSettingDcbFlags flags,
1562                          DcbGetUintFunc get_func)
1563 {
1564         GString *str;
1565         guint i;
1566
1567         if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
1568                 svSetValue (ifcfg, key, NULL, FALSE);
1569                 return;
1570         }
1571
1572         str = g_string_sized_new (30);
1573         for (i = 0; i < 8; i++) {
1574                 if (str->len)
1575                         g_string_append_c (str, ',');
1576                 g_string_append_printf (str, "%d", get_func (s_dcb, i));
1577         }
1578         svSetValue (ifcfg, key, str->str, FALSE);
1579         g_string_free (str, TRUE);
1580 }
1581
1582 static gboolean
1583 write_dcb_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1584 {
1585         NMSettingDcb *s_dcb;
1586         NMSettingDcbFlags flags;
1587
1588         s_dcb = nm_connection_get_setting_dcb (connection);
1589         if (!s_dcb) {
1590                 static const char *clear_keys[] = {
1591                     "DCB",
1592                     KEY_DCB_APP_FCOE_ENABLE,
1593                     KEY_DCB_APP_FCOE_ADVERTISE,
1594                     KEY_DCB_APP_FCOE_WILLING,
1595                     KEY_DCB_APP_FCOE_MODE,
1596                     KEY_DCB_APP_ISCSI_ENABLE,
1597                     KEY_DCB_APP_ISCSI_ADVERTISE,
1598                     KEY_DCB_APP_ISCSI_WILLING,
1599                     KEY_DCB_APP_FIP_ENABLE,
1600                     KEY_DCB_APP_FIP_ADVERTISE,
1601                     KEY_DCB_APP_FIP_WILLING,
1602                     KEY_DCB_PFC_ENABLE,
1603                     KEY_DCB_PFC_ADVERTISE,
1604                     KEY_DCB_PFC_WILLING,
1605                     KEY_DCB_PFC_UP,
1606                     KEY_DCB_PG_ENABLE,
1607                     KEY_DCB_PG_ADVERTISE,
1608                     KEY_DCB_PG_WILLING,
1609                     KEY_DCB_PG_ID,
1610                     KEY_DCB_PG_PCT,
1611                     KEY_DCB_PG_UPPCT,
1612                     KEY_DCB_PG_STRICT,
1613                     KEY_DCB_PG_UP2TC,
1614                     NULL };
1615                 const char **iter;
1616
1617                 for (iter = clear_keys; *iter; iter++)
1618                         svSetValue (ifcfg, *iter, NULL, FALSE);
1619                 return TRUE;
1620         }
1621
1622         svSetValue (ifcfg, "DCB", "yes", FALSE);
1623
1624         write_dcb_app (ifcfg, "APP_FCOE",
1625                        nm_setting_dcb_get_app_fcoe_flags (s_dcb),
1626                        nm_setting_dcb_get_app_fcoe_priority (s_dcb));
1627         if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE)
1628                 svSetValue (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb), FALSE);
1629         else
1630                 svSetValue (ifcfg, KEY_DCB_APP_FCOE_MODE, NULL, FALSE);
1631
1632         write_dcb_app (ifcfg, "APP_ISCSI",
1633                        nm_setting_dcb_get_app_iscsi_flags (s_dcb),
1634                        nm_setting_dcb_get_app_iscsi_priority (s_dcb));
1635         write_dcb_app (ifcfg, "APP_FIP",
1636                        nm_setting_dcb_get_app_fip_flags (s_dcb),
1637                        nm_setting_dcb_get_app_fip_priority (s_dcb));
1638
1639         write_dcb_flags (ifcfg, "PFC", nm_setting_dcb_get_priority_flow_control_flags (s_dcb));
1640         write_dcb_bool_array (ifcfg, KEY_DCB_PFC_UP, s_dcb,
1641                               nm_setting_dcb_get_priority_flow_control_flags (s_dcb),
1642                               nm_setting_dcb_get_priority_flow_control);
1643
1644         flags = nm_setting_dcb_get_priority_group_flags (s_dcb);
1645         write_dcb_flags (ifcfg, "PG", flags);
1646         write_dcb_uint_array (ifcfg, KEY_DCB_PG_ID, s_dcb, flags, nm_setting_dcb_get_priority_group_id);
1647         write_dcb_percent_array (ifcfg, KEY_DCB_PG_PCT, s_dcb, flags, nm_setting_dcb_get_priority_group_bandwidth);
1648         write_dcb_percent_array (ifcfg, KEY_DCB_PG_UPPCT, s_dcb, flags, nm_setting_dcb_get_priority_bandwidth);
1649         write_dcb_bool_array (ifcfg, KEY_DCB_PG_STRICT, s_dcb, flags, nm_setting_dcb_get_priority_strict_bandwidth);
1650         write_dcb_uint_array (ifcfg, KEY_DCB_PG_UP2TC, s_dcb, flags, nm_setting_dcb_get_priority_traffic_class);
1651
1652         return TRUE;
1653 }
1654
1655 static void
1656 write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
1657 {
1658         guint32 n, i;
1659         GString *str;
1660         const char *master;
1661         char *tmp;
1662         gint i_int;
1663
1664         svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE);
1665         svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE);
1666         svSetValue (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con), FALSE);
1667         svSetValue (ifcfg, "ONBOOT",
1668                     nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no",
1669                     FALSE);
1670
1671         i_int = nm_setting_connection_get_autoconnect_priority (s_con);
1672         tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT
1673               ? g_strdup_printf ("%d", i_int) : NULL;
1674         svSetValue (ifcfg, "AUTOCONNECT_PRIORITY", tmp, FALSE);
1675         g_free (tmp);
1676
1677         /* Permissions */
1678         svSetValue (ifcfg, "USERS", NULL, FALSE);
1679         n = nm_setting_connection_get_num_permissions (s_con);
1680         if (n > 0) {
1681                 str = g_string_sized_new (n * 20);
1682
1683                 for (i = 0; i < n; i++) {
1684                         const char *puser = NULL;
1685
1686                         /* Items separated by space for consistency with eg
1687                          * IPV6ADDR_SECONDARIES and DOMAIN.
1688                          */
1689                         if (str->len)
1690                                 g_string_append_c (str, ' ');
1691
1692                         if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL))
1693                                 g_string_append (str, puser);
1694                 }
1695                 svSetValue (ifcfg, "USERS", str->str, FALSE);
1696                 g_string_free (str, TRUE);
1697         }
1698
1699         svSetValue (ifcfg, "ZONE", nm_setting_connection_get_zone(s_con), FALSE);
1700
1701         master = nm_setting_connection_get_master (s_con);
1702         if (master) {
1703                 if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
1704                         svSetValue (ifcfg, "MASTER", master, FALSE);
1705                         svSetValue (ifcfg, "SLAVE", "yes", FALSE);
1706                 } else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME))
1707                         svSetValue (ifcfg, "BRIDGE", master, FALSE);
1708                 else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME)) {
1709                         svSetValue (ifcfg, "TEAM_MASTER", master, FALSE);
1710                         svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT, FALSE);
1711                         svSetValue (ifcfg, "TYPE", NULL, FALSE);
1712                 }
1713         }
1714
1715         /* secondary connection UUIDs */
1716         svSetValue (ifcfg, "SECONDARY_UUIDS", NULL, FALSE);
1717         n = nm_setting_connection_get_num_secondaries (s_con);
1718         if (n > 0) {
1719                 str = g_string_sized_new (n * 37);
1720
1721                 for (i = 0; i < n; i++) {
1722                         const char *uuid;
1723
1724                         /* Items separated by space for consistency with eg
1725                          * IPV6ADDR_SECONDARIES and DOMAIN.
1726                          */
1727                         if (str->len)
1728                                 g_string_append_c (str, ' ');
1729
1730                         if ((uuid = nm_setting_connection_get_secondary (s_con, i)) != NULL)
1731                                 g_string_append (str, uuid);
1732                 }
1733                 svSetValue (ifcfg, "SECONDARY_UUIDS", str->str, FALSE);
1734                 g_string_free (str, TRUE);
1735         }
1736
1737         svSetValue (ifcfg, "GATEWAY_PING_TIMEOUT", NULL, FALSE);
1738         if (nm_setting_connection_get_gateway_ping_timeout (s_con)) {
1739                 tmp = g_strdup_printf ("%" G_GUINT32_FORMAT, nm_setting_connection_get_gateway_ping_timeout (s_con));
1740                 svSetValue (ifcfg, "GATEWAY_PING_TIMEOUT", tmp, FALSE);
1741                 g_free (tmp);
1742         }
1743 }
1744
1745 static gboolean
1746 write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error)
1747 {
1748         const char *dest, *next_hop;
1749         char **route_items;
1750         char *route_contents;
1751         NMIPRoute *route;
1752         guint32 prefix;
1753         gint64 metric;
1754         guint32 i, num;
1755         gboolean success = FALSE;
1756
1757         g_return_val_if_fail (filename != NULL, FALSE);
1758         g_return_val_if_fail (s_ip4 != NULL, FALSE);
1759         g_return_val_if_fail (error != NULL, FALSE);
1760         g_return_val_if_fail (*error == NULL, FALSE);
1761
1762         num = nm_setting_ip_config_get_num_routes (s_ip4);
1763         if (num == 0) {
1764                 unlink (filename);
1765                 return TRUE;
1766         }
1767
1768         route_items = g_malloc0 (sizeof (char*) * (num + 1));
1769         for (i = 0; i < num; i++) {
1770                 route = nm_setting_ip_config_get_route (s_ip4, i);
1771
1772                 dest = nm_ip_route_get_dest (route);
1773                 prefix = nm_ip_route_get_prefix (route);
1774                 next_hop = nm_ip_route_get_next_hop (route);
1775                 metric = nm_ip_route_get_metric (route);
1776
1777                 if (metric == -1)
1778                         route_items[i] = g_strdup_printf ("%s/%u via %s\n", dest, prefix, next_hop);
1779                 else
1780                         route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, (guint32) metric);
1781         }
1782         route_items[num] = NULL;
1783         route_contents = g_strjoinv (NULL, route_items);
1784         g_strfreev (route_items);
1785
1786         if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
1787                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1788                              "Writing route file '%s' failed", filename);
1789                 goto error;
1790         }
1791
1792         success = TRUE;
1793
1794 error:
1795         g_free (route_contents);
1796
1797         return success;
1798 }
1799
1800 static gboolean
1801 write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
1802 {
1803         NMSettingIPConfig *s_ip4;
1804         const char *value;
1805         char *addr_key, *prefix_key, *netmask_key, *gw_key, *metric_key, *tmp;
1806         char *route_path = NULL;
1807         gint32 j;
1808         guint32 i, n, num;
1809         gint64 route_metric;
1810         GString *searches;
1811         gboolean success = FALSE;
1812         gboolean fake_ip4 = FALSE;
1813         const char *method = NULL;
1814
1815         s_ip4 = nm_connection_get_setting_ip4_config (connection);
1816         if (s_ip4)
1817                 method = nm_setting_ip_config_get_method (s_ip4);
1818
1819         /* Missing IP4 setting is assumed to be DHCP */
1820         if (!method)
1821                 method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
1822
1823         if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
1824                 int result;
1825
1826                 /* IPv4 disabled, clear IPv4 related parameters */
1827                 svSetValue (ifcfg, "BOOTPROTO", NULL, FALSE);
1828                 for (j = -1; j < 256; j++) {
1829                         if (j == -1) {
1830                                 addr_key = g_strdup ("IPADDR");
1831                                 prefix_key = g_strdup ("PREFIX");
1832                                 netmask_key = g_strdup ("NETMASK");
1833                                 gw_key = g_strdup ("GATEWAY");
1834                         } else {
1835                                 addr_key = g_strdup_printf ("IPADDR%d", j);
1836                                 prefix_key = g_strdup_printf ("PREFIX%d", j);
1837                                 netmask_key = g_strdup_printf ("NETMASK%d", j);
1838                                 gw_key = g_strdup_printf ("GATEWAY%d", j);
1839                         }
1840
1841                         svSetValue (ifcfg, addr_key, NULL, FALSE);
1842                         svSetValue (ifcfg, prefix_key, NULL, FALSE);
1843                         svSetValue (ifcfg, netmask_key, NULL, FALSE);
1844                         svSetValue (ifcfg, gw_key, NULL, FALSE);
1845
1846                         g_free (addr_key);
1847                         g_free (prefix_key);
1848                         g_free (netmask_key);
1849                         g_free (gw_key);
1850                 }
1851
1852                 route_path = utils_get_route_path (ifcfg->fileName);
1853                 result = unlink (route_path);
1854                 g_free (route_path);
1855                 return TRUE;
1856         }
1857
1858         /* Temporarily create fake IP4 setting if missing; method set to DHCP above */
1859         if (!s_ip4) {
1860                 s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
1861                 fake_ip4 = TRUE;
1862         }
1863
1864         if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
1865                 svSetValue (ifcfg, "BOOTPROTO", "dhcp", FALSE);
1866         else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
1867                 svSetValue (ifcfg, "BOOTPROTO", "none", FALSE);
1868         else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
1869                 svSetValue (ifcfg, "BOOTPROTO", "autoip", FALSE);
1870         else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
1871                 svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE);
1872
1873         /* Clear out un-numbered IP address fields */
1874         svSetValue (ifcfg, "IPADDR", NULL, FALSE);
1875         svSetValue (ifcfg, "PREFIX", NULL, FALSE);
1876         svSetValue (ifcfg, "NETMASK", NULL, FALSE);
1877         svSetValue (ifcfg, "GATEWAY", NULL, FALSE);
1878         /* Clear out zero-indexed IP address fields */
1879         svSetValue (ifcfg, "IPADDR0", NULL, FALSE);
1880         svSetValue (ifcfg, "PREFIX0", NULL, FALSE);
1881         svSetValue (ifcfg, "NETMASK0", NULL, FALSE);
1882         svSetValue (ifcfg, "GATEWAY0", NULL, FALSE);
1883
1884         /* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses
1885          * without labels. Unset obsolete NETMASK<n>.
1886          */
1887         num = nm_setting_ip_config_get_num_addresses (s_ip4);
1888         for (i = n = 0; i < num; i++) {
1889                 NMIPAddress *addr;
1890
1891                 addr = nm_setting_ip_config_get_address (s_ip4, i);
1892
1893                 if (i > 0) {
1894                         GVariant *label;
1895
1896                         label = nm_ip_address_get_attribute (addr, "label");
1897                         if (label)
1898                                 continue;
1899                 }
1900
1901                 if (n == 0) {
1902                         /* Instead of index 0 use un-numbered variables.
1903                          * It's needed for compatibility with ifup that only recognizes 'GATEAWAY'
1904                          * See https://bugzilla.redhat.com/show_bug.cgi?id=771673
1905                          * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770
1906                          */
1907                         addr_key = g_strdup ("IPADDR");
1908                         prefix_key = g_strdup ("PREFIX");
1909                         netmask_key = g_strdup ("NETMASK");
1910                         gw_key = g_strdup ("GATEWAY");
1911                 } else {
1912                         addr_key = g_strdup_printf ("IPADDR%d", n);
1913                         prefix_key = g_strdup_printf ("PREFIX%d", n);
1914                         netmask_key = g_strdup_printf ("NETMASK%d", n);
1915                         gw_key = g_strdup_printf ("GATEWAY%d", n);
1916                 }
1917
1918                 svSetValue (ifcfg, addr_key, nm_ip_address_get_address (addr), FALSE);
1919
1920                 tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
1921                 svSetValue (ifcfg, prefix_key, tmp, FALSE);
1922                 g_free (tmp);
1923
1924                 svSetValue (ifcfg, netmask_key, NULL, FALSE);
1925                 svSetValue (ifcfg, gw_key, NULL, FALSE);
1926
1927                 g_free (addr_key);
1928                 g_free (prefix_key);
1929                 g_free (netmask_key);
1930                 g_free (gw_key);
1931                 n++;
1932         }
1933
1934         /* Clear remaining IPADDR<n..255>, etc */
1935         for (; n < 256; n++) {
1936                 addr_key = g_strdup_printf ("IPADDR%d", n);
1937                 prefix_key = g_strdup_printf ("PREFIX%d", n);
1938                 netmask_key = g_strdup_printf ("NETMASK%d", n);
1939                 gw_key = g_strdup_printf ("GATEWAY%d", n);
1940
1941                 svSetValue (ifcfg, addr_key, NULL, FALSE);
1942                 svSetValue (ifcfg, prefix_key, NULL, FALSE);
1943                 svSetValue (ifcfg, netmask_key, NULL, FALSE);
1944                 svSetValue (ifcfg, gw_key, NULL, FALSE);
1945
1946                 g_free (addr_key);
1947                 g_free (prefix_key);
1948                 g_free (netmask_key);
1949                 g_free (gw_key);
1950         }
1951
1952         svSetValue (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4), FALSE);
1953
1954         num = nm_setting_ip_config_get_num_dns (s_ip4);
1955         for (i = 0; i < 254; i++) {
1956                 const char *dns;
1957
1958                 addr_key = g_strdup_printf ("DNS%d", i + 1);
1959
1960                 if (i >= num)
1961                         svSetValue (ifcfg, addr_key, NULL, FALSE);
1962                 else {
1963                         dns = nm_setting_ip_config_get_dns (s_ip4, i);
1964                         svSetValue (ifcfg, addr_key, dns, FALSE);
1965                 }
1966                 g_free (addr_key);
1967         }
1968
1969         num = nm_setting_ip_config_get_num_dns_searches (s_ip4);
1970         if (num > 0) {
1971                 searches = g_string_new (NULL);
1972                 for (i = 0; i < num; i++) {
1973                         if (i > 0)
1974                                 g_string_append_c (searches, ' ');
1975                         g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip4, i));
1976                 }
1977                 svSetValue (ifcfg, "DOMAIN", searches->str, FALSE);
1978                 g_string_free (searches, TRUE);
1979         } else
1980                 svSetValue (ifcfg, "DOMAIN", NULL, FALSE);
1981
1982         /* DEFROUTE; remember that it has the opposite meaning from never-default */
1983         svSetValue (ifcfg, "DEFROUTE",
1984                     nm_setting_ip_config_get_never_default (s_ip4) ? "no" : "yes",
1985                     FALSE);
1986
1987         svSetValue (ifcfg, "PEERDNS", NULL, FALSE);
1988         svSetValue (ifcfg, "PEERROUTES", NULL, FALSE);
1989         svSetValue (ifcfg, "DHCP_CLIENT_ID", NULL, FALSE);
1990         if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
1991                 svSetValue (ifcfg, "PEERDNS",
1992                             nm_setting_ip_config_get_ignore_auto_dns (s_ip4) ? "no" : "yes",
1993                             FALSE);
1994
1995                 svSetValue (ifcfg, "PEERROUTES",
1996                             nm_setting_ip_config_get_ignore_auto_routes (s_ip4) ? "no" : "yes",
1997                             FALSE);
1998
1999                 value = nm_setting_ip_config_get_dhcp_hostname (s_ip4);
2000                 if (value)
2001                         svSetValue (ifcfg, "DHCP_HOSTNAME", value, FALSE);
2002
2003                 /* Missing DHCP_SEND_HOSTNAME means TRUE, and we prefer not write it explicitly
2004                  * in that case, because it is NM-specific variable
2005                  */
2006                 svSetValue (ifcfg, "DHCP_SEND_HOSTNAME",
2007                             nm_setting_ip_config_get_dhcp_send_hostname (s_ip4) ? NULL : "no",
2008                             FALSE);
2009
2010                 value = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
2011                 if (value)
2012                         svSetValue (ifcfg, "DHCP_CLIENT_ID", value, FALSE);
2013         }
2014
2015         svSetValue (ifcfg, "IPV4_FAILURE_FATAL",
2016                     nm_setting_ip_config_get_may_fail (s_ip4) ? "no" : "yes",
2017                     FALSE);
2018
2019         route_metric = nm_setting_ip_config_get_route_metric (s_ip4);
2020         tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
2021         svSetValue (ifcfg, "IPV4_ROUTE_METRIC", tmp, FALSE);
2022         g_free (tmp);
2023
2024         /* Static routes - route-<name> file */
2025         route_path = utils_get_route_path (ifcfg->fileName);
2026         if (!route_path) {
2027                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2028                              "Could not get route file path for '%s'", ifcfg->fileName);
2029                 goto out;
2030         }
2031
2032         if (utils_has_route_file_new_syntax (route_path)) {
2033                 shvarFile *routefile;
2034
2035                 routefile = utils_get_route_ifcfg (ifcfg->fileName, TRUE);
2036                 if (!routefile) {
2037                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2038                                      "Could not create route file '%s'", route_path);
2039                         g_free (route_path);
2040                         goto out;
2041                 }
2042                 g_free (route_path);
2043
2044                 num = nm_setting_ip_config_get_num_routes (s_ip4);
2045                 for (i = 0; i < 256; i++) {
2046                         char buf[INET_ADDRSTRLEN];
2047                         NMIPRoute *route;
2048                         guint32 netmask;
2049                         gint64 metric;
2050
2051                         addr_key = g_strdup_printf ("ADDRESS%d", i);
2052                         netmask_key = g_strdup_printf ("NETMASK%d", i);
2053                         gw_key = g_strdup_printf ("GATEWAY%d", i);
2054                         metric_key = g_strdup_printf ("METRIC%d", i);
2055
2056                         if (i >= num) {
2057                                 svSetValue (routefile, addr_key, NULL, FALSE);
2058                                 svSetValue (routefile, netmask_key, NULL, FALSE);
2059                                 svSetValue (routefile, gw_key, NULL, FALSE);
2060                                 svSetValue (routefile, metric_key, NULL, FALSE);
2061                         } else {
2062                                 route = nm_setting_ip_config_get_route (s_ip4, i);
2063
2064                                 svSetValue (routefile, addr_key, nm_ip_route_get_dest (route), FALSE);
2065
2066                                 memset (buf, 0, sizeof (buf));
2067                                 netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
2068                                 inet_ntop (AF_INET, (const void *) &netmask, &buf[0], sizeof (buf));
2069                                 svSetValue (routefile, netmask_key, &buf[0], FALSE);
2070
2071                                 svSetValue (routefile, gw_key, nm_ip_route_get_next_hop (route), FALSE);
2072
2073                                 memset (buf, 0, sizeof (buf));
2074                                 metric = nm_ip_route_get_metric (route);
2075                                 if (metric == -1)
2076                                         svSetValue (routefile, metric_key, NULL, FALSE);
2077                                 else {
2078                                         tmp = g_strdup_printf ("%u", (guint32) metric);
2079                                         svSetValue (routefile, metric_key, tmp, FALSE);
2080                                         g_free (tmp);
2081                                 }
2082                         }
2083
2084                         g_free (addr_key);
2085                         g_free (netmask_key);
2086                         g_free (gw_key);
2087                         g_free (metric_key);
2088                 }
2089                 if (!svWriteFile (routefile, 0644, error)) {
2090                         svCloseFile (routefile);
2091                         goto out;
2092                 }
2093                 svCloseFile (routefile);
2094         } else {
2095                 write_route_file_legacy (route_path, s_ip4, error);
2096                 g_free (route_path);
2097                 if (error && *error)
2098                         goto out;
2099         }
2100
2101         success = TRUE;
2102
2103 out:
2104         if (fake_ip4)
2105                 g_object_unref (s_ip4);
2106
2107         return success;
2108 }
2109
2110 static void
2111 write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
2112 {
2113         NMSettingIPConfig *s_ip4;
2114         char *base_ifcfg_dir, *base_ifcfg_name, *base_name;
2115         int i, num, base_ifcfg_name_len, base_name_len;
2116         GDir *dir;
2117
2118         base_ifcfg_dir = g_path_get_dirname (base_ifcfg_path);
2119         base_ifcfg_name = g_path_get_basename (base_ifcfg_path);
2120         base_ifcfg_name_len = strlen (base_ifcfg_name);
2121         base_name = base_ifcfg_name + strlen (IFCFG_TAG);
2122         base_name_len = strlen (base_name);
2123
2124         /* Remove all existing aliases for this file first */
2125         dir = g_dir_open (base_ifcfg_dir, 0, NULL);
2126         if (dir) {
2127                 const char *item;
2128
2129                 while ((item = g_dir_read_name (dir))) {
2130                         char *full_path;
2131
2132                         if (   strncmp (item, base_ifcfg_name, base_ifcfg_name_len) != 0
2133                             || item[base_ifcfg_name_len] != ':')
2134                                 continue;
2135
2136                         full_path = g_build_filename (base_ifcfg_dir, item, NULL);
2137                         unlink (full_path);
2138                         g_free (full_path);
2139                 }
2140
2141                 g_dir_close (dir);
2142         }
2143
2144         if (utils_ignore_ip_config (connection))
2145                 return;
2146
2147         s_ip4 = nm_connection_get_setting_ip4_config (connection);
2148         if (!s_ip4)
2149                 return;
2150
2151         num = nm_setting_ip_config_get_num_addresses (s_ip4);
2152         for (i = 0; i < num; i++) {
2153                 GVariant *label_var;
2154                 const char *label, *p;
2155                 char *path, *tmp;
2156                 NMIPAddress *addr;
2157                 shvarFile *ifcfg;
2158
2159                 addr = nm_setting_ip_config_get_address (s_ip4, i);
2160
2161                 label_var = nm_ip_address_get_attribute (addr, "label");
2162                 if (!label_var)
2163                         continue;
2164                 label = g_variant_get_string (label_var, NULL);
2165                 if (   strncmp (label, base_name, base_name_len) != 0
2166                     || label[base_name_len] != ':')
2167                         continue;
2168
2169                 for (p = label; *p; p++) {
2170                         if (!g_ascii_isalnum (*p) && *p != '_' && *p != ':')
2171                                 break;
2172                 }
2173                 if (*p)
2174                         continue;
2175
2176                 path = g_strdup_printf ("%s%s", base_ifcfg_path, label + base_name_len);
2177                 ifcfg = svCreateFile (path);
2178                 g_free (path);
2179
2180                 svSetValue (ifcfg, "DEVICE", label, FALSE);
2181
2182                 addr = nm_setting_ip_config_get_address (s_ip4, i);
2183                 svSetValue (ifcfg, "IPADDR", nm_ip_address_get_address (addr), FALSE);
2184
2185                 tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
2186                 svSetValue (ifcfg, "PREFIX", tmp, FALSE);
2187                 g_free (tmp);
2188
2189                 svWriteFile (ifcfg, 0644, NULL);
2190                 svCloseFile (ifcfg);
2191         }
2192
2193         g_free (base_ifcfg_name);
2194         g_free (base_ifcfg_dir);
2195 }
2196
2197 static gboolean
2198 write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
2199 {
2200         char **route_items;
2201         char *route_contents;
2202         NMIPRoute *route;
2203         guint32 i, num;
2204         gboolean success = FALSE;
2205
2206         g_return_val_if_fail (filename != NULL, FALSE);
2207         g_return_val_if_fail (s_ip6 != NULL, FALSE);
2208         g_return_val_if_fail (error != NULL, FALSE);
2209         g_return_val_if_fail (*error == NULL, FALSE);
2210
2211         num = nm_setting_ip_config_get_num_routes (s_ip6);
2212         if (num == 0) {
2213                 unlink (filename);
2214                 return TRUE;
2215         }
2216
2217         route_items = g_malloc0 (sizeof (char*) * (num + 1));
2218         for (i = 0; i < num; i++) {
2219                 route = nm_setting_ip_config_get_route (s_ip6, i);
2220
2221                 if (nm_ip_route_get_metric (route) == -1) {
2222                         route_items[i] = g_strdup_printf ("%s/%u via %s\n",
2223                                                           nm_ip_route_get_dest (route),
2224                                                           nm_ip_route_get_prefix (route),
2225                                                           nm_ip_route_get_next_hop (route));
2226                 } else {
2227                         route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n",
2228                                                           nm_ip_route_get_dest (route),
2229                                                           nm_ip_route_get_prefix (route),
2230                                                           nm_ip_route_get_next_hop (route),
2231                                                           (guint32) nm_ip_route_get_metric (route));
2232                 }
2233         }
2234         route_items[num] = NULL;
2235         route_contents = g_strjoinv (NULL, route_items);
2236         g_strfreev (route_items);
2237
2238         if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
2239                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2240                              "Writing route6 file '%s' failed", filename);
2241                 goto error;
2242         }
2243
2244         success = TRUE;
2245
2246 error:
2247         g_free (route_contents);
2248         return success;
2249 }
2250
2251 static gboolean
2252 write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
2253 {
2254         NMSettingIPConfig *s_ip6;
2255         NMSettingIPConfig *s_ip4;
2256         const char *value;
2257         char *addr_key;
2258         char *tmp;
2259         guint32 i, num, num4;
2260         GString *searches;
2261         NMIPAddress *addr;
2262         const char *dns;
2263         gint64 route_metric;
2264         GString *ip_str1, *ip_str2, *ip_ptr;
2265         char *route6_path;
2266
2267         s_ip6 = nm_connection_get_setting_ip6_config (connection);
2268         if (!s_ip6) {
2269                 /* Treat missing IPv6 setting as a setting with method "auto" */
2270                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2271                 svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
2272                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2273                 svSetValue (ifcfg, "IPV6_DEFROUTE", "yes", FALSE);
2274                 svSetValue (ifcfg, "IPV6_PEERDNS", "yes", FALSE);
2275                 svSetValue (ifcfg, "IPV6_PEERROUTES", "yes", FALSE);
2276                 svSetValue (ifcfg, "IPV6_FAILURE_FATAL", "no", FALSE);
2277                 svSetValue (ifcfg, "IPV6_ROUTE_METRIC", NULL, FALSE);
2278                 return TRUE;
2279         }
2280
2281         value = nm_setting_ip_config_get_method (s_ip6);
2282         g_assert (value);
2283         if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
2284                 svSetValue (ifcfg, "IPV6INIT", "no", FALSE);
2285                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2286                 return TRUE;
2287         } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
2288                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2289                 svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
2290                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2291         } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
2292                 const char *hostname;
2293                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2294                 svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
2295                 svSetValue (ifcfg, "DHCPV6C", "yes", FALSE);
2296                 hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
2297                 if (hostname)
2298                         svSetValue (ifcfg, "DHCP_HOSTNAME", hostname, FALSE);
2299         } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
2300                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2301                 svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
2302                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2303         } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
2304                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2305                 svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
2306                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2307         } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
2308                 svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
2309                 svSetValue (ifcfg, "DHCPV6C", NULL, FALSE);
2310                 /* TODO */
2311         }
2312
2313         /* Write out IP addresses */
2314         num = nm_setting_ip_config_get_num_addresses (s_ip6);
2315         ip_str1 = g_string_new (NULL);
2316         ip_str2 = g_string_new (NULL);
2317         for (i = 0; i < num; i++) {
2318                 if (i == 0)
2319                         ip_ptr = ip_str1;
2320                 else
2321                         ip_ptr = ip_str2;
2322
2323                 addr = nm_setting_ip_config_get_address (s_ip6, i);
2324
2325                 if (i > 1)
2326                         g_string_append_c (ip_ptr, ' ');  /* separate addresses in IPV6ADDR_SECONDARIES */
2327                 g_string_append_printf (ip_ptr, "%s/%u",
2328                                         nm_ip_address_get_address (addr),
2329                                         nm_ip_address_get_prefix (addr));
2330         }
2331         svSetValue (ifcfg, "IPV6ADDR", ip_str1->str, FALSE);
2332         svSetValue (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str, FALSE);
2333         svSetValue (ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway (s_ip6), FALSE);
2334         g_string_free (ip_str1, TRUE);
2335         g_string_free (ip_str2, TRUE);
2336
2337         /* Write out DNS - 'DNS' key is used both for IPv4 and IPv6 */
2338         s_ip4 = nm_connection_get_setting_ip4_config (connection);
2339         num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
2340         num = nm_setting_ip_config_get_num_dns (s_ip6);
2341         for (i = 0; i < 254; i++) {
2342                 addr_key = g_strdup_printf ("DNS%d", i + num4 + 1);
2343
2344                 if (i >= num)
2345                         svSetValue (ifcfg, addr_key, NULL, FALSE);
2346                 else {
2347                         dns = nm_setting_ip_config_get_dns (s_ip6, i);
2348                         svSetValue (ifcfg, addr_key, dns, FALSE);
2349                 }
2350                 g_free (addr_key);
2351         }
2352
2353         /* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
2354         num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
2355         if (num > 0) {
2356                 char *ip4_domains;
2357                 ip4_domains = svGetValue (ifcfg, "DOMAIN", FALSE);
2358                 searches = g_string_new (ip4_domains);
2359                 for (i = 0; i < num; i++) {
2360                         if (searches->len > 0)
2361                                 g_string_append_c (searches, ' ');
2362                         g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip6, i));
2363                 }
2364                 svSetValue (ifcfg, "DOMAIN", searches->str, FALSE);
2365                 g_string_free (searches, TRUE);
2366                 g_free (ip4_domains);
2367         }
2368
2369         /* handle IPV6_DEFROUTE */
2370         /* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
2371         if (nm_setting_ip_config_get_never_default(s_ip6))
2372                 svSetValue (ifcfg, "IPV6_DEFROUTE", "no", FALSE);
2373         else
2374                 svSetValue (ifcfg, "IPV6_DEFROUTE", "yes", FALSE);
2375
2376         svSetValue (ifcfg, "IPV6_PEERDNS", NULL, FALSE);
2377         svSetValue (ifcfg, "IPV6_PEERROUTES", NULL, FALSE);
2378         if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
2379                 svSetValue (ifcfg, "IPV6_PEERDNS",
2380                             nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : "yes",
2381                             FALSE);
2382
2383                 svSetValue (ifcfg, "IPV6_PEERROUTES",
2384                             nm_setting_ip_config_get_ignore_auto_routes (s_ip6) ? "no" : "yes",
2385                             FALSE);
2386         }
2387
2388         svSetValue (ifcfg, "IPV6_FAILURE_FATAL",
2389                     nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes",
2390                     FALSE);
2391
2392         route_metric = nm_setting_ip_config_get_route_metric (s_ip6);
2393         tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
2394         svSetValue (ifcfg, "IPV6_ROUTE_METRIC", tmp, FALSE);
2395         g_free (tmp);
2396
2397         /* IPv6 Privacy Extensions */
2398         svSetValue (ifcfg, "IPV6_PRIVACY", NULL, FALSE);
2399         svSetValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", NULL, FALSE);
2400         switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))){
2401         case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
2402                 svSetValue (ifcfg, "IPV6_PRIVACY", "no", FALSE);
2403         break;
2404         case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
2405                 svSetValue (ifcfg, "IPV6_PRIVACY", "rfc3041", FALSE);
2406                 svSetValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes", FALSE);
2407         break;
2408         case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
2409                 svSetValue (ifcfg, "IPV6_PRIVACY", "rfc3041", FALSE);
2410         break;
2411         default:
2412         break;
2413         }
2414
2415         /* Static routes go to route6-<dev> file */
2416         route6_path = utils_get_route6_path (ifcfg->fileName);
2417         if (!route6_path) {
2418                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2419                              "Could not get route6 file path for '%s'", ifcfg->fileName);
2420                 goto error;
2421         }
2422         write_route6_file (route6_path, s_ip6, error);
2423         g_free (route6_path);
2424         if (error && *error)
2425                 goto error;
2426
2427         return TRUE;
2428
2429 error:
2430         return FALSE;
2431 }
2432
2433 static char *
2434 escape_id (const char *id)
2435 {
2436         char *escaped = g_strdup (id);
2437         char *p = escaped;
2438
2439         /* Escape random stuff */
2440         while (*p) {
2441                 if (*p == ' ')
2442                         *p = '_';
2443                 else if (strchr ("\\][|/=()!", *p))
2444                         *p = '-';
2445                 p++;
2446         }
2447
2448         return escaped;
2449 }
2450
2451 static gboolean
2452 write_connection (NMConnection *connection,
2453                   const char *ifcfg_dir,
2454                   const char *filename,
2455                   const char *keyfile,
2456                   char **out_filename,
2457                   GError **error)
2458 {
2459         NMSettingConnection *s_con;
2460         gboolean success = FALSE;
2461         shvarFile *ifcfg = NULL;
2462         char *ifcfg_name = NULL;
2463         const char *type;
2464         gboolean no_8021x = FALSE;
2465         gboolean wired = FALSE;
2466
2467         if (!writer_can_write_connection (connection, error))
2468                 return FALSE;
2469
2470         s_con = nm_connection_get_setting_connection (connection);
2471         g_assert (s_con);
2472
2473         if (filename) {
2474                 /* For existing connections, 'filename' should be full path to ifcfg file */
2475                 ifcfg = svOpenFile (filename, error);
2476                 if (!ifcfg)
2477                         return FALSE;
2478
2479                 ifcfg_name = g_strdup (filename);
2480         } else {
2481                 char *escaped;
2482
2483                 escaped = escape_id (nm_setting_connection_get_id (s_con));
2484                 ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped);
2485
2486                 /* If a file with this path already exists then we need another name.
2487                  * Multiple connections can have the same ID (ie if two connections with
2488                  * the same ID are visible to different users) but of course can't have
2489                  * the same path.
2490                  */
2491                 if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) {
2492                         guint32 idx = 0;
2493
2494                         g_free (ifcfg_name);
2495                         while (idx++ < 500) {
2496                                 ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx);
2497                                 if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE)
2498                                         break;
2499                                 g_free (ifcfg_name);
2500                                 ifcfg_name = NULL;
2501                         }
2502                 }
2503                 g_free (escaped);
2504
2505                 if (ifcfg_name == NULL) {
2506                         g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2507                                              "Failed to find usable ifcfg file name");
2508                         return FALSE;
2509                 }
2510
2511                 ifcfg = svCreateFile (ifcfg_name);
2512         }
2513
2514         type = nm_setting_connection_get_connection_type (s_con);
2515         if (!type) {
2516                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2517                              "Missing connection type!");
2518                 goto out;
2519         }
2520
2521         if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
2522                 // FIXME: can't write PPPoE at this time
2523                 if (nm_connection_get_setting_pppoe (connection)) {
2524                         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2525                                      "Can't write connection type '%s'",
2526                                      NM_SETTING_PPPOE_SETTING_NAME);
2527                         goto out;
2528                 }
2529
2530                 if (!write_wired_setting (connection, ifcfg, error))
2531                         goto out;
2532                 wired = TRUE;
2533         } else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME)) {
2534                 if (!write_vlan_setting (connection, ifcfg, &wired, error))
2535                         goto out;
2536         } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
2537                 if (!write_wireless_setting (connection, ifcfg, &no_8021x, error))
2538                         goto out;
2539         } else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
2540                 if (!write_infiniband_setting (connection, ifcfg, error))
2541                         goto out;
2542         } else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) {
2543                 if (!write_bonding_setting (connection, ifcfg, error))
2544                         goto out;
2545         } else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) {
2546                 if (!write_team_setting (connection, ifcfg, error))
2547                         goto out;
2548         } else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
2549                 if (!write_bridge_setting (connection, ifcfg, error))
2550                         goto out;
2551         } else {
2552                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2553                              "Can't write connection type '%s'", type);
2554                 goto out;
2555         }
2556
2557         if (!no_8021x) {
2558                 if (!write_8021x_setting (connection, ifcfg, wired, error))
2559                         goto out;
2560         }
2561
2562         if (!write_bridge_port_setting (connection, ifcfg, error))
2563                 goto out;
2564
2565         if (!write_team_port_setting (connection, ifcfg, error))
2566                 goto out;
2567
2568         if (!write_dcb_setting (connection, ifcfg, error))
2569                 goto out;
2570
2571         if (!utils_ignore_ip_config (connection)) {
2572                 svSetValue (ifcfg, "DHCP_HOSTNAME", NULL, FALSE);
2573
2574                 if (!write_ip4_setting (connection, ifcfg, error))
2575                         goto out;
2576                 write_ip4_aliases (connection, ifcfg_name);
2577
2578                 if (!write_ip6_setting (connection, ifcfg, error))
2579                         goto out;
2580         }
2581
2582         write_connection_setting (s_con, ifcfg);
2583
2584         if (!svWriteFile (ifcfg, 0644, error))
2585                 goto out;
2586
2587         /* Only return the filename if this was a newly written ifcfg */
2588         if (out_filename && !filename)
2589                 *out_filename = g_strdup (ifcfg_name);
2590
2591         success = TRUE;
2592
2593 out:
2594         if (ifcfg)
2595                 svCloseFile (ifcfg);
2596         g_free (ifcfg_name);
2597         return success;
2598 }
2599
2600 gboolean
2601 writer_can_write_connection (NMConnection *connection, GError **error)
2602 {
2603         NMSettingConnection *s_con;
2604
2605         if (   (   nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)
2606                 && !nm_connection_get_setting_pppoe (connection))
2607             || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)
2608             || nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME)
2609             || nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)
2610             || nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)
2611             || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)
2612             || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME))
2613                 return TRUE;
2614
2615         s_con = nm_connection_get_setting_connection (connection);
2616         g_assert (s_con);
2617         g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2618                      "The ifcfg-rh plugin cannot write the connection '%s' (type '%s' pppoe %d)",
2619                      nm_connection_get_id (connection),
2620                      nm_setting_connection_get_connection_type (s_con),
2621                      !!nm_connection_get_setting_pppoe (connection));
2622         return FALSE;
2623 }
2624
2625 gboolean
2626 writer_new_connection (NMConnection *connection,
2627                        const char *ifcfg_dir,
2628                        char **out_filename,
2629                        GError **error)
2630 {
2631         return write_connection (connection, ifcfg_dir, NULL, NULL, out_filename, error);
2632 }
2633
2634 gboolean
2635 writer_update_connection (NMConnection *connection,
2636                           const char *ifcfg_dir,
2637                           const char *filename,
2638                           const char *keyfile,
2639                           GError **error)
2640 {
2641         if (utils_has_complex_routes (filename)) {
2642                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
2643                              "Cannot modify a connection that has an associated 'rule-' or 'rule6-' file");
2644                 return FALSE;
2645         }
2646
2647         return write_connection (connection, ifcfg_dir, filename, keyfile, NULL, error);
2648 }
2649