Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Marcelo Ricardo Leitner and committed by
David S. Miller
1f45f78f f5d258e6

+38 -11
+7
include/net/sctp/structs.h
··· 1107 1107 }; 1108 1108 #define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0])) 1109 1109 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 + 1110 1117 /* These bind address data fields common between endpoints and associations */ 1111 1118 struct sctp_bind_addr { 1112 1119
+1 -1
include/net/sctp/ulpevent.h
··· 48 48 */ 49 49 struct sctp_ulpevent { 50 50 struct sctp_association *asoc; 51 + struct sctp_chunk *chunk; 51 52 unsigned int rmem_len; 52 53 __u32 ppid; 53 54 __u32 tsn; 54 55 __u32 cumtsn; 55 - int iif; 56 56 __u16 stream; 57 57 __u16 ssn; 58 58 __u16 flags;
+7
net/sctp/inqueue.c
··· 218 218 chunk->has_asconf = 0; 219 219 chunk->end_of_packet = 0; 220 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 230 chunk->chunk_hdr = ch;
+4 -5
net/sctp/ipv6.c
··· 420 420 addr->v6.sin6_flowinfo = 0; /* FIXME */ 421 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 424 sh = sctp_hdr(skb); 424 425 if (is_saddr) { 425 426 *port = sh->source; ··· 711 710 /* Where did this skb come from? */ 712 711 static int sctp_v6_skb_iif(const struct sk_buff *skb) 713 712 { 714 - struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; 715 - return opt->iif; 713 + return IP6CB(skb)->iif; 716 714 } 717 715 718 716 /* Was this packet marked by Explicit Congestion Notification? */ ··· 780 780 if (ip_hdr(skb)->version == 4) { 781 781 addr->v4.sin_family = AF_INET; 782 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 784 } else { 785 785 addr->v6.sin6_family = AF_INET6; 786 786 addr->v6.sin6_flowinfo = 0; 787 787 addr->v6.sin6_port = sh->source; 788 788 addr->v6.sin6_addr = ipv6_hdr(skb)->saddr; 789 789 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); 792 791 } 793 792 } 794 793
+1
net/sctp/protocol.c
··· 240 240 port = &addr->v4.sin_port; 241 241 addr->v4.sin_family = AF_INET; 242 242 243 + /* Always called on head skb, so this is safe */ 243 244 sh = sctp_hdr(skb); 244 245 if (is_saddr) { 245 246 *port = sh->source;
+2 -1
net/sctp/sm_statefuns.c
··· 6125 6125 af = sctp_get_af_specific( 6126 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 6130 /* Do real work as sideffect. */ 6130 6131 sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, 6131 6132 SCTP_U32(tsn));
+7 -3
net/sctp/socket.c
··· 2066 2066 { 2067 2067 struct sctp_ulpevent *event = NULL; 2068 2068 struct sctp_sock *sp = sctp_sk(sk); 2069 - struct sk_buff *skb; 2069 + struct sk_buff *skb, *head_skb; 2070 2070 int copied; 2071 2071 int err = 0; 2072 2072 int skb_len; ··· 2102 2102 if (err) 2103 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 2110 if (sctp_ulpevent_is_notification(event)) { 2107 2111 msg->msg_flags |= MSG_NOTIFICATION; 2108 2112 sp->pf->event_msgname(event, msg->msg_name, addr_len); 2109 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 2117 /* Check if we allow SCTP_NXTINFO. */
+9 -1
net/sctp/ulpevent.c
··· 701 701 702 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 710 event->stream = ntohs(chunk->subh.data_hdr->stream); 705 711 event->ssn = ntohs(chunk->subh.data_hdr->ssn); 706 712 event->ppid = chunk->subh.data_hdr->ppid; ··· 716 710 } 717 711 event->tsn = ntohl(chunk->subh.data_hdr->tsn); 718 712 event->msg_flags |= chunk->chunk_hdr->flags; 719 - event->iif = sctp_chunk_iif(chunk); 720 713 721 714 return event; 722 715 723 716 fail_mark: 717 + sctp_chunk_put(chunk); 724 718 kfree_skb(skb); 725 719 fail: 726 720 return NULL; ··· 1013 1007 1014 1008 done: 1015 1009 sctp_assoc_rwnd_increase(event->asoc, len); 1010 + sctp_chunk_put(event->chunk); 1016 1011 sctp_ulpevent_release_owner(event); 1017 1012 } 1018 1013 ··· 1036 1029 } 1037 1030 1038 1031 done: 1032 + sctp_chunk_put(event->chunk); 1039 1033 sctp_ulpevent_release_owner(event); 1040 1034 } 1041 1035