aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2014-02-14 09:30:36 -0500
committerSteffen Klassert <steffen.klassert@secunet.com>2014-02-17 01:18:19 -0500
commitd3623099d3509fa68fa28235366049dd3156c63a (patch)
treeff5daaf9b564f3a073a50ed461c7b823b899af7b
parent0f24558e91563888d51e9be5b70981da920c37ac (diff)
ipsec: add support of limited SA dump
The goal of this patch is to allow userland to dump only a part of SA by specifying a filter during the dump. The kernel is in charge to filter SA, this avoids to generate useless netlink traffic (it save also some cpu cycles). This is particularly useful when there is a big number of SA set on the system. Note that I removed the union in struct xfrm_state_walk to fix a problem on arm. struct netlink_callback->args is defined as a array of 6 long and the first long is used in xfrm code to flag the cb as initialized. Hence, we must have: sizeof(struct xfrm_state_walk) <= sizeof(long) * 5. With the union, it was false on arm (sizeof(struct xfrm_state_walk) was sizeof(long) * 7), due to the padding. In fact, whatever the arch is, this union seems useless, there will be always padding after it. Removing it will not increase the size of this struct (and reduce it on arm). Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
-rw-r--r--include/net/xfrm.h10
-rw-r--r--include/uapi/linux/pfkeyv2.h15
-rw-r--r--include/uapi/linux/xfrm.h10
-rw-r--r--net/key/af_key.c19
-rw-r--r--net/xfrm/xfrm_state.c25
-rw-r--r--net/xfrm/xfrm_user.c28
6 files changed, 98 insertions, 9 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 5313ccfdeedf..45332acac022 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -118,11 +118,10 @@
118struct xfrm_state_walk { 118struct xfrm_state_walk {
119 struct list_head all; 119 struct list_head all;
120 u8 state; 120 u8 state;
121 union { 121 u8 dying;
122 u8 dying; 122 u8 proto;
123 u8 proto;
124 };
125 u32 seq; 123 u32 seq;
124 struct xfrm_filter *filter;
126}; 125};
127 126
128/* Full description of state of transformer. */ 127/* Full description of state of transformer. */
@@ -1406,7 +1405,8 @@ static inline void xfrm_sysctl_fini(struct net *net)
1406} 1405}
1407#endif 1406#endif
1408 1407
1409void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); 1408void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
1409 struct xfrm_filter *filter);
1410int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, 1410int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
1411 int (*func)(struct xfrm_state *, int, void*), void *); 1411 int (*func)(struct xfrm_state *, int, void*), void *);
1412void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net); 1412void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net);
diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h
index 0b80c806631f..ada7f0171ccc 100644
--- a/include/uapi/linux/pfkeyv2.h
+++ b/include/uapi/linux/pfkeyv2.h
@@ -235,6 +235,18 @@ struct sadb_x_kmaddress {
235} __attribute__((packed)); 235} __attribute__((packed));
236/* sizeof(struct sadb_x_kmaddress) == 8 */ 236/* sizeof(struct sadb_x_kmaddress) == 8 */
237 237
238/* To specify the SA dump filter */
239struct sadb_x_filter {
240 __u16 sadb_x_filter_len;
241 __u16 sadb_x_filter_exttype;
242 __u32 sadb_x_filter_saddr[4];
243 __u32 sadb_x_filter_daddr[4];
244 __u16 sadb_x_filter_family;
245 __u8 sadb_x_filter_splen;
246 __u8 sadb_x_filter_dplen;
247} __attribute__((packed));
248/* sizeof(struct sadb_x_filter) == 40 */
249
238/* Message types */ 250/* Message types */
239#define SADB_RESERVED 0 251#define SADB_RESERVED 0
240#define SADB_GETSPI 1 252#define SADB_GETSPI 1
@@ -358,7 +370,8 @@ struct sadb_x_kmaddress {
358#define SADB_X_EXT_SEC_CTX 24 370#define SADB_X_EXT_SEC_CTX 24
359/* Used with MIGRATE to pass @ to IKE for negotiation */ 371/* Used with MIGRATE to pass @ to IKE for negotiation */
360#define SADB_X_EXT_KMADDRESS 25 372#define SADB_X_EXT_KMADDRESS 25
361#define SADB_EXT_MAX 25 373#define SADB_X_EXT_FILTER 26
374#define SADB_EXT_MAX 26
362 375
363/* Identity Extension values */ 376/* Identity Extension values */
364#define SADB_IDENTTYPE_RESERVED 0 377#define SADB_IDENTTYPE_RESERVED 0
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index a8cd6a4a2970..6550c679584f 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -298,6 +298,8 @@ enum xfrm_attr_type_t {
298 XFRMA_TFCPAD, /* __u32 */ 298 XFRMA_TFCPAD, /* __u32 */
299 XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ 299 XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */
300 XFRMA_SA_EXTRA_FLAGS, /* __u32 */ 300 XFRMA_SA_EXTRA_FLAGS, /* __u32 */
301 XFRMA_PROTO, /* __u8 */
302 XFRMA_FILTER, /* struct xfrm_filter */
301 __XFRMA_MAX 303 __XFRMA_MAX
302 304
303#define XFRMA_MAX (__XFRMA_MAX - 1) 305#define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -474,6 +476,14 @@ struct xfrm_user_mapping {
474 __be16 new_sport; 476 __be16 new_sport;
475}; 477};
476 478
479struct xfrm_filter {
480 xfrm_address_t saddr;
481 xfrm_address_t daddr;
482 __u16 family;
483 __u8 splen;
484 __u8 dplen;
485};
486
477#ifndef __KERNEL__ 487#ifndef __KERNEL__
478/* backwards compatibility for userspace */ 488/* backwards compatibility for userspace */
479#define XFRMGRP_ACQUIRE 1 489#define XFRMGRP_ACQUIRE 1
diff --git a/net/key/af_key.c b/net/key/af_key.c
index e1c69d024197..f0879c19f452 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1798,6 +1798,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
1798static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) 1798static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
1799{ 1799{
1800 u8 proto; 1800 u8 proto;
1801 struct xfrm_filter *filter = NULL;
1801 struct pfkey_sock *pfk = pfkey_sk(sk); 1802 struct pfkey_sock *pfk = pfkey_sk(sk);
1802 1803
1803 if (pfk->dump.dump != NULL) 1804 if (pfk->dump.dump != NULL)
@@ -1807,11 +1808,27 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms
1807 if (proto == 0) 1808 if (proto == 0)
1808 return -EINVAL; 1809 return -EINVAL;
1809 1810
1811 if (ext_hdrs[SADB_X_EXT_FILTER - 1]) {
1812 struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1];
1813
1814 filter = kmalloc(sizeof(*filter), GFP_KERNEL);
1815 if (filter == NULL)
1816 return -ENOMEM;
1817
1818 memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr,
1819 sizeof(xfrm_address_t));
1820 memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr,
1821 sizeof(xfrm_address_t));
1822 filter->family = xfilter->sadb_x_filter_family;
1823 filter->splen = xfilter->sadb_x_filter_splen;
1824 filter->dplen = xfilter->sadb_x_filter_dplen;
1825 }
1826
1810 pfk->dump.msg_version = hdr->sadb_msg_version; 1827 pfk->dump.msg_version = hdr->sadb_msg_version;
1811 pfk->dump.msg_portid = hdr->sadb_msg_pid; 1828 pfk->dump.msg_portid = hdr->sadb_msg_pid;
1812 pfk->dump.dump = pfkey_dump_sa; 1829 pfk->dump.dump = pfkey_dump_sa;
1813 pfk->dump.done = pfkey_dump_sa_done; 1830 pfk->dump.done = pfkey_dump_sa_done;
1814 xfrm_state_walk_init(&pfk->dump.u.state, proto); 1831 xfrm_state_walk_init(&pfk->dump.u.state, proto, filter);
1815 1832
1816 return pfkey_do_dump(pfk); 1833 return pfkey_do_dump(pfk);
1817} 1834}
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 0bf12f665b9b..a750901ac3db 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1603,6 +1603,23 @@ unlock:
1603} 1603}
1604EXPORT_SYMBOL(xfrm_alloc_spi); 1604EXPORT_SYMBOL(xfrm_alloc_spi);
1605 1605
1606static bool __xfrm_state_filter_match(struct xfrm_state *x,
1607 struct xfrm_filter *filter)
1608{
1609 if (filter) {
1610 if ((filter->family == AF_INET ||
1611 filter->family == AF_INET6) &&
1612 x->props.family != filter->family)
1613 return false;
1614
1615 return addr_match(&x->props.saddr, &filter->saddr,
1616 filter->splen) &&
1617 addr_match(&x->id.daddr, &filter->daddr,
1618 filter->dplen);
1619 }
1620 return true;
1621}
1622
1606int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, 1623int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
1607 int (*func)(struct xfrm_state *, int, void*), 1624 int (*func)(struct xfrm_state *, int, void*),
1608 void *data) 1625 void *data)
@@ -1625,6 +1642,8 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
1625 state = container_of(x, struct xfrm_state, km); 1642 state = container_of(x, struct xfrm_state, km);
1626 if (!xfrm_id_proto_match(state->id.proto, walk->proto)) 1643 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
1627 continue; 1644 continue;
1645 if (!__xfrm_state_filter_match(state, walk->filter))
1646 continue;
1628 err = func(state, walk->seq, data); 1647 err = func(state, walk->seq, data);
1629 if (err) { 1648 if (err) {
1630 list_move_tail(&walk->all, &x->all); 1649 list_move_tail(&walk->all, &x->all);
@@ -1643,17 +1662,21 @@ out:
1643} 1662}
1644EXPORT_SYMBOL(xfrm_state_walk); 1663EXPORT_SYMBOL(xfrm_state_walk);
1645 1664
1646void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) 1665void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
1666 struct xfrm_filter *filter)
1647{ 1667{
1648 INIT_LIST_HEAD(&walk->all); 1668 INIT_LIST_HEAD(&walk->all);
1649 walk->proto = proto; 1669 walk->proto = proto;
1650 walk->state = XFRM_STATE_DEAD; 1670 walk->state = XFRM_STATE_DEAD;
1651 walk->seq = 0; 1671 walk->seq = 0;
1672 walk->filter = filter;
1652} 1673}
1653EXPORT_SYMBOL(xfrm_state_walk_init); 1674EXPORT_SYMBOL(xfrm_state_walk_init);
1654 1675
1655void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net) 1676void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net)
1656{ 1677{
1678 kfree(walk->filter);
1679
1657 if (list_empty(&walk->all)) 1680 if (list_empty(&walk->all))
1658 return; 1681 return;
1659 1682
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index d7694f258294..023e5e7ea4c6 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -887,6 +887,7 @@ static int xfrm_dump_sa_done(struct netlink_callback *cb)
887 return 0; 887 return 0;
888} 888}
889 889
890static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
890static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) 891static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
891{ 892{
892 struct net *net = sock_net(skb->sk); 893 struct net *net = sock_net(skb->sk);
@@ -902,8 +903,31 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
902 info.nlmsg_flags = NLM_F_MULTI; 903 info.nlmsg_flags = NLM_F_MULTI;
903 904
904 if (!cb->args[0]) { 905 if (!cb->args[0]) {
906 struct nlattr *attrs[XFRMA_MAX+1];
907 struct xfrm_filter *filter = NULL;
908 u8 proto = 0;
909 int err;
910
905 cb->args[0] = 1; 911 cb->args[0] = 1;
906 xfrm_state_walk_init(walk, 0); 912
913 err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX,
914 xfrma_policy);
915 if (err < 0)
916 return err;
917
918 if (attrs[XFRMA_FILTER]) {
919 filter = kmalloc(sizeof(*filter), GFP_KERNEL);
920 if (filter == NULL)
921 return -ENOMEM;
922
923 memcpy(filter, nla_data(attrs[XFRMA_FILTER]),
924 sizeof(*filter));
925 }
926
927 if (attrs[XFRMA_PROTO])
928 proto = nla_get_u8(attrs[XFRMA_PROTO]);
929
930 xfrm_state_walk_init(walk, proto, filter);
907 } 931 }
908 932
909 (void) xfrm_state_walk(net, walk, dump_one_state, &info); 933 (void) xfrm_state_walk(net, walk, dump_one_state, &info);
@@ -2309,6 +2333,8 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
2309 [XFRMA_TFCPAD] = { .type = NLA_U32 }, 2333 [XFRMA_TFCPAD] = { .type = NLA_U32 },
2310 [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, 2334 [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) },
2311 [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, 2335 [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 },
2336 [XFRMA_PROTO] = { .type = NLA_U8 },
2337 [XFRMA_FILTER] = { .len = sizeof(struct xfrm_filter) },
2312}; 2338};
2313 2339
2314static const struct xfrm_link { 2340static const struct xfrm_link {