Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Vlad Yasevich and committed by
David S. Miller
07d93967 827bf122

+80 -31
+2
include/net/sctp/command.h
··· 100 100 SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */ 101 101 SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */ 102 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 105 SCTP_CMD_LAST 104 106 } sctp_verb_t; 105 107
+1
include/net/sctp/structs.h
··· 1857 1857 int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, 1858 1858 struct sctp_cookie*, 1859 1859 gfp_t gfp); 1860 + int sctp_assoc_set_id(struct sctp_association *, gfp_t); 1860 1861 1861 1862 int sctp_cmp_addr_exact(const union sctp_addr *ss1, 1862 1863 const union sctp_addr *ss2);
+29
net/sctp/associola.c
··· 1103 1103 asoc->ssnmap = new->ssnmap; 1104 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 ··· 1381 1374 out: 1382 1375 sctp_read_unlock(&asoc->base.addr_lock); 1383 1376 return found; 1377 + } 1378 + 1379 + /* Set an association id for a given association */ 1380 + int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) 1381 + { 1382 + int assoc_id; 1383 + int error = 0; 1384 + retry: 1385 + if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) 1386 + return -ENOMEM; 1387 + 1388 + spin_lock_bh(&sctp_assocs_id_lock); 1389 + error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1390 + 1, &assoc_id); 1391 + spin_unlock_bh(&sctp_assocs_id_lock); 1392 + if (error == -EAGAIN) 1393 + goto retry; 1394 + else if (error) 1395 + return error; 1396 + 1397 + asoc->assoc_id = (sctp_assoc_t) assoc_id; 1398 + return error; 1384 1399 }
+2 -13
net/sctp/sm_make_chunk.c
··· 1939 1939 * association. 1940 1940 */ 1941 1941 if (!asoc->temp) { 1942 - int assoc_id; 1943 1942 int error; 1944 1943 1945 1944 asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams, ··· 1946 1947 if (!asoc->ssnmap) 1947 1948 goto clean_up; 1948 1949 1949 - retry: 1950 - if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) 1950 + error = sctp_assoc_set_id(asoc, gfp); 1951 + if (error) 1951 1952 goto clean_up; 1952 - spin_lock_bh(&sctp_assocs_id_lock); 1953 - error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1, 1954 - &assoc_id); 1955 - spin_unlock_bh(&sctp_assocs_id_lock); 1956 - if (error == -EAGAIN) 1957 - goto retry; 1958 - else if (error) 1959 - goto clean_up; 1960 - 1961 - asoc->assoc_id = (sctp_assoc_t) assoc_id; 1962 1953 } 1963 1954 1964 1955 /* ADDIP Section 4.1 ASCONF Chunk Procedures
+35
net/sctp/sm_sideeffect.c
··· 862 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 892 /* These three macros allow us to pull the debugging code out of the 866 893 * main flow of sctp_do_sm() to keep attention focused on the real 867 894 * functionality there. ··· 1512 1485 case SCTP_CMD_SET_SK_ERR: 1513 1486 sctp_cmd_set_sk_err(asoc, cmd->obj.error); 1514 1487 break; 1488 + case SCTP_CMD_ASSOC_CHANGE: 1489 + sctp_cmd_assoc_change(commands, asoc, 1490 + cmd->obj.u8); 1491 + break; 1492 + case SCTP_CMD_ADAPTATION_IND: 1493 + sctp_cmd_adaptation_ind(commands, asoc); 1494 + break; 1495 + 1515 1496 default: 1516 1497 printk(KERN_WARNING "Impossible command: %u, %p\n", 1517 1498 cmd->verb, cmd->obj.ptr);
+11 -18
net/sctp/sm_statefuns.c
··· 1656 1656 struct sctp_association *new_asoc) 1657 1657 { 1658 1658 sctp_init_chunk_t *peer_init; 1659 - struct sctp_ulpevent *ev; 1660 1659 struct sctp_chunk *repl; 1661 1660 1662 1661 /* new_asoc is a brand-new association, so these are not yet ··· 1686 1687 * D) IMPLEMENTATION NOTE: An implementation may choose to 1687 1688 * send the Communication Up notification to the SCTP user 1688 1689 * upon reception of a valid COOKIE ECHO chunk. 1690 + * 1691 + * Sadly, this needs to be implemented as a side-effect, because 1692 + * we are not guaranteed to have set the association id of the real 1693 + * association and so these notifications need to be delayed until 1694 + * the association id is allocated. 1689 1695 */ 1690 - ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, 1691 - new_asoc->c.sinit_num_ostreams, 1692 - new_asoc->c.sinit_max_instreams, 1693 - NULL, GFP_ATOMIC); 1694 - if (!ev) 1695 - goto nomem_ev; 1696 1696 1697 - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); 1697 + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_CHANGE, SCTP_U8(SCTP_COMM_UP)); 1698 1698 1699 1699 /* Sockets API Draft Section 5.3.1.6 1700 1700 * When a peer sends a Adaptation Layer Indication parameter , SCTP 1701 1701 * delivers this notification to inform the application that of the 1702 1702 * peers requested adaptation layer. 1703 + * 1704 + * This also needs to be done as a side effect for the same reason as 1705 + * above. 1703 1706 */ 1704 - if (asoc->peer.adaptation_ind) { 1705 - ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); 1706 - if (!ev) 1707 - goto nomem_ev; 1708 - 1709 - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, 1710 - SCTP_ULPEVENT(ev)); 1711 - } 1707 + if (asoc->peer.adaptation_ind) 1708 + sctp_add_cmd_sf(commands, SCTP_CMD_ADAPTATION_IND, SCTP_NULL()); 1712 1709 1713 1710 return SCTP_DISPOSITION_CONSUME; 1714 1711 1715 - nomem_ev: 1716 - sctp_chunk_free(repl); 1717 1712 nomem: 1718 1713 return SCTP_DISPOSITION_NOMEM; 1719 1714 }