提交 07d93967 编写于 作者: V Vlad Yasevich 提交者: David S. Miller

[SCTP]: Set assoc_id correctly during INIT collision.

During the INIT/COOKIE-ACK collision cases, it's possible to get
into a situation where the association id is not yet set at the time
of the user event generation.  As a result, user events have an
association id set to 0 which will confuse applications.

This happens if we hit case B of duplicate cookie processing.
In the particular example found and provided by Oscar Isaula
<Oscar.Isaula@motorola.com>, flow looks like this:
A				B
---- INIT------->  (lost)
	    <---------INIT------
---- INIT-ACK--->
	    <------ Cookie ECHO

When the Cookie Echo is received, we end up trying to update the
association that was created on A as a result of the (lost) INIT,
but that association doesn't have the ID set yet.
Signed-off-by: NVlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 827bf122
...@@ -100,6 +100,8 @@ typedef enum { ...@@ -100,6 +100,8 @@ typedef enum {
SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */
SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */
SCTP_CMD_SET_SK_ERR, /* Set sk_err */ SCTP_CMD_SET_SK_ERR, /* Set sk_err */
SCTP_CMD_ASSOC_CHANGE, /* generate and send assoc_change event */
SCTP_CMD_ADAPTATION_IND, /* generate and send adaptation event */
SCTP_CMD_LAST SCTP_CMD_LAST
} sctp_verb_t; } sctp_verb_t;
......
...@@ -1857,6 +1857,7 @@ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, ...@@ -1857,6 +1857,7 @@ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *,
int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *,
struct sctp_cookie*, struct sctp_cookie*,
gfp_t gfp); gfp_t gfp);
int sctp_assoc_set_id(struct sctp_association *, gfp_t);
int sctp_cmp_addr_exact(const union sctp_addr *ss1, int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const union sctp_addr *ss2); const union sctp_addr *ss2);
......
...@@ -1103,6 +1103,13 @@ void sctp_assoc_update(struct sctp_association *asoc, ...@@ -1103,6 +1103,13 @@ void sctp_assoc_update(struct sctp_association *asoc,
asoc->ssnmap = new->ssnmap; asoc->ssnmap = new->ssnmap;
new->ssnmap = NULL; new->ssnmap = NULL;
} }
if (!asoc->assoc_id) {
/* get a new association id since we don't have one
* yet.
*/
sctp_assoc_set_id(asoc, GFP_ATOMIC);
}
} }
} }
...@@ -1375,3 +1382,25 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, ...@@ -1375,3 +1382,25 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
sctp_read_unlock(&asoc->base.addr_lock); sctp_read_unlock(&asoc->base.addr_lock);
return found; return found;
} }
/* Set an association id for a given association */
int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
{
int assoc_id;
int error = 0;
retry:
if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))
return -ENOMEM;
spin_lock_bh(&sctp_assocs_id_lock);
error = idr_get_new_above(&sctp_assocs_id, (void *)asoc,
1, &assoc_id);
spin_unlock_bh(&sctp_assocs_id_lock);
if (error == -EAGAIN)
goto retry;
else if (error)
return error;
asoc->assoc_id = (sctp_assoc_t) assoc_id;
return error;
}
...@@ -1939,7 +1939,6 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1939,7 +1939,6 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
* association. * association.
*/ */
if (!asoc->temp) { if (!asoc->temp) {
int assoc_id;
int error; int error;
asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams, asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams,
...@@ -1947,19 +1946,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1947,19 +1946,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
if (!asoc->ssnmap) if (!asoc->ssnmap)
goto clean_up; goto clean_up;
retry: error = sctp_assoc_set_id(asoc, gfp);
if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) if (error)
goto clean_up; goto clean_up;
spin_lock_bh(&sctp_assocs_id_lock);
error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1,
&assoc_id);
spin_unlock_bh(&sctp_assocs_id_lock);
if (error == -EAGAIN)
goto retry;
else if (error)
goto clean_up;
asoc->assoc_id = (sctp_assoc_t) assoc_id;
} }
/* ADDIP Section 4.1 ASCONF Chunk Procedures /* ADDIP Section 4.1 ASCONF Chunk Procedures
......
...@@ -862,6 +862,33 @@ static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error) ...@@ -862,6 +862,33 @@ static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error)
sk->sk_err = error; sk->sk_err = error;
} }
/* Helper function to generate an association change event */
static void sctp_cmd_assoc_change(sctp_cmd_seq_t *commands,
struct sctp_association *asoc,
u8 state)
{
struct sctp_ulpevent *ev;
ev = sctp_ulpevent_make_assoc_change(asoc, 0, state, 0,
asoc->c.sinit_num_ostreams,
asoc->c.sinit_max_instreams,
NULL, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&asoc->ulpq, ev);
}
/* Helper function to generate an adaptation indication event */
static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands,
struct sctp_association *asoc)
{
struct sctp_ulpevent *ev;
ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&asoc->ulpq, ev);
}
/* These three macros allow us to pull the debugging code out of the /* 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 * main flow of sctp_do_sm() to keep attention focused on the real
* functionality there. * functionality there.
...@@ -1485,6 +1512,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1485,6 +1512,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_SET_SK_ERR: case SCTP_CMD_SET_SK_ERR:
sctp_cmd_set_sk_err(asoc, cmd->obj.error); sctp_cmd_set_sk_err(asoc, cmd->obj.error);
break; break;
case SCTP_CMD_ASSOC_CHANGE:
sctp_cmd_assoc_change(commands, asoc,
cmd->obj.u8);
break;
case SCTP_CMD_ADAPTATION_IND:
sctp_cmd_adaptation_ind(commands, asoc);
break;
default: default:
printk(KERN_WARNING "Impossible command: %u, %p\n", printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr); cmd->verb, cmd->obj.ptr);
......
...@@ -1656,7 +1656,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, ...@@ -1656,7 +1656,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
struct sctp_association *new_asoc) struct sctp_association *new_asoc)
{ {
sctp_init_chunk_t *peer_init; sctp_init_chunk_t *peer_init;
struct sctp_ulpevent *ev;
struct sctp_chunk *repl; struct sctp_chunk *repl;
/* new_asoc is a brand-new association, so these are not yet /* new_asoc is a brand-new association, so these are not yet
...@@ -1687,34 +1686,28 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, ...@@ -1687,34 +1686,28 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
* D) IMPLEMENTATION NOTE: An implementation may choose to * D) IMPLEMENTATION NOTE: An implementation may choose to
* send the Communication Up notification to the SCTP user * send the Communication Up notification to the SCTP user
* upon reception of a valid COOKIE ECHO chunk. * upon reception of a valid COOKIE ECHO chunk.
*
* Sadly, this needs to be implemented as a side-effect, because
* we are not guaranteed to have set the association id of the real
* association and so these notifications need to be delayed until
* the association id is allocated.
*/ */
ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0,
new_asoc->c.sinit_num_ostreams,
new_asoc->c.sinit_max_instreams,
NULL, GFP_ATOMIC);
if (!ev)
goto nomem_ev;
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_CHANGE, SCTP_U8(SCTP_COMM_UP));
/* Sockets API Draft Section 5.3.1.6 /* Sockets API Draft Section 5.3.1.6
* When a peer sends a Adaptation Layer Indication parameter , SCTP * When a peer sends a Adaptation Layer Indication parameter , SCTP
* delivers this notification to inform the application that of the * delivers this notification to inform the application that of the
* peers requested adaptation layer. * peers requested adaptation layer.
*
* This also needs to be done as a side effect for the same reason as
* above.
*/ */
if (asoc->peer.adaptation_ind) { if (asoc->peer.adaptation_ind)
ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); sctp_add_cmd_sf(commands, SCTP_CMD_ADAPTATION_IND, SCTP_NULL());
if (!ev)
goto nomem_ev;
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(ev));
}
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem_ev:
sctp_chunk_free(repl);
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册