Index: head/sys/netinet/sctp_pcb.h =================================================================== --- head/sys/netinet/sctp_pcb.h (revision 359378) +++ head/sys/netinet/sctp_pcb.h (revision 359379) @@ -1,656 +1,656 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #ifndef _NETINET_SCTP_PCB_H_ #define _NETINET_SCTP_PCB_H_ #include #include #include #include LIST_HEAD(sctppcbhead, sctp_inpcb); LIST_HEAD(sctpasochead, sctp_tcb); LIST_HEAD(sctpladdr, sctp_laddr); LIST_HEAD(sctpvtaghead, sctp_tagblock); LIST_HEAD(sctp_vrflist, sctp_vrf); LIST_HEAD(sctp_ifnlist, sctp_ifn); LIST_HEAD(sctp_ifalist, sctp_ifa); TAILQ_HEAD(sctp_readhead, sctp_queued_to_read); TAILQ_HEAD(sctp_streamhead, sctp_stream_queue_pending); #include #include #define SCTP_PCBHASH_ALLADDR(port, mask) (port & mask) #define SCTP_PCBHASH_ASOC(tag, mask) (tag & mask) struct sctp_vrf { LIST_ENTRY(sctp_vrf) next_vrf; struct sctp_ifalist *vrf_addr_hash; struct sctp_ifnlist ifnlist; uint32_t vrf_id; uint32_t tbl_id_v4; /* default v4 table id */ uint32_t tbl_id_v6; /* default v6 table id */ uint32_t total_ifa_count; u_long vrf_addr_hashmark; uint32_t refcount; }; struct sctp_ifn { struct sctp_ifalist ifalist; struct sctp_vrf *vrf; LIST_ENTRY(sctp_ifn) next_ifn; LIST_ENTRY(sctp_ifn) next_bucket; void *ifn_p; /* never access without appropriate lock */ uint32_t ifn_mtu; uint32_t ifn_type; uint32_t ifn_index; /* shorthand way to look at ifn for reference */ uint32_t refcount; /* number of reference held should be >= * ifa_count */ uint32_t ifa_count; /* IFA's we hold (in our list - ifalist) */ uint32_t num_v6; /* number of v6 addresses */ uint32_t num_v4; /* number of v4 addresses */ uint32_t registered_af; /* registered address family for i/f events */ char ifn_name[SCTP_IFNAMSIZ]; }; /* SCTP local IFA flags */ #define SCTP_ADDR_VALID 0x00000001 /* its up and active */ #define SCTP_BEING_DELETED 0x00000002 /* being deleted, when * refcount = 0. Note that it * is pulled from the ifn list * and ifa_p is nulled right * away but it cannot be freed * until the last *net * pointing to it is deleted. */ #define SCTP_ADDR_DEFER_USE 0x00000004 /* Hold off using this one */ #define SCTP_ADDR_IFA_UNUSEABLE 0x00000008 struct sctp_ifa { LIST_ENTRY(sctp_ifa) next_ifa; LIST_ENTRY(sctp_ifa) next_bucket; struct sctp_ifn *ifn_p; /* back pointer to parent ifn */ void *ifa; /* pointer to ifa, needed for flag update for * that we MUST lock appropriate locks. This * is for V6. */ union sctp_sockstore address; uint32_t refcount; /* number of folks referring to this */ uint32_t flags; uint32_t localifa_flags; uint32_t vrf_id; /* vrf_id of this addr (for deleting) */ uint8_t src_is_loop; uint8_t src_is_priv; uint8_t src_is_glob; uint8_t resv; }; struct sctp_laddr { LIST_ENTRY(sctp_laddr) sctp_nxt_addr; /* next in list */ struct sctp_ifa *ifa; uint32_t action; /* Used during asconf and adding if no-zero * src-addr selection will not consider this * address. */ struct timeval start_time; /* time when this address was created */ }; struct sctp_block_entry { int error; }; struct sctp_timewait { uint32_t tv_sec_at_expire; /* the seconds from boot to expire */ uint32_t v_tag; /* the vtag that can not be reused */ uint16_t lport; /* the local port used in vtag */ uint16_t rport; /* the remote port used in vtag */ }; struct sctp_tagblock { LIST_ENTRY(sctp_tagblock) sctp_nxt_tagblock; struct sctp_timewait vtag_block[SCTP_NUMBER_IN_VTAG_BLOCK]; }; struct sctp_epinfo { #ifdef INET struct socket *udp4_tun_socket; #endif #ifdef INET6 struct socket *udp6_tun_socket; #endif struct sctpasochead *sctp_asochash; u_long hashasocmark; struct sctppcbhead *sctp_ephash; u_long hashmark; /*- * The TCP model represents a substantial overhead in that we get an * additional hash table to keep explicit connections in. The * listening TCP endpoint will exist in the usual ephash above and * accept only INIT's. It will be incapable of sending off an INIT. * When a dg arrives we must look in the normal ephash. If we find a * TCP endpoint that will tell us to go to the specific endpoint * hash and re-hash to find the right assoc/socket. If we find a UDP * model socket we then must complete the lookup. If this fails, * i.e. no association can be found then we must continue to see if * a sctp_peeloff()'d socket is in the tcpephash (a spun off socket * acts like a TCP model connected socket). */ struct sctppcbhead *sctp_tcpephash; u_long hashtcpmark; uint32_t hashtblsize; struct sctp_vrflist *sctp_vrfhash; u_long hashvrfmark; struct sctp_ifnlist *vrf_ifn_hash; u_long vrf_ifn_hashmark; struct sctppcbhead listhead; struct sctpladdr addr_wq; /* ep zone info */ sctp_zone_t ipi_zone_ep; sctp_zone_t ipi_zone_asoc; sctp_zone_t ipi_zone_laddr; sctp_zone_t ipi_zone_net; sctp_zone_t ipi_zone_chunk; sctp_zone_t ipi_zone_readq; sctp_zone_t ipi_zone_strmoq; sctp_zone_t ipi_zone_asconf; sctp_zone_t ipi_zone_asconf_ack; struct rwlock ipi_ep_mtx; struct mtx ipi_iterator_wq_mtx; struct rwlock ipi_addr_mtx; struct mtx ipi_pktlog_mtx; struct mtx wq_addr_mtx; uint32_t ipi_count_ep; /* assoc/tcb zone info */ uint32_t ipi_count_asoc; /* local addrlist zone info */ uint32_t ipi_count_laddr; /* remote addrlist zone info */ uint32_t ipi_count_raddr; /* chunk structure list for output */ uint32_t ipi_count_chunk; /* socket queue zone info */ uint32_t ipi_count_readq; /* socket queue zone info */ uint32_t ipi_count_strmoq; /* Number of vrfs */ uint32_t ipi_count_vrfs; /* Number of ifns */ uint32_t ipi_count_ifns; /* Number of ifas */ uint32_t ipi_count_ifas; /* system wide number of free chunks hanging around */ uint32_t ipi_free_chunks; uint32_t ipi_free_strmoq; struct sctpvtaghead vtag_timewait[SCTP_STACK_VTAG_HASH_SIZE]; /* address work queue handling */ struct sctp_timer addr_wq_timer; }; struct sctp_base_info { /* * All static structures that anchor the system must be here. */ struct sctp_epinfo sctppcbinfo; #if defined(__FreeBSD__) && defined(SMP) && defined(SCTP_USE_PERCPU_STAT) struct sctpstat *sctpstat; #else struct sctpstat sctpstat; #endif struct sctp_sysctl sctpsysctl; uint8_t first_time; char sctp_pcb_initialized; #if defined(SCTP_PACKET_LOGGING) int packet_log_writers; int packet_log_end; uint8_t packet_log_buffer[SCTP_PACKET_LOG_SIZE]; #endif eventhandler_tag eh_tag; }; /*- * Here we have all the relevant information for each SCTP entity created. We * will need to modify this as approprate. We also need to figure out how to * access /dev/random. */ struct sctp_pcb { unsigned int time_of_secret_change; /* number of seconds from * timeval.tv_sec */ uint32_t secret_key[SCTP_HOW_MANY_SECRETS][SCTP_NUMBER_OF_SECRETS]; unsigned int size_of_a_cookie; - unsigned int sctp_timeoutticks[SCTP_NUM_TMRS]; - unsigned int sctp_minrto; - unsigned int sctp_maxrto; - unsigned int initial_rto; - int initial_init_rto_max; + uint32_t sctp_timeoutticks[SCTP_NUM_TMRS]; + uint32_t sctp_minrto; + uint32_t sctp_maxrto; + uint32_t initial_rto; + uint32_t initial_init_rto_max; unsigned int sctp_sack_freq; uint32_t sctp_sws_sender; uint32_t sctp_sws_receiver; uint32_t sctp_default_cc_module; uint32_t sctp_default_ss_module; /* authentication related fields */ struct sctp_keyhead shared_keys; sctp_auth_chklist_t *local_auth_chunks; sctp_hmaclist_t *local_hmacs; uint16_t default_keyid; uint32_t default_mtu; /* various thresholds */ /* Max times I will init at a guy */ uint16_t max_init_times; /* Max times I will send before we consider someone dead */ uint16_t max_send_times; uint16_t def_net_failure; uint16_t def_net_pf_threshold; /* number of streams to pre-open on a association */ uint16_t pre_open_stream_count; uint16_t max_open_streams_intome; /* random number generator */ uint32_t random_counter; uint8_t random_numbers[SCTP_SIGNATURE_ALOC_SIZE]; uint8_t random_store[SCTP_SIGNATURE_ALOC_SIZE]; /* * This timer is kept running per endpoint. When it fires it will * change the secret key. The default is once a hour */ struct sctp_timer signature_change; uint32_t def_cookie_life; /* defaults to 0 */ uint32_t auto_close_time; uint32_t initial_sequence_debug; uint32_t adaptation_layer_indicator; uint8_t adaptation_layer_indicator_provided; uint32_t store_at; uint32_t max_burst; uint32_t fr_max_burst; #ifdef INET6 uint32_t default_flowlabel; #endif uint8_t default_dscp; char current_secret_number; char last_secret_number; uint16_t port; /* remote UDP encapsulation port */ }; #ifndef SCTP_ALIGNMENT #define SCTP_ALIGNMENT 32 #endif #ifndef SCTP_ALIGNM1 #define SCTP_ALIGNM1 (SCTP_ALIGNMENT-1) #endif #define sctp_lport ip_inp.inp.inp_lport struct sctp_pcbtsn_rlog { uint32_t vtag; uint16_t strm; uint16_t seq; uint16_t sz; uint16_t flgs; }; #define SCTP_READ_LOG_SIZE 135 /* we choose the number to make a pcb a page */ struct sctp_inpcb { /*- * put an inpcb in front of it all, kind of a waste but we need to * for compatibility with all the other stuff. */ union { struct inpcb inp; char align[(sizeof(struct inpcb) + SCTP_ALIGNM1) & ~SCTP_ALIGNM1]; } ip_inp; /* Socket buffer lock protects read_queue and of course sb_cc */ struct sctp_readhead read_queue; LIST_ENTRY(sctp_inpcb) sctp_list; /* lists all endpoints */ /* hash of all endpoints for model */ LIST_ENTRY(sctp_inpcb) sctp_hash; /* count of local addresses bound, 0 if bound all */ int laddr_count; /* list of addrs in use by the EP, NULL if bound-all */ struct sctpladdr sctp_addr_list; /* * used for source address selection rotation when we are subset * bound */ struct sctp_laddr *next_addr_touse; /* back pointer to our socket */ struct socket *sctp_socket; uint64_t sctp_features; /* Feature flags */ uint32_t sctp_flags; /* INP state flag set */ uint32_t sctp_mobility_features; /* Mobility Feature flags */ struct sctp_pcb sctp_ep; /* SCTP ep data */ /* head of the hash of all associations */ struct sctpasochead *sctp_tcbhash; u_long sctp_hashmark; /* head of the list of all associations */ struct sctpasochead sctp_asoc_list; #ifdef SCTP_TRACK_FREED_ASOCS struct sctpasochead sctp_asoc_free_list; #endif struct sctp_iterator *inp_starting_point_for_iterator; uint32_t sctp_frag_point; uint32_t partial_delivery_point; uint32_t sctp_context; uint32_t max_cwnd; uint8_t local_strreset_support; uint32_t sctp_cmt_on_off; uint8_t ecn_supported; uint8_t prsctp_supported; uint8_t auth_supported; uint8_t idata_supported; uint8_t asconf_supported; uint8_t reconfig_supported; uint8_t nrsack_supported; uint8_t pktdrop_supported; struct sctp_nonpad_sndrcvinfo def_send; /*- * These three are here for the sosend_dgram * (pkt, pkt_last and control). * routine. However, I don't think anyone in * the current FreeBSD kernel calls this. So * they are candidates with sctp_sendm for * de-supporting. */ struct mbuf *pkt, *pkt_last; struct mbuf *control; struct mtx inp_mtx; struct mtx inp_create_mtx; struct mtx inp_rdata_mtx; int32_t refcount; uint32_t def_vrf_id; uint16_t fibnum; uint32_t total_sends; uint32_t total_recvs; uint32_t last_abort_code; uint32_t total_nospaces; struct sctpasochead *sctp_asocidhash; u_long hashasocidmark; uint32_t sctp_associd_counter; #ifdef SCTP_ASOCLOG_OF_TSNS struct sctp_pcbtsn_rlog readlog[SCTP_READ_LOG_SIZE]; uint32_t readlog_index; #endif }; struct sctp_tcb { struct socket *sctp_socket; /* back pointer to socket */ struct sctp_inpcb *sctp_ep; /* back pointer to ep */ LIST_ENTRY(sctp_tcb) sctp_tcbhash; /* next link in hash * table */ LIST_ENTRY(sctp_tcb) sctp_tcblist; /* list of all of the * TCB's */ LIST_ENTRY(sctp_tcb) sctp_tcbasocidhash; /* next link in asocid * hash table */ LIST_ENTRY(sctp_tcb) sctp_asocs; /* vtag hash list */ struct sctp_block_entry *block_entry; /* pointer locked by socket * send buffer */ struct sctp_association asoc; /*- * freed_by_sorcv_sincelast is protected by the sockbuf_lock NOT the * tcb_lock. Its special in this way to help avoid extra mutex calls * in the reading of data. */ uint32_t freed_by_sorcv_sincelast; uint32_t total_sends; uint32_t total_recvs; int freed_from_where; uint16_t rport; /* remote port in network format */ uint16_t resv; struct mtx tcb_mtx; struct mtx tcb_send_mtx; }; #include /* TODO where to put non-_KERNEL things for __Userspace__? */ #if defined(_KERNEL) || defined(__Userspace__) /* Attention Julian, this is the extern that * goes with the base info. sctp_pcb.c has * the real definition. */ VNET_DECLARE(struct sctp_base_info, system_base_info); #ifdef INET6 int SCTP6_ARE_ADDR_EQUAL(struct sockaddr_in6 *a, struct sockaddr_in6 *b); #endif void sctp_fill_pcbinfo(struct sctp_pcbinfo *); struct sctp_ifn *sctp_find_ifn(void *ifn, uint32_t ifn_index); struct sctp_vrf *sctp_allocate_vrf(int vrfid); struct sctp_vrf *sctp_find_vrf(uint32_t vrfid); void sctp_free_vrf(struct sctp_vrf *vrf); /*- * Change address state, can be used if * O/S supports telling transports about * changes to IFA/IFN's (link layer triggers). * If a ifn goes down, we will do src-addr-selection * and NOT use that, as a source address. This does * not stop the routing system from routing out * that interface, but we won't put it as a source. */ void sctp_mark_ifa_addr_down(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index); void sctp_mark_ifa_addr_up(uint32_t vrf_id, struct sockaddr *addr, const char *if_name, uint32_t ifn_index); struct sctp_ifa * sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, uint32_t ifn_type, const char *if_name, void *ifa, struct sockaddr *addr, uint32_t ifa_flags, int dynamic_add); void sctp_update_ifn_mtu(uint32_t ifn_index, uint32_t mtu); void sctp_free_ifn(struct sctp_ifn *sctp_ifnp); void sctp_free_ifa(struct sctp_ifa *sctp_ifap); void sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr, uint32_t ifn_index, const char *if_name); struct sctp_nets *sctp_findnet(struct sctp_tcb *, struct sockaddr *); struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t); int sctp_inpcb_bind(struct socket *, struct sockaddr *, struct sctp_ifa *, struct thread *); struct sctp_tcb * sctp_findassociation_addr(struct mbuf *, int, struct sockaddr *, struct sockaddr *, struct sctphdr *, struct sctp_chunkhdr *, struct sctp_inpcb **, struct sctp_nets **, uint32_t vrf_id); struct sctp_tcb * sctp_findassociation_addr_sa(struct sockaddr *, struct sockaddr *, struct sctp_inpcb **, struct sctp_nets **, int, uint32_t); void sctp_move_pcb_and_assoc(struct sctp_inpcb *, struct sctp_inpcb *, struct sctp_tcb *); /*- * For this call ep_addr, the to is the destination endpoint address of the * peer (relative to outbound). The from field is only used if the TCP model * is enabled and helps distingush amongst the subset bound (non-boundall). * The TCP model MAY change the actual ep field, this is why it is passed. */ struct sctp_tcb * sctp_findassociation_ep_addr(struct sctp_inpcb **, struct sockaddr *, struct sctp_nets **, struct sockaddr *, struct sctp_tcb *); struct sctp_tcb *sctp_findasoc_ep_asocid_locked(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock); struct sctp_tcb * sctp_findassociation_ep_asocid(struct sctp_inpcb *, sctp_assoc_t, int); struct sctp_tcb * sctp_findassociation_ep_asconf(struct mbuf *, int, struct sockaddr *, struct sctphdr *, struct sctp_inpcb **, struct sctp_nets **, uint32_t vrf_id); int sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id); int sctp_is_address_on_local_host(struct sockaddr *addr, uint32_t vrf_id); void sctp_inpcb_free(struct sctp_inpcb *, int, int); #define SCTP_DONT_INITIALIZE_AUTH_PARAMS 0 #define SCTP_INITIALIZE_AUTH_PARAMS 1 struct sctp_tcb * sctp_aloc_assoc(struct sctp_inpcb *, struct sockaddr *, int *, uint32_t, uint32_t, uint16_t, uint16_t, struct thread *, int); int sctp_free_assoc(struct sctp_inpcb *, struct sctp_tcb *, int, int); void sctp_delete_from_timewait(uint32_t, uint16_t, uint16_t); int sctp_is_in_timewait(uint32_t tag, uint16_t lport, uint16_t rport); void sctp_add_vtag_to_timewait(uint32_t tag, uint32_t time, uint16_t lport, uint16_t rport); void sctp_add_local_addr_ep(struct sctp_inpcb *, struct sctp_ifa *, uint32_t); void sctp_del_local_addr_ep(struct sctp_inpcb *, struct sctp_ifa *); int sctp_add_remote_addr(struct sctp_tcb *, struct sockaddr *, struct sctp_nets **, uint16_t, int, int); void sctp_remove_net(struct sctp_tcb *, struct sctp_nets *); int sctp_del_remote_addr(struct sctp_tcb *, struct sockaddr *); void sctp_pcb_init(void); void sctp_pcb_finish(void); void sctp_add_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *); void sctp_del_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *); int sctp_load_addresses_from_init(struct sctp_tcb *, struct mbuf *, int, int, struct sockaddr *, struct sockaddr *, struct sockaddr *, uint16_t); int sctp_set_primary_addr(struct sctp_tcb *, struct sockaddr *, struct sctp_nets *); int sctp_is_vtag_good(uint32_t, uint16_t lport, uint16_t rport, struct timeval *); /* void sctp_drain(void); */ int sctp_destination_is_reachable(struct sctp_tcb *, struct sockaddr *); int sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp); void sctp_clean_up_stream(struct sctp_tcb *stcb, struct sctp_readhead *rh); /*- * Null in last arg inpcb indicate run on ALL ep's. Specific inp in last arg * indicates run on ONLY assoc's of the specified endpoint. */ int sctp_initiate_iterator(inp_func inpf, asoc_func af, inp_func inpe, uint32_t, uint32_t, uint32_t, void *, uint32_t, end_func ef, struct sctp_inpcb *, uint8_t co_off); #if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) void sctp_queue_to_mcore(struct mbuf *m, int off, int cpu_to_use); #endif #endif /* _KERNEL */ #endif /* !__sctp_pcb_h__ */ Index: head/sys/netinet/sctp_structs.h =================================================================== --- head/sys/netinet/sctp_structs.h (revision 359378) +++ head/sys/netinet/sctp_structs.h (revision 359379) @@ -1,1250 +1,1250 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #ifndef _NETINET_SCTP_STRUCTS_H_ #define _NETINET_SCTP_STRUCTS_H_ #include #include #include struct sctp_timer { sctp_os_timer_t timer; int type; /* * Depending on the timer type these will be setup and cast with the * appropriate entity. */ void *ep; void *tcb; void *net; void *vnet; /* for sanity checking */ void *self; uint32_t ticks; uint32_t stopped_from; }; struct sctp_foo_stuff { struct sctp_inpcb *inp; uint32_t lineno; uint32_t ticks; int updown; }; /* * This is the information we track on each interface that we know about from * the distant end. */ TAILQ_HEAD(sctpnetlisthead, sctp_nets); struct sctp_stream_reset_list { TAILQ_ENTRY(sctp_stream_reset_list) next_resp; uint32_t seq; uint32_t tsn; uint32_t number_entries; uint16_t list_of_streams[]; }; TAILQ_HEAD(sctp_resethead, sctp_stream_reset_list); /* * Users of the iterator need to malloc a iterator with a call to * sctp_initiate_iterator(inp_func, assoc_func, inp_func, pcb_flags, pcb_features, * asoc_state, void-ptr-arg, uint32-arg, end_func, inp); * * Use the following two defines if you don't care what pcb flags are on the EP * and/or you don't care what state the association is in. * * Note that if you specify an INP as the last argument then ONLY each * association of that single INP will be executed upon. Note that the pcb * flags STILL apply so if the inp you specify has different pcb_flags then * what you put in pcb_flags nothing will happen. use SCTP_PCB_ANY_FLAGS to * assure the inp you specify gets treated. */ #define SCTP_PCB_ANY_FLAGS 0x00000000 #define SCTP_PCB_ANY_FEATURES 0x00000000 #define SCTP_ASOC_ANY_STATE 0x00000000 typedef void (*asoc_func) (struct sctp_inpcb *, struct sctp_tcb *, void *ptr, uint32_t val); typedef int (*inp_func) (struct sctp_inpcb *, void *ptr, uint32_t val); typedef void (*end_func) (void *ptr, uint32_t val); #if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) /* whats on the mcore control struct */ struct sctp_mcore_queue { TAILQ_ENTRY(sctp_mcore_queue) next; struct vnet *vn; struct mbuf *m; int off; int v6; }; TAILQ_HEAD(sctp_mcore_qhead, sctp_mcore_queue); struct sctp_mcore_ctrl { SCTP_PROCESS_STRUCT thread_proc; struct sctp_mcore_qhead que; struct mtx core_mtx; struct mtx que_mtx; int running; int cpuid; }; #endif struct sctp_iterator { TAILQ_ENTRY(sctp_iterator) sctp_nxt_itr; struct vnet *vn; struct sctp_timer tmr; struct sctp_inpcb *inp; /* current endpoint */ struct sctp_tcb *stcb; /* current* assoc */ struct sctp_inpcb *next_inp; /* special hook to skip to */ asoc_func function_assoc; /* per assoc function */ inp_func function_inp; /* per endpoint function */ inp_func function_inp_end; /* end INP function */ end_func function_atend; /* iterator completion function */ void *pointer; /* pointer for apply func to use */ uint32_t val; /* value for apply func to use */ uint32_t pcb_flags; /* endpoint flags being checked */ uint32_t pcb_features; /* endpoint features being checked */ uint32_t asoc_state; /* assoc state being checked */ uint32_t iterator_flags; uint8_t no_chunk_output; uint8_t done_current_ep; }; /* iterator_flags values */ #define SCTP_ITERATOR_DO_ALL_INP 0x00000001 #define SCTP_ITERATOR_DO_SINGLE_INP 0x00000002 TAILQ_HEAD(sctpiterators, sctp_iterator); struct sctp_copy_all { struct sctp_inpcb *inp; /* ep */ struct mbuf *m; struct sctp_sndrcvinfo sndrcv; ssize_t sndlen; int cnt_sent; int cnt_failed; }; struct sctp_asconf_iterator { struct sctpladdr list_of_work; int cnt; }; struct iterator_control { struct mtx ipi_iterator_wq_mtx; struct mtx it_mtx; SCTP_PROCESS_STRUCT thread_proc; struct sctpiterators iteratorhead; struct sctp_iterator *cur_it; uint32_t iterator_running; uint32_t iterator_flags; }; #define SCTP_ITERATOR_STOP_CUR_IT 0x00000004 #define SCTP_ITERATOR_STOP_CUR_INP 0x00000008 struct sctp_net_route { sctp_rtentry_t *ro_rt; struct llentry *ro_lle; char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; uint16_t ro_mtu; uint16_t spare; union sctp_sockstore _l_addr; /* remote peer addr */ struct sctp_ifa *_s_addr; /* our selected src addr */ }; struct htcp { uint16_t alpha; /* Fixed point arith, << 7 */ uint8_t beta; /* Fixed point arith, << 7 */ uint8_t modeswitch; /* Delay modeswitch until we had at least one * congestion event */ uint32_t last_cong; /* Time since last congestion event end */ uint32_t undo_last_cong; uint16_t bytes_acked; uint32_t bytecount; uint32_t minRTT; uint32_t maxRTT; uint32_t undo_maxRTT; uint32_t undo_old_maxB; /* Bandwidth estimation */ uint32_t minB; uint32_t maxB; uint32_t old_maxB; uint32_t Bi; uint32_t lasttime; }; struct rtcc_cc { struct timeval tls; /* The time we started the sending */ uint64_t lbw; /* Our last estimated bw */ uint64_t lbw_rtt; /* RTT at bw estimate */ uint64_t bw_bytes; /* The total bytes since this sending began */ uint64_t bw_tot_time; /* The total time since sending began */ uint64_t new_tot_time; /* temp holding the new value */ uint64_t bw_bytes_at_last_rttc; /* What bw_bytes was at last rtt calc */ uint32_t cwnd_at_bw_set; /* Cwnd at last bw saved - lbw */ uint32_t vol_reduce; /* cnt of voluntary reductions */ uint16_t steady_step; /* The number required to be in steady state */ uint16_t step_cnt; /* The current number */ uint8_t ret_from_eq; /* When all things are equal what do I return * 0/1 - 1 no cc advance */ uint8_t use_dccc_ecn; /* Flag to enable DCCC ECN */ uint8_t tls_needs_set; /* Flag to indicate we need to set tls 0 or 1 * means set at send 2 not */ uint8_t last_step_state; /* Last state if steady state stepdown * is on */ uint8_t rtt_set_this_sack; /* Flag saying this sack had RTT calc * on it */ uint8_t last_inst_ind; /* Last saved inst indication */ }; struct sctp_nets { TAILQ_ENTRY(sctp_nets) sctp_next; /* next link */ /* * Things on the top half may be able to be split into a common * structure shared by all. */ struct sctp_timer pmtu_timer; struct sctp_timer hb_timer; /* * The following two in combination equate to a route entry for v6 * or v4. */ struct sctp_net_route ro; /* mtu discovered so far */ uint32_t mtu; uint32_t ssthresh; /* not sure about this one for split */ uint32_t last_cwr_tsn; uint32_t cwr_window_tsn; uint32_t ecn_ce_pkt_cnt; uint32_t lost_cnt; /* smoothed average things for RTT and RTO itself */ int lastsa; int lastsv; uint64_t rtt; /* last measured rtt value in us */ uint32_t RTO; /* This is used for SHUTDOWN/SHUTDOWN-ACK/SEND or INIT timers */ struct sctp_timer rxt_timer; /* last time in seconds I sent to it */ struct timeval last_sent_time; union cc_control_data { struct htcp htcp_ca; /* JRS - struct used in HTCP algorithm */ struct rtcc_cc rtcc; /* rtcc module cc stuff */ } cc_mod; int ref_count; /* Congestion stats per destination */ /* * flight size variables and such, sorry Vern, I could not avoid * this if I wanted performance :> */ uint32_t flight_size; uint32_t cwnd; /* actual cwnd */ uint32_t prev_cwnd; /* cwnd before any processing */ uint32_t ecn_prev_cwnd; /* ECN prev cwnd at first ecn_echo seen in new * window */ uint32_t partial_bytes_acked; /* in CA tracks when to incr a MTU */ /* tracking variables to avoid the aloc/free in sack processing */ unsigned int net_ack; unsigned int net_ack2; /* * JRS - 5/8/07 - Variable to track last time a destination was * active for CMT PF */ uint32_t last_active; /* * CMT variables (iyengar@cis.udel.edu) */ uint32_t this_sack_highest_newack; /* tracks highest TSN newly * acked for a given dest in * the current SACK. Used in * SFR and HTNA algos */ uint32_t pseudo_cumack; /* CMT CUC algorithm. Maintains next expected * pseudo-cumack for this destination */ uint32_t rtx_pseudo_cumack; /* CMT CUC algorithm. Maintains next * expected pseudo-cumack for this * destination */ /* CMT fast recovery variables */ uint32_t fast_recovery_tsn; uint32_t heartbeat_random1; uint32_t heartbeat_random2; #ifdef INET6 uint32_t flowlabel; #endif uint8_t dscp; struct timeval start_time; /* time when this net was created */ uint32_t marked_retrans; /* number or DATA chunks marked for * timer based retransmissions */ uint32_t marked_fastretrans; uint32_t heart_beat_delay; /* Heart Beat delay in ms */ /* if this guy is ok or not ... status */ uint16_t dest_state; /* number of timeouts to consider the destination unreachable */ uint16_t failure_threshold; /* number of timeouts to consider the destination potentially failed */ uint16_t pf_threshold; /* error stats on the destination */ uint16_t error_count; /* UDP port number in case of UDP tunneling */ uint16_t port; uint8_t fast_retran_loss_recovery; uint8_t will_exit_fast_recovery; /* Flags that probably can be combined into dest_state */ uint8_t fast_retran_ip; /* fast retransmit in progress */ uint8_t hb_responded; uint8_t saw_newack; /* CMT's SFR algorithm flag */ uint8_t src_addr_selected; /* if we split we move */ uint8_t indx_of_eligible_next_to_use; uint8_t addr_is_local; /* its a local address (if known) could move * in split */ /* * CMT variables (iyengar@cis.udel.edu) */ uint8_t find_pseudo_cumack; /* CMT CUC algorithm. Flag used to * find a new pseudocumack. This flag * is set after a new pseudo-cumack * has been received and indicates * that the sender should find the * next pseudo-cumack expected for * this destination */ uint8_t find_rtx_pseudo_cumack; /* CMT CUCv2 algorithm. Flag used to * find a new rtx-pseudocumack. This * flag is set after a new * rtx-pseudo-cumack has been received * and indicates that the sender * should find the next * rtx-pseudo-cumack expected for this * destination */ uint8_t new_pseudo_cumack; /* CMT CUC algorithm. Flag used to * indicate if a new pseudo-cumack or * rtx-pseudo-cumack has been received */ uint8_t window_probe; /* Doing a window probe? */ uint8_t RTO_measured; /* Have we done the first measure */ uint8_t last_hs_used; /* index into the last HS table entry we used */ uint8_t lan_type; uint8_t rto_needed; uint32_t flowid; uint8_t flowtype; }; struct sctp_data_chunkrec { uint32_t tsn; /* the TSN of this transmit */ uint32_t mid; /* the message identifier of this transmit */ uint16_t sid; /* the stream number of this guy */ uint32_t ppid; uint32_t context; /* from send */ uint32_t cwnd_at_send; /* * part of the Highest sacked algorithm to be able to stroke counts * on ones that are FR'd. */ uint32_t fast_retran_tsn; /* sending_seq at the time of FR */ struct timeval timetodrop; /* time we drop it from queue */ uint32_t fsn; /* Fragment Sequence Number */ uint8_t doing_fast_retransmit; uint8_t rcv_flags; /* flags pulled from data chunk on inbound for * outbound holds sending flags for PR-SCTP. */ uint8_t state_flags; uint8_t chunk_was_revoked; uint8_t fwd_tsn_cnt; }; TAILQ_HEAD(sctpchunk_listhead, sctp_tmit_chunk); /* The lower byte is used to enumerate PR_SCTP policies */ #define CHUNK_FLAGS_PR_SCTP_TTL SCTP_PR_SCTP_TTL #define CHUNK_FLAGS_PR_SCTP_BUF SCTP_PR_SCTP_BUF #define CHUNK_FLAGS_PR_SCTP_RTX SCTP_PR_SCTP_RTX /* The upper byte is used as a bit mask */ #define CHUNK_FLAGS_FRAGMENT_OK 0x0100 struct chk_id { uint8_t id; uint8_t can_take_data; }; struct sctp_tmit_chunk { union { struct sctp_data_chunkrec data; struct chk_id chunk_id; } rec; struct sctp_association *asoc; /* bp to asoc this belongs to */ struct timeval sent_rcv_time; /* filled in if RTT being calculated */ struct mbuf *data; /* pointer to mbuf chain of data */ struct mbuf *last_mbuf; /* pointer to last mbuf in chain */ struct sctp_nets *whoTo; TAILQ_ENTRY(sctp_tmit_chunk) sctp_next; /* next link */ int32_t sent; /* the send status */ uint16_t snd_count; /* number of times I sent */ uint16_t flags; /* flags, such as FRAGMENT_OK */ uint16_t send_size; uint16_t book_size; uint16_t mbcnt; uint16_t auth_keyid; uint8_t holds_key_ref; /* flag if auth keyid refcount is held */ uint8_t pad_inplace; uint8_t do_rtt; uint8_t book_size_scale; uint8_t no_fr_allowed; uint8_t copy_by_ref; uint8_t window_probe; }; struct sctp_queued_to_read { /* sinfo structure Pluse more */ uint16_t sinfo_stream; /* off the wire */ uint16_t sinfo_flags; /* SCTP_UNORDERED from wire use SCTP_EOF for * EOR */ uint32_t sinfo_ppid; /* off the wire */ uint32_t sinfo_context; /* pick this up from assoc def context? */ uint32_t sinfo_timetolive; /* not used by kernel */ uint32_t sinfo_tsn; /* Use this in reassembly as first TSN */ uint32_t sinfo_cumtsn; /* Use this in reassembly as last TSN */ sctp_assoc_t sinfo_assoc_id; /* our assoc id */ /* Non sinfo stuff */ uint32_t mid; /* Fragment Index */ uint32_t length; /* length of data */ uint32_t held_length; /* length held in sb */ uint32_t top_fsn; /* Highest FSN in queue */ uint32_t fsn_included; /* Highest FSN in *data portion */ struct sctp_nets *whoFrom; /* where it came from */ struct mbuf *data; /* front of the mbuf chain of data with * PKT_HDR */ struct mbuf *tail_mbuf; /* used for multi-part data */ struct mbuf *aux_data; /* used to hold/cache control if o/s does not * take it from us */ struct sctp_tcb *stcb; /* assoc, used for window update */ TAILQ_ENTRY(sctp_queued_to_read) next; TAILQ_ENTRY(sctp_queued_to_read) next_instrm; struct sctpchunk_listhead reasm; uint16_t port_from; uint16_t spec_flags; /* Flags to hold the notification field */ uint8_t do_not_ref_stcb; uint8_t end_added; uint8_t pdapi_aborted; uint8_t pdapi_started; uint8_t some_taken; uint8_t last_frag_seen; uint8_t first_frag_seen; uint8_t on_read_q; uint8_t on_strm_q; }; #define SCTP_ON_ORDERED 1 #define SCTP_ON_UNORDERED 2 /* This data structure will be on the outbound * stream queues. Data will be pulled off from * the front of the mbuf data and chunk-ified * by the output routines. We will custom * fit every chunk we pull to the send/sent * queue to make up the next full packet * if we can. An entry cannot be removed * from the stream_out queue until * the msg_is_complete flag is set. This * means at times data/tail_mbuf MIGHT * be NULL.. If that occurs it happens * for one of two reasons. Either the user * is blocked on a send() call and has not * awoken to copy more data down... OR * the user is in the explict MSG_EOR mode * and wrote some data, but has not completed * sending. */ struct sctp_stream_queue_pending { struct mbuf *data; struct mbuf *tail_mbuf; struct timeval ts; struct sctp_nets *net; TAILQ_ENTRY(sctp_stream_queue_pending) next; TAILQ_ENTRY(sctp_stream_queue_pending) ss_next; uint32_t fsn; uint32_t length; uint32_t timetolive; uint32_t ppid; uint32_t context; uint16_t sinfo_flags; uint16_t sid; uint16_t act_flags; uint16_t auth_keyid; uint8_t holds_key_ref; uint8_t msg_is_complete; uint8_t some_taken; uint8_t sender_all_done; uint8_t put_last_out; uint8_t discard_rest; }; /* * this struct contains info that is used to track inbound stream data and * help with ordering. */ TAILQ_HEAD(sctpwheelunrel_listhead, sctp_stream_in); struct sctp_stream_in { struct sctp_readhead inqueue; struct sctp_readhead uno_inqueue; uint32_t last_mid_delivered; /* used for re-order */ uint16_t sid; uint8_t delivery_started; uint8_t pd_api_started; }; TAILQ_HEAD(sctpwheel_listhead, sctp_stream_out); TAILQ_HEAD(sctplist_listhead, sctp_stream_queue_pending); /* Round-robin schedulers */ struct ss_rr { /* next link in wheel */ TAILQ_ENTRY(sctp_stream_out) next_spoke; }; /* Priority scheduler */ struct ss_prio { /* next link in wheel */ TAILQ_ENTRY(sctp_stream_out) next_spoke; /* priority id */ uint16_t priority; }; /* Fair Bandwidth scheduler */ struct ss_fb { /* next link in wheel */ TAILQ_ENTRY(sctp_stream_out) next_spoke; /* stores message size */ int32_t rounds; }; /* * This union holds all data necessary for * different stream schedulers. */ struct scheduling_data { struct sctp_stream_out *locked_on_sending; /* circular looking for output selection */ struct sctp_stream_out *last_out_stream; union { struct sctpwheel_listhead wheel; struct sctplist_listhead list; } out; }; /* * This union holds all parameters per stream * necessary for different stream schedulers. */ union scheduling_parameters { struct ss_rr rr; struct ss_prio prio; struct ss_fb fb; }; /* States for outgoing streams */ #define SCTP_STREAM_CLOSED 0x00 #define SCTP_STREAM_OPENING 0x01 #define SCTP_STREAM_OPEN 0x02 #define SCTP_STREAM_RESET_PENDING 0x03 #define SCTP_STREAM_RESET_IN_FLIGHT 0x04 #define SCTP_MAX_STREAMS_AT_ONCE_RESET 200 /* This struct is used to track the traffic on outbound streams */ struct sctp_stream_out { struct sctp_streamhead outqueue; union scheduling_parameters ss_params; uint32_t chunks_on_queues; /* send queue and sent queue */ #if defined(SCTP_DETAILED_STR_STATS) uint32_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1]; uint32_t abandoned_sent[SCTP_PR_SCTP_MAX + 1]; #else /* Only the aggregation */ uint32_t abandoned_unsent[1]; uint32_t abandoned_sent[1]; #endif /* * For associations using DATA chunks, the lower 16-bit of * next_mid_ordered are used as the next SSN. */ uint32_t next_mid_ordered; uint32_t next_mid_unordered; uint16_t sid; uint8_t last_msg_incomplete; uint8_t state; }; /* used to keep track of the addresses yet to try to add/delete */ TAILQ_HEAD(sctp_asconf_addrhead, sctp_asconf_addr); struct sctp_asconf_addr { TAILQ_ENTRY(sctp_asconf_addr) next; struct sctp_asconf_addr_param ap; struct sctp_ifa *ifa; /* save the ifa for add/del ip */ uint8_t sent; /* has this been sent yet? */ uint8_t special_del; /* not to be used in lookup */ }; struct sctp_scoping { uint8_t ipv4_addr_legal; uint8_t ipv6_addr_legal; uint8_t loopback_scope; uint8_t ipv4_local_scope; uint8_t local_scope; uint8_t site_scope; }; #define SCTP_TSN_LOG_SIZE 40 struct sctp_tsn_log { void *stcb; uint32_t tsn; uint32_t seq; uint16_t strm; uint16_t sz; uint16_t flgs; uint16_t in_pos; uint16_t in_out; uint16_t resv; }; #define SCTP_FS_SPEC_LOG_SIZE 200 struct sctp_fs_spec_log { uint32_t sent; uint32_t total_flight; uint32_t tsn; uint16_t book; uint8_t incr; uint8_t decr; }; /* This struct is here to cut out the compatiabilty * pad that bulks up both the inp and stcb. The non * pad portion MUST stay in complete sync with * sctp_sndrcvinfo... i.e. if sinfo_xxxx is added * this must be done here too. */ struct sctp_nonpad_sndrcvinfo { uint16_t sinfo_stream; uint16_t sinfo_ssn; uint16_t sinfo_flags; uint32_t sinfo_ppid; uint32_t sinfo_context; uint32_t sinfo_timetolive; uint32_t sinfo_tsn; uint32_t sinfo_cumtsn; sctp_assoc_t sinfo_assoc_id; uint16_t sinfo_keynumber; uint16_t sinfo_keynumber_valid; }; /* * JRS - Structure to hold function pointers to the functions responsible * for congestion control. */ struct sctp_cc_functions { void (*sctp_set_initial_cc_param) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_sack) (struct sctp_tcb *stcb, struct sctp_association *asoc, int accum_moved, int reneged_all, int will_exit); void (*sctp_cwnd_update_exit_pf) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_fr) (struct sctp_tcb *stcb, struct sctp_association *asoc); void (*sctp_cwnd_update_after_timeout) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_ecn_echo) (struct sctp_tcb *stcb, struct sctp_nets *net, int in_window, int num_pkt_lost); void (*sctp_cwnd_update_after_packet_dropped) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_pktdrop_chunk *cp, uint32_t *bottle_bw, uint32_t *on_queue); void (*sctp_cwnd_update_after_output) (struct sctp_tcb *stcb, struct sctp_nets *net, int burst_limit); void (*sctp_cwnd_update_packet_transmitted) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_tsn_acknowledged) (struct sctp_nets *net, struct sctp_tmit_chunk *); void (*sctp_cwnd_new_transmission_begins) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_prepare_net_for_sack) (struct sctp_tcb *stcb, struct sctp_nets *net); int (*sctp_cwnd_socket_option) (struct sctp_tcb *stcb, int set, struct sctp_cc_option *); void (*sctp_rtt_calculated) (struct sctp_tcb *, struct sctp_nets *, struct timeval *); }; /* * RS - Structure to hold function pointers to the functions responsible * for stream scheduling. */ struct sctp_ss_functions { void (*sctp_ss_init) (struct sctp_tcb *stcb, struct sctp_association *asoc, int holds_lock); void (*sctp_ss_clear) (struct sctp_tcb *stcb, struct sctp_association *asoc, int clear_values, int holds_lock); void (*sctp_ss_init_stream) (struct sctp_tcb *stcb, struct sctp_stream_out *strq, struct sctp_stream_out *with_strq); void (*sctp_ss_add_to_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); int (*sctp_ss_is_empty) (struct sctp_tcb *stcb, struct sctp_association *asoc); void (*sctp_ss_remove_from_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); struct sctp_stream_out *(*sctp_ss_select_stream) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc); void (*sctp_ss_scheduled) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc, struct sctp_stream_out *strq, int moved_how_much); void (*sctp_ss_packet_done) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc); int (*sctp_ss_get_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, uint16_t *value); int (*sctp_ss_set_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, uint16_t value); int (*sctp_ss_is_user_msgs_incomplete) (struct sctp_tcb *stcb, struct sctp_association *asoc); }; /* used to save ASCONF chunks for retransmission */ TAILQ_HEAD(sctp_asconf_head, sctp_asconf); struct sctp_asconf { TAILQ_ENTRY(sctp_asconf) next; uint32_t serial_number; uint16_t snd_count; struct mbuf *data; uint16_t len; }; /* used to save ASCONF-ACK chunks for retransmission */ TAILQ_HEAD(sctp_asconf_ackhead, sctp_asconf_ack); struct sctp_asconf_ack { TAILQ_ENTRY(sctp_asconf_ack) next; uint32_t serial_number; struct sctp_nets *last_sent_to; struct mbuf *data; uint16_t len; }; /* * Here we have information about each individual association that we track. * We probably in production would be more dynamic. But for ease of * implementation we will have a fixed array that we hunt for in a linear * fashion. */ struct sctp_association { /* association state */ int state; /* queue of pending addrs to add/delete */ struct sctp_asconf_addrhead asconf_queue; struct timeval time_entered; /* time we entered state */ struct timeval time_last_rcvd; struct timeval time_last_sent; struct timeval time_last_sat_advance; struct sctp_nonpad_sndrcvinfo def_send; /* timers and such */ struct sctp_timer dack_timer; /* Delayed ack timer */ struct sctp_timer asconf_timer; /* asconf */ struct sctp_timer strreset_timer; /* stream reset */ struct sctp_timer shut_guard_timer; /* shutdown guard */ struct sctp_timer autoclose_timer; /* automatic close timer */ struct sctp_timer delete_prim_timer; /* deleting primary dst */ /* list of restricted local addresses */ struct sctpladdr sctp_restricted_addrs; /* last local address pending deletion (waiting for an address add) */ struct sctp_ifa *asconf_addr_del_pending; /* Deleted primary destination (used to stop timer) */ struct sctp_nets *deleted_primary; struct sctpnetlisthead nets; /* remote address list */ /* Free chunk list */ struct sctpchunk_listhead free_chunks; /* Control chunk queue */ struct sctpchunk_listhead control_send_queue; /* ASCONF chunk queue */ struct sctpchunk_listhead asconf_send_queue; /* * Once a TSN hits the wire it is moved to the sent_queue. We * maintain two counts here (don't know if any but retran_cnt is * needed). The idea is that the sent_queue_retran_cnt reflects how * many chunks have been marked for retranmission by either T3-rxt * or FR. */ struct sctpchunk_listhead sent_queue; struct sctpchunk_listhead send_queue; /* Scheduling queues */ struct scheduling_data ss_data; /* If an iterator is looking at me, this is it */ struct sctp_iterator *stcb_starting_point_for_iterator; /* ASCONF save the last ASCONF-ACK so we can resend it if necessary */ struct sctp_asconf_ackhead asconf_ack_sent; /* * pointer to last stream reset queued to control queue by us with * requests. */ struct sctp_tmit_chunk *str_reset; /* * if Source Address Selection happening, this will rotate through * the link list. */ struct sctp_laddr *last_used_address; /* stream arrays */ struct sctp_stream_in *strmin; struct sctp_stream_out *strmout; uint8_t *mapping_array; /* primary destination to use */ struct sctp_nets *primary_destination; struct sctp_nets *alternate; /* If primary is down or PF */ /* For CMT */ struct sctp_nets *last_net_cmt_send_started; /* last place I got a data chunk from */ struct sctp_nets *last_data_chunk_from; /* last place I got a control from */ struct sctp_nets *last_control_chunk_from; /* * wait to the point the cum-ack passes req->send_reset_at_tsn for * any req on the list. */ struct sctp_resethead resetHead; /* queue of chunks waiting to be sent into the local stack */ struct sctp_readhead pending_reply_queue; /* JRS - the congestion control functions are in this struct */ struct sctp_cc_functions cc_functions; /* * JRS - value to store the currently loaded congestion control * module */ uint32_t congestion_control_module; /* RS - the stream scheduling functions are in this struct */ struct sctp_ss_functions ss_functions; /* RS - value to store the currently loaded stream scheduling module */ uint32_t stream_scheduling_module; uint32_t vrf_id; uint32_t cookie_preserve_req; /* ASCONF next seq I am sending out, inits at init-tsn */ uint32_t asconf_seq_out; uint32_t asconf_seq_out_acked; /* ASCONF last received ASCONF from peer, starts at peer's TSN-1 */ uint32_t asconf_seq_in; /* next seq I am sending in str reset messages */ uint32_t str_reset_seq_out; /* next seq I am expecting in str reset messages */ uint32_t str_reset_seq_in; /* various verification tag information */ uint32_t my_vtag; /* The tag to be used. if assoc is re-initited * by remote end, and I have unlocked this * will be regenerated to a new random value. */ uint32_t peer_vtag; /* The peers last tag */ uint32_t my_vtag_nonce; uint32_t peer_vtag_nonce; uint32_t assoc_id; /* This is the SCTP fragmentation threshold */ uint32_t smallest_mtu; /* * Special hook for Fast retransmit, allows us to track the highest * TSN that is NEW in this SACK if gap ack blocks are present. */ uint32_t this_sack_highest_gap; /* * The highest consecutive TSN that has been acked by peer on my * sends */ uint32_t last_acked_seq; /* The next TSN that I will use in sending. */ uint32_t sending_seq; /* Original seq number I used ??questionable to keep?? */ uint32_t init_seq_number; /* The Advanced Peer Ack Point, as required by the PR-SCTP */ /* (A1 in Section 4.2) */ uint32_t advanced_peer_ack_point; /* * The highest consequetive TSN at the bottom of the mapping array * (for his sends). */ uint32_t cumulative_tsn; /* * Used to track the mapping array and its offset bits. This MAY be * lower then cumulative_tsn. */ uint32_t mapping_array_base_tsn; /* * used to track highest TSN we have received and is listed in the * mapping array. */ uint32_t highest_tsn_inside_map; /* EY - new NR variables used for nr_sack based on mapping_array */ uint8_t *nr_mapping_array; uint32_t highest_tsn_inside_nr_map; uint32_t fast_recovery_tsn; uint32_t sat_t3_recovery_tsn; uint32_t tsn_last_delivered; /* * For the pd-api we should re-write this a bit more efficient. We * could have multiple sctp_queued_to_read's that we are building at * once. Now we only do this when we get ready to deliver to the * socket buffer. Note that we depend on the fact that the struct is * "stuck" on the read queue until we finish all the pd-api. */ struct sctp_queued_to_read *control_pdapi; uint32_t tsn_of_pdapi_last_delivered; uint32_t pdapi_ppid; uint32_t context; uint32_t last_reset_action[SCTP_MAX_RESET_PARAMS]; uint32_t last_sending_seq[SCTP_MAX_RESET_PARAMS]; uint32_t last_base_tsnsent[SCTP_MAX_RESET_PARAMS]; #ifdef SCTP_ASOCLOG_OF_TSNS /* * special log - This adds considerable size to the asoc, but * provides a log that you can use to detect problems via kgdb. */ struct sctp_tsn_log in_tsnlog[SCTP_TSN_LOG_SIZE]; struct sctp_tsn_log out_tsnlog[SCTP_TSN_LOG_SIZE]; uint32_t cumack_log[SCTP_TSN_LOG_SIZE]; uint32_t cumack_logsnt[SCTP_TSN_LOG_SIZE]; uint16_t tsn_in_at; uint16_t tsn_out_at; uint16_t tsn_in_wrapped; uint16_t tsn_out_wrapped; uint16_t cumack_log_at; uint16_t cumack_log_atsnt; #endif /* SCTP_ASOCLOG_OF_TSNS */ #ifdef SCTP_FS_SPEC_LOG struct sctp_fs_spec_log fslog[SCTP_FS_SPEC_LOG_SIZE]; uint16_t fs_index; #endif /* * window state information and smallest MTU that I use to bound * segmentation */ uint32_t peers_rwnd; uint32_t my_rwnd; uint32_t my_last_reported_rwnd; uint32_t sctp_frag_point; uint32_t total_output_queue_size; uint32_t sb_cc; /* shadow of sb_cc */ uint32_t sb_send_resv; /* amount reserved on a send */ uint32_t my_rwnd_control_len; /* shadow of sb_mbcnt used for rwnd * control */ #ifdef INET6 uint32_t default_flowlabel; #endif uint32_t pr_sctp_cnt; int ctrl_queue_cnt; /* could be removed REM - NO IT CAN'T!! RRS */ /* * All outbound datagrams queue into this list from the individual * stream queue. Here they get assigned a TSN and then await * sending. The stream seq comes when it is first put in the * individual str queue */ unsigned int stream_queue_cnt; unsigned int send_queue_cnt; unsigned int sent_queue_cnt; unsigned int sent_queue_cnt_removeable; /* * Number on sent queue that are marked for retran until this value * is 0 we only send one packet of retran'ed data. */ unsigned int sent_queue_retran_cnt; unsigned int size_on_reasm_queue; unsigned int cnt_on_reasm_queue; unsigned int fwd_tsn_cnt; /* amount of data (bytes) currently in flight (on all destinations) */ unsigned int total_flight; /* Total book size in flight */ unsigned int total_flight_count; /* count of chunks used with * book total */ /* count of destinaton nets and list of destination nets */ unsigned int numnets; /* Total error count on this association */ unsigned int overall_error_count; unsigned int cnt_msg_on_sb; /* All stream count of chunks for delivery */ unsigned int size_on_all_streams; unsigned int cnt_on_all_streams; /* Heart Beat delay in ms */ uint32_t heart_beat_delay; /* autoclose */ - unsigned int sctp_autoclose_ticks; + uint32_t sctp_autoclose_ticks; /* how many preopen streams we have */ unsigned int pre_open_streams; /* How many streams I support coming into me */ unsigned int max_inbound_streams; /* the cookie life I award for any cookie, in seconds */ uint32_t cookie_life; /* time to delay acks for */ unsigned int delayed_ack; unsigned int old_delayed_ack; unsigned int sack_freq; unsigned int data_pkts_seen; unsigned int numduptsns; int dup_tsns[SCTP_MAX_DUP_TSNS]; uint32_t initial_init_rto_max; /* initial RTO for INIT's */ uint32_t initial_rto; /* initial send RTO */ uint32_t minrto; /* per assoc RTO-MIN */ uint32_t maxrto; /* per assoc RTO-MAX */ /* authentication fields */ sctp_auth_chklist_t *local_auth_chunks; sctp_auth_chklist_t *peer_auth_chunks; sctp_hmaclist_t *local_hmacs; /* local HMACs supported */ sctp_hmaclist_t *peer_hmacs; /* peer HMACs supported */ struct sctp_keyhead shared_keys; /* assoc's shared keys */ sctp_authinfo_t authinfo; /* randoms, cached keys */ /* * refcnt to block freeing when a sender or receiver is off coping * user data in. */ uint32_t refcnt; uint32_t chunks_on_out_queue; /* total chunks floating around, * locked by send socket buffer */ uint32_t peers_adaptation; uint32_t default_mtu; uint16_t peer_hmac_id; /* peer HMAC id to send */ /* * Being that we have no bag to collect stale cookies, and that we * really would not want to anyway.. we will count them in this * counter. We of course feed them to the pigeons right away (I have * always thought of pigeons as flying rats). */ uint16_t stale_cookie_count; /* * For the partial delivery API, if up, invoked this is what last * TSN I delivered */ uint16_t str_of_pdapi; uint16_t ssn_of_pdapi; /* counts of actual built streams. Allocation may be more however */ /* could re-arrange to optimize space here. */ uint16_t streamincnt; uint16_t streamoutcnt; uint16_t strm_realoutsize; uint16_t strm_pending_add_size; /* my maximum number of retrans of INIT and SEND */ /* copied from SCTP but should be individually setable */ uint16_t max_init_times; uint16_t max_send_times; uint16_t def_net_failure; uint16_t def_net_pf_threshold; /* * lock flag: 0 is ok to send, 1+ (duals as a retran count) is * awaiting ACK */ uint16_t mapping_array_size; uint16_t last_strm_seq_delivered; uint16_t last_strm_no_delivered; uint16_t last_revoke_count; int16_t num_send_timers_up; uint16_t stream_locked_on; uint16_t ecn_echo_cnt_onq; uint16_t free_chunk_cnt; uint8_t stream_locked; uint8_t authenticated; /* packet authenticated ok */ /* * This flag indicates that a SACK need to be sent. Initially this * is 1 to send the first sACK immediately. */ uint8_t send_sack; /* max burst of new packets into the network */ uint32_t max_burst; /* max burst of fast retransmit packets */ uint32_t fr_max_burst; uint8_t sat_network; /* RTT is in range of sat net or greater */ uint8_t sat_network_lockout; /* lockout code */ uint8_t burst_limit_applied; /* Burst limit in effect at last send? */ /* flag goes on when we are doing a partial delivery api */ uint8_t hb_random_values[4]; uint8_t fragmented_delivery_inprogress; uint8_t fragment_flags; uint8_t last_flags_delivered; uint8_t hb_ect_randombit; uint8_t hb_random_idx; uint8_t default_dscp; uint8_t asconf_del_pending; /* asconf delete last addr pending */ uint8_t trigger_reset; /* * This value, plus all other ack'd but above cum-ack is added * together to cross check against the bit that we have yet to * define (probably in the SACK). When the cum-ack is updated, this * sum is updated as well. */ /* Flags whether an extension is supported or not */ uint8_t ecn_supported; uint8_t prsctp_supported; uint8_t auth_supported; uint8_t asconf_supported; uint8_t reconfig_supported; uint8_t nrsack_supported; uint8_t pktdrop_supported; uint8_t idata_supported; /* Did the peer make the stream config (add out) request */ uint8_t peer_req_out; uint8_t local_strreset_support; uint8_t peer_supports_nat; struct sctp_scoping scope; /* flags to handle send alternate net tracking */ uint8_t used_alt_asconfack; uint8_t fast_retran_loss_recovery; uint8_t sat_t3_loss_recovery; uint8_t dropped_special_cnt; uint8_t seen_a_sack_this_pkt; uint8_t stream_reset_outstanding; uint8_t stream_reset_out_is_outstanding; uint8_t delayed_connection; uint8_t ifp_had_enobuf; uint8_t saw_sack_with_frags; uint8_t saw_sack_with_nr_frags; uint8_t in_asocid_hash; uint8_t assoc_up_sent; uint8_t adaptation_needed; uint8_t adaptation_sent; /* CMT variables */ uint8_t cmt_dac_pkts_rcvd; uint8_t sctp_cmt_on_off; uint8_t iam_blocking; uint8_t cookie_how[8]; /* JRS 5/21/07 - CMT PF variable */ uint8_t sctp_cmt_pf; uint8_t use_precise_time; uint64_t sctp_features; uint32_t max_cwnd; uint16_t port; /* remote UDP encapsulation port */ /* * The mapping array is used to track out of order sequences above * last_acked_seq. 0 indicates packet missing 1 indicates packet * rec'd. We slide it up every time we raise last_acked_seq and 0 * trailing locactions out. If I get a TSN above the array * mappingArraySz, I discard the datagram and let retransmit happen. */ uint32_t marked_retrans; uint32_t timoinit; uint32_t timodata; uint32_t timosack; uint32_t timoshutdown; uint32_t timoheartbeat; uint32_t timocookie; uint32_t timoshutdownack; struct timeval start_time; struct timeval discontinuity_time; uint64_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1]; uint64_t abandoned_sent[SCTP_PR_SCTP_MAX + 1]; }; #endif Index: head/sys/netinet/sctp_timer.c =================================================================== --- head/sys/netinet/sctp_timer.c (revision 359378) +++ head/sys/netinet/sctp_timer.c (revision 359379) @@ -1,1597 +1,1596 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define _IP_VHL #include #include #ifdef INET6 #endif #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #endif void sctp_audit_retranmission_queue(struct sctp_association *asoc) { struct sctp_tmit_chunk *chk; SCTPDBG(SCTP_DEBUG_TIMER4, "Audit invoked on send queue cnt:%d onqueue:%d\n", asoc->sent_queue_retran_cnt, asoc->sent_queue_cnt); asoc->sent_queue_retran_cnt = 0; asoc->sent_queue_cnt = 0; TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) { if (chk->sent == SCTP_DATAGRAM_RESEND) { sctp_ucount_incr(asoc->sent_queue_retran_cnt); } asoc->sent_queue_cnt++; } TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { if (chk->sent == SCTP_DATAGRAM_RESEND) { sctp_ucount_incr(asoc->sent_queue_retran_cnt); } } TAILQ_FOREACH(chk, &asoc->asconf_send_queue, sctp_next) { if (chk->sent == SCTP_DATAGRAM_RESEND) { sctp_ucount_incr(asoc->sent_queue_retran_cnt); } } SCTPDBG(SCTP_DEBUG_TIMER4, "Audit completes retran:%d onqueue:%d\n", asoc->sent_queue_retran_cnt, asoc->sent_queue_cnt); } static int sctp_threshold_management(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net, uint16_t threshold) { if (net) { net->error_count++; SCTPDBG(SCTP_DEBUG_TIMER4, "Error count for %p now %d thresh:%d\n", (void *)net, net->error_count, net->failure_threshold); if (net->error_count > net->failure_threshold) { /* We had a threshold failure */ if (net->dest_state & SCTP_ADDR_REACHABLE) { net->dest_state &= ~SCTP_ADDR_REACHABLE; net->dest_state &= ~SCTP_ADDR_REQ_PRIMARY; net->dest_state &= ~SCTP_ADDR_PF; sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, 0, (void *)net, SCTP_SO_NOT_LOCKED); } } else if ((net->pf_threshold < net->failure_threshold) && (net->error_count > net->pf_threshold)) { if (!(net->dest_state & SCTP_ADDR_PF)) { net->dest_state |= SCTP_ADDR_PF; net->last_active = sctp_get_tick_count(); sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_1); sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); } } } if (stcb == NULL) return (0); if (net) { if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { sctp_misc_ints(SCTP_THRESHOLD_INCR, stcb->asoc.overall_error_count, (stcb->asoc.overall_error_count + 1), SCTP_FROM_SCTP_TIMER, __LINE__); } stcb->asoc.overall_error_count++; } } else { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { sctp_misc_ints(SCTP_THRESHOLD_INCR, stcb->asoc.overall_error_count, (stcb->asoc.overall_error_count + 1), SCTP_FROM_SCTP_TIMER, __LINE__); } stcb->asoc.overall_error_count++; } SCTPDBG(SCTP_DEBUG_TIMER4, "Overall error count for %p now %d thresh:%u state:%x\n", (void *)&stcb->asoc, stcb->asoc.overall_error_count, (uint32_t)threshold, ((net == NULL) ? (uint32_t)0 : (uint32_t)net->dest_state)); /* * We specifically do not do >= to give the assoc one more change * before we fail it. */ if (stcb->asoc.overall_error_count > threshold) { /* Abort notification sends a ULP notify */ struct mbuf *op_err; op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), "Association error counter exceeded"); inp->last_abort_code = SCTP_FROM_SCTP_TIMER + SCTP_LOC_2; sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED); return (1); } return (0); } /* * sctp_find_alternate_net() returns a non-NULL pointer as long * the argument net is non-NULL. */ struct sctp_nets * sctp_find_alternate_net(struct sctp_tcb *stcb, struct sctp_nets *net, int mode) { /* Find and return an alternate network if possible */ struct sctp_nets *alt, *mnet, *min_errors_net = NULL, *max_cwnd_net = NULL; int once; /* JRS 5/14/07 - Initialize min_errors to an impossible value. */ int min_errors = -1; uint32_t max_cwnd = 0; if (stcb->asoc.numnets == 1) { /* No others but net */ return (TAILQ_FIRST(&stcb->asoc.nets)); } /* * JRS 5/14/07 - If mode is set to 2, use the CMT PF find alternate * net algorithm. This algorithm chooses the active destination (not * in PF state) with the largest cwnd value. If all destinations are * in PF state, unreachable, or unconfirmed, choose the desination * that is in PF state with the lowest error count. In case of a * tie, choose the destination that was most recently active. */ if (mode == 2) { TAILQ_FOREACH(mnet, &stcb->asoc.nets, sctp_next) { /* * JRS 5/14/07 - If the destination is unreachable * or unconfirmed, skip it. */ if (((mnet->dest_state & SCTP_ADDR_REACHABLE) != SCTP_ADDR_REACHABLE) || (mnet->dest_state & SCTP_ADDR_UNCONFIRMED)) { continue; } /* * JRS 5/14/07 - If the destination is reachable * but in PF state, compare the error count of the * destination to the minimum error count seen thus * far. Store the destination with the lower error * count. If the error counts are equal, store the * destination that was most recently active. */ if (mnet->dest_state & SCTP_ADDR_PF) { /* * JRS 5/14/07 - If the destination under * consideration is the current destination, * work as if the error count is one higher. * The actual error count will not be * incremented until later in the t3 * handler. */ if (mnet == net) { if (min_errors == -1) { min_errors = mnet->error_count + 1; min_errors_net = mnet; } else if (mnet->error_count + 1 < min_errors) { min_errors = mnet->error_count + 1; min_errors_net = mnet; } else if (mnet->error_count + 1 == min_errors && mnet->last_active > min_errors_net->last_active) { min_errors_net = mnet; min_errors = mnet->error_count + 1; } continue; } else { if (min_errors == -1) { min_errors = mnet->error_count; min_errors_net = mnet; } else if (mnet->error_count < min_errors) { min_errors = mnet->error_count; min_errors_net = mnet; } else if (mnet->error_count == min_errors && mnet->last_active > min_errors_net->last_active) { min_errors_net = mnet; min_errors = mnet->error_count; } continue; } } /* * JRS 5/14/07 - If the destination is reachable and * not in PF state, compare the cwnd of the * destination to the highest cwnd seen thus far. * Store the destination with the higher cwnd value. * If the cwnd values are equal, randomly choose one * of the two destinations. */ if (max_cwnd < mnet->cwnd) { max_cwnd_net = mnet; max_cwnd = mnet->cwnd; } else if (max_cwnd == mnet->cwnd) { uint32_t rndval; uint8_t this_random; if (stcb->asoc.hb_random_idx > 3) { rndval = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep); memcpy(stcb->asoc.hb_random_values, &rndval, sizeof(stcb->asoc.hb_random_values)); this_random = stcb->asoc.hb_random_values[0]; stcb->asoc.hb_random_idx++; stcb->asoc.hb_ect_randombit = 0; } else { this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx]; stcb->asoc.hb_random_idx++; stcb->asoc.hb_ect_randombit = 0; } if (this_random % 2 == 1) { max_cwnd_net = mnet; max_cwnd = mnet->cwnd; /* Useless? */ } } } if (max_cwnd_net == NULL) { if (min_errors_net == NULL) { return (net); } return (min_errors_net); } else { return (max_cwnd_net); } } /* JRS 5/14/07 - If mode is set to 1, use the * CMT policy for choosing an alternate net. */ else if (mode == 1) { TAILQ_FOREACH(mnet, &stcb->asoc.nets, sctp_next) { if (((mnet->dest_state & SCTP_ADDR_REACHABLE) != SCTP_ADDR_REACHABLE) || (mnet->dest_state & SCTP_ADDR_UNCONFIRMED)) { /* * will skip ones that are not-reachable or * unconfirmed */ continue; } if (max_cwnd < mnet->cwnd) { max_cwnd_net = mnet; max_cwnd = mnet->cwnd; } else if (max_cwnd == mnet->cwnd) { uint32_t rndval; uint8_t this_random; if (stcb->asoc.hb_random_idx > 3) { rndval = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep); memcpy(stcb->asoc.hb_random_values, &rndval, sizeof(stcb->asoc.hb_random_values)); this_random = stcb->asoc.hb_random_values[0]; stcb->asoc.hb_random_idx = 0; stcb->asoc.hb_ect_randombit = 0; } else { this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx]; stcb->asoc.hb_random_idx++; stcb->asoc.hb_ect_randombit = 0; } if (this_random % 2) { max_cwnd_net = mnet; max_cwnd = mnet->cwnd; } } } if (max_cwnd_net) { return (max_cwnd_net); } } mnet = net; once = 0; if (mnet == NULL) { mnet = TAILQ_FIRST(&stcb->asoc.nets); if (mnet == NULL) { return (NULL); } } for (;;) { alt = TAILQ_NEXT(mnet, sctp_next); if (alt == NULL) { once++; if (once > 1) { break; } alt = TAILQ_FIRST(&stcb->asoc.nets); if (alt == NULL) { return (NULL); } } if (alt->ro.ro_rt == NULL) { if (alt->ro._s_addr) { sctp_free_ifa(alt->ro._s_addr); alt->ro._s_addr = NULL; } alt->src_addr_selected = 0; } if (((alt->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE) && (alt->ro.ro_rt != NULL) && (!(alt->dest_state & SCTP_ADDR_UNCONFIRMED))) { /* Found a reachable address */ break; } mnet = alt; } if (alt == NULL) { /* Case where NO insv network exists (dormant state) */ /* we rotate destinations */ once = 0; mnet = net; for (;;) { if (mnet == NULL) { return (TAILQ_FIRST(&stcb->asoc.nets)); } alt = TAILQ_NEXT(mnet, sctp_next); if (alt == NULL) { once++; if (once > 1) { break; } alt = TAILQ_FIRST(&stcb->asoc.nets); if (alt == NULL) { break; } } if ((!(alt->dest_state & SCTP_ADDR_UNCONFIRMED)) && (alt != net)) { /* Found an alternate address */ break; } mnet = alt; } } if (alt == NULL) { return (net); } return (alt); } static void sctp_backoff_on_timeout(struct sctp_tcb *stcb, struct sctp_nets *net, int win_probe, int num_marked, int num_abandoned) { if (net->RTO == 0) { if (net->RTO_measured) { net->RTO = stcb->asoc.minrto; } else { net->RTO = stcb->asoc.initial_rto; } } net->RTO <<= 1; if (net->RTO > stcb->asoc.maxrto) { net->RTO = stcb->asoc.maxrto; } if ((win_probe == 0) && (num_marked || num_abandoned)) { /* We don't apply penalty to window probe scenarios */ /* JRS - Use the congestion control given in the CC module */ stcb->asoc.cc_functions.sctp_cwnd_update_after_timeout(stcb, net); } } #ifndef INVARIANTS static void sctp_recover_sent_list(struct sctp_tcb *stcb) { struct sctp_tmit_chunk *chk, *nchk; struct sctp_association *asoc; asoc = &stcb->asoc; TAILQ_FOREACH_SAFE(chk, &asoc->sent_queue, sctp_next, nchk) { if (SCTP_TSN_GE(asoc->last_acked_seq, chk->rec.data.tsn)) { SCTP_PRINTF("Found chk:%p tsn:%x <= last_acked_seq:%x\n", (void *)chk, chk->rec.data.tsn, asoc->last_acked_seq); if (chk->sent != SCTP_DATAGRAM_NR_ACKED) { if (asoc->strmout[chk->rec.data.sid].chunks_on_queues > 0) { asoc->strmout[chk->rec.data.sid].chunks_on_queues--; } } if ((asoc->strmout[chk->rec.data.sid].chunks_on_queues == 0) && (asoc->strmout[chk->rec.data.sid].state == SCTP_STREAM_RESET_PENDING) && TAILQ_EMPTY(&asoc->strmout[chk->rec.data.sid].outqueue)) { asoc->trigger_reset = 1; } TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); if (PR_SCTP_ENABLED(chk->flags)) { if (asoc->pr_sctp_cnt != 0) asoc->pr_sctp_cnt--; } if (chk->data) { /* sa_ignore NO_NULL_CHK */ sctp_free_bufspace(stcb, asoc, chk, 1); sctp_m_freem(chk->data); chk->data = NULL; if (asoc->prsctp_supported && PR_SCTP_BUF_ENABLED(chk->flags)) { asoc->sent_queue_cnt_removeable--; } } asoc->sent_queue_cnt--; sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); } } SCTP_PRINTF("after recover order is as follows\n"); TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) { SCTP_PRINTF("chk:%p TSN:%x\n", (void *)chk, chk->rec.data.tsn); } } #endif static int sctp_mark_all_for_resend(struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_nets *alt, int window_probe, int *num_marked, int *num_abandoned) { /* * Mark all chunks (well not all) that were sent to *net for * retransmission. Move them to alt for there destination as well... * We only mark chunks that have been outstanding long enough to * have received feed-back. */ struct sctp_tmit_chunk *chk, *nchk; struct sctp_nets *lnets; struct timeval now, min_wait, tv; int cur_rto; int cnt_abandoned; int audit_tf, num_mk, fir; unsigned int cnt_mk; uint32_t orig_flight, orig_tf; uint32_t tsnlast, tsnfirst; int recovery_cnt = 0; /* none in flight now */ audit_tf = 0; fir = 0; /* * figure out how long a data chunk must be pending before we can * mark it .. */ (void)SCTP_GETTIME_TIMEVAL(&now); /* get cur rto in micro-seconds */ cur_rto = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; cur_rto *= 1000; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(cur_rto, stcb->asoc.peers_rwnd, window_probe, SCTP_FR_T3_MARK_TIME); sctp_log_fr(net->flight_size, 0, 0, SCTP_FR_CWND_REPORT); sctp_log_fr(net->flight_size, net->cwnd, stcb->asoc.total_flight, SCTP_FR_CWND_REPORT); } tv.tv_sec = cur_rto / 1000000; tv.tv_usec = cur_rto % 1000000; min_wait = now; timevalsub(&min_wait, &tv); if (min_wait.tv_sec < 0 || min_wait.tv_usec < 0) { /* * if we hit here, we don't have enough seconds on the clock * to account for the RTO. We just let the lower seconds be * the bounds and don't worry about it. This may mean we * will mark a lot more than we should. */ min_wait.tv_sec = min_wait.tv_usec = 0; } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(cur_rto, now.tv_sec, now.tv_usec, SCTP_FR_T3_MARK_TIME); sctp_log_fr(0, min_wait.tv_sec, min_wait.tv_usec, SCTP_FR_T3_MARK_TIME); } /* * Our rwnd will be incorrect here since we are not adding back the * cnt * mbuf but we will fix that down below. */ orig_flight = net->flight_size; orig_tf = stcb->asoc.total_flight; net->fast_retran_ip = 0; /* Now on to each chunk */ cnt_abandoned = 0; num_mk = cnt_mk = 0; tsnfirst = tsnlast = 0; #ifndef INVARIANTS start_again: #endif TAILQ_FOREACH_SAFE(chk, &stcb->asoc.sent_queue, sctp_next, nchk) { if (SCTP_TSN_GE(stcb->asoc.last_acked_seq, chk->rec.data.tsn)) { /* Strange case our list got out of order? */ SCTP_PRINTF("Our list is out of order? last_acked:%x chk:%x\n", (unsigned int)stcb->asoc.last_acked_seq, (unsigned int)chk->rec.data.tsn); recovery_cnt++; #ifdef INVARIANTS panic("last acked >= chk on sent-Q"); #else SCTP_PRINTF("Recover attempts a restart cnt:%d\n", recovery_cnt); sctp_recover_sent_list(stcb); if (recovery_cnt < 10) { goto start_again; } else { SCTP_PRINTF("Recovery fails %d times??\n", recovery_cnt); } #endif } if ((chk->whoTo == net) && (chk->sent < SCTP_DATAGRAM_ACKED)) { /* * found one to mark: If it is less than * DATAGRAM_ACKED it MUST not be a skipped or marked * TSN but instead one that is either already set * for retransmission OR one that needs * retransmission. */ /* validate its been outstanding long enough */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(chk->rec.data.tsn, chk->sent_rcv_time.tv_sec, chk->sent_rcv_time.tv_usec, SCTP_FR_T3_MARK_TIME); } if ((chk->sent_rcv_time.tv_sec > min_wait.tv_sec) && (window_probe == 0)) { /* * we have reached a chunk that was sent * some seconds past our min.. forget it we * will find no more to send. */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(0, chk->sent_rcv_time.tv_sec, chk->sent_rcv_time.tv_usec, SCTP_FR_T3_STOPPED); } continue; } else if ((chk->sent_rcv_time.tv_sec == min_wait.tv_sec) && (window_probe == 0)) { /* * we must look at the micro seconds to * know. */ if (chk->sent_rcv_time.tv_usec >= min_wait.tv_usec) { /* * ok it was sent after our boundary * time. */ continue; } } if (stcb->asoc.prsctp_supported && PR_SCTP_TTL_ENABLED(chk->flags)) { /* Is it expired? */ if (timevalcmp(&now, &chk->rec.data.timetodrop, >)) { /* Yes so drop it */ if (chk->data) { (void)sctp_release_pr_sctp_chunk(stcb, chk, 1, SCTP_SO_NOT_LOCKED); cnt_abandoned++; } continue; } } if (stcb->asoc.prsctp_supported && PR_SCTP_RTX_ENABLED(chk->flags)) { /* Has it been retransmitted tv_sec times? */ if (chk->snd_count > chk->rec.data.timetodrop.tv_sec) { if (chk->data) { (void)sctp_release_pr_sctp_chunk(stcb, chk, 1, SCTP_SO_NOT_LOCKED); cnt_abandoned++; } continue; } } if (chk->sent < SCTP_DATAGRAM_RESEND) { sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); num_mk++; if (fir == 0) { fir = 1; tsnfirst = chk->rec.data.tsn; } tsnlast = chk->rec.data.tsn; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(chk->rec.data.tsn, chk->snd_count, 0, SCTP_FR_T3_MARKED); } if (chk->rec.data.chunk_was_revoked) { /* deflate the cwnd */ chk->whoTo->cwnd -= chk->book_size; chk->rec.data.chunk_was_revoked = 0; } net->marked_retrans++; stcb->asoc.marked_retrans++; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) { sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_RSND_TO, chk->whoTo->flight_size, chk->book_size, (uint32_t)(uintptr_t)chk->whoTo, chk->rec.data.tsn); } sctp_flight_size_decrease(chk); sctp_total_flight_decrease(stcb, chk); stcb->asoc.peers_rwnd += chk->send_size; stcb->asoc.peers_rwnd += SCTP_BASE_SYSCTL(sctp_peer_chunk_oh); } chk->sent = SCTP_DATAGRAM_RESEND; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; SCTP_STAT_INCR(sctps_markedretrans); /* reset the TSN for striking and other FR stuff */ chk->rec.data.doing_fast_retransmit = 0; /* Clear any time so NO RTT is being done */ if (chk->do_rtt) { if (chk->whoTo->rto_needed == 0) { chk->whoTo->rto_needed = 1; } } chk->do_rtt = 0; if (alt != net) { sctp_free_remote_addr(chk->whoTo); chk->no_fr_allowed = 1; chk->whoTo = alt; atomic_add_int(&alt->ref_count, 1); } else { chk->no_fr_allowed = 0; if (TAILQ_EMPTY(&stcb->asoc.send_queue)) { chk->rec.data.fast_retran_tsn = stcb->asoc.sending_seq; } else { chk->rec.data.fast_retran_tsn = (TAILQ_FIRST(&stcb->asoc.send_queue))->rec.data.tsn; } } /* * CMT: Do not allow FRs on retransmitted TSNs. */ if (stcb->asoc.sctp_cmt_on_off > 0) { chk->no_fr_allowed = 1; } #ifdef THIS_SHOULD_NOT_BE_DONE } else if (chk->sent == SCTP_DATAGRAM_ACKED) { /* remember highest acked one */ could_be_sent = chk; #endif } if (chk->sent == SCTP_DATAGRAM_RESEND) { cnt_mk++; } } if ((orig_flight - net->flight_size) != (orig_tf - stcb->asoc.total_flight)) { /* we did not subtract the same things? */ audit_tf = 1; } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(tsnfirst, tsnlast, num_mk, SCTP_FR_T3_TIMEOUT); } #ifdef SCTP_DEBUG if (num_mk) { SCTPDBG(SCTP_DEBUG_TIMER1, "LAST TSN marked was %x\n", tsnlast); SCTPDBG(SCTP_DEBUG_TIMER1, "Num marked for retransmission was %d peer-rwd:%u\n", num_mk, stcb->asoc.peers_rwnd); } #endif *num_marked = num_mk; *num_abandoned = cnt_abandoned; /* * Now check for a ECN Echo that may be stranded And include the * cnt_mk'd to have all resends in the control queue. */ TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) { if (chk->sent == SCTP_DATAGRAM_RESEND) { cnt_mk++; } if ((chk->whoTo == net) && (chk->rec.chunk_id.id == SCTP_ECN_ECHO)) { sctp_free_remote_addr(chk->whoTo); chk->whoTo = alt; if (chk->sent != SCTP_DATAGRAM_RESEND) { chk->sent = SCTP_DATAGRAM_RESEND; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); cnt_mk++; } atomic_add_int(&alt->ref_count, 1); } } #ifdef THIS_SHOULD_NOT_BE_DONE if ((stcb->asoc.sent_queue_retran_cnt == 0) && (could_be_sent)) { /* fix it so we retransmit the highest acked anyway */ sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); cnt_mk++; could_be_sent->sent = SCTP_DATAGRAM_RESEND; } #endif if (stcb->asoc.sent_queue_retran_cnt != cnt_mk) { #ifdef INVARIANTS SCTP_PRINTF("Local Audit says there are %d for retran asoc cnt:%d we marked:%d this time\n", cnt_mk, stcb->asoc.sent_queue_retran_cnt, num_mk); #endif #ifndef SCTP_AUDITING_ENABLED stcb->asoc.sent_queue_retran_cnt = cnt_mk; #endif } if (audit_tf) { SCTPDBG(SCTP_DEBUG_TIMER4, "Audit total flight due to negative value net:%p\n", (void *)net); stcb->asoc.total_flight = 0; stcb->asoc.total_flight_count = 0; /* Clear all networks flight size */ TAILQ_FOREACH(lnets, &stcb->asoc.nets, sctp_next) { lnets->flight_size = 0; SCTPDBG(SCTP_DEBUG_TIMER4, "Net:%p c-f cwnd:%d ssthresh:%d\n", (void *)lnets, lnets->cwnd, lnets->ssthresh); } TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) { if (chk->sent < SCTP_DATAGRAM_RESEND) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) { sctp_misc_ints(SCTP_FLIGHT_LOG_UP, chk->whoTo->flight_size, chk->book_size, (uint32_t)(uintptr_t)chk->whoTo, chk->rec.data.tsn); } sctp_flight_size_increase(chk); sctp_total_flight_increase(stcb, chk); } } } /* We return 1 if we only have a window probe outstanding */ return (0); } int sctp_t3rxt_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_nets *alt; int win_probe, num_mk, num_abandoned; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(0, 0, 0, SCTP_FR_T3_TIMEOUT); } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { struct sctp_nets *lnet; TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { if (net == lnet) { sctp_log_cwnd(stcb, lnet, 1, SCTP_CWND_LOG_FROM_T3); } else { sctp_log_cwnd(stcb, lnet, 0, SCTP_CWND_LOG_FROM_T3); } } } /* Find an alternate and mark those for retransmission */ if ((stcb->asoc.peers_rwnd == 0) && (stcb->asoc.total_flight < net->mtu)) { SCTP_STAT_INCR(sctps_timowindowprobe); win_probe = 1; } else { win_probe = 0; } if (win_probe == 0) { /* We don't do normal threshold management on window probes */ if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Association was destroyed */ return (1); } else { if (net != stcb->asoc.primary_destination) { /* send a immediate HB if our RTO is stale */ struct timeval now; unsigned int ms_goneby; (void)SCTP_GETTIME_TIMEVAL(&now); if (net->last_sent_time.tv_sec) { ms_goneby = (now.tv_sec - net->last_sent_time.tv_sec) * 1000; } else { ms_goneby = 0; } if ((net->dest_state & SCTP_ADDR_PF) == 0) { if ((ms_goneby > net->RTO) || (net->RTO == 0)) { /* * no recent feed back in an * RTO or more, request a * RTT update */ sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); } } } } } else { /* * For a window probe we don't penalize the net's but only * the association. This may fail it if SACKs are not coming * back. If sack's are coming with rwnd locked at 0, we will * continue to hold things waiting for rwnd to raise */ if (sctp_threshold_management(inp, stcb, NULL, stcb->asoc.max_send_times)) { /* Association was destroyed */ return (1); } } if (stcb->asoc.sctp_cmt_on_off > 0) { if (net->pf_threshold < net->failure_threshold) { alt = sctp_find_alternate_net(stcb, net, 2); } else { /* * CMT: Using RTX_SSTHRESH policy for CMT. If CMT is * being used, then pick dest with largest ssthresh * for any retransmission. */ alt = sctp_find_alternate_net(stcb, net, 1); /* * CUCv2: If a different dest is picked for the * retransmission, then new (rtx-)pseudo_cumack * needs to be tracked for orig dest. Let CUCv2 * track new (rtx-) pseudo-cumack always. */ net->find_pseudo_cumack = 1; net->find_rtx_pseudo_cumack = 1; } } else { alt = sctp_find_alternate_net(stcb, net, 0); } num_mk = 0; num_abandoned = 0; (void)sctp_mark_all_for_resend(stcb, net, alt, win_probe, &num_mk, &num_abandoned); /* FR Loss recovery just ended with the T3. */ stcb->asoc.fast_retran_loss_recovery = 0; /* CMT FR loss recovery ended with the T3 */ net->fast_retran_loss_recovery = 0; if ((stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) && (net->flight_size == 0)) { (*stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) (stcb, net); } /* * setup the sat loss recovery that prevents satellite cwnd advance. */ stcb->asoc.sat_t3_loss_recovery = 1; stcb->asoc.sat_t3_recovery_tsn = stcb->asoc.sending_seq; /* Backoff the timer and cwnd */ sctp_backoff_on_timeout(stcb, net, win_probe, num_mk, num_abandoned); if ((!(net->dest_state & SCTP_ADDR_REACHABLE)) || (net->dest_state & SCTP_ADDR_PF)) { /* Move all pending over too */ sctp_move_chunks_from_net(stcb, net); /* * Get the address that failed, to force a new src address * selecton and a route allocation. */ if (net->ro._s_addr) { sctp_free_ifa(net->ro._s_addr); net->ro._s_addr = NULL; } net->src_addr_selected = 0; /* Force a route allocation too */ if (net->ro.ro_rt) { RTFREE(net->ro.ro_rt); net->ro.ro_rt = NULL; } /* Was it our primary? */ if ((stcb->asoc.primary_destination == net) && (alt != net)) { /* * Yes, note it as such and find an alternate note: * this means HB code must use this to resent the * primary if it goes active AND if someone does a * change-primary then this flag must be cleared * from any net structures. */ if (stcb->asoc.alternate) { sctp_free_remote_addr(stcb->asoc.alternate); } stcb->asoc.alternate = alt; atomic_add_int(&stcb->asoc.alternate->ref_count, 1); } } /* * Special case for cookie-echo'ed case, we don't do output but must * await the COOKIE-ACK before retransmission */ if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED) { /* * Here we just reset the timer and start again since we * have not established the asoc */ sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net); return (0); } if (stcb->asoc.prsctp_supported) { struct sctp_tmit_chunk *lchk; lchk = sctp_try_advance_peer_ack_point(stcb, &stcb->asoc); /* C3. See if we need to send a Fwd-TSN */ if (SCTP_TSN_GT(stcb->asoc.advanced_peer_ack_point, stcb->asoc.last_acked_seq)) { send_forward_tsn(stcb, &stcb->asoc); if (lchk) { /* Assure a timer is up */ sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, lchk->whoTo); } } } if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { sctp_log_cwnd(stcb, net, net->cwnd, SCTP_CWND_LOG_FROM_RTX); } return (0); } int sctp_t1init_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { /* bump the thresholds */ if (stcb->asoc.delayed_connection) { /* * special hook for delayed connection. The library did NOT * complete the rest of its sends. */ stcb->asoc.delayed_connection = 0; sctp_send_initiate(inp, stcb, SCTP_SO_NOT_LOCKED); return (0); } if (SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_WAIT) { return (0); } if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_init_times)) { /* Association was destroyed */ return (1); } stcb->asoc.dropped_special_cnt = 0; sctp_backoff_on_timeout(stcb, stcb->asoc.primary_destination, 1, 0, 0); if (stcb->asoc.initial_init_rto_max < net->RTO) { net->RTO = stcb->asoc.initial_init_rto_max; } if (stcb->asoc.numnets > 1) { /* If we have more than one addr use it */ struct sctp_nets *alt; alt = sctp_find_alternate_net(stcb, stcb->asoc.primary_destination, 0); if (alt != stcb->asoc.primary_destination) { sctp_move_chunks_from_net(stcb, stcb->asoc.primary_destination); stcb->asoc.primary_destination = alt; } } /* Send out a new init */ sctp_send_initiate(inp, stcb, SCTP_SO_NOT_LOCKED); return (0); } /* * For cookie and asconf we actually need to find and mark for resend, then * increment the resend counter (after all the threshold management stuff of * course). */ int sctp_cookie_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net SCTP_UNUSED) { struct sctp_nets *alt; struct sctp_tmit_chunk *cookie; /* first before all else we must find the cookie */ TAILQ_FOREACH(cookie, &stcb->asoc.control_send_queue, sctp_next) { if (cookie->rec.chunk_id.id == SCTP_COOKIE_ECHO) { break; } } if (cookie == NULL) { if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED) { /* FOOBAR! */ struct mbuf *op_err; op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), "Cookie timer expired, but no cookie"); inp->last_abort_code = SCTP_FROM_SCTP_TIMER + SCTP_LOC_3; sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED); } else { #ifdef INVARIANTS panic("Cookie timer expires in wrong state?"); #else SCTP_PRINTF("Strange in state %d not cookie-echoed yet c-e timer expires?\n", SCTP_GET_STATE(stcb)); return (0); #endif } return (0); } /* Ok we found the cookie, threshold management next */ if (sctp_threshold_management(inp, stcb, cookie->whoTo, stcb->asoc.max_init_times)) { /* Assoc is over */ return (1); } /* * Cleared threshold management, now lets backoff the address and * select an alternate */ stcb->asoc.dropped_special_cnt = 0; sctp_backoff_on_timeout(stcb, cookie->whoTo, 1, 0, 0); alt = sctp_find_alternate_net(stcb, cookie->whoTo, 0); if (alt != cookie->whoTo) { sctp_free_remote_addr(cookie->whoTo); cookie->whoTo = alt; atomic_add_int(&alt->ref_count, 1); } /* Now mark the retran info */ if (cookie->sent != SCTP_DATAGRAM_RESEND) { sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); } cookie->sent = SCTP_DATAGRAM_RESEND; cookie->flags |= CHUNK_FLAGS_FRAGMENT_OK; /* * Now call the output routine to kick out the cookie again, Note we * don't mark any chunks for retran so that FR will need to kick in * to move these (or a send timer). */ return (0); } int sctp_strreset_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { struct sctp_nets *alt, *net; struct sctp_tmit_chunk *strrst = NULL, *chk = NULL; if (stcb->asoc.stream_reset_outstanding == 0) { return (0); } /* find the existing STRRESET, we use the seq number we sent out on */ (void)sctp_find_stream_reset(stcb, stcb->asoc.str_reset_seq_out, &strrst); if (strrst == NULL) { return (0); } net = strrst->whoTo; /* do threshold management */ if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Assoc is over */ return (1); } /* * Cleared threshold management, now lets backoff the address and * select an alternate */ sctp_backoff_on_timeout(stcb, net, 1, 0, 0); alt = sctp_find_alternate_net(stcb, net, 0); strrst->whoTo = alt; atomic_add_int(&alt->ref_count, 1); /* See if a ECN Echo is also stranded */ TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) { if ((chk->whoTo == net) && (chk->rec.chunk_id.id == SCTP_ECN_ECHO)) { sctp_free_remote_addr(chk->whoTo); if (chk->sent != SCTP_DATAGRAM_RESEND) { chk->sent = SCTP_DATAGRAM_RESEND; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); } chk->whoTo = alt; atomic_add_int(&alt->ref_count, 1); } } if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { /* * If the address went un-reachable, we need to move to * alternates for ALL chk's in queue */ sctp_move_chunks_from_net(stcb, net); } sctp_free_remote_addr(net); /* mark the retran info */ if (strrst->sent != SCTP_DATAGRAM_RESEND) sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); strrst->sent = SCTP_DATAGRAM_RESEND; strrst->flags |= CHUNK_FLAGS_FRAGMENT_OK; /* restart the timer */ sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, inp, stcb, alt); return (0); } int sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_nets *alt; struct sctp_tmit_chunk *asconf, *chk; /* is this a first send, or a retransmission? */ if (TAILQ_EMPTY(&stcb->asoc.asconf_send_queue)) { /* compose a new ASCONF chunk and send it */ sctp_send_asconf(stcb, net, SCTP_ADDR_NOT_LOCKED); } else { /* * Retransmission of the existing ASCONF is needed */ /* find the existing ASCONF */ asconf = TAILQ_FIRST(&stcb->asoc.asconf_send_queue); if (asconf == NULL) { return (0); } net = asconf->whoTo; /* do threshold management */ if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Assoc is over */ return (1); } if (asconf->snd_count > stcb->asoc.max_send_times) { /* * Something is rotten: our peer is not responding * to ASCONFs but apparently is to other chunks. * i.e. it is not properly handling the chunk type * upper bits. Mark this peer as ASCONF incapable * and cleanup. */ SCTPDBG(SCTP_DEBUG_TIMER1, "asconf_timer: Peer has not responded to our repeated ASCONFs\n"); sctp_asconf_cleanup(stcb); return (0); } /* * cleared threshold management, so now backoff the net and * select an alternate */ sctp_backoff_on_timeout(stcb, net, 1, 0, 0); alt = sctp_find_alternate_net(stcb, net, 0); if (asconf->whoTo != alt) { asconf->whoTo = alt; atomic_add_int(&alt->ref_count, 1); } /* See if an ECN Echo is also stranded */ TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) { if ((chk->whoTo == net) && (chk->rec.chunk_id.id == SCTP_ECN_ECHO)) { sctp_free_remote_addr(chk->whoTo); chk->whoTo = alt; if (chk->sent != SCTP_DATAGRAM_RESEND) { chk->sent = SCTP_DATAGRAM_RESEND; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); } atomic_add_int(&alt->ref_count, 1); } } TAILQ_FOREACH(chk, &stcb->asoc.asconf_send_queue, sctp_next) { if (chk->whoTo != alt) { sctp_free_remote_addr(chk->whoTo); chk->whoTo = alt; atomic_add_int(&alt->ref_count, 1); } if (asconf->sent != SCTP_DATAGRAM_RESEND && chk->sent != SCTP_DATAGRAM_UNSENT) sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); chk->sent = SCTP_DATAGRAM_RESEND; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; } if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { /* * If the address went un-reachable, we need to move * to the alternate for ALL chunks in queue */ sctp_move_chunks_from_net(stcb, net); } sctp_free_remote_addr(net); /* mark the retran info */ if (asconf->sent != SCTP_DATAGRAM_RESEND) sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); asconf->sent = SCTP_DATAGRAM_RESEND; asconf->flags |= CHUNK_FLAGS_FRAGMENT_OK; /* send another ASCONF if any and we can do */ sctp_send_asconf(stcb, alt, SCTP_ADDR_NOT_LOCKED); } return (0); } /* Mobility adaptation */ void sctp_delete_prim_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { if (stcb->asoc.deleted_primary == NULL) { SCTPDBG(SCTP_DEBUG_ASCONF1, "delete_prim_timer: deleted_primary is not stored...\n"); sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); return; } SCTPDBG(SCTP_DEBUG_ASCONF1, "delete_prim_timer: finished to keep deleted primary "); SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &stcb->asoc.deleted_primary->ro._l_addr.sa); sctp_free_remote_addr(stcb->asoc.deleted_primary); stcb->asoc.deleted_primary = NULL; sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED); return; } /* * For the shutdown and shutdown-ack, we do not keep one around on the * control queue. This means we must generate a new one and call the general * chunk output routine, AFTER having done threshold management. * It is assumed that net is non-NULL. */ int sctp_shutdown_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_nets *alt; /* first threshold management */ if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Assoc is over */ return (1); } sctp_backoff_on_timeout(stcb, net, 1, 0, 0); /* second select an alternative */ alt = sctp_find_alternate_net(stcb, net, 0); /* third generate a shutdown into the queue for out net */ sctp_send_shutdown(stcb, alt); /* fourth restart timer */ sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, inp, stcb, alt); return (0); } int sctp_shutdownack_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_nets *alt; /* first threshold management */ if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Assoc is over */ return (1); } sctp_backoff_on_timeout(stcb, net, 1, 0, 0); /* second select an alternative */ alt = sctp_find_alternate_net(stcb, net, 0); /* third generate a shutdown into the queue for out net */ sctp_send_shutdown_ack(stcb, alt); /* fourth restart timer */ sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, inp, stcb, alt); return (0); } static void sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { struct sctp_stream_queue_pending *sp; unsigned int i, chks_in_queue = 0; int being_filled = 0; /* * This function is ONLY called when the send/sent queues are empty. */ if ((stcb == NULL) || (inp == NULL)) return; if (stcb->asoc.sent_queue_retran_cnt) { SCTP_PRINTF("Hmm, sent_queue_retran_cnt is non-zero %d\n", stcb->asoc.sent_queue_retran_cnt); stcb->asoc.sent_queue_retran_cnt = 0; } if (stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, &stcb->asoc)) { /* No stream scheduler information, initialize scheduler */ stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 0); if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, &stcb->asoc)) { /* yep, we lost a stream or two */ SCTP_PRINTF("Found additional streams NOT managed by scheduler, corrected\n"); } else { /* no streams lost */ stcb->asoc.total_output_queue_size = 0; } } /* Check to see if some data queued, if so report it */ for (i = 0; i < stcb->asoc.streamoutcnt; i++) { if (!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) { TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) { if (sp->msg_is_complete) being_filled++; chks_in_queue++; } } } if (chks_in_queue != stcb->asoc.stream_queue_cnt) { SCTP_PRINTF("Hmm, stream queue cnt at %d I counted %d in stream out wheel\n", stcb->asoc.stream_queue_cnt, chks_in_queue); } if (chks_in_queue) { /* call the output queue function */ sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED); if ((TAILQ_EMPTY(&stcb->asoc.send_queue)) && (TAILQ_EMPTY(&stcb->asoc.sent_queue))) { /* * Probably should go in and make it go back through * and add fragments allowed */ if (being_filled == 0) { SCTP_PRINTF("Still nothing moved %d chunks are stuck\n", chks_in_queue); } } } else { SCTP_PRINTF("Found no chunks on any queue tot:%lu\n", (u_long)stcb->asoc.total_output_queue_size); stcb->asoc.total_output_queue_size = 0; } } int sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { uint8_t net_was_pf; if (net->dest_state & SCTP_ADDR_PF) { net_was_pf = 1; } else { net_was_pf = 0; } if (net->hb_responded == 0) { if (net->ro._s_addr) { /* * Invalidate the src address if we did not get a * response last time. */ sctp_free_ifa(net->ro._s_addr); net->ro._s_addr = NULL; net->src_addr_selected = 0; } sctp_backoff_on_timeout(stcb, net, 1, 0, 0); if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { /* Assoc is over */ return (1); } } /* Zero PBA, if it needs it */ if (net->partial_bytes_acked) { net->partial_bytes_acked = 0; } if ((stcb->asoc.total_output_queue_size > 0) && (TAILQ_EMPTY(&stcb->asoc.send_queue)) && (TAILQ_EMPTY(&stcb->asoc.sent_queue))) { sctp_audit_stream_queues_for_size(inp, stcb); } if (!(net->dest_state & SCTP_ADDR_NOHB) && !((net_was_pf == 0) && (net->dest_state & SCTP_ADDR_PF))) { /* * when move to PF during threshold mangement, a HB has been * queued in that routine */ uint32_t ms_gone_by; if ((net->last_sent_time.tv_sec > 0) || (net->last_sent_time.tv_usec > 0)) { struct timeval diff; SCTP_GETTIME_TIMEVAL(&diff); timevalsub(&diff, &net->last_sent_time); ms_gone_by = (uint32_t)(diff.tv_sec * 1000) + (uint32_t)(diff.tv_usec / 1000); } else { ms_gone_by = 0xffffffff; } if ((ms_gone_by >= net->heart_beat_delay) || (net->dest_state & SCTP_ADDR_PF)) { sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); } } return (0); } void sctp_pathmtu_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { uint32_t next_mtu, mtu; next_mtu = sctp_get_next_mtu(net->mtu); if ((next_mtu > net->mtu) && (net->port == 0)) { if ((net->src_addr_selected == 0) || (net->ro._s_addr == NULL) || (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { if ((net->ro._s_addr != NULL) && (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { sctp_free_ifa(net->ro._s_addr); net->ro._s_addr = NULL; net->src_addr_selected = 0; } else if (net->ro._s_addr == NULL) { #if defined(INET6) && defined(SCTP_EMBEDDED_V6_SCOPE) if (net->ro._l_addr.sa.sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; /* KAME hack: embed scopeid */ (void)sa6_embedscope(sin6, MODULE_GLOBAL(ip6_use_defzone)); } #endif net->ro._s_addr = sctp_source_address_selection(inp, stcb, (sctp_route_t *)&net->ro, net, 0, stcb->asoc.vrf_id); #if defined(INET6) && defined(SCTP_EMBEDDED_V6_SCOPE) if (net->ro._l_addr.sa.sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; (void)sa6_recoverscope(sin6); } #endif /* INET6 */ } if (net->ro._s_addr) net->src_addr_selected = 1; } if (net->ro._s_addr) { mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._s_addr.sa, net->ro.ro_rt); #if defined(INET) || defined(INET6) if (net->port) { mtu -= sizeof(struct udphdr); } #endif if (mtu > next_mtu) { net->mtu = next_mtu; } else { net->mtu = mtu; } } } /* restart the timer */ sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net); } void sctp_autoclose_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb) { struct timeval tn, *tim_touse; struct sctp_association *asoc; - int ticks_gone_by; + uint32_t ticks_gone_by; (void)SCTP_GETTIME_TIMEVAL(&tn); - if (stcb->asoc.sctp_autoclose_ticks && + if (stcb->asoc.sctp_autoclose_ticks > 0 && sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) { /* Auto close is on */ asoc = &stcb->asoc; /* pick the time to use */ if (asoc->time_last_rcvd.tv_sec > asoc->time_last_sent.tv_sec) { tim_touse = &asoc->time_last_rcvd; } else { tim_touse = &asoc->time_last_sent; } /* Now has long enough transpired to autoclose? */ - ticks_gone_by = SEC_TO_TICKS(tn.tv_sec - tim_touse->tv_sec); - if ((ticks_gone_by > 0) && - (ticks_gone_by >= (int)asoc->sctp_autoclose_ticks)) { + ticks_gone_by = SEC_TO_TICKS((uint32_t)(tn.tv_sec - tim_touse->tv_sec)); + if (ticks_gone_by >= asoc->sctp_autoclose_ticks) { /* * autoclose time has hit, call the output routine, * which should do nothing just to be SURE we don't * have hanging data. We can then safely check the * queues and know that we are clear to send * shutdown */ sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_AUTOCLOSE_TMR, SCTP_SO_NOT_LOCKED); /* Are we clean? */ if (TAILQ_EMPTY(&asoc->send_queue) && TAILQ_EMPTY(&asoc->sent_queue)) { /* * there is nothing queued to send, so I'm * done... */ if (SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) { /* only send SHUTDOWN 1st time thru */ struct sctp_nets *net; if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_SENT); sctp_stop_timers_for_shutdown(stcb); if (stcb->asoc.alternate) { net = stcb->asoc.alternate; } else { net = stcb->asoc.primary_destination; } sctp_send_shutdown(stcb, net); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, NULL); } } } else { /* * No auto close at this time, reset t-o to check * later */ - int tmp; + uint32_t tmp; /* fool the timer startup to use the time left */ tmp = asoc->sctp_autoclose_ticks; asoc->sctp_autoclose_ticks -= ticks_gone_by; sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL); /* restore the real tick value */ asoc->sctp_autoclose_ticks = tmp; } } }