Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
Steffen Klassert says: ==================== This pull request fixes some issues that arise when 6in4 or 4in6 tunnels are used in combination with IPsec, all from Hannes Frederic Sowa and a null pointer dereference when queueing packets to the policy hold queue. 1) We might access the local error handler of the wrong address family if 6in4 or 4in6 tunnel is protected by ipsec. Fix this by addind a pointer to the correct local_error to xfrm_state_afinet. 2) Add a helper function to always refer to the correct interpretation of skb->sk. 3) Call skb_reset_inner_headers to record the position of the inner headers when adding a new one in various ipv6 tunnels. This is needed to identify the addresses where to send back errors in the xfrm layer. 4) Dereference inner ipv6 header if encapsulated to always call the right error handler. 5) Choose protocol family by skb protocol to not call the wrong xfrm{4,6}_local_error handler in case an ipv6 sockets is used in ipv4 mode. 6) Partly revert "xfrm: introduce helper for safe determination of mtu" because this introduced pmtu discovery problems. 7) Set skb->protocol on tcp, raw and ip6_append_data genereated skbs. We need this to get the correct mtu informations in xfrm. 8) Fix null pointer dereference in xdst_queue_output. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
79f9ab7e0a
|
@ -317,4 +317,12 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
|
|||
return hoplimit;
|
||||
}
|
||||
|
||||
static inline int ip_skb_dst_mtu(struct sk_buff *skb)
|
||||
{
|
||||
struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
|
||||
|
||||
return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
|
||||
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
|
||||
}
|
||||
|
||||
#endif /* _ROUTE_H */
|
||||
|
|
|
@ -341,10 +341,13 @@ struct xfrm_state_afinfo {
|
|||
struct sk_buff *skb);
|
||||
int (*transport_finish)(struct sk_buff *skb,
|
||||
int async);
|
||||
void (*local_error)(struct sk_buff *skb, u32 mtu);
|
||||
};
|
||||
|
||||
extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
|
||||
extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
|
||||
extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
|
||||
extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
|
||||
|
||||
extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
|
||||
|
||||
|
@ -1477,6 +1480,7 @@ extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
|
|||
extern int xfrm_output_resume(struct sk_buff *skb, int err);
|
||||
extern int xfrm_output(struct sk_buff *skb);
|
||||
extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
|
||||
extern void xfrm_local_error(struct sk_buff *skb, int mtu);
|
||||
extern int xfrm4_extract_header(struct sk_buff *skb);
|
||||
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
|
||||
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
|
||||
|
@ -1497,6 +1501,7 @@ extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short fam
|
|||
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
|
||||
extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler);
|
||||
extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler);
|
||||
extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
|
||||
extern int xfrm6_extract_header(struct sk_buff *skb);
|
||||
extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
|
||||
extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
|
||||
|
@ -1514,6 +1519,7 @@ extern int xfrm6_output(struct sk_buff *skb);
|
|||
extern int xfrm6_output_finish(struct sk_buff *skb);
|
||||
extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
|
||||
u8 **prevhdr);
|
||||
extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
|
||||
|
||||
#ifdef CONFIG_XFRM
|
||||
extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
|
||||
|
|
|
@ -211,14 +211,6 @@ static inline int ip_finish_output2(struct sk_buff *skb)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int ip_skb_dst_mtu(struct sk_buff *skb)
|
||||
{
|
||||
struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
|
||||
|
||||
return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
|
||||
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
|
||||
}
|
||||
|
||||
static int ip_finish_output(struct sk_buff *skb)
|
||||
{
|
||||
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
static int xfrm4_tunnel_check_size(struct sk_buff *skb)
|
||||
{
|
||||
int mtu, ret = 0;
|
||||
struct dst_entry *dst;
|
||||
|
||||
if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
|
||||
goto out;
|
||||
|
@ -29,12 +28,10 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
|
|||
if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
|
||||
goto out;
|
||||
|
||||
dst = skb_dst(skb);
|
||||
mtu = dst_mtu(dst);
|
||||
mtu = dst_mtu(skb_dst(skb));
|
||||
if (skb->len > mtu) {
|
||||
if (skb->sk)
|
||||
ip_local_error(skb->sk, EMSGSIZE, ip_hdr(skb)->daddr,
|
||||
inet_sk(skb->sk)->inet_dport, mtu);
|
||||
xfrm_local_error(skb, mtu);
|
||||
else
|
||||
icmp_send(skb, ICMP_DEST_UNREACH,
|
||||
ICMP_FRAG_NEEDED, htonl(mtu));
|
||||
|
@ -99,3 +96,12 @@ int xfrm4_output(struct sk_buff *skb)
|
|||
x->outer_mode->afinfo->output_finish,
|
||||
!(IPCB(skb)->flags & IPSKB_REROUTED));
|
||||
}
|
||||
|
||||
void xfrm4_local_error(struct sk_buff *skb, u32 mtu)
|
||||
{
|
||||
struct iphdr *hdr;
|
||||
|
||||
hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
|
||||
ip_local_error(skb->sk, EMSGSIZE, hdr->daddr,
|
||||
inet_sk(skb->sk)->inet_dport, mtu);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = {
|
|||
.extract_input = xfrm4_extract_input,
|
||||
.extract_output = xfrm4_extract_output,
|
||||
.transport_finish = xfrm4_transport_finish,
|
||||
.local_error = xfrm4_local_error,
|
||||
};
|
||||
|
||||
void __init xfrm4_state_init(void)
|
||||
|
|
|
@ -724,6 +724,11 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
|
|||
ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
|
||||
}
|
||||
|
||||
if (likely(!skb->encapsulation)) {
|
||||
skb_reset_inner_headers(skb);
|
||||
skb->encapsulation = 1;
|
||||
}
|
||||
|
||||
skb_push(skb, gre_hlen);
|
||||
skb_reset_network_header(skb);
|
||||
skb_set_transport_header(skb, sizeof(*ipv6h));
|
||||
|
|
|
@ -238,6 +238,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
|||
hdr->saddr = fl6->saddr;
|
||||
hdr->daddr = *first_hop;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
skb->priority = sk->sk_priority;
|
||||
skb->mark = sk->sk_mark;
|
||||
|
||||
|
@ -1057,6 +1058,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
|
|||
/* initialize protocol header pointer */
|
||||
skb->transport_header = skb->network_header + fragheaderlen;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
skb->ip_summed = CHECKSUM_PARTIAL;
|
||||
skb->csum = 0;
|
||||
}
|
||||
|
@ -1359,6 +1361,7 @@ alloc_new_skb:
|
|||
/*
|
||||
* Fill in the control structures
|
||||
*/
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
skb->csum = 0;
|
||||
/* reserve for fragmentation and ipsec header */
|
||||
|
|
|
@ -1027,6 +1027,12 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
|
|||
init_tel_txopt(&opt, encap_limit);
|
||||
ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
|
||||
}
|
||||
|
||||
if (likely(!skb->encapsulation)) {
|
||||
skb_reset_inner_headers(skb);
|
||||
skb->encapsulation = 1;
|
||||
}
|
||||
|
||||
skb_push(skb, sizeof(struct ipv6hdr));
|
||||
skb_reset_network_header(skb);
|
||||
ipv6h = ipv6_hdr(skb);
|
||||
|
|
|
@ -628,6 +628,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
|
|||
goto error;
|
||||
skb_reserve(skb, hlen);
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
skb->priority = sk->sk_priority;
|
||||
skb->mark = sk->sk_mark;
|
||||
skb_dst_set(skb, &rt->dst);
|
||||
|
|
|
@ -888,6 +888,11 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
|
|||
ttl = iph6->hop_limit;
|
||||
tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
|
||||
|
||||
if (likely(!skb->encapsulation)) {
|
||||
skb_reset_inner_headers(skb);
|
||||
skb->encapsulation = 1;
|
||||
}
|
||||
|
||||
err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, fl4.daddr,
|
||||
IPPROTO_IPV6, tos, ttl, df);
|
||||
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
|
||||
|
|
|
@ -34,8 +34,10 @@ static int xfrm6_local_dontfrag(struct sk_buff *skb)
|
|||
struct sock *sk = skb->sk;
|
||||
|
||||
if (sk) {
|
||||
proto = sk->sk_protocol;
|
||||
if (sk->sk_family != AF_INET6)
|
||||
return 0;
|
||||
|
||||
proto = sk->sk_protocol;
|
||||
if (proto == IPPROTO_UDP || proto == IPPROTO_RAW)
|
||||
return inet6_sk(sk)->dontfrag;
|
||||
}
|
||||
|
@ -54,13 +56,15 @@ static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
|
|||
ipv6_local_rxpmtu(sk, &fl6, mtu);
|
||||
}
|
||||
|
||||
static void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
|
||||
void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
|
||||
{
|
||||
struct flowi6 fl6;
|
||||
const struct ipv6hdr *hdr;
|
||||
struct sock *sk = skb->sk;
|
||||
|
||||
hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
|
||||
fl6.fl6_dport = inet_sk(sk)->inet_dport;
|
||||
fl6.daddr = ipv6_hdr(skb)->daddr;
|
||||
fl6.daddr = hdr->daddr;
|
||||
|
||||
ipv6_local_error(sk, EMSGSIZE, &fl6, mtu);
|
||||
}
|
||||
|
@ -80,7 +84,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
|
|||
if (xfrm6_local_dontfrag(skb))
|
||||
xfrm6_local_rxpmtu(skb, mtu);
|
||||
else if (skb->sk)
|
||||
xfrm6_local_error(skb, mtu);
|
||||
xfrm_local_error(skb, mtu);
|
||||
else
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
|
||||
ret = -EMSGSIZE;
|
||||
|
@ -136,13 +140,18 @@ static int __xfrm6_output(struct sk_buff *skb)
|
|||
{
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct xfrm_state *x = dst->xfrm;
|
||||
int mtu = ip6_skb_dst_mtu(skb);
|
||||
int mtu;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IPV6))
|
||||
mtu = ip6_skb_dst_mtu(skb);
|
||||
else
|
||||
mtu = dst_mtu(skb_dst(skb));
|
||||
|
||||
if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
|
||||
xfrm6_local_rxpmtu(skb, mtu);
|
||||
return -EMSGSIZE;
|
||||
} else if (!skb->local_df && skb->len > mtu && skb->sk) {
|
||||
xfrm6_local_error(skb, mtu);
|
||||
xfrm_local_error(skb, mtu);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
|
|
|
@ -183,6 +183,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = {
|
|||
.extract_input = xfrm6_extract_input,
|
||||
.extract_output = xfrm6_extract_output,
|
||||
.transport_finish = xfrm6_transport_finish,
|
||||
.local_error = xfrm6_local_error,
|
||||
};
|
||||
|
||||
int __init xfrm6_state_init(void)
|
||||
|
|
|
@ -214,5 +214,26 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
|
|||
return inner_mode->afinfo->extract_output(x, skb);
|
||||
}
|
||||
|
||||
void xfrm_local_error(struct sk_buff *skb, int mtu)
|
||||
{
|
||||
unsigned int proto;
|
||||
struct xfrm_state_afinfo *afinfo;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
proto = AF_INET;
|
||||
else if (skb->protocol == htons(ETH_P_IPV6))
|
||||
proto = AF_INET6;
|
||||
else
|
||||
return;
|
||||
|
||||
afinfo = xfrm_state_get_afinfo(proto);
|
||||
if (!afinfo)
|
||||
return;
|
||||
|
||||
afinfo->local_error(skb, mtu);
|
||||
xfrm_state_put_afinfo(afinfo);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(xfrm_output);
|
||||
EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
|
||||
EXPORT_SYMBOL_GPL(xfrm_local_error);
|
||||
|
|
|
@ -320,10 +320,8 @@ static void xfrm_queue_purge(struct sk_buff_head *list)
|
|||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(list)) != NULL) {
|
||||
dev_put(skb->dev);
|
||||
while ((skb = skb_dequeue(list)) != NULL)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rule must be locked. Release descentant resources, announce
|
||||
|
@ -1758,7 +1756,6 @@ static void xfrm_policy_queue_process(unsigned long arg)
|
|||
struct sk_buff *skb;
|
||||
struct sock *sk;
|
||||
struct dst_entry *dst;
|
||||
struct net_device *dev;
|
||||
struct xfrm_policy *pol = (struct xfrm_policy *)arg;
|
||||
struct xfrm_policy_queue *pq = &pol->polq;
|
||||
struct flowi fl;
|
||||
|
@ -1805,7 +1802,6 @@ static void xfrm_policy_queue_process(unsigned long arg)
|
|||
dst = xfrm_lookup(xp_net(pol), skb_dst(skb)->path,
|
||||
&fl, skb->sk, 0);
|
||||
if (IS_ERR(dst)) {
|
||||
dev_put(skb->dev);
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
@ -1814,9 +1810,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
|
|||
skb_dst_drop(skb);
|
||||
skb_dst_set(skb, dst);
|
||||
|
||||
dev = skb->dev;
|
||||
err = dst_output(skb);
|
||||
dev_put(dev);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1839,7 +1833,6 @@ static int xdst_queue_output(struct sk_buff *skb)
|
|||
}
|
||||
|
||||
skb_dst_force(skb);
|
||||
dev_hold(skb->dev);
|
||||
|
||||
spin_lock_bh(&pq->hold_queue.lock);
|
||||
|
||||
|
|
|
@ -39,9 +39,6 @@ static DEFINE_SPINLOCK(xfrm_state_lock);
|
|||
|
||||
static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
|
||||
|
||||
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
|
||||
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
|
||||
|
||||
static inline unsigned int xfrm_dst_hash(struct net *net,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
|
@ -1860,7 +1857,7 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
|
|||
}
|
||||
EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
|
||||
|
||||
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
|
||||
struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
|
||||
{
|
||||
struct xfrm_state_afinfo *afinfo;
|
||||
if (unlikely(family >= NPROTO))
|
||||
|
@ -1872,7 +1869,7 @@ static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
|
|||
return afinfo;
|
||||
}
|
||||
|
||||
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
|
||||
void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue