summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/sctp_pcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/sctp_pcb.c')
-rw-r--r--freebsd/sys/netinet/sctp_pcb.c229
1 files changed, 141 insertions, 88 deletions
diff --git a/freebsd/sys/netinet/sctp_pcb.c b/freebsd/sys/netinet/sctp_pcb.c
index e21c2e03..16dc231f 100644
--- a/freebsd/sys/netinet/sctp_pcb.c
+++ b/freebsd/sys/netinet/sctp_pcb.c
@@ -774,7 +774,14 @@ sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr,
}
SCTPDBG(SCTP_DEBUG_PCB4, "Deleting ifa %p\n", (void *)sctp_ifap);
sctp_ifap->localifa_flags &= SCTP_ADDR_VALID;
- sctp_ifap->localifa_flags |= SCTP_BEING_DELETED;
+ /*
+ * We don't set the flag. This means that the structure will
+ * hang around in EP's that have bound specific to it until
+ * they close. This gives us TCP like behavior if someone
+ * removes an address (or for that matter adds it right
+ * back).
+ */
+ /* sctp_ifap->localifa_flags |= SCTP_BEING_DELETED; */
vrf->total_ifa_count--;
LIST_REMOVE(sctp_ifap, next_bucket);
sctp_remove_ifa_from_ifn(sctp_ifap);
@@ -829,18 +836,30 @@ out_now:
static int
sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
{
- int loopback_scope, ipv4_local_scope, local_scope, site_scope;
- int ipv4_addr_legal, ipv6_addr_legal;
+ int loopback_scope;
+
+#if defined(INET)
+ int ipv4_local_scope, ipv4_addr_legal;
+
+#endif
+#if defined(INET6)
+ int local_scope, site_scope, ipv6_addr_legal;
+
+#endif
struct sctp_vrf *vrf;
struct sctp_ifn *sctp_ifn;
struct sctp_ifa *sctp_ifa;
loopback_scope = stcb->asoc.scope.loopback_scope;
+#if defined(INET)
ipv4_local_scope = stcb->asoc.scope.ipv4_local_scope;
+ ipv4_addr_legal = stcb->asoc.scope.ipv4_addr_legal;
+#endif
+#if defined(INET6)
local_scope = stcb->asoc.scope.local_scope;
site_scope = stcb->asoc.scope.site_scope;
- ipv4_addr_legal = stcb->asoc.scope.ipv4_addr_legal;
ipv6_addr_legal = stcb->asoc.scope.ipv6_addr_legal;
+#endif
SCTP_IPI_ADDR_RLOCK();
vrf = sctp_find_vrf(stcb->asoc.vrf_id);
@@ -865,6 +884,9 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
*/
continue;
}
+ if (sctp_ifa->address.sa.sa_family != to->sa_family) {
+ continue;
+ }
switch (sctp_ifa->address.sa.sa_family) {
#ifdef INET
case AF_INET:
@@ -878,6 +900,10 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
continue;
}
+ if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
+ &sin->sin_addr) != 0) {
+ continue;
+ }
if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
SCTP_IPI_ADDR_RUNLOCK();
return (1);
@@ -893,6 +919,10 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
sin6 = &sctp_ifa->address.sin6;
rsin6 = (struct sockaddr_in6 *)to;
+ if (prison_check_ip6(stcb->sctp_ep->ip_inp.inp.inp_cred,
+ &sin6->sin6_addr) != 0) {
+ continue;
+ }
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
if (local_scope == 0)
continue;
@@ -1040,6 +1070,39 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
SCTP_INP_RUNLOCK(inp);
continue;
}
+ switch (to->sa_family) {
+#ifdef INET
+ case AF_INET:
+ {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)to;
+ if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
+ &sin->sin_addr) != 0) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ break;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)to;
+ if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
+ &sin6->sin6_addr) != 0) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ break;
+ }
+#endif
+ default:
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
if (inp->def_vrf_id != vrf_id) {
SCTP_INP_RUNLOCK(inp);
continue;
@@ -1608,23 +1671,45 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) &&
(inp->sctp_lport == lport)) {
/* got it */
+ switch (nam->sa_family) {
#ifdef INET
- if ((nam->sa_family == AF_INET) &&
- (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
- SCTP_IPV6_V6ONLY(inp)) {
- /* IPv4 on a IPv6 socket with ONLY IPv6 set */
- SCTP_INP_RUNLOCK(inp);
- continue;
- }
+ case AF_INET:
+ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+ SCTP_IPV6_V6ONLY(inp)) {
+ /*
+ * IPv4 on a IPv6 socket with ONLY
+ * IPv6 set
+ */
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
+ &sin->sin_addr) != 0) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ break;
#endif
#ifdef INET6
- /* A V6 address and the endpoint is NOT bound V6 */
- if (nam->sa_family == AF_INET6 &&
- (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
- SCTP_INP_RUNLOCK(inp);
- continue;
- }
+ case AF_INET6:
+ /*
+ * A V6 address and the endpoint is NOT
+ * bound V6
+ */
+ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
+ &sin6->sin6_addr) != 0) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
+ break;
#endif
+ default:
+ break;
+ }
/* does a VRF id match? */
fnd = 0;
if (inp->def_vrf_id == vrf_id)
@@ -1973,8 +2058,13 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset,
struct sockaddr *dst)
{
struct sctp_paramhdr *phdr, parm_buf;
+
+#if defined(INET) || defined(INET6)
struct sctp_tcb *stcb;
- uint32_t ptype, plen;
+ uint16_t ptype;
+
+#endif
+ uint16_t plen;
#ifdef INET
struct sockaddr_in sin4;
@@ -1998,13 +2088,14 @@ sctp_findassociation_special_addr(struct mbuf *m, int offset,
sin6.sin6_port = sh->src_port;
#endif
- stcb = NULL;
offset += sizeof(struct sctp_init_chunk);
phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf));
while (phdr != NULL) {
/* now we must see if we want the parameter */
+#if defined(INET) || defined(INET6)
ptype = ntohs(phdr->param_type);
+#endif
plen = ntohs(phdr->param_length);
if (plen == 0) {
break;
@@ -2377,6 +2468,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
/* setup socket pointers */
inp->sctp_socket = so;
inp->ip_inp.inp.inp_socket = so;
+ inp->ip_inp.inp.inp_cred = crhold(so->so_cred);
#ifdef INET6
if (INP_SOCKAF(so) == AF_INET6) {
if (MODULE_GLOBAL(ip6_auto_flowlabel)) {
@@ -2395,6 +2487,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
/* init the small hash table we use to track asocid <-> tcb */
inp->sctp_asocidhash = SCTP_HASH_INIT(SCTP_STACK_VTAG_HASH_SIZE, &inp->hashasocidmark);
if (inp->sctp_asocidhash == NULL) {
+ crfree(inp->ip_inp.inp.inp_cred);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
SCTP_INP_INFO_WUNLOCK();
return (ENOBUFS);
@@ -2409,6 +2502,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
((struct in6pcb *)(&inp->ip_inp.inp))->in6p_sp = pcb_sp;
}
if (error != 0) {
+ crfree(inp->ip_inp.inp.inp_cred);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
SCTP_INP_INFO_WUNLOCK();
return error;
@@ -2439,6 +2533,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
*/
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EOPNOTSUPP);
so->so_pcb = NULL;
+ crfree(inp->ip_inp.inp.inp_cred);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
return (EOPNOTSUPP);
}
@@ -2458,6 +2553,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
SCTP_PRINTF("Out of SCTP-INPCB->hashinit - no resources\n");
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS);
so->so_pcb = NULL;
+ crfree(inp->ip_inp.inp.inp_cred);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
return (ENOBUFS);
}
@@ -2709,7 +2805,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
uint32_t vrf_id;
lport = 0;
- error = 0;
bindall = 1;
inp = (struct sctp_inpcb *)so->so_pcb;
ip_inp = (struct inpcb *)so->so_pcb;
@@ -2830,13 +2925,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
return (error);
}
}
- if (p == NULL) {
- SCTP_INP_DECR_REF(inp);
- SCTP_INP_WUNLOCK(inp);
- SCTP_INP_INFO_WUNLOCK();
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error);
- return (error);
- }
SCTP_INP_WUNLOCK(inp);
if (bindall) {
vrf_id = inp->def_vrf_id;
@@ -3314,17 +3402,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
/* Left with Data unread */
struct mbuf *op_err;
- op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr),
- 0, M_DONTWAIT, 1, MT_DATA);
- if (op_err) {
- /* Fill in the user initiated abort */
- struct sctp_paramhdr *ph;
-
- SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr);
- ph = mtod(op_err, struct sctp_paramhdr *);
- ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
- ph->param_length = htons(SCTP_BUF_LEN(op_err));
- }
+ op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_3;
sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED);
SCTP_STAT_INCR_COUNTER32(sctps_aborted);
@@ -3395,20 +3473,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
struct mbuf *op_err;
abort_anyway:
- op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr),
- 0, M_DONTWAIT, 1, MT_DATA);
- if (op_err) {
- /*
- * Fill in the user
- * initiated abort
- */
- struct sctp_paramhdr *ph;
-
- SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr);
- ph = mtod(op_err, struct sctp_paramhdr *);
- ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
- ph->param_length = htons(SCTP_BUF_LEN(op_err));
- }
+ op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_5;
sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED);
SCTP_STAT_INCR_COUNTER32(sctps_aborted);
@@ -3472,17 +3537,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
((asoc->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0)) {
struct mbuf *op_err;
- op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr),
- 0, M_DONTWAIT, 1, MT_DATA);
- if (op_err) {
- /* Fill in the user initiated abort */
- struct sctp_paramhdr *ph;
-
- SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr);
- ph = mtod(op_err, struct sctp_paramhdr *);
- ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
- ph->param_length = htons(SCTP_BUF_LEN(op_err));
- }
+ op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
asoc->sctp_ep->last_abort_code = SCTP_FROM_SCTP_PCB + SCTP_LOC_7;
sctp_send_abort_tcb(asoc, op_err, SCTP_SO_LOCKED);
SCTP_STAT_INCR_COUNTER32(sctps_aborted);
@@ -3647,6 +3702,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
inp->sctp_tcbhash = NULL;
}
/* Now we must put the ep memory back into the zone pool */
+ crfree(inp->ip_inp.inp.inp_cred);
INP_LOCK_DESTROY(&inp->ip_inp.inp);
SCTP_INP_LOCK_DESTROY(inp);
SCTP_INP_READ_DESTROY(inp);
@@ -3744,7 +3800,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
sin->sin_len = sizeof(struct sockaddr_in);
if (set_scope) {
#ifdef SCTP_DONT_DO_PRIVADDR_SCOPE
- stcb->ipv4_local_scope = 1;
+ stcb->asoc.scope.ipv4_local_scope = 1;
#else
if (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
stcb->asoc.scope.ipv4_local_scope = 1;
@@ -4318,6 +4374,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
asoc->nr_mapping_array = NULL;
}
SCTP_DECR_ASOC_COUNT();
+ SCTP_TCB_UNLOCK(stcb);
SCTP_TCB_LOCK_DESTROY(stcb);
SCTP_TCB_SEND_LOCK_DESTROY(stcb);
LIST_REMOVE(stcb, sctp_tcbasocidhash);
@@ -5120,6 +5177,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
/* Insert new items here :> */
/* Get rid of LOCK */
+ SCTP_TCB_UNLOCK(stcb);
SCTP_TCB_LOCK_DESTROY(stcb);
SCTP_TCB_SEND_LOCK_DESTROY(stcb);
if (from_inpcbfree == SCTP_NORMAL_PROC) {
@@ -5845,7 +5903,6 @@ sctp_pcb_init()
for (i = 0; i < SCTP_STACK_VTAG_HASH_SIZE; i++) {
LIST_INIT(&SCTP_BASE_INFO(vtag_timewait)[i]);
}
-
sctp_startup_iterator();
#if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP)
@@ -5874,35 +5931,31 @@ sctp_pcb_finish(void)
struct sctp_tagblock *twait_block, *prev_twait_block;
struct sctp_laddr *wi, *nwi;
int i;
+ struct sctp_iterator *it, *nit;
/*
- * Free BSD the it thread never exits but we do clean up. The only
- * way freebsd reaches here if we have VRF's but we still add the
- * ifdef to make it compile on old versions.
+ * In FreeBSD the iterator thread never exits but we do clean up.
+ * The only way FreeBSD reaches here is if we have VRF's but we
+ * still add the ifdef to make it compile on old versions.
*/
- {
- struct sctp_iterator *it, *nit;
-
- SCTP_IPI_ITERATOR_WQ_LOCK();
- TAILQ_FOREACH_SAFE(it, &sctp_it_ctl.iteratorhead, sctp_nxt_itr, nit) {
- if (it->vn != curvnet) {
- continue;
- }
- TAILQ_REMOVE(&sctp_it_ctl.iteratorhead, it, sctp_nxt_itr);
- if (it->function_atend != NULL) {
- (*it->function_atend) (it->pointer, it->val);
- }
- SCTP_FREE(it, SCTP_M_ITER);
+ SCTP_IPI_ITERATOR_WQ_LOCK();
+ TAILQ_FOREACH_SAFE(it, &sctp_it_ctl.iteratorhead, sctp_nxt_itr, nit) {
+ if (it->vn != curvnet) {
+ continue;
}
- SCTP_IPI_ITERATOR_WQ_UNLOCK();
- SCTP_ITERATOR_LOCK();
- if ((sctp_it_ctl.cur_it) &&
- (sctp_it_ctl.cur_it->vn == curvnet)) {
- sctp_it_ctl.iterator_flags |= SCTP_ITERATOR_STOP_CUR_IT;
+ TAILQ_REMOVE(&sctp_it_ctl.iteratorhead, it, sctp_nxt_itr);
+ if (it->function_atend != NULL) {
+ (*it->function_atend) (it->pointer, it->val);
}
- SCTP_ITERATOR_UNLOCK();
+ SCTP_FREE(it, SCTP_M_ITER);
}
-
+ SCTP_IPI_ITERATOR_WQ_UNLOCK();
+ SCTP_ITERATOR_LOCK();
+ if ((sctp_it_ctl.cur_it) &&
+ (sctp_it_ctl.cur_it->vn == curvnet)) {
+ sctp_it_ctl.iterator_flags |= SCTP_ITERATOR_STOP_CUR_IT;
+ }
+ SCTP_ITERATOR_UNLOCK();
SCTP_OS_TIMER_STOP(&SCTP_BASE_INFO(addr_wq_timer.timer));
SCTP_WQ_ADDR_LOCK();
LIST_FOREACH_SAFE(wi, &SCTP_BASE_INFO(addr_wq), sctp_nxt_addr, nwi) {