Skip to content

Commit 1f45f78

Browse files
marceloleitnerdavem330
authored andcommitted
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 90017ac ("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: 90017ac ("sctp: Add GSO support") Signed-off-by: Marcelo Ricardo Leitner <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f5d258e commit 1f45f78

File tree

8 files changed

+38
-11
lines changed

8 files changed

+38
-11
lines changed

include/net/sctp/structs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,13 @@ struct sctp_input_cb {
11071107
};
11081108
#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0]))
11091109

1110+
static 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+
11101117
/* These bind address data fields common between endpoints and associations */
11111118
struct sctp_bind_addr {
11121119

include/net/sctp/ulpevent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@
4848
*/
4949
struct sctp_ulpevent {
5050
struct sctp_association *asoc;
51+
struct sctp_chunk *chunk;
5152
unsigned int rmem_len;
5253
__u32 ppid;
5354
__u32 tsn;
5455
__u32 cumtsn;
55-
int iif;
5656
__u16 stream;
5757
__u16 ssn;
5858
__u16 flags;

net/sctp/inqueue.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
218218
chunk->has_asconf = 0;
219219
chunk->end_of_packet = 0;
220220
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+
}
221228
}
222229

223230
chunk->chunk_hdr = ch;

net/sctp/ipv6.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb,
420420
addr->v6.sin6_flowinfo = 0; /* FIXME */
421421
addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif;
422422

423+
/* Always called on head skb, so this is safe */
423424
sh = sctp_hdr(skb);
424425
if (is_saddr) {
425426
*port = sh->source;
@@ -710,8 +711,7 @@ static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr)
710711
/* Where did this skb come from? */
711712
static int sctp_v6_skb_iif(const struct sk_buff *skb)
712713
{
713-
struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
714-
return opt->iif;
714+
return IP6CB(skb)->iif;
715715
}
716716

717717
/* Was this packet marked by Explicit Congestion Notification? */
@@ -780,15 +780,14 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
780780
if (ip_hdr(skb)->version == 4) {
781781
addr->v4.sin_family = AF_INET;
782782
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;
784784
} else {
785785
addr->v6.sin6_family = AF_INET6;
786786
addr->v6.sin6_flowinfo = 0;
787787
addr->v6.sin6_port = sh->source;
788788
addr->v6.sin6_addr = ipv6_hdr(skb)->saddr;
789789
if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
790-
struct sctp_ulpevent *ev = sctp_skb2event(skb);
791-
addr->v6.sin6_scope_id = ev->iif;
790+
addr->v6.sin6_scope_id = sctp_v6_skb_iif(skb);
792791
}
793792
}
794793

net/sctp/protocol.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
240240
port = &addr->v4.sin_port;
241241
addr->v4.sin_family = AF_INET;
242242

243+
/* Always called on head skb, so this is safe */
243244
sh = sctp_hdr(skb);
244245
if (is_saddr) {
245246
*port = sh->source;

net/sctp/sm_statefuns.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6125,7 +6125,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
61256125
af = sctp_get_af_specific(
61266126
ipver2af(ip_hdr(chunk->skb)->version));
61276127

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) {
61296130
/* Do real work as sideffect. */
61306131
sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
61316132
SCTP_U32(tsn));

net/sctp/socket.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,7 +2066,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
20662066
{
20672067
struct sctp_ulpevent *event = NULL;
20682068
struct sctp_sock *sp = sctp_sk(sk);
2069-
struct sk_buff *skb;
2069+
struct sk_buff *skb, *head_skb;
20702070
int copied;
20712071
int err = 0;
20722072
int skb_len;
@@ -2102,12 +2102,16 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
21022102
if (err)
21032103
goto out_free;
21042104

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);
21062110
if (sctp_ulpevent_is_notification(event)) {
21072111
msg->msg_flags |= MSG_NOTIFICATION;
21082112
sp->pf->event_msgname(event, msg->msg_name, addr_len);
21092113
} else {
2110-
sp->pf->skb_msgname(skb, msg->msg_name, addr_len);
2114+
sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len);
21112115
}
21122116

21132117
/* Check if we allow SCTP_NXTINFO. */

net/sctp/ulpevent.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
701701

702702
sctp_ulpevent_receive_data(event, asoc);
703703

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+
704710
event->stream = ntohs(chunk->subh.data_hdr->stream);
705711
event->ssn = ntohs(chunk->subh.data_hdr->ssn);
706712
event->ppid = chunk->subh.data_hdr->ppid;
@@ -710,11 +716,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
710716
}
711717
event->tsn = ntohl(chunk->subh.data_hdr->tsn);
712718
event->msg_flags |= chunk->chunk_hdr->flags;
713-
event->iif = sctp_chunk_iif(chunk);
714719

715720
return event;
716721

717722
fail_mark:
723+
sctp_chunk_put(chunk);
718724
kfree_skb(skb);
719725
fail:
720726
return NULL;
@@ -1007,6 +1013,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
10071013

10081014
done:
10091015
sctp_assoc_rwnd_increase(event->asoc, len);
1016+
sctp_chunk_put(event->chunk);
10101017
sctp_ulpevent_release_owner(event);
10111018
}
10121019

@@ -1029,6 +1036,7 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
10291036
}
10301037

10311038
done:
1039+
sctp_chunk_put(event->chunk);
10321040
sctp_ulpevent_release_owner(event);
10331041
}
10341042

0 commit comments

Comments
 (0)