Commit 125903c5 authored by Sridhar Samudrala's avatar Sridhar Samudrala

Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/linux-2.5.43

into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.43
parents 5a7728c6 33db3af1
......@@ -139,6 +139,7 @@ sctp_state_fn_t sctp_sf_do_5_2_1_siminit;
sctp_state_fn_t sctp_sf_do_5_2_2_dupinit;
sctp_state_fn_t sctp_sf_do_5_2_4_dupcook;
sctp_state_fn_t sctp_sf_unk_chunk;
sctp_state_fn_t sctp_sf_do_8_5_1_E_sa;
/* Prototypes for primitive event state functions. */
sctp_state_fn_t sctp_sf_do_prm_asoc;
......@@ -329,10 +330,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */
void sctp_param2sockaddr(sockaddr_storage_t *addr, const sctpParam_t param,
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *,
__u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctpParam_t);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *);
/* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
......@@ -432,4 +433,36 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_a
BUG();
}
/* Check VTAG of the packet matches the sender's own tag OR its peer's
* tag and the T bit is set in the Chunk Flags.
*/
static inline int
sctp_vtag_verify_either(const sctp_chunk_t *chunk,
const sctp_association_t *asoc)
{
/* RFC 2960 Section 8.5.1, sctpimpguide-06 Section 2.13.2
*
* B) The receiver of a ABORT shall accept the packet if the
* Verification Tag field of the packet matches its own tag OR it
* is set to its peer's tag and the T bit is set in the Chunk
* Flags. Otherwise, the receiver MUST silently discard the packet
* and take no further action.
*
* (C) The receiver of a SHUTDOWN COMPLETE shall accept the
* packet if the Verification Tag field of the packet
* matches its own tag OR it is set to its peer's tag and
* the T bit is set in the Chunk Flags. Otherwise, the
* receiver MUST silently discard the packet and take no
* further action....
*
*/
if ((ntohl(chunk->sctp_hdr->vtag) == asoc->c.my_vtag) ||
(sctp_test_T_bit(chunk) && (ntohl(chunk->sctp_hdr->vtag)
== asoc->c.peer_vtag))) {
return 1;
}
return 0;
}
#endif /* __sctp_sm_h__ */
......@@ -378,7 +378,7 @@ typedef union {
typedef union {
sctp_ipv4addr_param_t v4;
sctp_ipv6addr_param_t v6;
} sctpIpAddress_t;
} sctp_addr_param_t;
/* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length
......@@ -1044,6 +1044,9 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_t *);
int sctp_has_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr);
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
......
......@@ -476,13 +476,6 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.retran_path = peer;
}
/* If we do not yet have a primary path, set one. */
if (NULL == asoc->peer.primary_path) {
asoc->peer.primary_path = peer;
asoc->peer.active_path = peer;
asoc->peer.retran_path = peer;
}
if (asoc->peer.active_path == asoc->peer.retran_path)
asoc->peer.retran_path = peer;
......
......@@ -199,11 +199,10 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority)
{
sctpParam_t rawaddr;
sctpParam_t addrparms;
sctpParam_t retval;
int addrparms_len;
sctpIpAddress_t rawaddr_space;
sctp_addr_param_t rawaddr;
int len;
struct sockaddr_storage_list *addr;
struct list_head *pos;
......@@ -214,7 +213,7 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
/* Allocate enough memory at once. */
list_for_each(pos, &bp->address_list) {
len += sizeof(sctp_ipv6addr_param_t);
len += sizeof(sctp_addr_param_t);
}
addrparms.v = kmalloc(len, priority);
......@@ -222,12 +221,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
goto end_raw;
retval = addrparms;
rawaddr.v4 = &rawaddr_space.v4;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
len = sockaddr2sctp_addr(&addr->a, rawaddr);
memcpy(addrparms.v, rawaddr.v, len);
len = sockaddr2sctp_addr(&addr->a, &rawaddr);
memcpy(addrparms.v, &rawaddr, len);
addrparms.v += len;
addrparms_len += len;
}
......@@ -244,33 +242,39 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
int addrs_len, __u16 port, int priority)
{
sctpParam_t rawaddr;
sctp_addr_param_t *rawaddr;
sctp_paramhdr_t *param;
sockaddr_storage_t addr;
int retval = 0;
int len;
/* Convert the raw address to standard address format */
while (addrs_len) {
rawaddr.v = raw_addr_list;
if (SCTP_PARAM_IPV4_ADDRESS==rawaddr.p->type
|| SCTP_PARAM_IPV6_ADDRESS==rawaddr.p->type) {
param = (sctp_paramhdr_t *)raw_addr_list;
rawaddr = (sctp_addr_param_t *)raw_addr_list;
switch (param->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
break;
break;;
}
len = ntohs(rawaddr.p->length);
len = ntohs(param->length);
addrs_len -= len;
raw_addr_list += len;
} else {
break;
default:
/* Corrupted raw addr list! */
retval = -EINVAL;
sctp_bind_addr_clean(bp);
break;
}
if (retval)
break;
}
return retval;
......
......@@ -522,7 +522,7 @@ void __sctp_unhash_established(sctp_association_t *asoc)
}
/* Look up an association. */
sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr,
sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr,
sctp_transport_t **transportp)
{
......@@ -557,6 +557,36 @@ sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *ladd
return asoc;
}
/* Look up an association. BH-safe. */
sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr,
sctp_transport_t **transportp)
{
sctp_association_t *asoc;
sctp_local_bh_disable();
asoc = __sctp_lookup_association(laddr, paddr, transportp);
sctp_local_bh_enable();
return asoc;
}
/* Is there an association matching the given local and peer addresses? */
int sctp_has_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr)
{
sctp_association_t *asoc;
sctp_transport_t *transport;
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
sock_put(asoc->base.sk);
sctp_association_put(asoc);
return 1;
}
return 0;
}
/*
* SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK.
......@@ -584,14 +614,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
__u8 *ch_end, *data;
sctpParam_t parm;
sctp_paramhdr_t *parm;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
if (SCTP_CID_INIT_ACK != ch->type)
/* If this is INIT/INIT-ACK look inside the chunk too. */
switch (ch->type) {
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
break;
default:
return NULL;
}
/*
* This code will NOT touch anything inside the chunk--it is
......@@ -609,26 +645,25 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters.
*/
data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
parm.v = data;
parm = (sctp_paramhdr_t *)data;
if (!parm.p->length)
if (!parm->length)
break;
data += WORD_ROUND(ntohs(parm.p->length));
data += WORD_ROUND(ntohs(parm->length));
/* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm.p->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm.p->type))
if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm->type))
continue;
sctp_param2sockaddr(paddr, parm, ntohs(sh->source));
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
ntohs(sh->source));
asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
}
......@@ -644,7 +679,7 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
{
sctp_association_t *asoc;
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
asoc = __sctp_lookup_association(laddr, paddr, transportp);
/* Further lookup for INIT-ACK packet.
* SCTP Implementors Guide, 2.18 Handling of address
......
......@@ -530,10 +530,18 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* to a given transport address if it has cwnd or more bytes
* of data outstanding to that transport address.
*/
/* RFC 7.2.4 & the Implementers Guide 2.8.
*
* 3) ...
* When a Fast Retransmit is being performed the sender SHOULD
* ignore the value of cwnd and SHOULD NOT delay retransmission.
*/
if (!chunk->fast_retransmit) {
if (transport->flight_size >= transport->cwnd) {
retval = SCTP_XMIT_RWND_FULL;
goto finish;
}
}
/* Keep track of how many bytes are in flight over this transport. */
transport->flight_size += datasize;
......
......@@ -1710,6 +1710,7 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
sctp_cid_t cid, int priority)
{
sockaddr_storage_t addr;
sctp_addr_param_t *addrparm;
int j;
int i;
int retval = 1;
......@@ -1721,24 +1722,23 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
if (SCTP_CID_INIT != cid) {
sctp_param2sockaddr(&addr, param, asoc->peer.port);
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
case SCTP_PARAM_IPV6_ADDRESS:
if (SCTP_CID_INIT != cid) {
/* Rethink this as we may need to keep for
* restart considerations.
*/
if (PF_INET6 == asoc->base.sk->family) {
sctp_param2sockaddr(&addr, param,
asoc->peer.port);
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr,
priority);
}
sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
......@@ -1833,7 +1833,6 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *ep)
/* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
{
/* I believe that this random number generator complies with RFC1750. */
__u32 retval;
get_random_bytes(&retval, sizeof(__u32));
......@@ -1845,26 +1844,27 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctpParam_t param, __u16 port)
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
__u16 port)
{
switch(param.p->type) {
switch(param->v4.param_hdr.type) {
case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param.v4->addr.s_addr;
addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
break;
case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
addr->v6.sin6_addr = param.v6->addr;
addr->v6.sin6_addr = param->v6.addr;
addr->v6.sin6_scope_id = 0; /* BUG */
break;
default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n",
ntohs(param.p->type));
ntohs(param->v4.param_hdr.type));
break;
};
}
......@@ -1904,11 +1904,9 @@ int ipver2af(__u8 ipver)
case 4:
family = AF_INET;
break;
case 6:
family = AF_INET6;
break;
default:
family = 0;
break;
......@@ -1917,25 +1915,26 @@ int ipver2af(__u8 ipver)
return family;
}
/* Convert a sockaddr_in to IP address in an SCTP para. */
/* Returns true if a valid conversion was possible. */
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctpParam_t p)
/* Convert a sockaddr_in to an IP address in an SCTP param.
* Returns len if a valid conversion was possible.
*/
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p)
{
int len = 0;
switch (sa->v4.sin_family) {
case AF_INET:
p.p->type = SCTP_PARAM_IPV4_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv4addr_param_t));
p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = sizeof(sctp_ipv4addr_param_t);
p.v4->addr.s_addr = sa->v4.sin_addr.s_addr;
p->v4.addr.s_addr = sa->v4.sin_addr.s_addr;
break;
case AF_INET6:
p.p->type = SCTP_PARAM_IPV6_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv6addr_param_t));
p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = sizeof(sctp_ipv6addr_param_t);
p.v6->addr = *(&sa->v6.sin6_addr);
p->v6.addr = *(&sa->v6.sin6_addr);
break;
default:
......
This diff is collapsed.
......@@ -271,9 +271,9 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
{.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
{.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
......
......@@ -726,9 +726,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
* flags - flags sent or received with the user message, see Section
* 5 for complete description of the flags.
*
* NB: The argument 'msg' is a user space address.
* Note: This function could use a rewrite especially when explicit
* connect support comes in.
*/
/* BUG: We do not implement timeouts. */
/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
......@@ -738,7 +738,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
{
sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *asoc = NULL;
sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL;
sockaddr_storage_t to;
......@@ -821,7 +821,32 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* If a msg_name has been specified, assume this is to be used. */
if (msg_name) {
/* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) {
struct list_head *pos;
struct sockaddr_storage_list *addr;
sctp_bind_addr_t *bp = &ep->base.bind_addr;
sctp_read_lock(&ep->base.addr_lock);
/* If we could not find a matching association on
* the endpoint, make sure that there is no peeled-
* off association.
*/
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos,
struct sockaddr_storage_list,
list);
if (sctp_has_association(&addr->a, &to)) {
err = -EINVAL;
sctp_read_unlock(&ep->base.addr_lock);
goto out_unlock;
}
}
sctp_read_unlock(&ep->base.addr_lock);
}
} else {
/* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO.
......@@ -907,11 +932,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
scope = sctp_scope(&to);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc) {
new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!new_asoc) {
err = -ENOMEM;
goto out_unlock;
}
asoc = new_asoc;
/* If the SCTP_INIT ancillary data is specified, set all
* the association init values accordingly.
......@@ -946,7 +972,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
/* ASSERT: we have a valid association at this point. */
SCTP_DEBUG_PRINTK("We have a valid association. \n");
SCTP_DEBUG_PRINTK("We have a valid association.\n");
/* API 7.1.7, the sndbuf size per association bounds the
* maximum size of data that can be sent in a single send call.
......@@ -1054,10 +1080,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
err = msg_len;
goto out_unlock;
}
/* If we are already past ASSOCIATE, the lower
* layers are responsible for its cleanup.
*/
goto out_free_chunk;
out_free:
if (SCTP_STATE_CLOSED == asoc->state)
if (new_asoc)
sctp_association_free(asoc);
out_free_chunk:
if (chunk)
sctp_free_chunk(chunk);
......@@ -1577,6 +1609,8 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
sctp_endpoint_t *newep;
sctp_opt_t *oldsp = sctp_sk(oldsk);
sctp_opt_t *newsp;
struct sk_buff *skb, *tmp;
sctp_ulpevent_t *event;
int err = 0;
/* An association cannot be branched off from an already peeled-off
......@@ -1606,6 +1640,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
*/
newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = (sctp_ulpevent_t *)skb->cb;
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Set the type of socket to indicate that it is peeled off from the
* original socket.
*/
......@@ -1623,39 +1668,46 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
{
sctp_peeloff_arg_t peeloff;
struct socket *newsock;
int err, sd;
int retval = 0;
sctp_association_t *assoc;
if (len != sizeof(sctp_peeloff_arg_t))
return -EINVAL;
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
sctp_lock_sock(sk);
assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc)
return -EINVAL;
if (NULL == assoc) {
retval = -EINVAL;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
err = sctp_do_peeloff(assoc, &newsock);
if (err < 0)
return err;
retval = sctp_do_peeloff(assoc, &newsock);
if (retval < 0)
goto out_unlock;
/* Map the socket to an unused fd that can be returned to the user. */
sd = sock_map_fd(newsock);
if (sd < 0) {
retval = sock_map_fd(newsock);
if (retval < 0) {
sock_release(newsock);
return sd;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
__FUNCTION__, sk, assoc, newsock->sk, sd);
__FUNCTION__, sk, assoc, newsock->sk, retval);
/* Return the fd mapped to the new socket. */
peeloff.sd = sd;
peeloff.sd = retval;
if (copy_to_user(optval, &peeloff, len))
return -EFAULT;
retval = -EFAULT;
return 0;
out_unlock:
sctp_release_sock(sk);
return retval;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment