Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
67e6a91e
Commit
67e6a91e
authored
Oct 02, 2003
by
David S. Miller
Browse files
Options
Browse Files
Download
Plain Diff
Merge
http://linux-lksctp.bkbits.net/lksctp-2.5
into nuts.ninka.net:/disk1/davem/BK/net-2.5
parents
773bc0e3
0b49f36e
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
471 additions
and
177 deletions
+471
-177
include/linux/sctp.h
include/linux/sctp.h
+0
-109
include/net/sctp/command.h
include/net/sctp/command.h
+4
-2
include/net/sctp/sctp.h
include/net/sctp/sctp.h
+16
-16
include/net/sctp/sm.h
include/net/sctp/sm.h
+9
-3
include/net/sctp/structs.h
include/net/sctp/structs.h
+8
-0
net/sctp/associola.c
net/sctp/associola.c
+11
-5
net/sctp/bind_addr.c
net/sctp/bind_addr.c
+37
-0
net/sctp/endpointola.c
net/sctp/endpointola.c
+2
-2
net/sctp/sm_make_chunk.c
net/sctp/sm_make_chunk.c
+105
-12
net/sctp/sm_sideeffect.c
net/sctp/sm_sideeffect.c
+42
-1
net/sctp/sm_statefuns.c
net/sctp/sm_statefuns.c
+4
-1
net/sctp/socket.c
net/sctp/socket.c
+232
-25
net/sctp/sysctl.c
net/sctp/sysctl.c
+1
-1
No files found.
include/linux/sctp.h
View file @
67e6a91e
...
...
@@ -524,113 +524,4 @@ typedef struct sctp_addip_chunk {
sctp_addiphdr_t
addip_hdr
;
}
sctp_addip_chunk_t
__attribute__
((
packed
));
/* FIXME: Cleanup needs to continue below this line. */
/* ADDIP Section 3.1.1
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This is an opaque integer assigned by the sender to identify each
* request parameter. It is in host byte order and is only meaningful
* to the sender. The receiver of the ASCONF Chunk will copy this 32
* bit value into the ASCONF Correlation ID field of the
* ASCONF-ACK. The sender of the ASCONF can use this same value in the
* ASCONF-ACK to find which request the response is for.
*
* ASCONF Parameter: TLV format
*
* Each Address configuration change is represented by a TLV parameter
* as defined in Section 3.2. One or more requests may be present in
* an ASCONF Chunk.
*/
typedef
struct
{
__u32
correlation
;
sctp_paramhdr_t
p
;
__u8
payload
[
0
];
}
sctpAsconfReq_t
;
/* ADDIP
* 3.1.1 Address/Stream Configuration Change Chunk (ASCONF)
*
* This chunk is used to communicate to the remote endpoint one of the
* configuration change requests that MUST be acknowledged. The
* information carried in the ASCONF Chunk uses the form of a
* Tag-Length-Value (TLV), as described in "3.2.1
* Optional/Variable-length Parameter Format" in [RFC2960], for all
* variable parameters.
*/
typedef
struct
{
__u32
serial
;
__u8
reserved
[
3
];
__u8
addr_type
;
__u32
addr
[
4
];
sctpAsconfReq_t
requests
[
0
];
}
sctpAsconf_t
;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This value is copied from the ASCONF Correlation ID received in the
* ASCONF Chunk. It is used by the receiver of the ASCONF-ACK to identify
* which ASCONF parameter this response is associated with.
*
* ASCONF Parameter Response : TLV format
*
* The ASCONF Parameter Response is used in the ASCONF-ACK to report
* status of ASCONF processing. By default, if a responding endpoint
* does not include any Error Cause, a success is indicated. Thus a
* sender of an ASCONF-ACK MAY indicate complete success of all TLVs in
* an ASCONF by returning only the Chunk Type, Chunk Flags, Chunk Length
* (set to 8) and the Serial Number.
*/
typedef
union
{
struct
{
__u32
correlation
;
sctp_paramhdr_t
header
;
/* success report */
}
success
;
struct
{
__u32
correlation
;
sctp_paramhdr_t
header
;
/* error cause indication */
sctp_paramhdr_t
errcause
;
uint8_t
request
[
0
];
/* original request from ASCONF */
}
error
;
#define __correlation success.correlation
#define __header success.header
#define __cause error.errcause
#define __request error.request
}
sctpAsconfAckRsp_t
;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* This chunk is used by the receiver of an ASCONF Chunk to
* acknowledge the reception. It carries zero or more results for any
* ASCONF Parameters that were processed by the receiver.
*/
typedef
struct
{
__u32
serial
;
sctpAsconfAckRsp_t
responses
[
0
];
}
sctpAsconfAck_t
;
/*********************************************************************
* Internal structures
*
* These are data structures which never go out on the wire.
*********************************************************************/
/* What is this data structure for? The TLV isn't one--it is just a
* value. Perhaps this data structure ought to have a type--otherwise
* it is not unambigiously parseable. --piggy
*/
typedef
struct
{
struct
list_head
hook
;
int
length
;
/* length of the TLV */
/* the actually TLV to be copied into ASCONF_ACK */
sctpAsconfAckRsp_t
TLV
;
}
sctpAsconfAckRspNode_t
;
#endif
/* __LINUX_SCTP_H__ */
include/net/sctp/command.h
View file @
67e6a91e
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, and IBM
/* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (C) 1999-2001 Cisco, Motorola
*
* This file is part of the SCTP kernel reference Implementation
*
...
...
@@ -88,6 +89,7 @@ typedef enum {
SCTP_CMD_PART_DELIVER
,
/* Partial data delivery considerations. */
SCTP_CMD_RENEGE
,
/* Renege data on an association. */
SCTP_CMD_SETUP_T4
,
/* ADDIP, setup T4 RTO timer parms. */
SCTP_CMD_PROCESS_OPERR
,
/* Process an ERROR chunk. */
SCTP_CMD_LAST
}
sctp_verb_t
;
...
...
include/net/sctp/sctp.h
View file @
67e6a91e
...
...
@@ -116,6 +116,9 @@
#define SCTP_STATIC static
#endif
#define MSECS_TO_JIFFIES(msec) (msec * HZ / 1000)
#define JIFFIES_TO_MSECS(jiff) (jiff * 1000 / HZ)
/*
* Function declarations.
*/
...
...
@@ -495,22 +498,19 @@ for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
#define tv_lt(s, t) \
(s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
/* Stolen from net/profile.h. Using it from there is more grief than
* it is worth.
*/
static
inline
void
tv_add
(
const
struct
timeval
*
entered
,
struct
timeval
*
leaved
)
{
time_t
usecs
=
leaved
->
tv_usec
+
entered
->
tv_usec
;
time_t
secs
=
leaved
->
tv_sec
+
entered
->
tv_sec
;
if
(
usecs
>=
1000000
)
{
usecs
-=
1000000
;
secs
++
;
}
leaved
->
tv_sec
=
secs
;
leaved
->
tv_usec
=
usecs
;
}
/* Add tv1 to tv2. */
#define TIMEVAL_ADD(tv1, tv2) \
({ \
suseconds_t usecs = (tv2).tv_usec + (tv1).tv_usec; \
time_t secs = (tv2).tv_sec + (tv1).tv_sec; \
\
if (usecs >= 1000000) { \
usecs -= 1000000; \
secs++; \
} \
(tv2).tv_sec = secs; \
(tv2).tv_usec = usecs; \
})
/* External references. */
...
...
include/net/sctp/sm.h
View file @
67e6a91e
...
...
@@ -265,13 +265,19 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *,
struct
sctp_chunk
*
sctp_make_asconf
(
struct
sctp_association
*
asoc
,
union
sctp_addr
*
addr
,
int
vparam_len
);
struct
sctp_chunk
*
sctp_make_asconf_update_ip
(
struct
sctp_association
*
,
union
sctp_addr
*
,
struct
sockaddr
*
,
int
,
int
);
struct
sctp_chunk
*
sctp_make_asconf_set_prim
(
struct
sctp_association
*
asoc
,
union
sctp_addr
*
addr
);
struct
sctp_chunk
*
sctp_make_asconf_ack
(
struct
sctp_association
*
asoc
,
int
serial
,
int
vparam_len
);
struct
sctp_chunk
*
sctp_process_asconf
(
struct
sctp_association
*
asoc
,
struct
sctp_chunk
*
asconf
,
int
vparam_len
);
struct
sctp_chunk
*
sctp_make_asconf_set_prim
(
struct
sctp_association
*
asoc
,
union
sctp_addr
*
addr
);
void
sctp_chunk_assign_tsn
(
struct
sctp_chunk
*
);
void
sctp_chunk_assign_ssn
(
struct
sctp_chunk
*
);
...
...
include/net/sctp/structs.h
View file @
67e6a91e
...
...
@@ -1085,6 +1085,10 @@ int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
int
sctp_del_bind_addr
(
struct
sctp_bind_addr
*
,
union
sctp_addr
*
);
int
sctp_bind_addr_match
(
struct
sctp_bind_addr
*
,
const
union
sctp_addr
*
,
struct
sctp_opt
*
);
union
sctp_addr
*
sctp_find_unmatch_addr
(
struct
sctp_bind_addr
*
bp
,
const
union
sctp_addr
*
addrs
,
int
addrcnt
,
struct
sctp_opt
*
opt
);
union
sctp_params
sctp_bind_addrs_to_raw
(
const
struct
sctp_bind_addr
*
bp
,
int
*
addrs_len
,
int
gfp
);
int
sctp_raw_to_bind_addrs
(
struct
sctp_bind_addr
*
bp
,
__u8
*
raw
,
int
len
,
...
...
@@ -1389,6 +1393,10 @@ struct sctp_association {
__u8
ipv4_address
;
/* Peer understands IPv4 addresses? */
__u8
ipv6_address
;
/* Peer understands IPv6 addresses? */
__u8
hostname_address
;
/* Peer understands DNS addresses? */
/* Does peer support ADDIP? */
__u8
asconf_capable
;
struct
sctp_inithdr
i
;
int
cookie_len
;
void
*
cookie
;
...
...
net/sctp/associola.c
View file @
67e6a91e
...
...
@@ -141,9 +141,9 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
* socket values.
*/
asoc
->
max_retrans
=
sp
->
assocparams
.
sasoc_asocmaxrxt
;
asoc
->
rto_initial
=
sp
->
rtoinfo
.
srto_initial
*
HZ
/
1000
;
asoc
->
rto_max
=
sp
->
rtoinfo
.
srto_max
*
HZ
/
1000
;
asoc
->
rto_min
=
sp
->
rtoinfo
.
srto_min
*
HZ
/
1000
;
asoc
->
rto_initial
=
MSECS_TO_JIFFIES
(
sp
->
rtoinfo
.
srto_initial
)
;
asoc
->
rto_max
=
MSECS_TO_JIFFIES
(
sp
->
rtoinfo
.
srto_max
)
;
asoc
->
rto_min
=
MSECS_TO_JIFFIES
(
sp
->
rtoinfo
.
srto_min
)
;
asoc
->
overall_error_count
=
0
;
...
...
@@ -168,7 +168,8 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
asoc
->
c
.
sinit_num_ostreams
=
sp
->
initmsg
.
sinit_num_ostreams
;
asoc
->
max_init_attempts
=
sp
->
initmsg
.
sinit_max_attempts
;
asoc
->
max_init_timeo
=
sp
->
initmsg
.
sinit_max_init_timeo
*
HZ
/
1000
;
asoc
->
max_init_timeo
=
MSECS_TO_JIFFIES
(
sp
->
initmsg
.
sinit_max_init_timeo
);
/* Allocate storage for the ssnmap after the inbound and outbound
* streams have been negotiated during Init.
...
...
@@ -246,6 +247,11 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
*/
asoc
->
peer
.
sack_needed
=
1
;
/* Assume that the peer recongizes ASCONF until reported otherwise
* via an ERROR chunk.
*/
asoc
->
peer
.
asconf_capable
=
1
;
/* Create an input queue. */
sctp_inq_init
(
&
asoc
->
base
.
inqueue
);
sctp_inq_set_th_handler
(
&
asoc
->
base
.
inqueue
,
...
...
@@ -495,7 +501,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Initialize the peer's heartbeat interval based on the
* sock configured value.
*/
peer
->
hb_interval
=
sp
->
paddrparam
.
spp_hbinterval
*
HZ
;
peer
->
hb_interval
=
MSECS_TO_JIFFIES
(
sp
->
paddrparam
.
spp_hbinterval
)
;
/* Set the path max_retrans. */
peer
->
max_retrans
=
asoc
->
max_retrans
;
...
...
net/sctp/bind_addr.c
View file @
67e6a91e
...
...
@@ -324,6 +324,43 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
return
0
;
}
/* Find the first address in the bind address list that is not present in
* the addrs packed array.
*/
union
sctp_addr
*
sctp_find_unmatch_addr
(
struct
sctp_bind_addr
*
bp
,
const
union
sctp_addr
*
addrs
,
int
addrcnt
,
struct
sctp_opt
*
opt
)
{
struct
sctp_sockaddr_entry
*
laddr
;
union
sctp_addr
*
addr
;
void
*
addr_buf
;
struct
sctp_af
*
af
;
struct
list_head
*
pos
;
int
i
;
list_for_each
(
pos
,
&
bp
->
address_list
)
{
laddr
=
list_entry
(
pos
,
struct
sctp_sockaddr_entry
,
list
);
addr_buf
=
(
union
sctp_addr
*
)
addrs
;
for
(
i
=
0
;
i
<
addrcnt
;
i
++
)
{
addr
=
(
union
sctp_addr
*
)
addr_buf
;
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
if
(
!
af
)
return
NULL
;
if
(
opt
->
pf
->
cmp_addr
(
&
laddr
->
a
,
addr
,
opt
))
break
;
addr_buf
+=
af
->
sockaddr_len
;
}
if
(
i
==
addrcnt
)
return
&
laddr
->
a
;
}
return
NULL
;
}
/* Copy out addresses from the global local address list. */
static
int
sctp_copy_one_addr
(
struct
sctp_bind_addr
*
dest
,
union
sctp_addr
*
addr
,
...
...
net/sctp/endpointola.c
View file @
67e6a91e
...
...
@@ -129,7 +129,7 @@ struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_T1_INIT
]
=
SCTP_DEFAULT_TIMEOUT_T1_INIT
;
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_T2_SHUTDOWN
]
=
sp
->
rtoinfo
.
srto_initial
*
HZ
/
1000
;
MSECS_TO_JIFFIES
(
sp
->
rtoinfo
.
srto_initial
)
;
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_T3_RTX
]
=
0
;
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_T4_RTO
]
=
0
;
...
...
@@ -138,7 +138,7 @@ struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
* recommended value of 5 times 'RTO.Max'.
*/
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD
]
=
5
*
(
sp
->
rtoinfo
.
srto_max
*
HZ
/
1000
);
=
5
*
MSECS_TO_JIFFIES
(
sp
->
rtoinfo
.
srto_max
);
ep
->
timeouts
[
SCTP_EVENT_TIMEOUT_HEARTBEAT
]
=
SCTP_DEFAULT_TIMEOUT_HEARTBEAT
;
...
...
net/sctp/sm_make_chunk.c
View file @
67e6a91e
/* SCTP kernel reference Implementation
*
Copyright (C)
IBM Corp. 2001, 2003
*
(C) Copyright
IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 Intel Corp.
...
...
@@ -1288,7 +1288,7 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
/* Set an expiration time for the cookie. */
do_gettimeofday
(
&
cookie
->
c
.
expiration
);
tv_add
(
&
asoc
->
cookie_life
,
&
cookie
->
c
.
expiration
);
TIMEVAL_ADD
(
asoc
->
cookie_life
,
cookie
->
c
.
expiration
);
/* Copy the peer's init packet. */
memcpy
(
&
cookie
->
c
.
peer_init
[
0
],
init_chunk
->
chunk_hdr
,
...
...
@@ -2021,11 +2021,11 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
sctp_addiphdr_t
asconf
;
struct
sctp_chunk
*
retval
;
int
length
=
sizeof
(
asconf
)
+
vparam_len
;
union
sctp_
params
addrparam
;
union
sctp_
addr_param
addrparam
;
int
addrlen
;
struct
sctp_af
*
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
addrlen
=
af
->
to_addr_param
(
addr
,
(
union
sctp_addr_param
*
)
&
addrparam
);
addrlen
=
af
->
to_addr_param
(
addr
,
&
addrparam
);
if
(
!
addrlen
)
return
NULL
;
length
+=
addrlen
;
...
...
@@ -2045,6 +2045,83 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
return
retval
;
}
/* ADDIP
* 3.2.1 Add IP Address
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 0xC001 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ASCONF-Request Correlation ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Parameter |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 3.2.2 Delete IP Address
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 0xC002 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ASCONF-Request Correlation ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Parameter |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
struct
sctp_chunk
*
sctp_make_asconf_update_ip
(
struct
sctp_association
*
asoc
,
union
sctp_addr
*
laddr
,
struct
sockaddr
*
addrs
,
int
addrcnt
,
int
flags
)
{
sctp_addip_param_t
param
;
struct
sctp_chunk
*
retval
;
union
sctp_addr_param
addr_param
;
union
sctp_addr
*
addr
;
void
*
addr_buf
;
struct
sctp_af
*
af
;
int
paramlen
=
sizeof
(
param
);
int
addr_param_len
=
0
;
int
totallen
=
0
;
int
i
;
/* Get total length of all the address parameters. */
addr_buf
=
addrs
;
for
(
i
=
0
;
i
<
addrcnt
;
i
++
)
{
addr
=
(
union
sctp_addr
*
)
addr_buf
;
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
addr_param_len
=
af
->
to_addr_param
(
addr
,
&
addr_param
);
totallen
+=
paramlen
;
totallen
+=
addr_param_len
;
addr_buf
+=
af
->
sockaddr_len
;
}
/* Create an asconf chunk with the required length. */
retval
=
sctp_make_asconf
(
asoc
,
laddr
,
totallen
);
if
(
!
retval
)
return
NULL
;
/* Add the address parameters to the asconf chunk. */
addr_buf
=
addrs
;
for
(
i
=
0
;
i
<
addrcnt
;
i
++
)
{
addr
=
(
union
sctp_addr
*
)
addr_buf
;
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
addr_param_len
=
af
->
to_addr_param
(
addr
,
&
addr_param
);
param
.
param_hdr
.
type
=
flags
;
param
.
param_hdr
.
length
=
htons
(
paramlen
+
addr_param_len
);
param
.
crr_id
=
htonl
(
i
);
sctp_addto_chunk
(
retval
,
paramlen
,
&
param
);
sctp_addto_chunk
(
retval
,
addr_param_len
,
&
addr_param
);
addr_buf
+=
af
->
sockaddr_len
;
}
return
retval
;
}
/* ADDIP
* 3.2.4 Set Primary IP Address
* 0 1 2 3
...
...
@@ -2065,11 +2142,11 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
sctp_addip_param_t
param
;
struct
sctp_chunk
*
retval
;
int
len
=
sizeof
(
param
);
union
sctp_
params
addrparam
;
union
sctp_
addr_param
addrparam
;
int
addrlen
;
struct
sctp_af
*
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
addrlen
=
af
->
to_addr_param
(
addr
,
(
union
sctp_addr_param
*
)
&
addrparam
);
addrlen
=
af
->
to_addr_param
(
addr
,
&
addrparam
);
if
(
!
addrlen
)
return
NULL
;
len
+=
addrlen
;
...
...
@@ -2089,11 +2166,7 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
return
retval
;
}
/*
* Unpack the parameters in an ASCONF chunk into an association and
* generate ASCONF-ACK chunk.
*
* ADDIP 3.1.2 Address Configuration Acknowledgement Chunk (ASCONF-ACK)
/* ADDIP 3.1.2 Address Configuration Acknowledgement Chunk (ASCONF-ACK)
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
...
@@ -2110,8 +2183,28 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
* | ASCONF Parameter Response#N |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*
All the parameter respoinces will be added in this function.
*
Create an ASCONF_ACK chunk with enough space for the parameter responses.
*/
struct
sctp_chunk
*
sctp_make_asconf_ack
(
struct
sctp_association
*
asoc
,
int
serial
,
int
vparam_len
)
{
sctp_addiphdr_t
asconf
;
struct
sctp_chunk
*
retval
;
int
length
=
sizeof
(
asconf
)
+
vparam_len
;
/* Create the chunk. */
retval
=
sctp_make_chunk
(
asoc
,
SCTP_CID_ASCONF_ACK
,
0
,
length
);
if
(
!
retval
)
return
NULL
;
asconf
.
serial
=
serial
;
retval
->
subh
.
addip_hdr
=
sctp_addto_chunk
(
retval
,
sizeof
(
asconf
),
&
asconf
);
return
retval
;
}
struct
sctp_chunk
*
sctp_process_asconf
(
struct
sctp_association
*
asoc
,
struct
sctp_chunk
*
asconf
,
int
vparam_len
)
...
...
net/sctp/sm_sideeffect.c
View file @
67e6a91e
/* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
...
...
@@ -690,6 +690,44 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds,
chunk
->
transport
=
t
;
}
/* Process an incoming Operation Error Chunk. */
static
void
sctp_cmd_process_operr
(
sctp_cmd_seq_t
*
cmds
,
struct
sctp_association
*
asoc
,
struct
sctp_chunk
*
chunk
)
{
struct
sctp_operr_chunk
*
operr_chunk
;
struct
sctp_errhdr
*
err_hdr
;
operr_chunk
=
(
struct
sctp_operr_chunk
*
)
chunk
->
chunk_hdr
;
err_hdr
=
&
operr_chunk
->
err_hdr
;
switch
(
err_hdr
->
cause
)
{
case
SCTP_ERROR_UNKNOWN_CHUNK
:
{
struct
sctp_chunkhdr
*
unk_chunk_hdr
;
unk_chunk_hdr
=
(
struct
sctp_chunkhdr
*
)
err_hdr
->
variable
;
switch
(
unk_chunk_hdr
->
type
)
{
/* ADDIP 4.1 A9) If the peer responds to an ASCONF with an
* ERROR chunk reporting that it did not recognized the ASCONF
* chunk type, the sender of the ASCONF MUST NOT send any
* further ASCONF chunks and MUST stop its T-4 timer.
*/
case
SCTP_CID_ASCONF
:
asoc
->
peer
.
asconf_capable
=
0
;
sctp_add_cmd_sf
(
cmds
,
SCTP_CMD_TIMER_STOP
,
SCTP_TO
(
SCTP_EVENT_TIMEOUT_T4_RTO
));
break
;
default:
break
;
}
break
;
}
default:
break
;
}
}
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
* functionality there.
...
...
@@ -1205,6 +1243,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_setup_t4
(
commands
,
asoc
,
cmd
->
obj
.
ptr
);
break
;
case
SCTP_CMD_PROCESS_OPERR
:
sctp_cmd_process_operr
(
commands
,
asoc
,
chunk
);
break
;
default:
printk
(
KERN_WARNING
"Impossible command: %u, %p
\n
"
,
cmd
->
verb
,
cmd
->
obj
.
ptr
);
...
...
net/sctp/sm_statefuns.c
View file @
67e6a91e
/* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2002 Nokia Corp.
*
...
...
@@ -2864,6 +2864,9 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
sctp_ulpevent_free
(
ev
);
goto
nomem
;
}
sctp_add_cmd_sf
(
commands
,
SCTP_CMD_PROCESS_OPERR
,
SCTP_CHUNK
(
chunk
));
}
return
SCTP_DISPOSITION_CONSUME
;
...
...
net/sctp/socket.c
View file @
67e6a91e
...
...
@@ -97,6 +97,8 @@ static void sctp_wait_for_close(struct sock *sk, long timeo);
static
inline
int
sctp_verify_addr
(
struct
sock
*
,
union
sctp_addr
*
,
int
);
static
int
sctp_bindx_add
(
struct
sock
*
,
struct
sockaddr
*
,
int
);
static
int
sctp_bindx_rem
(
struct
sock
*
,
struct
sockaddr
*
,
int
);
static
int
sctp_send_asconf_add_ip
(
struct
sock
*
,
struct
sockaddr
*
,
int
);
static
int
sctp_send_asconf_del_ip
(
struct
sock
*
,
struct
sockaddr
*
,
int
);
static
int
sctp_do_bind
(
struct
sock
*
,
union
sctp_addr
*
,
int
);
static
int
sctp_autobind
(
struct
sock
*
sk
);
static
void
sctp_sock_migrate
(
struct
sock
*
,
struct
sock
*
,
...
...
@@ -349,6 +351,106 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
return
retval
;
}
/* Send an ASCONF chunk with Add IP address parameters to all the peers of the
* associations that are part of the endpoint indicating that a list of local
* addresses are added to the endpoint.
*
* If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
* Only sctp_setsockopt_bindx() is supposed to call this function.
*/
static
int
sctp_send_asconf_add_ip
(
struct
sock
*
sk
,
struct
sockaddr
*
addrs
,
int
addrcnt
)
{
struct
sctp_opt
*
sp
;
struct
sctp_endpoint
*
ep
;
struct
sctp_association
*
asoc
;
struct
sctp_bind_addr
*
bp
;
struct
sctp_chunk
*
chunk
;
struct
sctp_sockaddr_entry
*
laddr
;
union
sctp_addr
*
addr
;
void
*
addr_buf
;
struct
sctp_af
*
af
;
struct
list_head
*
pos
;
struct
list_head
*
p
;
int
i
;
int
retval
=
0
;
sp
=
sctp_sk
(
sk
);
ep
=
sp
->
ep
;
SCTP_DEBUG_PRINTK
(
"%s: (sk: %p, addrs: %p, addrcnt: %d)
\n
"
,
__FUNCTION__
,
sk
,
addrs
,
addrcnt
);
list_for_each
(
pos
,
&
ep
->
asocs
)
{
asoc
=
list_entry
(
pos
,
struct
sctp_association
,
asocs
);
if
(
!
sctp_state
(
asoc
,
ESTABLISHED
))
continue
;
if
(
!
asoc
->
peer
.
asconf_capable
)
continue
;
/* Check if any address in the packed array of addresses is
* in the bind address list of the association. If so,
* do not send the asconf chunk to its peer, but continue with
* other associations.
*/
addr_buf
=
addrs
;
for
(
i
=
0
;
i
<
addrcnt
;
i
++
)
{
addr
=
(
union
sctp_addr
*
)
addr_buf
;
af
=
sctp_get_af_specific
(
addr
->
v4
.
sin_family
);
if
(
!
af
)
{
retval
=
-
EINVAL
;
goto
out
;
}
if
(
sctp_assoc_lookup_laddr
(
asoc
,
addr
))
break
;
addr_buf
+=
af
->
sockaddr_len
;
}
if
(
i
<
addrcnt
)
continue
;
/* Use the first address in bind addr list of association as
* Address Parameter of ASCONF CHUNK.
*/
sctp_read_lock
(
&
asoc
->
base
.
addr_lock
);
bp
=
&
asoc
->
base
.
bind_addr
;
p
=
bp
->
address_list
.
next
;
laddr
=
list_entry
(
p
,
struct
sctp_sockaddr_entry
,
list
);
sctp_read_unlock
(
&
asoc
->
base
.
addr_lock
);
chunk
=
sctp_make_asconf_update_ip
(
asoc
,
&
laddr
->
a
,
addrs
,
addrcnt
,
SCTP_PARAM_ADD_IP
);
if
(
!
chunk
)
{
retval
=
-
ENOMEM
;
goto
out
;
}
retval
=
sctp_primitive_ASCONF
(
asoc
,
chunk
);
if
(
retval
)
{
sctp_chunk_free
(
chunk
);
goto
out
;
}
/* FIXME: After sending the add address ASCONF chunk, we
* cannot append the address to the association's binding
* address list, because the new address may be used as the
* source of a message sent to the peer before the ASCONF
* chunk is received by the peer. So we should wait until
* ASCONF_ACK is received.
*/
}
out:
return
retval
;
}
/* Remove a list of addresses from bind addresses list. Do not remove the
* last address.
*
...
...
@@ -436,6 +538,106 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
return
retval
;
}
/* Send an ASCONF chunk with Delete IP address parameters to all the peers of
* the associations that are part of the endpoint indicating that a list of
* local addresses are removed from the endpoint.
*
* If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
* Only sctp_setsockopt_bindx() is supposed to call this function.
*/
static
int
sctp_send_asconf_del_ip
(
struct
sock
*
sk
,
struct
sockaddr
*
addrs
,
int
addrcnt
)
{
struct
sctp_opt
*
sp
;
struct
sctp_endpoint
*
ep
;
struct
sctp_association
*
asoc
;
struct
sctp_bind_addr
*
bp
;
struct
sctp_chunk
*
chunk
;
union
sctp_addr
*
laddr
;
void
*
addr_buf
;
struct
sctp_af
*
af
;
struct
list_head
*
pos
;
int
i
;
int
retval
=
0
;
sp
=
sctp_sk
(
sk
);
ep
=
sp
->
ep
;
SCTP_DEBUG_PRINTK
(
"%s: (sk: %p, addrs: %p, addrcnt: %d)
\n
"
,
__FUNCTION__
,
sk
,
addrs
,
addrcnt
);
list_for_each
(
pos
,
&
ep
->
asocs
)
{
asoc
=
list_entry
(
pos
,
struct
sctp_association
,
asocs
);
if
(
!
sctp_state
(
asoc
,
ESTABLISHED
))
continue
;
if
(
!
asoc
->
peer
.
asconf_capable
)
continue
;
/* Check if any address in the packed array of addresses is
* not present in the bind address list of the association.
* If so, do not send the asconf chunk to its peer, but
* continue with other associations.
*/
addr_buf
=
addrs
;
for
(
i
=
0
;
i
<
addrcnt
;
i
++
)
{
laddr
=
(
union
sctp_addr
*
)
addr_buf
;
af
=
sctp_get_af_specific
(
laddr
->
v4
.
sin_family
);
if
(
!
af
)
{
retval
=
-
EINVAL
;
goto
out
;
}
if
(
!
sctp_assoc_lookup_laddr
(
asoc
,
laddr
))
break
;
addr_buf
+=
af
->
sockaddr_len
;
}
if
(
i
<
addrcnt
)
continue
;
/* Find one address in the association's bind address list
* that is not in the packed array of addresses. This is to
* make sure that we do not delete all the addresses in the
* association.
*/
sctp_read_lock
(
&
asoc
->
base
.
addr_lock
);
bp
=
&
asoc
->
base
.
bind_addr
;
laddr
=
sctp_find_unmatch_addr
(
bp
,
(
union
sctp_addr
*
)
addrs
,
addrcnt
,
sp
);
sctp_read_unlock
(
&
asoc
->
base
.
addr_lock
);
if
(
!
laddr
)
continue
;
chunk
=
sctp_make_asconf_update_ip
(
asoc
,
laddr
,
addrs
,
addrcnt
,
SCTP_PARAM_DEL_IP
);
if
(
!
chunk
)
{
retval
=
-
ENOMEM
;
goto
out
;
}
retval
=
sctp_primitive_ASCONF
(
asoc
,
chunk
);
if
(
retval
)
{
sctp_chunk_free
(
chunk
);
goto
out
;
}
/* FIXME: After sending the delete address ASCONF chunk, we
* cannot remove the addresses from the association's bind
* address list, because there maybe some packet send to
* the delete addresses, so we should wait until ASCONF_ACK
* packet is received.
*/
}
out:
return
retval
;
}
/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
*
* API 8.1
...
...
@@ -564,10 +766,16 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr *addrs,
switch
(
op
)
{
case
SCTP_BINDX_ADD_ADDR
:
err
=
sctp_bindx_add
(
sk
,
kaddrs
,
addrcnt
);
if
(
err
)
goto
out
;
err
=
sctp_send_asconf_add_ip
(
sk
,
kaddrs
,
addrcnt
);
break
;
case
SCTP_BINDX_REM_ADDR
:
err
=
sctp_bindx_rem
(
sk
,
kaddrs
,
addrcnt
);
if
(
err
)
goto
out
;
err
=
sctp_send_asconf_del_ip
(
sk
,
kaddrs
,
addrcnt
);
break
;
default:
...
...
@@ -575,6 +783,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr *addrs,
break
;
};
out:
kfree
(
kaddrs
);
return
err
;
...
...
@@ -962,8 +1171,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
=
sinit
->
sinit_max_attempts
;
}
if
(
sinit
->
sinit_max_init_timeo
)
{
asoc
->
max_init_timeo
=
sinit
->
sinit_max_init_timeo
*
HZ
;
asoc
->
max_init_timeo
=
MSECS_TO_JIFFIES
(
sinit
->
sinit_max_init_timeo
)
;
}
}
...
...
@@ -1401,7 +1610,8 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
*/
if
(
params
.
spp_hbinterval
)
{
trans
->
hb_allowed
=
1
;
trans
->
hb_interval
=
params
.
spp_hbinterval
*
HZ
/
1000
;
trans
->
hb_interval
=
MSECS_TO_JIFFIES
(
params
.
spp_hbinterval
);
}
else
trans
->
hb_allowed
=
0
;
}
...
...
@@ -1560,11 +1770,12 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char *optval, int optlen) {
if
(
asoc
)
{
if
(
rtoinfo
.
srto_initial
!=
0
)
asoc
->
rto_initial
=
rtoinfo
.
srto_initial
*
HZ
/
1000
;
asoc
->
rto_initial
=
MSECS_TO_JIFFIES
(
rtoinfo
.
srto_initial
);
if
(
rtoinfo
.
srto_max
!=
0
)
asoc
->
rto_max
=
rtoinfo
.
srto_max
*
HZ
/
1000
;
asoc
->
rto_max
=
MSECS_TO_JIFFIES
(
rtoinfo
.
srto_max
)
;
if
(
rtoinfo
.
srto_min
!=
0
)
asoc
->
rto_min
=
rtoinfo
.
srto_min
*
HZ
/
1000
;
asoc
->
rto_min
=
MSECS_TO_JIFFIES
(
rtoinfo
.
srto_min
)
;
}
else
{
/* If there is no association or the association-id = 0
* set the values to the endpoint.
...
...
@@ -2088,14 +2299,14 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp
->
initmsg
.
sinit_num_ostreams
=
sctp_max_outstreams
;
sp
->
initmsg
.
sinit_max_instreams
=
sctp_max_instreams
;
sp
->
initmsg
.
sinit_max_attempts
=
sctp_max_retrans_init
;
sp
->
initmsg
.
sinit_max_init_timeo
=
(
sctp_rto_max
/
HZ
)
*
1000
;
sp
->
initmsg
.
sinit_max_init_timeo
=
JIFFIES_TO_MSECS
(
sctp_rto_max
)
;
/* Initialize default RTO related parameters. These parameters can
* be modified for with the SCTP_RTOINFO socket option.
*/
sp
->
rtoinfo
.
srto_initial
=
(
sctp_rto_initial
/
HZ
)
*
1000
;
sp
->
rtoinfo
.
srto_max
=
(
sctp_rto_max
/
HZ
)
*
1000
;
sp
->
rtoinfo
.
srto_min
=
(
sctp_rto_min
/
HZ
)
*
1000
;
sp
->
rtoinfo
.
srto_initial
=
JIFFIES_TO_MSECS
(
sctp_rto_initial
)
;
sp
->
rtoinfo
.
srto_max
=
JIFFIES_TO_MSECS
(
sctp_rto_max
)
;
sp
->
rtoinfo
.
srto_min
=
JIFFIES_TO_MSECS
(
sctp_rto_min
)
;
/* Initialize default association related parameters. These parameters
* can be modified with the SCTP_ASSOCINFO socket option.
...
...
@@ -2104,8 +2315,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp
->
assocparams
.
sasoc_number_peer_destinations
=
0
;
sp
->
assocparams
.
sasoc_peer_rwnd
=
0
;
sp
->
assocparams
.
sasoc_local_rwnd
=
0
;
sp
->
assocparams
.
sasoc_cookie_life
=
(
sctp_valid_cookie_life
/
HZ
)
*
1000
;
sp
->
assocparams
.
sasoc_cookie_life
=
JIFFIES_TO_MSECS
(
sctp_valid_cookie_life
)
;
/* Initialize default event subscriptions. By default, all the
* options are off.
...
...
@@ -2115,7 +2326,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Default Peer Address Parameters. These defaults can
* be modified via SCTP_PEER_ADDR_PARAMS
*/
sp
->
paddrparam
.
spp_hbinterval
=
(
sctp_hb_interval
/
HZ
)
*
1000
;
sp
->
paddrparam
.
spp_hbinterval
=
JIFFIES_TO_MSECS
(
sctp_hb_interval
)
;
sp
->
paddrparam
.
spp_pathmaxrxt
=
sctp_max_retrans_path
;
/* If enabled no SCTP message fragmentation will be performed.
...
...
@@ -2265,7 +2476,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status
.
sstat_primary
.
spinfo_state
=
transport
->
active
;
status
.
sstat_primary
.
spinfo_cwnd
=
transport
->
cwnd
;
status
.
sstat_primary
.
spinfo_srtt
=
transport
->
srtt
;
status
.
sstat_primary
.
spinfo_rto
=
(
transport
->
rto
/
HZ
)
*
1000
;
status
.
sstat_primary
.
spinfo_rto
=
JIFFIES_TO_MSECS
(
transport
->
rto
)
;
status
.
sstat_primary
.
spinfo_mtu
=
transport
->
pmtu
;
if
(
put_user
(
len
,
optlen
))
{
...
...
@@ -2320,7 +2531,7 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
pinfo
.
spinfo_state
=
transport
->
active
;
pinfo
.
spinfo_cwnd
=
transport
->
cwnd
;
pinfo
.
spinfo_srtt
=
transport
->
srtt
;
pinfo
.
spinfo_rto
=
(
transport
->
rto
/
HZ
)
*
1000
;
pinfo
.
spinfo_rto
=
JIFFIES_TO_MSECS
(
transport
->
rto
)
;
pinfo
.
spinfo_mtu
=
transport
->
pmtu
;
if
(
put_user
(
len
,
optlen
))
{
...
...
@@ -2524,7 +2735,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
if
(
!
trans
->
hb_allowed
)
params
.
spp_hbinterval
=
0
;
else
params
.
spp_hbinterval
=
trans
->
hb_interval
*
1000
/
HZ
;
params
.
spp_hbinterval
=
JIFFIES_TO_MSECS
(
trans
->
hb_interval
)
;
/* spp_pathmaxrxt contains the maximum number of retransmissions
* before this address shall be considered unreachable.
...
...
@@ -2582,10 +2793,8 @@ static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
list_for_each
(
pos
,
&
asoc
->
peer
.
transport_addr_list
)
{
cnt
++
;
}
if
(
copy_to_user
(
optval
,
&
cnt
,
sizeof
(
int
)))
return
-
EFAULT
;
return
0
;
return
cnt
;
}
static
int
sctp_getsockopt_peer_addrs
(
struct
sock
*
sk
,
int
len
,
...
...
@@ -2666,10 +2875,8 @@ static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
list_for_each
(
pos
,
&
bp
->
address_list
)
{
cnt
++
;
}
if
(
copy_to_user
(
optval
,
&
cnt
,
sizeof
(
int
)))
return
-
EFAULT
;
return
0
;
return
cnt
;
}
static
int
sctp_getsockopt_local_addrs
(
struct
sock
*
sk
,
int
len
,
...
...
@@ -2879,9 +3086,9 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len, char *optval,
/* Values corresponding to the specific association. */
if
(
asoc
)
{
rtoinfo
.
srto_initial
=
(
asoc
->
rto_initial
/
HZ
)
*
1000
;
rtoinfo
.
srto_max
=
(
asoc
->
rto_max
/
HZ
)
*
1000
;
rtoinfo
.
srto_min
=
(
asoc
->
rto_min
/
HZ
)
*
1000
;
rtoinfo
.
srto_initial
=
JIFFIES_TO_MSECS
(
asoc
->
rto_initial
)
;
rtoinfo
.
srto_max
=
JIFFIES_TO_MSECS
(
asoc
->
rto_max
)
;
rtoinfo
.
srto_min
=
JIFFIES_TO_MSECS
(
asoc
->
rto_min
)
;
}
else
{
/* Values corresponding to the endpoint. */
struct
sctp_opt
*
sp
=
sctp_sk
(
sk
);
...
...
net/sctp/sysctl.c
View file @
67e6a91e
...
...
@@ -44,7 +44,7 @@
#include <linux/sysctl.h>
static
ctl_handler
sctp_sysctl_jiffies_ms
;
static
long
rto_timer_min
=
0
;
static
long
rto_timer_min
=
1
;
static
long
rto_timer_max
=
86400000
;
/* One day */
static
ctl_table
sctp_table
[]
=
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment