From 1402a73c6615ff2d0419401764086bb8214c0540 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Mon, 24 Feb 2003 03:56:02 -0600 Subject: [SCTP] Add SCTP_SET_PEER_PRIMARY get/setsockopt. Also, rename all sctp_protocol_t -> struct sctp_protocol. Non-UDP-style SCTP sockets should ignore associd fields, so added this function into sctp_id2assoc() in anticipation of Sridhar's TCP-style work. --- include/net/sctp/sctp.h | 34 ++----- include/net/sctp/structs.h | 25 +++-- net/sctp/associola.c | 30 ++++-- net/sctp/bind_addr.c | 2 +- net/sctp/endpointola.c | 5 +- net/sctp/outqueue.c | 14 +-- net/sctp/protocol.c | 33 +++--- net/sctp/sm_make_chunk.c | 2 +- net/sctp/socket.c | 248 +++++++++++++++++++++++++++++---------------- net/sctp/sysctl.c | 2 +- net/sctp/transport.c | 4 +- 11 files changed, 235 insertions(+), 164 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index b2e19ebde563..bf8aaa165bd9 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -121,9 +121,10 @@ /* * sctp_protocol.c */ -extern sctp_protocol_t sctp_proto; +extern struct sctp_protocol sctp_proto; extern struct sock *sctp_get_ctl_sock(void); -extern int sctp_copy_local_addr_list(sctp_protocol_t *, sctp_bind_addr_t *, +extern int sctp_copy_local_addr_list(struct sctp_protocol *, + struct sctp_bind_addr *, sctp_scope_t, int priority, int flags); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern int sctp_register_pf(struct sctp_pf *, sa_family_t); @@ -348,25 +349,10 @@ static inline sctp_assoc_t sctp_assoc2id(const sctp_association_t *asoc) return (sctp_assoc_t) asoc; } + /* Look up the association by its id. */ -static inline sctp_association_t *sctp_id2assoc(const struct sock *sk, sctp_assoc_t id) -{ - sctp_association_t *asoc = NULL; - - /* First, verify that this is a kernel address. */ - if (sctp_is_valid_kaddr((unsigned long) id)) { - sctp_association_t *temp = (sctp_association_t *) id; - - /* Verify that this _is_ an sctp_association_t - * data structure and if so, that the socket matches. - */ - if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) && - (temp->base.sk == sk)) - asoc = temp; - } +sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id); - return asoc; -} /* A macro to walk a list of skbs. */ #define sctp_skb_for_each(pos, head, tmp) \ @@ -494,7 +480,7 @@ extern void sctp_put_port(struct sock *sk); /* Static inline functions. */ /* Return the SCTP protocol structure. */ -static inline sctp_protocol_t *sctp_get_protocol(void) +static inline struct sctp_protocol *sctp_get_protocol(void) { return &sctp_proto; } @@ -523,21 +509,21 @@ static inline int ipver2af(__u8 ipver) /* This is the hash function for the SCTP port hash table. */ static inline int sctp_phashfn(__u16 lport) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); return (lport & (sctp_proto->port_hashsize - 1)); } /* This is the hash function for the endpoint hash table. */ static inline int sctp_ep_hashfn(__u16 lport) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); return (lport & (sctp_proto->ep_hashsize - 1)); } /* This is the hash function for the association hash table. */ static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); int h = (lport << 16) + rport; h ^= h>>8; return (h & (sctp_proto->assoc_hashsize - 1)); @@ -549,7 +535,7 @@ static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) */ static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); int h = (lport << 16) + rport; h ^= vtag; return (h & (sctp_proto->assoc_hashsize-1)); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d136122af892..2cc13c137a18 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -118,7 +118,6 @@ struct sctp_opt; struct sctp_endpoint_common; struct sctp_ssnmap; -typedef struct sctp_protocol sctp_protocol_t; typedef struct sctp_endpoint sctp_endpoint_t; typedef struct sctp_association sctp_association_t; typedef struct sctp_packet sctp_packet_t; @@ -254,7 +253,7 @@ struct sctp_af { void (*get_saddr) (struct sctp_association *asoc, struct dst_entry *dst, union sctp_addr *daddr, - union sctp_addr *saddr); + union sctp_addr *saddr); void (*copy_addrlist) (struct list_head *, struct net_device *); void (*dst_saddr) (union sctp_addr *saddr, @@ -1130,8 +1129,9 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base) } /* These are function signatures for manipulating endpoints. */ -sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *, struct sock *, int); -sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *, sctp_protocol_t *, +sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *, struct sock *, int); +sctp_endpoint_t *sctp_endpoint_init(struct sctp_endpoint *, + struct sctp_protocol *, struct sock *, int priority); void sctp_endpoint_free(sctp_endpoint_t *); void sctp_endpoint_put(sctp_endpoint_t *); @@ -1143,7 +1143,6 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *); sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, const union sctp_addr *); - int sctp_has_association(const union sctp_addr *laddr, const union sctp_addr *paddr); @@ -1619,7 +1618,7 @@ struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *, struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *, const union sctp_addr *address, const int priority); -void sctp_assoc_control_transport(sctp_association_t *, +void sctp_assoc_control_transport(struct sctp_association *, struct sctp_transport *, sctp_transport_cmd_t, sctp_sn_error_t); struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32); @@ -1629,14 +1628,14 @@ struct sctp_transport *sctp_assoc_is_match(sctp_association_t *, void sctp_assoc_migrate(sctp_association_t *, struct sock *); void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src); -__u32 __sctp_association_get_next_tsn(sctp_association_t *); -__u32 __sctp_association_get_tsn_block(sctp_association_t *, int); -__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid); - -void sctp_assoc_sync_pmtu(sctp_association_t *); -void sctp_assoc_rwnd_increase(sctp_association_t *, int); -void sctp_assoc_rwnd_decrease(sctp_association_t *, int); +__u32 sctp_association_get_next_tsn(struct sctp_association *); +__u32 sctp_association_get_tsn_block(struct sctp_association *, int); +void sctp_assoc_sync_pmtu(struct sctp_association *); +void sctp_assoc_rwnd_increase(struct sctp_association *, int); +void sctp_assoc_rwnd_decrease(struct sctp_association *, int); +void sctp_assoc_set_primary(struct sctp_association *, + struct sctp_transport *); int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int); int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *, sctp_cookie_t *, int); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 230b5602004d..0edd69c2b861 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -360,9 +360,25 @@ static void sctp_association_destroy(sctp_association_t *asoc) } } +/* Change the primary destination address for the peer. */ +void sctp_assoc_set_primary(struct sctp_association *asoc, + struct sctp_transport *transport) +{ + asoc->peer.primary_path = transport; + + /* Set a default msg_name for events. */ + memcpy(&asoc->peer.primary_addr, &transport->ipaddr, + sizeof(union sctp_addr)); + + /* If the primary path is changing, assume that the + * user wants to use this new path. + */ + if (transport->active) + asoc->peer.active_path = transport; +} /* Add a transport address to an association. */ -struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc, +struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, const union sctp_addr *addr, int priority) { @@ -460,11 +476,7 @@ struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc, /* If we do not yet have a primary path, set one. */ if (NULL == asoc->peer.primary_path) { - asoc->peer.primary_path = peer; - /* Set a default msg_name for events. */ - memcpy(&asoc->peer.primary_addr, &peer->ipaddr, - sizeof(union sctp_addr)); - asoc->peer.active_path = peer; + sctp_assoc_set_primary(asoc, peer); asoc->peer.retran_path = peer; } @@ -603,7 +615,7 @@ void sctp_association_put(sctp_association_t *asoc) /* Allocate the next TSN, Transmission Sequence Number, for the given * association. */ -__u32 __sctp_association_get_next_tsn(sctp_association_t *asoc) +__u32 sctp_association_get_next_tsn(sctp_association_t *asoc) { /* From Section 1.6 Serial Number Arithmetic: * Transmission Sequence Numbers wrap around when they reach @@ -618,7 +630,7 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *asoc) } /* Allocate 'num' TSNs by incrementing the association's TSN by num. */ -__u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int num) +__u32 sctp_association_get_tsn_block(sctp_association_t *asoc, int num) { __u32 retval = asoc->next_tsn; @@ -942,7 +954,7 @@ struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t * { /* If this is the first time SHUTDOWN is sent, use the active path, * else use the retran path. If the last SHUTDOWN was sent over the - * retran path, update the retran path and use it. + * retran path, update the retran path and use it. */ if (!asoc->shutdown_last_sent_to) return asoc->peer.active_path; diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 2ae655f2c775..26c62125226b 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -302,7 +302,7 @@ int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr, static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr, sctp_scope_t scope, int priority, int flags) { - sctp_protocol_t *proto = sctp_get_protocol(); + struct sctp_protocol *proto = sctp_get_protocol(); int error = 0; if (sctp_is_any(addr)) { diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 8efbd4af013e..d24f06cdcaa9 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -65,7 +65,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep); /* Create a sctp_endpoint_t with all that boring stuff initialized. * Returns NULL if there isn't enough memory. */ -sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *proto, +sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *proto, struct sock *sk, int priority) { sctp_endpoint_t *ep; @@ -89,7 +89,8 @@ fail: /* * Initialize the base fields of the endpoint structure. */ -sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto, +sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, + struct sctp_protocol *proto, struct sock *sk, int priority) { struct sctp_opt *sp = sctp_sk(sk); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 1171984c3e20..a1bed254eba1 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -125,7 +125,7 @@ void sctp_outq_teardown(struct sctp_outq *q) sctp_free_chunk(chunk); } - /* Throw away any chunks in the retransmit queue. */ + /* Throw away any chunks in the retransmit queue. */ list_for_each_safe(lchunk, temp, &q->retransmit) { list_del(lchunk); chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); @@ -241,7 +241,7 @@ void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q) } /* Mark all the eligible packets on a transport for retransmission. */ -void sctp_retransmit_mark(struct sctp_outq *q, +void sctp_retransmit_mark(struct sctp_outq *q, struct sctp_transport *transport, __u8 fast_retransmit) { @@ -553,7 +553,7 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, } /* Get a TSN block of nfrags TSNs. */ - tsn = __sctp_association_get_tsn_block(asoc, nfrags); + tsn = sctp_association_get_tsn_block(asoc, nfrags); pos = skb_peek(&q->out); /* Transmit the first fragment. */ @@ -595,7 +595,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, old_flags = chunk->chunk_hdr->flags; if (old_flags & SCTP_DATA_FIRST_FRAG) flags = SCTP_DATA_FIRST_FRAG; - else + else flags = SCTP_DATA_MIDDLE_FRAG; /* Make the first fragment. */ @@ -1003,7 +1003,7 @@ sctp_flush_out: */ while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) { struct sctp_transport *t = list_entry(ltransport, - struct sctp_transport, + struct sctp_transport, send_ready); if (t != transport) transport = t; @@ -1125,7 +1125,7 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) * This is a MASSIVE candidate for optimization. */ list_for_each(pos, transport_list) { - transport = list_entry(pos, struct sctp_transport, + transport = list_entry(pos, struct sctp_transport, transports); sctp_check_transmitted(q, &transport->transmitted, transport, sack, highest_new_tsn); @@ -1179,7 +1179,7 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) goto finish; list_for_each(pos, transport_list) { - transport = list_entry(pos, struct sctp_transport, + transport = list_entry(pos, struct sctp_transport, transports); q->empty = q->empty && list_empty(&transport->transmitted); if (!q->empty) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 85a5a2941af5..33ff2f5a434e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -58,7 +58,7 @@ #include /* Global data structures. */ -sctp_protocol_t sctp_proto; +struct sctp_protocol sctp_proto; struct proc_dir_entry *proc_net_sctp; DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); @@ -152,7 +152,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist, /* Extract our IP addresses from the system and stash them in the * protocol structure. */ -static void __sctp_get_local_addr_list(sctp_protocol_t *proto) +static void __sctp_get_local_addr_list(struct sctp_protocol *proto) { struct net_device *dev; struct list_head *pos; @@ -168,7 +168,7 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto) read_unlock(&dev_base_lock); } -static void sctp_get_local_addr_list(sctp_protocol_t *proto) +static void sctp_get_local_addr_list(struct sctp_protocol *proto) { long flags __attribute__ ((unused)); @@ -178,7 +178,7 @@ static void sctp_get_local_addr_list(sctp_protocol_t *proto) } /* Free the existing local addresses. */ -static void __sctp_free_local_addr_list(sctp_protocol_t *proto) +static void __sctp_free_local_addr_list(struct sctp_protocol *proto) { struct sockaddr_storage_list *addr; struct list_head *pos, *temp; @@ -191,7 +191,7 @@ static void __sctp_free_local_addr_list(sctp_protocol_t *proto) } /* Free the existing local addresses. */ -static void sctp_free_local_addr_list(sctp_protocol_t *proto) +static void sctp_free_local_addr_list(struct sctp_protocol *proto) { long flags __attribute__ ((unused)); @@ -201,8 +201,9 @@ static void sctp_free_local_addr_list(sctp_protocol_t *proto) } /* Copy the local addresses which are valid for 'scope' into 'bp'. */ -int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp, - sctp_scope_t scope, int priority, int copy_flags) +int sctp_copy_local_addr_list(struct sctp_protocol *proto, + struct sctp_bind_addr *bp, sctp_scope_t scope, + int priority, int copy_flags) { struct sockaddr_storage_list *addr; int error = 0; @@ -331,7 +332,7 @@ static int sctp_v4_addr_valid(union sctp_addr *addr) static int sctp_v4_available(const union sctp_addr *addr) { int ret = inet_addr_type(addr->v4.sin_addr.s_addr); - + /* FIXME: ip_nonlocal_bind sysctl support. */ if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL) @@ -380,7 +381,7 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) /* Returns a valid dst cache entry for the given source and destination ip * addresses. If an association is passed, trys to get a dst entry with a - * source adddress that matches an address in the bind address list. + * source adddress that matches an address in the bind address list. */ struct dst_entry *sctp_v4_get_dst(sctp_association_t *asoc, union sctp_addr *daddr, @@ -756,7 +757,7 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) static int __init init_sctp_mibs(void) { int i; - + sctp_statistics[0] = kmalloc_percpu(sizeof (struct sctp_mib), GFP_KERNEL); if (!sctp_statistics[0]) @@ -778,7 +779,7 @@ static int __init init_sctp_mibs(void) } } return 0; - + } static void cleanup_sctp_mibs(void) @@ -802,9 +803,9 @@ __init int sctp_init(void) /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(); - if (status) + if (status) goto err_init_mibs; - + /* Initialize proc fs directory. */ sctp_proc_init(); @@ -831,7 +832,7 @@ __init int sctp_init(void) /* Valid.Cookie.Life - 60 seconds */ sctp_proto.valid_cookie_life = 60 * HZ; - /* Whether Cookie Preservative is enabled(1) or not(0) */ + /* Whether Cookie Preservative is enabled(1) or not(0) */ sctp_proto.cookie_preserve_enable = 1; /* Max.Burst - 4 */ @@ -920,7 +921,7 @@ __init int sctp_init(void) INIT_LIST_HEAD(&sctp_proto.local_addr_list); sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED; - /* Register notifier for inet address additions/deletions. */ + /* Register notifier for inet address additions/deletions. */ register_inetaddr_notifier(&sctp_inetaddr_notifier); sctp_get_local_addr_list(&sctp_proto); @@ -942,7 +943,7 @@ err_ahash_alloc: sctp_dbg_objcnt_exit(); sctp_proc_exit(); cleanup_sctp_mibs(); -err_init_mibs: +err_init_mibs: inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); inet_unregister_protosw(&sctp_protosw); return status; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0677dbbbd802..ec0e2bf6a105 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1282,7 +1282,7 @@ void sctp_chunk_assign_tsn(sctp_chunk_t *chunk) * assign a TSN. */ chunk->subh.data_hdr->tsn = - htonl(__sctp_association_get_next_tsn(chunk->asoc)); + htonl(sctp_association_get_next_tsn(chunk->asoc)); chunk->has_tsn = 1; } } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 85fcc4fa6ee9..de934672f74e 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -94,6 +94,37 @@ static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); +/* Look up the association by its id. If this is not a UDP-style + * socket, the ID field is always ignored. + */ +sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id) +{ + sctp_association_t *asoc = NULL; + + /* If this is not a UDP-style socket, assoc id should be + * ignored. + */ + if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) { + if (!list_empty(&sctp_sk(sk)->ep->asocs)) + asoc = list_entry(sctp_sk(sk)->ep->asocs.next, + sctp_association_t, asocs); + return asoc; + } + + /* First, verify that this is a kernel address. */ + if (sctp_is_valid_kaddr((unsigned long) id)) { + sctp_association_t *temp = (sctp_association_t *) id; + + /* Verify that this _is_ an sctp_association_t + * data structure and if so, that the socket matches. + */ + if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) && + (temp->base.sk == sk)) + asoc = temp; + } + + return asoc; +} /* API 3.1.2 bind() - UDP Style Syntax * The syntax of bind() is, @@ -818,19 +849,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } } } else { - /* For a peeled-off socket, ignore any associd specified by - * the user with SNDRCVINFO. - */ - if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) { - if (list_empty(&ep->asocs)) { - err = -EINVAL; - goto out_unlock; - } - asoc = list_entry(ep->asocs.next, sctp_association_t, - asocs); - } else if (associd) { - asoc = sctp_id2assoc(sk, associd); - } + asoc = sctp_id2assoc(sk, associd); if (!asoc) { err = -EINVAL; goto out_unlock; @@ -1007,7 +1026,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, sctp_set_owner_w(chunk); /* This flag, in the UDP model, requests the SCTP stack to - * override the primary destination address with the + * override the primary destination address with the * address found with the sendto/sendmsg call. */ if (sinfo_flags & MSG_ADDR_OVER) { @@ -1126,15 +1145,10 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr int err = 0; int skb_len; - SCTP_DEBUG_PRINTK("sctp_recvmsg(" - "%s: %p, %s: %p, %s: %d, %s: %d, %s: " - "0x%x, %s: %p)\n", - "sk", sk, - "msghdr", msg, - "len", len, - "knoblauch", noblock, - "flags", flags, - "addr_len", addr_len); + SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %d, %s: %d, %s: " + "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg, + "len", len, "knoblauch", noblock, + "flags", flags, "addr_len", addr_len); sctp_lock_sock(sk); skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); @@ -1223,8 +1237,8 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk, return 0; } -static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, - int optlen) +static inline int sctp_setsockopt_events(struct sock *sk, char *optval, + int optlen) { if (optlen != sizeof(struct sctp_event_subscribe)) return -EINVAL; @@ -1250,9 +1264,8 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, return 0; } -static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, - char *optval, - int optlen) +static inline int sctp_setsockopt_peer_addr_params(struct sock *sk, + char *optval, int optlen) { struct sctp_paddrparams params; sctp_association_t *asoc; @@ -1290,8 +1303,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans); if (error) return error; - } - else { + } else { /* The value of the heartbeat interval, in milliseconds. A value of 0, * when modifying the parameter, specifies that the heartbeat on this * address should be disabled. @@ -1336,7 +1348,7 @@ static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, * sinfo_timetolive. The user must provide the sinfo_assoc_id field in * to this call if the caller is using the UDP model. */ -static inline int sctp_setsockopt_set_default_send_param(struct sock *sk, +static inline int sctp_setsockopt_default_send_param(struct sock *sk, char *optval, int optlen) { struct sctp_sndrcvinfo info; @@ -1359,6 +1371,41 @@ static inline int sctp_setsockopt_set_default_send_param(struct sock *sk, return 0; } +/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. + */ +static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen) +{ + struct sctp_setpeerprim prim; + struct sctp_association *asoc; + union sctp_addr *addr; + struct sctp_transport *trans; + + if (optlen != sizeof(struct sctp_setpeerprim)) + return -EINVAL; + + if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + asoc = sctp_id2assoc(sk, prim.sspp_assoc_id); + if (!asoc) + return -EINVAL; + + /* Find the requested address. */ + addr = (union sctp_addr *) &(prim.sspp_addr); + + trans = sctp_assoc_lookup_paddr(asoc, addr); + if (!trans) + return -ENOENT; + + sctp_assoc_set_primary(asoc, trans); + + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -1434,7 +1481,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, break; case SCTP_SET_EVENTS: - retval = sctp_setsockopt_set_events(sk, optval, optlen); + retval = sctp_setsockopt_events(sk, optval, optlen); break; case SCTP_AUTOCLOSE: @@ -1442,8 +1489,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, break; case SCTP_SET_PEER_ADDR_PARAMS: - retval = sctp_setsockopt_set_peer_addr_params(sk, optval, - optlen); + retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); break; case SCTP_INITMSG: @@ -1451,8 +1497,12 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, break; case SCTP_SET_DEFAULT_SEND_PARAM: - retval = sctp_setsockopt_set_default_send_param(sk, - optval, optlen); + retval = sctp_setsockopt_default_send_param(sk, optval, + optlen); + break; + + case SCTP_SET_PEER_PRIMARY_ADDR: + retval = sctp_setsockopt_peer_prim(sk, optval, optlen); break; default: @@ -1607,7 +1657,7 @@ SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) SCTP_STATIC int sctp_init_sock(struct sock *sk) { sctp_endpoint_t *ep; - sctp_protocol_t *proto; + struct sctp_protocol *proto; struct sctp_opt *sp; SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk); @@ -1714,6 +1764,13 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) /* STUB */ } +/* 7.2.1 Association Status (SCTP_STATUS) + + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. + */ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, int *optlen) { @@ -1735,20 +1792,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, } associd = status.sstat_assoc_id; - if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) { - assoc = sctp_id2assoc(sk, associd); - if (!assoc) { - retval = -EINVAL; - goto out; - } - } else { - ep = sctp_sk(sk)->ep; - if (list_empty(&ep->asocs)) { - retval = -EINVAL; - goto out; - } - - assoc = list_entry(ep->asocs.next, sctp_association_t, asocs); + assoc = sctp_id2assoc(sk, associd); + if (!assoc) { + retval = -EINVAL; + goto out; } transport = assoc->peer.primary_path; @@ -1970,8 +2017,8 @@ out: return retval; } -static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, - int len, char *optval, int *optlen) +static inline int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, + char *optval, int *optlen) { struct sctp_paddrparams params; sctp_association_t *asoc; @@ -2023,8 +2070,8 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval return 0; } -static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, - char *optval, int *optlen) +static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) { sctp_assoc_t id; sctp_association_t *asoc; @@ -2053,7 +2100,7 @@ static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len, +static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, char *optval, int *optlen) { sctp_association_t *asoc; @@ -2093,8 +2140,8 @@ static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, - char *optval, int *optlen) +static inline int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) { sctp_assoc_t id; sctp_bind_addr_t *bp; @@ -2132,8 +2179,8 @@ static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, - char *optval, int *optlen) +static inline int sctp_getsockopt_local_addrs(struct sock *sk, int len, + char *optval, int *optlen) { sctp_bind_addr_t *bp; sctp_association_t *asoc; @@ -2183,6 +2230,40 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, return 0; } +/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. + */ +static int sctp_getsockopt_peer_prim(struct sock *sk, int len, + char *optval, int *optlen) +{ + struct sctp_setpeerprim prim; + struct sctp_association *asoc; + + if (len != sizeof(struct sctp_setpeerprim)) + return -EINVAL; + + if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + asoc = sctp_id2assoc(sk, prim.sspp_assoc_id); + if (!asoc) + return -EINVAL; + + if (!asoc->peer.primary_path) + return -ENOTCONN; + + memcpy(&prim.sspp_addr, &asoc->peer.primary_path->ipaddr, + sizeof(union sctp_addr)); + + if (copy_to_user(optval, &prim, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + return 0; +} + /* * * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM) @@ -2200,7 +2281,7 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, * * For getsockopt, it get the default sctp_sndrcvinfo structure. */ -static inline int sctp_getsockopt_set_default_send_param(struct sock *sk, +static inline int sctp_getsockopt_default_send_param(struct sock *sk, int len, char *optval, int *optlen) { struct sctp_sndrcvinfo info; @@ -2257,58 +2338,49 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_STATUS: retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); break; - case SCTP_DISABLE_FRAGMENTS: retval = sctp_getsockopt_disable_fragments(sk, len, optval, optlen); break; - case SCTP_SET_EVENTS: retval = sctp_getsockopt_set_events(sk, len, optval, optlen); break; - case SCTP_AUTOCLOSE: retval = sctp_getsockopt_autoclose(sk, len, optval, optlen); break; - case SCTP_SOCKOPT_PEELOFF: retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); break; - case SCTP_GET_PEER_ADDR_PARAMS: - retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addr_params(sk, len, optval, + optlen); break; - case SCTP_INITMSG: retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); break; - case SCTP_GET_PEER_ADDRS_NUM: - retval = sctp_getsockopt_get_peer_addrs_num(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addrs_num(sk, len, optval, + optlen); break; - case SCTP_GET_LOCAL_ADDRS_NUM: - retval = sctp_getsockopt_get_local_addrs_num(sk, len, optval, - optlen); + retval = sctp_getsockopt_local_addrs_num(sk, len, optval, + optlen); break; - case SCTP_GET_PEER_ADDRS: - retval = sctp_getsockopt_get_peer_addrs(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addrs(sk, len, optval, + optlen); break; - case SCTP_GET_LOCAL_ADDRS: - retval = sctp_getsockopt_get_local_addrs(sk, len, optval, - optlen); + retval = sctp_getsockopt_local_addrs(sk, len, optval, + optlen); break; - case SCTP_SET_DEFAULT_SEND_PARAM: - retval = sctp_getsockopt_set_default_send_param(sk, len, - optval, optlen); + retval = sctp_getsockopt_default_send_param(sk, len, + optval, optlen); + break; + case SCTP_SET_PEER_PRIMARY_ADDR: + retval = sctp_getsockopt_peer_prim(sk, len, optval, optlen); break; - default: retval = -ENOPROTOOPT; break; @@ -2331,7 +2403,7 @@ static void sctp_unhash(struct sock *sk) /* Check if port is acceptable. Possibly find first available port. * * The port hash table (contained in the 'global' SCTP protocol storage - * returned by sctp_protocol_t * sctp_get_protocol()). The hash + * returned by struct sctp_protocol *sctp_get_protocol()). The hash * table is an array of 4096 lists (sctp_bind_hashbucket_t). Each * list (the list number is the port number hashed out, so as you * would expect from a hash function, all the ports in a given list have @@ -2346,7 +2418,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) { sctp_bind_hashbucket_t *head; /* hash list */ sctp_bind_bucket_t *pp; /* hash list port iterator */ - sctp_protocol_t *sctp = sctp_get_protocol(); + struct sctp_protocol *sctp = sctp_get_protocol(); unsigned short snum; int ret; @@ -2684,7 +2756,7 @@ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsi /* FIXME: Commments! */ static __inline__ void __sctp_put_port(struct sock *sk) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); sctp_bind_hashbucket_t *head = &sctp_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)]; sctp_bind_bucket_t *pp; diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index f2fcce00c6ed..1e54322277e6 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -42,7 +42,7 @@ #include #include -extern sctp_protocol_t sctp_proto; +extern struct sctp_protocol sctp_proto; static ctl_table sctp_table[] = { { diff --git a/net/sctp/transport.c b/net/sctp/transport.c index b9d68744a621..6ee6ca94aa6b 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -83,7 +83,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, const union sctp_addr *addr, int priority) { - sctp_protocol_t *proto = sctp_get_protocol(); + struct sctp_protocol *proto = sctp_get_protocol(); /* Copy in the address. */ peer->ipaddr = *addr; @@ -262,7 +262,7 @@ void sctp_transport_put(struct sctp_transport *transport) /* Update transport's RTO based on the newly calculated RTT. */ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) { - sctp_protocol_t *proto = sctp_get_protocol(); + struct sctp_protocol *proto = sctp_get_protocol(); /* Check for valid transport. */ SCTP_ASSERT(tp, "NULL transport", return); -- cgit v1.2.3 From 44310455879437f96eade4ba5721408f38fcd711 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Wed, 26 Feb 2003 02:07:01 -0600 Subject: [SCTP] Supported address types should be based on pf_family. PF_INET sockets are advertising v6 address support. Make this choice a pf_family function. --- include/net/sctp/sctp.h | 3 ++- include/net/sctp/structs.h | 3 +-- net/sctp/ipv6.c | 15 +++++++++++++++ net/sctp/protocol.c | 11 +++++++++++ net/sctp/sm_make_chunk.c | 48 ++++++++++++++++++++-------------------------- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index bf8aaa165bd9..1898cb15c70f 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -323,7 +323,8 @@ static inline int sctp_ipv6_addr_type(const struct in6_addr *addr) return ipv6_addr_type((struct in6_addr*) addr); } -#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 2 * sizeof(__u16)) +/* Size of Supported Address Parameter for 'x' address types. */ +#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16)) /* Note: These V6 macros are obsolescent. */ /* Use this macro to enclose code fragments which are V6-dependent. */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 2cc13c137a18..f027a8c90aa8 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -293,6 +293,7 @@ struct sctp_pf { const union sctp_addr *, struct sctp_opt *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *); + int (*supported_addrs)(const struct sctp_opt *, __u16 *); struct sctp_af *af; }; @@ -397,8 +398,6 @@ typedef struct sctp_signed_cookie { sctp_cookie_t c; } sctp_signed_cookie_t; - - /* This is another convenience type to allocate memory for address * params for the maximum size and pass such structures around * internally. diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index db2c10135190..72467df4eb5c 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -565,6 +565,20 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) return af->available(addr); } +/* Fill in Supported Address Type information for INIT and INIT-ACK + * chunks. Note: In the future, we may want to look at sock options + * to determine whether a PF_INET6 socket really wants to have IPV4 + * addresses. + * Returns number of addresses supported. + */ +static int sctp_inet6_supported_addrs(const struct sctp_opt *opt, + __u16 *types) +{ + types[0] = SCTP_PARAM_IPV4_ADDRESS; + types[1] = SCTP_PARAM_IPV6_ADDRESS; + return 2; +} + static struct proto_ops inet6_seqpacket_ops = { .family = PF_INET6, .release = inet6_release, @@ -627,6 +641,7 @@ static struct sctp_pf sctp_pf_inet6_specific = { .af_supported = sctp_inet6_af_supported, .cmp_addr = sctp_inet6_cmp_addr, .bind_verify = sctp_inet6_bind_verify, + .supported_addrs = sctp_inet6_supported_addrs, .af = &sctp_ipv6_specific, }; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 33ff2f5a434e..340aa00e38ec 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -631,6 +631,16 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) return sctp_v4_available(addr); } +/* Fill in Supported Address Type information for INIT and INIT-ACK + * chunks. Returns number of addresses supported. + */ +static int sctp_inet_supported_addrs(const struct sctp_opt *opt, + __u16 *types) +{ + types[0] = SCTP_PARAM_IPV4_ADDRESS; + return 1; +} + /* Wrapper routine that calls the ip transmit routine. */ static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *transport, int ipfragok) @@ -653,6 +663,7 @@ static struct sctp_pf sctp_pf_inet = { .af_supported = sctp_inet_af_supported, .cmp_addr = sctp_inet_cmp_addr, .bind_verify = sctp_inet_bind_verify, + .supported_addrs = sctp_inet_supported_addrs, .af = &sctp_ipv4_specific, }; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index ec0e2bf6a105..f2308f8373cb 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -66,29 +66,6 @@ #include #include -/* RFC 2960 3.3.2 Initiation (INIT) (1) - * - * Note 4: This parameter, when present, specifies all the - * address types the sending endpoint can support. The absence - * of this parameter indicates that the sending endpoint can - * support any address type. - */ -static const sctp_supported_addrs_param_t sat_param = { - { - SCTP_PARAM_SUPPORTED_ADDRESS_TYPES, - __constant_htons(SCTP_SAT_LEN), - } -}; - -/* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above - * structure is split and the address types array is initialized using a - * fixed length array. - */ -static const __u16 sat_addr_types[2] = { - SCTP_PARAM_IPV4_ADDRESS, - SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,) -}; - /* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 2: The ECN capable field is reserved for future use of @@ -174,7 +151,10 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, union sctp_params addrs; size_t chunksize; sctp_chunk_t *retval = NULL; - int addrs_len = 0; + int num_types, addrs_len = 0; + struct sctp_opt *sp; + sctp_supported_addrs_param_t sat; + __u16 types[2]; /* RFC 2960 3.3.2 Initiation (INIT) (1) * @@ -195,7 +175,11 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, init.num_inbound_streams = htons(asoc->c.sinit_max_instreams); init.initial_tsn = htonl(asoc->c.initial_tsn); - chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN; + /* How many address types are needed? */ + sp = sctp_sk(asoc->base.sk); + num_types = sp->pf->supported_addrs(sp, types); + + chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types); chunksize += sizeof(ecap_param); chunksize += vparam_len; @@ -220,8 +204,18 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v); - sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &sat_param); - sctp_addto_chunk(retval, sizeof(sat_addr_types), sat_addr_types); + /* RFC 2960 3.3.2 Initiation (INIT) (1) + * + * Note 4: This parameter, when present, specifies all the + * address types the sending endpoint can support. The absence + * of this parameter indicates that the sending endpoint can + * support any address type. + */ + sat.param_hdr.type = SCTP_PARAM_SUPPORTED_ADDRESS_TYPES; + sat.param_hdr.length = htons(SCTP_SAT_LEN(num_types)); + sctp_addto_chunk(retval, sizeof(sat), &sat); + sctp_addto_chunk(retval, num_types * sizeof(__u16), &types); + sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); nodata: if (addrs.v) -- cgit v1.2.3 From c613b2aa5715feff3f280556f1baa9fa9cb021e7 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Fri, 28 Feb 2003 03:14:38 -0600 Subject: [SCTP] Only consider C-E bundling up until C-E has been sent. Yes, it is _that_ obvious. If someone does a connect (its not required, but one can) the C-E may have already been sent by the time the first DATA is available. Don't calculate in the C-E bundling overhead if we've already sent the C-E. --- net/sctp/associola.c | 9 ++++----- net/sctp/sm_make_chunk.c | 2 +- net/sctp/socket.c | 1 - net/sctp/ulpqueue.c | 43 ++++++++++++++++++++----------------------- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 0edd69c2b861..7863476f2396 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -413,17 +413,16 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, * If not and the current association PMTU is higher than the new * peer's PMTU, reset the association PMTU to the new peer's PMTU. */ - if (asoc->pmtu) { + if (asoc->pmtu) asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu); - } else { + else asoc->pmtu = peer->pmtu; - } SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " "%d\n", asoc, asoc->pmtu); - asoc->frag_point = asoc->pmtu - - (SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t)); + asoc->frag_point = asoc->pmtu; + asoc->frag_point -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk); /* The asoc->peer.port might not be meaningful yet, but * initialize the packet structure anyway. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index f2308f8373cb..e64403d3a999 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1153,7 +1153,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc, first_len = max; /* Encourage Cookie-ECHO bundling. */ - if (asoc->state < SCTP_STATE_ESTABLISHED) { + if (asoc->state < SCTP_STATE_COOKIE_ECHOED) { whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN); /* Account for the DATA to be bundled with the COOKIE-ECHO. */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index de934672f74e..d04c6b7faf98 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1775,7 +1775,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, int *optlen) { struct sctp_status status; - sctp_endpoint_t *ep; sctp_association_t *assoc = NULL; struct sctp_transport *transport; sctp_assoc_t associd; diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 9f8d3b7826ee..4226e08b4931 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -220,7 +220,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) if (sctp_event2skb(event)->list) sctp_skb_list_tail(sctp_event2skb(event)->list, queue); else - skb_queue_tail(queue, sctp_event2skb(event)); + __skb_queue_tail(queue, sctp_event2skb(event)); /* Did we just complete partial delivery and need to get * rolling again? Move pending data to the receive @@ -247,14 +247,14 @@ out_free: static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; __u32 tsn, ctsn; tsn = event->sndrcvinfo.sinfo_tsn; /* Find the right place in this list. We store them by TSN. */ - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -334,7 +334,7 @@ static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff * */ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; struct sk_buff *first_frag = NULL; __u32 ctsn, next_tsn; @@ -355,7 +355,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u * fragment in order. If not, first_frag is reset to NULL and we * start the next pass when we find another first fragment. */ - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -374,29 +374,26 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u case SCTP_DATA_LAST_FRAG: if (first_frag && (ctsn == next_tsn)) - retval = sctp_make_reassembled_event( - first_frag, pos); + goto found; else first_frag = NULL; break; }; - /* We have the reassembled event. There is no need to look - * further. - */ - if (retval) { - retval->msg_flags |= MSG_EOR; - break; - } } - +done: return retval; +found: + retval = sctp_make_reassembled_event(first_frag, pos); + if (retval) + retval->msg_flags |= MSG_EOR; + goto done; } /* Retrieve the next set of fragments of a partial message. */ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq) { - struct sk_buff *pos, *tmp, *last_frag, *first_frag; + struct sk_buff *pos, *last_frag, *first_frag; struct sctp_ulpevent *cevent; __u32 ctsn, next_tsn; int is_last; @@ -415,7 +412,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq next_tsn = 0; is_last = 0; - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -448,7 +445,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq */ done: retval = sctp_make_reassembled_event(first_frag, last_frag); - if (is_last) + if (retval && is_last) retval->msg_flags |= MSG_EOR; return retval; @@ -490,7 +487,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq, /* Retrieve the first part (sequential fragments) for partial delivery. */ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq) { - struct sk_buff *pos, *tmp, *last_frag, *first_frag; + struct sk_buff *pos, *last_frag, *first_frag; struct sctp_ulpevent *cevent; __u32 ctsn, next_tsn; struct sctp_ulpevent *retval; @@ -507,7 +504,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *u retval = NULL; next_tsn = 0; - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -590,7 +587,7 @@ static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq, static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; __u16 sid, csid; __u16 ssn, cssn; @@ -601,7 +598,7 @@ static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq, /* Find the right place in this list. We store them by * stream ID and then by SSN. */ - sctp_skb_for_each(pos, &ulpq->lobby, tmp) { + skb_queue_walk(&ulpq->lobby, pos) { cevent = (struct sctp_ulpevent *) pos->cb; csid = cevent->sndrcvinfo.sinfo_stream; cssn = cevent->sndrcvinfo.sinfo_ssn; @@ -786,7 +783,7 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority) SCTP_PARTIAL_DELIVERY_ABORTED, priority); if (ev) - skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev)); + __skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev)); /* If there is data waiting, send it up the socket now. */ if (sctp_ulpq_clear_pd(ulpq) || ev) -- cgit v1.2.3 From 1026ffaf60b6fe133070face187debc40c4aa3c1 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Sun, 2 Mar 2003 18:52:52 -0600 Subject: [SCTP] Fix typo in tsnmap.c (Norbert Kiesel) --- net/sctp/tsnmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index 8773a7ee3ead..310c7f0b8c1b 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -250,7 +250,7 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, /* The Gap Ack Block happens to end at the end of the * overflow map. */ - if (started & !ended) { + if (started && !ended) { ended++; _end = map->len + map->len - 1; } @@ -395,7 +395,7 @@ void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn) return; if (!TSN_lt(tsn, map->base_tsn + map->len + map->len)) return; - + /* Assert: TSN is in range. */ gap = tsn - map->base_tsn; -- cgit v1.2.3 From bf9539992ec2eaa4a11b2b7a3cc04ee2fe56f643 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Sun, 2 Mar 2003 23:14:00 -0800 Subject: [SCTP] accept() support for TCP-style SCTP sockets. --- include/net/sctp/structs.h | 2 + net/sctp/endpointola.c | 2 + net/sctp/ipv6.c | 76 ++++++++++- net/sctp/protocol.c | 78 ++++++++++- net/sctp/sm_sideeffect.c | 27 +++- net/sctp/socket.c | 331 +++++++++++++++++++++++++++++++++------------ net/sctp/ulpqueue.c | 4 +- 7 files changed, 419 insertions(+), 101 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 0fa7c1d2db90..dd8e37364fcd 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -262,6 +262,8 @@ struct sctp_pf { struct sctp_opt *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *); int (*supported_addrs)(const struct sctp_opt *, __u16 *); + struct sock *(*create_accept_sk) (struct sock *sk, + struct sctp_association *asoc); struct sctp_af *af; }; diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index d24f06cdcaa9..1f4cdc25d81c 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -195,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep) { SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); + ep->base.sk->state = SCTP_SS_CLOSED; + /* Unlink this endpoint, so we can't find it again! */ sctp_unhash_endpoint(ep); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index fd3ac5c93deb..1133d3fd93bb 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) return retval; } +/* Create and initialize a new sk for the socket to be returned by accept(). */ +struct sock *sctp_v6_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) +{ + struct inet_opt *inet = inet_sk(sk); + struct sock *newsk; + struct inet_opt *newinet; + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); + struct sctp6_sock *newsctp6sk; + + newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sctp6_sock), + sk->slab); + if (!newsk) + goto out; + + sock_init_data(NULL, newsk); + + newsk->type = SOCK_STREAM; + + newsk->prot = sk->prot; + newsk->no_check = sk->no_check; + newsk->reuse = sk->reuse; + + newsk->destruct = inet_sock_destruct; + newsk->zapped = 0; + newsk->family = PF_INET6; + newsk->protocol = IPPROTO_SCTP; + newsk->backlog_rcv = sk->prot->backlog_rcv; + + newsctp6sk = (struct sctp6_sock *)newsk; + newsctp6sk->pinet6 = &newsctp6sk->inet6; + + newinet = inet_sk(newsk); + newnp = inet6_sk(newsk); + + memcpy(newnp, np, sizeof(struct ipv6_pinfo)); + + ipv6_addr_copy(&newnp->daddr, &asoc->peer.primary_addr.v6.sin6_addr); + + newinet->sport = inet->sport; + newinet->dport = asoc->peer.port; + +#ifdef INET_REFCNT_DEBUG + atomic_inc(&inet6_sock_nr); + atomic_inc(&inet_sock_nr); +#endif + + if (0 != newsk->prot->init(newsk)) { + inet_sock_release(newsk); + newsk = NULL; + } + +out: + return newsk; +} + /* Initialize a PF_INET6 socket msg_name. */ static void sctp_inet6_msgname(char *msgname, int *addr_len) { @@ -597,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = { .mmap = sock_no_mmap, }; -static struct inet_protosw sctpv6_protosw = { +static struct inet_protosw sctpv6_seqpacket_protosw = { .type = SOCK_SEQPACKET, .protocol = IPPROTO_SCTP, .prot = &sctp_prot, @@ -606,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = { .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; +static struct inet_protosw sctpv6_stream_protosw = { + .type = SOCK_STREAM, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet6_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG +}; static struct inet6_protocol sctpv6_protocol = { .handler = sctp_rcv, @@ -641,6 +706,7 @@ static struct sctp_pf sctp_pf_inet6_specific = { .cmp_addr = sctp_inet6_cmp_addr, .bind_verify = sctp_inet6_bind_verify, .supported_addrs = sctp_inet6_supported_addrs, + .create_accept_sk = sctp_v6_create_accept_sk, .af = &sctp_ipv6_specific, }; @@ -651,8 +717,9 @@ int sctp_v6_init(void) if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) return -EAGAIN; - /* Add SCTPv6 to inetsw6 linked list. */ - inet6_register_protosw(&sctpv6_protosw); + /* 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 specfic PF_INET6 functions. */ sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); @@ -671,6 +738,7 @@ void sctp_v6_exit(void) { list_del(&sctp_ipv6_specific.list); inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); - inet6_unregister_protosw(&sctpv6_protosw); + inet6_unregister_protosw(&sctpv6_seqpacket_protosw); + inet6_unregister_protosw(&sctpv6_stream_protosw); unregister_inet6addr_notifier(&sctp_inetaddr_notifier); } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 340aa00e38ec..f53710475167 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -480,6 +480,61 @@ void sctp_v4_get_saddr(sctp_association_t *asoc, } +/* Create and initialize a new sk for the socket returned by accept(). */ +struct sock *sctp_v4_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) +{ + struct sock *newsk; + struct inet_opt *inet = inet_sk(sk); + struct inet_opt *newinet; + + newsk = sk_alloc(PF_INET, GFP_KERNEL, sizeof(struct sctp_sock), + sk->slab); + if (!newsk) + goto out; + + sock_init_data(NULL, newsk); + + newsk->type = SOCK_STREAM; + + newsk->prot = sk->prot; + newsk->no_check = sk->no_check; + newsk->reuse = sk->reuse; + + newsk->destruct = inet_sock_destruct; + newsk->zapped = 0; + newsk->family = PF_INET; + newsk->protocol = IPPROTO_SCTP; + newsk->backlog_rcv = sk->prot->backlog_rcv; + + newinet = inet_sk(newsk); + newinet->sport = inet->sport; + newinet->saddr = inet->saddr; + newinet->rcv_saddr = inet->saddr; + newinet->dport = asoc->peer.port; + newinet->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr; + newinet->pmtudisc = inet->pmtudisc; + newinet->id = 0; + + newinet->ttl = sysctl_ip_default_ttl; + newinet->mc_loop = 1; + newinet->mc_ttl = 1; + newinet->mc_index = 0; + newinet->mc_list = NULL; + +#ifdef INET_REFCNT_DEBUG + atomic_inc(&inet_sock_nr); +#endif + + if (0 != newsk->prot->init(newsk)) { + inet_sock_release(newsk); + newsk = NULL; + } + +out: + return newsk; +} + /* Event handler for inet address addition/deletion events. * Basically, whenever there is an event, we re-build our local address list. */ @@ -664,6 +719,7 @@ static struct sctp_pf sctp_pf_inet = { .cmp_addr = sctp_inet_cmp_addr, .bind_verify = sctp_inet_bind_verify, .supported_addrs = sctp_inet_supported_addrs, + .create_accept_sk = sctp_v4_create_accept_sk, .af = &sctp_ipv4_specific, }; @@ -694,7 +750,7 @@ struct proto_ops inet_seqpacket_ops = { }; /* Registration with AF_INET family. */ -struct inet_protosw sctp_protosw = { +static struct inet_protosw sctp_seqpacket_protosw = { .type = SOCK_SEQPACKET, .protocol = IPPROTO_SCTP, .prot = &sctp_prot, @@ -703,6 +759,15 @@ struct inet_protosw sctp_protosw = { .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; +static struct inet_protosw sctp_stream_protosw = { + .type = SOCK_STREAM, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG +}; /* Register with IP layer. */ static struct inet_protocol sctp_protocol = { @@ -809,8 +874,9 @@ __init int sctp_init(void) if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) return -EAGAIN; - /* Add SCTP to inetsw linked list. */ - inet_register_protosw(&sctp_protosw); + /* Add SCTP(TCP and UDP style) to inetsw linked list. */ + inet_register_protosw(&sctp_seqpacket_protosw); + inet_register_protosw(&sctp_stream_protosw); /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(); @@ -956,7 +1022,8 @@ err_ahash_alloc: cleanup_sctp_mibs(); err_init_mibs: inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); - inet_unregister_protosw(&sctp_protosw); + inet_unregister_protosw(&sctp_seqpacket_protosw); + inet_unregister_protosw(&sctp_stream_protosw); return status; } @@ -989,7 +1056,8 @@ __exit void sctp_exit(void) cleanup_sctp_mibs(); inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); - inet_unregister_protosw(&sctp_protosw); + inet_unregister_protosw(&sctp_seqpacket_protosw); + inet_unregister_protosw(&sctp_stream_protosw); } module_init(sctp_init); diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1228f55dfdfb..74c1eefb96d5 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1223,13 +1223,28 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, sctp_state_t state) { + + struct sock *sk = asoc->base.sk; + struct sctp_opt *sp = sctp_sk(sk); + asoc->state = state; asoc->state_timestamp = jiffies; - /* Wake up any process waiting for the association to - * get established. - */ - if ((SCTP_STATE_ESTABLISHED == asoc->state) && - (waitqueue_active(&asoc->wait))) - wake_up_interruptible(&asoc->wait); + if ((SCTP_STATE_ESTABLISHED == asoc->state) || + (SCTP_STATE_CLOSED == asoc->state)) { + /* Wake up any processes waiting in the asoc's wait queue in + * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). + */ + if (waitqueue_active(&asoc->wait)) + wake_up_interruptible(&asoc->wait); + + /* Wake up any processes waiting in the sk's sleep queue of + * a tcp-style or udp-style peeled-off socket in + * sctp_wait_for_accept() or sctp_wait_for_packet(). + * For a udp-style socket, the waiters are woken up by the + * notifications. + */ + if (sp->type != SCTP_SOCKET_UDP) + sk->state_change(sk); + } } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index de934672f74e..dbf8be0cce5c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -88,11 +88,14 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, int msg_len); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); +static int sctp_wait_for_accept(struct sock *sk, long timeo); static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int); static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); +static void sctp_sock_migrate(struct sock *, struct sock *, + struct sctp_association *, sctp_socket_type_t); /* Look up the association by its id. If this is not a UDP-style * socket, the ID field is always ignored. @@ -1151,6 +1154,13 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr "flags", flags, "addr_len", addr_len); sctp_lock_sock(sk); + + if ((SCTP_SOCKET_TCP == sp->type) && + (SCTP_SS_ESTABLISHED != sk->state)) { + err = -ENOTCONN; + goto out; + } + skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; @@ -1563,6 +1573,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, if (err) goto out_unlock; + if (addr_len > sizeof(to)) + addr_len = sizeof(to); memcpy(&to, uaddr, addr_len); to.v4.sin_port = ntohs(to.v4.sin_port); @@ -1635,13 +1647,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags) return -EOPNOTSUPP; /* STUB */ } -/* FIXME: Write comments. */ +/* 4.1.4 accept() - TCP Style Syntax + * + * Applications use accept() call to remove an established SCTP + * association from the accept queue of the endpoint. A new socket + * descriptor will be returned from accept() to represent the newly + * formed association. + */ SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err) { - int error = -EOPNOTSUPP; + struct sctp_opt *sp; + struct sctp_endpoint *ep; + struct sock *newsk = NULL; + struct sctp_association *assoc; + long timeo; + int error = 0; + + sctp_lock_sock(sk); - *err = error; - return NULL; + sp = sctp_sk(sk); + ep = sp->ep; + + if (SCTP_SOCKET_TCP != sp->type) { + error = -EOPNOTSUPP; + goto out; + } + + if (SCTP_SS_LISTENING != sk->state) { + error = -EINVAL; + goto out; + } + + timeo = sock_rcvtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK); + + error = sctp_wait_for_accept(sk, timeo); + if (error) + goto out; + + /* We treat the list of associations on the endpoint as the accept + * queue and pick the first association on the list. + */ + assoc = list_entry(ep->asocs.next, struct sctp_association, asocs); + + newsk = sp->pf->create_accept_sk(sk, assoc); + if (!newsk) { + error = -ENOMEM; + goto out; + } + + /* Populate the fields of the newsk from the oldsk and migrate the + * assoc to the newsk. + */ + sctp_sock_migrate(sk, newsk, assoc, SCTP_SOCKET_TCP); + +out: + sctp_release_sock(sk); + *err = error; + return newsk; } /* FIXME: Write Comments. */ @@ -1667,7 +1729,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp = sctp_sk(sk); /* Initialize the SCTP per socket area. */ - sp->type = SCTP_SOCKET_UDP; + switch (sk->type) { + case SOCK_SEQPACKET: + sp->type = SCTP_SOCKET_UDP; + break; + case SOCK_STREAM: + sp->type = SCTP_SOCKET_TCP; + break; + default: + return -ESOCKTNOSUPPORT; + } /* FIXME: The next draft (04) of the SCTP Sockets Extensions * should include a socket option for manipulating these @@ -1775,7 +1846,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, int *optlen) { struct sctp_status status; - sctp_endpoint_t *ep; sctp_association_t *assoc = NULL; struct sctp_transport *transport; sctp_assoc_t associd; @@ -1879,11 +1949,6 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso struct sock *oldsk = assoc->base.sk; struct sock *newsk; struct socket *tmpsock; - sctp_endpoint_t *newep; - struct sctp_opt *oldsp = sctp_sk(oldsk); - struct sctp_opt *newsp; - struct sk_buff *skb, *tmp; - struct sctp_ulpevent *event; int err = 0; /* An association cannot be branched off from an already peeled-off @@ -1893,81 +1958,17 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso return -EOPNOTSUPP; /* Create a new socket. */ - err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock); + err = sock_create(oldsk->family, SOCK_SEQPACKET, IPPROTO_SCTP, + &tmpsock); if (err < 0) return err; newsk = tmpsock->sk; - newsp = sctp_sk(newsk); - newep = newsp->ep; - - /* Migrate socket buffer sizes and all the socket level options to the - * new socket. - */ - newsk->sndbuf = oldsk->sndbuf; - newsk->rcvbuf = oldsk->rcvbuf; - *newsp = *oldsp; - - /* Restore the ep value that was overwritten with the above structure - * copy. - */ - 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_skb2event(skb); - if (event->asoc == assoc) { - __skb_unlink(skb, skb->list); - __skb_queue_tail(&newsk->receive_queue, skb); - } - } - - /* Clean up an messages pending delivery due to partial - * delivery. Three cases: - * 1) No partial deliver; no work. - * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby. - * 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue. - */ - skb_queue_head_init(&newsp->pd_lobby); - sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;; - - if (sctp_sk(oldsk)->pd_mode) { - struct sk_buff_head *queue; - /* Decide which queue to move pd_lobby skbs to. */ - if (assoc->ulpq.pd_mode) { - queue = &newsp->pd_lobby; - } else - queue = &newsk->receive_queue; - - /* Walk through the pd_lobby, looking for skbs that - * need moved to the new socket. - */ - sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) { - event = sctp_skb2event(skb); - if (event->asoc == assoc) { - __skb_unlink(skb, skb->list); - __skb_queue_tail(queue, skb); - } - } - - /* Clear up any skbs waiting for the partial - * delivery to finish. - */ - if (assoc->ulpq.pd_mode) - sctp_clear_pd(oldsk); - - } - - /* Set the type of socket to indicate that it is peeled off from the - * original socket. - */ - newsp->type = SCTP_SOCKET_UDP_HIGH_BANDWIDTH; - - /* Migrate the association to the new socket. */ - sctp_assoc_migrate(assoc, newsk); + /* Populate the fields of the newsk from the oldsk and migrate the + * assoc to the newsk. + */ + sctp_sock_migrate(oldsk, newsk, assoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH); *newsock = tmpsock; @@ -2615,6 +2616,42 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) if (SCTP_SOCKET_UDP != sp->type) return -EINVAL; + if (sk->state == SCTP_SS_LISTENING) + return 0; + + /* + * If a bind() or sctp_bindx() is not called prior to a listen() + * call that allows new associations to be accepted, the system + * picks an ephemeral port and will choose an address set equivalent + * to binding with a wildcard address. + * + * This is not currently spelled out in the SCTP sockets + * extensions draft, but follows the practice as seen in TCP + * sockets. + */ + if (!ep->base.bind_addr.port) { + if (sctp_autobind(sk)) + return -EAGAIN; + } + sk->state = SCTP_SS_LISTENING; + sctp_hash_endpoint(ep); + return 0; +} + +/* + * 4.1.3 listen() - TCP Style Syntax + * + * Applications uses listen() to ready the SCTP endpoint for accepting + * inbound associations. + */ +SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog) +{ + struct sctp_opt *sp = sctp_sk(sk); + sctp_endpoint_t *ep = sp->ep; + + if (sk->state == SCTP_SS_LISTENING) + return 0; + /* * If a bind() or sctp_bindx() is not called prior to a listen() * call that allows new associations to be accepted, the system @@ -2630,6 +2667,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) return -EAGAIN; } sk->state = SCTP_SS_LISTENING; + sk->max_ack_backlog = backlog; sctp_hash_endpoint(ep); return 0; } @@ -2653,8 +2691,8 @@ int sctp_inet_listen(struct socket *sock, int backlog) break; case SOCK_STREAM: - /* FIXME for TCP-style sockets. */ - err = -EOPNOTSUPP; + err = sctp_stream_listen(sk, backlog); + break; default: goto out; @@ -3285,7 +3323,7 @@ out: return err; do_error: - err = -ECONNABORTED; + err = -ECONNREFUSED; goto out; do_interrupted: @@ -3297,6 +3335,131 @@ do_nonblock: goto out; } +static int sctp_wait_for_accept(struct sock *sk, long timeo) +{ + struct sctp_endpoint *ep; + int err = 0; + DECLARE_WAITQUEUE(wait, current); + + ep = sctp_sk(sk)->ep; + + add_wait_queue_exclusive(sk->sleep, &wait); + + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (list_empty(&ep->asocs)) { + sctp_release_sock(sk); + timeo = schedule_timeout(timeo); + sctp_lock_sock(sk); + } + + err = -EINVAL; + if (sk->state != SCTP_SS_LISTENING) + break; + + err = 0; + if (!list_empty(&ep->asocs)) + break; + + err = sock_intr_errno(timeo); + if (signal_pending(current)) + break; + + err = -EAGAIN; + if (!timeo) + break; + } + + remove_wait_queue(sk->sleep, &wait); + __set_current_state(TASK_RUNNING); + + return err; +} + +/* Populate the fields of the newsk from the oldsk and migrate the assoc + * and its messages to the newsk. + */ +void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, + struct sctp_association *assoc, sctp_socket_type_t type) +{ + struct sctp_opt *oldsp = sctp_sk(oldsk); + struct sctp_opt *newsp = sctp_sk(newsk); + sctp_endpoint_t *newep = newsp->ep; + struct sk_buff *skb, *tmp; + struct sctp_ulpevent *event; + + /* Migrate socket buffer sizes and all the socket level options to the + * new socket. + */ + newsk->sndbuf = oldsk->sndbuf; + newsk->rcvbuf = oldsk->rcvbuf; + *newsp = *oldsp; + + /* Restore the ep value that was overwritten with the above structure + * copy. + */ + 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_skb2event(skb); + if (event->asoc == assoc) { + __skb_unlink(skb, skb->list); + __skb_queue_tail(&newsk->receive_queue, skb); + } + } + + /* Clean up any messages pending delivery due to partial + * delivery. Three cases: + * 1) No partial deliver; no work. + * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby. + * 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue. + */ + skb_queue_head_init(&newsp->pd_lobby); + sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;; + + if (sctp_sk(oldsk)->pd_mode) { + struct sk_buff_head *queue; + + /* Decide which queue to move pd_lobby skbs to. */ + if (assoc->ulpq.pd_mode) { + queue = &newsp->pd_lobby; + } else + queue = &newsk->receive_queue; + + /* Walk through the pd_lobby, looking for skbs that + * need moved to the new socket. + */ + sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) { + event = sctp_skb2event(skb); + if (event->asoc == assoc) { + __skb_unlink(skb, skb->list); + __skb_queue_tail(queue, skb); + } + } + + /* Clear up any skbs waiting for the partial + * delivery to finish. + */ + if (assoc->ulpq.pd_mode) + sctp_clear_pd(oldsk); + + } + + /* Set the type of socket to indicate that it is peeled off from the + * original UDP-style socket or created with the accept() call on a + * TCP-style socket.. + */ + newsp->type = type; + + /* Migrate the association to the new socket. */ + sctp_assoc_migrate(assoc, newsk); + + newsk->state = SCTP_SS_ESTABLISHED; +} + /* This proto struct describes the ULP interface for SCTP. */ struct proto sctp_prot = { .name = "SCTP", diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 96fdd8afbbb6..dd14e7554d97 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -230,7 +230,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) sctp_ulpq_clear_pd(ulpq); if (queue == &sk->receive_queue) - wake_up_interruptible(sk->sleep); + sk->data_ready(sk, 0); return 1; out_free: @@ -790,5 +790,5 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority) /* If there is data waiting, send it up the socket now. */ if (sctp_ulpq_clear_pd(ulpq) || ev) - wake_up_interruptible(sk->sleep); + sk->data_ready(sk, 0); } -- cgit v1.2.3 From 020fec6ee15d8ae40a666bc4ea1efbddc52c4a5d Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Mon, 3 Mar 2003 21:13:58 -0600 Subject: [SCTP] Add SCTP_NODELAY sockopt and message delay (ardelle.fan) Submitted by Ardelle Fan. Add Nagle-like delay to SCTP so small messages try to bundle together. Add sockopt to enable/disable the delay functionality. --- include/net/sctp/command.h | 16 +++---- include/net/sctp/constants.h | 1 + include/net/sctp/sctp.h | 16 ++----- include/net/sctp/sm.h | 46 +++++++++---------- include/net/sctp/structs.h | 26 +++++------ include/net/sctp/user.h | 2 + net/sctp/output.c | 90 ++++++++++++++++++++++-------------- net/sctp/outqueue.c | 106 +++++++++++++++++++++++++------------------ net/sctp/protocol.c | 9 ++-- net/sctp/sm_sideeffect.c | 2 +- net/sctp/sm_statefuns.c | 34 +++++++------- net/sctp/socket.c | 95 ++++++++++++++++++++++++++++++-------- 12 files changed, 267 insertions(+), 176 deletions(-) diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 6603202d91f7..f730dd55f5ad 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -110,13 +110,13 @@ typedef union { sctp_event_timeout_t to; sctp_counter_t counter; void *ptr; - sctp_chunk_t *chunk; - sctp_association_t *asoc; + struct sctp_chunk *chunk; + struct sctp_association *asoc; struct sctp_transport *transport; - sctp_bind_addr_t *bp; + struct sctp_bind_addr *bp; sctp_init_chunk_t *init; struct sctp_ulpevent *ulpevent; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_sackhdr_t *sackh; } sctp_arg_t; @@ -158,13 +158,13 @@ SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter) SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) -SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk) -SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc) +SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) +SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc) SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport) -SCTP_ARG_CONSTRUCTOR(BA, sctp_bind_addr_t *, bp) +SCTP_ARG_CONSTRUCTOR(BA, struct sctp_bind_addr *, bp) SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init) SCTP_ARG_CONSTRUCTOR(ULPEVENT, struct sctp_ulpevent *, ulpevent) -SCTP_ARG_CONSTRUCTOR(PACKET, sctp_packet_t *, packet) +SCTP_ARG_CONSTRUCTOR(PACKET, struct sctp_packet *, packet) SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh) typedef struct { diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 3cc94d900f95..23a956762e41 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -345,6 +345,7 @@ typedef enum { SCTP_XMIT_PMTU_FULL, SCTP_XMIT_RWND_FULL, SCTP_XMIT_MUST_FRAG, + SCTP_XMIT_NAGLE_DELAY, } sctp_xmit_t; /* These are the commands for manipulating transports. */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 1898cb15c70f..27a69518c2f9 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -313,31 +313,21 @@ static inline void sctp_sysctl_unregister(void) { return; } #endif +/* Size of Supported Address Parameter for 'x' address types. */ +#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16)) + #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) extern int sctp_v6_init(void); extern void sctp_v6_exit(void); - static inline int sctp_ipv6_addr_type(const struct in6_addr *addr) { return ipv6_addr_type((struct in6_addr*) addr); } -/* Size of Supported Address Parameter for 'x' address types. */ -#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16)) - -/* Note: These V6 macros are obsolescent. */ -/* Use this macro to enclose code fragments which are V6-dependent. */ -#define SCTP_V6(m...) m -#define SCTP_V6_SUPPORT 1 - #else /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ #define sctp_ipv6_addr_type(a) 0 -#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 1 * sizeof(__u16)) -#define SCTP_V6(m...) /* Do nothing. */ -#undef SCTP_V6_SUPPORT - static inline int sctp_v6_init(void) { return 0; } static inline void sctp_v6_exit(void) { return; } diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 7b08e90a102a..16737eda8d8c 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -3,40 +3,40 @@ * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001-2002 International Business Machines Corp. - * + * * This file is part of the SCTP kernel reference Implementation - * + * * This file is part of the implementation of the add-IP extension, * based on June 29, 2001, * for the SCTP kernel reference Implementation. - * + * * These are definitions needed by the state machine. - * - * The SCTP reference implementation is free software; - * you can redistribute it and/or modify it under the terms of + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. - * - * The SCTP reference implementation is distributed in the hope that it + * + * The SCTP reference implementation is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * ************************ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * + * Boston, MA 02111-1307, USA. + * * Please send any bug reports or fixes you make to the * email addresses: * lksctp developers - * + * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp * - * Written or modified by: + * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson * Xingang Guo @@ -313,18 +313,18 @@ void sctp_generate_t3_rtx_event(unsigned long peer); void sctp_generate_heartbeat_event(unsigned long peer); sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *); -sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, - const sctp_association_t *asoc, - sctp_chunk_t *chunk, - const void *payload, - size_t paylen); -sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, - const sctp_chunk_t *chunk); -void sctp_ootb_pkt_free(sctp_packet_t *packet); +struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *, + const struct sctp_association *, + struct sctp_chunk *chunk, + const void *payload, + size_t paylen); +struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *, + const struct sctp_chunk *); +void sctp_ootb_pkt_free(struct sctp_packet *); sctp_cookie_param_t * -sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *, - const sctp_chunk_t *, int *cookie_len, +sctp_pack_cookie(const struct sctp_endpoint *, const struct sctp_association *, + const struct sctp_chunk *, int *cookie_len, const __u8 *, int addrs_len); sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *, const sctp_association_t *, diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 0fa7c1d2db90..a595ac501cca 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -88,7 +88,6 @@ struct sctp_ssnmap; typedef struct sctp_endpoint sctp_endpoint_t; typedef struct sctp_association sctp_association_t; -typedef struct sctp_packet sctp_packet_t; typedef struct sctp_chunk sctp_chunk_t; typedef struct sctp_bind_addr sctp_bind_addr_t; typedef struct sctp_endpoint_common sctp_endpoint_common_t; @@ -602,26 +601,26 @@ struct sctp_packet { typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *); typedef int (sctp_outq_ehandler_t)(struct sctp_outq *); -typedef sctp_packet_t *(sctp_outq_ohandler_init_t) - (sctp_packet_t *, +typedef struct sctp_packet *(sctp_outq_ohandler_init_t) + (struct sctp_packet *, struct sctp_transport *, __u16 sport, __u16 dport); -typedef sctp_packet_t *(sctp_outq_ohandler_config_t) - (sctp_packet_t *, +typedef struct sctp_packet *(sctp_outq_ohandler_config_t) + (struct sctp_packet *, __u32 vtag, int ecn_capable, sctp_packet_phandler_t *get_prepend_chunk); -typedef sctp_xmit_t (sctp_outq_ohandler_t)(sctp_packet_t *, +typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *, sctp_chunk_t *); -typedef int (sctp_outq_ohandler_force_t)(sctp_packet_t *); +typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *); sctp_outq_ohandler_init_t sctp_packet_init; sctp_outq_ohandler_config_t sctp_packet_config; sctp_outq_ohandler_t sctp_packet_append_chunk; sctp_outq_ohandler_t sctp_packet_transmit_chunk; sctp_outq_ohandler_force_t sctp_packet_transmit; -void sctp_packet_free(sctp_packet_t *); +void sctp_packet_free(struct sctp_packet *); /* This represents a remote transport address. @@ -787,7 +786,7 @@ struct sctp_transport { struct list_head transmitted; /* We build bundle-able packets for this transport here. */ - sctp_packet_t packet; + struct sctp_packet packet; /* This is the list of transports that have chunks to send. */ struct list_head send_ready; @@ -863,12 +862,11 @@ void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *); struct sctp_outq { sctp_association_t *asoc; - /* BUG: This really should be an array of streams. - * This really holds a list of chunks (one stream). - * FIXME: If true, why so? - */ + /* Data pending that has never been transmitted. */ struct sk_buff_head out; + unsigned out_qlen; /* Total length of queued data chunks. */ + /* These are control chunks we want to send. */ struct sk_buff_head control; @@ -883,7 +881,7 @@ struct sctp_outq { struct list_head retransmit; /* Call these functions to send chunks down to the next lower - * layer. This is always SCTP_packet, but we separate the two + * layer. This is always sctp_packet, but we separate the two * structures to make testing simpler. */ sctp_outq_ohandler_init_t *init_output; diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 69e241b1a88a..fc99306d35e6 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -108,6 +108,8 @@ enum sctp_optname { #define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */ #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS + SCTP_NODELAY, /* Get/set nodelay option. */ +#define SCTP_NODELAY SCTP_NODELAY }; diff --git a/net/sctp/output.c b/net/sctp/output.c index d7826c2216e6..0291d157c5f7 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -62,17 +62,16 @@ #include /* Forward declarations for private helpers. */ -static void sctp_packet_reset(sctp_packet_t *packet); -static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, - sctp_chunk_t *chunk); +static void sctp_packet_reset(struct sctp_packet *packet); +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); /* Config a packet. * This appears to be a followup set of initializations.) */ -sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, - __u32 vtag, - int ecn_capable, - sctp_packet_phandler_t *prepend_handler) +struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, + __u32 vtag, int ecn_capable, + sctp_packet_phandler_t *prepend_handler) { int packet_empty = (packet->size == SCTP_IP_OVERHEAD); @@ -89,10 +88,9 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, } /* Initialize the packet structure. */ -sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, - struct sctp_transport *transport, - __u16 sport, - __u16 dport) +struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, + struct sctp_transport *transport, + __u16 sport, __u16 dport) { packet->transport = transport; packet->source_port = sport; @@ -109,14 +107,12 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, } /* Free a packet. */ -void sctp_packet_free(sctp_packet_t *packet) +void sctp_packet_free(struct sctp_packet *packet) { - sctp_chunk_t *chunk; + struct sctp_chunk *chunk; - while (NULL != - (chunk = (sctp_chunk_t *)skb_dequeue(&packet->chunks))) { + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) sctp_free_chunk(chunk); - } if (packet->malloced) kfree(packet); @@ -129,8 +125,8 @@ void sctp_packet_free(sctp_packet_t *packet) * as it can fit in the packet, but any more data that does not fit in this * packet can be sent only after receiving the COOKIE_ACK. */ -sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, - sctp_chunk_t *chunk) +sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) { sctp_xmit_t retval; int error = 0; @@ -152,6 +148,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, case SCTP_XMIT_MUST_FRAG: case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_OK: + case SCTP_XMIT_NAGLE_DELAY: break; }; @@ -161,7 +158,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, /* Append a chunk to the offered packet reporting back any inability to do * so. */ -sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) +sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); @@ -223,7 +221,7 @@ append: } /* It is OK to send this chunk. */ - skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk); + __skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk); packet->size += chunk_len; finish: return retval; @@ -234,18 +232,18 @@ finish: * * The return value is a normal kernel error return value. */ -int sctp_packet_transmit(sctp_packet_t *packet) +int sctp_packet_transmit(struct sctp_packet *packet) { struct sctp_transport *transport = packet->transport; - sctp_association_t *asoc = transport->asoc; + struct sctp_association *asoc = transport->asoc; struct sctphdr *sh; __u32 crc32; struct sk_buff *nskb; - sctp_chunk_t *chunk; + struct sctp_chunk *chunk; struct sock *sk; int err = 0; int padding; /* How much padding do we need? */ - __u8 packet_has_data = 0; + __u8 has_data = 0; struct dst_entry *dst; /* Do NOT generate a chunkless packet... */ @@ -253,7 +251,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) return err; /* Set up convenience variables... */ - chunk = (sctp_chunk_t *) (packet->chunks.next); + chunk = (struct sctp_chunk *) (packet->chunks.next); sk = chunk->skb->sk; /* Allocate the new skb. */ @@ -291,8 +289,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) * [This whole comment explains WORD_ROUND() below.] */ SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); - while (NULL != (chunk = (sctp_chunk_t *) - skb_dequeue(&packet->chunks))) { + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) { chunk->num_times_sent++; chunk->sent_at = jiffies; if (sctp_chunk_is_data(chunk)) { @@ -309,7 +306,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) chunk->rtt_in_progress = 1; transport->rto_pending = 1; } - packet_has_data = 1; + has_data = 1; } memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, chunk->skb->len); @@ -399,7 +396,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) asoc->peer.last_sent_to = transport; } - if (packet_has_data) { + if (has_data) { struct timer_list *timer; unsigned long timeout; @@ -456,9 +453,9 @@ no_route: /* * This private function resets the packet to a fresh state. */ -static void sctp_packet_reset(sctp_packet_t *packet) +static void sctp_packet_reset(struct sctp_packet *packet) { - sctp_chunk_t *chunk = NULL; + struct sctp_chunk *chunk = NULL; packet->size = SCTP_IP_OVERHEAD; @@ -473,13 +470,15 @@ static void sctp_packet_reset(sctp_packet_t *packet) } /* This private function handles the specifics of appending DATA chunks. */ -static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, - sctp_chunk_t *chunk) +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; size_t datasize, rwnd, inflight; struct sctp_transport *transport = packet->transport; __u32 max_burst_bytes; + struct sctp_opt *sp = sctp_sk(transport->asoc->base.sk); + struct sctp_outq *q = &transport->asoc->outqueue; /* RFC 2960 6.1 Transmission of DATA Chunks * @@ -543,11 +542,34 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, * 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 (!chunk->fast_retransmit) if (transport->flight_size >= transport->cwnd) { retval = SCTP_XMIT_RWND_FULL; goto finish; } + + /* Nagle's algorithm to solve small-packet problem: + * inhibit the sending of new chunks when new outgoing data arrives + * if any proeviously transmitted data on the connection remains + * unacknowledged. Unless the connection was previously idle. Check + * whether the connection is idle. No outstanding means idle, flush + * it. If outstanding bytes are less than half cwnd, the connection + * is not in the state of congestion, so also flush it. + */ + if (!sp->nodelay && q->outstanding_bytes >= transport->cwnd >> 1) { + /* Check whether this chunk and all the rest of pending + * data will fit or whether we'll choose to delay in + * hopes of bundling a full sized packet. + */ + if ((datasize + q->out_qlen) < transport->asoc->frag_point) { + + /* If the the chunk should be delay + * for future sending, we could not + * append it. + */ + retval = SCTP_XMIT_NAGLE_DELAY; + goto finish; + } } /* Keep track of how many bytes are in flight over this transport. */ diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index a1bed254eba1..2ef83c5c040a 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. - * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001-2003 Intel Corp. * Copyright (c) 2001-2003 International Business Machines Corp. * * This file is part of the SCTP kernel reference Implementation @@ -62,6 +62,33 @@ static void sctp_check_transmitted(struct sctp_outq *q, sctp_sackhdr_t *sack, __u32 highest_new_tsn); +/* Add data to the front of the queue. */ +static inline void sctp_outq_head_data(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + __skb_queue_head(&q->out, (struct sk_buff *)ch); + q->out_qlen += ch->skb->len; + return; +} + +/* Take data from the front of the queue. */ +static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q) +{ + struct sctp_chunk *ch; + ch = (struct sctp_chunk *)__skb_dequeue(&q->out); + if (ch) + q->out_qlen -= ch->skb->len; + return ch; +} +/* Add data chunk to the end of the queue. */ +static inline void sctp_outq_tail_data(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + __skb_queue_tail(&q->out, (struct sk_buff *)ch); + q->out_qlen += ch->skb->len; + return; +} + /* Generate a new outqueue. */ struct sctp_outq *sctp_outq_new(sctp_association_t *asoc) { @@ -97,6 +124,7 @@ void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q) q->empty = 1; q->malloced = 0; + q->out_qlen = 0; } /* Free the outqueue structure and any related pending chunks. @@ -133,7 +161,7 @@ void sctp_outq_teardown(struct sctp_outq *q) } /* Throw away any leftover data chunks. */ - while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out))) + while ((chunk = sctp_outq_dequeue_data(q))) sctp_free_chunk(chunk); /* Throw away any leftover control chunks. */ @@ -192,7 +220,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) : "Illegal Chunk"); - skb_queue_tail(&q->out, (struct sk_buff *) chunk); + sctp_outq_tail_data(q, chunk); if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) SCTP_INC_STATS(SctpOutUnorderChunks); else @@ -201,7 +229,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) break; }; } else { - skb_queue_tail(&q->control, (struct sk_buff *) chunk); + __skb_queue_tail(&q->control, (struct sk_buff *) chunk); SCTP_INC_STATS(SctpOutCtrlChunks); } @@ -351,7 +379,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, * * The return value is a normal kernel error return value. */ -static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, +static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, int rtx_timeout, int *start_timer) { struct list_head *lqueue; @@ -385,17 +413,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, while (lchunk) { chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); -#if 0 - /* If a chunk has been tried for more than SCTP_DEF_MAX_SEND - * times, discard it, and check the empty flag of the outqueue. - * - * --xguo - */ - if (chunk->snd_count > SCTP_DEF_MAX_SEND) { - sctp_free_chunk(chunk); - continue; - } -#endif /* Make sure that Gap Acked TSNs are not retransmitted. A * simple approach is just to move such TSNs out of the @@ -462,7 +479,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, * chunk that is currently in the process of fragmentation. */ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, - sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn) + struct sctp_packet *packet, sctp_chunk_t *frag, __u32 tsn) { struct sctp_transport *transport = packet->transport; struct sk_buff_head *queue = &q->out; @@ -480,11 +497,11 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. " "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + __skb_insert((struct sk_buff *)frag, pos->prev, + pos, pos->list); + else + __skb_queue_tail(queue, (struct sk_buff *) frag); return; } @@ -496,11 +513,11 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. " "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + __skb_insert((struct sk_buff *)frag, pos->prev, + pos, pos->list); + else + __skb_queue_tail(queue, (struct sk_buff *)frag); break; case SCTP_XMIT_OK: @@ -512,11 +529,11 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " "failed. adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + __skb_insert((struct sk_buff *)frag, pos->prev, + pos, pos->list); + else + __skb_queue_tail(queue,(struct sk_buff *)frag); } else { SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " "success. 0x%x sent\n", @@ -537,7 +554,7 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, * The argument 'frag' point to the first fragment and it holds the list * of all the other fragments in the 'frag_list' field. */ -void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, +void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt, sctp_chunk_t *frag) { sctp_association_t *asoc = frag->asoc; @@ -557,13 +574,13 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, pos = skb_peek(&q->out); /* Transmit the first fragment. */ - sctp_xmit_frag(q, pos, packet, frag, tsn++); + sctp_xmit_frag(q, pos, pkt, frag, tsn++); /* Transmit the rest of fragments. */ frag_list = &frag->frag_list; list_for_each(lfrag, frag_list) { frag = list_entry(lfrag, sctp_chunk_t, frag_list); - sctp_xmit_frag(q, pos, packet, frag, tsn++); + sctp_xmit_frag(q, pos, pkt, frag, tsn++); } } @@ -672,15 +689,14 @@ err: * * Description: Send everything in q which we legally can, subject to * congestion limitations. - * - * Note: This function can be called from multiple contexts so appropriate + * * Note: This function can be called from multiple contexts so appropriate * locking concerns must be made. Today we use the sock lock to protect * this function. */ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) { - sctp_packet_t *packet; - sctp_packet_t singleton; + struct sctp_packet *packet; + struct sctp_packet singleton; sctp_association_t *asoc = q->asoc; int ecn_capable = asoc->peer.ecn_capable; __u16 sport = asoc->base.bind_addr.port; @@ -852,7 +868,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) /* Finally, transmit new packets. */ start_timer = 0; queue = &q->out; - while (NULL != (chunk = (sctp_chunk_t *) skb_dequeue(queue))) { + + while (NULL != (chunk = sctp_outq_dequeue_data(q))) { /* RFC 2960 6.5 Every DATA chunk MUST carry a valid * stream identifier. */ @@ -925,6 +942,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) switch (status) { case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_RWND_FULL: + case SCTP_XMIT_NAGLE_DELAY: /* We could not append this chunk, so put * the chunk back on the output queue. */ @@ -932,7 +950,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) "not transmit TSN: 0x%x, status: %d\n", ntohl(chunk->subh.data_hdr->tsn), status); - skb_queue_head(queue, (struct sk_buff *)chunk); + sctp_outq_head_data(q, chunk); goto sctp_flush_out; break; @@ -994,6 +1012,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } sctp_flush_out: + /* Before returning, examine all the transports touched in * this call. Right now, we bluntly force clear all the * transports. Things might change after we implement Nagle. @@ -1163,11 +1182,10 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) sack_a_rwnd = ntohl(sack->a_rwnd); outstanding = q->outstanding_bytes; - if (outstanding < sack_a_rwnd) { + if (outstanding < sack_a_rwnd) sack_a_rwnd -= outstanding; - } else { + else sack_a_rwnd = 0; - } asoc->peer.rwnd = sack_a_rwnd; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 340aa00e38ec..711564bf898e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -502,10 +502,13 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long event, */ int sctp_ctl_sock_init(void) { - int err = 0; - int family = PF_INET; + int err; + sa_family_t family; - SCTP_V6(family = PF_INET6;) + if (sctp_get_pf_specific(PF_INET6)) + family = PF_INET6; + else + family = PF_INET; err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP, &sctp_ctl_socket); diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1228f55dfdfb..37dfce4fb24c 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -256,7 +256,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, sctp_cmd_t *cmd; sctp_chunk_t *new_obj; sctp_chunk_t *chunk = NULL; - sctp_packet_t *packet; + struct sctp_packet *packet; struct list_head *pos; struct timer_list *timer; unsigned long timeout; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 12889a5d350f..fac774a0f504 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -189,7 +189,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, sctp_chunk_t *repl; sctp_association_t *new_asoc; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; int len; @@ -354,10 +354,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, sctp_init_chunk_t *initchunk; __u32 init_tag; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_disposition_t ret; - /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -912,14 +911,14 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, sctp_cmd_seq_t *commands) { int len; - sctp_packet_t *pkt; + struct sctp_packet *pkt; sctp_addr_param_t *addrparm; sctp_errhdr_t *errhdr; sctp_endpoint_t *ep; char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)]; - /* Build the error on the stack. We are way to malloc - * malloc crazy throughout the code today. + /* Build the error on the stack. We are way to malloc crazy + * throughout the code today. */ errhdr = (sctp_errhdr_t *)buffer; addrparm = (sctp_addr_param_t *)errhdr->variable; @@ -1105,11 +1104,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( sctp_chunk_t *repl; sctp_association_t *new_asoc; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; int len; - /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -2351,7 +2349,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, * room. Note: Playing nice with a confused sender. A * malicious sender can still eat up all our buffer * space and in the future we may want to detect and - * do more drastic reneging. + * do more drastic reneging. */ if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) && (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { @@ -2751,7 +2749,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - sctp_packet_t *packet = NULL; + struct sctp_packet *packet = NULL; sctp_chunk_t *chunk = arg; sctp_chunk_t *abort; @@ -2953,7 +2951,7 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - sctp_packet_t *packet = NULL; + struct sctp_packet *packet = NULL; sctp_chunk_t *chunk = arg; sctp_chunk_t *shut; @@ -4377,13 +4375,13 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk) /* Create an ABORT packet to be sent as a response, with the specified * error causes. */ -sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, +struct sctp_packet *sctp_abort_pkt_new(const sctp_endpoint_t *ep, const sctp_association_t *asoc, sctp_chunk_t *chunk, const void *payload, size_t paylen) { - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_chunk_t *abort; packet = sctp_ootb_pkt_new(asoc, chunk); @@ -4413,10 +4411,10 @@ sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, } /* Allocate a packet for responding in the OOTB conditions. */ -sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, +struct sctp_packet *sctp_ootb_pkt_new(const sctp_association_t *asoc, const sctp_chunk_t *chunk) { - sctp_packet_t *packet; + struct sctp_packet *packet; struct sctp_transport *transport; __u16 sport; __u16 dport; @@ -4449,7 +4447,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, goto nomem; /* Allocate a new packet for sending the response. */ - packet = t_new(sctp_packet_t, GFP_ATOMIC); + packet = t_new(struct sctp_packet, GFP_ATOMIC); if (!packet) goto nomem_packet; @@ -4471,7 +4469,7 @@ nomem: } /* Free the packet allocated earlier for responding in the OOTB condition. */ -void sctp_ootb_pkt_free(sctp_packet_t *packet) +void sctp_ootb_pkt_free(struct sctp_packet *packet) { sctp_transport_free(packet->transport); sctp_packet_free(packet); @@ -4484,7 +4482,7 @@ void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep, sctp_cmd_seq_t *commands, sctp_chunk_t *err_chunk) { - sctp_packet_t *packet; + struct sctp_packet *packet; if (err_chunk) { packet = sctp_ootb_pkt_new(asoc, chunk); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d04c6b7faf98..b7b42f8829ef 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1221,7 +1221,7 @@ out: return err; } -static inline int sctp_setsockopt_disable_fragments(struct sock *sk, +static int sctp_setsockopt_disable_fragments(struct sock *sk, char *optval, int optlen) { int val; @@ -1237,7 +1237,7 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk, return 0; } -static inline int sctp_setsockopt_events(struct sock *sk, char *optval, +static int sctp_setsockopt_events(struct sock *sk, char *optval, int optlen) { if (optlen != sizeof(struct sctp_event_subscribe)) @@ -1247,7 +1247,7 @@ static inline int sctp_setsockopt_events(struct sock *sk, char *optval, return 0; } -static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, +static int sctp_setsockopt_autoclose(struct sock *sk, char *optval, int optlen) { struct sctp_opt *sp = sctp_sk(sk); @@ -1264,7 +1264,7 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, return 0; } -static inline int sctp_setsockopt_peer_addr_params(struct sock *sk, +static int sctp_setsockopt_peer_addr_params(struct sock *sk, char *optval, int optlen) { struct sctp_paddrparams params; @@ -1323,7 +1323,7 @@ static inline int sctp_setsockopt_peer_addr_params(struct sock *sk, return 0; } -static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, +static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen) { if (optlen != sizeof(struct sctp_initmsg)) @@ -1348,7 +1348,7 @@ static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, * sinfo_timetolive. The user must provide the sinfo_assoc_id field in * to this call if the caller is using the UDP model. */ -static inline int sctp_setsockopt_default_send_param(struct sock *sk, +static int sctp_setsockopt_default_send_param(struct sock *sk, char *optval, int optlen) { struct sctp_sndrcvinfo info; @@ -1406,6 +1406,31 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen) return 0; } +/* + * + * 7.1.5 SCTP_NODELAY + * + * Turn on/off any Nagle-like algorithm. This means that packets are + * generally sent as soon as possible and no unnecessary delays are + * introduced, at the cost of more packets in the network. Expects an + * integer boolean flag. + */ +static int sctp_setsockopt_nodelay(struct sock *sk, char *optval, + int optlen) +{ + __u8 val; + + if (optlen < sizeof(__u8)) + return -EINVAL; + + if (get_user(val, (__u8 *)optval)) + return -EFAULT; + + sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1; + + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -1505,6 +1530,10 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, retval = sctp_setsockopt_peer_prim(sk, optval, optlen); break; + case SCTP_NODELAY: + retval = sctp_setsockopt_nodelay(sk, optval, optlen); + break; + default: retval = -ENOPROTOOPT; break; @@ -1715,7 +1744,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp->disable_fragments = 0; /* Turn on/off any Nagle-like algorithm. */ - sp->nodelay = 0; + sp->nodelay = 1; /* Auto-close idle associations after the configured * number of seconds. A value of 0 disables this @@ -1834,7 +1863,7 @@ out: return (retval); } -static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len, +static int sctp_getsockopt_disable_fragments(struct sock *sk, int len, char *optval, int *optlen) { int val; @@ -1851,7 +1880,7 @@ static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen) +static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen) { if (len != sizeof(struct sctp_event_subscribe)) return -EINVAL; @@ -1860,7 +1889,7 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt return 0; } -static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen) +static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen) { /* Applicable to UDP-style socket only */ if (SCTP_SOCKET_TCP == sctp_sk(sk)->type) @@ -1973,7 +2002,7 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso return err; } -static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen) +static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen) { sctp_peeloff_arg_t peeloff; struct socket *newsock; @@ -2016,7 +2045,7 @@ out: return retval; } -static inline int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, +static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, char *optval, int *optlen) { struct sctp_paddrparams params; @@ -2060,7 +2089,7 @@ static inline int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) +static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) { if (len != sizeof(struct sctp_initmsg)) return -EINVAL; @@ -2139,7 +2168,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, +static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, char *optval, int *optlen) { sctp_assoc_t id; @@ -2178,7 +2207,7 @@ static inline int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_local_addrs(struct sock *sk, int len, +static int sctp_getsockopt_local_addrs(struct sock *sk, int len, char *optval, int *optlen) { sctp_bind_addr_t *bp; @@ -2280,7 +2309,7 @@ static int sctp_getsockopt_peer_prim(struct sock *sk, int len, * * For getsockopt, it get the default sctp_sndrcvinfo structure. */ -static inline int sctp_getsockopt_default_send_param(struct sock *sk, +static int sctp_getsockopt_default_send_param(struct sock *sk, int len, char *optval, int *optlen) { struct sctp_sndrcvinfo info; @@ -2307,6 +2336,33 @@ static inline int sctp_getsockopt_default_send_param(struct sock *sk, return 0; } +/* + * + * 7.1.5 SCTP_NODELAY + * + * Turn on/off any Nagle-like algorithm. This means that packets are + * generally sent as soon as possible and no unnecessary delays are + * introduced, at the cost of more packets in the network. Expects an + * integer boolean flag. + */ + +static int sctp_getsockopt_nodelay(struct sock *sk, int len, + char *optval, int *optlen) +{ + __u8 val; + + if (len < sizeof(__u8)) + return -EINVAL; + + len = sizeof(__u8); + val = (sctp_sk(sk)->nodelay == 1); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { @@ -2380,6 +2436,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_SET_PEER_PRIMARY_ADDR: retval = sctp_getsockopt_peer_prim(sk, len, optval, optlen); break; + case SCTP_NODELAY: + retval = sctp_getsockopt_nodelay(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -2650,7 +2709,6 @@ int sctp_inet_listen(struct socket *sock, int backlog) case SOCK_SEQPACKET: err = sctp_seqpacket_listen(sk, backlog); break; - case SOCK_STREAM: /* FIXME for TCP-style sockets. */ err = -EOPNOTSUPP; @@ -3038,7 +3096,8 @@ no_packet: } /* Verify that this is a valid address. */ -static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len) +static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, + int len) { struct sctp_af *af; -- cgit v1.2.3 From ab52120b58a5a20f873eb3b120450cb39f683369 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Wed, 5 Mar 2003 01:22:04 -0600 Subject: [SCTP] Fix out_qlen (pending data) count. Missed a few places on integration of TX delay code. Also removed out the 1/2 cwnd check so we behave more like pure Nagle. --- net/sctp/output.c | 54 ++++++++++++++++++++++++----------------------------- net/sctp/outqueue.c | 37 +++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/net/sctp/output.c b/net/sctp/output.c index 0291d157c5f7..566e690ad53a 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -158,7 +158,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, /* Append a chunk to the offered packet reporting back any inability to do * so. */ -sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, +sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; @@ -180,7 +180,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, /* Both control chunks and data chunks with TSNs are * non-fragmentable. */ - int fragmentable = sctp_chunk_is_data(chunk) && + int fragmentable = sctp_chunk_is_data(chunk) && (!chunk->has_tsn); if (packet_empty) { if (fragmentable) { @@ -470,15 +470,16 @@ static void sctp_packet_reset(struct sctp_packet *packet) } /* This private function handles the specifics of appending DATA chunks. */ -static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; size_t datasize, rwnd, inflight; struct sctp_transport *transport = packet->transport; __u32 max_burst_bytes; - struct sctp_opt *sp = sctp_sk(transport->asoc->base.sk); - struct sctp_outq *q = &transport->asoc->outqueue; + struct sctp_association *asoc = transport->asoc; + struct sctp_opt *sp = sctp_sk(asoc->base.sk); + struct sctp_outq *q = &asoc->outqueue; /* RFC 2960 6.1 Transmission of DATA Chunks * @@ -493,8 +494,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * receiver to the data sender. */ - rwnd = transport->asoc->peer.rwnd; - inflight = transport->asoc->outqueue.outstanding_bytes; + rwnd = asoc->peer.rwnd; + inflight = asoc->outqueue.outstanding_bytes; datasize = sctp_data_size(chunk); @@ -516,7 +517,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * if ((flightsize + Max.Burst * MTU) < cwnd) * cwnd = flightsize + Max.Burst * MTU */ - max_burst_bytes = transport->asoc->max_burst * transport->asoc->pmtu; + max_burst_bytes = asoc->max_burst * asoc->pmtu; if ((transport->flight_size + max_burst_bytes) < transport->cwnd) { transport->cwnd = transport->flight_size + max_burst_bytes; SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: " @@ -549,26 +550,20 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, } /* Nagle's algorithm to solve small-packet problem: - * inhibit the sending of new chunks when new outgoing data arrives - * if any proeviously transmitted data on the connection remains - * unacknowledged. Unless the connection was previously idle. Check - * whether the connection is idle. No outstanding means idle, flush - * it. If outstanding bytes are less than half cwnd, the connection - * is not in the state of congestion, so also flush it. + * Inhibit the sending of new chunks when new outgoing data arrives + * if any previously transmitted data on the connection remains + * unacknowledged. */ - if (!sp->nodelay && q->outstanding_bytes >= transport->cwnd >> 1) { - /* Check whether this chunk and all the rest of pending - * data will fit or whether we'll choose to delay in - * hopes of bundling a full sized packet. + if (!sp->nodelay && q->outstanding_bytes) { + unsigned len = datasize + q->out_qlen; + /* Check whether this chunk and all the rest of pending + * data will fit or delay in hopes of bundling a full + * sized packet. */ - if ((datasize + q->out_qlen) < transport->asoc->frag_point) { - - /* If the the chunk should be delay - * for future sending, we could not - * append it. - */ + + if (len < asoc->pmtu - SCTP_IP_OVERHEAD) { retval = SCTP_XMIT_NAGLE_DELAY; - goto finish; + goto finish; } } @@ -576,16 +571,15 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, transport->flight_size += datasize; /* Keep track of how many bytes are in flight to the receiver. */ - transport->asoc->outqueue.outstanding_bytes += datasize; + asoc->outqueue.outstanding_bytes += datasize; /* Update our view of the receiver's rwnd. */ - if (datasize < rwnd) { + if (datasize < rwnd) rwnd -= datasize; - } else { + else rwnd = 0; - } - transport->asoc->peer.rwnd = rwnd; + asoc->peer.rwnd = rwnd; finish: return retval; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 2ef83c5c040a..ada4a6f103f8 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -89,6 +89,16 @@ static inline void sctp_outq_tail_data(struct sctp_outq *q, return; } +/* Insert a chunk behind chunk 'pos'. */ +static inline void sctp_outq_insert_data(struct sctp_outq *q, + struct sctp_chunk *ch, + struct sctp_chunk *pos) +{ + __skb_insert((struct sk_buff *)ch, (struct sk_buff *)pos->prev, + (struct sk_buff *)pos, pos->list); + q->out_qlen += ch->skb->len; +} + /* Generate a new outqueue. */ struct sctp_outq *sctp_outq_new(sctp_association_t *asoc) { @@ -478,8 +488,8 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, * queue. 'pos' points to the next chunk in the output queue after the * chunk that is currently in the process of fragmentation. */ -void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, - struct sctp_packet *packet, sctp_chunk_t *frag, __u32 tsn) +void sctp_xmit_frag(struct sctp_outq *q, struct sctp_chunk *pos, + struct sctp_packet *packet, struct sctp_chunk *frag, __u32 tsn) { struct sctp_transport *transport = packet->transport; struct sk_buff_head *queue = &q->out; @@ -497,11 +507,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. " "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) - __skb_insert((struct sk_buff *)frag, pos->prev, - pos, pos->list); + if (pos) + sctp_outq_insert_data(q, frag, pos); else - __skb_queue_tail(queue, (struct sk_buff *) frag); + sctp_outq_tail_data(q, frag); return; } @@ -514,10 +523,9 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); if (pos) - __skb_insert((struct sk_buff *)frag, pos->prev, - pos, pos->list); + sctp_outq_insert_data(q, frag, pos); else - __skb_queue_tail(queue, (struct sk_buff *)frag); + sctp_outq_tail_data(q, frag); break; case SCTP_XMIT_OK: @@ -530,10 +538,9 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, "failed. adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); if (pos) - __skb_insert((struct sk_buff *)frag, pos->prev, - pos, pos->list); + sctp_outq_insert_data(q, frag, pos); else - __skb_queue_tail(queue,(struct sk_buff *)frag); + sctp_outq_tail_data(q, frag); } else { SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " "success. 0x%x sent\n", @@ -561,7 +568,7 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt, struct list_head *lfrag, *frag_list; __u32 tsn; int nfrags = 1; - struct sk_buff *pos; + struct sctp_chunk *pos; /* Count the number of fragments. */ frag_list = &frag->frag_list; @@ -572,7 +579,7 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt, /* Get a TSN block of nfrags TSNs. */ tsn = sctp_association_get_tsn_block(asoc, nfrags); - pos = skb_peek(&q->out); + pos = (struct sctp_chunk *)skb_peek(&q->out); /* Transmit the first fragment. */ sctp_xmit_frag(q, pos, pkt, frag, tsn++); @@ -735,7 +742,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } queue = &q->control; - while (NULL != (chunk = (sctp_chunk_t *)skb_dequeue(queue))) { + while ((chunk = (sctp_chunk_t *)skb_dequeue(queue))) { /* Pick the right transport to use. */ new_transport = chunk->transport; -- cgit v1.2.3 From 78f4b6b868f0e5bee37d896109fb772cc56e1550 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Wed, 5 Mar 2003 17:45:15 -0800 Subject: [SCTP] Minor changes for tcp-style socket support. --- include/net/sctp/constants.h | 21 +++++++++++++-------- net/sctp/sm_sideeffect.c | 16 +++++++++++----- net/sctp/socket.c | 10 ++++++++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 3cc94d900f95..99a8a1deeadc 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -210,14 +210,19 @@ typedef enum { /* These are values for sk->state. * For a UDP-style SCTP socket, the states are defined as follows - * (at this point of time, may change later after more discussions: FIXME) - * A socket in SCTP_SS_UNCONNECTED state indicates that it is not willing - * to accept new associations, but it can initiate the creation of new - * ones. - * A socket in SCTP_SS_LISTENING state indicates that it is willing to - * accept new associations and can initiate the creation of new ones. - * A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off - * socket with one association. + * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to + * accept new associations, but it can initiate the creation of new ones. + * - A socket in SCTP_SS_LISTENING state indicates that it is willing to + * accept new associations and can initiate the creation of new ones. + * - A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off + * socket with one association. + * For a TCP-style SCTP socket, the states are defined as follows + * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to + * accept new associations, but it can initiate the creation of new ones. + * - A socket in SCTP_SS_LISTENING state indicates that it is willing to + * accept new associations, but cannot initiate the creation of new ones. + * - A socket in SCTP_SS_ESTABLISHED state indicates that it has a single + * association in ESTABLISHED state. */ typedef enum { SCTP_SS_CLOSED = TCP_CLOSE, diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 74c1eefb96d5..038eb5366f3c 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -105,8 +105,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *, #define DEBUG_POST_SFX \ SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ error, asoc, \ - sctp_state_tbl[sctp_id2assoc(ep->base.sk, \ - sctp_assoc2id(asoc))?asoc->state:SCTP_STATE_CLOSED]) + sctp_state_tbl[asoc?asoc->state:SCTP_STATE_CLOSED]) /* * This is the master state machine processing function. @@ -1239,12 +1238,19 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, wake_up_interruptible(&asoc->wait); /* Wake up any processes waiting in the sk's sleep queue of - * a tcp-style or udp-style peeled-off socket in + * a TCP-style or UDP-style peeled-off socket in * sctp_wait_for_accept() or sctp_wait_for_packet(). - * For a udp-style socket, the waiters are woken up by the + * For a UDP-style socket, the waiters are woken up by the * notifications. */ - if (sp->type != SCTP_SOCKET_UDP) + if (SCTP_SOCKET_UDP != sp->type) sk->state_change(sk); } + + /* Change the sk->state of a TCP-style socket that has sucessfully + * completed a connect() call. + */ + if ((SCTP_STATE_ESTABLISHED == asoc->state) && + (SCTP_SOCKET_TCP == sp->type) && (SCTP_SS_CLOSED == sk->state)) + sk->state = SCTP_SS_ESTABLISHED; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index dbf8be0cce5c..f5e5b2d7f281 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1563,8 +1563,14 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, sp = sctp_sk(sk); ep = sp->ep; - /* connect() cannot be done on a peeled-off socket. */ - if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) { + /* connect() cannot be done on a socket that is already in ESTABLISHED + * state - UDP-style peeled off socket or a TCP-style socket that + * is already connected. + * It cannot be done even on a TCP-style listening socket. + */ + if ((SCTP_SS_ESTABLISHED == sk->state) || + ((SCTP_SOCKET_TCP == sp->type) && + (SCTP_SS_LISTENING == sk->state))) { err = -EISCONN; goto out_unlock; } -- cgit v1.2.3 From e7ee91fd79cd3d569443e43124821400e1bc5bfc Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Wed, 5 Mar 2003 23:05:04 -0600 Subject: [SCTP] Receiver SWS prevention. Still had a bit of receiver SWS as the 'SACK every other packet' & 'delayed SACK timer' advertise the true buffer size. So while the 'rwnd update' code wasn't sending SACKS, small reads in combination with many incoming packets would still generate small windows advertisements. Don't advertise a larger a_rwnd until a segment opens up. Also, fix bug over in the Nagle that was preventing most Nagling from occuring. --- net/sctp/associola.c | 32 +++++++++++++++++++++++--------- net/sctp/output.c | 5 +++-- net/sctp/sm_make_chunk.c | 2 +- net/sctp/sm_sideeffect.c | 5 ++--- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 7863476f2396..ff98ab3f7fda 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -181,7 +181,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, else asoc->rwnd = sk->rcvbuf; - asoc->a_rwnd = 0; + asoc->a_rwnd = asoc->rwnd; asoc->rwnd_over = 0; @@ -994,6 +994,24 @@ void sctp_assoc_sync_pmtu(sctp_association_t *asoc) __FUNCTION__, asoc, asoc->pmtu, asoc->frag_point); } +/* Should we send a SACK to update our peer? */ +static inline int sctp_peer_needs_update(struct sctp_association *asoc) +{ + switch (asoc->state) { + case SCTP_STATE_ESTABLISHED: + case SCTP_STATE_SHUTDOWN_PENDING: + case SCTP_STATE_SHUTDOWN_RECEIVED: + if ((asoc->rwnd > asoc->a_rwnd) && + ((asoc->rwnd - asoc->a_rwnd) >= + min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) + return 1; + break; + default: + break; + } + return 0; +} + /* Increase asoc's rwnd by len and send any window update SACK if needed. */ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) { @@ -1020,10 +1038,8 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) * The algorithm used is similar to the one described in * Section 4.2.3.3 of RFC 1122. */ - if ((asoc->state == SCTP_STATE_ESTABLISHED) && - (asoc->rwnd > asoc->a_rwnd) && - ((asoc->rwnd - asoc->a_rwnd) >= - min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) { + if (sctp_peer_needs_update(asoc)) { + asoc->a_rwnd = asoc->rwnd; SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " "rwnd: %u a_rwnd: %u\n", __FUNCTION__, asoc, asoc->rwnd, asoc->a_rwnd); @@ -1031,9 +1047,6 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) if (!sack) return; - /* Update the last advertised rwnd value. */ - asoc->a_rwnd = asoc->rwnd; - asoc->peer.sack_needed = 0; sctp_outq_tail(&asoc->outqueue, sack); @@ -1057,7 +1070,8 @@ void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len) asoc->rwnd = 0; } SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", - __FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over); + __FUNCTION__, asoc, len, asoc->rwnd, + asoc->rwnd_over); } /* Build the bind address list for the association based on info from the diff --git a/net/sctp/output.c b/net/sctp/output.c index 566e690ad53a..c02f99d602f0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -554,13 +554,14 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * if any previously transmitted data on the connection remains * unacknowledged. */ - if (!sp->nodelay && q->outstanding_bytes) { + if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size && + q->outstanding_bytes && SCTP_STATE_ESTABLISHED == asoc->state) { unsigned len = datasize + q->out_qlen; + /* Check whether this chunk and all the rest of pending * data will fit or delay in hopes of bundling a full * sized packet. */ - if (len < asoc->pmtu - SCTP_IP_OVERHEAD) { retval = SCTP_XMIT_NAGLE_DELAY; goto finish; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e64403d3a999..0f3f35affe8e 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -598,7 +598,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc) /* Initialize the SACK header. */ sack.cum_tsn_ack = htonl(ctsn); - sack.a_rwnd = htonl(asoc->rwnd); + sack.a_rwnd = htonl(asoc->a_rwnd); sack.num_gap_ack_blocks = htons(num_gabs); sack.num_dup_tsns = htons(num_dup_tsns); diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 5b3dc8a63338..76fac4ff9ec0 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -716,13 +716,12 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands) asoc->peer.sack_needed = 1; goto out; } else { + if (asoc->a_rwnd > asoc->rwnd) + asoc->a_rwnd = asoc->rwnd; sack = sctp_make_sack(asoc); if (!sack) goto nomem; - /* Update the last advertised rwnd value. */ - asoc->a_rwnd = asoc->rwnd; - asoc->peer.sack_needed = 0; error = sctp_outq_tail(&asoc->outqueue, sack); -- cgit v1.2.3 From 74f53f9a1fa2832e89e28d77fa3d4b2217d0949b Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Thu, 6 Mar 2003 16:28:19 -0600 Subject: [SCTP] Fix panic on close() TCP-style changes took out the check against valid address in the statemachine debugging code. Not good, as this protects us from accidentally touching the dead or dying association struct. --- net/sctp/sm_sideeffect.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 773a3fde4073..004b6d2f0b03 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -105,7 +105,8 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *, #define DEBUG_POST_SFX \ SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ error, asoc, \ - sctp_state_tbl[asoc?asoc->state:SCTP_STATE_CLOSED]) + sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ + sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) /* * This is the master state machine processing function. -- cgit v1.2.3