/* make sure the source address exists as a destination net */
src_net = sctp_findnet(stcb, src); if (src_net == NULL) { /* not found */ return (-1);
}
/* make sure the source address is not being deleted */ if (sctp_cmpaddr(sa, src)) { /* trying to delete the source address! */
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_asconf_delete_ip: tried to delete source addr\n");
m_reply = sctp_asconf_error_response(aph->correlation_id,
SCTP_CAUSE_DELETING_SRC_ADDR, (uint8_t *) aph,
aparam_length); return (m_reply);
}
/* if deleting 0.0.0.0/::0, delete all addresses except src addr */ if (zero_address && SCTP_BASE_SYSCTL(sctp_nat_friendly)) {
result = sctp_asconf_del_remote_addrs_except(stcb, src);
if (result) { /* src address did not exist? */
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_asconf_delete_ip: src addr does not exist?\n"); /* what error to reply with?? */
m_reply =
sctp_asconf_error_response(aph->correlation_id,
SCTP_CAUSE_REQUEST_REFUSED, (uint8_t *) aph,
aparam_length);
} elseif (response_required) {
m_reply =
sctp_asconf_success_response(aph->correlation_id);
} return (m_reply);
}
/* delete the address */
result = sctp_del_remote_addr(stcb, sa); /* *noteifresult==-2,theaddressdoesn'texistintheasocbut *sinceit'sbeingdeletedanyways,wejustackthedelete--but *thisprobablymeanssomethinghasalreadygoneawry
*/ if (result == -1) { /* only one address in the asoc */
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_asconf_delete_ip: tried to delete last IP addr!\n");
m_reply = sctp_asconf_error_response(aph->correlation_id,
SCTP_CAUSE_DELETING_LAST_ADDR, (uint8_t *) aph,
aparam_length);
} else { if (response_required) {
m_reply = sctp_asconf_success_response(aph->correlation_id);
} /* notify upper layer */
sctp_ulp_notify(SCTP_NOTIFY_ASCONF_DELETE_IP, stcb, 0, sa, SCTP_SO_NOT_LOCKED);
} return (m_reply);
}
/* verify minimum length */ if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_asconf_chunk)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: chunk too small = %xh\n",
ntohs(cp->ch.chunk_length)); return;
}
asoc = &stcb->asoc;
serial_num = ntohl(cp->serial_number);
if (SCTP_TSN_GE(asoc->asconf_seq_in, serial_num)) { /* got a duplicate ASCONF */
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: got duplicate serial number = %xh\n",
serial_num); return;
} elseif (serial_num != (asoc->asconf_seq_in + 1)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: incorrect serial number = %xh (expected next = %xh)\n",
serial_num, asoc->asconf_seq_in + 1); return;
}
/* it's the expected "next" sequence number, so process it */
asoc->asconf_seq_in = serial_num; /* update sequence */ /* get length of all the param's in the ASCONF */
asconf_limit = offset + ntohs(cp->ch.chunk_length);
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: asconf_limit=%u, sequence=%xh\n",
asconf_limit, serial_num);
if (first) { /* delete old cache */
SCTPDBG(SCTP_DEBUG_ASCONF1,"handle_asconf: Now processing first ASCONF. Try to delete old cache\n");
m_ack = sctp_get_mbuf_for_msg(sizeof(struct sctp_asconf_ack_chunk), 0,
M_NOWAIT, 1, MT_DATA); if (m_ack == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: couldn't get mbuf!\n"); return;
}
m_tail = m_ack; /* current reply chain's tail */
/* fill in ASCONF-ACK header */
ack_cp = mtod(m_ack, struct sctp_asconf_ack_chunk *);
ack_cp->ch.chunk_type = SCTP_ASCONF_ACK;
ack_cp->ch.chunk_flags = 0;
ack_cp->serial_number = htonl(serial_num); /* set initial lengths (eg. just an ASCONF-ACK), ntohx at the end! */
SCTP_BUF_LEN(m_ack) = sizeof(struct sctp_asconf_ack_chunk);
ack_cp->ch.chunk_length = sizeof(struct sctp_asconf_ack_chunk);
/* skip the lookup address parameter */
offset += sizeof(struct sctp_asconf_chunk);
p_addr = (struct sctp_ipv6addr_param *)sctp_m_getptr(m, offset, sizeof(struct sctp_paramhdr), (uint8_t *)&aparam_buf); if (p_addr == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: couldn't get lookup addr!\n"); /* respond with a missing/invalid mandatory parameter error */
sctp_m_freem(m_ack); return;
} /* skip lookup addr */
offset += SCTP_SIZE32(ntohs(p_addr->ph.param_length)); /* get pointer to first asconf param in ASCONF */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_asconf_paramhdr), (uint8_t *)&aparam_buf); if (aph == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "Empty ASCONF received?\n"); goto send_reply;
} /* process through all parameters */
cnt = 0; while (aph != NULL) { unsignedint param_length, param_type;
param_type = ntohs(aph->ph.param_type);
param_length = ntohs(aph->ph.param_length); if (offset + param_length > asconf_limit) { /* parameter goes beyond end of chunk! */
sctp_m_freem(m_ack); return;
}
m_result = NULL;
if (param_length > sizeof(aparam_buf)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: param length (%u) larger than buffer size!\n", param_length);
sctp_m_freem(m_ack); return;
} if (param_length < sizeof(struct sctp_asconf_paramhdr)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: param length (%u) too short\n", param_length);
sctp_m_freem(m_ack); return;
} /* get the entire parameter */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, param_length, aparam_buf); if (aph == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: couldn't get entire param\n");
sctp_m_freem(m_ack); return;
} switch (param_type) { case SCTP_ADD_IP_ADDRESS:
m_result = sctp_process_asconf_add_ip(src, aph, stcb,
(cnt < SCTP_BASE_SYSCTL(sctp_hb_maxburst)), error);
cnt++; break; case SCTP_DEL_IP_ADDRESS:
m_result = sctp_process_asconf_delete_ip(src, aph, stcb,
error); break; case SCTP_ERROR_CAUSE_IND: /* not valid in an ASCONF chunk */ break; case SCTP_SET_PRIM_ADDR:
m_result = sctp_process_asconf_set_primary(src, aph,
stcb, error); break; case SCTP_NAT_VTAGS:
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: sees a NAT VTAG state parameter\n"); break; case SCTP_SUCCESS_REPORT: /* not valid in an ASCONF chunk */ break; case SCTP_ULP_ADAPTATION: /* FIX */ break; default: if ((param_type & 0x8000) == 0) { /* Been told to STOP at this param */
asconf_limit = offset; /* *FIXFIX-Weneedtocall *sctp_arethere_unrecognized_parameters() *togetaoperrandsenditforany *param'swiththe0x4000bitsetORdoit *hereourselves...notewestillmustSTOP *ifthe0x8000bitisclear.
*/
} /* unknown/invalid param type */ break;
} /* switch */
/* add any (error) result to the reply mbuf chain */ if (m_result != NULL) {
SCTP_BUF_NEXT(m_tail) = m_result;
m_tail = m_result;
ack_cp->ch.chunk_length += SCTP_BUF_LEN(m_result); /* set flag to force success reports */
error = 1;
}
offset += SCTP_SIZE32(param_length); /* update remaining ASCONF message length to process */ if (offset >= asconf_limit) { /* no more data in the mbuf chain */ break;
} /* get pointer to next asconf param */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_asconf_paramhdr),
(uint8_t *)&aparam_buf); if (aph == NULL) { /* can't get an asconf paramhdr */
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: can't get asconf param hdr!\n"); /* FIX ME - add error here... */
}
}
send_reply:
ack_cp->ch.chunk_length = htons(ack_cp->ch.chunk_length); /* save the ASCONF-ACK reply */
ack = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_asconf_ack), struct sctp_asconf_ack); if (ack == NULL) {
sctp_m_freem(m_ack); return;
}
ack->serial_number = serial_num;
ack->last_sent_to = NULL;
ack->data = m_ack;
ack->len = 0; for (n = m_ack; n != NULL; n = SCTP_BUF_NEXT(n)) {
ack->len += SCTP_BUF_LEN(n);
}
TAILQ_INSERT_TAIL(&stcb->asoc.asconf_ack_sent, ack, next);
/* see if last_control_chunk_from is set properly (use IP src addr) */ if (stcb->asoc.last_control_chunk_from == NULL) { /* *thiscouldhappenifthesourceaddresswasjustnewly *added
*/
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: looking up net for IP source address\n");
SCTPDBG(SCTP_DEBUG_ASCONF1, "Looking for IP source: ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, src); /* look up the from address */
stcb->asoc.last_control_chunk_from = sctp_findnet(stcb, src); #ifdef SCTP_DEBUG if (stcb->asoc.last_control_chunk_from == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "handle_asconf: IP source address not found?!\n");
} #endif
}
}
/* queue in an advisory set primary too */
(void)sctp_asconf_queue_mgmt(stcb, ifa, SCTP_SET_PRIM_ADDR); /* let caller know we should send this out immediately */
status = 1;
} return (status);
}
if (stcb == NULL) { return (-1);
} /* see if peer supports ASCONF */ if (stcb->asoc.asconf_supported == 0) { return (-1);
} /* make sure the request isn't already in the queue */
TAILQ_FOREACH_SAFE(aa, &stcb->asoc.asconf_queue, next, aa_next) { /* address match? */ if (sctp_asconf_addr_match(aa, sa) == 0) continue; /* is the request already in queue (sent or not) */ if (aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) { return (-1);
} /* is the negative request already in queue, and not sent */ if (aa->sent == 1) continue; if (aa->ap.aph.ph.param_type == SCTP_ADD_IP_ADDRESS) { /* add already queued, so remove existing entry */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
sctp_del_local_addr_restricted(stcb, aa->ifa); /* free the entry */
SCTP_FREE(aa, SCTP_M_ASC_ADDR); return (-1);
}
} /* for each aa */
/* find any existing ifa-- NOTE ifa CAN be allowed to be NULL */
ifa = sctp_find_ifa_by_addr(sa, stcb->asoc.vrf_id, SCTP_ADDR_NOT_LOCKED);
/* adding new request to the queue */
SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa),
SCTP_M_ASC_ADDR); if (aa == NULL) { /* didn't get memory */
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_queue_sa_delete: failed to get memory!\n"); return (-1);
}
aa->special_del = 0; /* fill in asconf address parameter fields */ /* top level elements are "networked" during send */
aa->ap.aph.ph.param_type = SCTP_DEL_IP_ADDRESS;
aa->ifa = ifa; if (ifa)
atomic_add_int(&ifa->refcount, 1); /* correlation_id filled in during send routine later... */ switch (sa->sa_family) { #ifdef INET6 case AF_INET6:
{ /* IPv6 address */ struct sockaddr_in6 *sin6;
/* process this param */
param_type = aparam->ap.aph.ph.param_type; switch (param_type) { case SCTP_ADD_IP_ADDRESS:
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_param_ack: added IP address\n");
sctp_asconf_addr_mgmt_ack(stcb, aparam->ifa, flag); break; case SCTP_DEL_IP_ADDRESS:
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_param_ack: deleted IP address\n"); /* nothing really to do... lists already updated */ break; case SCTP_SET_PRIM_ADDR:
SCTPDBG(SCTP_DEBUG_ASCONF1, "process_param_ack: set primary IP address\n"); /* nothing to do... peer may start using this addr */ break; default: /* should NEVER happen */ break;
}
/* remove the param and free it */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aparam, next); if (aparam->ifa)
sctp_free_ifa(aparam->ifa);
SCTP_FREE(aparam, SCTP_M_ASC_ADDR);
}
/* *cleanupfromabadasconfackparameter
*/ staticvoid
sctp_asconf_ack_clear(struct sctp_tcb *stcb SCTP_UNUSED)
{ /* assume peer doesn't really know how to do asconfs */ /* XXX we could free the pending queue here */
/* process the ASCONF-ACK contents */
ack_length = ntohs(cp->ch.chunk_length) - sizeof(struct sctp_asconf_ack_chunk);
offset += sizeof(struct sctp_asconf_ack_chunk); /* process through all parameters */ while (ack_length >= sizeof(struct sctp_asconf_paramhdr)) { unsignedint param_length, param_type;
/* get pointer to next asconf parameter */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_asconf_paramhdr), aparam_buf); if (aph == NULL) { /* can't get an asconf paramhdr */
sctp_asconf_ack_clear(stcb); return;
}
param_type = ntohs(aph->ph.param_type);
param_length = ntohs(aph->ph.param_length); if (param_length > ack_length) {
sctp_asconf_ack_clear(stcb); return;
} if (param_length < sizeof(struct sctp_asconf_paramhdr)) {
sctp_asconf_ack_clear(stcb); return;
} /* get the complete parameter... */ if (param_length > sizeof(aparam_buf)) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "param length (%u) larger than buffer size!\n", param_length);
sctp_asconf_ack_clear(stcb); return;
}
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, param_length, aparam_buf); if (aph == NULL) {
sctp_asconf_ack_clear(stcb); return;
} /* correlation_id is transparent to peer, no ntohl needed */
id = aph->correlation_id;
switch (param_type) { case SCTP_ERROR_CAUSE_IND:
last_error_id = id; /* find the corresponding asconf param in our queue */
ap = sctp_asconf_find_param(stcb, id); if (ap == NULL) { /* hmm... can't find this in our queue! */ break;
} /* process the parameter, failed flag */
sctp_asconf_process_param_ack(stcb, ap, 0); /* process the error response */
sctp_asconf_process_error(stcb, aph); break; case SCTP_SUCCESS_REPORT: /* find the corresponding asconf param in our queue */
ap = sctp_asconf_find_param(stcb, id); if (ap == NULL) { /* hmm... can't find this in our queue! */ break;
} /* process the parameter, success flag */
sctp_asconf_process_param_ack(stcb, ap, 1); break; default: break;
} /* switch */
/* update remaining ASCONF-ACK message length to process */ if (ack_length > SCTP_SIZE32(param_length)) {
ack_length -= SCTP_SIZE32(param_length);
} else { break;
}
offset += SCTP_SIZE32(param_length);
} /* while */
/* *ifthereareany"sent"paramsstillonthequeue,theseare *implicitly"success",or"failed"(ifwegotanerrorback)... *soprocesstheseappropriately * *weassumethatthecorrelation_id'saremonotonicallyincreasing *beginningfrom1andthatwedon'thave*that*manyoutstanding *atanygiventime
*/ if (last_error_id == 0)
last_error_id--; /* set to "max" value */
TAILQ_FOREACH_SAFE(aa, &stcb->asoc.asconf_queue, next, aa_next) { if (aa->sent == 1) { /* *implicitlysuccessfulorfailedifcorrelation_id *<last_error_id,thensuccesselse,failure
*/ if (aa->ap.aph.correlation_id < last_error_id)
sctp_asconf_process_param_ack(stcb, aa, 1); else
sctp_asconf_process_param_ack(stcb, aa, 0);
} else { /* *sincewealwaysprocessinorder(FIFOqueue)if *wereachonethathasn'tbeensent,therest *shouldnothavebeensenteither.so,we're *done...
*/ break;
}
}
/* update the next sequence number to use */
asoc->asconf_seq_out_acked++; /* remove the old ASCONF on our outbound queue */
sctp_toss_old_asconf(stcb); if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) { #ifdef SCTP_TIMER_BASED_ASCONF /* we have more params, so restart our timer */
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep,
stcb, net); #else /* we have more params, so send out more */
sctp_send_asconf(stcb, net, SCTP_ADDR_NOT_LOCKED); #endif
}
}
if (sa->sa_family != AF_INET6) { /* wrong family */ return (0);
}
sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) == 0) { /* not link local address */ return (0);
} /* hunt through our destination nets list for this scope_id */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (((struct sockaddr *)(&net->ro._l_addr))->sa_family !=
AF_INET6) continue;
net6 = (struct sockaddr_in6 *)&net->ro._l_addr; if (IN6_IS_ADDR_LINKLOCAL(&net6->sin6_addr) == 0) continue; if (sctp_is_same_scope(sin6, net6)) { /* found one */ return (1);
}
} /* didn't find one */ return (0);
} #endif
/* *addressmanagementfunctions
*/ staticvoid
sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_ifa *ifa, uint16_t type, int addr_locked)
{ int status;
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0 ||
sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { /* subset bound, no ASCONF allowed case, so ignore */ return;
} /* *note:weknowthisisnotthesubsetbound,noASCONFcaseeg. *thisisboundallorsubsetboundw/ASCONFallowed
*/
/* first, make sure that the address is IPv4 or IPv6 and not jailed */ switch (ifa->address.sa.sa_family) { #ifdef INET6 case AF_INET6: #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
&ifa->address.sin6.sin6_addr) != 0) { return;
} #endif break; #endif #ifdef INET case AF_INET: #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
&ifa->address.sin.sin_addr) != 0) { return;
} #endif break; #endif default: return;
} #ifdef INET6 /* make sure we're "allowed" to add this type of addr */ if (ifa->address.sa.sa_family == AF_INET6) { /* invalid if we're not a v6 endpoint */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) return; /* is the v6 addr really valid ? */ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { return;
}
} #endif /* put this address on the "pending/do not use yet" list */
sctp_add_local_addr_restricted(stcb, ifa); /* *checkaddressscopeifaddressisoutofscope,don'tqueue *anything...note:thiswouldleavetheaddressonbothinpand *asoclists
*/ switch (ifa->address.sa.sa_family) { #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6;
sin6 = &ifa->address.sin6; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* we skip unspecified addresses */ return;
} if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { if (stcb->asoc.scope.local_scope == 0) { return;
} /* is it the right link local scope? */ if (sctp_is_scopeid_in_nets(stcb, &ifa->address.sa) == 0) { return;
}
} if (stcb->asoc.scope.site_scope == 0 &&
IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { return;
} break;
} #endif #ifdef INET case AF_INET:
{ struct sockaddr_in *sin;
/* invalid if we are a v6 only endpoint */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) return;
sin = &ifa->address.sin; if (sin->sin_addr.s_addr == 0) { /* we skip unspecified addresses */ return;
} if (stcb->asoc.scope.ipv4_local_scope == 0 &&
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) { return;
} break;
} #endif default: /* else, not AF_INET or AF_INET6, so skip */ return;
}
/* queue an asconf for this address add/delete */ if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { /* does the peer do asconf? */ if (stcb->asoc.asconf_supported) { /* queue an asconf for this addr */
status = sctp_asconf_queue_add(stcb, ifa, type);
/* Only for specific case not bound all */
asc = (struct sctp_asconf_iterator *)ptr;
LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) {
ifa = l->ifa; if (l->action == SCTP_ADD_IP_ADDRESS) {
LIST_FOREACH(laddr, &inp->sctp_addr_list,
sctp_nxt_addr) { if (laddr->ifa == ifa) {
laddr->action = 0; break;
}
}
} elseif (l->action == SCTP_DEL_IP_ADDRESS) {
LIST_FOREACH_SAFE(laddr, &inp->sctp_addr_list, sctp_nxt_addr, nladdr) { /* remove only after all guys are done */ if (laddr->ifa == ifa) {
sctp_del_local_addr_ep(inp, ifa);
}
}
}
} return (0);
}
void
sctp_asconf_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, uint32_t val SCTP_UNUSED)
{ struct sctp_asconf_iterator *asc; struct sctp_ifa *ifa; struct sctp_laddr *l; int cnt_invalid = 0; int type, status; int num_queued = 0;
asc = (struct sctp_asconf_iterator *)ptr;
LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) {
ifa = l->ifa;
type = l->action;
/* address's vrf_id must be the vrf_id of the assoc */ if (ifa->vrf_id != stcb->asoc.vrf_id) { continue;
}
/* Same checks again for assoc */ switch (ifa->address.sa.sa_family) { #ifdef INET6 case AF_INET6:
{ /* invalid if we're not a v6 endpoint */ struct sockaddr_in6 *sin6;
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
cnt_invalid++; if (asc->cnt == cnt_invalid) return; else continue;
}
sin6 = &ifa->address.sin6; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* we skip unspecified addresses */ continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) { continue;
} #endif if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { if (stcb->asoc.scope.local_scope == 0) { continue;
} /* is it the right link local scope? */ if (sctp_is_scopeid_in_nets(stcb, &ifa->address.sa) == 0) { continue;
}
} break;
} #endif #ifdef INET case AF_INET:
{ /* invalid if we are a v6 only endpoint */ struct sockaddr_in *sin;
/* invalid if we are a v6 only endpoint */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) continue;
sin = &ifa->address.sin; if (sin->sin_addr.s_addr == 0) { /* we skip unspecified addresses */ continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) { continue;
} #endif if (stcb->asoc.scope.ipv4_local_scope == 0 &&
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) { continue;
} if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) {
cnt_invalid++; if (asc->cnt == cnt_invalid) return; else continue;
} break;
} #endif default: /* invalid address family */
cnt_invalid++; if (asc->cnt == cnt_invalid) return; else continue;
}
if (type == SCTP_ADD_IP_ADDRESS) { /* prevent this address from being used as a source */
sctp_add_local_addr_restricted(stcb, ifa);
} elseif (type == SCTP_DEL_IP_ADDRESS) { struct sctp_nets *net;
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { /* delete this address if cached */ if (net->ro._s_addr == ifa) {
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
net->src_addr_selected = 0; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
RO_NHFREE(&net->ro); #else if (net->ro.ro_rt) {
RTFREE(net->ro.ro_rt);
net->ro.ro_rt = NULL;
} #endif /* *Nowwedeletedoursrcaddress, *shouldwenotalsonowresetthe *cwnd/rtotostartasifitsanew *address?
*/
stcb->asoc.cc_functions.sctp_set_initial_cc_param(stcb, net);
net->RTO = 0;
}
}
} elseif (type == SCTP_SET_PRIM_ADDR) { if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { /* must validate the ifa is in the ep */ if (sctp_is_addr_in_ep(stcb->sctp_ep, ifa) == 0) { continue;
}
} else { /* Need to check scopes for this guy */ if (sctp_is_address_in_scope(ifa, &stcb->asoc.scope, 0) == 0) { continue;
}
}
} /* queue an asconf for this address add/delete */ if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF) &&
stcb->asoc.asconf_supported == 1) { /* queue an asconf for this addr */
status = sctp_asconf_queue_add(stcb, ifa, type); /* *ifqueuedok,andintheopenstate,updatethe *countofqueuedparams.Ifinthenon-openstate, *thesegetsentwhentheassocgoesopen.
*/ if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) { if (status >= 0) {
num_queued++;
}
}
}
} /* *Ifwehavequeuedparamsintheopenstate,sendoutanASCONF.
*/ if (num_queued > 0) {
sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED);
}
}
/* find the ifa for the desired set primary */
vrf_id = stcb->asoc.vrf_id;
ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED); if (ifa == NULL) { /* Invalid address */ return (-1);
}
/* queue an ASCONF:SET_PRIM_ADDR to be sent */ if (!sctp_asconf_queue_add(stcb, ifa, SCTP_SET_PRIM_ADDR)) { /* set primary queuing succeeded */
SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address_sa: queued on tcb=%p, ",
(void *)stcb);
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa); if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) { #ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination); #else
sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED); #endif
}
} else {
SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address_sa: failed to add to queue on tcb=%p, ",
(void *)stcb);
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa); return (-1);
} return (0);
}
offset += SCTP_SIZE32(param_length); if (offset >= asconf_limit) { /* no more data in the mbuf chain */ break;
} /* get pointer to next asconf param */
aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(chk->data, offset, sizeof(struct sctp_asconf_paramhdr), aparam_buf);
}
}
/* we want to find the sequences which consist of ADD -> DEL -> ADD or DEL -> ADD */ if (add_cnt > del_cnt ||
(add_cnt == del_cnt && last_param_type == SCTP_ADD_IP_ADDRESS)) { return (1);
} return (0);
}
if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RLOCK();
vrf = sctp_find_vrf(stcb->asoc.vrf_id); if (vrf == NULL) { if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RUNLOCK(); return (NULL);
}
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if (stcb->asoc.scope.loopback_scope == 0 &&
SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* Skip if loopback_scope not set */ continue;
}
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { switch (sctp_ifa->address.sa.sa_family) { #ifdef INET case AF_INET: if (stcb->asoc.scope.ipv4_addr_legal) { struct sockaddr_in *sin;
sin = &sctp_ifa->address.sin; if (sin->sin_addr.s_addr == 0) { /* skip unspecified addresses */ continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) { continue;
} #endif if (stcb->asoc.scope.ipv4_local_scope == 0 &&
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) continue;
if (sctp_is_addr_restricted(stcb, sctp_ifa) &&
(!sctp_is_addr_pending(stcb, sctp_ifa))) continue; /* found a valid local v4 address to use */ if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RUNLOCK(); return (&sctp_ifa->address.sa);
} break; #endif #ifdef INET6 case AF_INET6: if (stcb->asoc.scope.ipv6_addr_legal) { struct sockaddr_in6 *sin6;
if (sctp_ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { continue;
}
sin6 = &sctp_ifa->address.sin6; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* we skip unspecified addresses */ continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) { continue;
} #endif if (stcb->asoc.scope.local_scope == 0 &&
IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) continue; if (stcb->asoc.scope.site_scope == 0 &&
IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) continue;
if (sctp_is_addr_restricted(stcb, sctp_ifa) &&
(!sctp_is_addr_pending(stcb, sctp_ifa))) continue; /* found a valid local v6 address to use */ if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RUNLOCK(); return (&sctp_ifa->address.sa);
} break; #endif default: break;
}
}
} /* no valid addresses found */ if (addr_locked == SCTP_ADDR_NOT_LOCKED)
SCTP_IPI_ADDR_RUNLOCK(); return (NULL);
}
/* are there any asconf params to send? */
TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) { if (aa->sent == 0) break;
} if (aa == NULL) return (NULL);
/* Consider IP header and SCTP common header. */ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
overhead = SCTP_MIN_OVERHEAD;
} else {
overhead = SCTP_MIN_V4_OVERHEAD;
} /* Consider ASONF chunk. */
overhead += sizeof(struct sctp_asconf_chunk); /* Consider AUTH chunk. */
overhead += sctp_get_auth_chunk_len(stcb->asoc.peer_hmac_id); if (stcb->asoc.smallest_mtu <= overhead) { /* MTU too small. */ return (NULL);
} /* *getachunkheadermbufandaclusterfortheasconfparamssince *it'ssimplertofillintheasconfchunkheaderlookupaddresson *thefly
*/
m_asconf_chk = sctp_get_mbuf_for_msg(sizeof(struct sctp_asconf_chunk), 0, M_NOWAIT, 1, MT_DATA); if (m_asconf_chk == NULL) { /* no mbuf's */
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_compose_asconf: couldn't get chunk mbuf!\n"); return (NULL);
}
m_asconf = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_DATA); if (m_asconf == NULL) { /* no mbuf's */
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_compose_asconf: couldn't get mbuf!\n");
sctp_m_freem(m_asconf_chk); return (NULL);
}
SCTP_BUF_LEN(m_asconf_chk) = sizeof(struct sctp_asconf_chunk);
SCTP_BUF_LEN(m_asconf) = 0;
acp = mtod(m_asconf_chk, struct sctp_asconf_chunk *);
memset(acp, 0, sizeof(struct sctp_asconf_chunk)); /* save pointers to lookup address and asconf params */
lookup_ptr = (caddr_t)(acp + 1); /* after the header */
ptr = mtod(m_asconf, caddr_t); /* beginning of cluster */
/* fill in chunk header info */
acp->ch.chunk_type = SCTP_ASCONF;
acp->ch.chunk_flags = 0;
acp->serial_number = htonl(stcb->asoc.asconf_seq_out);
stcb->asoc.asconf_seq_out++;
/* add parameters... up to smallest MTU allowed */
TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) { if (aa->sent) continue; /* get the parameter length */
p_length = SCTP_SIZE32(aa->ap.aph.ph.param_length); /* will it fit in current chunk? */ if ((SCTP_BUF_LEN(m_asconf) + p_length > stcb->asoc.smallest_mtu - overhead) ||
(SCTP_BUF_LEN(m_asconf) + p_length > MCLBYTES)) { /* won't fit, so we're done with this chunk */ break;
} /* assign (and store) a correlation id */
aa->ap.aph.correlation_id = correlation_id++;
/* *theseparamsareremovedoffthependinglistupon *gettinganASCONF-ACKbackfromthepeer,justsetflag
*/
aa->sent = 1;
} /* check to see if the lookup addr has been populated yet */ if (lookup_used == 0) { /* NOTE: if the address param is optional, can skip this... */ /* add any valid (existing) address... */ struct sctp_ipv6addr_param *lookup;
uint16_t p_size, addr_size; struct sockaddr *found_addr;
caddr_t addr_ptr;
/* go through the endpoint list */
LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) { /* be paranoid and validate the laddr */ if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "check_addr_list_ep: laddr->ifa is NULL"); continue;
} /* do i have it implicitly? */ if (sctp_cmpaddr(&laddr->ifa->address.sa, init_addr)) { continue;
} /* check to see if in the init-ack */ if (!sctp_addr_in_initack(m, offset, length, &laddr->ifa->address.sa)) { /* try to add it */
sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb, laddr->ifa,
SCTP_ADD_IP_ADDRESS, SCTP_ADDR_NOT_LOCKED);
}
}
}
if (stcb) {
vrf_id = stcb->asoc.vrf_id;
} else { return;
}
SCTP_IPI_ADDR_RLOCK();
vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) {
SCTP_IPI_ADDR_RUNLOCK(); return;
} /* go through all our known interfaces */
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if (loopback_scope == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* skip loopback interface */ continue;
} /* go through each interface address */
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { /* do i have it implicitly? */ if (sctp_cmpaddr(&sctp_ifa->address.sa, init_addr)) { continue;
} switch (sctp_ifa->address.sa.sa_family) { #ifdef INET case AF_INET:
sin = &sctp_ifa->address.sin; #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) { continue;
} #endif if ((ipv4_scope == 0) &&
(IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) { /* private address not in scope */ continue;
} break; #endif #ifdef INET6 case AF_INET6:
sin6 = &sctp_ifa->address.sin6; #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) { continue;
} #endif if ((local_scope == 0) &&
(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) { continue;
} if ((site_scope == 0) &&
(IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) { continue;
} break; #endif default: break;
} /* check to see if in the init-ack */ if (!sctp_addr_in_initack(m, offset, length, &sctp_ifa->address.sa)) { /* try to add it */
sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb,
sctp_ifa, SCTP_ADD_IP_ADDRESS,
SCTP_ADDR_LOCKED);
}
} /* end foreach ifa */
} /* end foreach ifn */
SCTP_IPI_ADDR_RUNLOCK();
}
/* *validatesaninit-ackchunk(fromacookie-echo)withcurrentaddresses *addsaddressesfromtheinit-ackintoourlocaladdresslist,ifneeded *queuesasconfadds/deletesaddressesasneededandmakesappropriatelist *changesforsourceaddressselectionm,offset:pointstothestartofthe *addresslistinaninit-ackchunklength:totallengthoftheaddress *paramsonlyinit_addr:addresswheremyINIT-ACKwassentfrom
*/ void
sctp_check_address_list(struct sctp_tcb *stcb, struct mbuf *m, int offset, int length, struct sockaddr *init_addr,
uint16_t local_scope, uint16_t site_scope,
uint16_t ipv4_scope, uint16_t loopback_scope)
{ /* process the local addresses in the initack */
sctp_process_initack_addresses(stcb, m, offset, length);
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* bound all case */
sctp_check_address_list_all(stcb, m, offset, length, init_addr,
local_scope, site_scope, ipv4_scope, loopback_scope);
} else { /* subset bound case */ if (sctp_is_feature_on(stcb->sctp_ep,
SCTP_PCB_FLAGS_DO_ASCONF)) { /* asconf's allowed */
sctp_check_address_list_ep(stcb, m, offset, length,
init_addr);
} /* else, no asconfs allowed, so what we sent is what we get */
}
}
#ifdef HAVE_SA_LEN if (sa->sa_len == 0) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_ASCONF, EINVAL); return (EINVAL);
} #endif if (type == SCTP_ADD_IP_ADDRESS) { /* For an add the address MUST be on the system */
ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED);
} elseif (type == SCTP_DEL_IP_ADDRESS) { /* For a delete we need to find it in the inp */
ifa = sctp_find_ifa_in_ep(inp, sa, SCTP_ADDR_NOT_LOCKED);
} else {
ifa = NULL;
} if (ifa != NULL) { if (type == SCTP_ADD_IP_ADDRESS) {
sctp_add_local_addr_ep(inp, ifa, type);
} elseif (type == SCTP_DEL_IP_ADDRESS) { if (inp->laddr_count < 2) { /* can't delete the last local address */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_ASCONF, EINVAL); return (EINVAL);
}
LIST_FOREACH(laddr, &inp->sctp_addr_list,
sctp_nxt_addr) { if (ifa == laddr->ifa) { /* Mark in the delete */
laddr->action = type;
}
}
} if (LIST_EMPTY(&inp->sctp_asoc_list)) { /* *Thereisnoneedtostarttheiteratorif *theinphasnoassociations.
*/ if (type == SCTP_DEL_IP_ADDRESS) {
LIST_FOREACH_SAFE(laddr, &inp->sctp_addr_list, sctp_nxt_addr, nladdr) { if (laddr->ifa == ifa) {
sctp_del_local_addr_ep(inp, ifa);
}
}
}
} else { struct sctp_asconf_iterator *asc; struct sctp_laddr *wi; int ret;
if (net == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_send_nat_state_update: Missing net\n");
return;
}
if (stcb == NULL) {
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_send_nat_state_update: Missing stcb\n");
return;
}
/* Need to have in the ASCONF:
* - VTAG(my_vtag/peer_vtag)
* - ADD(wildcard)
* - DEL(wildcard)
* - ADD(Any global addresses)
*/
SCTP_MALLOC(aa_vtag, struct sctp_asconf_addr *, sizeof(struct sctp_asconf_addr), SCTP_M_ASC_ADDR);
SCTP_MALLOC(aa_add, struct sctp_asconf_addr *, sizeof(struct sctp_asconf_addr), SCTP_M_ASC_ADDR);
SCTP_MALLOC(aa_del, struct sctp_asconf_addr *, sizeof(struct sctp_asconf_addr), SCTP_M_ASC_ADDR);
if ((aa_vtag == NULL) || (aa_add == NULL) || (aa_del == NULL)) {
/* Didn't get memory */
SCTPDBG(SCTP_DEBUG_ASCONF1, "sctp_asconf_send_nat_state_update: failed to get memory!\n");
out:
if (aa_vtag != NULL) {
SCTP_FREE(aa_vtag, SCTP_M_ASC_ADDR);
}
if (aa_add != NULL) {
SCTP_FREE(aa_add, SCTP_M_ASC_ADDR);
}
if (aa_del != NULL) {
SCTP_FREE(aa_del, SCTP_M_ASC_ADDR);
}
return;
}
memset(aa_vtag, 0, sizeof(struct sctp_asconf_addr));
aa_vtag->special_del = 0;
/* Fill in ASCONF address parameter fields. */
/* Top level elements are "networked" during send. */
aa_vtag->ifa = NULL;
aa_vtag->sent = 0; /* clear sent flag */
vtag = (struct sctp_asconf_tag_param *)&aa_vtag->ap.aph;
vtag->aph.ph.param_type = SCTP_NAT_VTAGS;
vtag->aph.ph.param_length = sizeof(struct sctp_asconf_tag_param);
vtag->local_vtag = htonl(stcb->asoc.my_vtag);
vtag->remote_vtag = htonl(stcb->asoc.peer_vtag);
memset(aa_add, 0, sizeof(struct sctp_asconf_addr));
memset(aa_del, 0, sizeof(struct sctp_asconf_addr));
switch (net->ro._l_addr.sa.sa_family) {
#ifdef INET
case AF_INET:
aa_add->ap.aph.ph.param_type = SCTP_ADD_IP_ADDRESS;
aa_add->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addrv4_param);
aa_add->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
aa_add->ap.addrp.ph.param_length = sizeof (struct sctp_ipv4addr_param);
/* No need to fill the address, we are using 0.0.0.0 */
aa_del->ap.aph.ph.param_type = SCTP_DEL_IP_ADDRESS;
aa_del->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addrv4_param);
aa_del->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
aa_del->ap.addrp.ph.param_length = sizeof (struct sctp_ipv4addr_param);
/* No need to fill the address, we are using 0.0.0.0 */
break;
#endif
#ifdef INET6
case AF_INET6:
aa_add->ap.aph.ph.param_type = SCTP_ADD_IP_ADDRESS;
aa_add->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addr_param);
aa_add->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
aa_add->ap.addrp.ph.param_length = sizeof (struct sctp_ipv6addr_param);
/* No need to fill the address, we are using ::0 */
aa_del->ap.aph.ph.param_type = SCTP_DEL_IP_ADDRESS;
aa_del->ap.aph.ph.param_length = sizeof(struct sctp_asconf_addr_param);
aa_del->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
aa_del->ap.addrp.ph.param_length = sizeof (struct sctp_ipv6addr_param);
/* No need to fill the address, we are using ::0 */
break;
#endif
default:
SCTPDBG(SCTP_DEBUG_ASCONF1,
"sctp_asconf_send_nat_state_update: unknown address family %d\n",
net->ro._l_addr.sa.sa_family);
goto out;
}
TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa_vtag, next);
TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa_add, next);
TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa_del, next);
/* Now we must hunt the addresses and add all global addresses */
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
struct sctp_vrf *vrf = NULL;
struct sctp_ifn *sctp_ifnp;
uint32_t vrf_id;
LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) {
if (laddr->ifa == NULL) {
continue;
}
if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED)
/* Address being deleted by the system, dont
* list.
*/
continue;
if (laddr->action == SCTP_DEL_IP_ADDRESS) {
/* Address being deleted on this ep
* don't list.
*/
continue;
}
sctp_ifap = laddr->ifa;
switch (sctp_ifap->address.sa.sa_family) {
#ifdef INET
case AF_INET:
to = &sctp_ifap->address.sin;
if (IN4_ISPRIVATE_ADDRESS(&to->sin_addr)) {
continue;
}
if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
continue;
}
break;
#endif
#ifdef INET6
case AF_INET6:
to6 = &sctp_ifap->address.sin6;
if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) {
continue;
}
if (IN6_IS_ADDR_LINKLOCAL(&to6->sin6_addr)) {
continue;
}
break;
#endif
default:
continue;
}
sctp_asconf_queue_mgmt(stcb, sctp_ifap, SCTP_ADD_IP_ADDRESS);
}
}
skip_rest:
/* Now we must send the asconf into the queue */
sctp_send_asconf(stcb, net, SCTP_ADDR_NOT_LOCKED);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.84 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.