Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/klassert/ipsec

Steffen Klassert says:

====================
pull request (net): ipsec 2019-04-30

1) Fix an out-of-bound array accesses in __xfrm_policy_unlink.
   From YueHaibing.

2) Reset the secpath on failure in the ESP GRO handlers
   to avoid dereferencing an invalid pointer on error.
   From Myungho Jung.

3) Add and revert a patch that tried to add rcu annotations
   to netns_xfrm. From Su Yanjun.

4) Wait for rcu callbacks before freeing xfrm6_tunnel_spi_kmem.
   From Su Yanjun.

5) Fix forgotten vti4 ipip tunnel deregistration.
   From Jeremy Sowden:

6) Remove some duplicated log messages in vti4.
   From Jeremy Sowden.

7) Don't use IPSEC_PROTO_ANY when flushing states because
   this will flush only IPsec portocol speciffic states.
   IPPROTO_ROUTING states may remain in the lists when
   doing net exit. Fix this by replacing IPSEC_PROTO_ANY
   with zero. From Cong Wang.

8) Add length check for UDP encapsulation to fix "Oversized IP packet"
   warnings on receive side. From Sabrina Dubroca.

9) Fix xfrm interface lookup when the interface is associated to
   a vrf layer 3 master device. From Martin Willi.

10) Reload header pointers after pskb_may_pull() in _decode_session4(),
    otherwise we may read from uninitialized memory.

11) Update the documentation about xfrm[46]_gc_thresh, it
    is not used anymore after the flowcache removal.
    From Nicolas Dichtel.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
davem330 committed Apr 30, 2019
2 parents 6c0afef + 837f741 commit b145745
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 49 deletions.
2 changes: 2 additions & 0 deletions Documentation/networking/ip-sysctl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,7 @@ tag - INTEGER
Default value is 0.

xfrm4_gc_thresh - INTEGER
(Obsolete since linux-4.14)
The threshold at which we will start garbage collecting for IPv4
destination cache entries. At twice this value the system will
refuse new allocations.
Expand Down Expand Up @@ -1920,6 +1921,7 @@ echo_ignore_all - BOOLEAN
Default: 0

xfrm6_gc_thresh - INTEGER
(Obsolete since linux-4.14)
The threshold at which we will start garbage collecting for IPv6
destination cache entries. At twice this value the system will
refuse new allocations.
Expand Down
20 changes: 19 additions & 1 deletion include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ struct xfrm_replay {
};

struct xfrm_if_cb {
struct xfrm_if *(*decode_session)(struct sk_buff *skb);
struct xfrm_if *(*decode_session)(struct sk_buff *skb,
unsigned short family);
};

void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb);
Expand Down Expand Up @@ -1404,6 +1405,23 @@ static inline int xfrm_state_kern(const struct xfrm_state *x)
return atomic_read(&x->tunnel_users);
}

static inline bool xfrm_id_proto_valid(u8 proto)
{
switch (proto) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_COMP:
#if IS_ENABLED(CONFIG_IPV6)
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
#endif
return true;
default:
return false;
}
}

/* IPSEC_PROTO_ANY only matches 3 IPsec protocols, 0 could match all. */
static inline int xfrm_id_proto_match(u8 proto, u8 userproto)
{
return (!userproto || proto == userproto ||
Expand Down
20 changes: 15 additions & 5 deletions net/ipv4/esp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,26 +226,30 @@ static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto)
tail[plen - 1] = proto;
}

static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp)
static int esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp)
{
int encap_type;
struct udphdr *uh;
__be32 *udpdata32;
__be16 sport, dport;
struct xfrm_encap_tmpl *encap = x->encap;
struct ip_esp_hdr *esph = esp->esph;
unsigned int len;

spin_lock_bh(&x->lock);
sport = encap->encap_sport;
dport = encap->encap_dport;
encap_type = encap->encap_type;
spin_unlock_bh(&x->lock);

len = skb->len + esp->tailen - skb_transport_offset(skb);
if (len + sizeof(struct iphdr) >= IP_MAX_MTU)
return -EMSGSIZE;

uh = (struct udphdr *)esph;
uh->source = sport;
uh->dest = dport;
uh->len = htons(skb->len + esp->tailen
- skb_transport_offset(skb));
uh->len = htons(len);
uh->check = 0;

switch (encap_type) {
Expand All @@ -262,6 +266,8 @@ static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, stru

*skb_mac_header(skb) = IPPROTO_UDP;
esp->esph = esph;

return 0;
}

int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp)
Expand All @@ -275,8 +281,12 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
int tailen = esp->tailen;

/* this is non-NULL only with UDP Encapsulation */
if (x->encap)
esp_output_udp_encap(x, skb, esp);
if (x->encap) {
int err = esp_output_udp_encap(x, skb, esp);

if (err < 0)
return err;
}

if (!skb_cloned(skb)) {
if (tailen <= skb_tailroom(skb)) {
Expand Down
8 changes: 5 additions & 3 deletions net/ipv4/esp4_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,21 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
goto out;

if (sp->len == XFRM_MAX_DEPTH)
goto out;
goto out_reset;

x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
(xfrm_address_t *)&ip_hdr(skb)->daddr,
spi, IPPROTO_ESP, AF_INET);
if (!x)
goto out;
goto out_reset;

sp->xvec[sp->len++] = x;
sp->olen++;

xo = xfrm_offload(skb);
if (!xo) {
xfrm_state_put(x);
goto out;
goto out_reset;
}
}

Expand All @@ -82,6 +82,8 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
xfrm_input(skb, IPPROTO_ESP, spi, -2);

return ERR_PTR(-EINPROGRESS);
out_reset:
secpath_reset(skb);
out:
skb_push(skb, offset);
NAPI_GRO_CB(skb)->same_flow = 0;
Expand Down
9 changes: 4 additions & 5 deletions net/ipv4/ip_vti.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,10 +646,8 @@ static int __init vti_init(void)

msg = "ipip tunnel";
err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
if (err < 0) {
pr_info("%s: cant't register tunnel\n",__func__);
if (err < 0)
goto xfrm_tunnel_failed;
}

msg = "netlink interface";
err = rtnl_link_register(&vti_link_ops);
Expand All @@ -659,9 +657,9 @@ static int __init vti_init(void)
return err;

rtnl_link_failed:
xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
xfrm_tunnel_failed:
xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
xfrm_tunnel_failed:
xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
xfrm_proto_comp_failed:
xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
xfrm_proto_ah_failed:
Expand All @@ -676,6 +674,7 @@ static int __init vti_init(void)
static void __exit vti_fini(void)
{
rtnl_link_unregister(&vti_link_ops);
xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
Expand Down
24 changes: 13 additions & 11 deletions net/ipv4/xfrm4_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ static void
_decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
{
const struct iphdr *iph = ip_hdr(skb);
u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
int ihl = iph->ihl;
u8 *xprth = skb_network_header(skb) + ihl * 4;
struct flowi4 *fl4 = &fl->u.ip4;
int oif = 0;

Expand All @@ -122,6 +123,11 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
fl4->flowi4_mark = skb->mark;
fl4->flowi4_oif = reverse ? skb->skb_iif : oif;

fl4->flowi4_proto = iph->protocol;
fl4->daddr = reverse ? iph->saddr : iph->daddr;
fl4->saddr = reverse ? iph->daddr : iph->saddr;
fl4->flowi4_tos = iph->tos;

if (!ip_is_fragment(iph)) {
switch (iph->protocol) {
case IPPROTO_UDP:
Expand All @@ -133,7 +139,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
pskb_may_pull(skb, xprth + 4 - skb->data)) {
__be16 *ports;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
ports = (__be16 *)xprth;

fl4->fl4_sport = ports[!!reverse];
Expand All @@ -146,7 +152,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
pskb_may_pull(skb, xprth + 2 - skb->data)) {
u8 *icmp;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
icmp = xprth;

fl4->fl4_icmp_type = icmp[0];
Expand All @@ -159,7 +165,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
pskb_may_pull(skb, xprth + 4 - skb->data)) {
__be32 *ehdr;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
ehdr = (__be32 *)xprth;

fl4->fl4_ipsec_spi = ehdr[0];
Expand All @@ -171,7 +177,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
pskb_may_pull(skb, xprth + 8 - skb->data)) {
__be32 *ah_hdr;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
ah_hdr = (__be32 *)xprth;

fl4->fl4_ipsec_spi = ah_hdr[1];
Expand All @@ -183,7 +189,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
pskb_may_pull(skb, xprth + 4 - skb->data)) {
__be16 *ipcomp_hdr;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
ipcomp_hdr = (__be16 *)xprth;

fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
Expand All @@ -196,7 +202,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
__be16 *greflags;
__be32 *gre_hdr;

xprth = skb_network_header(skb) + iph->ihl * 4;
xprth = skb_network_header(skb) + ihl * 4;
greflags = (__be16 *)xprth;
gre_hdr = (__be32 *)xprth;

Expand All @@ -213,10 +219,6 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
break;
}
}
fl4->flowi4_proto = iph->protocol;
fl4->daddr = reverse ? iph->saddr : iph->daddr;
fl4->saddr = reverse ? iph->daddr : iph->saddr;
fl4->flowi4_tos = iph->tos;
}

static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk,
Expand Down
8 changes: 5 additions & 3 deletions net/ipv6/esp6_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
goto out;

if (sp->len == XFRM_MAX_DEPTH)
goto out;
goto out_reset;

x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
spi, IPPROTO_ESP, AF_INET6);
if (!x)
goto out;
goto out_reset;

sp->xvec[sp->len++] = x;
sp->olen++;

xo = xfrm_offload(skb);
if (!xo) {
xfrm_state_put(x);
goto out;
goto out_reset;
}
}

Expand All @@ -109,6 +109,8 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
xfrm_input(skb, IPPROTO_ESP, spi, -2);

return ERR_PTR(-EINPROGRESS);
out_reset:
secpath_reset(skb);
out:
skb_push(skb, offset);
NAPI_GRO_CB(skb)->same_flow = 0;
Expand Down
6 changes: 5 additions & 1 deletion net/ipv6/xfrm6_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net)
unsigned int i;

xfrm_flush_gc();
xfrm_state_flush(net, IPSEC_PROTO_ANY, false, true);
xfrm_state_flush(net, 0, false, true);

for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
Expand Down Expand Up @@ -402,6 +402,10 @@ static void __exit xfrm6_tunnel_fini(void)
xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
unregister_pernet_subsys(&xfrm6_tunnel_net_ops);
/* Someone maybe has gotten the xfrm6_tunnel_spi.
* So need to wait it.
*/
rcu_barrier();
kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
}

Expand Down
4 changes: 3 additions & 1 deletion net/key/af_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -1951,8 +1951,10 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)

if (rq->sadb_x_ipsecrequest_mode == 0)
return -EINVAL;
if (!xfrm_id_proto_valid(rq->sadb_x_ipsecrequest_proto))
return -EINVAL;

t->id.proto = rq->sadb_x_ipsecrequest_proto; /* XXX check proto */
t->id.proto = rq->sadb_x_ipsecrequest_proto;
if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0)
return -EINVAL;
t->mode = mode;
Expand Down
17 changes: 14 additions & 3 deletions net/xfrm/xfrm_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,28 @@ static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x)
return NULL;
}

static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb)
static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb,
unsigned short family)
{
struct xfrmi_net *xfrmn;
int ifindex;
struct xfrm_if *xi;
int ifindex = 0;

if (!secpath_exists(skb) || !skb->dev)
return NULL;

switch (family) {
case AF_INET6:
ifindex = inet6_sdif(skb);
break;
case AF_INET:
ifindex = inet_sdif(skb);
break;
}
if (!ifindex)
ifindex = skb->dev->ifindex;

xfrmn = net_generic(xs_net(xfrm_input_state(skb)), xfrmi_net_id);
ifindex = skb->dev->ifindex;

for_each_xfrmi_rcu(xfrmn->xfrmi[0], xi) {
if (ifindex == xi->dev->ifindex &&
Expand Down
2 changes: 1 addition & 1 deletion net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3313,7 +3313,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
ifcb = xfrm_if_get_cb();

if (ifcb) {
xi = ifcb->decode_session(skb);
xi = ifcb->decode_session(skb, family);
if (xi) {
if_id = xi->p.if_id;
net = xi->net;
Expand Down
2 changes: 1 addition & 1 deletion net/xfrm/xfrm_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -2384,7 +2384,7 @@ void xfrm_state_fini(struct net *net)

flush_work(&net->xfrm.state_hash_work);
flush_work(&xfrm_state_gc_work);
xfrm_state_flush(net, IPSEC_PROTO_ANY, false, true);
xfrm_state_flush(net, 0, false, true);

WARN_ON(!list_empty(&net->xfrm.state_all));

Expand Down
Loading

0 comments on commit b145745

Please sign in to comment.