This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / kernel / vserver / network.c
1 /*
2  *  linux/kernel/vserver/network.c
3  *
4  *  Virtual Server: Network Support
5  *
6  *  Copyright (C) 2003-2004  Herbert Pƶtzl
7  *
8  *  V0.01  broken out from vcontext V0.05
9  *  V0.02  cleaned up implementation
10  *  V0.03  added equiv nx commands
11  *
12  */
13
14 #include <linux/config.h>
15 #include <linux/slab.h>
16 #include <linux/vserver/network.h>
17 #include <linux/ninline.h>
18
19 #include <asm/errno.h>
20
21
22 LIST_HEAD(nx_infos);
23
24 spinlock_t nxlist_lock
25         __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
26
27
28 /*
29  *      struct nx_info allocation and deallocation
30  */
31
32 static struct nx_info *alloc_nx_info(void)
33 {
34         struct nx_info *new = NULL;
35         
36         nxdprintk("alloc_nx_info()\n");
37         /* would this benefit from a slab cache? */
38         new = kmalloc(sizeof(struct nx_info), GFP_KERNEL);
39         if (!new)
40                 return 0;
41         
42         memset (new, 0, sizeof(struct nx_info));
43         /* rest of init goes here */
44         
45         nxdprintk("alloc_nx_info() = %p\n", new);
46         return new;
47 }
48
49 void free_nx_info(struct nx_info *nxi)
50 {
51         nxdprintk("free_nx_info(%p)\n", nxi);
52         kfree(nxi);
53 }
54
55 struct nx_info *create_nx_info(void)
56 {
57         struct nx_info *new;
58         static int gnid = 1;
59         
60         nxdprintk("create_nx_info()\n");
61         if (!(new = alloc_nx_info()))
62                 return 0;
63
64         spin_lock(&nxlist_lock);
65
66         /* new ip info */
67         atomic_set(&new->nx_refcount, 1);
68         new->nx_id = gnid++;
69         list_add(&new->nx_list, &nx_infos);
70
71         spin_unlock(&nxlist_lock);
72         return new;
73 }
74
75
76 /*
77  *      struct nx_info search by id
78  *      assumes nxlist_lock is held
79  */
80
81 static __inline__ struct nx_info *__find_nx_info(int id)
82 {
83         struct nx_info *nxi;
84
85         list_for_each_entry(nxi, &nx_infos, nx_list)
86                 if (nxi->nx_id == id)
87                         return nxi;
88         return 0;
89 }
90
91
92 /*
93  *      struct nx_info ref stuff
94  */
95
96 struct nx_info *find_nx_info(int id)
97 {
98         struct nx_info *nxi;
99         
100         if (id < 0) {
101                 nxi = current->nx_info;
102                 get_nx_info(nxi);
103         } else {
104                 spin_lock(&nxlist_lock);
105                 if ((nxi = __find_nx_info(id)))
106                         get_nx_info(nxi);
107                 spin_unlock(&nxlist_lock);
108         }
109         return nxi;
110 }
111
112 /*
113  *      verify that id is a valid nid
114  */
115
116 int nx_info_id_valid(int id)
117 {
118         int valid;
119         
120         spin_lock(&nxlist_lock);
121         valid = (__find_nx_info(id) != NULL);
122         spin_unlock(&nxlist_lock);
123         return valid;
124 }
125
126
127 /*
128  *      dynamic context id ...
129  */
130
131 static __inline__ nid_t __nx_dynamic_id(void)
132 {
133         static nid_t seq = MAX_N_CONTEXT;
134         nid_t barrier = seq;
135         
136         do {
137                 if (++seq > MAX_N_CONTEXT)
138                         seq = MIN_D_CONTEXT;
139                 if (!__find_nx_info(seq))
140                         return seq;
141         } while (barrier != seq);
142         return 0;
143 }
144
145 static struct nx_info * __foc_nx_info(int id, int *err)
146 {
147         struct nx_info *new, *nxi = NULL;
148         
149         nxdprintk("foc_nx_info(%d)\n", id);
150         // if (!(new = alloc_nx_info(id))) {
151         if (!(new = alloc_nx_info())) {
152                 *err = -ENOMEM;
153                 return NULL;
154         }
155
156         spin_lock(&nxlist_lock);
157
158         /* dynamic context requested */
159         if (id == IP_DYNAMIC_ID) {
160                 id = __nx_dynamic_id();
161                 if (!id) {
162                         printk(KERN_ERR "no dynamic context available.\n");
163                         goto out_unlock;
164                 }
165                 new->nx_id = id;
166         }
167         /* existing context requested */
168         else if ((nxi = __find_nx_info(id))) {
169                 /* context in setup is not available */
170                 if (nxi->nx_flags & VXF_STATE_SETUP) {
171                         nxdprintk("foc_nx_info(%d) = %p (not available)\n", id, nxi);
172                         nxi = NULL;
173                         *err = -EBUSY;
174                 } else {
175                         nxdprintk("foc_nx_info(%d) = %p (found)\n", id, nxi);
176                         get_nx_info(nxi);
177                         *err = 0;
178                 }
179                 goto out_unlock;
180         }
181
182         /* new context requested */
183         nxdprintk("foc_nx_info(%d) = %p (new)\n", id, new);
184         atomic_set(&new->nx_refcount, 1);
185         list_add(&new->nx_list, &nx_infos);
186         nxi = new, new = NULL;
187         *err = 1;
188
189 out_unlock:
190         spin_unlock(&nxlist_lock);
191         if (new)
192                 free_nx_info(new);
193         return nxi;
194 }
195
196
197 struct nx_info *find_or_create_nx_info(int id)
198 {
199         int err;
200
201         return __foc_nx_info(id, &err);
202 }
203
204 /*
205  *      migrate task to new network
206  */
207
208 int nx_migrate_task(struct task_struct *p, struct nx_info *nxi)
209 {
210         struct nx_info *old_nxi = task_get_nx_info(p);
211         int ret = 0;
212         
213         if (!p || !nxi)
214                 BUG();
215
216         nxdprintk("nx_migrate_task(%p,%p[#%d.%d)\n", p, nxi,
217                 nxi->nx_id, atomic_read(&nxi->nx_refcount));
218         if (old_nxi == nxi)
219                 goto out;
220
221         task_lock(p);
222         set_nx_info(&p->nx_info, nxi);
223         p->nid = nxi->nx_id;
224         task_unlock(p);
225
226         put_nx_info(old_nxi);
227 out:
228         put_nx_info(old_nxi);
229         return ret;
230 }
231
232
233 #include <linux/netdevice.h>
234 #include <linux/inetdevice.h>
235
236 static inline int __addr_in_nx_info(u32 addr, struct nx_info *nxi)
237 {
238         int i, nbip;
239
240         nbip = nxi->nbipv4;
241         for (i=0; i<nbip; i++)
242                 if (nxi->ipv4[i] == addr)
243                         return 1;
244         return 0;
245 }
246
247 int ifa_in_nx_info(struct in_ifaddr *ifa, struct nx_info *nxi)
248 {
249         if (!nxi)
250                 return 1;
251         
252         return __addr_in_nx_info(ifa->ifa_address, nxi);
253 }
254
255 int dev_in_nx_info(struct net_device *dev, struct nx_info *nxi)
256 {
257         struct in_device *in_dev = __in_dev_get(dev);
258         struct in_ifaddr **ifap = NULL;
259         struct in_ifaddr *ifa = NULL;
260
261         if (!nxi)
262                 return 1;
263         if (!in_dev)
264                 return 0;
265
266         for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
267                 ifap = &ifa->ifa_next) {
268                 if (__addr_in_nx_info(ifa->ifa_address, nxi))
269                         return 1;
270         }
271         return 0;
272 }
273
274
275
276
277 /* vserver syscall commands below here */
278
279 /* taks nid and nx_info functions */
280
281 #include <asm/uaccess.h>
282
283
284 int vc_task_nid(uint32_t id, void __user *data)
285 {
286         nid_t nid;
287
288         if (id) {
289                 struct task_struct *tsk;
290
291                 if (!vx_check(0, VX_ADMIN|VX_WATCH))
292                         return -EPERM;
293
294                 read_lock(&tasklist_lock);
295                 tsk = find_task_by_pid(id);
296                 nid = (tsk) ? tsk->nid : -ESRCH;
297                 read_unlock(&tasklist_lock);
298         }
299         else
300                 nid = current->nid;
301         return nid;
302 }
303
304
305 int vc_nx_info(uint32_t id, void __user *data)
306 {
307         struct nx_info *nxi;
308         struct vcmd_nx_info_v0 vc_data;
309
310         if (!vx_check(0, VX_ADMIN))
311                 return -ENOSYS;
312         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RESOURCE))
313                 return -EPERM;
314
315         nxi = find_nx_info(id);
316         if (!nxi)
317                 return -ESRCH;
318
319         vc_data.nid = nxi->nx_id;
320         put_nx_info(nxi);
321
322         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
323                 return -EFAULT;
324         return 0;
325 }
326
327
328 /* network functions */
329
330 int vc_net_create(uint32_t nid, void __user *data)
331 {
332         // int ret = -ENOMEM;
333         struct nx_info *new_nxi;
334         int ret;
335
336         if (!capable(CAP_SYS_ADMIN))
337                 return -EPERM;
338
339         if ((nid >= MIN_D_CONTEXT) && (nid != VX_DYNAMIC_ID))
340                 return -EINVAL;
341
342         if (nid < 1)
343                 return -EINVAL;
344
345         new_nxi = __foc_nx_info(nid, &ret);
346         if (!new_nxi)
347                 return ret;
348         if (!(new_nxi->nx_flags & VXF_STATE_SETUP)) {
349                 ret = -EEXIST;
350                 goto out_put;
351         }
352
353         ret = new_nxi->nx_id;
354         nx_migrate_task(current, new_nxi);
355 out_put:
356         put_nx_info(new_nxi);
357         return ret;
358 }
359
360
361 int vc_net_migrate(uint32_t id, void __user *data)
362 {
363         struct nx_info *nxi;
364         
365         if (!capable(CAP_SYS_ADMIN))
366                 return -EPERM;
367
368         nxi = find_nx_info(id);
369         if (!nxi)
370                 return -ESRCH;
371         nx_migrate_task(current, nxi);
372         put_nx_info(nxi);
373         return 0;
374 }
375
376 int vc_net_add(uint32_t id, void __user *data)
377 {
378         struct nx_info *nxi;
379         struct vcmd_net_nx_v0 vc_data;
380
381         if (!capable(CAP_SYS_ADMIN))
382                 return -EPERM;
383         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
384                 return -EFAULT;
385
386         nxi = find_nx_info(id);
387         if (!nxi)
388                 return -ESRCH;
389
390         // add ip to net context here
391         put_nx_info(nxi);
392         return 0;
393 }
394
395 int vc_net_remove(uint32_t id, void __user *data)
396 {
397         struct nx_info *nxi;
398         struct vcmd_net_nx_v0 vc_data;
399
400         if (!capable(CAP_SYS_ADMIN))
401                 return -EPERM;
402         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
403                 return -EFAULT;
404
405         nxi = find_nx_info(id);
406         if (!nxi)
407                 return -ESRCH;
408
409         // rem ip from net context here
410         put_nx_info(nxi);
411         return 0;
412 }
413
414
415
416 int vc_get_nflags(uint32_t id, void __user *data)
417 {
418         struct nx_info *nxi;
419         struct vcmd_net_flags_v0 vc_data;
420
421         if (!capable(CAP_SYS_ADMIN))
422                 return -EPERM;
423
424         nxi = find_nx_info(id);
425         if (!nxi)
426                 return -ESRCH;
427
428         vc_data.flagword = nxi->nx_flags;
429
430         // vc_data.mask = ~0UL;
431         /* special STATE flag handling */
432         vc_data.mask = vx_mask_flags(~0UL, nxi->nx_flags, IPF_ONE_TIME);
433
434         put_nx_info(nxi);
435
436         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
437                 return -EFAULT;
438         return 0;
439 }
440
441 int vc_set_nflags(uint32_t id, void __user *data)
442 {
443         struct nx_info *nxi;
444         struct vcmd_net_flags_v0 vc_data;
445         uint64_t mask, trigger;
446
447         if (!capable(CAP_SYS_ADMIN))
448                 return -EPERM;
449         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
450                 return -EFAULT;
451
452         nxi = find_nx_info(id);
453         if (!nxi)
454                 return -ESRCH;
455
456         /* special STATE flag handling */
457         mask = vx_mask_mask(vc_data.mask, nxi->nx_flags, IPF_ONE_TIME);
458         trigger = (mask & nxi->nx_flags) ^ (mask & vc_data.flagword);
459         // if (trigger & IPF_STATE_SETUP)
460
461         nxi->nx_flags = vx_mask_flags(nxi->nx_flags,
462                 vc_data.flagword, mask);
463         put_nx_info(nxi);
464         return 0;
465 }
466
467 int vc_get_ncaps(uint32_t id, void __user *data)
468 {
469         struct nx_info *nxi;
470         struct vcmd_net_caps_v0 vc_data;
471
472         if (!capable(CAP_SYS_ADMIN))
473                 return -EPERM;
474
475         nxi = find_nx_info(id);
476         if (!nxi)
477                 return -ESRCH;
478
479         vc_data.ncaps = nxi->nx_ncaps;
480         vc_data.cmask = ~0UL;
481         put_nx_info(nxi);
482
483         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
484                 return -EFAULT;
485         return 0;
486 }
487
488 int vc_set_ncaps(uint32_t id, void __user *data)
489 {
490         struct nx_info *nxi;
491         struct vcmd_net_caps_v0 vc_data;
492
493         if (!capable(CAP_SYS_ADMIN))
494                 return -EPERM;
495         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
496                 return -EFAULT;
497
498         nxi = find_nx_info(id);
499         if (!nxi)
500                 return -ESRCH;
501
502         nxi->nx_ncaps = vx_mask_flags(nxi->nx_ncaps,
503                 vc_data.ncaps, vc_data.cmask);
504         put_nx_info(nxi);
505         return 0;
506 }
507
508
509 #include <linux/module.h>
510
511 EXPORT_SYMBOL_GPL(free_nx_info);
512 EXPORT_SYMBOL_GPL(nxlist_lock);
513