diff options
author | Christophe Gouault <christophe.gouault@6wind.com> | 2014-08-29 10:16:05 -0400 |
---|---|---|
committer | Steffen Klassert <steffen.klassert@secunet.com> | 2014-09-02 07:37:56 -0400 |
commit | 880a6fab8f6ba5b5abe59ea68533202ddea1012c (patch) | |
tree | d5e83238ad632d0473aa3b75a5233359154df049 /net | |
parent | b58555f1767c9f4e330fcf168e4e753d2d9196e0 (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')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 87 | ||||
-rw-r--r-- | net/xfrm/xfrm_user.c | 80 |
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 | ||
569 | static 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 | |||
643 | void xfrm_policy_hash_rebuild(struct net *net) | ||
644 | { | ||
645 | schedule_work(&net->xfrm.policy_hthresh.work); | ||
646 | } | ||
647 | EXPORT_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. */ |
571 | static u32 xfrm_gen_index(struct net *net, int dir, u32 index) | 651 | static 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 | ||
970 | static int build_spdinfo(struct sk_buff *skb, struct net *net, | 972 | static 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 | ||
1024 | static 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 | |||
1007 | static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, | 1069 | static 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 | ||
2374 | static 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 | |||
2311 | static const struct xfrm_link { | 2379 | static 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 | ||