diff options
Diffstat (limited to 'net/sctp')
-rw-r--r-- | net/sctp/associola.c | 29 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 49 | ||||
-rw-r--r-- | net/sctp/protocol.c | 81 | ||||
-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 | ||||
-rw-r--r-- | net/sctp/socket.c | 40 |
7 files changed, 183 insertions, 95 deletions
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, asoc->ssnmap = new->ssnmap; 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 @@ out: sctp_read_unlock(&asoc->base.addr_lock); 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; +} diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ca527a27dd05..84cd53635fe8 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -992,45 +992,52 @@ static struct sctp_pf sctp_pf_inet6_specific = { .af = &sctp_ipv6_specific, }; -/* Initialize IPv6 support and register with inet6 stack. */ +/* Initialize IPv6 support and register with socket layer. */ int sctp_v6_init(void) { - int rc = proto_register(&sctpv6_prot, 1); + int rc; + /* Register the SCTP specific PF_INET6 functions. */ + sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); + + /* Register the SCTP specific AF_INET6 functions. */ + sctp_register_af(&sctp_ipv6_specific); + + rc = proto_register(&sctpv6_prot, 1); if (rc) - goto out; - /* Register inet6 protocol. */ - rc = -EAGAIN; - if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) - goto out_unregister_sctp_proto; + return rc; /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */ inet6_register_protosw(&sctpv6_seqpacket_protosw); inet6_register_protosw(&sctpv6_stream_protosw); - /* Register the SCTP specific PF_INET6 functions. */ - sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); - - /* Register the SCTP specific AF_INET6 functions. */ - sctp_register_af(&sctp_ipv6_specific); + return 0; +} +/* Register with inet6 layer. */ +int sctp_v6_add_protocol(void) +{ /* Register notifier for inet6 address additions/deletions. */ register_inet6addr_notifier(&sctp_inet6addr_notifier); - rc = 0; -out: - return rc; -out_unregister_sctp_proto: - proto_unregister(&sctpv6_prot); - goto out; + + if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) + return -EAGAIN; + + return 0; } /* IPv6 specific exit support. */ void sctp_v6_exit(void) { - list_del(&sctp_ipv6_specific.list); - inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); inet6_unregister_protosw(&sctpv6_seqpacket_protosw); inet6_unregister_protosw(&sctpv6_stream_protosw); - unregister_inet6addr_notifier(&sctp_inet6addr_notifier); proto_unregister(&sctpv6_prot); + list_del(&sctp_ipv6_specific.list); +} + +/* Unregister with inet6 layer. */ +void sctp_v6_del_protocol(void) +{ + inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); + unregister_inet6addr_notifier(&sctp_inet6addr_notifier); } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index c361deb6cea9..34bab36637ac 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -170,7 +170,7 @@ static void sctp_get_local_addr_list(void) struct sctp_af *af; read_lock(&dev_base_lock); - for (dev = dev_base; dev; dev = dev->next) { + for_each_netdev(dev) { __list_for_each(pos, &sctp_address_families) { af = list_entry(pos, struct sctp_af, list); af->copy_addrlist(&sctp_local_addr_list, dev); @@ -975,28 +975,14 @@ SCTP_STATIC __init int sctp_init(void) if (!sctp_sanity_check()) goto out; - status = proto_register(&sctp_prot, 1); - if (status) - goto out; - - /* Add SCTP to inet_protos hash table. */ - status = -EAGAIN; - if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) - goto err_add_protocol; - - /* Add SCTP(TCP and UDP style) to inetsw linked list. */ - inet_register_protosw(&sctp_seqpacket_protosw); - inet_register_protosw(&sctp_stream_protosw); - - /* Allocate a cache pools. */ + /* Allocate bind_bucket and chunk caches. */ status = -ENOBUFS; sctp_bucket_cachep = kmem_cache_create("sctp_bind_bucket", sizeof(struct sctp_bind_bucket), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!sctp_bucket_cachep) - goto err_bucket_cachep; + goto out; sctp_chunk_cachep = kmem_cache_create("sctp_chunk", sizeof(struct sctp_chunk), @@ -1153,6 +1139,14 @@ SCTP_STATIC __init int sctp_init(void) INIT_LIST_HEAD(&sctp_address_families); sctp_register_af(&sctp_ipv4_specific); + status = proto_register(&sctp_prot, 1); + if (status) + goto err_proto_register; + + /* Register SCTP(UDP and TCP style) with socket layer. */ + inet_register_protosw(&sctp_seqpacket_protosw); + inet_register_protosw(&sctp_stream_protosw); + status = sctp_v6_init(); if (status) goto err_v6_init; @@ -1166,19 +1160,39 @@ SCTP_STATIC __init int sctp_init(void) /* Initialize the local address list. */ INIT_LIST_HEAD(&sctp_local_addr_list); - sctp_get_local_addr_list(); /* Register notifier for inet address additions/deletions. */ register_inetaddr_notifier(&sctp_inetaddr_notifier); + /* Register SCTP with inet layer. */ + if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) { + status = -EAGAIN; + goto err_add_protocol; + } + + /* Register SCTP with inet6 layer. */ + status = sctp_v6_add_protocol(); + if (status) + goto err_v6_add_protocol; + __unsafe(THIS_MODULE); status = 0; out: return status; +err_v6_add_protocol: + inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); +err_add_protocol: + sctp_free_local_addr_list(); + sock_release(sctp_ctl_socket); err_ctl_sock_init: sctp_v6_exit(); err_v6_init: + inet_unregister_protosw(&sctp_stream_protosw); + inet_unregister_protosw(&sctp_seqpacket_protosw); + proto_unregister(&sctp_prot); +err_proto_register: sctp_sysctl_unregister(); list_del(&sctp_ipv4_specific.list); free_pages((unsigned long)sctp_port_hashtable, @@ -1192,19 +1206,13 @@ err_ehash_alloc: sizeof(struct sctp_hashbucket))); err_ahash_alloc: sctp_dbg_objcnt_exit(); -err_init_proc: sctp_proc_exit(); +err_init_proc: cleanup_sctp_mibs(); err_init_mibs: kmem_cache_destroy(sctp_chunk_cachep); err_chunk_cachep: kmem_cache_destroy(sctp_bucket_cachep); -err_bucket_cachep: - inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); - inet_unregister_protosw(&sctp_seqpacket_protosw); - inet_unregister_protosw(&sctp_stream_protosw); -err_add_protocol: - proto_unregister(&sctp_prot); goto out; } @@ -1215,8 +1223,9 @@ SCTP_STATIC __exit void sctp_exit(void) * up all the remaining associations and all that memory. */ - /* Unregister notifier for inet address additions/deletions. */ - unregister_inetaddr_notifier(&sctp_inetaddr_notifier); + /* Unregister with inet6/inet layers. */ + sctp_v6_del_protocol(); + inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); /* Free the local address list. */ sctp_free_local_addr_list(); @@ -1224,7 +1233,16 @@ SCTP_STATIC __exit void sctp_exit(void) /* Free the control endpoint. */ sock_release(sctp_ctl_socket); + /* Cleanup v6 initializations. */ sctp_v6_exit(); + + /* Unregister with socket layer. */ + inet_unregister_protosw(&sctp_stream_protosw); + inet_unregister_protosw(&sctp_seqpacket_protosw); + + /* Unregister notifier for inet address additions/deletions. */ + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); + sctp_sysctl_unregister(); list_del(&sctp_ipv4_specific.list); @@ -1236,16 +1254,13 @@ SCTP_STATIC __exit void sctp_exit(void) get_order(sctp_port_hashsize * sizeof(struct sctp_bind_hashbucket))); - kmem_cache_destroy(sctp_chunk_cachep); - kmem_cache_destroy(sctp_bucket_cachep); - sctp_dbg_objcnt_exit(); sctp_proc_exit(); cleanup_sctp_mibs(); - inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); - inet_unregister_protosw(&sctp_seqpacket_protosw); - inet_unregister_protosw(&sctp_stream_protosw); + kmem_cache_destroy(sctp_chunk_cachep); + kmem_cache_destroy(sctp_bucket_cachep); + proto_unregister(&sctp_prot); } 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, * association. */ if (!asoc->temp) { - int assoc_id; int error; 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, if (!asoc->ssnmap) goto clean_up; - retry: - if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) + error = sctp_assoc_set_id(asoc, gfp); + if (error) 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 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) 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 * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. @@ -1485,6 +1512,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_SET_SK_ERR: sctp_cmd_set_sk_err(asoc, cmd->obj.error); 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: printk(KERN_WARNING "Impossible command: %u, %p\n", 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, struct sctp_association *new_asoc) { sctp_init_chunk_t *peer_init; - struct sctp_ulpevent *ev; struct sctp_chunk *repl; /* 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, * D) IMPLEMENTATION NOTE: An implementation may choose to * send the Communication Up notification to the SCTP user * 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 * When a peer sends a Adaptation Layer Indication parameter , SCTP * delivers this notification to inform the application that of the * 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) { - ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); - if (!ev) - goto nomem_ev; - - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, - SCTP_ULPEVENT(ev)); - } + if (asoc->peer.adaptation_ind) + sctp_add_cmd_sf(commands, SCTP_CMD_ADAPTATION_IND, SCTP_NULL()); return SCTP_DISPOSITION_CONSUME; -nomem_ev: - sctp_chunk_free(repl); nomem: return SCTP_DISPOSITION_NOMEM; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2fc0a92caa78..9f1a908776de 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -972,6 +972,7 @@ static int __sctp_connect(struct sock* sk, int walk_size = 0; union sctp_addr *sa_addr; void *addr_buf; + unsigned short port; sp = sctp_sk(sk); ep = sp->ep; @@ -992,6 +993,7 @@ static int __sctp_connect(struct sock* sk, while (walk_size < addrs_size) { sa_addr = (union sctp_addr *)addr_buf; af = sctp_get_af_specific(sa_addr->sa.sa_family); + port = ntohs(sa_addr->v4.sin_port); /* If the address family is not supported or if this address * causes the address buffer to overflow return EINVAL. @@ -1005,6 +1007,12 @@ static int __sctp_connect(struct sock* sk, if (err) goto out_free; + /* Make sure the destination port is correctly set + * in all addresses. + */ + if (asoc && asoc->peer.port && asoc->peer.port != port) + goto out_free; + memcpy(&to, sa_addr, af->sockaddr_len); /* Check if there already is a matching association on the @@ -5012,7 +5020,8 @@ pp_found: struct hlist_node *node; SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n"); - if (pp->fastreuse && sk->sk_reuse) + if (pp->fastreuse && sk->sk_reuse && + sk->sk_state != SCTP_SS_LISTENING) goto success; /* Run through the list of sockets bound to the port @@ -5029,7 +5038,8 @@ pp_found: struct sctp_endpoint *ep2; ep2 = sctp_sk(sk2)->ep; - if (reuse && sk2->sk_reuse) + if (reuse && sk2->sk_reuse && + sk2->sk_state != SCTP_SS_LISTENING) continue; if (sctp_bind_addr_match(&ep2->base.bind_addr, addr, @@ -5050,9 +5060,13 @@ pp_not_found: * if sk->sk_reuse is too (that is, if the caller requested * SO_REUSEADDR on this socket -sk-). */ - if (hlist_empty(&pp->owner)) - pp->fastreuse = sk->sk_reuse ? 1 : 0; - else if (pp->fastreuse && !sk->sk_reuse) + if (hlist_empty(&pp->owner)) { + if (sk->sk_reuse && sk->sk_state != SCTP_SS_LISTENING) + pp->fastreuse = 1; + else + pp->fastreuse = 0; + } else if (pp->fastreuse && + (!sk->sk_reuse || sk->sk_state == SCTP_SS_LISTENING)) pp->fastreuse = 0; /* We are set, so fill up all the data in the hash table @@ -5060,8 +5074,8 @@ pp_not_found: * sockets FIXME: Blurry, NPI (ipg). */ success: - inet_sk(sk)->num = snum; if (!sctp_sk(sk)->bind_hash) { + inet_sk(sk)->num = snum; sk_add_bind_node(sk, &pp->owner); sctp_sk(sk)->bind_hash = pp; } @@ -5134,12 +5148,16 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) * This is not currently spelled out in the SCTP sockets * extensions draft, but follows the practice as seen in TCP * sockets. + * + * Additionally, turn off fastreuse flag since we are not listening */ + sk->sk_state = SCTP_SS_LISTENING; if (!ep->base.bind_addr.port) { if (sctp_autobind(sk)) return -EAGAIN; - } - sk->sk_state = SCTP_SS_LISTENING; + } else + sctp_sk(sk)->bind_hash->fastreuse = 0; + sctp_hash_endpoint(ep); return 0; } @@ -5177,11 +5195,13 @@ SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog) * extensions draft, but follows the practice as seen in TCP * sockets. */ + sk->sk_state = SCTP_SS_LISTENING; if (!ep->base.bind_addr.port) { if (sctp_autobind(sk)) return -EAGAIN; - } - sk->sk_state = SCTP_SS_LISTENING; + } else + sctp_sk(sk)->bind_hash->fastreuse = 0; + sk->sk_max_ack_backlog = backlog; sctp_hash_endpoint(ep); return 0; |