platform: add nmp_netns_bind_to_path() helper function
[NetworkManager.git] / src / platform / nmp-netns.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* nm-platform.c - Handle runtime kernel networking configuration
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2016 Red Hat, Inc.
19  */
20
21 #include "nm-default.h"
22 #include "nmp-netns.h"
23
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <sys/mount.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30 #include "NetworkManagerUtils.h"
31
32 #define PROC_SELF_NS_MNT "/proc/self/ns/mnt"
33 #define PROC_SELF_NS_NET "/proc/self/ns/net"
34
35 #define _CLONE_NS_ALL    ((int) (CLONE_NEWNS | CLONE_NEWNET))
36 #define _CLONE_NS_ALL_V          CLONE_NEWNS , CLONE_NEWNET
37
38 NM_UTILS_FLAGS2STR_DEFINE_STATIC (_clone_ns_to_str, int,
39         NM_UTILS_FLAGS2STR (CLONE_NEWNS,  "mnt"),
40         NM_UTILS_FLAGS2STR (CLONE_NEWNET, "net"),
41 );
42
43 static const char *
44 __ns_types_to_str (int ns_types, int ns_types_already_set, char *buf, gsize len)
45 {
46         const char *b = buf;
47         char bb[200];
48
49         nm_utils_strbuf_append_c (&buf, &len, '[');
50         if (ns_types & ~ns_types_already_set) {
51                 nm_utils_strbuf_append_str (&buf, &len,
52                                             _clone_ns_to_str (ns_types & ~ns_types_already_set, bb, sizeof (bb)));
53         }
54         if (ns_types & ns_types_already_set) {
55                 if (ns_types & ~ns_types_already_set)
56                         nm_utils_strbuf_append_c (&buf, &len, '/');
57                 nm_utils_strbuf_append_str (&buf, &len,
58                                             _clone_ns_to_str (ns_types & ns_types_already_set, bb, sizeof (bb)));
59         }
60         nm_utils_strbuf_append_c (&buf, &len, ']');
61         return b;
62 }
63 #define _ns_types_to_str(ns_types, ns_types_already_set, buf) \
64         __ns_types_to_str (ns_types, ns_types_already_set, buf, sizeof (buf))
65
66 /*********************************************************************************************/
67
68 #define _NMLOG_DOMAIN        LOGD_PLATFORM
69 #define _NMLOG_PREFIX_NAME   "netns"
70 #define _NMLOG(level, netns, ...) \
71     G_STMT_START { \
72         NMLogLevel _level = (level); \
73         \
74         if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
75             NMPNetns *_netns = (netns); \
76             char _sbuf[20]; \
77             \
78             _nm_log (_level, _NMLOG_DOMAIN, 0, \
79                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
80                      _NMLOG_PREFIX_NAME, \
81                      (_netns ? nm_sprintf_buf (_sbuf, "[%p]", _netns) : "") \
82                      _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
83         } \
84     } G_STMT_END
85
86 /*********************************************************************************************/
87
88 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
89         PROP_FD_NET,
90         PROP_FD_MNT,
91 );
92
93 typedef struct _NMPNetnsPrivate NMPNetnsPrivate;
94
95 struct _NMPNetnsPrivate {
96         int fd_net;
97         int fd_mnt;
98 };
99
100 typedef struct {
101         NMPNetns *netns;
102         int count;
103         int ns_types;
104 } NetnsInfo;
105
106 static void _stack_push (NMPNetns *netns, int ns_types);
107 static NMPNetns *_netns_new (GError **error);
108
109 /*********************************************************************************************/
110
111 static GArray *netns_stack = NULL;
112
113 static void
114 _stack_ensure_init_impl (void)
115 {
116         NMPNetns *netns;
117         GError *error = NULL;
118
119         nm_assert (!netns_stack);
120
121         netns_stack = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
122
123         /* at the bottom of the stack we must try to create a netns instance
124          * that we never pop. It's the base to which we need to return. */
125
126         netns = _netns_new (&error);
127
128         if (!netns) {
129                 /* don't know how to recover from this error. Netns are not supported. */
130                 _LOGE (NULL, "failed to create initial netns: %s", error->message);
131                 g_clear_error (&error);
132                 return;
133         }
134
135         _stack_push (netns, _CLONE_NS_ALL);
136
137         /* we leak this instance inside netns_stack. It cannot be popped. */
138         g_object_unref (netns);
139 }
140 #define _stack_ensure_init() \
141         G_STMT_START { \
142                 if (G_UNLIKELY (!netns_stack)) { \
143                         _stack_ensure_init_impl (); \
144                 } \
145         } G_STMT_END
146
147 static NMPNetns *
148 _stack_current_netns (int ns_types)
149 {
150         guint j;
151
152         nm_assert (netns_stack && netns_stack->len > 0);
153
154         /* we search the stack top-down to find the netns that has
155          * all @ns_types set. */
156         for (j = netns_stack->len; ns_types && j >= 1; ) {
157                 NetnsInfo *info;
158
159                 info = &g_array_index (netns_stack, NetnsInfo, --j);
160
161                 if (NM_FLAGS_ALL (info->ns_types, ns_types))
162                         return info->netns;
163         }
164
165         g_return_val_if_reached (NULL);
166 }
167
168 static int
169 _stack_current_ns_types (NMPNetns *netns, int ns_types)
170 {
171         const int ns_types_check[] = { _CLONE_NS_ALL_V };
172         guint i, j;
173         int res = 0;
174
175         nm_assert (netns);
176         nm_assert (netns_stack && netns_stack->len > 0);
177
178         /* we search the stack top-down to check which of @ns_types
179          * are already set to @netns. */
180         for (j = netns_stack->len; ns_types && j >= 1; ) {
181                 NetnsInfo *info;
182
183                 info = &g_array_index (netns_stack, NetnsInfo, --j);
184                 if (info->netns != netns) {
185                         ns_types = NM_FLAGS_UNSET (ns_types, info->ns_types);
186                         continue;
187                 }
188
189                 for (i = 0; i < G_N_ELEMENTS (ns_types_check); i++) {
190                         if (   NM_FLAGS_HAS (ns_types, ns_types_check[i])
191                             && NM_FLAGS_HAS (info->ns_types, ns_types_check[i])) {
192                                 res = NM_FLAGS_SET (res, ns_types_check[i]);
193                                 ns_types = NM_FLAGS_UNSET (ns_types, ns_types_check[i]);
194                         }
195                 }
196         }
197
198         return res;
199 }
200
201 static NetnsInfo *
202 _stack_peek (void)
203 {
204         nm_assert (netns_stack);
205
206         if (netns_stack->len > 0)
207                 return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
208         return NULL;
209 }
210
211 static NetnsInfo *
212 _stack_bottom (void)
213 {
214         nm_assert (netns_stack);
215
216         if (netns_stack->len > 0)
217                 return &g_array_index (netns_stack, NetnsInfo, 0);
218         return NULL;
219 }
220
221 static void
222 _stack_push (NMPNetns *netns, int ns_types)
223 {
224         NetnsInfo *info;
225
226         nm_assert (netns_stack);
227         nm_assert (NMP_IS_NETNS (netns));
228         nm_assert (NM_FLAGS_ANY (ns_types, _CLONE_NS_ALL));
229         nm_assert (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL));
230
231         g_array_set_size (netns_stack, netns_stack->len + 1);
232
233         info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
234         info->netns = g_object_ref (netns);
235         info->ns_types = ns_types;
236         info->count = 1;
237 }
238
239 static void
240 _stack_pop (void)
241 {
242         NetnsInfo *info;
243
244         nm_assert (netns_stack);
245         nm_assert (netns_stack->len > 1);
246
247         info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
248
249         nm_assert (NMP_IS_NETNS (info->netns));
250         nm_assert (info->count == 1);
251
252         g_object_unref (info->netns);
253
254         g_array_set_size (netns_stack, netns_stack->len - 1);
255 }
256
257 static guint
258 _stack_size (void)
259 {
260         nm_assert (netns_stack);
261
262         return netns_stack->len;
263 }
264
265 /*********************************************************************************************/
266
267 G_DEFINE_TYPE (NMPNetns, nmp_netns, G_TYPE_OBJECT);
268
269 #define NMP_NETNS_GET_PRIVATE(o) ((o)->priv)
270
271 /*********************************************************************************************/
272
273 static NMPNetns *
274 _netns_new (GError **error)
275 {
276         NMPNetns *self;
277         int fd_net, fd_mnt;
278         int errsv;
279
280         fd_net = open (PROC_SELF_NS_NET, O_RDONLY);
281         if (fd_net == -1) {
282                 errsv = errno;
283                 g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
284                              "Failed opening netns: %s",
285                              g_strerror (errsv));
286                 return NULL;
287         }
288
289         fd_mnt = open (PROC_SELF_NS_MNT, O_RDONLY);
290         if (fd_mnt == -1) {
291                 errsv = errno;
292                 g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
293                              "Failed opening mntns: %s",
294                              g_strerror (errsv));
295                 close (fd_net);
296                 return NULL;
297         }
298
299         self = g_object_new (NMP_TYPE_NETNS,
300                              NMP_NETNS_FD_NET, fd_net,
301                              NMP_NETNS_FD_MNT, fd_mnt,
302                              NULL);
303
304         _LOGD (self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt);
305
306         return self;
307 }
308
309 static int
310 _setns (NMPNetns *self, int type)
311 {
312         char buf[100];
313         int fd;
314
315         nm_assert (NM_IN_SET (type, _CLONE_NS_ALL_V));
316
317         fd = (type == CLONE_NEWNET) ? self->priv->fd_net : self->priv->fd_mnt;
318
319         _LOGt (self, "set netns(%s, %d)", _ns_types_to_str (type, 0, buf), fd);
320
321         return setns (fd, type);
322 }
323
324 static gboolean
325 _netns_switch_push (NMPNetns *self, int ns_types)
326 {
327         int errsv;
328
329         if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
330             && !_stack_current_ns_types (self, CLONE_NEWNET)
331             && _setns (self, CLONE_NEWNET) != 0) {
332                 errsv = errno;
333                 _LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
334                 return FALSE;
335         }
336         if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
337             && !_stack_current_ns_types (self, CLONE_NEWNS)
338             && _setns (self, CLONE_NEWNS) != 0) {
339                 errsv = errno;
340                 _LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
341
342                 /* try to fix the mess by returning to the previous netns. */
343                 if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
344                 && !_stack_current_ns_types (self, CLONE_NEWNET)) {
345                         self = _stack_current_netns (CLONE_NEWNET);
346                         if (   self
347                             && _setns (self, CLONE_NEWNET) != 0) {
348                                 errsv = errno;
349                                 _LOGE (self, "failed to restore netns: %s", g_strerror (errsv));
350                         }
351                 }
352                 return FALSE;
353         }
354
355         return TRUE;
356 }
357
358 static gboolean
359 _netns_switch_pop (NMPNetns *self, int ns_types)
360 {
361         int errsv;
362         NMPNetns *current;
363         int success = TRUE;
364
365         if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
366             && (!self || !_stack_current_ns_types (self, CLONE_NEWNET))) {
367                 current = _stack_current_netns (CLONE_NEWNET);
368                 if (!current) {
369                         g_warn_if_reached ();
370                         success = FALSE;
371                 } else if (_setns (current, CLONE_NEWNET) != 0) {
372                         errsv = errno;
373                         _LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
374                         success = FALSE;
375                 }
376         }
377         if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
378             && (!self || !_stack_current_ns_types (self, CLONE_NEWNS))) {
379                 current = _stack_current_netns (CLONE_NEWNS);
380                 if (!current) {
381                         g_warn_if_reached ();
382                         success = FALSE;
383                 } else if (_setns (current, CLONE_NEWNS) != 0) {
384                         errsv = errno;
385                         _LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
386                         success = FALSE;
387                 }
388         }
389
390         return success;
391 }
392
393 /*********************************************************************************************/
394
395 int
396 nmp_netns_get_fd_net (NMPNetns *self)
397 {
398         g_return_val_if_fail (NMP_IS_NETNS (self), 0);
399
400         return self->priv->fd_net;
401 }
402
403 int
404 nmp_netns_get_fd_mnt (NMPNetns *self)
405 {
406         g_return_val_if_fail (NMP_IS_NETNS (self), 0);
407
408         return self->priv->fd_mnt;
409 }
410
411 /*********************************************************************************************/
412
413 static gboolean
414 _nmp_netns_push_type (NMPNetns *self, int ns_types)
415 {
416         NetnsInfo *info;
417         char sbuf[100];
418
419         _stack_ensure_init ();
420
421         info = _stack_peek ();
422         g_return_val_if_fail (info, FALSE);
423
424         if (info->netns == self && info->ns_types == ns_types) {
425                 info->count++;
426                 _LOGt (self, "push#%u* %s (increase count to %d)",
427                        _stack_size () - 1,
428                        _ns_types_to_str (ns_types, ns_types, sbuf), info->count);
429                 return TRUE;
430         }
431
432         _LOGD (self, "push#%u %s",
433                _stack_size (),
434                _ns_types_to_str (ns_types,
435                                  _stack_current_ns_types (self, ns_types),
436                                  sbuf));
437
438         if (!_netns_switch_push (self, ns_types))
439                 return FALSE;
440
441         _stack_push (self, ns_types);
442         return TRUE;
443 }
444
445 gboolean
446 nmp_netns_push (NMPNetns *self)
447 {
448         g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
449
450         return _nmp_netns_push_type (self, _CLONE_NS_ALL);
451 }
452
453 gboolean
454 nmp_netns_push_type (NMPNetns *self, int ns_types)
455 {
456         g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
457         g_return_val_if_fail (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL), FALSE);
458
459         return _nmp_netns_push_type (self, ns_types == 0 ? _CLONE_NS_ALL : ns_types);
460 }
461
462 NMPNetns *
463 nmp_netns_new (void)
464 {
465         NMPNetns *self;
466         int errsv;
467         GError *error = NULL;
468
469         _stack_ensure_init ();
470
471         if (!_stack_peek ()) {
472                 /* there are no netns instances. We cannot create a new one
473                  * (because after unshare we couldn't return to the original one). */
474                 return NULL;
475         }
476
477         if (unshare (_CLONE_NS_ALL) != 0) {
478                 errsv = errno;
479                 _LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
480                 return NULL;
481         }
482
483         if (mount ("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) {
484                 errsv = errno;
485                 _LOGE (NULL, "failed mount --make-rslave: %s", g_strerror (errsv));
486                 goto err_out;
487         }
488
489         if (umount2 ("/sys", MNT_DETACH) != 0) {
490                 errsv = errno;
491                 _LOGE (NULL, "failed umount /sys: %s", g_strerror (errsv));
492                 goto err_out;
493         }
494
495         if (mount ("sysfs", "/sys", "sysfs", 0, NULL) != 0) {
496                 errsv = errno;
497                 _LOGE (NULL, "failed mount /sys: %s", g_strerror (errsv));
498                 goto err_out;
499         }
500
501         self = _netns_new (&error);
502         if (!self) {
503                 _LOGE (NULL, "failed to create netns after unshare: %s", error->message);
504                 g_clear_error (&error);
505                 goto err_out;
506         }
507
508         _stack_push (self, _CLONE_NS_ALL);
509
510         return self;
511 err_out:
512         _netns_switch_pop (NULL, _CLONE_NS_ALL);
513         return NULL;
514 }
515
516 gboolean
517 nmp_netns_pop (NMPNetns *self)
518 {
519         NetnsInfo *info;
520         int ns_types;
521
522         g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
523
524         _stack_ensure_init ();
525
526         info = _stack_peek ();
527
528         g_return_val_if_fail (info, FALSE);
529         g_return_val_if_fail (info->netns == self, FALSE);
530
531         if (info->count > 1) {
532                 info->count--;
533                 _LOGt (self, "pop#%u* (decrease count to %d)",
534                        _stack_size () - 1, info->count);
535                 return TRUE;
536         }
537         g_return_val_if_fail (info->count == 1, FALSE);
538
539         /* cannot pop the original netns. */
540         g_return_val_if_fail (_stack_size () > 1, FALSE);
541
542         _LOGD (self, "pop#%u", _stack_size () - 1);
543
544         ns_types = info->ns_types;
545
546         _stack_pop ();
547
548         return _netns_switch_pop (self, ns_types);
549 }
550
551 NMPNetns *
552 nmp_netns_get_current (void)
553 {
554         NetnsInfo *info;
555
556         _stack_ensure_init ();
557
558         info = _stack_peek ();
559         return info ? info->netns : NULL;
560 }
561
562 NMPNetns *
563 nmp_netns_get_initial (void)
564 {
565         NetnsInfo *info;
566
567         _stack_ensure_init ();
568
569         info = _stack_bottom ();
570         return info ? info->netns : NULL;
571 }
572
573 gboolean
574 nmp_netns_is_initial (void)
575 {
576         if (G_UNLIKELY (!netns_stack))
577                 return TRUE;
578
579         return nmp_netns_get_current () == nmp_netns_get_initial ();
580 }
581
582 /*********************************************************************************************/
583
584 gboolean
585 nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
586 {
587         gs_free char *dirname = NULL;
588         int errsv;
589         int fd;
590         nm_auto_pop_netns NMPNetns *netns_pop = NULL;
591
592         g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
593         g_return_val_if_fail (filename && filename[0] == '/', FALSE);
594
595         if (!nmp_netns_push_type (self, CLONE_NEWNET))
596                 return FALSE;
597         netns_pop = self;
598
599         dirname = g_path_get_dirname (filename);
600         if (mkdir (dirname, 0) != 0) {
601                 errsv = errno;
602                 if (errsv != EEXIST) {
603                         _LOGE (self, "bind: failed to create directory %s: %s",
604                                dirname, g_strerror (errsv));
605                         return FALSE;
606                 }
607         }
608
609         if ((fd = creat (filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
610                 errsv = errno;
611                 _LOGE (self, "bind: failed to create %s: %s",
612                        filename, g_strerror (errsv));
613                 return FALSE;
614         }
615         close (fd);
616
617         if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) {
618                 errsv = errno;
619                 _LOGE (self, "bind: failed to mount %s to %s: %s",
620                        PROC_SELF_NS_NET, filename, g_strerror (errsv));
621                 unlink (filename);
622                 return FALSE;
623         }
624
625         if (out_fd) {
626                 if ((fd = open (filename, O_RDONLY)) == -1) {
627                         errsv = errno;
628                         _LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv));
629                         umount2 (filename, MNT_DETACH);
630                         unlink (filename);
631                         return FALSE;
632                 }
633                 *out_fd = fd;
634         }
635
636         return TRUE;
637 }
638
639 gboolean
640 nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename)
641 {
642         int errsv;
643
644         g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
645         g_return_val_if_fail (filename && filename[0] == '/', FALSE);
646
647         if (umount2 (filename, MNT_DETACH) != 0) {
648                 errsv = errno;
649                 _LOGE (self, "bind: failed to unmount2 %s: %s", filename, g_strerror (errsv));
650                 return FALSE;
651         }
652         if (unlink (filename) != 0) {
653                 errsv = errno;
654                 _LOGE (self, "bind: failed to unlink %s: %s", filename, g_strerror (errsv));
655                 return FALSE;
656         }
657         return TRUE;
658 }
659
660 /******************************************************************************/
661
662 static void
663 set_property (GObject *object, guint prop_id,
664               const GValue *value, GParamSpec *pspec)
665 {
666         NMPNetns *self = NMP_NETNS (object);
667
668         switch (prop_id) {
669         case PROP_FD_NET:
670                 /* construct only */
671                 self->priv->fd_net = g_value_get_int (value);
672                 g_return_if_fail (self->priv->fd_net > 0);
673                 break;
674         case PROP_FD_MNT:
675                 /* construct only */
676                 self->priv->fd_mnt = g_value_get_int (value);
677                 g_return_if_fail (self->priv->fd_mnt > 0);
678                 break;
679         default:
680                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
681                 break;
682         }
683 }
684
685 static void
686 nmp_netns_init (NMPNetns *self)
687 {
688         self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NMP_TYPE_NETNS, NMPNetnsPrivate);
689 }
690
691 static void
692 dispose (GObject *object)
693 {
694         NMPNetns *self = NMP_NETNS (object);
695
696         if (self->priv->fd_net > 0) {
697                 close (self->priv->fd_net);
698                 self->priv->fd_net = 0;
699         }
700
701         if (self->priv->fd_mnt > 0) {
702                 close (self->priv->fd_mnt);
703                 self->priv->fd_mnt = 0;
704         }
705
706         G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object);
707 }
708
709 static void
710 nmp_netns_class_init (NMPNetnsClass *klass)
711 {
712         GObjectClass *object_class = G_OBJECT_CLASS (klass);
713
714         g_type_class_add_private (klass, sizeof (NMPNetnsPrivate));
715
716         object_class->set_property = set_property;
717         object_class->dispose = dispose;
718
719         obj_properties[PROP_FD_NET]
720             = g_param_spec_int (NMP_NETNS_FD_NET, "", "",
721                                 0, G_MAXINT, 0,
722                                 G_PARAM_WRITABLE |
723                                 G_PARAM_CONSTRUCT_ONLY |
724                                 G_PARAM_STATIC_STRINGS);
725         obj_properties[PROP_FD_MNT]
726             = g_param_spec_int (NMP_NETNS_FD_MNT, "", "",
727                                 0, G_MAXINT, 0,
728                                 G_PARAM_WRITABLE |
729                                 G_PARAM_CONSTRUCT_ONLY |
730                                 G_PARAM_STATIC_STRINGS);
731         g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
732 }