~ubuntu-branches/ubuntu/raring/iproute/raring-proposed

« back to all changes in this revision

Viewing changes to .pc/make-ip-netns-delete-more-likely-to-succeed.patch/ip/ipnetns.c

  • Committer: Package Import Robot
  • Author(s): Chris J Arges
  • Date: 2013-10-15 14:50:33 UTC
  • Revision ID: package-import@ubuntu.com-20131015145033-porc4pg38nut4332
Tags: 20121211-2ubuntu1
Fix ip netns delete failures. (LP: #1238981)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define _ATFILE_SOURCE
 
2
#include <sys/types.h>
 
3
#include <sys/stat.h>
 
4
#include <sys/wait.h>
 
5
#include <sys/inotify.h>
 
6
#include <sys/mount.h>
 
7
#include <sys/param.h>
 
8
#include <sys/syscall.h>
 
9
#include <stdio.h>
 
10
#include <string.h>
 
11
#include <sched.h>
 
12
#include <fcntl.h>
 
13
#include <dirent.h>
 
14
#include <errno.h>
 
15
#include <unistd.h>
 
16
 
 
17
#include "utils.h"
 
18
#include "ip_common.h"
 
19
 
 
20
#define NETNS_RUN_DIR "/var/run/netns"
 
21
#define NETNS_ETC_DIR "/etc/netns"
 
22
 
 
23
#ifndef CLONE_NEWNET
 
24
#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
 
25
#endif
 
26
 
 
27
#ifndef MNT_DETACH
 
28
#define MNT_DETACH      0x00000002      /* Just detach from the tree */
 
29
#endif /* MNT_DETACH */
 
30
 
 
31
#ifndef HAVE_SETNS
 
32
static int setns(int fd, int nstype)
 
33
{
 
34
#ifdef __NR_setns
 
35
        return syscall(__NR_setns, fd, nstype);
 
36
#else
 
37
        errno = ENOSYS;
 
38
        return -1;
 
39
#endif
 
40
}
 
41
#endif /* HAVE_SETNS */
 
42
 
 
43
 
 
44
static void usage(void) __attribute__((noreturn));
 
45
 
 
46
static void usage(void)
 
47
{
 
48
        fprintf(stderr, "Usage: ip netns list\n");
 
49
        fprintf(stderr, "       ip netns add NAME\n");
 
50
        fprintf(stderr, "       ip netns delete NAME\n");
 
51
        fprintf(stderr, "       ip netns exec NAME cmd ...\n");
 
52
        fprintf(stderr, "       ip netns monitor\n");
 
53
        exit(-1);
 
54
}
 
55
 
 
56
int get_netns_fd(const char *name)
 
57
{
 
58
        char pathbuf[MAXPATHLEN];
 
59
        const char *path, *ptr;
 
60
 
 
61
        path = name;
 
62
        ptr = strchr(name, '/');
 
63
        if (!ptr) {
 
64
                snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
 
65
                        NETNS_RUN_DIR, name );
 
66
                path = pathbuf;
 
67
        }
 
68
        return open(path, O_RDONLY);
 
69
}
 
70
 
 
71
static int netns_list(int argc, char **argv)
 
72
{
 
73
        struct dirent *entry;
 
74
        DIR *dir;
 
75
 
 
76
        dir = opendir(NETNS_RUN_DIR);
 
77
        if (!dir)
 
78
                return 0;
 
79
 
 
80
        while ((entry = readdir(dir)) != NULL) {
 
81
                if (strcmp(entry->d_name, ".") == 0)
 
82
                        continue;
 
83
                if (strcmp(entry->d_name, "..") == 0)
 
84
                        continue;
 
85
                printf("%s\n", entry->d_name);
 
86
        }
 
87
        closedir(dir);
 
88
        return 0;
 
89
}
 
90
 
 
91
static void bind_etc(const char *name)
 
92
{
 
93
        char etc_netns_path[MAXPATHLEN];
 
94
        char netns_name[MAXPATHLEN];
 
95
        char etc_name[MAXPATHLEN];
 
96
        struct dirent *entry;
 
97
        DIR *dir;
 
98
 
 
99
        snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name);
 
100
        dir = opendir(etc_netns_path);
 
101
        if (!dir)
 
102
                return;
 
103
 
 
104
        while ((entry = readdir(dir)) != NULL) {
 
105
                if (strcmp(entry->d_name, ".") == 0)
 
106
                        continue;
 
107
                if (strcmp(entry->d_name, "..") == 0)
 
108
                        continue;
 
109
                snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name);
 
110
                snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name);
 
111
                if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) {
 
112
                        fprintf(stderr, "Bind %s -> %s failed: %s\n",
 
113
                                netns_name, etc_name, strerror(errno));
 
114
                }
 
115
        }
 
116
        closedir(dir);
 
117
}
 
118
 
 
119
static int netns_exec(int argc, char **argv)
 
120
{
 
121
        /* Setup the proper environment for apps that are not netns
 
122
         * aware, and execute a program in that environment.
 
123
         */
 
124
        const char *name, *cmd;
 
125
        char net_path[MAXPATHLEN];
 
126
        int netns;
 
127
 
 
128
        if (argc < 1) {
 
129
                fprintf(stderr, "No netns name specified\n");
 
130
                return -1;
 
131
        }
 
132
        if (argc < 2) {
 
133
                fprintf(stderr, "No cmd specified\n");
 
134
                return -1;
 
135
        }
 
136
        name = argv[0];
 
137
        cmd = argv[1];
 
138
        snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
 
139
        netns = open(net_path, O_RDONLY);
 
140
        if (netns < 0) {
 
141
                fprintf(stderr, "Cannot open network namespace: %s\n",
 
142
                        strerror(errno));
 
143
                return -1;
 
144
        }
 
145
        if (setns(netns, CLONE_NEWNET) < 0) {
 
146
                fprintf(stderr, "seting the network namespace failed: %s\n",
 
147
                        strerror(errno));
 
148
                return -1;
 
149
        }
 
150
 
 
151
        if (unshare(CLONE_NEWNS) < 0) {
 
152
                fprintf(stderr, "unshare failed: %s\n", strerror(errno));
 
153
                return -1;
 
154
        }
 
155
        /* Mount a version of /sys that describes the network namespace */
 
156
        if (umount2("/sys", MNT_DETACH) < 0) {
 
157
                fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
 
158
                return -1;
 
159
        }
 
160
        if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
 
161
                fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
 
162
                return -1;
 
163
        }
 
164
 
 
165
        /* Setup bind mounts for config files in /etc */
 
166
        bind_etc(name);
 
167
 
 
168
        if (execvp(cmd, argv + 1)  < 0)
 
169
                fprintf(stderr, "exec of %s failed: %s\n",
 
170
                        cmd, strerror(errno));
 
171
        exit(-1);
 
172
}
 
173
 
 
174
static int netns_delete(int argc, char **argv)
 
175
{
 
176
        const char *name;
 
177
        char netns_path[MAXPATHLEN];
 
178
 
 
179
        if (argc < 1) {
 
180
                fprintf(stderr, "No netns name specified\n");
 
181
                return -1;
 
182
        }
 
183
 
 
184
        name = argv[0];
 
185
        snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
 
186
        umount2(netns_path, MNT_DETACH);
 
187
        if (unlink(netns_path) < 0) {
 
188
                fprintf(stderr, "Cannot remove %s: %s\n",
 
189
                        netns_path, strerror(errno));
 
190
                return -1;
 
191
        }
 
192
        return 0;
 
193
}
 
194
 
 
195
static int netns_add(int argc, char **argv)
 
196
{
 
197
        /* This function creates a new network namespace and
 
198
         * a new mount namespace and bind them into a well known
 
199
         * location in the filesystem based on the name provided.
 
200
         *
 
201
         * The mount namespace is created so that any necessary
 
202
         * userspace tweaks like remounting /sys, or bind mounting
 
203
         * a new /etc/resolv.conf can be shared between uers.
 
204
         */
 
205
        char netns_path[MAXPATHLEN];
 
206
        const char *name;
 
207
        int fd;
 
208
 
 
209
        if (argc < 1) {
 
210
                fprintf(stderr, "No netns name specified\n");
 
211
                return -1;
 
212
        }
 
213
        name = argv[0];
 
214
 
 
215
        snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
 
216
 
 
217
        /* Create the base netns directory if it doesn't exist */
 
218
        mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
 
219
 
 
220
        /* Create the filesystem state */
 
221
        fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
 
222
        if (fd < 0) {
 
223
                fprintf(stderr, "Could not create %s: %s\n",
 
224
                        netns_path, strerror(errno));
 
225
                return -1;
 
226
        }
 
227
        close(fd);
 
228
        if (unshare(CLONE_NEWNET) < 0) {
 
229
                fprintf(stderr, "Failed to create a new network namespace: %s\n",
 
230
                        strerror(errno));
 
231
                goto out_delete;
 
232
        }
 
233
 
 
234
        /* Bind the netns last so I can watch for it */
 
235
        if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) {
 
236
                fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n",
 
237
                        netns_path, strerror(errno));
 
238
                goto out_delete;
 
239
        }
 
240
        return 0;
 
241
out_delete:
 
242
        netns_delete(argc, argv);
 
243
        exit(-1);
 
244
        return -1;
 
245
}
 
246
 
 
247
 
 
248
static int netns_monitor(int argc, char **argv)
 
249
{
 
250
        char buf[4096];
 
251
        struct inotify_event *event;
 
252
        int fd;
 
253
        fd = inotify_init();
 
254
        if (fd < 0) {
 
255
                fprintf(stderr, "inotify_init failed: %s\n",
 
256
                        strerror(errno));
 
257
                return -1;
 
258
        }
 
259
        if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) {
 
260
                fprintf(stderr, "inotify_add_watch failed: %s\n",
 
261
                        strerror(errno));
 
262
                return -1;
 
263
        }
 
264
        for(;;) {
 
265
                ssize_t len = read(fd, buf, sizeof(buf));
 
266
                if (len < 0) {
 
267
                        fprintf(stderr, "read failed: %s\n",
 
268
                                strerror(errno));
 
269
                        return -1;
 
270
                }
 
271
                for (event = (struct inotify_event *)buf;
 
272
                     (char *)event < &buf[len];
 
273
                     event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) {
 
274
                        if (event->mask & IN_CREATE)
 
275
                                printf("add %s\n", event->name);
 
276
                        if (event->mask & IN_DELETE)
 
277
                                printf("delete %s\n", event->name);
 
278
                }
 
279
        }
 
280
        return 0;
 
281
}
 
282
 
 
283
int do_netns(int argc, char **argv)
 
284
{
 
285
        if (argc < 1)
 
286
                return netns_list(0, NULL);
 
287
 
 
288
        if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) ||
 
289
            (matches(*argv, "lst") == 0))
 
290
                return netns_list(argc-1, argv+1);
 
291
 
 
292
        if (matches(*argv, "help") == 0)
 
293
                usage();
 
294
 
 
295
        if (matches(*argv, "add") == 0)
 
296
                return netns_add(argc-1, argv+1);
 
297
 
 
298
        if (matches(*argv, "delete") == 0)
 
299
                return netns_delete(argc-1, argv+1);
 
300
 
 
301
        if (matches(*argv, "exec") == 0)
 
302
                return netns_exec(argc-1, argv+1);
 
303
 
 
304
        if (matches(*argv, "monitor") == 0)
 
305
                return netns_monitor(argc-1, argv+1);
 
306
 
 
307
        fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
 
308
        exit(-1);
 
309
}