mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 13:05:10 +07:00
ef2820a735
Implementation of (a)rwnd calculation might lead to severe performance issues and associations completely stalling. These problems are described and solution is proposed which improves lksctp's robustness in congestion state. 1) Sudden drop of a_rwnd and incomplete window recovery afterwards Data accounted in sctp_assoc_rwnd_decrease takes only payload size (sctp data), but size of sk_buff, which is blamed against receiver buffer, is not accounted in rwnd. Theoretically, this should not be the problem as actual size of buffer is double the amount requested on the socket (SO_RECVBUF). Problem here is that this will have bad scaling for data which is less then sizeof sk_buff. E.g. in 4G (LTE) networks, link interfacing radio side will have a large portion of traffic of this size (less then 100B). An example of sudden drop and incomplete window recovery is given below. Node B exhibits problematic behavior. Node A initiates association and B is configured to advertise rwnd of 10000. A sends messages of size 43B (size of typical sctp message in 4G (LTE) network). On B data is left in buffer by not reading socket in userspace. Lets examine when we will hit pressure state and declare rwnd to be 0 for scenario with above stated parameters (rwnd == 10000, chunk size == 43, each chunk is sent in separate sctp packet) Logic is implemented in sctp_assoc_rwnd_decrease: socket_buffer (see below) is maximum size which can be held in socket buffer (sk_rcvbuf). current_alloced is amount of data currently allocated (rx_count) A simple expression is given for which it will be examined after how many packets for above stated parameters we enter pressure state: We start by condition which has to be met in order to enter pressure state: socket_buffer < currently_alloced; currently_alloced is represented as size of sctp packets received so far and not yet delivered to userspace. x is the number of chunks/packets (since there is no bundling, and each chunk is delivered in separate packet, we can observe each chunk also as sctp packet, and what is important here, having its own sk_buff): socket_buffer < x*each_sctp_packet; each_sctp_packet is sctp chunk size + sizeof(struct sk_buff). socket_buffer is twice the amount of initially requested size of socket buffer, which is in case of sctp, twice the a_rwnd requested: 2*rwnd < x*(payload+sizeof(struc sk_buff)); sizeof(struct sk_buff) is 190 (3.13.0-rc4+). Above is stated that rwnd is 10000 and each payload size is 43 20000 < x(43+190); x > 20000/233; x ~> 84; After ~84 messages, pressure state is entered and 0 rwnd is advertised while received 84*43B ~= 3612B sctp data. This is why external observer notices sudden drop from 6474 to 0, as it will be now shown in example: IP A.34340 > B.12345: sctp (1) [INIT] [init tag: 1875509148] [rwnd: 81920] [OS: 10] [MIS: 65535] [init TSN: 1096057017] IP B.12345 > A.34340: sctp (1) [INIT ACK] [init tag: 3198966556] [rwnd: 10000] [OS: 10] [MIS: 10] [init TSN: 902132839] IP A.34340 > B.12345: sctp (1) [COOKIE ECHO] IP B.12345 > A.34340: sctp (1) [COOKIE ACK] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057017] [SID: 0] [SSEQ 0] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057017] [a_rwnd 9957] [#gap acks 0] [#dup tsns 0] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057018] [SID: 0] [SSEQ 1] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057018] [a_rwnd 9957] [#gap acks 0] [#dup tsns 0] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057019] [SID: 0] [SSEQ 2] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057019] [a_rwnd 9914] [#gap acks 0] [#dup tsns 0] <...> IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057098] [SID: 0] [SSEQ 81] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057098] [a_rwnd 6517] [#gap acks 0] [#dup tsns 0] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057099] [SID: 0] [SSEQ 82] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057099] [a_rwnd 6474] [#gap acks 0] [#dup tsns 0] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057100] [SID: 0] [SSEQ 83] [PPID 0x18] --> Sudden drop IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057100] [a_rwnd 0] [#gap acks 0] [#dup tsns 0] At this point, rwnd_press stores current rwnd value so it can be later restored in sctp_assoc_rwnd_increase. This however doesn't happen as condition to start slowly increasing rwnd until rwnd_press is returned to rwnd is never met. This condition is not met since rwnd, after it hit 0, must first reach rwnd_press by adding amount which is read from userspace. Let us observe values in above example. Initial a_rwnd is 10000, pressure was hit when rwnd was ~6500 and the amount of actual sctp data currently waiting to be delivered to userspace is ~3500. When userspace starts to read, sctp_assoc_rwnd_increase will be blamed only for sctp data, which is ~3500. Condition is never met, and when userspace reads all data, rwnd stays on 3569. IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057100] [a_rwnd 1505] [#gap acks 0] [#dup tsns 0] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057100] [a_rwnd 3010] [#gap acks 0] [#dup tsns 0] IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057101] [SID: 0] [SSEQ 84] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057101] [a_rwnd 3569] [#gap acks 0] [#dup tsns 0] --> At this point userspace read everything, rwnd recovered only to 3569 IP A.34340 > B.12345: sctp (1) [DATA] (B)(E) [TSN: 1096057102] [SID: 0] [SSEQ 85] [PPID 0x18] IP B.12345 > A.34340: sctp (1) [SACK] [cum ack 1096057102] [a_rwnd 3569] [#gap acks 0] [#dup tsns 0] Reproduction is straight forward, it is enough for sender to send packets of size less then sizeof(struct sk_buff) and receiver keeping them in its buffers. 2) Minute size window for associations sharing the same socket buffer In case multiple associations share the same socket, and same socket buffer (sctp.rcvbuf_policy == 0), different scenarios exist in which congestion on one of the associations can permanently drop rwnd of other association(s). Situation will be typically observed as one association suddenly having rwnd dropped to size of last packet received and never recovering beyond that point. Different scenarios will lead to it, but all have in common that one of the associations (let it be association from 1)) nearly depleted socket buffer, and the other association blames socket buffer just for the amount enough to start the pressure. This association will enter pressure state, set rwnd_press and announce 0 rwnd. When data is read by userspace, similar situation as in 1) will occur, rwnd will increase just for the size read by userspace but rwnd_press will be high enough so that association doesn't have enough credit to reach rwnd_press and restore to previous state. This case is special case of 1), being worse as there is, in the worst case, only one packet in buffer for which size rwnd will be increased. Consequence is association which has very low maximum rwnd ('minute size', in our case down to 43B - size of packet which caused pressure) and as such unusable. Scenario happened in the field and labs frequently after congestion state (link breaks, different probabilities of packet drop, packet reordering) and with scenario 1) preceding. Here is given a deterministic scenario for reproduction: >From node A establish two associations on the same socket, with rcvbuf_policy being set to share one common buffer (sctp.rcvbuf_policy == 0). On association 1 repeat scenario from 1), that is, bring it down to 0 and restore up. Observe scenario 1). Use small payload size (here we use 43). Once rwnd is 'recovered', bring it down close to 0, as in just one more packet would close it. This has as a consequence that association number 2 is able to receive (at least) one more packet which will bring it in pressure state. E.g. if association 2 had rwnd of 10000, packet received was 43, and we enter at this point into pressure, rwnd_press will have 9957. Once payload is delivered to userspace, rwnd will increase for 43, but conditions to restore rwnd to original state, just as in 1), will never be satisfied. --> Association 1, between A.y and B.12345 IP A.55915 > B.12345: sctp (1) [INIT] [init tag: 836880897] [rwnd: 10000] [OS: 10] [MIS: 65535] [init TSN: 4032536569] IP B.12345 > A.55915: sctp (1) [INIT ACK] [init tag: 2873310749] [rwnd: 81920] [OS: 10] [MIS: 10] [init TSN: 3799315613] IP A.55915 > B.12345: sctp (1) [COOKIE ECHO] IP B.12345 > A.55915: sctp (1) [COOKIE ACK] --> Association 2, between A.z and B.12346 IP A.55915 > B.12346: sctp (1) [INIT] [init tag: 534798321] [rwnd: 10000] [OS: 10] [MIS: 65535] [init TSN: 2099285173] IP B.12346 > A.55915: sctp (1) [INIT ACK] [init tag: 516668823] [rwnd: 81920] [OS: 10] [MIS: 10] [init TSN: 3676403240] IP A.55915 > B.12346: sctp (1) [COOKIE ECHO] IP B.12346 > A.55915: sctp (1) [COOKIE ACK] --> Deplete socket buffer by sending messages of size 43B over association 1 IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315613] [SID: 0] [SSEQ 0] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315613] [a_rwnd 9957] [#gap acks 0] [#dup tsns 0] <...> IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315696] [a_rwnd 6388] [#gap acks 0] [#dup tsns 0] IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315697] [SID: 0] [SSEQ 84] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315697] [a_rwnd 6345] [#gap acks 0] [#dup tsns 0] --> Sudden drop on 1 IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315698] [SID: 0] [SSEQ 85] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315698] [a_rwnd 0] [#gap acks 0] [#dup tsns 0] --> Here userspace read, rwnd 'recovered' to 3698, now deplete again using association 1 so there is place in buffer for only one more packet IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315799] [SID: 0] [SSEQ 186] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315799] [a_rwnd 86] [#gap acks 0] [#dup tsns 0] IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315800] [SID: 0] [SSEQ 187] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315800] [a_rwnd 43] [#gap acks 0] [#dup tsns 0] --> Socket buffer is almost depleted, but there is space for one more packet, send them over association 2, size 43B IP B.12346 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3676403240] [SID: 0] [SSEQ 0] [PPID 0x18] IP A.55915 > B.12346: sctp (1) [SACK] [cum ack 3676403240] [a_rwnd 0] [#gap acks 0] [#dup tsns 0] --> Immediate drop IP A.60995 > B.12346: sctp (1) [SACK] [cum ack 387491510] [a_rwnd 0] [#gap acks 0] [#dup tsns 0] --> Read everything from the socket, both association recover up to maximum rwnd they are capable of reaching, note that association 1 recovered up to 3698, and association 2 recovered only to 43 IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315800] [a_rwnd 1548] [#gap acks 0] [#dup tsns 0] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315800] [a_rwnd 3053] [#gap acks 0] [#dup tsns 0] IP B.12345 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3799315801] [SID: 0] [SSEQ 188] [PPID 0x18] IP A.55915 > B.12345: sctp (1) [SACK] [cum ack 3799315801] [a_rwnd 3698] [#gap acks 0] [#dup tsns 0] IP B.12346 > A.55915: sctp (1) [DATA] (B)(E) [TSN: 3676403241] [SID: 0] [SSEQ 1] [PPID 0x18] IP A.55915 > B.12346: sctp (1) [SACK] [cum ack 3676403241] [a_rwnd 43] [#gap acks 0] [#dup tsns 0] A careful reader might wonder why it is necessary to reproduce 1) prior reproduction of 2). It is simply easier to observe when to send packet over association 2 which will push association into the pressure state. Proposed solution: Both problems share the same root cause, and that is improper scaling of socket buffer with rwnd. Solution in which sizeof(sk_buff) is taken into concern while calculating rwnd is not possible due to fact that there is no linear relationship between amount of data blamed in increase/decrease with IP packet in which payload arrived. Even in case such solution would be followed, complexity of the code would increase. Due to nature of current rwnd handling, slow increase (in sctp_assoc_rwnd_increase) of rwnd after pressure state is entered is rationale, but it gives false representation to the sender of current buffer space. Furthermore, it implements additional congestion control mechanism which is defined on implementation, and not on standard basis. Proposed solution simplifies whole algorithm having on mind definition from rfc: o Receiver Window (rwnd): This gives the sender an indication of the space available in the receiver's inbound buffer. Core of the proposed solution is given with these lines: sctp_assoc_rwnd_update: if ((asoc->base.sk->sk_rcvbuf - rx_count) > 0) asoc->rwnd = (asoc->base.sk->sk_rcvbuf - rx_count) >> 1; else asoc->rwnd = 0; We advertise to sender (half of) actual space we have. Half is in the braces depending whether you would like to observe size of socket buffer as SO_RECVBUF or twice the amount, i.e. size is the one visible from userspace, that is, from kernelspace. In this way sender is given with good approximation of our buffer space, regardless of the buffer policy - we always advertise what we have. Proposed solution fixes described problems and removes necessity for rwnd restoration algorithm. Finally, as proposed solution is simplification, some lines of code, along with some bytes in struct sctp_association are saved. Version 2 of the patch addressed comments from Vlad. Name of the function is set to be more descriptive, and two parts of code are changed, in one removing the superfluous call to sctp_assoc_rwnd_update since call would not result in update of rwnd, and the other being reordering of the code in a way that call to sctp_assoc_rwnd_update updates rwnd. Version 3 corrected change introduced in v2 in a way that existing function is not reordered/copied in line, but it is correctly called. Thanks Vlad for suggesting. Signed-off-by: Matija Glavinic Pecotic <matija.glavinic-pecotic.ext@nsn.com> Reviewed-by: Alexander Sverdlin <alexander.sverdlin@nsn.com> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1099 lines
30 KiB
C
1099 lines
30 KiB
C
/* SCTP kernel implementation
|
|
* (C) Copyright IBM Corp. 2001, 2004
|
|
* Copyright (c) 1999-2000 Cisco, Inc.
|
|
* Copyright (c) 1999-2001 Motorola, Inc.
|
|
* Copyright (c) 2001 Intel Corp.
|
|
* Copyright (c) 2001 Nokia, Inc.
|
|
* Copyright (c) 2001 La Monte H.P. Yarroll
|
|
*
|
|
* These functions manipulate an sctp event. The struct ulpevent is used
|
|
* to carry notifications and data to the ULP (sockets).
|
|
*
|
|
* This SCTP implementation is free software;
|
|
* you can redistribute it and/or modify it under the terms of
|
|
* the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This SCTP implementation is distributed in the hope that it
|
|
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
* ************************
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GNU CC; see the file COPYING. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Please send any bug reports or fixes you make to the
|
|
* email address(es):
|
|
* lksctp developers <linux-sctp@vger.kernel.org>
|
|
*
|
|
* Written or modified by:
|
|
* Jon Grimm <jgrimm@us.ibm.com>
|
|
* La Monte H.P. Yarroll <piggy@acm.org>
|
|
* Ardelle Fan <ardelle.fan@intel.com>
|
|
* Sridhar Samudrala <sri@us.ibm.com>
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/sctp/structs.h>
|
|
#include <net/sctp/sctp.h>
|
|
#include <net/sctp/sm.h>
|
|
|
|
static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
|
|
struct sctp_association *asoc);
|
|
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event);
|
|
static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
|
|
|
|
|
|
/* Initialize an ULP event from an given skb. */
|
|
static void sctp_ulpevent_init(struct sctp_ulpevent *event,
|
|
int msg_flags,
|
|
unsigned int len)
|
|
{
|
|
memset(event, 0, sizeof(struct sctp_ulpevent));
|
|
event->msg_flags = msg_flags;
|
|
event->rmem_len = len;
|
|
}
|
|
|
|
/* Create a new sctp_ulpevent. */
|
|
static struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
|
|
gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sk_buff *skb;
|
|
|
|
skb = alloc_skb(size, gfp);
|
|
if (!skb)
|
|
goto fail;
|
|
|
|
event = sctp_skb2event(skb);
|
|
sctp_ulpevent_init(event, msg_flags, skb->truesize);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Is this a MSG_NOTIFICATION? */
|
|
int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event)
|
|
{
|
|
return MSG_NOTIFICATION == (event->msg_flags & MSG_NOTIFICATION);
|
|
}
|
|
|
|
/* Hold the association in case the msg_name needs read out of
|
|
* the association.
|
|
*/
|
|
static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
|
|
const struct sctp_association *asoc)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
/* Cast away the const, as we are just wanting to
|
|
* bump the reference count.
|
|
*/
|
|
sctp_association_hold((struct sctp_association *)asoc);
|
|
skb = sctp_event2skb(event);
|
|
event->asoc = (struct sctp_association *)asoc;
|
|
atomic_add(event->rmem_len, &event->asoc->rmem_alloc);
|
|
sctp_skb_set_owner_r(skb, asoc->base.sk);
|
|
}
|
|
|
|
/* A simple destructor to give up the reference to the association. */
|
|
static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
|
|
{
|
|
struct sctp_association *asoc = event->asoc;
|
|
|
|
atomic_sub(event->rmem_len, &asoc->rmem_alloc);
|
|
sctp_association_put(asoc);
|
|
}
|
|
|
|
/* Create and initialize an SCTP_ASSOC_CHANGE event.
|
|
*
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* Communication notifications inform the ULP that an SCTP association
|
|
* has either begun or ended. The identifier for a new association is
|
|
* provided by this notification.
|
|
*
|
|
* Note: There is no field checking here. If a field is unused it will be
|
|
* zero'd out.
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
|
|
const struct sctp_association *asoc,
|
|
__u16 flags, __u16 state, __u16 error, __u16 outbound,
|
|
__u16 inbound, struct sctp_chunk *chunk, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_assoc_change *sac;
|
|
struct sk_buff *skb;
|
|
|
|
/* If the lower layer passed in the chunk, it will be
|
|
* an ABORT, so we need to include it in the sac_info.
|
|
*/
|
|
if (chunk) {
|
|
/* Copy the chunk data to a new skb and reserve enough
|
|
* head room to use as notification.
|
|
*/
|
|
skb = skb_copy_expand(chunk->skb,
|
|
sizeof(struct sctp_assoc_change), 0, gfp);
|
|
|
|
if (!skb)
|
|
goto fail;
|
|
|
|
/* Embed the event fields inside the cloned skb. */
|
|
event = sctp_skb2event(skb);
|
|
sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
|
|
|
|
/* Include the notification structure */
|
|
sac = (struct sctp_assoc_change *)
|
|
skb_push(skb, sizeof(struct sctp_assoc_change));
|
|
|
|
/* Trim the buffer to the right length. */
|
|
skb_trim(skb, sizeof(struct sctp_assoc_change) +
|
|
ntohs(chunk->chunk_hdr->length) -
|
|
sizeof(sctp_chunkhdr_t));
|
|
} else {
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
sac = (struct sctp_assoc_change *) skb_put(skb,
|
|
sizeof(struct sctp_assoc_change));
|
|
}
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_type:
|
|
* It should be SCTP_ASSOC_CHANGE.
|
|
*/
|
|
sac->sac_type = SCTP_ASSOC_CHANGE;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_state: 32 bits (signed integer)
|
|
* This field holds one of a number of values that communicate the
|
|
* event that happened to the association.
|
|
*/
|
|
sac->sac_state = state;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_flags: 16 bits (unsigned integer)
|
|
* Currently unused.
|
|
*/
|
|
sac->sac_flags = 0;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_length: sizeof (__u32)
|
|
* This field is the total length of the notification data, including
|
|
* the notification header.
|
|
*/
|
|
sac->sac_length = skb->len;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_error: 32 bits (signed integer)
|
|
*
|
|
* If the state was reached due to a error condition (e.g.
|
|
* COMMUNICATION_LOST) any relevant error information is available in
|
|
* this field. This corresponds to the protocol error codes defined in
|
|
* [SCTP].
|
|
*/
|
|
sac->sac_error = error;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_outbound_streams: 16 bits (unsigned integer)
|
|
* sac_inbound_streams: 16 bits (unsigned integer)
|
|
*
|
|
* The maximum number of streams allowed in each direction are
|
|
* available in sac_outbound_streams and sac_inbound streams.
|
|
*/
|
|
sac->sac_outbound_streams = outbound;
|
|
sac->sac_inbound_streams = inbound;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* sac_assoc_id: sizeof (sctp_assoc_t)
|
|
*
|
|
* The association id field, holds the identifier for the association.
|
|
* All notifications for a given association have the same association
|
|
* identifier. For TCP style socket, this field is ignored.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sac->sac_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create and initialize an SCTP_PEER_ADDR_CHANGE event.
|
|
*
|
|
* Socket Extensions for SCTP - draft-01
|
|
* 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* When a destination address on a multi-homed peer encounters a change
|
|
* an interface details event is sent.
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
|
|
const struct sctp_association *asoc,
|
|
const struct sockaddr_storage *aaddr,
|
|
int flags, int state, int error, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_paddr_change *spc;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_paddr_change),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
spc = (struct sctp_paddr_change *)
|
|
skb_put(skb, sizeof(struct sctp_paddr_change));
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_type:
|
|
*
|
|
* It should be SCTP_PEER_ADDR_CHANGE.
|
|
*/
|
|
spc->spc_type = SCTP_PEER_ADDR_CHANGE;
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_length: sizeof (__u32)
|
|
*
|
|
* This field is the total length of the notification data, including
|
|
* the notification header.
|
|
*/
|
|
spc->spc_length = sizeof(struct sctp_paddr_change);
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_flags: 16 bits (unsigned integer)
|
|
* Currently unused.
|
|
*/
|
|
spc->spc_flags = 0;
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_state: 32 bits (signed integer)
|
|
*
|
|
* This field holds one of a number of values that communicate the
|
|
* event that happened to the address.
|
|
*/
|
|
spc->spc_state = state;
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_error: 32 bits (signed integer)
|
|
*
|
|
* If the state was reached due to any error condition (e.g.
|
|
* ADDRESS_UNREACHABLE) any relevant error information is available in
|
|
* this field.
|
|
*/
|
|
spc->spc_error = error;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
|
*
|
|
* spc_assoc_id: sizeof (sctp_assoc_t)
|
|
*
|
|
* The association id field, holds the identifier for the association.
|
|
* All notifications for a given association have the same association
|
|
* identifier. For TCP style socket, this field is ignored.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
spc->spc_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
|
*
|
|
* spc_aaddr: sizeof (struct sockaddr_storage)
|
|
*
|
|
* The affected address field, holds the remote peer's address that is
|
|
* encountering the change of state.
|
|
*/
|
|
memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage));
|
|
|
|
/* Map ipv4 address into v4-mapped-on-v6 address. */
|
|
sctp_get_pf_specific(asoc->base.sk->sk_family)->addr_v4map(
|
|
sctp_sk(asoc->base.sk),
|
|
(union sctp_addr *)&spc->spc_aaddr);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create and initialize an SCTP_REMOTE_ERROR notification.
|
|
*
|
|
* Note: This assumes that the chunk->skb->data already points to the
|
|
* operation error payload.
|
|
*
|
|
* Socket Extensions for SCTP - draft-01
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* A remote peer may send an Operational Error message to its peer.
|
|
* This message indicates a variety of error conditions on an
|
|
* association. The entire error TLV as it appears on the wire is
|
|
* included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP
|
|
* specification [SCTP] and any extensions for a list of possible
|
|
* error formats.
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
|
|
const struct sctp_association *asoc, struct sctp_chunk *chunk,
|
|
__u16 flags, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_remote_error *sre;
|
|
struct sk_buff *skb;
|
|
sctp_errhdr_t *ch;
|
|
__be16 cause;
|
|
int elen;
|
|
|
|
ch = (sctp_errhdr_t *)(chunk->skb->data);
|
|
cause = ch->cause;
|
|
elen = WORD_ROUND(ntohs(ch->length)) - sizeof(sctp_errhdr_t);
|
|
|
|
/* Pull off the ERROR header. */
|
|
skb_pull(chunk->skb, sizeof(sctp_errhdr_t));
|
|
|
|
/* Copy the skb to a new skb with room for us to prepend
|
|
* notification with.
|
|
*/
|
|
skb = skb_copy_expand(chunk->skb, sizeof(struct sctp_remote_error),
|
|
0, gfp);
|
|
|
|
/* Pull off the rest of the cause TLV from the chunk. */
|
|
skb_pull(chunk->skb, elen);
|
|
if (!skb)
|
|
goto fail;
|
|
|
|
/* Embed the event fields inside the cloned skb. */
|
|
event = sctp_skb2event(skb);
|
|
sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
|
|
|
|
sre = (struct sctp_remote_error *)
|
|
skb_push(skb, sizeof(struct sctp_remote_error));
|
|
|
|
/* Trim the buffer to the right length. */
|
|
skb_trim(skb, sizeof(struct sctp_remote_error) + elen);
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* sre_type:
|
|
* It should be SCTP_REMOTE_ERROR.
|
|
*/
|
|
sre->sre_type = SCTP_REMOTE_ERROR;
|
|
|
|
/*
|
|
* Socket Extensions for SCTP
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* sre_flags: 16 bits (unsigned integer)
|
|
* Currently unused.
|
|
*/
|
|
sre->sre_flags = 0;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* sre_length: sizeof (__u32)
|
|
*
|
|
* This field is the total length of the notification data,
|
|
* including the notification header.
|
|
*/
|
|
sre->sre_length = skb->len;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* sre_error: 16 bits (unsigned integer)
|
|
* This value represents one of the Operational Error causes defined in
|
|
* the SCTP specification, in network byte order.
|
|
*/
|
|
sre->sre_error = cause;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.3 SCTP_REMOTE_ERROR
|
|
*
|
|
* sre_assoc_id: sizeof (sctp_assoc_t)
|
|
*
|
|
* The association id field, holds the identifier for the association.
|
|
* All notifications for a given association have the same association
|
|
* identifier. For TCP style socket, this field is ignored.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sre->sre_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create and initialize a SCTP_SEND_FAILED notification.
|
|
*
|
|
* Socket Extensions for SCTP - draft-01
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
|
|
const struct sctp_association *asoc, struct sctp_chunk *chunk,
|
|
__u16 flags, __u32 error, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_send_failed *ssf;
|
|
struct sk_buff *skb;
|
|
|
|
/* Pull off any padding. */
|
|
int len = ntohs(chunk->chunk_hdr->length);
|
|
|
|
/* Make skb with more room so we can prepend notification. */
|
|
skb = skb_copy_expand(chunk->skb,
|
|
sizeof(struct sctp_send_failed), /* headroom */
|
|
0, /* tailroom */
|
|
gfp);
|
|
if (!skb)
|
|
goto fail;
|
|
|
|
/* Pull off the common chunk header and DATA header. */
|
|
skb_pull(skb, sizeof(struct sctp_data_chunk));
|
|
len -= sizeof(struct sctp_data_chunk);
|
|
|
|
/* Embed the event fields inside the cloned skb. */
|
|
event = sctp_skb2event(skb);
|
|
sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
|
|
|
|
ssf = (struct sctp_send_failed *)
|
|
skb_push(skb, sizeof(struct sctp_send_failed));
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_type:
|
|
* It should be SCTP_SEND_FAILED.
|
|
*/
|
|
ssf->ssf_type = SCTP_SEND_FAILED;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_flags: 16 bits (unsigned integer)
|
|
* The flag value will take one of the following values
|
|
*
|
|
* SCTP_DATA_UNSENT - Indicates that the data was never put on
|
|
* the wire.
|
|
*
|
|
* SCTP_DATA_SENT - Indicates that the data was put on the wire.
|
|
* Note that this does not necessarily mean that the
|
|
* data was (or was not) successfully delivered.
|
|
*/
|
|
ssf->ssf_flags = flags;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_length: sizeof (__u32)
|
|
* This field is the total length of the notification data, including
|
|
* the notification header.
|
|
*/
|
|
ssf->ssf_length = sizeof(struct sctp_send_failed) + len;
|
|
skb_trim(skb, ssf->ssf_length);
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_error: 16 bits (unsigned integer)
|
|
* This value represents the reason why the send failed, and if set,
|
|
* will be a SCTP protocol error code as defined in [SCTP] section
|
|
* 3.3.10.
|
|
*/
|
|
ssf->ssf_error = error;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_info: sizeof (struct sctp_sndrcvinfo)
|
|
* The original send information associated with the undelivered
|
|
* message.
|
|
*/
|
|
memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
|
|
|
|
/* Per TSVWG discussion with Randy. Allow the application to
|
|
* reassemble a fragmented message.
|
|
*/
|
|
ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.4 SCTP_SEND_FAILED
|
|
*
|
|
* ssf_assoc_id: sizeof (sctp_assoc_t)
|
|
* The association id field, sf_assoc_id, holds the identifier for the
|
|
* association. All notifications for a given association have the
|
|
* same association identifier. For TCP style socket, this field is
|
|
* ignored.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
ssf->ssf_assoc_id = sctp_assoc2id(asoc);
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create and initialize a SCTP_SHUTDOWN_EVENT notification.
|
|
*
|
|
* Socket Extensions for SCTP - draft-01
|
|
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
|
|
const struct sctp_association *asoc,
|
|
__u16 flags, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_shutdown_event *sse;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_shutdown_event),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
sse = (struct sctp_shutdown_event *)
|
|
skb_put(skb, sizeof(struct sctp_shutdown_event));
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
|
*
|
|
* sse_type
|
|
* It should be SCTP_SHUTDOWN_EVENT
|
|
*/
|
|
sse->sse_type = SCTP_SHUTDOWN_EVENT;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
|
*
|
|
* sse_flags: 16 bits (unsigned integer)
|
|
* Currently unused.
|
|
*/
|
|
sse->sse_flags = 0;
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
|
*
|
|
* sse_length: sizeof (__u32)
|
|
* This field is the total length of the notification data, including
|
|
* the notification header.
|
|
*/
|
|
sse->sse_length = sizeof(struct sctp_shutdown_event);
|
|
|
|
/* Socket Extensions for SCTP
|
|
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
|
*
|
|
* sse_assoc_id: sizeof (sctp_assoc_t)
|
|
* The association id field, holds the identifier for the association.
|
|
* All notifications for a given association have the same association
|
|
* identifier. For TCP style socket, this field is ignored.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sse->sse_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create and initialize a SCTP_ADAPTATION_INDICATION notification.
|
|
*
|
|
* Socket Extensions for SCTP
|
|
* 5.3.1.6 SCTP_ADAPTATION_INDICATION
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
|
|
const struct sctp_association *asoc, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_adaptation_event *sai;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_adaptation_event),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
sai = (struct sctp_adaptation_event *)
|
|
skb_put(skb, sizeof(struct sctp_adaptation_event));
|
|
|
|
sai->sai_type = SCTP_ADAPTATION_INDICATION;
|
|
sai->sai_flags = 0;
|
|
sai->sai_length = sizeof(struct sctp_adaptation_event);
|
|
sai->sai_adaptation_ind = asoc->peer.adaptation_ind;
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sai->sai_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* A message has been received. Package this message as a notification
|
|
* to pass it to the upper layers. Go ahead and calculate the sndrcvinfo
|
|
* even if filtered out later.
|
|
*
|
|
* Socket Extensions for SCTP
|
|
* 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
|
|
struct sctp_chunk *chunk,
|
|
gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event = NULL;
|
|
struct sk_buff *skb;
|
|
size_t padding, len;
|
|
int rx_count;
|
|
|
|
/*
|
|
* check to see if we need to make space for this
|
|
* new skb, expand the rcvbuffer if needed, or drop
|
|
* the frame
|
|
*/
|
|
if (asoc->ep->rcvbuf_policy)
|
|
rx_count = atomic_read(&asoc->rmem_alloc);
|
|
else
|
|
rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc);
|
|
|
|
if (rx_count >= asoc->base.sk->sk_rcvbuf) {
|
|
|
|
if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
|
|
(!sk_rmem_schedule(asoc->base.sk, chunk->skb,
|
|
chunk->skb->truesize)))
|
|
goto fail;
|
|
}
|
|
|
|
/* Clone the original skb, sharing the data. */
|
|
skb = skb_clone(chunk->skb, gfp);
|
|
if (!skb)
|
|
goto fail;
|
|
|
|
/* Now that all memory allocations for this chunk succeeded, we
|
|
* can mark it as received so the tsn_map is updated correctly.
|
|
*/
|
|
if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
|
|
ntohl(chunk->subh.data_hdr->tsn),
|
|
chunk->transport))
|
|
goto fail_mark;
|
|
|
|
/* First calculate the padding, so we don't inadvertently
|
|
* pass up the wrong length to the user.
|
|
*
|
|
* RFC 2960 - Section 3.2 Chunk Field Descriptions
|
|
*
|
|
* The total length of a chunk(including Type, Length and Value fields)
|
|
* MUST be a multiple of 4 bytes. If the length of the chunk is not a
|
|
* multiple of 4 bytes, the sender MUST pad the chunk with all zero
|
|
* bytes and this padding is not included in the chunk length field.
|
|
* The sender should never pad with more than 3 bytes. The receiver
|
|
* MUST ignore the padding bytes.
|
|
*/
|
|
len = ntohs(chunk->chunk_hdr->length);
|
|
padding = WORD_ROUND(len) - len;
|
|
|
|
/* Fixup cloned skb with just this chunks data. */
|
|
skb_trim(skb, chunk->chunk_end - padding - skb->data);
|
|
|
|
/* Embed the event fields inside the cloned skb. */
|
|
event = sctp_skb2event(skb);
|
|
|
|
/* Initialize event with flags 0 and correct length
|
|
* Since this is a clone of the original skb, only account for
|
|
* the data of this chunk as other chunks will be accounted separately.
|
|
*/
|
|
sctp_ulpevent_init(event, 0, skb->len + sizeof(struct sk_buff));
|
|
|
|
sctp_ulpevent_receive_data(event, asoc);
|
|
|
|
event->stream = ntohs(chunk->subh.data_hdr->stream);
|
|
event->ssn = ntohs(chunk->subh.data_hdr->ssn);
|
|
event->ppid = chunk->subh.data_hdr->ppid;
|
|
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
|
|
event->flags |= SCTP_UNORDERED;
|
|
event->cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
|
|
}
|
|
event->tsn = ntohl(chunk->subh.data_hdr->tsn);
|
|
event->msg_flags |= chunk->chunk_hdr->flags;
|
|
event->iif = sctp_chunk_iif(chunk);
|
|
|
|
return event;
|
|
|
|
fail_mark:
|
|
kfree_skb(skb);
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/* Create a partial delivery related event.
|
|
*
|
|
* 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
|
|
*
|
|
* When a receiver is engaged in a partial delivery of a
|
|
* message this notification will be used to indicate
|
|
* various events.
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
|
|
const struct sctp_association *asoc, __u32 indication,
|
|
gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_pdapi_event *pd;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_pdapi_event),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
pd = (struct sctp_pdapi_event *)
|
|
skb_put(skb, sizeof(struct sctp_pdapi_event));
|
|
|
|
/* pdapi_type
|
|
* It should be SCTP_PARTIAL_DELIVERY_EVENT
|
|
*
|
|
* pdapi_flags: 16 bits (unsigned integer)
|
|
* Currently unused.
|
|
*/
|
|
pd->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
|
|
pd->pdapi_flags = 0;
|
|
|
|
/* pdapi_length: 32 bits (unsigned integer)
|
|
*
|
|
* This field is the total length of the notification data, including
|
|
* the notification header. It will generally be sizeof (struct
|
|
* sctp_pdapi_event).
|
|
*/
|
|
pd->pdapi_length = sizeof(struct sctp_pdapi_event);
|
|
|
|
/* pdapi_indication: 32 bits (unsigned integer)
|
|
*
|
|
* This field holds the indication being sent to the application.
|
|
*/
|
|
pd->pdapi_indication = indication;
|
|
|
|
/* pdapi_assoc_id: sizeof (sctp_assoc_t)
|
|
*
|
|
* The association id field, holds the identifier for the association.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
pd->pdapi_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
struct sctp_ulpevent *sctp_ulpevent_make_authkey(
|
|
const struct sctp_association *asoc, __u16 key_id,
|
|
__u32 indication, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_authkey_event *ak;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_authkey_event),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
goto fail;
|
|
|
|
skb = sctp_event2skb(event);
|
|
ak = (struct sctp_authkey_event *)
|
|
skb_put(skb, sizeof(struct sctp_authkey_event));
|
|
|
|
ak->auth_type = SCTP_AUTHENTICATION_EVENT;
|
|
ak->auth_flags = 0;
|
|
ak->auth_length = sizeof(struct sctp_authkey_event);
|
|
|
|
ak->auth_keynumber = key_id;
|
|
ak->auth_altkeynumber = 0;
|
|
ak->auth_indication = indication;
|
|
|
|
/*
|
|
* The association id field, holds the identifier for the association.
|
|
*/
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
ak->auth_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
fail:
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Socket Extensions for SCTP
|
|
* 6.3.10. SCTP_SENDER_DRY_EVENT
|
|
*/
|
|
struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event(
|
|
const struct sctp_association *asoc, gfp_t gfp)
|
|
{
|
|
struct sctp_ulpevent *event;
|
|
struct sctp_sender_dry_event *sdry;
|
|
struct sk_buff *skb;
|
|
|
|
event = sctp_ulpevent_new(sizeof(struct sctp_sender_dry_event),
|
|
MSG_NOTIFICATION, gfp);
|
|
if (!event)
|
|
return NULL;
|
|
|
|
skb = sctp_event2skb(event);
|
|
sdry = (struct sctp_sender_dry_event *)
|
|
skb_put(skb, sizeof(struct sctp_sender_dry_event));
|
|
|
|
sdry->sender_dry_type = SCTP_SENDER_DRY_EVENT;
|
|
sdry->sender_dry_flags = 0;
|
|
sdry->sender_dry_length = sizeof(struct sctp_sender_dry_event);
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sdry->sender_dry_assoc_id = sctp_assoc2id(asoc);
|
|
|
|
return event;
|
|
}
|
|
|
|
/* Return the notification type, assuming this is a notification
|
|
* event.
|
|
*/
|
|
__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event)
|
|
{
|
|
union sctp_notification *notification;
|
|
struct sk_buff *skb;
|
|
|
|
skb = sctp_event2skb(event);
|
|
notification = (union sctp_notification *) skb->data;
|
|
return notification->sn_header.sn_type;
|
|
}
|
|
|
|
/* Copy out the sndrcvinfo into a msghdr. */
|
|
void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
|
|
struct msghdr *msghdr)
|
|
{
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
if (sctp_ulpevent_is_notification(event))
|
|
return;
|
|
|
|
/* Sockets API Extensions for SCTP
|
|
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
|
|
*
|
|
* sinfo_stream: 16 bits (unsigned integer)
|
|
*
|
|
* For recvmsg() the SCTP stack places the message's stream number in
|
|
* this value.
|
|
*/
|
|
sinfo.sinfo_stream = event->stream;
|
|
/* sinfo_ssn: 16 bits (unsigned integer)
|
|
*
|
|
* For recvmsg() this value contains the stream sequence number that
|
|
* the remote endpoint placed in the DATA chunk. For fragmented
|
|
* messages this is the same number for all deliveries of the message
|
|
* (if more than one recvmsg() is needed to read the message).
|
|
*/
|
|
sinfo.sinfo_ssn = event->ssn;
|
|
/* sinfo_ppid: 32 bits (unsigned integer)
|
|
*
|
|
* In recvmsg() this value is
|
|
* the same information that was passed by the upper layer in the peer
|
|
* application. Please note that byte order issues are NOT accounted
|
|
* for and this information is passed opaquely by the SCTP stack from
|
|
* one end to the other.
|
|
*/
|
|
sinfo.sinfo_ppid = event->ppid;
|
|
/* sinfo_flags: 16 bits (unsigned integer)
|
|
*
|
|
* This field may contain any of the following flags and is composed of
|
|
* a bitwise OR of these values.
|
|
*
|
|
* recvmsg() flags:
|
|
*
|
|
* SCTP_UNORDERED - This flag is present when the message was sent
|
|
* non-ordered.
|
|
*/
|
|
sinfo.sinfo_flags = event->flags;
|
|
/* sinfo_tsn: 32 bit (unsigned integer)
|
|
*
|
|
* For the receiving side, this field holds a TSN that was
|
|
* assigned to one of the SCTP Data Chunks.
|
|
*/
|
|
sinfo.sinfo_tsn = event->tsn;
|
|
/* sinfo_cumtsn: 32 bit (unsigned integer)
|
|
*
|
|
* This field will hold the current cumulative TSN as
|
|
* known by the underlying SCTP layer. Note this field is
|
|
* ignored when sending and only valid for a receive
|
|
* operation when sinfo_flags are set to SCTP_UNORDERED.
|
|
*/
|
|
sinfo.sinfo_cumtsn = event->cumtsn;
|
|
/* sinfo_assoc_id: sizeof (sctp_assoc_t)
|
|
*
|
|
* The association handle field, sinfo_assoc_id, holds the identifier
|
|
* for the association announced in the COMMUNICATION_UP notification.
|
|
* All notifications for a given association have the same identifier.
|
|
* Ignored for one-to-one style sockets.
|
|
*/
|
|
sinfo.sinfo_assoc_id = sctp_assoc2id(event->asoc);
|
|
|
|
/* context value that is set via SCTP_CONTEXT socket option. */
|
|
sinfo.sinfo_context = event->asoc->default_rcv_context;
|
|
|
|
/* These fields are not used while receiving. */
|
|
sinfo.sinfo_timetolive = 0;
|
|
|
|
put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
|
|
sizeof(struct sctp_sndrcvinfo), (void *)&sinfo);
|
|
}
|
|
|
|
/* Do accounting for bytes received and hold a reference to the association
|
|
* for each skb.
|
|
*/
|
|
static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
|
|
struct sctp_association *asoc)
|
|
{
|
|
struct sk_buff *skb, *frag;
|
|
|
|
skb = sctp_event2skb(event);
|
|
/* Set the owner and charge rwnd for bytes received. */
|
|
sctp_ulpevent_set_owner(event, asoc);
|
|
sctp_assoc_rwnd_update(asoc, false);
|
|
|
|
if (!skb->data_len)
|
|
return;
|
|
|
|
/* Note: Not clearing the entire event struct as this is just a
|
|
* fragment of the real event. However, we still need to do rwnd
|
|
* accounting.
|
|
* In general, the skb passed from IP can have only 1 level of
|
|
* fragments. But we allow multiple levels of fragments.
|
|
*/
|
|
skb_walk_frags(skb, frag)
|
|
sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
|
|
}
|
|
|
|
/* Do accounting for bytes just read by user and release the references to
|
|
* the association.
|
|
*/
|
|
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
|
|
{
|
|
struct sk_buff *skb, *frag;
|
|
unsigned int len;
|
|
struct sctp_association *asoc;
|
|
|
|
/* Current stack structures assume that the rcv buffer is
|
|
* per socket. For UDP style sockets this is not true as
|
|
* multiple associations may be on a single UDP-style socket.
|
|
* Use the local private area of the skb to track the owning
|
|
* association.
|
|
*/
|
|
|
|
skb = sctp_event2skb(event);
|
|
len = skb->len;
|
|
|
|
if (!skb->data_len)
|
|
goto done;
|
|
|
|
/* Don't forget the fragments. */
|
|
skb_walk_frags(skb, frag) {
|
|
/* NOTE: skb_shinfos are recursive. Although IP returns
|
|
* skb's with only 1 level of fragments, SCTP reassembly can
|
|
* increase the levels.
|
|
*/
|
|
sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
|
|
}
|
|
|
|
done:
|
|
asoc = event->asoc;
|
|
sctp_association_hold(asoc);
|
|
sctp_ulpevent_release_owner(event);
|
|
sctp_assoc_rwnd_update(asoc, true);
|
|
sctp_association_put(asoc);
|
|
}
|
|
|
|
static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
|
|
{
|
|
struct sk_buff *skb, *frag;
|
|
|
|
skb = sctp_event2skb(event);
|
|
|
|
if (!skb->data_len)
|
|
goto done;
|
|
|
|
/* Don't forget the fragments. */
|
|
skb_walk_frags(skb, frag) {
|
|
/* NOTE: skb_shinfos are recursive. Although IP returns
|
|
* skb's with only 1 level of fragments, SCTP reassembly can
|
|
* increase the levels.
|
|
*/
|
|
sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
|
|
}
|
|
|
|
done:
|
|
sctp_ulpevent_release_owner(event);
|
|
}
|
|
|
|
/* Free a ulpevent that has an owner. It includes releasing the reference
|
|
* to the owner, updating the rwnd in case of a DATA event and freeing the
|
|
* skb.
|
|
*/
|
|
void sctp_ulpevent_free(struct sctp_ulpevent *event)
|
|
{
|
|
if (sctp_ulpevent_is_notification(event))
|
|
sctp_ulpevent_release_owner(event);
|
|
else
|
|
sctp_ulpevent_release_data(event);
|
|
|
|
kfree_skb(sctp_event2skb(event));
|
|
}
|
|
|
|
/* Purge the skb lists holding ulpevents. */
|
|
unsigned int sctp_queue_purge_ulpevents(struct sk_buff_head *list)
|
|
{
|
|
struct sk_buff *skb;
|
|
unsigned int data_unread = 0;
|
|
|
|
while ((skb = skb_dequeue(list)) != NULL) {
|
|
struct sctp_ulpevent *event = sctp_skb2event(skb);
|
|
|
|
if (!sctp_ulpevent_is_notification(event))
|
|
data_unread += skb->len;
|
|
|
|
sctp_ulpevent_free(event);
|
|
}
|
|
|
|
return data_unread;
|
|
}
|