aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2018-11-07 17:00:37 -0500
committerSteffen Klassert <steffen.klassert@secunet.com>2018-11-09 05:57:59 -0500
commit6be3b0db6db82cf056a72cc18042048edd27f8ee (patch)
tree3154fe6b3db794b88ec1dd69ed1056c327772b19 /net/xfrm
parentb5fe22e2337d47cd68bb7d8e4103a628808c4d5e (diff)
xfrm: policy: add inexact policy search tree infrastructure
At this time inexact policies are all searched in-order until the first match is found. After removal of the flow cache, this resolution has to be performed for every packetm resulting in major slowdown when number of inexact policies is high. This adds infrastructure to later sort inexact policies into a tree. This only introduces a single class: any:any. Next patch will add a search tree to pre-sort policies that have a fixed daddr/prefixlen, so in this patch the any:any class will still be used for all policies. 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.c301
1 files changed, 247 insertions, 54 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index dda27fd7b8a4..4eb12e9b40c2 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -46,6 +46,9 @@ struct xfrm_flo {
46 u8 flags; 46 u8 flags;
47}; 47};
48 48
49/* prefixes smaller than this are stored in lists, not trees. */
50#define INEXACT_PREFIXLEN_IPV4 16
51#define INEXACT_PREFIXLEN_IPV6 48
49struct xfrm_pol_inexact_key { 52struct xfrm_pol_inexact_key {
50 possible_net_t net; 53 possible_net_t net;
51 u32 if_id; 54 u32 if_id;
@@ -56,6 +59,7 @@ struct xfrm_pol_inexact_key {
56struct xfrm_pol_inexact_bin { 59struct xfrm_pol_inexact_bin {
57 struct xfrm_pol_inexact_key k; 60 struct xfrm_pol_inexact_key k;
58 struct rhash_head head; 61 struct rhash_head head;
62 /* list containing '*:*' policies */
59 struct hlist_head hhead; 63 struct hlist_head hhead;
60 64
61 /* slow path below */ 65 /* slow path below */
@@ -63,6 +67,16 @@ struct xfrm_pol_inexact_bin {
63 struct rcu_head rcu; 67 struct rcu_head rcu;
64}; 68};
65 69
70enum xfrm_pol_inexact_candidate_type {
71 XFRM_POL_CAND_ANY,
72
73 XFRM_POL_CAND_MAX,
74};
75
76struct xfrm_pol_inexact_candidates {
77 struct hlist_head *res[XFRM_POL_CAND_MAX];
78};
79
66static DEFINE_SPINLOCK(xfrm_if_cb_lock); 80static DEFINE_SPINLOCK(xfrm_if_cb_lock);
67static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly; 81static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly;
68 82
@@ -98,6 +112,12 @@ xfrm_policy_insert_list(struct hlist_head *chain, struct xfrm_policy *policy,
98static void xfrm_policy_insert_inexact_list(struct hlist_head *chain, 112static void xfrm_policy_insert_inexact_list(struct hlist_head *chain,
99 struct xfrm_policy *policy); 113 struct xfrm_policy *policy);
100 114
115static bool
116xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand,
117 struct xfrm_pol_inexact_bin *b,
118 const xfrm_address_t *saddr,
119 const xfrm_address_t *daddr);
120
101static inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy) 121static inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy)
102{ 122{
103 return refcount_inc_not_zero(&policy->refcnt); 123 return refcount_inc_not_zero(&policy->refcnt);
@@ -652,13 +672,48 @@ xfrm_policy_inexact_alloc_bin(const struct xfrm_policy *pol, u8 dir)
652 return IS_ERR(prev) ? NULL : prev; 672 return IS_ERR(prev) ? NULL : prev;
653} 673}
654 674
655static void xfrm_policy_inexact_delete_bin(struct net *net, 675static bool xfrm_pol_inexact_addr_use_any_list(const xfrm_address_t *addr,
656 struct xfrm_pol_inexact_bin *b) 676 int family, u8 prefixlen)
657{ 677{
658 lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 678 if (xfrm_addr_any(addr, family))
679 return true;
680
681 if (family == AF_INET6 && prefixlen < INEXACT_PREFIXLEN_IPV6)
682 return true;
683
684 if (family == AF_INET && prefixlen < INEXACT_PREFIXLEN_IPV4)
685 return true;
686
687 return false;
688}
689
690static bool
691xfrm_policy_inexact_insert_use_any_list(const struct xfrm_policy *policy)
692{
693 const xfrm_address_t *addr;
694 bool saddr_any, daddr_any;
695 u8 prefixlen;
696
697 addr = &policy->selector.saddr;
698 prefixlen = policy->selector.prefixlen_s;
659 699
660 if (!hlist_empty(&b->hhead)) 700 saddr_any = xfrm_pol_inexact_addr_use_any_list(addr,
701 policy->family,
702 prefixlen);
703 addr = &policy->selector.daddr;
704 prefixlen = policy->selector.prefixlen_d;
705 daddr_any = xfrm_pol_inexact_addr_use_any_list(addr,
706 policy->family,
707 prefixlen);
708 return saddr_any && daddr_any;
709}
710
711static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool net_exit)
712{
713 if (!hlist_empty(&b->hhead)) {
714 WARN_ON_ONCE(net_exit);
661 return; 715 return;
716 }
662 717
663 if (rhashtable_remove_fast(&xfrm_policy_inexact_table, &b->head, 718 if (rhashtable_remove_fast(&xfrm_policy_inexact_table, &b->head,
664 xfrm_pol_inexact_params) == 0) { 719 xfrm_pol_inexact_params) == 0) {
@@ -667,14 +722,23 @@ static void xfrm_policy_inexact_delete_bin(struct net *net,
667 } 722 }
668} 723}
669 724
725static void xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b)
726{
727 struct net *net = read_pnet(&b->k.net);
728
729 spin_lock_bh(&net->xfrm.xfrm_policy_lock);
730 __xfrm_policy_inexact_prune_bin(b, false);
731 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
732}
733
670static void __xfrm_policy_inexact_flush(struct net *net) 734static void __xfrm_policy_inexact_flush(struct net *net)
671{ 735{
672 struct xfrm_pol_inexact_bin *bin; 736 struct xfrm_pol_inexact_bin *bin, *t;
673 737
674 lockdep_assert_held(&net->xfrm.xfrm_policy_lock); 738 lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
675 739
676 list_for_each_entry(bin, &net->xfrm.inexact_bins, inexact_bins) 740 list_for_each_entry_safe(bin, t, &net->xfrm.inexact_bins, inexact_bins)
677 xfrm_policy_inexact_delete_bin(net, bin); 741 __xfrm_policy_inexact_prune_bin(bin, false);
678} 742}
679 743
680static struct xfrm_policy * 744static struct xfrm_policy *
@@ -689,14 +753,28 @@ xfrm_policy_inexact_insert(struct xfrm_policy *policy, u8 dir, int excl)
689 if (!bin) 753 if (!bin)
690 return ERR_PTR(-ENOMEM); 754 return ERR_PTR(-ENOMEM);
691 755
692 delpol = xfrm_policy_insert_list(&bin->hhead, policy, excl); 756 net = xp_net(policy);
693 if (delpol && excl) 757 lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
758
759 if (xfrm_policy_inexact_insert_use_any_list(policy)) {
760 chain = &bin->hhead;
761 goto insert_to_list;
762 }
763
764 chain = &bin->hhead;
765insert_to_list:
766 delpol = xfrm_policy_insert_list(chain, policy, excl);
767 if (delpol && excl) {
768 __xfrm_policy_inexact_prune_bin(bin, false);
694 return ERR_PTR(-EEXIST); 769 return ERR_PTR(-EEXIST);
770 }
695 771
696 net = xp_net(policy);
697 chain = &net->xfrm.policy_inexact[dir]; 772 chain = &net->xfrm.policy_inexact[dir];
698 xfrm_policy_insert_inexact_list(chain, policy); 773 xfrm_policy_insert_inexact_list(chain, policy);
699 774
775 if (delpol)
776 __xfrm_policy_inexact_prune_bin(bin, false);
777
700 return delpol; 778 return delpol;
701} 779}
702 780
@@ -733,6 +811,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
733 * we start with destructive action. 811 * we start with destructive action.
734 */ 812 */
735 list_for_each_entry(policy, &net->xfrm.policy_all, walk.all) { 813 list_for_each_entry(policy, &net->xfrm.policy_all, walk.all) {
814 struct xfrm_pol_inexact_bin *bin;
736 u8 dbits, sbits; 815 u8 dbits, sbits;
737 816
738 dir = xfrm_policy_id2dir(policy->index); 817 dir = xfrm_policy_id2dir(policy->index);
@@ -761,7 +840,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
761 policy->selector.prefixlen_s < sbits) 840 policy->selector.prefixlen_s < sbits)
762 continue; 841 continue;
763 842
764 if (!xfrm_policy_inexact_alloc_bin(policy, dir)) 843 bin = xfrm_policy_inexact_alloc_bin(policy, dir);
844 if (!bin)
765 goto out_unlock; 845 goto out_unlock;
766 } 846 }
767 847
@@ -820,6 +900,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
820 } 900 }
821 901
822out_unlock: 902out_unlock:
903 __xfrm_policy_inexact_flush(net);
823 spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 904 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
824 905
825 mutex_unlock(&hash_resize_mutex); 906 mutex_unlock(&hash_resize_mutex);
@@ -977,6 +1058,7 @@ static void xfrm_policy_insert_inexact_list(struct hlist_head *chain,
977{ 1058{
978 struct xfrm_policy *pol, *delpol = NULL; 1059 struct xfrm_policy *pol, *delpol = NULL;
979 struct hlist_node *newpos = NULL; 1060 struct hlist_node *newpos = NULL;
1061 int i = 0;
980 1062
981 hlist_for_each_entry(pol, chain, bydst_inexact_list) { 1063 hlist_for_each_entry(pol, chain, bydst_inexact_list) {
982 if (pol->type == policy->type && 1064 if (pol->type == policy->type &&
@@ -1000,6 +1082,11 @@ static void xfrm_policy_insert_inexact_list(struct hlist_head *chain,
1000 hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos); 1082 hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos);
1001 else 1083 else
1002 hlist_add_head_rcu(&policy->bydst_inexact_list, chain); 1084 hlist_add_head_rcu(&policy->bydst_inexact_list, chain);
1085
1086 hlist_for_each_entry(pol, chain, bydst_inexact_list) {
1087 pol->pos = i;
1088 i++;
1089 }
1003} 1090}
1004 1091
1005static struct xfrm_policy *xfrm_policy_insert_list(struct hlist_head *chain, 1092static struct xfrm_policy *xfrm_policy_insert_list(struct hlist_head *chain,
@@ -1083,6 +1170,29 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
1083} 1170}
1084EXPORT_SYMBOL(xfrm_policy_insert); 1171EXPORT_SYMBOL(xfrm_policy_insert);
1085 1172
1173static struct xfrm_policy *
1174__xfrm_policy_bysel_ctx(struct hlist_head *chain, u32 mark, u32 if_id,
1175 u8 type, int dir,
1176 struct xfrm_selector *sel,
1177 struct xfrm_sec_ctx *ctx)
1178{
1179 struct xfrm_policy *pol;
1180
1181 if (!chain)
1182 return NULL;
1183
1184 hlist_for_each_entry(pol, chain, bydst) {
1185 if (pol->type == type &&
1186 pol->if_id == if_id &&
1187 (mark & pol->mark.m) == pol->mark.v &&
1188 !selector_cmp(sel, &pol->selector) &&
1189 xfrm_sec_ctx_match(ctx, pol->security))
1190 return pol;
1191 }
1192
1193 return NULL;
1194}
1195
1086struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id, 1196struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
1087 u8 type, int dir, 1197 u8 type, int dir,
1088 struct xfrm_selector *sel, 1198 struct xfrm_selector *sel,
@@ -1097,6 +1207,9 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
1097 spin_lock_bh(&net->xfrm.xfrm_policy_lock); 1207 spin_lock_bh(&net->xfrm.xfrm_policy_lock);
1098 chain = policy_hash_bysel(net, sel, sel->family, dir); 1208 chain = policy_hash_bysel(net, sel, sel->family, dir);
1099 if (!chain) { 1209 if (!chain) {
1210 struct xfrm_pol_inexact_candidates cand;
1211 int i;
1212
1100 bin = xfrm_policy_inexact_lookup(net, type, 1213 bin = xfrm_policy_inexact_lookup(net, type,
1101 sel->family, dir, if_id); 1214 sel->family, dir, if_id);
1102 if (!bin) { 1215 if (!bin) {
@@ -1104,35 +1217,46 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
1104 return NULL; 1217 return NULL;
1105 } 1218 }
1106 1219
1107 chain = &bin->hhead; 1220 if (!xfrm_policy_find_inexact_candidates(&cand, bin,
1221 &sel->saddr,
1222 &sel->daddr)) {
1223 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1224 return NULL;
1225 }
1226
1227 pol = NULL;
1228 for (i = 0; i < ARRAY_SIZE(cand.res); i++) {
1229 struct xfrm_policy *tmp;
1230
1231 tmp = __xfrm_policy_bysel_ctx(cand.res[i], mark,
1232 if_id, type, dir,
1233 sel, ctx);
1234 if (tmp && pol && tmp->pos < pol->pos)
1235 pol = tmp;
1236 }
1237 } else {
1238 pol = __xfrm_policy_bysel_ctx(chain, mark, if_id, type, dir,
1239 sel, ctx);
1108 } 1240 }
1109 1241
1110 ret = NULL; 1242 if (pol) {
1111 hlist_for_each_entry(pol, chain, bydst) { 1243 xfrm_pol_hold(pol);
1112 if (pol->type == type && 1244 if (delete) {
1113 pol->if_id == if_id && 1245 *err = security_xfrm_policy_delete(pol->security);
1114 (mark & pol->mark.m) == pol->mark.v && 1246 if (*err) {
1115 !selector_cmp(sel, &pol->selector) && 1247 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1116 xfrm_sec_ctx_match(ctx, pol->security)) { 1248 return pol;
1117 xfrm_pol_hold(pol);
1118 if (delete) {
1119 *err = security_xfrm_policy_delete(
1120 pol->security);
1121 if (*err) {
1122 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1123 return pol;
1124 }
1125 __xfrm_policy_unlink(pol, dir);
1126 xfrm_policy_inexact_delete_bin(net, bin);
1127 } 1249 }
1128 ret = pol; 1250 __xfrm_policy_unlink(pol, dir);
1129 break;
1130 } 1251 }
1252 ret = pol;
1131 } 1253 }
1132 spin_unlock_bh(&net->xfrm.xfrm_policy_lock); 1254 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1133 1255
1134 if (ret && delete) 1256 if (ret && delete)
1135 xfrm_policy_kill(ret); 1257 xfrm_policy_kill(ret);
1258 if (bin && delete)
1259 xfrm_policy_inexact_prune_bin(bin);
1136 return ret; 1260 return ret;
1137} 1261}
1138EXPORT_SYMBOL(xfrm_policy_bysel_ctx); 1262EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
@@ -1338,6 +1462,20 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
1338 return ret; 1462 return ret;
1339} 1463}
1340 1464
1465static bool
1466xfrm_policy_find_inexact_candidates(struct xfrm_pol_inexact_candidates *cand,
1467 struct xfrm_pol_inexact_bin *b,
1468 const xfrm_address_t *saddr,
1469 const xfrm_address_t *daddr)
1470{
1471 if (!b)
1472 return false;
1473
1474 memset(cand, 0, sizeof(*cand));
1475 cand->res[XFRM_POL_CAND_ANY] = &b->hhead;
1476 return true;
1477}
1478
1341static struct xfrm_pol_inexact_bin * 1479static struct xfrm_pol_inexact_bin *
1342xfrm_policy_inexact_lookup_rcu(struct net *net, u8 type, u16 family, 1480xfrm_policy_inexact_lookup_rcu(struct net *net, u8 type, u16 family,
1343 u8 dir, u32 if_id) 1481 u8 dir, u32 if_id)
@@ -1370,11 +1508,76 @@ xfrm_policy_inexact_lookup(struct net *net, u8 type, u16 family,
1370 return bin; 1508 return bin;
1371} 1509}
1372 1510
1511static struct xfrm_policy *
1512__xfrm_policy_eval_candidates(struct hlist_head *chain,
1513 struct xfrm_policy *prefer,
1514 const struct flowi *fl,
1515 u8 type, u16 family, int dir, u32 if_id)
1516{
1517 u32 priority = prefer ? prefer->priority : ~0u;
1518 struct xfrm_policy *pol;
1519
1520 if (!chain)
1521 return NULL;
1522
1523 hlist_for_each_entry_rcu(pol, chain, bydst) {
1524 int err;
1525
1526 if (pol->priority > priority)
1527 break;
1528
1529 err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
1530 if (err) {
1531 if (err != -ESRCH)
1532 return ERR_PTR(err);
1533
1534 continue;
1535 }
1536
1537 if (prefer) {
1538 /* matches. Is it older than *prefer? */
1539 if (pol->priority == priority &&
1540 prefer->pos < pol->pos)
1541 return prefer;
1542 }
1543
1544 return pol;
1545 }
1546
1547 return NULL;
1548}
1549
1550static struct xfrm_policy *
1551xfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand,
1552 struct xfrm_policy *prefer,
1553 const struct flowi *fl,
1554 u8 type, u16 family, int dir, u32 if_id)
1555{
1556 struct xfrm_policy *tmp;
1557 int i;
1558
1559 for (i = 0; i < ARRAY_SIZE(cand->res); i++) {
1560 tmp = __xfrm_policy_eval_candidates(cand->res[i],
1561 prefer,
1562 fl, type, family, dir,
1563 if_id);
1564 if (!tmp)
1565 continue;
1566
1567 if (IS_ERR(tmp))
1568 return tmp;
1569 prefer = tmp;
1570 }
1571
1572 return prefer;
1573}
1574
1373static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, 1575static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
1374 const struct flowi *fl, 1576 const struct flowi *fl,
1375 u16 family, u8 dir, 1577 u16 family, u8 dir,
1376 u32 if_id) 1578 u32 if_id)
1377{ 1579{
1580 struct xfrm_pol_inexact_candidates cand;
1378 const xfrm_address_t *daddr, *saddr; 1581 const xfrm_address_t *daddr, *saddr;
1379 struct xfrm_pol_inexact_bin *bin; 1582 struct xfrm_pol_inexact_bin *bin;
1380 struct xfrm_policy *pol, *ret; 1583 struct xfrm_policy *pol, *ret;
@@ -1413,25 +1616,16 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
1413 } 1616 }
1414 } 1617 }
1415 bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id); 1618 bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id);
1416 if (!bin) 1619 if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr,
1620 daddr))
1417 goto skip_inexact; 1621 goto skip_inexact;
1418 chain = &bin->hhead;
1419 hlist_for_each_entry_rcu(pol, chain, bydst) {
1420 if ((pol->priority >= priority) && ret)
1421 break;
1422 1622
1423 err = xfrm_policy_match(pol, fl, type, family, dir, if_id); 1623 pol = xfrm_policy_eval_candidates(&cand, ret, fl, type,
1424 if (err) { 1624 family, dir, if_id);
1425 if (err == -ESRCH) 1625 if (pol) {
1426 continue; 1626 ret = pol;
1427 else { 1627 if (IS_ERR(pol))
1428 ret = ERR_PTR(err); 1628 goto fail;
1429 goto fail;
1430 }
1431 } else {
1432 ret = pol;
1433 break;
1434 }
1435 } 1629 }
1436 1630
1437skip_inexact: 1631skip_inexact:
@@ -3168,7 +3362,7 @@ out_byidx:
3168 3362
3169static void xfrm_policy_fini(struct net *net) 3363static void xfrm_policy_fini(struct net *net)
3170{ 3364{
3171 struct xfrm_pol_inexact_bin *bin, *tmp; 3365 struct xfrm_pol_inexact_bin *b, *t;
3172 unsigned int sz; 3366 unsigned int sz;
3173 int dir; 3367 int dir;
3174 3368
@@ -3195,11 +3389,10 @@ static void xfrm_policy_fini(struct net *net)
3195 WARN_ON(!hlist_empty(net->xfrm.policy_byidx)); 3389 WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
3196 xfrm_hash_free(net->xfrm.policy_byidx, sz); 3390 xfrm_hash_free(net->xfrm.policy_byidx, sz);
3197 3391
3198 list_for_each_entry_safe(bin, tmp, &net->xfrm.inexact_bins, 3392 spin_lock_bh(&net->xfrm.xfrm_policy_lock);
3199 inexact_bins) { 3393 list_for_each_entry_safe(b, t, &net->xfrm.inexact_bins, inexact_bins)
3200 WARN_ON(!hlist_empty(&bin->hhead)); 3394 __xfrm_policy_inexact_prune_bin(b, true);
3201 xfrm_policy_inexact_delete_bin(net, bin); 3395 spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
3202 }
3203} 3396}
3204 3397
3205static int __net_init xfrm_net_init(struct net *net) 3398static int __net_init xfrm_net_init(struct net *net)