aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm
diff options
context:
space:
mode:
authorChristophe Gouault <christophe.gouault@6wind.com>2014-08-29 10:16:05 -0400
committerSteffen Klassert <steffen.klassert@secunet.com>2014-09-02 07:37:56 -0400
commit880a6fab8f6ba5b5abe59ea68533202ddea1012c (patch)
treed5e83238ad632d0473aa3b75a5233359154df049 /net/xfrm
parentb58555f1767c9f4e330fcf168e4e753d2d9196e0 (diff)
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the policy hash table via a netlink XFRM_MSG_NEWSPDINFO message. prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh). example: struct xfrmu_spdhthresh thresh4 = { .lbits = 0; .rbits = 24; }; struct xfrmu_spdhthresh thresh6 = { .lbits = 0; .rbits = 56; }; struct nlmsghdr *hdr; struct nl_msg *msg; msg = nlmsg_alloc(); hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST); nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4); nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6); nla_send_auto(sk, msg); The numbers are the policy selector minimum prefix lengths to put a policy in the hash table. - lbits is the local threshold (source address for out policies, destination address for in and fwd policies). - rbits is the remote threshold (destination address for out policies, source address for in and fwd policies). The default values are: XFRMA_SPD_IPV4_HTHRESH: 32 32 XFRMA_SPD_IPV6_HTHRESH: 128 128 Dynamic re-building of the SPD is performed when the thresholds values are changed. The current thresholds can be read via a XFRM_MSG_GETSPDINFO request: the kernel replies to XFRM_MSG_GETSPDINFO requests by an XFRM_MSG_NEWSPDINFO message, with both attributes XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH. Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_policy.c87
-rw-r--r--net/xfrm/xfrm_user.c80
2 files changed, 164 insertions, 3 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index e6ff7b4046ea..55bcb8604bc6 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -566,6 +566,86 @@ static void xfrm_hash_resize(struct work_struct *work)
566 mutex_unlock(&hash_resize_mutex); 566 mutex_unlock(&hash_resize_mutex);
567} 567}
568 568
569static void xfrm_hash_rebuild(struct work_struct *work)
570{
571 struct net *net = container_of(work, struct net,
572 xfrm.policy_hthresh.work);
573 unsigned int hmask;
574 struct xfrm_policy *pol;
575 struct xfrm_policy *policy;
576 struct hlist_head *chain;
577 struct hlist_head *odst;
578 struct hlist_node *newpos;
579 int i;
580 int dir;
581 unsigned seq;
582 u8 lbits4, rbits4, lbits6, rbits6;
583
584 mutex_lock(&hash_resize_mutex);
585
586 /* read selector prefixlen thresholds */
587 do {
588 seq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
589
590 lbits4 = net->xfrm.policy_hthresh.lbits4;
591 rbits4 = net->xfrm.policy_hthresh.rbits4;
592 lbits6 = net->xfrm.policy_hthresh.lbits6;
593 rbits6 = net->xfrm.policy_hthresh.rbits6;
594 } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
595
596 write_lock_bh(&net->xfrm.xfrm_policy_lock);
597
598 /* reset the bydst and inexact table in all directions */
599 for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
600 INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
601 hmask = net->xfrm.policy_bydst[dir].hmask;
602 odst = net->xfrm.policy_bydst[dir].table;
603 for (i = hmask; i >= 0; i--)
604 INIT_HLIST_HEAD(odst + i);
605 if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) {
606 /* dir out => dst = remote, src = local */
607 net->xfrm.policy_bydst[dir].dbits4 = rbits4;
608 net->xfrm.policy_bydst[dir].sbits4 = lbits4;
609 net->xfrm.policy_bydst[dir].dbits6 = rbits6;
610 net->xfrm.policy_bydst[dir].sbits6 = lbits6;
611 } else {
612 /* dir in/fwd => dst = local, src = remote */
613 net->xfrm.policy_bydst[dir].dbits4 = lbits4;
614 net->xfrm.policy_bydst[dir].sbits4 = rbits4;
615 net->xfrm.policy_bydst[dir].dbits6 = lbits6;
616 net->xfrm.policy_bydst[dir].sbits6 = rbits6;
617 }
618 }
619
620 /* re-insert all policies by order of creation */
621 list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
622 newpos = NULL;
623 chain = policy_hash_bysel(net, &policy->selector,
624 policy->family,
625 xfrm_policy_id2dir(policy->index));
626 hlist_for_each_entry(pol, chain, bydst) {
627 if (policy->priority >= pol->priority)
628 newpos = &pol->bydst;
629 else
630 break;
631 }
632 if (newpos)
633 hlist_add_behind(&policy->bydst, newpos);
634 else
635 hlist_add_head(&policy->bydst, chain);
636 }
637
638 write_unlock_bh(&net->xfrm.xfrm_policy_lock);
639
640 mutex_unlock(&hash_resize_mutex);
641}
642
643void xfrm_policy_hash_rebuild(struct net *net)
644{
645 schedule_work(&net->xfrm.policy_hthresh.work);
646}
647EXPORT_SYMBOL(xfrm_policy_hash_rebuild);
648
569/* Generate new index... KAME seems to generate them ordered by cost 649/* Generate new index... KAME seems to generate them ordered by cost
570 * of an absolute inpredictability of ordering of rules. This will not pass. */ 650 * of an absolute inpredictability of ordering of rules. This will not pass. */
571static u32 xfrm_gen_index(struct net *net, int dir, u32 index) 651static u32 xfrm_gen_index(struct net *net, int dir, u32 index)
@@ -2872,9 +2952,16 @@ static int __net_init xfrm_policy_init(struct net *net)
2872 htab->dbits6 = 128; 2952 htab->dbits6 = 128;
2873 htab->sbits6 = 128; 2953 htab->sbits6 = 128;
2874 } 2954 }
2955 net->xfrm.policy_hthresh.lbits4 = 32;
2956 net->xfrm.policy_hthresh.rbits4 = 32;
2957 net->xfrm.policy_hthresh.lbits6 = 128;
2958 net->xfrm.policy_hthresh.rbits6 = 128;
2959
2960 seqlock_init(&net->xfrm.policy_hthresh.lock);
2875 2961
2876 INIT_LIST_HEAD(&net->xfrm.policy_all); 2962 INIT_LIST_HEAD(&net->xfrm.policy_all);
2877 INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); 2963 INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
2964 INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild);
2878 if (net_eq(net, &init_net)) 2965 if (net_eq(net, &init_net))
2879 register_netdevice_notifier(&xfrm_dev_notifier); 2966 register_netdevice_notifier(&xfrm_dev_notifier);
2880 return 0; 2967 return 0;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index d4db6ebb089d..eaf8a8f1cbe8 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -964,7 +964,9 @@ static inline size_t xfrm_spdinfo_msgsize(void)
964{ 964{
965 return NLMSG_ALIGN(4) 965 return NLMSG_ALIGN(4)
966 + nla_total_size(sizeof(struct xfrmu_spdinfo)) 966 + nla_total_size(sizeof(struct xfrmu_spdinfo))
967 + nla_total_size(sizeof(struct xfrmu_spdhinfo)); 967 + nla_total_size(sizeof(struct xfrmu_spdhinfo))
968 + nla_total_size(sizeof(struct xfrmu_spdhthresh))
969 + nla_total_size(sizeof(struct xfrmu_spdhthresh));
968} 970}
969 971
970static int build_spdinfo(struct sk_buff *skb, struct net *net, 972static int build_spdinfo(struct sk_buff *skb, struct net *net,
@@ -973,9 +975,11 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
973 struct xfrmk_spdinfo si; 975 struct xfrmk_spdinfo si;
974 struct xfrmu_spdinfo spc; 976 struct xfrmu_spdinfo spc;
975 struct xfrmu_spdhinfo sph; 977 struct xfrmu_spdhinfo sph;
978 struct xfrmu_spdhthresh spt4, spt6;
976 struct nlmsghdr *nlh; 979 struct nlmsghdr *nlh;
977 int err; 980 int err;
978 u32 *f; 981 u32 *f;
982 unsigned lseq;
979 983
980 nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); 984 nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
981 if (nlh == NULL) /* shouldn't really happen ... */ 985 if (nlh == NULL) /* shouldn't really happen ... */
@@ -993,9 +997,22 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
993 sph.spdhcnt = si.spdhcnt; 997 sph.spdhcnt = si.spdhcnt;
994 sph.spdhmcnt = si.spdhmcnt; 998 sph.spdhmcnt = si.spdhmcnt;
995 999
1000 do {
1001 lseq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
1002
1003 spt4.lbits = net->xfrm.policy_hthresh.lbits4;
1004 spt4.rbits = net->xfrm.policy_hthresh.rbits4;
1005 spt6.lbits = net->xfrm.policy_hthresh.lbits6;
1006 spt6.rbits = net->xfrm.policy_hthresh.rbits6;
1007 } while (read_seqretry(&net->xfrm.policy_hthresh.lock, lseq));
1008
996 err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); 1009 err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc);
997 if (!err) 1010 if (!err)
998 err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); 1011 err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph);
1012 if (!err)
1013 err = nla_put(skb, XFRMA_SPD_IPV4_HTHRESH, sizeof(spt4), &spt4);
1014 if (!err)
1015 err = nla_put(skb, XFRMA_SPD_IPV6_HTHRESH, sizeof(spt6), &spt6);
999 if (err) { 1016 if (err) {
1000 nlmsg_cancel(skb, nlh); 1017 nlmsg_cancel(skb, nlh);
1001 return err; 1018 return err;
@@ -1004,6 +1021,51 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
1004 return nlmsg_end(skb, nlh); 1021 return nlmsg_end(skb, nlh);
1005} 1022}
1006 1023
1024static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
1025 struct nlattr **attrs)
1026{
1027 struct net *net = sock_net(skb->sk);
1028 struct xfrmu_spdhthresh *thresh4 = NULL;
1029 struct xfrmu_spdhthresh *thresh6 = NULL;
1030
1031 /* selector prefixlen thresholds to hash policies */
1032 if (attrs[XFRMA_SPD_IPV4_HTHRESH]) {
1033 struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH];
1034
1035 if (nla_len(rta) < sizeof(*thresh4))
1036 return -EINVAL;
1037 thresh4 = nla_data(rta);
1038 if (thresh4->lbits > 32 || thresh4->rbits > 32)
1039 return -EINVAL;
1040 }
1041 if (attrs[XFRMA_SPD_IPV6_HTHRESH]) {
1042 struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH];
1043
1044 if (nla_len(rta) < sizeof(*thresh6))
1045 return -EINVAL;
1046 thresh6 = nla_data(rta);
1047 if (thresh6->lbits > 128 || thresh6->rbits > 128)
1048 return -EINVAL;
1049 }
1050
1051 if (thresh4 || thresh6) {
1052 write_seqlock(&net->xfrm.policy_hthresh.lock);
1053 if (thresh4) {
1054 net->xfrm.policy_hthresh.lbits4 = thresh4->lbits;
1055 net->xfrm.policy_hthresh.rbits4 = thresh4->rbits;
1056 }
1057 if (thresh6) {
1058 net->xfrm.policy_hthresh.lbits6 = thresh6->lbits;
1059 net->xfrm.policy_hthresh.rbits6 = thresh6->rbits;
1060 }
1061 write_sequnlock(&net->xfrm.policy_hthresh.lock);
1062
1063 xfrm_policy_hash_rebuild(net);
1064 }
1065
1066 return 0;
1067}
1068
1007static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, 1069static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
1008 struct nlattr **attrs) 1070 struct nlattr **attrs)
1009{ 1071{
@@ -2274,6 +2336,7 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
2274 [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report), 2336 [XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
2275 [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id), 2337 [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
2276 [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), 2338 [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
2339 [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
2277 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), 2340 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
2278}; 2341};
2279 2342
@@ -2308,10 +2371,17 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
2308 [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, 2371 [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
2309}; 2372};
2310 2373
2374static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
2375 [XFRMA_SPD_IPV4_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
2376 [XFRMA_SPD_IPV6_HTHRESH] = { .len = sizeof(struct xfrmu_spdhthresh) },
2377};
2378
2311static const struct xfrm_link { 2379static const struct xfrm_link {
2312 int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); 2380 int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
2313 int (*dump)(struct sk_buff *, struct netlink_callback *); 2381 int (*dump)(struct sk_buff *, struct netlink_callback *);
2314 int (*done)(struct netlink_callback *); 2382 int (*done)(struct netlink_callback *);
2383 const struct nla_policy *nla_pol;
2384 int nla_max;
2315} xfrm_dispatch[XFRM_NR_MSGTYPES] = { 2385} xfrm_dispatch[XFRM_NR_MSGTYPES] = {
2316 [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa }, 2386 [XFRM_MSG_NEWSA - XFRM_MSG_BASE] = { .doit = xfrm_add_sa },
2317 [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa }, 2387 [XFRM_MSG_DELSA - XFRM_MSG_BASE] = { .doit = xfrm_del_sa },
@@ -2335,6 +2405,9 @@ static const struct xfrm_link {
2335 [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae }, 2405 [XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae },
2336 [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate }, 2406 [XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate },
2337 [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo }, 2407 [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo },
2408 [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_set_spdinfo,
2409 .nla_pol = xfrma_spd_policy,
2410 .nla_max = XFRMA_SPD_MAX },
2338 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, 2411 [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
2339}; 2412};
2340 2413
@@ -2371,8 +2444,9 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
2371 } 2444 }
2372 } 2445 }
2373 2446
2374 err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, 2447 err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs,
2375 xfrma_policy); 2448 link->nla_max ? : XFRMA_MAX,
2449 link->nla_pol ? : xfrma_policy);
2376 if (err < 0) 2450 if (err < 0)
2377 return err; 2451 return err;
2378 2452