f1a110ba6181ebbdbc3acf5a2d29d971e5897110
[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  *  V0.04  switch to RCU based hash
12  *
13  */
14
15 #include <linux/config.h>
16 #include <linux/slab.h>
17 #include <linux/vserver/network_cmd.h>
18 #include <linux/rcupdate.h>
19 #include <net/tcp.h>
20
21 #include <asm/errno.h>
22
23
24 /*      __alloc_nx_info()
25
26         * allocate an initialized nx_info struct
27         * doesn't make it visible (hash)                        */
28
29 static struct nx_info *__alloc_nx_info(nid_t nid)
30 {
31         struct nx_info *new = NULL;
32
33         vxdprintk(VXD_CBIT(nid, 1), "alloc_nx_info(%d)*", nid);
34
35         /* would this benefit from a slab cache? */
36         new = kmalloc(sizeof(struct nx_info), GFP_KERNEL);
37         if (!new)
38                 return 0;
39
40         memset (new, 0, sizeof(struct nx_info));
41         new->nx_id = nid;
42         INIT_RCU_HEAD(&new->nx_rcu);
43         INIT_HLIST_NODE(&new->nx_hlist);
44         atomic_set(&new->nx_refcnt, 0);
45         atomic_set(&new->nx_usecnt, 0);
46
47         /* rest of init goes here */
48
49         vxdprintk(VXD_CBIT(nid, 0),
50                 "alloc_nx_info() = %p", new);
51         return new;
52 }
53
54 /*      __dealloc_nx_info()
55
56         * final disposal of nx_info                             */
57
58 static void __dealloc_nx_info(struct nx_info *nxi)
59 {
60         vxdprintk(VXD_CBIT(nid, 0),
61                 "dealloc_nx_info(%p)", nxi);
62
63         nxi->nx_hlist.next = LIST_POISON1;
64         nxi->nx_id = -1;
65
66         BUG_ON(atomic_read(&nxi->nx_usecnt));
67         BUG_ON(atomic_read(&nxi->nx_refcnt));
68
69         kfree(nxi);
70 }
71
72 static inline int __free_nx_info(struct nx_info *nxi)
73 {
74         int usecnt, refcnt;
75
76         BUG_ON(!nxi);
77
78         usecnt = atomic_read(&nxi->nx_usecnt);
79         BUG_ON(usecnt < 0);
80
81         refcnt = atomic_read(&nxi->nx_refcnt);
82         BUG_ON(refcnt < 0);
83
84         if (!usecnt)
85                 __dealloc_nx_info(nxi);
86         return usecnt;
87 }
88
89 static void __rcu_put_nx_info(struct rcu_head *head)
90 {
91         struct nx_info *nxi = container_of(head, struct nx_info, nx_rcu);
92
93         vxdprintk(VXD_CBIT(nid, 3),
94                 "__rcu_put_nx_info(%p[#%d]): %d,%d",
95                 nxi, nxi->nx_id,
96                 atomic_read(&nxi->nx_usecnt),
97                 atomic_read(&nxi->nx_refcnt));
98         put_nx_info(nxi);
99 }
100
101
102 /*      hash table for nx_info hash */
103
104 #define NX_HASH_SIZE    13
105
106 struct hlist_head nx_info_hash[NX_HASH_SIZE];
107
108 static spinlock_t nx_info_hash_lock = SPIN_LOCK_UNLOCKED;
109
110
111 static inline unsigned int __hashval(nid_t nid)
112 {
113         return (nid % NX_HASH_SIZE);
114 }
115
116
117
118 /*      __hash_nx_info()
119
120         * add the nxi to the global hash table
121         * requires the hash_lock to be held                     */
122
123 static inline void __hash_nx_info(struct nx_info *nxi)
124 {
125         struct hlist_head *head;
126
127         vxdprintk(VXD_CBIT(nid, 4),
128                 "__hash_nx_info: %p[#%d]", nxi, nxi->nx_id);
129         get_nx_info(nxi);
130         head = &nx_info_hash[__hashval(nxi->nx_id)];
131         hlist_add_head_rcu(&nxi->nx_hlist, head);
132 }
133
134 /*      __unhash_nx_info()
135
136         * remove the nxi from the global hash table
137         * requires the hash_lock to be held                     */
138
139 static inline void __unhash_nx_info(struct nx_info *nxi)
140 {
141         vxdprintk(VXD_CBIT(nid, 4),
142                 "__unhash_nx_info: %p[#%d]", nxi, nxi->nx_id);
143         hlist_del_rcu(&nxi->nx_hlist);
144         call_rcu(&nxi->nx_rcu, __rcu_put_nx_info);
145 }
146
147
148 /*      __lookup_nx_info()
149
150         * requires the rcu_read_lock()
151         * doesn't increment the nx_refcnt                       */
152
153 static inline struct nx_info *__lookup_nx_info(nid_t nid)
154 {
155         struct hlist_head *head = &nx_info_hash[__hashval(nid)];
156         struct hlist_node *pos;
157
158         hlist_for_each_rcu(pos, head) {
159                 struct nx_info *nxi =
160                         hlist_entry(pos, struct nx_info, nx_hlist);
161
162                 if (nxi->nx_id == nid) {
163                         return nxi;
164                 }
165         }
166         return NULL;
167 }
168
169
170 /*      __nx_dynamic_id()
171
172         * find unused dynamic nid
173         * requires the rcu_read_lock()
174         * requires the hash_lock to be held                     */
175
176 static inline nid_t __nx_dynamic_id(void)
177 {
178         static nid_t seq = MAX_N_CONTEXT;
179         nid_t barrier = seq;
180
181         do {
182                 if (++seq > MAX_N_CONTEXT)
183                         seq = MIN_D_CONTEXT;
184                 if (!__lookup_nx_info(seq)) {
185                         vxdprintk(VXD_CBIT(nid, 4),
186                                 "__nx_dynamic_id: [#%d]", seq);
187                         return seq;
188                 }
189         } while (barrier != seq);
190         return 0;
191 }
192
193 /*      __loc_nx_info()
194
195         * locate or create the requested context
196         * get() it and if new hash it                           */
197
198 static struct nx_info * __loc_nx_info(int id, int *err)
199 {
200         struct nx_info *new, *nxi = NULL;
201
202         vxdprintk(VXD_CBIT(nid, 1), "loc_nx_info(%d)*", id);
203
204         if (!(new = __alloc_nx_info(id))) {
205                 *err = -ENOMEM;
206                 return NULL;
207         }
208
209         /* FIXME is this required at all ? */
210         rcu_read_lock();
211         /* required to make dynamic xids unique */
212         spin_lock(&nx_info_hash_lock);
213
214         /* dynamic context requested */
215         if (id == NX_DYNAMIC_ID) {
216                 id = __nx_dynamic_id();
217                 if (!id) {
218                         printk(KERN_ERR "no dynamic context available.\n");
219                         goto out_unlock;
220                 }
221                 new->nx_id = id;
222         }
223         /* existing context requested */
224         else if ((nxi = __lookup_nx_info(id))) {
225                 /* context in setup is not available */
226                 if (nxi->nx_flags & VXF_STATE_SETUP) {
227                         vxdprintk(VXD_CBIT(nid, 0),
228                                 "loc_nx_info(%d) = %p (not available)", id, nxi);
229                         nxi = NULL;
230                         *err = -EBUSY;
231                 } else {
232                         vxdprintk(VXD_CBIT(nid, 0),
233                                 "loc_nx_info(%d) = %p (found)", id, nxi);
234                         get_nx_info(nxi);
235                         *err = 0;
236                 }
237                 goto out_unlock;
238         }
239
240         /* new context requested */
241         vxdprintk(VXD_CBIT(nid, 0),
242                 "loc_nx_info(%d) = %p (new)", id, new);
243         __hash_nx_info(get_nx_info(new));
244         nxi = new, new = NULL;
245         *err = 1;
246
247 out_unlock:
248         spin_unlock(&nx_info_hash_lock);
249         rcu_read_unlock();
250         if (new)
251                 __dealloc_nx_info(new);
252         return nxi;
253 }
254
255
256
257 /*      exported stuff                                          */
258
259 void free_nx_info(struct nx_info *nxi)
260 {
261         BUG_ON(__free_nx_info(nxi));
262 }
263
264 void unhash_nx_info(struct nx_info *nxi)
265 {
266         spin_lock(&nx_info_hash_lock);
267         __unhash_nx_info(nxi);
268         spin_unlock(&nx_info_hash_lock);
269 }
270
271 /*      locate_nx_info()
272
273         * search for a nx_info and get() it
274         * negative id means current                             */
275
276 struct nx_info *locate_nx_info(int id)
277 {
278         struct nx_info *nxi;
279
280         if (id < 0) {
281                 nxi = get_nx_info(current->nx_info);
282         } else {
283                 rcu_read_lock();
284                 nxi = get_nx_info(__lookup_nx_info(id));
285                 rcu_read_unlock();
286         }
287         return nxi;
288 }
289
290 /*      nx_info_is_hashed()
291
292         * verify that nid is still hashed                       */
293
294 int nx_info_is_hashed(nid_t nid)
295 {
296         int hashed;
297
298         rcu_read_lock();
299         hashed = (__lookup_nx_info(nid) != NULL);
300         rcu_read_unlock();
301         return hashed;
302 }
303
304 #ifdef  CONFIG_VSERVER_LEGACY
305
306 struct nx_info *locate_or_create_nx_info(int id)
307 {
308         int err;
309
310         return __loc_nx_info(id, &err);
311 }
312
313 struct nx_info *create_nx_info(void)
314 {
315         struct nx_info *new;
316         int err;
317
318         vxdprintk(VXD_CBIT(nid, 5), "create_nx_info(%s)", "void");
319         if (!(new = __loc_nx_info(NX_DYNAMIC_ID, &err)))
320                 return NULL;
321         return new;
322 }
323
324
325 #endif
326
327 #ifdef  CONFIG_PROC_FS
328
329 int get_nid_list(int index, unsigned int *nids, int size)
330 {
331         int hindex, nr_nids = 0;
332
333         rcu_read_lock();
334         for (hindex = 0; hindex < NX_HASH_SIZE; hindex++) {
335                 struct hlist_head *head = &nx_info_hash[hindex];
336                 struct hlist_node *pos;
337
338                 hlist_for_each_rcu(pos, head) {
339                         struct nx_info *nxi;
340
341                         if (--index > 0)
342                                 continue;
343
344                         nxi = hlist_entry(pos, struct nx_info, nx_hlist);
345                         nids[nr_nids] = nxi->nx_id;
346                         if (++nr_nids >= size)
347                                 goto out;
348                 }
349         }
350 out:
351         rcu_read_unlock();
352         return nr_nids;
353 }
354 #endif
355
356
357 /*
358  *      migrate task to new network
359  */
360
361 int nx_migrate_task(struct task_struct *p, struct nx_info *nxi)
362 {
363         struct nx_info *old_nxi;
364         int ret = 0;
365
366         if (!p || !nxi)
367                 BUG();
368
369         vxdprintk(VXD_CBIT(nid, 5),
370                 "nx_migrate_task(%p,%p[#%d.%d.%d])",
371                 p, nxi, nxi->nx_id,
372                 atomic_read(&nxi->nx_usecnt),
373                 atomic_read(&nxi->nx_refcnt));
374
375         old_nxi = task_get_nx_info(p);
376         if (old_nxi == nxi)
377                 goto out;
378
379         task_lock(p);
380         /* should be handled in set_nx_info !! */
381         if (old_nxi)
382                 clr_nx_info(&p->nx_info);
383         set_nx_info(&p->nx_info, nxi);
384         p->nid = nxi->nx_id;
385         task_unlock(p);
386
387         /* obsoleted by clr/set */
388         // put_nx_info(old_nxi);
389 out:
390         put_nx_info(old_nxi);
391         return ret;
392 }
393
394
395 #include <linux/netdevice.h>
396 #include <linux/inetdevice.h>
397
398
399 int ifa_in_nx_info(struct in_ifaddr *ifa, struct nx_info *nxi)
400 {
401         if (!nxi)
402                 return 1;
403         if (!ifa)
404                 return 0;
405         return addr_in_nx_info(nxi, ifa->ifa_address);
406 }
407
408 int dev_in_nx_info(struct net_device *dev, struct nx_info *nxi)
409 {
410         struct in_device *in_dev = __in_dev_get(dev);
411         struct in_ifaddr **ifap = NULL;
412         struct in_ifaddr *ifa = NULL;
413
414         if (!nxi)
415                 return 1;
416         if (!in_dev)
417                 return 0;
418
419         for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
420                 ifap = &ifa->ifa_next) {
421                 if (addr_in_nx_info(nxi, ifa->ifa_address))
422                         return 1;
423         }
424         return 0;
425 }
426
427 /*
428  *      check if address is covered by socket
429  *
430  *      sk:     the socket to check against
431  *      addr:   the address in question (must be != 0)
432  */
433 static inline int __addr_in_socket(struct sock *sk, uint32_t addr)
434 {
435         struct nx_info *nxi = sk->sk_nx_info;
436         uint32_t saddr = tcp_v4_rcv_saddr(sk);
437
438         vxdprintk(VXD_CBIT(net, 5),
439                 "__addr_in_socket(%p,%d.%d.%d.%d) %p:%d.%d.%d.%d %p;%lx",
440                 sk, VXD_QUAD(addr), nxi, VXD_QUAD(saddr), sk->sk_socket,
441                 (sk->sk_socket?sk->sk_socket->flags:0));
442
443         if (saddr) {
444                 /* direct address match */
445                 return (saddr == addr);
446         } else if (nxi) {
447                 /* match against nx_info */
448                 return addr_in_nx_info(nxi, addr);
449         } else {
450                 /* unrestricted any socket */
451                 return 1;
452         }
453 }
454
455
456 int nx_addr_conflict(struct nx_info *nxi, uint32_t addr, struct sock *sk)
457 {
458         vxdprintk(VXD_CBIT(net, 2),
459                 "nx_addr_conflict(%p,%p) %d.%d,%d.%d",
460                 nxi, sk, VXD_QUAD(addr));
461
462         if (addr) {
463                 /* check real address */
464                 return __addr_in_socket(sk, addr);
465         } else if (nxi) {
466                 /* check against nx_info */
467                 int i, n = nxi->nbipv4;
468
469                 for (i=0; i<n; i++)
470                         if (__addr_in_socket(sk, nxi->ipv4[i]))
471                                 return 1;
472                 return 0;
473         } else {
474                 /* check against any */
475                 return 1;
476         }
477 }
478
479
480 /* vserver syscall commands below here */
481
482 /* taks nid and nx_info functions */
483
484 #include <asm/uaccess.h>
485
486
487 int vc_task_nid(uint32_t id, void __user *data)
488 {
489         nid_t nid;
490
491         if (id) {
492                 struct task_struct *tsk;
493
494                 if (!vx_check(0, VX_ADMIN|VX_WATCH))
495                         return -EPERM;
496
497                 read_lock(&tasklist_lock);
498                 tsk = find_task_by_real_pid(id);
499                 nid = (tsk) ? tsk->nid : -ESRCH;
500                 read_unlock(&tasklist_lock);
501         }
502         else
503                 nid = current->nid;
504         return nid;
505 }
506
507
508 int vc_nx_info(uint32_t id, void __user *data)
509 {
510         struct nx_info *nxi;
511         struct vcmd_nx_info_v0 vc_data;
512
513         if (!vx_check(0, VX_ADMIN))
514                 return -ENOSYS;
515         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RESOURCE))
516                 return -EPERM;
517
518         nxi = locate_nx_info(id);
519         if (!nxi)
520                 return -ESRCH;
521
522         vc_data.nid = nxi->nx_id;
523         put_nx_info(nxi);
524
525         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
526                 return -EFAULT;
527         return 0;
528 }
529
530
531 /* network functions */
532
533 int vc_net_create(uint32_t nid, void __user *data)
534 {
535         // int ret = -ENOMEM;
536         struct nx_info *new_nxi;
537         int ret;
538
539         if (!capable(CAP_SYS_ADMIN))
540                 return -EPERM;
541
542         if ((nid >= MIN_D_CONTEXT) && (nid != VX_DYNAMIC_ID))
543                 return -EINVAL;
544
545         if (nid < 1)
546                 return -EINVAL;
547
548         new_nxi = __loc_nx_info(nid, &ret);
549         if (!new_nxi)
550                 return ret;
551         if (!(new_nxi->nx_flags & VXF_STATE_SETUP)) {
552                 ret = -EEXIST;
553                 goto out_put;
554         }
555
556         ret = new_nxi->nx_id;
557         nx_migrate_task(current, new_nxi);
558 out_put:
559         put_nx_info(new_nxi);
560         return ret;
561 }
562
563
564 int vc_net_migrate(uint32_t id, void __user *data)
565 {
566         struct nx_info *nxi;
567
568         if (!capable(CAP_SYS_ADMIN))
569                 return -EPERM;
570
571         nxi = locate_nx_info(id);
572         if (!nxi)
573                 return -ESRCH;
574         nx_migrate_task(current, nxi);
575         put_nx_info(nxi);
576         return 0;
577 }
578
579 int vc_net_add(uint32_t id, void __user *data)
580 {
581         struct nx_info *nxi;
582         struct vcmd_net_nx_v0 vc_data;
583
584         if (!capable(CAP_SYS_ADMIN))
585                 return -EPERM;
586         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
587                 return -EFAULT;
588
589         nxi = locate_nx_info(id);
590         if (!nxi)
591                 return -ESRCH;
592
593         // add ip to net context here
594         put_nx_info(nxi);
595         return 0;
596 }
597
598 int vc_net_remove(uint32_t id, void __user *data)
599 {
600         struct nx_info *nxi;
601         struct vcmd_net_nx_v0 vc_data;
602
603         if (!capable(CAP_SYS_ADMIN))
604                 return -EPERM;
605         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
606                 return -EFAULT;
607
608         nxi = locate_nx_info(id);
609         if (!nxi)
610                 return -ESRCH;
611
612         // rem ip from net context here
613         put_nx_info(nxi);
614         return 0;
615 }
616
617
618
619 int vc_get_nflags(uint32_t id, void __user *data)
620 {
621         struct nx_info *nxi;
622         struct vcmd_net_flags_v0 vc_data;
623
624         if (!capable(CAP_SYS_ADMIN))
625                 return -EPERM;
626
627         nxi = locate_nx_info(id);
628         if (!nxi)
629                 return -ESRCH;
630
631         vc_data.flagword = nxi->nx_flags;
632
633         /* special STATE flag handling */
634         vc_data.mask = vx_mask_flags(~0UL, nxi->nx_flags, IPF_ONE_TIME);
635
636         put_nx_info(nxi);
637
638         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
639                 return -EFAULT;
640         return 0;
641 }
642
643 int vc_set_nflags(uint32_t id, void __user *data)
644 {
645         struct nx_info *nxi;
646         struct vcmd_net_flags_v0 vc_data;
647         uint64_t mask, trigger;
648
649         if (!capable(CAP_SYS_ADMIN))
650                 return -EPERM;
651         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
652                 return -EFAULT;
653
654         nxi = locate_nx_info(id);
655         if (!nxi)
656                 return -ESRCH;
657
658         /* special STATE flag handling */
659         mask = vx_mask_mask(vc_data.mask, nxi->nx_flags, IPF_ONE_TIME);
660         trigger = (mask & nxi->nx_flags) ^ (mask & vc_data.flagword);
661         // if (trigger & IPF_STATE_SETUP)
662
663         nxi->nx_flags = vx_mask_flags(nxi->nx_flags,
664                 vc_data.flagword, mask);
665         put_nx_info(nxi);
666         return 0;
667 }
668
669 int vc_get_ncaps(uint32_t id, void __user *data)
670 {
671         struct nx_info *nxi;
672         struct vcmd_net_caps_v0 vc_data;
673
674         if (!capable(CAP_SYS_ADMIN))
675                 return -EPERM;
676
677         nxi = locate_nx_info(id);
678         if (!nxi)
679                 return -ESRCH;
680
681         vc_data.ncaps = nxi->nx_ncaps;
682         vc_data.cmask = ~0UL;
683         put_nx_info(nxi);
684
685         if (copy_to_user (data, &vc_data, sizeof(vc_data)))
686                 return -EFAULT;
687         return 0;
688 }
689
690 int vc_set_ncaps(uint32_t id, void __user *data)
691 {
692         struct nx_info *nxi;
693         struct vcmd_net_caps_v0 vc_data;
694
695         if (!capable(CAP_SYS_ADMIN))
696                 return -EPERM;
697         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
698                 return -EFAULT;
699
700         nxi = locate_nx_info(id);
701         if (!nxi)
702                 return -ESRCH;
703
704         nxi->nx_ncaps = vx_mask_flags(nxi->nx_ncaps,
705                 vc_data.ncaps, vc_data.cmask);
706         put_nx_info(nxi);
707         return 0;
708 }
709
710
711 #include <linux/module.h>
712
713 EXPORT_SYMBOL_GPL(free_nx_info);
714 EXPORT_SYMBOL_GPL(unhash_nx_info);
715