aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/ip_output.c4
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_sctp.c2
-rw-r--r--net/ipv4/tcp.c17
-rw-r--r--net/ipv4/tcp_ipv4.c2
-rw-r--r--net/ipv4/tcp_output.c9
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/inet6_connection_sock.c2
-rw-r--r--net/ipv6/ip6_input.c7
-rw-r--r--net/ipv6/ip6_output.c4
-rw-r--r--net/ipv6/ipv6_sockglue.c62
-rw-r--r--net/ipv6/tcp_ipv6.c7
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c2
13 files changed, 91 insertions, 33 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 0c872fd0849..ca0e714613f 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -743,7 +743,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
if (!err) {
/* specify the length of each IP datagram fragment*/
skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
- skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+ skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
__skb_queue_tail(&sk->sk_write_queue, skb);
return 0;
@@ -1088,7 +1088,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page,
if ((sk->sk_protocol == IPPROTO_UDP) &&
(rt->u.dst.dev->features & NETIF_F_UFO)) {
skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
- skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+ skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
}
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c
index 0416073c560..2d3612cd5f1 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c
@@ -254,7 +254,7 @@ static int do_basic_checks(struct ip_conntrack *conntrack,
}
DEBUGP("Basic checks passed\n");
- return 0;
+ return count == 0;
}
static int new_state(enum ip_conntrack_dir dir,
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index a97450e9eb3..804458712d8 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -642,7 +642,7 @@ static inline int select_size(struct sock *sk, struct tcp_sock *tp)
int tmp = tp->mss_cache;
if (sk->sk_route_caps & NETIF_F_SG) {
- if (sk->sk_route_caps & NETIF_F_TSO)
+ if (sk_can_gso(sk))
tmp = 0;
else {
int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER);
@@ -2165,13 +2165,19 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
if (!pskb_may_pull(skb, thlen))
goto out;
- segs = NULL;
- if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST))
- goto out;
-
oldlen = (u16)~skb->len;
__skb_pull(skb, thlen);
+ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+ /* Packet is from an untrusted source, reset gso_segs. */
+ int mss = skb_shinfo(skb)->gso_size;
+
+ skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss;
+
+ segs = NULL;
+ goto out;
+ }
+
segs = skb_segment(skb, features);
if (IS_ERR(segs))
goto out;
@@ -2208,6 +2214,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
out:
return segs;
}
+EXPORT_SYMBOL(tcp_tso_segment);
extern void __skb_cb_too_small_for_tcp(int, int);
extern struct tcp_congestion_ops tcp_reno;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index e02a84cf9a5..8355b729fa9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -241,6 +241,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
goto failure;
/* OK, now commit destination to socket. */
+ sk->sk_gso_type = SKB_GSO_TCPV4;
sk_setup_caps(sk, &rt->u.dst);
if (!tp->write_seq)
@@ -883,6 +884,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
if (!newsk)
goto exit;
+ newsk->sk_gso_type = SKB_GSO_TCPV4;
sk_setup_caps(newsk, dst);
newtp = tcp_sk(newsk);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5a7cb4a9c86..5c08ea20a18 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -510,8 +510,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
{
- if (skb->len <= mss_now ||
- !(sk->sk_route_caps & NETIF_F_TSO)) {
+ if (skb->len <= mss_now || !sk_can_gso(sk)) {
/* Avoid the costly divide in the normal
* non-TSO case.
*/
@@ -525,7 +524,7 @@ static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned
factor /= mss_now;
skb_shinfo(skb)->gso_segs = factor;
skb_shinfo(skb)->gso_size = mss_now;
- skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+ skb_shinfo(skb)->gso_type = sk->sk_gso_type;
}
}
@@ -824,9 +823,7 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
mss_now = tp->mss_cache;
- if (large_allowed &&
- (sk->sk_route_caps & NETIF_F_TSO) &&
- !tp->urg_mode)
+ if (large_allowed && sk_can_gso(sk) && !tp->urg_mode)
doing_tso = 1;
if (dst) {
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index a94c91b407e..5a0ba58b86c 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -659,8 +659,6 @@ int inet6_sk_rebuild_header(struct sock *sk)
}
ip6_dst_store(sk, dst, NULL);
- sk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
}
return 0;
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index a18d4256372..9d0ee7f0eeb 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -179,7 +179,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp)
static struct inet6_protocol destopt_protocol = {
.handler = ipv6_destopt_rcv,
- .flags = INET6_PROTO_NOPOLICY,
+ .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
};
void __init ipv6_destopt_init(void)
@@ -340,7 +340,7 @@ looped_back:
static struct inet6_protocol rthdr_protocol = {
.handler = ipv6_rthdr_rcv,
- .flags = INET6_PROTO_NOPOLICY,
+ .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
};
void __init ipv6_rthdr_init(void)
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 11536db225d..5c950cc79d8 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -186,8 +186,6 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
}
ip6_dst_store(sk, dst, NULL);
- sk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
}
skb->dst = dst_clone(dst);
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index aceee252503..df8f051c0fc 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -84,14 +84,9 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
*/
IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex;
- if (skb->len < sizeof(struct ipv6hdr))
+ if (unlikely(!pskb_may_pull(skb, sizeof(*hdr))))
goto err;
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) {
- IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
- goto drop;
- }
-
hdr = skb->nh.ipv6h;
if (hdr->version != 6)
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index aa2b2c3e507..2c5b44575af 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -229,7 +229,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
skb->priority = sk->sk_priority;
mtu = dst_mtu(dst);
- if ((skb->len <= mtu) || ipfragok) {
+ if ((skb->len <= mtu) || ipfragok || skb_shinfo(skb)->gso_size) {
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev,
dst_output);
@@ -834,7 +834,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
/* specify the length of each IP datagram fragment*/
skb_shinfo(skb)->gso_size = mtu - fragheaderlen -
sizeof(struct frag_hdr);
- skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4;
+ skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
ipv6_select_ident(skb, &fhdr);
skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
__skb_queue_tail(&sk->sk_write_queue, skb);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 97199d6ec79..c28e5c28744 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -57,9 +57,71 @@
DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;
+static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ struct ipv6hdr *ipv6h;
+ struct inet6_protocol *ops;
+ int proto;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+ goto out;
+
+ ipv6h = skb->nh.ipv6h;
+ proto = ipv6h->nexthdr;
+ __skb_pull(skb, sizeof(*ipv6h));
+
+ rcu_read_lock();
+ for (;;) {
+ struct ipv6_opt_hdr *opth;
+ int len;
+
+ if (proto != NEXTHDR_HOP) {
+ ops = rcu_dereference(inet6_protos[proto]);
+
+ if (unlikely(!ops))
+ goto unlock;
+
+ if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
+ break;
+ }
+
+ if (unlikely(!pskb_may_pull(skb, 8)))
+ goto unlock;
+
+ opth = (void *)skb->data;
+ len = opth->hdrlen * 8 + 8;
+
+ if (unlikely(!pskb_may_pull(skb, len)))
+ goto unlock;
+
+ proto = opth->nexthdr;
+ __skb_pull(skb, len);
+ }
+
+ skb->h.raw = skb->data;
+ if (likely(ops->gso_segment))
+ segs = ops->gso_segment(skb, features);
+
+unlock:
+ rcu_read_unlock();
+
+ if (unlikely(IS_ERR(segs)))
+ goto out;
+
+ for (skb = segs; skb; skb = skb->next) {
+ ipv6h = skb->nh.ipv6h;
+ ipv6h->payload_len = htons(skb->len - skb->mac_len);
+ }
+
+out:
+ return segs;
+}
+
static struct packet_type ipv6_packet_type = {
.type = __constant_htons(ETH_P_IPV6),
.func = ipv6_rcv,
+ .gso_segment = ipv6_gso_segment,
};
struct ip6_ra_chain *ip6_ra_chain;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 81dbc9c3bf0..5bdcb9002cf 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -269,9 +269,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
ipv6_addr_copy(&np->saddr, saddr);
inet->rcv_saddr = LOOPBACK4_IPV6;
+ sk->sk_gso_type = SKB_GSO_TCPV6;
ip6_dst_store(sk, dst, NULL);
- sk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
icsk->icsk_ext_hdr_len = 0;
if (np->opt)
@@ -929,9 +928,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* comment in that function for the gory details. -acme
*/
+ sk->sk_gso_type = SKB_GSO_TCPV6;
ip6_dst_store(newsk, dst, NULL);
- newsk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
newtcp6sk = (struct tcp6_sock *)newsk;
inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
@@ -1605,6 +1603,7 @@ struct proto tcpv6_prot = {
static struct inet6_protocol tcpv6_protocol = {
.handler = tcp_v6_rcv,
.err_handler = tcp_v6_err,
+ .gso_segment = tcp_tso_segment,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 0839b701b93..9bd8a7877fd 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -261,7 +261,7 @@ static int do_basic_checks(struct nf_conn *conntrack,
}
DEBUGP("Basic checks passed\n");
- return 0;
+ return count == 0;
}
static int new_state(enum ip_conntrack_dir dir,