aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2018-11-07 17:00:38 -0500
committerSteffen Klassert <steffen.klassert@secunet.com>2018-11-09 05:58:07 -0500
commit9cf545ebd591da673bb6b6c88150212ad83567a9 (patch)
treed394bcd44723236e3ef7b7bc3edb14f4d2aa2977 /net/xfrm
parent6be3b0db6db82cf056a72cc18042048edd27f8ee (diff)
xfrm: policy: store inexact policies in a tree ordered by destination address
This adds inexact lists per destination network, stored in a search tree. Inexact lookups now return two 'candidate lists', the 'any' policies ('any' destionations), and a list of policies that share same daddr/prefix. Next patch will add a second search tree for 'saddr:any' policies so we can avoid placing those on the 'any:any' list too. Signed-off-by: Florian Westphal <fw@strlen.de> Acked-by: David S. Miller <davem@davemloft.net> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_policy.c333
1 files changed, 327 insertions, 6 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 4eb12e9b40c2..81447d5d02e6 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -49,6 +49,38 @@ struct xfrm_flo {
49/* prefixes smaller than this are stored in lists, not trees. */ 49/* prefixes smaller than this are stored in lists, not trees. */
50#define INEXACT_PREFIXLEN_IPV4 16 50#define INEXACT_PREFIXLEN_IPV4 16
51#define INEXACT_PREFIXLEN_IPV6 48 51#define INEXACT_PREFIXLEN_IPV6 48
52
53struct xfrm_pol_inexact_node {
54 struct rb_node node;
55 union {
56 xfrm_address_t addr;
57 struct rcu_head rcu;
58 };
59 u8 prefixlen;
60
61 /* the policies matching this node, can be empty list */
62 struct hlist_head hhead;
63};
64
65/* xfrm inexact policy search tree:
66 * xfrm_pol_inexact_bin = hash(dir,type,family,if_id);
67 * |
68 * +---- root_d: sorted by daddr:prefix
69 * | |
70 * | xfrm_pol_inexact_node
71 * | |
72 * | +- coarse policies and all any:daddr policies
73 * |
74 * +---- coarse policies and all any:any policies
75 *
76 * Lookups return two candidate lists:
77 * 1. any:any list from top-level xfrm_pol_inexact_bin
78 * 2. any:daddr list from daddr tree
79 *
80 * This result set then needs to be searched for the policy with
81 * the lowest priority. If two results have same prio, youngest one wins.
82 */
83
52struct xfrm_pol_inexact_key { 84struct xfrm_pol_inexact_key {
53 possible_net_t net; 85 possible_net_t net;
54 u32 if_id; 86 u32 if_id;
@@ -62,12 +94,17 @@ struct xfrm_pol_inexact_bin {
62 /* list containing '*:*' policies */ 94 /* list containing '*:*' policies */
63 struct hlist_head hhead; 95 struct hlist_head hhead;
64 96
97 seqcount_t count;
98 /* tree sorted by daddr/prefix */
99 struct rb_root root_d;
100
65 /* slow path below */ 101 /* slow path below */
66 struct list_head inexact_bins; 102 struct list_head inexact_bins;
67 struct rcu_head rcu; 103 struct rcu_head rcu;
68}; 104};
69 105
70enum xfrm_pol_inexact_candidate_type { 106enum xfrm_pol_inexact_candidate_type {
107 XFRM_POL_CAND_DADDR,
71 XFRM_POL_CAND_ANY, 108 XFRM_POL_CAND_ANY,
72 109
73 XFRM_POL_CAND_MAX, 110 XFRM_POL_CAND_MAX,
@@ -658,6 +695,8 @@ xfrm_policy_inexact_alloc_bin(const struct xfrm_policy *pol, u8 dir)
658 695
659 bin->k = k; 696 bin->k = k;
660 INIT_HLIST_HEAD(&bin->hhead); 697 INIT_HLIST_HEAD(&bin->hhead);
698 bin->root_d = RB_ROOT;
699 seqcount_init(&bin->count);
661 700
662 prev = rhashtable_lookup_get_insert_key(&xfrm_policy_inexact_table, 701 prev = rhashtable_lookup_get_insert_key(&xfrm_policy_inexact_table,
663 &bin->k, &bin->head, 702 &bin->k, &bin->head,
@@ -708,9 +747,211 @@ xfrm_policy_inexact_insert_use_any_list(const struct xfrm_policy *policy)
708 return saddr_any && daddr_any; 747 return saddr_any && daddr_any;
709} 748}
710 749
750static void xfrm_pol_inexact_node_init(struct xfrm_pol_inexact_node *node,
751 const xfrm_address_t *addr, u8 prefixlen)
752{
753 node->addr = *addr;
754 node->prefixlen = prefixlen;
755}
756
757static struct xfrm_pol_inexact_node *
758xfrm_pol_inexact_node_alloc(const xfrm_address_t *addr, u8 prefixlen)
759{
760 struct xfrm_pol_inexact_node *node;
761
762 node = kzalloc(sizeof(*node), GFP_ATOMIC);
763 if (node)
764 xfrm_pol_inexact_node_init(node, addr, prefixlen);
765
766 return node;
767}
768
769static int xfrm_policy_addr_delta(const xfrm_address_t *a,
770 const xfrm_address_t *b,
771 u8 prefixlen, u16 family)
772{
773 unsigned int pdw, pbi;
774 int delta = 0;
775
776 switch (family) {
777 case AF_INET:
778 if (sizeof(long) == 4 && prefixlen == 0)
779 return ntohl(a->a4) - ntohl(b->a4);
780 return (ntohl(a->a4) & ((~0UL << (32 - prefixlen)))) -
781 (ntohl(b->a4) & ((~0UL << (32 - prefixlen))));
782 case AF_INET6:
783 pdw = prefixlen >> 5;
784 pbi = prefixlen & 0x1f;
785
786 if (pdw) {
787 delta = memcmp(a->a6, b->a6, pdw << 2);
788 if (delta)
789 return delta;
790 }
791 if (pbi) {
792 u32 mask = ~0u << (32 - pbi);
793
794 delta = (ntohl(a->a6[pdw]) & mask) -
795 (ntohl(b->a6[pdw]) & mask);
796 }
797 break;
798 default:
799 break;
800 }
801
802 return delta;
803}
804
805static void xfrm_policy_inexact_list_reinsert(struct net *net,
806 struct xfrm_pol_inexact_node *n,
807 u16 family)
808{
809 struct hlist_node *newpos = NULL;
810 struct xfrm_policy *policy, *p;
811
812 list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
813 if (!policy->bydst_reinsert)
814 continue;
815
816 WARN_ON_ONCE(policy->family != family);
817
818 policy->bydst_reinsert = false;
819 hlist_for_each_entry(p, &n->hhead, bydst) {
820 if (policy->priority >= p->priority)
821 newpos = &p->bydst;
822 else
823 break;
824 }
825
826 if (newpos)
827 hlist_add_behind(&policy->bydst, newpos);
828 else
829 hlist_add_head(&policy->bydst, &n->hhead);
830 }
831}
832
833/* merge nodes v and n */
834static void xfrm_policy_inexact_node_merge(struct net *net,
835 struct xfrm_pol_inexact_node *v,
836 struct xfrm_pol_inexact_node *n,
837 u16 family)
838{
839 struct xfrm_policy *tmp;
840
841 hlist_for_each_entry(tmp, &v->hhead, bydst)
842 tmp->bydst_reinsert = true;
843 hlist_for_each_entry(tmp, &n->hhead, bydst)
844 tmp->bydst_reinsert = true;
845
846 INIT_HLIST_HEAD(&n->hhead);
847 xfrm_policy_inexact_list_reinsert(net, n, family);
848}
849
850static struct xfrm_pol_inexact_node *
851xfrm_policy_inexact_insert_node(struct net *net,
852 struct rb_root *root,
853 xfrm_address_t *addr,
854 u16 family, u8 prefixlen, u8 dir)
855{
856 struct xfrm_pol_inexact_node *cached = NULL;
857 struct rb_node **p, *parent = NULL;
858 struct xfrm_pol_inexact_node *node;
859
860 p = &root->rb_node;
861 while (*p) {
862 int delta;
863
864 parent = *p;
865 node = rb_entry(*p, struct xfrm_pol_inexact_node, node);
866
867 delta = xfrm_policy_addr_delta(addr, &node->addr,
868 node->prefixlen,
869 family);
870 if (delta == 0 && prefixlen >= node->prefixlen) {
871 WARN_ON_ONCE(cached); /* ipsec policies got lost */
872 return node;
873 }
874
875 if (delta < 0)
876 p = &parent->rb_left;
877 else
878 p = &parent->rb_right;
879
880 if (prefixlen < node->prefixlen) {
881 delta = xfrm_policy_addr_delta(addr, &node->addr,
882 prefixlen,
883 family);
884 if (delta)
885 continue;
886
887 /* This node is a subnet of the new prefix. It needs
888 * to be removed and re-inserted with the smaller
889 * prefix and all nodes that are now also covered
890 * by the reduced prefixlen.
891 */
892 rb_erase(&node->node, root);
893
894 if (!cached) {
895 xfrm_pol_inexact_node_init(node, addr,
896 prefixlen);
897 cached = node;
898 } else {
899 /* This node also falls within the new
900 * prefixlen. Merge the to-be-reinserted
901 * node and this one.
902 */
903 xfrm_policy_inexact_node_merge(net, node,
904 cached, family);
905 kfree_rcu(node, rcu);
906 }
907
908 /* restart */
909 p = &root->rb_node;
910 parent = NULL;
911 }
912 }
913
914 node = cached;
915 if (!node) {
916 node = xfrm_pol_inexact_node_alloc(addr, prefixlen);
917 if (!node)
918 return NULL;
919 }
920
921 rb_link_node_rcu(&node->node, parent, p);
922 rb_insert_color(&node->node, root);
923
924 return node;
925}
926
927static void xfrm_policy_inexact_gc_tree(struct rb_root *r, bool rm)
928{
929 struct xfrm_pol_inexact_node *node;
930 struct rb_node *rn = rb_first(r);
931
932 while (rn) {
933 node = rb_entry(rn, struct xfrm_pol_inexact_node, node);
934
935 rn = rb_next(rn);
936
937 if (!hlist_empty(&node->hhead)) {
938 WARN_ON_ONCE(rm);
939 continue;
940 }
941
942 rb_erase(&node->node, r);
943 kfree_rcu(node, rcu);
944 }
945}
946
711static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool net_exit) 947static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool net_exit)
712{ 948{
713 if (!hlist_empty(&b->hhead)) { 949 write_seqcount_begin(&b->count);
950 xfrm_policy_inexact_gc_tree(&b->root_d, net_exit);
951 write_seqcount_end(&b->count);
952
953 if (!RB_EMPTY_ROOT(&b->root_d) ||
954 !hlist_empty(&b->hhead)) {
714 WARN_ON_ONCE(net_exit); 955 WARN_ON_ONCE(net_exit);
715 return; 956 return;
716 } 957 }
@@ -741,6 +982,37 @@ static void __xfrm_policy_inexact_flush(struct net *net)
741 __xfrm_policy_inexact_prune_bin(bin, false); 982 __xfrm_policy_inexact_prune_bin(bin, false);
742} 983}
743 984
985static struct hlist_head *
986xfrm_policy_inexact_alloc_chain(struct xfrm_pol_inexact_bin *bin,
987 struct xfrm_policy *policy, u8 dir)
988{
989 struct xfrm_pol_inexact_node *n;
990 struct net *net;
991
992 net = xp_net(policy);
993 lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
994
995 if (xfrm_policy_inexact_insert_use_any_list(policy))
996 return &bin->hhead;
997
998 if (xfrm_pol_inexact_addr_use_any_list(&policy->selector.daddr,
999 policy->family,
1000 policy->selector.prefixlen_d))
1001 return &bin->hhead;
1002
1003 /* daddr is fixed */
1004 write_seqcount_begin(&bin->count);
1005 n = xfrm_policy_inexact_insert_node(net,
1006 &bin->root_d,
1007 &policy->selector.daddr,
1008 policy->family,
1009 policy->selector.prefixlen_d, dir);
1010 write_seqcount_end(&bin->count);
1011 if (!n)
1012 return NULL;
1013 return &n->hhead;
1014}
1015
744static struct xfrm_policy * 1016static struct xfrm_policy *
745xfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl) 1017xfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl)
746{ 1018{
@@ -756,13 +1028,12 @@ xfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl)
756 net = xp_net(policy); 1028 net = xp_net(policy);
757 lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 1029 lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
758 1030
759 if (xfrm_policy_inexact_insert_use_any_list(policy)) { 1031 chain = xfrm_policy_inexact_alloc_chain(bin, policy, dir);
760 chain = &bin->hhead; 1032 if (!chain) {
761 goto insert_to_list; 1033 __xfrm_policy_inexact_prune_bin(bin, false);
1034 return ERR_PTR(-ENOMEM);
762 } 1035 }
763 1036
764 chain = &bin->hhead;
765insert_to_list:
766 delpol = xfrm_policy_insert_list(chain, policy, excl); 1037 delpol = xfrm_policy_insert_list(chain, policy, excl);
767 if (delpol && excl) { 1038 if (delpol && excl) {
768 __xfrm_policy_inexact_prune_bin(bin, false); 1039 __xfrm_policy_inexact_prune_bin(bin, false);
@@ -843,6 +1114,9 @@ static void xfrm_hash_rebuild(struct work_struct *work)
843 bin = xfrm_policy_inexact_alloc_bin(policy, dir); 1114 bin = xfrm_policy_inexact_alloc_bin(policy, dir);
844 if (!bin) 1115 if (!bin)
845 goto out_unlock; 1116 goto out_unlock;
1117
1118 if (!xfrm_policy_inexact_alloc_chain(bin, policy, dir))
1119 goto out_unlock;
846 } 1120 }
847 1121
848 /* reset the bydst and inexact table in all directions */ 1122 /* reset the bydst and inexact table in all directions */
@@ -1462,17 +1736,64 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
1462 return ret; 1736 return ret;
1463} 1737}
1464 1738
1739static struct xfrm_pol_inexact_node *
1740xfrm_policy_lookup_inexact_addr(const struct rb_root *r,
1741 seqcount_t *count,
1742 const xfrm_address_t *addr, u16 family)
1743{
1744 const struct rb_node *parent;
1745 int seq;
1746
1747again:
1748 seq = read_seqcount_begin(count);
1749
1750 parent = rcu_dereference_raw(r->rb_node);
1751 while (parent) {
1752 struct xfrm_pol_inexact_node *node;
1753 int delta;
1754
1755 node = rb_entry(parent, struct xfrm_pol_inexact_node, node);
1756
1757 delta = xfrm_policy_addr_delta(addr, &node->addr,
1758 node->prefixlen, family);
1759 if (delta < 0) {
1760 parent = rcu_dereference_raw(parent->rb_left);
1761 continue;
1762 } else if (delta > 0) {
1763 parent = rcu_dereference_raw(parent->rb_right);
1764 continue;
1765 }
1766
1767 return node;
1768 }
1769
1770 if (read_seqcount_retry(count, seq))
1771 goto again;
1772
1773 return NULL;
1774}
1775
1465static bool 1776static bool
1466xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand, 1777xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand,
1467 struct xfrm_pol_inexact_bin *b, 1778 struct xfrm_pol_inexact_bin *b,
1468 const xfrm_address_t *saddr, 1779 const xfrm_address_t *saddr,
1469 const xfrm_address_t *daddr) 1780 const xfrm_address_t *daddr)
1470{ 1781{
1782 struct xfrm_pol_inexact_node *n;
1783 u16 family;
1784
1471 if (!b) 1785 if (!b)
1472 return false; 1786 return false;
1473 1787
1788 family = b->k.family;
1474 memset(cand, 0, sizeof(*cand)); 1789 memset(cand, 0, sizeof(*cand));
1475 cand->res[XFRM_POL_CAND_ANY] = &b->hhead; 1790 cand->res[XFRM_POL_CAND_ANY] = &b->hhead;
1791
1792 n = xfrm_policy_lookup_inexact_addr(&b->root_d, &b->count, daddr,
1793 family);
1794 if (n)
1795 cand->res[XFRM_POL_CAND_DADDR] = &n->hhead;
1796
1476 return true; 1797 return true;
1477} 1798}
1478 1799