diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2007-05-04 16:55:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-05-04 16:55:27 -0400 |
commit | 07d939677166cc4f000c767196872a9becc2697b (patch) | |
tree | bef3d3c75ac3dd56813adbc63281feb4195a5b47 | |
parent | 827bf12236fbafc02bc899aec1b37c342c8cf4e5 (diff) |
[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: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sctp/command.h | 2 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 1 | ||||
-rw-r--r-- | net/sctp/associola.c | 29 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 15 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 35 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 29 |
6 files changed, 80 insertions, 31 deletions
diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 6114c4f54b0a..f56c8d695a82 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h | |||
@@ -100,6 +100,8 @@ typedef enum { | |||
100 | SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ | 100 | SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ |
101 | SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ | 101 | SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ |
102 | SCTP_CMD_SET_SK_ERR, /* Set sk_err */ | 102 | SCTP_CMD_SET_SK_ERR, /* Set sk_err */ |
103 | SCTP_CMD_ASSOC_CHANGE, /* generate and send assoc_change event */ | ||
104 | SCTP_CMD_ADAPTATION_IND, /* generate and send adaptation event */ | ||
103 | SCTP_CMD_LAST | 105 | SCTP_CMD_LAST |
104 | } sctp_verb_t; | 106 | } sctp_verb_t; |
105 | 107 | ||
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7b4fff93ba7f..5e81984b8478 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -1857,6 +1857,7 @@ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, | |||
1857 | int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, | 1857 | int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, |
1858 | struct sctp_cookie*, | 1858 | struct sctp_cookie*, |
1859 | gfp_t gfp); | 1859 | gfp_t gfp); |
1860 | int sctp_assoc_set_id(struct sctp_association *, gfp_t); | ||
1860 | 1861 | ||
1861 | int sctp_cmp_addr_exact(const union sctp_addr *ss1, | 1862 | int sctp_cmp_addr_exact(const union sctp_addr *ss1, |
1862 | const union sctp_addr *ss2); | 1863 | const union sctp_addr *ss2); |
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index db73ef97485a..df94e3cdfba3 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
@@ -1103,6 +1103,13 @@ void sctp_assoc_update(struct sctp_association *asoc, | |||
1103 | asoc->ssnmap = new->ssnmap; | 1103 | asoc->ssnmap = new->ssnmap; |
1104 | new->ssnmap = NULL; | 1104 | new->ssnmap = NULL; |
1105 | } | 1105 | } |
1106 | |||
1107 | if (!asoc->assoc_id) { | ||
1108 | /* get a new association id since we don't have one | ||
1109 | * yet. | ||
1110 | */ | ||
1111 | sctp_assoc_set_id(asoc, GFP_ATOMIC); | ||
1112 | } | ||
1106 | } | 1113 | } |
1107 | } | 1114 | } |
1108 | 1115 | ||
@@ -1375,3 +1382,25 @@ out: | |||
1375 | sctp_read_unlock(&asoc->base.addr_lock); | 1382 | sctp_read_unlock(&asoc->base.addr_lock); |
1376 | return found; | 1383 | return found; |
1377 | } | 1384 | } |
1385 | |||
1386 | /* Set an association id for a given association */ | ||
1387 | int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) | ||
1388 | { | ||
1389 | int assoc_id; | ||
1390 | int error = 0; | ||
1391 | retry: | ||
1392 | if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) | ||
1393 | return -ENOMEM; | ||
1394 | |||
1395 | spin_lock_bh(&sctp_assocs_id_lock); | ||
1396 | error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, | ||
1397 | 1, &assoc_id); | ||
1398 | spin_unlock_bh(&sctp_assocs_id_lock); | ||
1399 | if (error == -EAGAIN) | ||
1400 | goto retry; | ||
1401 | else if (error) | ||
1402 | return error; | ||
1403 | |||
1404 | asoc->assoc_id = (sctp_assoc_t) assoc_id; | ||
1405 | return error; | ||
1406 | } | ||
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index be783a3761c4..8d18f570c2e6 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
@@ -1939,7 +1939,6 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, | |||
1939 | * association. | 1939 | * association. |
1940 | */ | 1940 | */ |
1941 | if (!asoc->temp) { | 1941 | if (!asoc->temp) { |
1942 | int assoc_id; | ||
1943 | int error; | 1942 | int error; |
1944 | 1943 | ||
1945 | asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams, | 1944 | 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 | if (!asoc->ssnmap) | 1946 | if (!asoc->ssnmap) |
1948 | goto clean_up; | 1947 | goto clean_up; |
1949 | 1948 | ||
1950 | retry: | 1949 | error = sctp_assoc_set_id(asoc, gfp); |
1951 | if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) | 1950 | if (error) |
1952 | goto clean_up; | 1951 | goto clean_up; |
1953 | spin_lock_bh(&sctp_assocs_id_lock); | ||
1954 | error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1, | ||
1955 | &assoc_id); | ||
1956 | spin_unlock_bh(&sctp_assocs_id_lock); | ||
1957 | if (error == -EAGAIN) | ||
1958 | goto retry; | ||
1959 | else if (error) | ||
1960 | goto clean_up; | ||
1961 | |||
1962 | asoc->assoc_id = (sctp_assoc_t) assoc_id; | ||
1963 | } | 1952 | } |
1964 | 1953 | ||
1965 | /* ADDIP Section 4.1 ASCONF Chunk Procedures | 1954 | /* ADDIP Section 4.1 ASCONF Chunk Procedures |
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index b37a7adeb150..d9fad4f6ffc3 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c | |||
@@ -862,6 +862,33 @@ static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error) | |||
862 | sk->sk_err = error; | 862 | sk->sk_err = error; |
863 | } | 863 | } |
864 | 864 | ||
865 | /* Helper function to generate an association change event */ | ||
866 | static void sctp_cmd_assoc_change(sctp_cmd_seq_t *commands, | ||
867 | struct sctp_association *asoc, | ||
868 | u8 state) | ||
869 | { | ||
870 | struct sctp_ulpevent *ev; | ||
871 | |||
872 | ev = sctp_ulpevent_make_assoc_change(asoc, 0, state, 0, | ||
873 | asoc->c.sinit_num_ostreams, | ||
874 | asoc->c.sinit_max_instreams, | ||
875 | NULL, GFP_ATOMIC); | ||
876 | if (ev) | ||
877 | sctp_ulpq_tail_event(&asoc->ulpq, ev); | ||
878 | } | ||
879 | |||
880 | /* Helper function to generate an adaptation indication event */ | ||
881 | static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands, | ||
882 | struct sctp_association *asoc) | ||
883 | { | ||
884 | struct sctp_ulpevent *ev; | ||
885 | |||
886 | ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); | ||
887 | |||
888 | if (ev) | ||
889 | sctp_ulpq_tail_event(&asoc->ulpq, ev); | ||
890 | } | ||
891 | |||
865 | /* These three macros allow us to pull the debugging code out of the | 892 | /* These three macros allow us to pull the debugging code out of the |
866 | * main flow of sctp_do_sm() to keep attention focused on the real | 893 | * main flow of sctp_do_sm() to keep attention focused on the real |
867 | * functionality there. | 894 | * functionality there. |
@@ -1485,6 +1512,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, | |||
1485 | case SCTP_CMD_SET_SK_ERR: | 1512 | case SCTP_CMD_SET_SK_ERR: |
1486 | sctp_cmd_set_sk_err(asoc, cmd->obj.error); | 1513 | sctp_cmd_set_sk_err(asoc, cmd->obj.error); |
1487 | break; | 1514 | break; |
1515 | case SCTP_CMD_ASSOC_CHANGE: | ||
1516 | sctp_cmd_assoc_change(commands, asoc, | ||
1517 | cmd->obj.u8); | ||
1518 | break; | ||
1519 | case SCTP_CMD_ADAPTATION_IND: | ||
1520 | sctp_cmd_adaptation_ind(commands, asoc); | ||
1521 | break; | ||
1522 | |||
1488 | default: | 1523 | default: |
1489 | printk(KERN_WARNING "Impossible command: %u, %p\n", | 1524 | printk(KERN_WARNING "Impossible command: %u, %p\n", |
1490 | cmd->verb, cmd->obj.ptr); | 1525 | cmd->verb, cmd->obj.ptr); |
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9e28a5d51200..f02ce3dddb7b 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c | |||
@@ -1656,7 +1656,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, | |||
1656 | struct sctp_association *new_asoc) | 1656 | struct sctp_association *new_asoc) |
1657 | { | 1657 | { |
1658 | sctp_init_chunk_t *peer_init; | 1658 | sctp_init_chunk_t *peer_init; |
1659 | struct sctp_ulpevent *ev; | ||
1660 | struct sctp_chunk *repl; | 1659 | struct sctp_chunk *repl; |
1661 | 1660 | ||
1662 | /* new_asoc is a brand-new association, so these are not yet | 1661 | /* 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 | * D) IMPLEMENTATION NOTE: An implementation may choose to | 1686 | * D) IMPLEMENTATION NOTE: An implementation may choose to |
1688 | * send the Communication Up notification to the SCTP user | 1687 | * send the Communication Up notification to the SCTP user |
1689 | * upon reception of a valid COOKIE ECHO chunk. | 1688 | * upon reception of a valid COOKIE ECHO chunk. |
1689 | * | ||
1690 | * Sadly, this needs to be implemented as a side-effect, because | ||
1691 | * we are not guaranteed to have set the association id of the real | ||
1692 | * association and so these notifications need to be delayed until | ||
1693 | * the association id is allocated. | ||
1690 | */ | 1694 | */ |
1691 | ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, | ||
1692 | new_asoc->c.sinit_num_ostreams, | ||
1693 | new_asoc->c.sinit_max_instreams, | ||
1694 | NULL, GFP_ATOMIC); | ||
1695 | if (!ev) | ||
1696 | goto nomem_ev; | ||
1697 | 1695 | ||
1698 | sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); | 1696 | sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_CHANGE, SCTP_U8(SCTP_COMM_UP)); |
1699 | 1697 | ||
1700 | /* Sockets API Draft Section 5.3.1.6 | 1698 | /* Sockets API Draft Section 5.3.1.6 |
1701 | * When a peer sends a Adaptation Layer Indication parameter , SCTP | 1699 | * When a peer sends a Adaptation Layer Indication parameter , SCTP |
1702 | * delivers this notification to inform the application that of the | 1700 | * delivers this notification to inform the application that of the |
1703 | * peers requested adaptation layer. | 1701 | * peers requested adaptation layer. |
1702 | * | ||
1703 | * This also needs to be done as a side effect for the same reason as | ||
1704 | * above. | ||
1704 | */ | 1705 | */ |
1705 | if (asoc->peer.adaptation_ind) { | 1706 | if (asoc->peer.adaptation_ind) |
1706 | ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); | 1707 | sctp_add_cmd_sf(commands, SCTP_CMD_ADAPTATION_IND, SCTP_NULL()); |
1707 | if (!ev) | ||
1708 | goto nomem_ev; | ||
1709 | |||
1710 | sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, | ||
1711 | SCTP_ULPEVENT(ev)); | ||
1712 | } | ||
1713 | 1708 | ||
1714 | return SCTP_DISPOSITION_CONSUME; | 1709 | return SCTP_DISPOSITION_CONSUME; |
1715 | 1710 | ||
1716 | nomem_ev: | ||
1717 | sctp_chunk_free(repl); | ||
1718 | nomem: | 1711 | nomem: |
1719 | return SCTP_DISPOSITION_NOMEM; | 1712 | return SCTP_DISPOSITION_NOMEM; |
1720 | } | 1713 | } |