📄 sm_sideeffect.c
字号:
struct sctp_association *asoc, struct sctp_chunk *chunk, sctp_init_chunk_t *peer_init, gfp_t gfp){ int error; /* We only process the init as a sideeffect in a single * case. This is when we process the INIT-ACK. If we * fail during INIT processing (due to malloc problems), * just return the error and stop processing the stack. */ if (!sctp_process_init(asoc, chunk->chunk_hdr->type, sctp_source(chunk), peer_init, gfp)) error = -ENOMEM; else error = 0; return error;}/* Helper function to break out starting up of heartbeat timers. */static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, struct sctp_association *asoc){ struct sctp_transport *t; struct list_head *pos; /* Start a heartbeat timer for each transport on the association. * hold a reference on the transport to make sure none of * the needed data structures go away. */ list_for_each(pos, &asoc->peer.transport_addr_list) { t = list_entry(pos, struct sctp_transport, transports); if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) sctp_transport_hold(t); }}static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, struct sctp_association *asoc){ struct sctp_transport *t; struct list_head *pos; /* Stop all heartbeat timers. */ list_for_each(pos, &asoc->peer.transport_addr_list) { t = list_entry(pos, struct sctp_transport, transports); if (del_timer(&t->hb_timer)) sctp_transport_put(t); }}/* Helper function to stop any pending T3-RTX timers */static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds, struct sctp_association *asoc){ struct sctp_transport *t; struct list_head *pos; list_for_each(pos, &asoc->peer.transport_addr_list) { t = list_entry(pos, struct sctp_transport, transports); if (timer_pending(&t->T3_rtx_timer) && del_timer(&t->T3_rtx_timer)) { sctp_transport_put(t); } }}/* Helper function to update the heartbeat timer. */static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_transport *t){ /* Update the heartbeat timer. */ if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) sctp_transport_hold(t);}/* Helper function to handle the reception of an HEARTBEAT ACK. */static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_transport *t, struct sctp_chunk *chunk){ sctp_sender_hb_info_t *hbinfo; /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the * HEARTBEAT should clear the error counter of the destination * transport address to which the HEARTBEAT was sent. * The association's overall error count is also cleared. */ t->error_count = 0; t->asoc->overall_error_count = 0; /* Mark the destination transport address as active if it is not so * marked. */ if ((t->state == SCTP_INACTIVE) || (t->state == SCTP_UNCONFIRMED)) sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, SCTP_HEARTBEAT_SUCCESS); /* The receiver of the HEARTBEAT ACK should also perform an * RTT measurement for that destination transport address * using the time value carried in the HEARTBEAT ACK chunk. * If the transport's rto_pending variable has been cleared, * it was most likely due to a retransmit. However, we want * to re-enable it to properly update the rto. */ if (t->rto_pending == 0) t->rto_pending = 1; hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); /* Update the heartbeat timer. */ if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) sctp_transport_hold(t);}/* Helper function to do a transport reset at the expiry of the hearbeat * timer. */static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_transport *t){ sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); /* Mark one strike against a transport. */ sctp_do_8_2_transport_strike(asoc, t);}/* Helper function to process the process SACK command. */static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_sackhdr *sackh){ int err; if (sctp_outq_sack(&asoc->outqueue, sackh)) { /* There are no more TSNs awaiting SACK. */ err = sctp_do_sm(SCTP_EVENT_T_OTHER, SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), asoc->state, asoc->ep, asoc, NULL, GFP_ATOMIC); } else { /* Windows may have opened, so we need * to check if we have DATA to transmit */ err = sctp_outq_flush(&asoc->outqueue, 0); } return err;}/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set * the transport for a shutdown chunk. */static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk){ struct sctp_transport *t; t = sctp_assoc_choose_shutdown_transport(asoc); asoc->shutdown_last_sent_to = t; asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; chunk->transport = t;}/* Helper function to change the state of an association. */static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, sctp_state_t state){ struct sock *sk = asoc->base.sk; asoc->state = state; SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n", asoc, sctp_state_tbl[state]); if (sctp_style(sk, TCP)) { /* Change the sk->sk_state of a TCP-style socket that has * sucessfully completed a connect() call. */ if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED)) sk->sk_state = SCTP_SS_ESTABLISHED; /* Set the RCV_SHUTDOWN flag when a SHUTDOWN is received. */ if (sctp_state(asoc, SHUTDOWN_RECEIVED) && sctp_sstate(sk, ESTABLISHED)) sk->sk_shutdown |= RCV_SHUTDOWN; } if (sctp_state(asoc, COOKIE_WAIT)) { /* Reset init timeouts since they may have been * increased due to timer expirations. */ asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] = asoc->rto_initial; asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] = asoc->rto_initial; } if (sctp_state(asoc, ESTABLISHED) || sctp_state(asoc, CLOSED) || sctp_state(asoc, SHUTDOWN_RECEIVED)) { /* Wake up any processes waiting in the asoc's wait queue in * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). */ if (waitqueue_active(&asoc->wait)) wake_up_interruptible(&asoc->wait); /* Wake up any processes waiting in the sk's sleep queue of * a TCP-style or UDP-style peeled-off socket in * sctp_wait_for_accept() or sctp_wait_for_packet(). * For a UDP-style socket, the waiters are woken up by the * notifications. */ if (!sctp_style(sk, UDP)) sk->sk_state_change(sk); }}/* Helper function to delete an association. */static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, struct sctp_association *asoc){ struct sock *sk = asoc->base.sk; /* If it is a non-temporary association belonging to a TCP-style * listening socket that is not closed, do not free it so that accept() * can pick it up later. */ if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING) && (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK)) return; sctp_unhash_established(asoc); sctp_association_free(asoc);}/* * ADDIP Section 4.1 ASCONF Chunk Procedures * A4) Start a T-4 RTO timer, using the RTO value of the selected * destination address (we use active path instead of primary path just * because primary path may be inactive. */static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk){ struct sctp_transport *t; t = asoc->peer.active_path; asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto; chunk->transport = t;}/* Process an incoming Operation Error Chunk. */static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk){ struct sctp_operr_chunk *operr_chunk; struct sctp_errhdr *err_hdr; operr_chunk = (struct sctp_operr_chunk *)chunk->chunk_hdr; err_hdr = &operr_chunk->err_hdr; switch (err_hdr->cause) { case SCTP_ERROR_UNKNOWN_CHUNK: { struct sctp_chunkhdr *unk_chunk_hdr; unk_chunk_hdr = (struct sctp_chunkhdr *)err_hdr->variable; switch (unk_chunk_hdr->type) { /* ADDIP 4.1 A9) If the peer responds to an ASCONF with an * ERROR chunk reporting that it did not recognized the ASCONF * chunk type, the sender of the ASCONF MUST NOT send any * further ASCONF chunks and MUST stop its T-4 timer. */ case SCTP_CID_ASCONF: asoc->peer.asconf_capable = 0; sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); break; default: break; } break; } default: break; }}/* Process variable FWDTSN chunk information. */static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk){ struct sctp_fwdtsn_skip *skip; /* Walk through all the skipped SSNs */ sctp_walk_fwdtsn(skip, chunk) { sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn)); } return;}/* Helper function to remove the association non-primary peer * transports. */static void sctp_cmd_del_non_primary(struct sctp_association *asoc){ struct sctp_transport *t; struct list_head *pos; struct list_head *temp; list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { t = list_entry(pos, struct sctp_transport, transports); if (!sctp_cmp_addr_exact(&t->ipaddr, &asoc->peer.primary_addr)) { sctp_assoc_del_peer(asoc, &t->ipaddr); } } return;}/* Helper function to set sk_err on a 1-1 style socket. */static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error){ struct sock *sk = asoc->base.sk; if (!sctp_style(sk, UDP)) sk->sk_err = error;}/* Helper function to generate an association change event */static void sctp_cmd_assoc_change(sctp_cmd_seq_t *commands, struct sctp_association *asoc, u8 state){ struct sctp_ulpevent *ev; ev = sctp_ulpevent_make_assoc_change(asoc, 0, state, 0, asoc->c.sinit_num_ostreams, asoc->c.sinit_max_instreams, NULL, GFP_ATOMIC); if (ev) sctp_ulpq_tail_event(&asoc->ulpq, ev);}/* Helper function to generate an adaptation indication event */static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands, struct sctp_association *asoc){ struct sctp_ulpevent *ev; ev = sctp_ulpevent_make_adaptation_indication(asoc, GFP_ATOMIC); if (ev) sctp_ulpq_tail_event(&asoc->ulpq, ev);}/* These three macros allow us to pull the debugging code out of the * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. */#define DEBUG_PRE \ SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \ "ep %p, %s, %s, asoc %p[%s], %s\n", \ ep, sctp_evttype_tbl[event_type], \ (*debug_fn)(subtype), asoc, \ sctp_state_tbl[state], state_fn->name)#define DEBUG_POST \ SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \ "asoc %p, status: %s\n", \ asoc, sctp_status_tbl[status])#define DEBUG_POST_SFX \ SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ error, asoc, \ sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED])/* * This is the master state machine processing function. * * If you want to understand all of lksctp, this is a * good place to start. */int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, sctp_state_t state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, gfp_t gfp){ sctp_cmd_seq_t commands; const sctp_sm_table_entry_t *state_fn; sctp_disposition_t status; int error = 0; typedef const char *(printfn_t)(sctp_subtype_t); static printfn_t *table[] = { NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, }; printfn_t *debug_fn __attribute__ ((unused)) = table[event_type]; /* Look up the state function, run it, and then process the * side effects. These three steps are the heart of lksctp. */ state_fn = sctp_sm_lookup_event(event_type, state, subtype); sctp_init_cmd_seq(&commands); DEBUG_PRE; status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands); DEBUG_POST; error = sctp_side_effects(event_type, subtype, state, ep, asoc, event_arg, status, &commands, gfp); DEBUG_POST_SFX; return error;}#undef DEBUG_PRE#undef DEBUG_POST/***************************************************************** * This the master state function side effect processing function. *****************************************************************/static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, sctp_state_t state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, sctp_disposition_t status, sctp_cmd_seq_t *commands, gfp_t gfp){ int error; /* FIXME - Most of the dispositions left today would be categorized * as "exceptional" dispositions. For those dispositions, it * may not be proper to run through any of the commands at all. * For example, the command interpreter might be run only with * disposition SCTP_DISPOSITION_CONSUME. */ if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state, ep, asoc, event_arg, status, commands, gfp))) goto bail; switch (status) { case SCTP_DISPOSITION_DISCARD: SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, " "event_type %d, event_id %d\n", state, event_type, subtype.chunk); break; case SCTP_DISPOSITION_NOMEM: /* We ran out of memory, so we need to discard this * packet. */ /* BUG--we should now recover some memory, probably by * reneging... */ error = -ENOMEM; break; case SCTP_DISPOSITION_DELETE_TCB: /* This should now be a command. */ break; case SCTP_DISPOSITION_CONSUME: case SCTP_DISPOSITION_ABORT: /* * We should no longer have much work to do here as the * real work has been done as explicit commands above. */ break; case SCTP_DISPOSITION_VIOLATION: if (net_ratelimit()) printk(KERN_ERR "sctp protocol violation state %d " "chunkid %d\n", state, subtype.chunk); break; case SCTP_DISPOSITION_NOT_IMPL: printk(KERN_WARNING "sctp unimplemented feature in state %d, " "event_type %d, event_id %d\n", state, event_type, subtype.chunk); break; case SCTP_DISPOSITION_BUG: printk(KERN_ERR "sctp bug in state %d, " "event_type %d, event_id %d\n", state, event_type, subtype.chunk); BUG(); break; default: printk(KERN_ERR "sctp impossible disposition %d " "in state %d, event_type %d, event_id %d\n", status, state, event_type, subtype.chunk); BUG(); break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -