Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git
authorDavid S. Miller <davem@davemloft.net>
Mon, 28 Jun 2021 20:17:16 +0000 (13:17 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jun 2021 20:17:16 +0000 (13:17 -0700)
/klassert/ipsec-next

Steffen Klassert says:

====================
pull request (net-next): ipsec-next 2021-06-28

1) Remove an unneeded error assignment in esp4_gro_receive().
   From Yang Li.

2) Add a new byseq state hashtable to find acquire states faster.
   From Sabrina Dubroca.

3) Remove some unnecessary variables in pfkey_create().
   From zuoqilin.

4) Remove the unused description from xfrm_type struct.
   From Florian Westphal.

5) Fix a spelling mistake in the comment of xfrm_state_ok().
   From gushengxian.

6) Replace hdr_off indirections by a small helper function.
   From Florian Westphal.

7) Remove xfrm4_output_finish and xfrm6_output_finish declarations,
   they are not used anymore.From Antony Antony.

8) Remove xfrm replay indirections.
   From Florian Westphal.

Please pull or let me know if there are problems.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
21 files changed:
include/net/netns/xfrm.h
include/net/xfrm.h
net/ipv4/ah4.c
net/ipv4/esp4.c
net/ipv4/esp4_offload.c
net/ipv4/ipcomp.c
net/ipv4/xfrm4_tunnel.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/esp6_offload.c
net/ipv6/ipcomp6.c
net/ipv6/mip6.c
net/ipv6/xfrm6_output.c
net/ipv6/xfrm6_tunnel.c
net/key/af_key.c
net/xfrm/xfrm_hash.h
net/xfrm/xfrm_input.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_replay.c
net/xfrm/xfrm_state.c

index e816b6a..e946366 100644 (file)
@@ -42,6 +42,7 @@ struct netns_xfrm {
        struct hlist_head       __rcu *state_bydst;
        struct hlist_head       __rcu *state_bysrc;
        struct hlist_head       __rcu *state_byspi;
+       struct hlist_head       __rcu *state_byseq;
        unsigned int            state_hmask;
        unsigned int            state_num;
        struct work_struct      state_hash_work;
index 1d803e8..b7e65ae 100644 (file)
@@ -145,6 +145,12 @@ enum {
        XFRM_MODE_FLAG_TUNNEL = 1,
 };
 
+enum xfrm_replay_mode {
+       XFRM_REPLAY_MODE_LEGACY,
+       XFRM_REPLAY_MODE_BMP,
+       XFRM_REPLAY_MODE_ESN,
+};
+
 /* Full description of state of transformer. */
 struct xfrm_state {
        possible_net_t          xs_net;
@@ -154,6 +160,7 @@ struct xfrm_state {
        };
        struct hlist_node       bysrc;
        struct hlist_node       byspi;
+       struct hlist_node       byseq;
 
        refcount_t              refcnt;
        spinlock_t              lock;
@@ -214,9 +221,8 @@ struct xfrm_state {
        struct xfrm_replay_state preplay;
        struct xfrm_replay_state_esn *preplay_esn;
 
-       /* The functions for replay detection. */
-       const struct xfrm_replay *repl;
-
+       /* replay detection mode */
+       enum xfrm_replay_mode    repl_mode;
        /* internal flag that only holds state for delayed aevent at the
         * moment
        */
@@ -296,18 +302,6 @@ struct km_event {
        struct net *net;
 };
 
-struct xfrm_replay {
-       void    (*advance)(struct xfrm_state *x, __be32 net_seq);
-       int     (*check)(struct xfrm_state *x,
-                        struct sk_buff *skb,
-                        __be32 net_seq);
-       int     (*recheck)(struct xfrm_state *x,
-                          struct sk_buff *skb,
-                          __be32 net_seq);
-       void    (*notify)(struct xfrm_state *x, int event);
-       int     (*overflow)(struct xfrm_state *x, struct sk_buff *skb);
-};
-
 struct xfrm_if_cb {
        struct xfrm_if  *(*decode_session)(struct sk_buff *skb,
                                           unsigned short family);
@@ -387,7 +381,6 @@ void xfrm_flush_gc(void);
 void xfrm_state_delete_tunnel(struct xfrm_state *x);
 
 struct xfrm_type {
-       char                    *description;
        struct module           *owner;
        u8                      proto;
        u8                      flags;
@@ -402,14 +395,12 @@ struct xfrm_type {
        int                     (*output)(struct xfrm_state *, struct sk_buff *pskb);
        int                     (*reject)(struct xfrm_state *, struct sk_buff *,
                                          const struct flowi *);
-       int                     (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **);
 };
 
 int xfrm_register_type(const struct xfrm_type *type, unsigned short family);
 void xfrm_unregister_type(const struct xfrm_type *type, unsigned short family);
 
 struct xfrm_type_offload {
-       char            *description;
        struct module   *owner;
        u8              proto;
        void            (*encap)(struct xfrm_state *, struct sk_buff *pskb);
@@ -1582,7 +1573,6 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
 }
 
 int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb);
-int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb);
 int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
 int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
 int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
@@ -1606,9 +1596,6 @@ int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family)
 __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr);
 __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr);
 int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb);
-int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb);
-int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
-                         u8 **prevhdr);
 
 #ifdef CONFIG_XFRM
 void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu);
@@ -1722,6 +1709,12 @@ static inline int xfrm_policy_id2dir(u32 index)
 }
 
 #ifdef CONFIG_XFRM
+void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq);
+int xfrm_replay_check(struct xfrm_state *x, struct sk_buff *skb, __be32 net_seq);
+void xfrm_replay_notify(struct xfrm_state *x, int event);
+int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb);
+int xfrm_replay_recheck(struct xfrm_state *x, struct sk_buff *skb, __be32 net_seq);
+
 static inline int xfrm_aevent_is_on(struct net *net)
 {
        struct sock *nlsk;
index 36ed85b..2d2d08a 100644 (file)
@@ -554,7 +554,6 @@ static int ah4_rcv_cb(struct sk_buff *skb, int err)
 
 static const struct xfrm_type ah_type =
 {
-       .description    = "AH4",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_AH,
        .flags          = XFRM_TYPE_REPLAY_PROT,
index 35803ab..f5362b9 100644 (file)
@@ -1198,7 +1198,6 @@ static int esp4_rcv_cb(struct sk_buff *skb, int err)
 
 static const struct xfrm_type esp_type =
 {
-       .description    = "ESP4",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_ESP,
        .flags          = XFRM_TYPE_REPLAY_PROT,
index 33687cf..8e4e9aa 100644 (file)
@@ -33,12 +33,11 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
        struct xfrm_state *x;
        __be32 seq;
        __be32 spi;
-       int err;
 
        if (!pskb_pull(skb, offset))
                return NULL;
 
-       if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0)
+       if (xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq) != 0)
                goto out;
 
        xo = xfrm_offload(skb);
@@ -343,7 +342,6 @@ static const struct net_offload esp4_offload = {
 };
 
 static const struct xfrm_type_offload esp_type_offload = {
-       .description    = "ESP4 OFFLOAD",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_ESP,
        .input_tail     = esp_input_tail,
index b426832..2e69e81 100644 (file)
@@ -152,7 +152,6 @@ static int ipcomp4_rcv_cb(struct sk_buff *skb, int err)
 }
 
 static const struct xfrm_type ipcomp_type = {
-       .description    = "IPCOMP4",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_COMP,
        .init_state     = ipcomp4_init_state,
index fb0648e..f4555a8 100644 (file)
@@ -42,7 +42,6 @@ static void ipip_destroy(struct xfrm_state *x)
 }
 
 static const struct xfrm_type ipip_type = {
-       .description    = "IPIP",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_IPIP,
        .init_state     = ipip_init_state,
index 20d492d..828e625 100644 (file)
@@ -755,7 +755,6 @@ static int ah6_rcv_cb(struct sk_buff *skb, int err)
 }
 
 static const struct xfrm_type ah6_type = {
-       .description    = "AH6",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_AH,
        .flags          = XFRM_TYPE_REPLAY_PROT,
@@ -763,7 +762,6 @@ static const struct xfrm_type ah6_type = {
        .destructor     = ah6_destroy,
        .input          = ah6_input,
        .output         = ah6_output,
-       .hdr_offset     = xfrm6_find_1stfragopt,
 };
 
 static struct xfrm6_protocol ah6_protocol = {
index 393ae2b..37c4b17 100644 (file)
@@ -1243,7 +1243,6 @@ static int esp6_rcv_cb(struct sk_buff *skb, int err)
 }
 
 static const struct xfrm_type esp6_type = {
-       .description    = "ESP6",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_ESP,
        .flags          = XFRM_TYPE_REPLAY_PROT,
@@ -1251,7 +1250,6 @@ static const struct xfrm_type esp6_type = {
        .destructor     = esp6_destroy,
        .input          = esp6_input,
        .output         = esp6_output,
-       .hdr_offset     = xfrm6_find_1stfragopt,
 };
 
 static struct xfrm6_protocol esp6_protocol = {
index 40ed4fc..a349d47 100644 (file)
@@ -377,7 +377,6 @@ static const struct net_offload esp6_offload = {
 };
 
 static const struct xfrm_type_offload esp6_type_offload = {
-       .description    = "ESP6 OFFLOAD",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_ESP,
        .input_tail     = esp6_input_tail,
index daef890..15f984b 100644 (file)
@@ -172,14 +172,12 @@ static int ipcomp6_rcv_cb(struct sk_buff *skb, int err)
 }
 
 static const struct xfrm_type ipcomp6_type = {
-       .description    = "IPCOMP6",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_COMP,
        .init_state     = ipcomp6_init_state,
        .destructor     = ipcomp_destroy,
        .input          = ipcomp_input,
        .output         = ipcomp_output,
-       .hdr_offset     = xfrm6_find_1stfragopt,
 };
 
 static struct xfrm6_protocol ipcomp6_protocol = {
index 878fcec..aeb35d2 100644 (file)
@@ -247,54 +247,6 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb,
        return err;
 }
 
-static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb,
-                              u8 **nexthdr)
-{
-       u16 offset = sizeof(struct ipv6hdr);
-       struct ipv6_opt_hdr *exthdr =
-                                  (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-       const unsigned char *nh = skb_network_header(skb);
-       unsigned int packet_len = skb_tail_pointer(skb) -
-               skb_network_header(skb);
-       int found_rhdr = 0;
-
-       *nexthdr = &ipv6_hdr(skb)->nexthdr;
-
-       while (offset + 1 <= packet_len) {
-
-               switch (**nexthdr) {
-               case NEXTHDR_HOP:
-                       break;
-               case NEXTHDR_ROUTING:
-                       found_rhdr = 1;
-                       break;
-               case NEXTHDR_DEST:
-                       /*
-                        * HAO MUST NOT appear more than once.
-                        * XXX: It is better to try to find by the end of
-                        * XXX: packet if HAO exists.
-                        */
-                       if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
-                               net_dbg_ratelimited("mip6: hao exists already, override\n");
-                               return offset;
-                       }
-
-                       if (found_rhdr)
-                               return offset;
-
-                       break;
-               default:
-                       return offset;
-               }
-
-               offset += ipv6_optlen(exthdr);
-               *nexthdr = &exthdr->nexthdr;
-               exthdr = (struct ipv6_opt_hdr *)(nh + offset);
-       }
-
-       return offset;
-}
-
 static int mip6_destopt_init_state(struct xfrm_state *x)
 {
        if (x->id.spi) {
@@ -324,7 +276,6 @@ static void mip6_destopt_destroy(struct xfrm_state *x)
 }
 
 static const struct xfrm_type mip6_destopt_type = {
-       .description    = "MIP6DESTOPT",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_DSTOPTS,
        .flags          = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_LOCAL_COADDR,
@@ -333,7 +284,6 @@ static const struct xfrm_type mip6_destopt_type = {
        .input          = mip6_destopt_input,
        .output         = mip6_destopt_output,
        .reject         = mip6_destopt_reject,
-       .hdr_offset     = mip6_destopt_offset,
 };
 
 static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb)
@@ -383,53 +333,6 @@ static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
-static int mip6_rthdr_offset(struct xfrm_state *x, struct sk_buff *skb,
-                            u8 **nexthdr)
-{
-       u16 offset = sizeof(struct ipv6hdr);
-       struct ipv6_opt_hdr *exthdr =
-                                  (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-       const unsigned char *nh = skb_network_header(skb);
-       unsigned int packet_len = skb_tail_pointer(skb) -
-               skb_network_header(skb);
-       int found_rhdr = 0;
-
-       *nexthdr = &ipv6_hdr(skb)->nexthdr;
-
-       while (offset + 1 <= packet_len) {
-
-               switch (**nexthdr) {
-               case NEXTHDR_HOP:
-                       break;
-               case NEXTHDR_ROUTING:
-                       if (offset + 3 <= packet_len) {
-                               struct ipv6_rt_hdr *rt;
-                               rt = (struct ipv6_rt_hdr *)(nh + offset);
-                               if (rt->type != 0)
-                                       return offset;
-                       }
-                       found_rhdr = 1;
-                       break;
-               case NEXTHDR_DEST:
-                       if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
-                               return offset;
-
-                       if (found_rhdr)
-                               return offset;
-
-                       break;
-               default:
-                       return offset;
-               }
-
-               offset += ipv6_optlen(exthdr);
-               *nexthdr = &exthdr->nexthdr;
-               exthdr = (struct ipv6_opt_hdr *)(nh + offset);
-       }
-
-       return offset;
-}
-
 static int mip6_rthdr_init_state(struct xfrm_state *x)
 {
        if (x->id.spi) {
@@ -456,7 +359,6 @@ static void mip6_rthdr_destroy(struct xfrm_state *x)
 }
 
 static const struct xfrm_type mip6_rthdr_type = {
-       .description    = "MIP6RT",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_ROUTING,
        .flags          = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_REMOTE_COADDR,
@@ -464,7 +366,6 @@ static const struct xfrm_type mip6_rthdr_type = {
        .destructor     = mip6_rthdr_destroy,
        .input          = mip6_rthdr_input,
        .output         = mip6_rthdr_output,
-       .hdr_offset     = mip6_rthdr_offset,
 };
 
 static int __init mip6_init(void)
index 8b84d53..57fa27c 100644 (file)
 #include <net/ip6_route.h>
 #include <net/xfrm.h>
 
-int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
-                         u8 **prevhdr)
-{
-       return ip6_find_1stfragopt(skb, prevhdr);
-}
-EXPORT_SYMBOL(xfrm6_find_1stfragopt);
-
 void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
 {
        struct flowi6 fl6;
index f696d46..2b31112 100644 (file)
@@ -291,7 +291,6 @@ static void xfrm6_tunnel_destroy(struct xfrm_state *x)
 }
 
 static const struct xfrm_type xfrm6_tunnel_type = {
-       .description    = "IP6IP6",
        .owner          = THIS_MODULE,
        .proto          = IPPROTO_IPV6,
        .init_state     = xfrm6_tunnel_init_state,
index ef9b4ac..de24a7d 100644 (file)
@@ -141,7 +141,6 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
        struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
        struct sock *sk;
        struct pfkey_sock *pfk;
-       int err;
 
        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -150,10 +149,9 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
        if (protocol != PF_KEY_V2)
                return -EPROTONOSUPPORT;
 
-       err = -ENOMEM;
        sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto, kern);
        if (sk == NULL)
-               goto out;
+               return -ENOMEM;
 
        pfk = pfkey_sk(sk);
        mutex_init(&pfk->dump_lock);
@@ -169,8 +167,6 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
        pfkey_insert(sk);
 
        return 0;
-out:
-       return err;
 }
 
 static int pfkey_release(struct socket *sock)
index ce66323..d12bb90 100644 (file)
@@ -131,6 +131,13 @@ __xfrm_spi_hash(const xfrm_address_t *daddr, __be32 spi, u8 proto,
        return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
 }
 
+static inline unsigned int
+__xfrm_seq_hash(u32 seq, unsigned int hmask)
+{
+       unsigned int h = seq;
+       return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
+}
+
 static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
 {
        return (index ^ (index >> 8)) & hmask;
index 1158cd0..3df0861 100644 (file)
@@ -612,7 +612,7 @@ lock:
                        goto drop_unlock;
                }
 
-               if (x->repl->check(x, skb, seq)) {
+               if (xfrm_replay_check(x, skb, seq)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
                        goto drop_unlock;
                }
@@ -660,12 +660,12 @@ resume:
                /* only the first xfrm gets the encap type */
                encap_type = 0;
 
-               if (x->repl->recheck(x, skb, seq)) {
+               if (xfrm_replay_recheck(x, skb, seq)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
                        goto drop_unlock;
                }
 
-               x->repl->advance(x, seq);
+               xfrm_replay_advance(x, seq);
 
                x->curlft.bytes += skb->len;
                x->curlft.packets++;
index e321fc6..ab2fbe4 100644 (file)
@@ -77,6 +77,83 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+static int mip6_rthdr_offset(struct sk_buff *skb, u8 **nexthdr, int type)
+{
+       const unsigned char *nh = skb_network_header(skb);
+       unsigned int offset = sizeof(struct ipv6hdr);
+       unsigned int packet_len;
+       int found_rhdr = 0;
+
+       packet_len = skb_tail_pointer(skb) - nh;
+       *nexthdr = &ipv6_hdr(skb)->nexthdr;
+
+       while (offset <= packet_len) {
+               struct ipv6_opt_hdr *exthdr;
+
+               switch (**nexthdr) {
+               case NEXTHDR_HOP:
+                       break;
+               case NEXTHDR_ROUTING:
+                       if (type == IPPROTO_ROUTING && offset + 3 <= packet_len) {
+                               struct ipv6_rt_hdr *rt;
+
+                               rt = (struct ipv6_rt_hdr *)(nh + offset);
+                               if (rt->type != 0)
+                                       return offset;
+                       }
+                       found_rhdr = 1;
+                       break;
+               case NEXTHDR_DEST:
+                       /* HAO MUST NOT appear more than once.
+                        * XXX: It is better to try to find by the end of
+                        * XXX: packet if HAO exists.
+                        */
+                       if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
+                               net_dbg_ratelimited("mip6: hao exists already, override\n");
+                               return offset;
+                       }
+
+                       if (found_rhdr)
+                               return offset;
+
+                       break;
+               default:
+                       return offset;
+               }
+
+               if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
+                       return -EINVAL;
+
+               exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
+                                                offset);
+               offset += ipv6_optlen(exthdr);
+               if (offset > IPV6_MAXPLEN)
+                       return -EINVAL;
+               *nexthdr = &exthdr->nexthdr;
+       }
+
+       return -EINVAL;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int xfrm6_hdr_offset(struct xfrm_state *x, struct sk_buff *skb, u8 **prevhdr)
+{
+       switch (x->type->proto) {
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+       case IPPROTO_DSTOPTS:
+       case IPPROTO_ROUTING:
+               return mip6_rthdr_offset(skb, prevhdr, x->type->proto);
+#endif
+       default:
+               break;
+       }
+
+       return ip6_find_1stfragopt(skb, prevhdr);
+}
+#endif
+
 /* Add encapsulation header.
  *
  * The IP header and mutable extension headers will be moved forward to make
@@ -92,7 +169,7 @@ static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
        iph = ipv6_hdr(skb);
        skb_set_inner_transport_header(skb, skb_transport_offset(skb));
 
-       hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
+       hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr);
        if (hdr_len < 0)
                return hdr_len;
        skb_set_mac_header(skb,
@@ -122,7 +199,7 @@ static int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb)
 
        iph = ipv6_hdr(skb);
 
-       hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
+       hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr);
        if (hdr_len < 0)
                return hdr_len;
        skb_set_mac_header(skb,
@@ -448,7 +525,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
                        goto error;
                }
 
-               err = x->repl->overflow(x, skb);
+               err = xfrm_replay_overflow(x, skb);
                if (err) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR);
                        goto error;
index ce500f8..1e24b21 100644 (file)
@@ -3247,7 +3247,7 @@ xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x,
 
 /*
  * 0 or more than 0 is returned when validation is succeeded (either bypass
- * because of optional transport mode, or next index of the mathced secpath
+ * because of optional transport mode, or next index of the matched secpath
  * state with the template.
  * -1 is returned when no matching template is found.
  * Otherwise "-2 - errored_index" is returned.
index c6a4338..9277d81 100644 (file)
@@ -34,8 +34,11 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
        return seq_hi;
 }
 EXPORT_SYMBOL(xfrm_replay_seqhi);
-;
-static void xfrm_replay_notify(struct xfrm_state *x, int event)
+
+static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event);
+static void xfrm_replay_notify_esn(struct xfrm_state *x, int event);
+
+void xfrm_replay_notify(struct xfrm_state *x, int event)
 {
        struct km_event c;
        /* we send notify messages in case
@@ -48,6 +51,17 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)
         *  The state structure must be locked!
         */
 
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               xfrm_replay_notify_bmp(x, event);
+               return;
+       case XFRM_REPLAY_MODE_ESN:
+               xfrm_replay_notify_esn(x, event);
+               return;
+       }
+
        switch (event) {
        case XFRM_REPLAY_UPDATE:
                if (!x->replay_maxdiff ||
@@ -81,7 +95,7 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event)
                x->xflags &= ~XFRM_TIME_DEFER;
 }
 
-static int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
+static int __xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
 {
        int err = 0;
        struct net *net = xs_net(x);
@@ -98,14 +112,14 @@ static int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
                        return err;
                }
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
 }
 
-static int xfrm_replay_check(struct xfrm_state *x,
-                     struct sk_buff *skb, __be32 net_seq)
+static int xfrm_replay_check_legacy(struct xfrm_state *x,
+                                   struct sk_buff *skb, __be32 net_seq)
 {
        u32 diff;
        u32 seq = ntohl(net_seq);
@@ -136,14 +150,26 @@ err:
        return -EINVAL;
 }
 
-static void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
+static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq);
+static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq);
+
+void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
 {
-       u32 diff;
-       u32 seq = ntohl(net_seq);
+       u32 diff, seq;
+
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               return xfrm_replay_advance_bmp(x, net_seq);
+       case XFRM_REPLAY_MODE_ESN:
+               return xfrm_replay_advance_esn(x, net_seq);
+       }
 
        if (!x->props.replay_window)
                return;
 
+       seq = ntohl(net_seq);
        if (seq > x->replay.seq) {
                diff = seq - x->replay.seq;
                if (diff < x->props.replay_window)
@@ -157,7 +183,7 @@ static void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
        }
 
        if (xfrm_aevent_is_on(xs_net(x)))
-               x->repl->notify(x, XFRM_REPLAY_UPDATE);
+               xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
 
 static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb)
@@ -178,7 +204,7 @@ static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb)
                        return err;
                }
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
@@ -273,7 +299,7 @@ static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
        replay_esn->bmp[nr] |= (1U << bitnr);
 
        if (xfrm_aevent_is_on(xs_net(x)))
-               x->repl->notify(x, XFRM_REPLAY_UPDATE);
+               xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
 
 static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
@@ -416,7 +442,7 @@ static int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb)
                        }
                }
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
@@ -481,6 +507,21 @@ err:
        return -EINVAL;
 }
 
+int xfrm_replay_check(struct xfrm_state *x,
+                     struct sk_buff *skb, __be32 net_seq)
+{
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               return xfrm_replay_check_bmp(x, skb, net_seq);
+       case XFRM_REPLAY_MODE_ESN:
+               return xfrm_replay_check_esn(x, skb, net_seq);
+       }
+
+       return xfrm_replay_check_legacy(x, skb, net_seq);
+}
+
 static int xfrm_replay_recheck_esn(struct xfrm_state *x,
                                   struct sk_buff *skb, __be32 net_seq)
 {
@@ -493,6 +534,22 @@ static int xfrm_replay_recheck_esn(struct xfrm_state *x,
        return xfrm_replay_check_esn(x, skb, net_seq);
 }
 
+int xfrm_replay_recheck(struct xfrm_state *x,
+                       struct sk_buff *skb, __be32 net_seq)
+{
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               /* no special recheck treatment */
+               return xfrm_replay_check_bmp(x, skb, net_seq);
+       case XFRM_REPLAY_MODE_ESN:
+               return xfrm_replay_recheck_esn(x, skb, net_seq);
+       }
+
+       return xfrm_replay_check_legacy(x, skb, net_seq);
+}
+
 static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
 {
        unsigned int bitnr, nr, i;
@@ -548,7 +605,7 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
        replay_esn->bmp[nr] |= (1U << bitnr);
 
        if (xfrm_aevent_is_on(xs_net(x)))
-               x->repl->notify(x, XFRM_REPLAY_UPDATE);
+               xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
 
 #ifdef CONFIG_XFRM_OFFLOAD
@@ -560,7 +617,7 @@ static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *sk
        __u32 oseq = x->replay.oseq;
 
        if (!xo)
-               return xfrm_replay_overflow(x, skb);
+               return __xfrm_replay_overflow(x, skb);
 
        if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
                if (!skb_is_gso(skb)) {
@@ -585,7 +642,7 @@ static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *sk
                x->replay.oseq = oseq;
 
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
@@ -625,7 +682,7 @@ static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff
                }
 
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
@@ -674,59 +731,39 @@ static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff
                replay_esn->oseq = oseq;
 
                if (xfrm_aevent_is_on(net))
-                       x->repl->notify(x, XFRM_REPLAY_UPDATE);
+                       xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
        }
 
        return err;
 }
 
-static const struct xfrm_replay xfrm_replay_legacy = {
-       .advance        = xfrm_replay_advance,
-       .check          = xfrm_replay_check,
-       .recheck        = xfrm_replay_check,
-       .notify         = xfrm_replay_notify,
-       .overflow       = xfrm_replay_overflow_offload,
-};
-
-static const struct xfrm_replay xfrm_replay_bmp = {
-       .advance        = xfrm_replay_advance_bmp,
-       .check          = xfrm_replay_check_bmp,
-       .recheck        = xfrm_replay_check_bmp,
-       .notify         = xfrm_replay_notify_bmp,
-       .overflow       = xfrm_replay_overflow_offload_bmp,
-};
-
-static const struct xfrm_replay xfrm_replay_esn = {
-       .advance        = xfrm_replay_advance_esn,
-       .check          = xfrm_replay_check_esn,
-       .recheck        = xfrm_replay_recheck_esn,
-       .notify         = xfrm_replay_notify_esn,
-       .overflow       = xfrm_replay_overflow_offload_esn,
-};
+int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
+{
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               return xfrm_replay_overflow_offload_bmp(x, skb);
+       case XFRM_REPLAY_MODE_ESN:
+               return xfrm_replay_overflow_offload_esn(x, skb);
+       }
+
+       return xfrm_replay_overflow_offload(x, skb);
+}
 #else
-static const struct xfrm_replay xfrm_replay_legacy = {
-       .advance        = xfrm_replay_advance,
-       .check          = xfrm_replay_check,
-       .recheck        = xfrm_replay_check,
-       .notify         = xfrm_replay_notify,
-       .overflow       = xfrm_replay_overflow,
-};
-
-static const struct xfrm_replay xfrm_replay_bmp = {
-       .advance        = xfrm_replay_advance_bmp,
-       .check          = xfrm_replay_check_bmp,
-       .recheck        = xfrm_replay_check_bmp,
-       .notify         = xfrm_replay_notify_bmp,
-       .overflow       = xfrm_replay_overflow_bmp,
-};
-
-static const struct xfrm_replay xfrm_replay_esn = {
-       .advance        = xfrm_replay_advance_esn,
-       .check          = xfrm_replay_check_esn,
-       .recheck        = xfrm_replay_recheck_esn,
-       .notify         = xfrm_replay_notify_esn,
-       .overflow       = xfrm_replay_overflow_esn,
-};
+int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
+{
+       switch (x->repl_mode) {
+       case XFRM_REPLAY_MODE_LEGACY:
+               break;
+       case XFRM_REPLAY_MODE_BMP:
+               return xfrm_replay_overflow_bmp(x, skb);
+       case XFRM_REPLAY_MODE_ESN:
+               return xfrm_replay_overflow_esn(x, skb);
+       }
+
+       return __xfrm_replay_overflow(x, skb);
+}
 #endif
 
 int xfrm_init_replay(struct xfrm_state *x)
@@ -741,12 +778,12 @@ int xfrm_init_replay(struct xfrm_state *x)
                if (x->props.flags & XFRM_STATE_ESN) {
                        if (replay_esn->replay_window == 0)
                                return -EINVAL;
-                       x->repl = &xfrm_replay_esn;
+                       x->repl_mode = XFRM_REPLAY_MODE_ESN;
                } else {
-                       x->repl = &xfrm_replay_bmp;
+                       x->repl_mode = XFRM_REPLAY_MODE_BMP;
                }
        } else {
-               x->repl = &xfrm_replay_legacy;
+               x->repl_mode = XFRM_REPLAY_MODE_LEGACY;
        }
 
        return 0;
index 4496f7e..c2ce1e6 100644 (file)
@@ -78,10 +78,16 @@ xfrm_spi_hash(struct net *net, const xfrm_address_t *daddr,
        return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
 }
 
+static unsigned int xfrm_seq_hash(struct net *net, u32 seq)
+{
+       return __xfrm_seq_hash(seq, net->xfrm.state_hmask);
+}
+
 static void xfrm_hash_transfer(struct hlist_head *list,
                               struct hlist_head *ndsttable,
                               struct hlist_head *nsrctable,
                               struct hlist_head *nspitable,
+                              struct hlist_head *nseqtable,
                               unsigned int nhashmask)
 {
        struct hlist_node *tmp;
@@ -106,6 +112,11 @@ static void xfrm_hash_transfer(struct hlist_head *list,
                                            nhashmask);
                        hlist_add_head_rcu(&x->byspi, nspitable + h);
                }
+
+               if (x->km.seq) {
+                       h = __xfrm_seq_hash(x->km.seq, nhashmask);
+                       hlist_add_head_rcu(&x->byseq, nseqtable + h);
+               }
        }
 }
 
@@ -117,7 +128,7 @@ static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
 static void xfrm_hash_resize(struct work_struct *work)
 {
        struct net *net = container_of(work, struct net, xfrm.state_hash_work);
-       struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
+       struct hlist_head *ndst, *nsrc, *nspi, *nseq, *odst, *osrc, *ospi, *oseq;
        unsigned long nsize, osize;
        unsigned int nhashmask, ohashmask;
        int i;
@@ -137,6 +148,13 @@ static void xfrm_hash_resize(struct work_struct *work)
                xfrm_hash_free(nsrc, nsize);
                return;
        }
+       nseq = xfrm_hash_alloc(nsize);
+       if (!nseq) {
+               xfrm_hash_free(ndst, nsize);
+               xfrm_hash_free(nsrc, nsize);
+               xfrm_hash_free(nspi, nsize);
+               return;
+       }
 
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
        write_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
@@ -144,15 +162,17 @@ static void xfrm_hash_resize(struct work_struct *work)
        nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
        odst = xfrm_state_deref_prot(net->xfrm.state_bydst, net);
        for (i = net->xfrm.state_hmask; i >= 0; i--)
-               xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nhashmask);
+               xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq, nhashmask);
 
        osrc = xfrm_state_deref_prot(net->xfrm.state_bysrc, net);
        ospi = xfrm_state_deref_prot(net->xfrm.state_byspi, net);
+       oseq = xfrm_state_deref_prot(net->xfrm.state_byseq, net);
        ohashmask = net->xfrm.state_hmask;
 
        rcu_assign_pointer(net->xfrm.state_bydst, ndst);
        rcu_assign_pointer(net->xfrm.state_bysrc, nsrc);
        rcu_assign_pointer(net->xfrm.state_byspi, nspi);
+       rcu_assign_pointer(net->xfrm.state_byseq, nseq);
        net->xfrm.state_hmask = nhashmask;
 
        write_seqcount_end(&net->xfrm.xfrm_state_hash_generation);
@@ -165,6 +185,7 @@ static void xfrm_hash_resize(struct work_struct *work)
        xfrm_hash_free(odst, osize);
        xfrm_hash_free(osrc, osize);
        xfrm_hash_free(ospi, osize);
+       xfrm_hash_free(oseq, osize);
 }
 
 static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
@@ -621,6 +642,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
+               INIT_HLIST_NODE(&x->byseq);
                hrtimer_init(&x->mtimer, CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
                x->mtimer.function = xfrm_timer_handler;
                timer_setup(&x->rtimer, xfrm_replay_timer_handler, 0);
@@ -664,6 +686,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
                list_del(&x->km.all);
                hlist_del_rcu(&x->bydst);
                hlist_del_rcu(&x->bysrc);
+               if (x->km.seq)
+                       hlist_del_rcu(&x->byseq);
                if (x->id.spi)
                        hlist_del_rcu(&x->byspi);
                net->xfrm.state_num--;
@@ -1148,6 +1172,10 @@ found:
                                h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
                                hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
                        }
+                       if (x->km.seq) {
+                               h = xfrm_seq_hash(net, x->km.seq);
+                               hlist_add_head_rcu(&x->byseq, net->xfrm.state_byseq + h);
+                       }
                        x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
                        hrtimer_start(&x->mtimer,
                                      ktime_set(net->xfrm.sysctl_acq_expires, 0),
@@ -1263,6 +1291,12 @@ static void __xfrm_state_insert(struct xfrm_state *x)
                hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
        }
 
+       if (x->km.seq) {
+               h = xfrm_seq_hash(net, x->km.seq);
+
+               hlist_add_head_rcu(&x->byseq, net->xfrm.state_byseq + h);
+       }
+
        hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
        if (x->replay_maxage)
                mod_timer(&x->rtimer, jiffies + x->replay_maxage);
@@ -1932,20 +1966,18 @@ xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
 
 static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)
 {
-       int i;
-
-       for (i = 0; i <= net->xfrm.state_hmask; i++) {
-               struct xfrm_state *x;
+       unsigned int h = xfrm_seq_hash(net, seq);
+       struct xfrm_state *x;
 
-               hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
-                       if (x->km.seq == seq &&
-                           (mark & x->mark.m) == x->mark.v &&
-                           x->km.state == XFRM_STATE_ACQ) {
-                               xfrm_state_hold(x);
-                               return x;
-                       }
+       hlist_for_each_entry_rcu(x, net->xfrm.state_byseq + h, byseq) {
+               if (x->km.seq == seq &&
+                   (mark & x->mark.m) == x->mark.v &&
+                   x->km.state == XFRM_STATE_ACQ) {
+                       xfrm_state_hold(x);
+                       return x;
                }
        }
+
        return NULL;
 }
 
@@ -2145,7 +2177,7 @@ static void xfrm_replay_timer_handler(struct timer_list *t)
 
        if (x->km.state == XFRM_STATE_VALID) {
                if (xfrm_aevent_is_on(xs_net(x)))
-                       x->repl->notify(x, XFRM_REPLAY_TIMEOUT);
+                       xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
                else
                        x->xflags |= XFRM_TIME_DEFER;
        }
@@ -2660,6 +2692,9 @@ int __net_init xfrm_state_init(struct net *net)
        net->xfrm.state_byspi = xfrm_hash_alloc(sz);
        if (!net->xfrm.state_byspi)
                goto out_byspi;
+       net->xfrm.state_byseq = xfrm_hash_alloc(sz);
+       if (!net->xfrm.state_byseq)
+               goto out_byseq;
        net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
 
        net->xfrm.state_num = 0;
@@ -2669,6 +2704,8 @@ int __net_init xfrm_state_init(struct net *net)
                               &net->xfrm.xfrm_state_lock);
        return 0;
 
+out_byseq:
+       xfrm_hash_free(net->xfrm.state_byspi, sz);
 out_byspi:
        xfrm_hash_free(net->xfrm.state_bysrc, sz);
 out_bysrc:
@@ -2688,6 +2725,8 @@ void xfrm_state_fini(struct net *net)
        WARN_ON(!list_empty(&net->xfrm.state_all));
 
        sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
+       WARN_ON(!hlist_empty(net->xfrm.state_byseq));
+       xfrm_hash_free(net->xfrm.state_byseq, sz);
        WARN_ON(!hlist_empty(net->xfrm.state_byspi));
        xfrm_hash_free(net->xfrm.state_byspi, sz);
        WARN_ON(!hlist_empty(net->xfrm.state_bysrc));