diff options
Diffstat (limited to 'net/xfrm')
-rw-r--r-- | net/xfrm/xfrm_state.c | 210 |
1 files changed, 159 insertions, 51 deletions
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 80f5f9dc2b9e..4a3832f81c37 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
@@ -38,6 +38,8 @@ EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth); | |||
38 | 38 | ||
39 | static DEFINE_SPINLOCK(xfrm_state_lock); | 39 | static DEFINE_SPINLOCK(xfrm_state_lock); |
40 | 40 | ||
41 | #define XFRM_DST_HSIZE 1024 | ||
42 | |||
41 | /* Hash table to find appropriate SA towards given target (endpoint | 43 | /* Hash table to find appropriate SA towards given target (endpoint |
42 | * of tunnel or destination of transport mode) allowed by selector. | 44 | * of tunnel or destination of transport mode) allowed by selector. |
43 | * | 45 | * |
@@ -49,6 +51,48 @@ static struct list_head xfrm_state_bysrc[XFRM_DST_HSIZE]; | |||
49 | static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE]; | 51 | static struct list_head xfrm_state_byspi[XFRM_DST_HSIZE]; |
50 | 52 | ||
51 | static __inline__ | 53 | static __inline__ |
54 | unsigned __xfrm4_dst_hash(xfrm_address_t *addr) | ||
55 | { | ||
56 | unsigned h; | ||
57 | h = ntohl(addr->a4); | ||
58 | h = (h ^ (h>>16)) % XFRM_DST_HSIZE; | ||
59 | return h; | ||
60 | } | ||
61 | |||
62 | static __inline__ | ||
63 | unsigned __xfrm6_dst_hash(xfrm_address_t *addr) | ||
64 | { | ||
65 | unsigned h; | ||
66 | h = ntohl(addr->a6[2]^addr->a6[3]); | ||
67 | h = (h ^ (h>>16)) % XFRM_DST_HSIZE; | ||
68 | return h; | ||
69 | } | ||
70 | |||
71 | static __inline__ | ||
72 | unsigned __xfrm4_src_hash(xfrm_address_t *addr) | ||
73 | { | ||
74 | return __xfrm4_dst_hash(addr); | ||
75 | } | ||
76 | |||
77 | static __inline__ | ||
78 | unsigned __xfrm6_src_hash(xfrm_address_t *addr) | ||
79 | { | ||
80 | return __xfrm6_dst_hash(addr); | ||
81 | } | ||
82 | |||
83 | static __inline__ | ||
84 | unsigned xfrm_src_hash(xfrm_address_t *addr, unsigned short family) | ||
85 | { | ||
86 | switch (family) { | ||
87 | case AF_INET: | ||
88 | return __xfrm4_src_hash(addr); | ||
89 | case AF_INET6: | ||
90 | return __xfrm6_src_hash(addr); | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static __inline__ | ||
52 | unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family) | 96 | unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family) |
53 | { | 97 | { |
54 | switch (family) { | 98 | switch (family) { |
@@ -60,6 +104,36 @@ unsigned xfrm_dst_hash(xfrm_address_t *addr, unsigned short family) | |||
60 | return 0; | 104 | return 0; |
61 | } | 105 | } |
62 | 106 | ||
107 | static __inline__ | ||
108 | unsigned __xfrm4_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto) | ||
109 | { | ||
110 | unsigned h; | ||
111 | h = ntohl(addr->a4^spi^proto); | ||
112 | h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; | ||
113 | return h; | ||
114 | } | ||
115 | |||
116 | static __inline__ | ||
117 | unsigned __xfrm6_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto) | ||
118 | { | ||
119 | unsigned h; | ||
120 | h = ntohl(addr->a6[2]^addr->a6[3]^spi^proto); | ||
121 | h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE; | ||
122 | return h; | ||
123 | } | ||
124 | |||
125 | static __inline__ | ||
126 | unsigned xfrm_spi_hash(xfrm_address_t *addr, u32 spi, u8 proto, unsigned short family) | ||
127 | { | ||
128 | switch (family) { | ||
129 | case AF_INET: | ||
130 | return __xfrm4_spi_hash(addr, spi, proto); | ||
131 | case AF_INET6: | ||
132 | return __xfrm6_spi_hash(addr, spi, proto); | ||
133 | } | ||
134 | return 0; /*XXX*/ | ||
135 | } | ||
136 | |||
63 | DECLARE_WAIT_QUEUE_HEAD(km_waitq); | 137 | DECLARE_WAIT_QUEUE_HEAD(km_waitq); |
64 | EXPORT_SYMBOL(km_waitq); | 138 | EXPORT_SYMBOL(km_waitq); |
65 | 139 | ||
@@ -342,6 +416,83 @@ xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, | |||
342 | return 0; | 416 | return 0; |
343 | } | 417 | } |
344 | 418 | ||
419 | static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family) | ||
420 | { | ||
421 | unsigned int h = xfrm_spi_hash(daddr, spi, proto, family); | ||
422 | struct xfrm_state *x; | ||
423 | |||
424 | list_for_each_entry(x, xfrm_state_byspi+h, byspi) { | ||
425 | if (x->props.family != family || | ||
426 | x->id.spi != spi || | ||
427 | x->id.proto != proto) | ||
428 | continue; | ||
429 | |||
430 | switch (family) { | ||
431 | case AF_INET: | ||
432 | if (x->id.daddr.a4 != daddr->a4) | ||
433 | continue; | ||
434 | break; | ||
435 | case AF_INET6: | ||
436 | if (!ipv6_addr_equal((struct in6_addr *)daddr, | ||
437 | (struct in6_addr *) | ||
438 | x->id.daddr.a6)) | ||
439 | continue; | ||
440 | break; | ||
441 | }; | ||
442 | |||
443 | xfrm_state_hold(x); | ||
444 | return x; | ||
445 | } | ||
446 | |||
447 | return NULL; | ||
448 | } | ||
449 | |||
450 | static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family) | ||
451 | { | ||
452 | unsigned int h = xfrm_src_hash(saddr, family); | ||
453 | struct xfrm_state *x; | ||
454 | |||
455 | list_for_each_entry(x, xfrm_state_bysrc+h, bysrc) { | ||
456 | if (x->props.family != family || | ||
457 | x->id.proto != proto) | ||
458 | continue; | ||
459 | |||
460 | switch (family) { | ||
461 | case AF_INET: | ||
462 | if (x->id.daddr.a4 != daddr->a4 || | ||
463 | x->props.saddr.a4 != saddr->a4) | ||
464 | continue; | ||
465 | break; | ||
466 | case AF_INET6: | ||
467 | if (!ipv6_addr_equal((struct in6_addr *)daddr, | ||
468 | (struct in6_addr *) | ||
469 | x->id.daddr.a6) || | ||
470 | !ipv6_addr_equal((struct in6_addr *)saddr, | ||
471 | (struct in6_addr *) | ||
472 | x->props.saddr.a6)) | ||
473 | continue; | ||
474 | break; | ||
475 | }; | ||
476 | |||
477 | xfrm_state_hold(x); | ||
478 | return x; | ||
479 | } | ||
480 | |||
481 | return NULL; | ||
482 | } | ||
483 | |||
484 | static inline struct xfrm_state * | ||
485 | __xfrm_state_locate(struct xfrm_state *x, int use_spi, int family) | ||
486 | { | ||
487 | if (use_spi) | ||
488 | return __xfrm_state_lookup(&x->id.daddr, x->id.spi, | ||
489 | x->id.proto, family); | ||
490 | else | ||
491 | return __xfrm_state_lookup_byaddr(&x->id.daddr, | ||
492 | &x->props.saddr, | ||
493 | x->id.proto, family); | ||
494 | } | ||
495 | |||
345 | struct xfrm_state * | 496 | struct xfrm_state * |
346 | xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | 497 | xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, |
347 | struct flowi *fl, struct xfrm_tmpl *tmpl, | 498 | struct flowi *fl, struct xfrm_tmpl *tmpl, |
@@ -353,14 +504,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | |||
353 | int acquire_in_progress = 0; | 504 | int acquire_in_progress = 0; |
354 | int error = 0; | 505 | int error = 0; |
355 | struct xfrm_state *best = NULL; | 506 | struct xfrm_state *best = NULL; |
356 | struct xfrm_state_afinfo *afinfo; | ||
357 | 507 | ||
358 | afinfo = xfrm_state_get_afinfo(family); | ||
359 | if (afinfo == NULL) { | ||
360 | *err = -EAFNOSUPPORT; | ||
361 | return NULL; | ||
362 | } | ||
363 | |||
364 | spin_lock_bh(&xfrm_state_lock); | 508 | spin_lock_bh(&xfrm_state_lock); |
365 | list_for_each_entry(x, xfrm_state_bydst+h, bydst) { | 509 | list_for_each_entry(x, xfrm_state_bydst+h, bydst) { |
366 | if (x->props.family == family && | 510 | if (x->props.family == family && |
@@ -406,8 +550,8 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | |||
406 | x = best; | 550 | x = best; |
407 | if (!x && !error && !acquire_in_progress) { | 551 | if (!x && !error && !acquire_in_progress) { |
408 | if (tmpl->id.spi && | 552 | if (tmpl->id.spi && |
409 | (x0 = afinfo->state_lookup(daddr, tmpl->id.spi, | 553 | (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi, |
410 | tmpl->id.proto)) != NULL) { | 554 | tmpl->id.proto, family)) != NULL) { |
411 | xfrm_state_put(x0); | 555 | xfrm_state_put(x0); |
412 | error = -EEXIST; | 556 | error = -EEXIST; |
413 | goto out; | 557 | goto out; |
@@ -457,7 +601,6 @@ out: | |||
457 | else | 601 | else |
458 | *err = acquire_in_progress ? -EAGAIN : error; | 602 | *err = acquire_in_progress ? -EAGAIN : error; |
459 | spin_unlock_bh(&xfrm_state_lock); | 603 | spin_unlock_bh(&xfrm_state_lock); |
460 | xfrm_state_put_afinfo(afinfo); | ||
461 | return x; | 604 | return x; |
462 | } | 605 | } |
463 | 606 | ||
@@ -584,34 +727,20 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re | |||
584 | return x; | 727 | return x; |
585 | } | 728 | } |
586 | 729 | ||
587 | static inline struct xfrm_state * | ||
588 | __xfrm_state_locate(struct xfrm_state_afinfo *afinfo, struct xfrm_state *x, | ||
589 | int use_spi) | ||
590 | { | ||
591 | if (use_spi) | ||
592 | return afinfo->state_lookup(&x->id.daddr, x->id.spi, x->id.proto); | ||
593 | else | ||
594 | return afinfo->state_lookup_byaddr(&x->id.daddr, &x->props.saddr, x->id.proto); | ||
595 | } | ||
596 | |||
597 | static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); | 730 | static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq); |
598 | 731 | ||
599 | int xfrm_state_add(struct xfrm_state *x) | 732 | int xfrm_state_add(struct xfrm_state *x) |
600 | { | 733 | { |
601 | struct xfrm_state_afinfo *afinfo; | ||
602 | struct xfrm_state *x1; | 734 | struct xfrm_state *x1; |
603 | int family; | 735 | int family; |
604 | int err; | 736 | int err; |
605 | int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); | 737 | int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); |
606 | 738 | ||
607 | family = x->props.family; | 739 | family = x->props.family; |
608 | afinfo = xfrm_state_get_afinfo(family); | ||
609 | if (unlikely(afinfo == NULL)) | ||
610 | return -EAFNOSUPPORT; | ||
611 | 740 | ||
612 | spin_lock_bh(&xfrm_state_lock); | 741 | spin_lock_bh(&xfrm_state_lock); |
613 | 742 | ||
614 | x1 = __xfrm_state_locate(afinfo, x, use_spi); | 743 | x1 = __xfrm_state_locate(x, use_spi, family); |
615 | if (x1) { | 744 | if (x1) { |
616 | xfrm_state_put(x1); | 745 | xfrm_state_put(x1); |
617 | x1 = NULL; | 746 | x1 = NULL; |
@@ -637,7 +766,6 @@ int xfrm_state_add(struct xfrm_state *x) | |||
637 | 766 | ||
638 | out: | 767 | out: |
639 | spin_unlock_bh(&xfrm_state_lock); | 768 | spin_unlock_bh(&xfrm_state_lock); |
640 | xfrm_state_put_afinfo(afinfo); | ||
641 | 769 | ||
642 | if (!err) | 770 | if (!err) |
643 | xfrm_flush_all_bundles(); | 771 | xfrm_flush_all_bundles(); |
@@ -653,17 +781,12 @@ EXPORT_SYMBOL(xfrm_state_add); | |||
653 | 781 | ||
654 | int xfrm_state_update(struct xfrm_state *x) | 782 | int xfrm_state_update(struct xfrm_state *x) |
655 | { | 783 | { |
656 | struct xfrm_state_afinfo *afinfo; | ||
657 | struct xfrm_state *x1; | 784 | struct xfrm_state *x1; |
658 | int err; | 785 | int err; |
659 | int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); | 786 | int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); |
660 | 787 | ||
661 | afinfo = xfrm_state_get_afinfo(x->props.family); | ||
662 | if (unlikely(afinfo == NULL)) | ||
663 | return -EAFNOSUPPORT; | ||
664 | |||
665 | spin_lock_bh(&xfrm_state_lock); | 788 | spin_lock_bh(&xfrm_state_lock); |
666 | x1 = __xfrm_state_locate(afinfo, x, use_spi); | 789 | x1 = __xfrm_state_locate(x, use_spi, x->props.family); |
667 | 790 | ||
668 | err = -ESRCH; | 791 | err = -ESRCH; |
669 | if (!x1) | 792 | if (!x1) |
@@ -683,7 +806,6 @@ int xfrm_state_update(struct xfrm_state *x) | |||
683 | 806 | ||
684 | out: | 807 | out: |
685 | spin_unlock_bh(&xfrm_state_lock); | 808 | spin_unlock_bh(&xfrm_state_lock); |
686 | xfrm_state_put_afinfo(afinfo); | ||
687 | 809 | ||
688 | if (err) | 810 | if (err) |
689 | return err; | 811 | return err; |
@@ -776,14 +898,10 @@ xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, | |||
776 | unsigned short family) | 898 | unsigned short family) |
777 | { | 899 | { |
778 | struct xfrm_state *x; | 900 | struct xfrm_state *x; |
779 | struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); | ||
780 | if (!afinfo) | ||
781 | return NULL; | ||
782 | 901 | ||
783 | spin_lock_bh(&xfrm_state_lock); | 902 | spin_lock_bh(&xfrm_state_lock); |
784 | x = afinfo->state_lookup(daddr, spi, proto); | 903 | x = __xfrm_state_lookup(daddr, spi, proto, family); |
785 | spin_unlock_bh(&xfrm_state_lock); | 904 | spin_unlock_bh(&xfrm_state_lock); |
786 | xfrm_state_put_afinfo(afinfo); | ||
787 | return x; | 905 | return x; |
788 | } | 906 | } |
789 | EXPORT_SYMBOL(xfrm_state_lookup); | 907 | EXPORT_SYMBOL(xfrm_state_lookup); |
@@ -793,14 +911,10 @@ xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, | |||
793 | u8 proto, unsigned short family) | 911 | u8 proto, unsigned short family) |
794 | { | 912 | { |
795 | struct xfrm_state *x; | 913 | struct xfrm_state *x; |
796 | struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); | ||
797 | if (!afinfo) | ||
798 | return NULL; | ||
799 | 914 | ||
800 | spin_lock_bh(&xfrm_state_lock); | 915 | spin_lock_bh(&xfrm_state_lock); |
801 | x = afinfo->state_lookup_byaddr(daddr, saddr, proto); | 916 | x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family); |
802 | spin_unlock_bh(&xfrm_state_lock); | 917 | spin_unlock_bh(&xfrm_state_lock); |
803 | xfrm_state_put_afinfo(afinfo); | ||
804 | return x; | 918 | return x; |
805 | } | 919 | } |
806 | EXPORT_SYMBOL(xfrm_state_lookup_byaddr); | 920 | EXPORT_SYMBOL(xfrm_state_lookup_byaddr); |
@@ -1272,11 +1386,8 @@ int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) | |||
1272 | write_lock_bh(&xfrm_state_afinfo_lock); | 1386 | write_lock_bh(&xfrm_state_afinfo_lock); |
1273 | if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) | 1387 | if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) |
1274 | err = -ENOBUFS; | 1388 | err = -ENOBUFS; |
1275 | else { | 1389 | else |
1276 | afinfo->state_bysrc = xfrm_state_bysrc; | ||
1277 | afinfo->state_byspi = xfrm_state_byspi; | ||
1278 | xfrm_state_afinfo[afinfo->family] = afinfo; | 1390 | xfrm_state_afinfo[afinfo->family] = afinfo; |
1279 | } | ||
1280 | write_unlock_bh(&xfrm_state_afinfo_lock); | 1391 | write_unlock_bh(&xfrm_state_afinfo_lock); |
1281 | return err; | 1392 | return err; |
1282 | } | 1393 | } |
@@ -1293,11 +1404,8 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) | |||
1293 | if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { | 1404 | if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { |
1294 | if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) | 1405 | if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) |
1295 | err = -EINVAL; | 1406 | err = -EINVAL; |
1296 | else { | 1407 | else |
1297 | xfrm_state_afinfo[afinfo->family] = NULL; | 1408 | xfrm_state_afinfo[afinfo->family] = NULL; |
1298 | afinfo->state_byspi = NULL; | ||
1299 | afinfo->state_bysrc = NULL; | ||
1300 | } | ||
1301 | } | 1409 | } |
1302 | write_unlock_bh(&xfrm_state_afinfo_lock); | 1410 | write_unlock_bh(&xfrm_state_afinfo_lock); |
1303 | return err; | 1411 | return err; |