diff options
| author | Randall Stewart <rrs@FreeBSD.org> | 2007-09-08 17:48:46 +0000 |
|---|---|---|
| committer | Randall Stewart <rrs@FreeBSD.org> | 2007-09-08 17:48:46 +0000 |
| commit | 851b7298b3f8f09d1a337bcd5bbe7222543872f5 (patch) | |
| tree | c5cd7d7c9d05970381c8f80e243b1da248135d4c /sys/netinet6 | |
| parent | ceaad40ae729dea2c5d8ffcfdd45bb96fb8969d2 (diff) | |
Notes
Diffstat (limited to 'sys/netinet6')
| -rw-r--r-- | sys/netinet6/sctp6_usrreq.c | 129 |
1 files changed, 119 insertions, 10 deletions
diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 2d4c82c7d7cc..89bb51c5af75 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -315,13 +315,124 @@ out: } +static void +sctp6_notify(struct sctp_inpcb *inp, + struct icmp6_hdr *icmph, + struct sctphdr *sh, + struct sockaddr *to, + struct sctp_tcb *stcb, + struct sctp_nets *net) +{ +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + struct socket *so; + +#endif + /* protection */ + int reason; + + + if ((inp == NULL) || (stcb == NULL) || (net == NULL) || + (sh == NULL) || (to == NULL)) { + if (stcb) + SCTP_TCB_UNLOCK(stcb); + return; + } + /* First job is to verify the vtag matches what I would send */ + if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag)) { + SCTP_TCB_UNLOCK(stcb); + return; + } + if (icmph->icmp6_type != ICMP_UNREACH) { + /* We only care about unreachable */ + SCTP_TCB_UNLOCK(stcb); + return; + } + if ((icmph->icmp6_code == ICMP_UNREACH_NET) || + (icmph->icmp6_code == ICMP_UNREACH_HOST) || + (icmph->icmp6_code == ICMP_UNREACH_NET_UNKNOWN) || + (icmph->icmp6_code == ICMP_UNREACH_HOST_UNKNOWN) || + (icmph->icmp6_code == ICMP_UNREACH_ISOLATED) || + (icmph->icmp6_code == ICMP_UNREACH_NET_PROHIB) || + (icmph->icmp6_code == ICMP_UNREACH_HOST_PROHIB) || + (icmph->icmp6_code == ICMP_UNREACH_FILTER_PROHIB)) { + + /* + * Hmm reachablity problems we must examine closely. If its + * not reachable, we may have lost a network. Or if there is + * NO protocol at the other end named SCTP. well we consider + * it a OOTB abort. + */ + if (net->dest_state & SCTP_ADDR_REACHABLE) { + /* Ok that destination is NOT reachable */ + SCTP_PRINTF("ICMP (thresh %d/%d) takes interface %p down\n", + net->error_count, + net->failure_threshold, + net); + + net->dest_state &= ~SCTP_ADDR_REACHABLE; + net->dest_state |= SCTP_ADDR_NOT_REACHABLE; + /* + * JRS 5/14/07 - If a destination is unreachable, + * the PF bit is turned off. This allows an + * unambiguous use of the PF bit for destinations + * that are reachable but potentially failed. If the + * destination is set to the unreachable state, also + * set the destination to the PF state. + */ + /* + * Add debug message here if destination is not in + * PF state. + */ + /* Stop any running T3 timers here? */ + if (sctp_cmt_on_off && sctp_cmt_pf) { + net->dest_state &= ~SCTP_ADDR_PF; + SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from PF to unreachable.\n", + net); + } + net->error_count = net->failure_threshold + 1; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, + stcb, SCTP_FAILED_THRESHOLD, + (void *)net, SCTP_SO_NOT_LOCKED); + } + SCTP_TCB_UNLOCK(stcb); + } else if ((icmph->icmp6_code == ICMP_UNREACH_PROTOCOL) || + (icmph->icmp6_code == ICMP_UNREACH_PORT)) { + /* + * Here the peer is either playing tricks on us, including + * an address that belongs to someone who does not support + * SCTP OR was a userland implementation that shutdown and + * now is dead. In either case treat it like a OOTB abort + * with no TCB + */ + reason = SCTP_PEER_FAULTY; + sctp_abort_notification(stcb, reason, SCTP_SO_NOT_LOCKED); +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + so = SCTP_INP_SO(inp); + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); + SCTP_SOCKET_LOCK(so, 1); + SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); +#endif + (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_2); +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + SCTP_SOCKET_UNLOCK(so, 1); + /* SCTP_TCB_UNLOCK(stcb); MT: I think this is not needed. */ +#endif + /* no need to unlock here, since the TCB is gone */ + } else { + SCTP_TCB_UNLOCK(stcb); + } +} + + + void sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) { struct sctphdr sh; struct ip6ctlparam *ip6cp = NULL; uint32_t vrf_id; - int cm; vrf_id = SCTP_DEFAULT_VRFID; @@ -381,12 +492,7 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) net); /* inp's ref-count reduced && stcb unlocked */ } else { - if (cmd == PRC_HOSTDEAD) { - cm = EHOSTUNREACH; - } else { - cm = inet6ctlerrmap[cmd]; - } - sctp_notify(inp, cm, &sh, + sctp6_notify(inp, ip6cp->ip6c_icmp6, &sh, (struct sockaddr *)&final, stcb, net); /* inp's ref-count reduced && stcb unlocked */ @@ -489,8 +595,10 @@ sctp6_abort(struct socket *so) uint32_t flags; inp = (struct sctp_inpcb *)so->so_pcb; - if (inp == 0) + if (inp == 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); return; + } sctp_must_try_again: flags = inp->sctp_flags; #ifdef SCTP_LOG_CLOSING @@ -627,11 +735,12 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p) sin6_p = (struct sockaddr_in6 *)addr; - if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) + if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { /* can't bind v4-mapped addrs either! */ /* NOTE: we don't support SIIT */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return EINVAL; + } } } error = sctp_inpcb_bind(so, addr, NULL, p); |
