aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>2016-07-13 14:08:57 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-13 21:10:14 -0400
commit1f45f78f8e511203f03138f2ccde3d2cf90d2cbf (patch)
treeb02dd1fde1a8e9e50adb8842d00c086930e02867
parentf5d258e60722142e88cb6f0f337d78bca67cf973 (diff)
sctp: allow GSO frags to access the chunk too
SCTP will try to access original IP headers on sctp_recvmsg in order to copy the addresses used. There are also other places that do similar access to IP or even SCTP headers. But after 90017accff61 ("sctp: Add GSO support") they aren't always there because they are only present in the header skb. SCTP handles the queueing of incoming data by cloning the incoming skb and limiting to only the relevant payload. This clone has its cb updated to something different and it's then queued on socket rx queue. Thus we need to fix this in two moments. For rx path, not related to socket queue yet, this patch uses a partially copied sctp_input_cb to such GSO frags. This restores the ability to access the headers for this part of the code. Regarding the socket rx queue, it removes iif member from sctp_event and also add a chunk pointer on it. With these changes we're always able to reach the headers again. The biggest change here is that now the sctp_chunk struct and the original skb are only freed after the application consumed the buffer. Note however that the original payload was already like this due to the skb cloning. For iif, SCTP's IPv4 code doesn't use it, so no change is necessary. IPv6 now can fetch it directly from original's IPv6 CB as the original skb is still accessible. In the future we probably can simplify sctp_v*_skb_iif() stuff, as sctp_v4_skb_iif() was called but it's return value not used, and now it's not even called, but such cleanup is out of scope for this change. Fixes: 90017accff61 ("sctp: Add GSO support") Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/structs.h7
-rw-r--r--include/net/sctp/ulpevent.h2
-rw-r--r--net/sctp/inqueue.c7
-rw-r--r--net/sctp/ipv6.c9
-rw-r--r--net/sctp/protocol.c1
-rw-r--r--net/sctp/sm_statefuns.c3
-rw-r--r--net/sctp/socket.c10
-rw-r--r--net/sctp/ulpevent.c10
8 files changed, 38 insertions, 11 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 966c3a40039c..f6f201de6fa4 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1107,6 +1107,13 @@ struct sctp_input_cb {
1107}; 1107};
1108#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0])) 1108#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0]))
1109 1109
1110static inline const struct sk_buff *sctp_gso_headskb(const struct sk_buff *skb)
1111{
1112 const struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
1113
1114 return chunk->head_skb ? : skb;
1115}
1116
1110/* These bind address data fields common between endpoints and associations */ 1117/* These bind address data fields common between endpoints and associations */
1111struct sctp_bind_addr { 1118struct sctp_bind_addr {
1112 1119
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index aa342645dbce..2c098cd7e7e2 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -48,11 +48,11 @@
48 */ 48 */
49struct sctp_ulpevent { 49struct sctp_ulpevent {
50 struct sctp_association *asoc; 50 struct sctp_association *asoc;
51 struct sctp_chunk *chunk;
51 unsigned int rmem_len; 52 unsigned int rmem_len;
52 __u32 ppid; 53 __u32 ppid;
53 __u32 tsn; 54 __u32 tsn;
54 __u32 cumtsn; 55 __u32 cumtsn;
55 int iif;
56 __u16 stream; 56 __u16 stream;
57 __u16 ssn; 57 __u16 ssn;
58 __u16 flags; 58 __u16 flags;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index edabbbdfca54..147d975b0455 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -218,6 +218,13 @@ new_skb:
218 chunk->has_asconf = 0; 218 chunk->has_asconf = 0;
219 chunk->end_of_packet = 0; 219 chunk->end_of_packet = 0;
220 chunk->ecn_ce_done = 0; 220 chunk->ecn_ce_done = 0;
221 if (chunk->head_skb) {
222 struct sctp_input_cb
223 *cb = SCTP_INPUT_CB(chunk->skb),
224 *head_cb = SCTP_INPUT_CB(chunk->head_skb);
225
226 cb->chunk = head_cb->chunk;
227 }
221 } 228 }
222 229
223 chunk->chunk_hdr = ch; 230 chunk->chunk_hdr = ch;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 0657d18a85bf..ae6f1a2178ba 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -420,6 +420,7 @@ static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb,
420 addr->v6.sin6_flowinfo = 0; /* FIXME */ 420 addr->v6.sin6_flowinfo = 0; /* FIXME */
421 addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif; 421 addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif;
422 422
423 /* Always called on head skb, so this is safe */
423 sh = sctp_hdr(skb); 424 sh = sctp_hdr(skb);
424 if (is_saddr) { 425 if (is_saddr) {
425 *port = sh->source; 426 *port = sh->source;
@@ -710,8 +711,7 @@ static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr)
710/* Where did this skb come from? */ 711/* Where did this skb come from? */
711static int sctp_v6_skb_iif(const struct sk_buff *skb) 712static int sctp_v6_skb_iif(const struct sk_buff *skb)
712{ 713{
713 struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; 714 return IP6CB(skb)->iif;
714 return opt->iif;
715} 715}
716 716
717/* Was this packet marked by Explicit Congestion Notification? */ 717/* Was this packet marked by Explicit Congestion Notification? */
@@ -780,15 +780,14 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
780 if (ip_hdr(skb)->version == 4) { 780 if (ip_hdr(skb)->version == 4) {
781 addr->v4.sin_family = AF_INET; 781 addr->v4.sin_family = AF_INET;
782 addr->v4.sin_port = sh->source; 782 addr->v4.sin_port = sh->source;
783 addr->v4.sin_addr.s_addr = ip_hdr(skb)->saddr; 783 addr->v4.sin_addr.s_addr = ip_hdr(skb)->saddr;
784 } else { 784 } else {
785 addr->v6.sin6_family = AF_INET6; 785 addr->v6.sin6_family = AF_INET6;
786 addr->v6.sin6_flowinfo = 0; 786 addr->v6.sin6_flowinfo = 0;
787 addr->v6.sin6_port = sh->source; 787 addr->v6.sin6_port = sh->source;
788 addr->v6.sin6_addr = ipv6_hdr(skb)->saddr; 788 addr->v6.sin6_addr = ipv6_hdr(skb)->saddr;
789 if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { 789 if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
790 struct sctp_ulpevent *ev = sctp_skb2event(skb); 790 addr->v6.sin6_scope_id = sctp_v6_skb_iif(skb);
791 addr->v6.sin6_scope_id = ev->iif;
792 } 791 }
793 } 792 }
794 793
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 3b56ae55aba3..1adb9270e317 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -240,6 +240,7 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
240 port = &addr->v4.sin_port; 240 port = &addr->v4.sin_port;
241 addr->v4.sin_family = AF_INET; 241 addr->v4.sin_family = AF_INET;
242 242
243 /* Always called on head skb, so this is safe */
243 sh = sctp_hdr(skb); 244 sh = sctp_hdr(skb);
244 if (is_saddr) { 245 if (is_saddr) {
245 *port = sh->source; 246 *port = sh->source;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index f1f08c8f277b..5aabf42065e2 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -6125,7 +6125,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
6125 af = sctp_get_af_specific( 6125 af = sctp_get_af_specific(
6126 ipver2af(ip_hdr(chunk->skb)->version)); 6126 ipver2af(ip_hdr(chunk->skb)->version));
6127 6127
6128 if (af && af->is_ce(chunk->skb) && asoc->peer.ecn_capable) { 6128 if (af && af->is_ce(sctp_gso_headskb(chunk->skb)) &&
6129 asoc->peer.ecn_capable) {
6129 /* Do real work as sideffect. */ 6130 /* Do real work as sideffect. */
6130 sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, 6131 sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
6131 SCTP_U32(tsn)); 6132 SCTP_U32(tsn));
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 71c7dc5ea62e..52fdd540a9ef 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2066,7 +2066,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
2066{ 2066{
2067 struct sctp_ulpevent *event = NULL; 2067 struct sctp_ulpevent *event = NULL;
2068 struct sctp_sock *sp = sctp_sk(sk); 2068 struct sctp_sock *sp = sctp_sk(sk);
2069 struct sk_buff *skb; 2069 struct sk_buff *skb, *head_skb;
2070 int copied; 2070 int copied;
2071 int err = 0; 2071 int err = 0;
2072 int skb_len; 2072 int skb_len;
@@ -2102,12 +2102,16 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
2102 if (err) 2102 if (err)
2103 goto out_free; 2103 goto out_free;
2104 2104
2105 sock_recv_ts_and_drops(msg, sk, skb); 2105 if (event->chunk && event->chunk->head_skb)
2106 head_skb = event->chunk->head_skb;
2107 else
2108 head_skb = skb;
2109 sock_recv_ts_and_drops(msg, sk, head_skb);
2106 if (sctp_ulpevent_is_notification(event)) { 2110 if (sctp_ulpevent_is_notification(event)) {
2107 msg->msg_flags |= MSG_NOTIFICATION; 2111 msg->msg_flags |= MSG_NOTIFICATION;
2108 sp->pf->event_msgname(event, msg->msg_name, addr_len); 2112 sp->pf->event_msgname(event, msg->msg_name, addr_len);
2109 } else { 2113 } else {
2110 sp->pf->skb_msgname(skb, msg->msg_name, addr_len); 2114 sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len);
2111 } 2115 }
2112 2116
2113 /* Check if we allow SCTP_NXTINFO. */ 2117 /* Check if we allow SCTP_NXTINFO. */
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 706f5bc9f0c3..f6219b164b42 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -701,6 +701,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
701 701
702 sctp_ulpevent_receive_data(event, asoc); 702 sctp_ulpevent_receive_data(event, asoc);
703 703
704 /* And hold the chunk as we need it for getting the IP headers
705 * later in recvmsg
706 */
707 sctp_chunk_hold(chunk);
708 event->chunk = chunk;
709
704 event->stream = ntohs(chunk->subh.data_hdr->stream); 710 event->stream = ntohs(chunk->subh.data_hdr->stream);
705 event->ssn = ntohs(chunk->subh.data_hdr->ssn); 711 event->ssn = ntohs(chunk->subh.data_hdr->ssn);
706 event->ppid = chunk->subh.data_hdr->ppid; 712 event->ppid = chunk->subh.data_hdr->ppid;
@@ -710,11 +716,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
710 } 716 }
711 event->tsn = ntohl(chunk->subh.data_hdr->tsn); 717 event->tsn = ntohl(chunk->subh.data_hdr->tsn);
712 event->msg_flags |= chunk->chunk_hdr->flags; 718 event->msg_flags |= chunk->chunk_hdr->flags;
713 event->iif = sctp_chunk_iif(chunk);
714 719
715 return event; 720 return event;
716 721
717fail_mark: 722fail_mark:
723 sctp_chunk_put(chunk);
718 kfree_skb(skb); 724 kfree_skb(skb);
719fail: 725fail:
720 return NULL; 726 return NULL;
@@ -1007,6 +1013,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
1007 1013
1008done: 1014done:
1009 sctp_assoc_rwnd_increase(event->asoc, len); 1015 sctp_assoc_rwnd_increase(event->asoc, len);
1016 sctp_chunk_put(event->chunk);
1010 sctp_ulpevent_release_owner(event); 1017 sctp_ulpevent_release_owner(event);
1011} 1018}
1012 1019
@@ -1029,6 +1036,7 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
1029 } 1036 }
1030 1037
1031done: 1038done:
1039 sctp_chunk_put(event->chunk);
1032 sctp_ulpevent_release_owner(event); 1040 sctp_ulpevent_release_owner(event);
1033} 1041}
1034 1042