summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_pcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_pcb.c')
-rw-r--r--sys/netinet/sctp_pcb.c965
1 files changed, 577 insertions, 388 deletions
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index e68c2b8..82e6e24 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_os.h>
#include <sys/proc.h>
#include <netinet/sctp_var.h>
+#include <netinet/sctp_sysctl.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctputil.h>
#include <netinet/sctp.h>
@@ -43,18 +44,9 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_timer.h>
+#include <netinet/sctp_bsd_addr.h>
-#ifdef SCTP_DEBUG
-uint32_t sctp_debug_on = 0;
-
-#endif /* SCTP_DEBUG */
-
-
-extern int sctp_pcbtblsize;
-extern int sctp_hashtblsize;
-extern int sctp_chunkscale;
-
struct sctp_epinfo sctppcbinfo;
/* FIX: we don't handle multiple link local scopes */
@@ -72,7 +64,6 @@ SCTP6_ARE_ADDR_EQUAL(struct in6_addr *a, struct in6_addr *b)
return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b));
}
-
void
sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb)
{
@@ -93,6 +84,298 @@ sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb)
SCTP_INP_INFO_RUNLOCK();
}
+/*
+ * Addresses are added to VRF's (Virtual Router's). For BSD we
+ * have only the default VRF 0. We maintain a hash list of
+ * VRF's. Each VRF has its own list of sctp_ifn's. Each of
+ * these has a list of addresses. When we add a new address
+ * to a VRF we lookup the ifn/ifn_index, if the ifn does
+ * not exist we create it and add it to the list of IFN's
+ * within the VRF. Once we have the sctp_ifn, we add the
+ * address to the list. So we look something like:
+ *
+ * hash-vrf-table
+ * vrf-> ifn-> ifn -> ifn
+ * vrf |
+ * ... +--ifa-> ifa -> ifa
+ * vrf
+ *
+ * We keep these seperate lists since the SCTP subsystem will
+ * point to these from its source address selection nets structure.
+ * When an address is deleted it does not happen right away on
+ * the SCTP side, it gets scheduled. What we do when a
+ * delete happens is immediately remove the address from
+ * the master list and decrement the refcount. As our
+ * addip iterator works through and frees the src address
+ * selection pointing to the sctp_ifa, eventually the refcount
+ * will reach 0 and we will delete it. Note that it is assumed
+ * that any locking on system level ifn/ifa is done at the
+ * caller of these functions and these routines will only
+ * lock the SCTP structures as they add or delete things.
+ *
+ * Other notes on VRF concepts.
+ * - An endpoint can be in multiple VRF's
+ * - An association lives within a VRF and only one VRF.
+ * - Any incoming packet we can deduce the VRF for by
+ * looking at the mbuf/pak inbound (for BSD its VRF=0 :D)
+ * - Any downward send call or connect call must supply the
+ * VRF via ancillary data or via some sort of set default
+ * VRF socket option call (again for BSD no brainer since
+ * the VRF is always 0).
+ * - An endpoint may add multiple VRF's to it.
+ * - Listening sockets can accept associations in any
+ * of the VRF's they are in but the assoc will end up
+ * in only one VRF (gotten from the packet or connect/send).
+ *
+ */
+
+struct sctp_vrf *
+sctp_allocate_vrf(int vrfid)
+{
+ struct sctp_vrf *vrf = NULL;
+ struct sctp_vrflist *bucket;
+
+ /* First allocate the VRF structure */
+ vrf = sctp_find_vrf(vrfid);
+ if (vrf) {
+ /* Already allocated */
+ return (vrf);
+ }
+ SCTP_MALLOC(vrf, struct sctp_vrf *, sizeof(struct sctp_vrf),
+ "SCTP_VRF");
+ if (vrf == NULL) {
+ /* No memory */
+#ifdef INVARIANTS
+ panic("No memory for VRF:%d", vrfid);
+#endif
+ return (NULL);
+ }
+ /* setup the VRF */
+ memset(vrf, 0, sizeof(struct sctp_vrf));
+ vrf->vrf_id = vrfid;
+ LIST_INIT(&vrf->ifnlist);
+ vrf->total_ifa_count = 0;
+ /* Add it to the hash table */
+ bucket = &sctppcbinfo.sctp_vrfhash[(vrfid & sctppcbinfo.hashvrfmark)];
+ LIST_INSERT_HEAD(bucket, vrf, next_vrf);
+ return (vrf);
+}
+
+
+struct sctp_ifn *
+sctp_find_ifn(struct sctp_vrf *vrf, void *ifn, uint32_t ifn_index)
+{
+ struct sctp_ifn *sctp_ifnp;
+
+ /*
+ * We assume the lock is held for the addresses if thats wrong
+ * problems could occur :-)
+ */
+ LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) {
+ if (sctp_ifnp->ifn_index == ifn_index) {
+ return (sctp_ifnp);
+ }
+ if (sctp_ifnp->ifn_p && ifn && (sctp_ifnp->ifn_p == ifn)) {
+ return (sctp_ifnp);
+ }
+ }
+ return (NULL);
+}
+
+struct sctp_vrf *
+sctp_find_vrf(uint32_t vrfid)
+{
+ struct sctp_vrflist *bucket;
+ struct sctp_vrf *liste;
+
+ bucket = &sctppcbinfo.sctp_vrfhash[(vrfid & sctppcbinfo.hashvrfmark)];
+ LIST_FOREACH(liste, bucket, next_vrf) {
+ if (vrfid == liste->vrf_id) {
+ return (liste);
+ }
+ }
+ return (NULL);
+}
+
+void
+sctp_free_ifa(struct sctp_ifa *sctp_ifap)
+{
+ int ret;
+
+ ret = atomic_fetchadd_int(&sctp_ifap->refcount, -1);
+ if (ret == 1) {
+ /* We zero'd the count */
+ SCTP_FREE(sctp_ifap);
+ }
+}
+
+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)
+{
+ struct sctp_vrf *vrf;
+ struct sctp_ifn *sctp_ifnp = NULL;
+ struct sctp_ifa *sctp_ifap = NULL;
+
+ /* How granular do we need the locks to be here? */
+ SCTP_IPI_ADDR_LOCK();
+ vrf = sctp_find_vrf(vrfid);
+ if (vrf == NULL) {
+ vrf = sctp_allocate_vrf(vrfid);
+ if (vrf == NULL) {
+ SCTP_IPI_ADDR_UNLOCK();
+ return (NULL);
+ }
+ }
+ sctp_ifnp = sctp_find_ifn(vrf, ifn, ifn_index);
+ if (sctp_ifnp == NULL) {
+ /*
+ * build one and add it, can't hold lock until after malloc
+ * done though.
+ */
+ SCTP_IPI_ADDR_UNLOCK();
+ SCTP_MALLOC(sctp_ifnp, struct sctp_ifn *, sizeof(struct sctp_ifn), "SCTP_IFN");
+ if (sctp_ifnp == NULL) {
+#ifdef INVARIANTS
+ panic("No memory for IFN:%u", sctp_ifnp->ifn_index);
+#endif
+ return (NULL);
+ }
+ sctp_ifnp->ifn_index = ifn_index;
+ sctp_ifnp->ifn_p = ifn;
+ sctp_ifnp->ifn_type = ifn_type;
+ sctp_ifnp->ifa_count = 0;
+ sctp_ifnp->refcount = 0;
+ sctp_ifnp->vrf = vrf;
+ memcpy(sctp_ifnp->ifn_name, if_name, SCTP_IFNAMSIZ);
+ LIST_INIT(&sctp_ifnp->ifalist);
+ SCTP_IPI_ADDR_LOCK();
+ LIST_INSERT_HEAD(&vrf->ifnlist, sctp_ifnp, next_ifn);
+ }
+ sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1);
+ if (sctp_ifap) {
+ /* Hmm, it already exists? */
+ if ((sctp_ifap->ifn_p) &&
+ (sctp_ifap->ifn_p->ifn_index == ifn_index)) {
+ if (sctp_ifap->localifa_flags & SCTP_BEING_DELETED) {
+ /* easy to solve, just switch back to active */
+ sctp_ifap->localifa_flags = SCTP_ADDR_VALID;
+ sctp_ifap->ifn_p = sctp_ifnp;
+ exit_stage_left:
+ SCTP_IPI_ADDR_UNLOCK();
+ return (sctp_ifap);
+ } else {
+ goto exit_stage_left;
+ }
+ } else {
+ if (sctp_ifap->ifn_p) {
+ /*
+ * The first IFN gets the address,
+ * duplicates are ignored.
+ */
+ goto exit_stage_left;
+ } else {
+ /* repair ifnp which was NULL ? */
+ sctp_ifap->localifa_flags = SCTP_ADDR_VALID;
+ sctp_ifap->ifn_p = sctp_ifnp;
+ atomic_add_int(&sctp_ifnp->refcount, 1);
+ }
+ goto exit_stage_left;
+ }
+ }
+ SCTP_IPI_ADDR_UNLOCK();
+ SCTP_MALLOC(sctp_ifap, struct sctp_ifa *, sizeof(struct sctp_ifa), "SCTP_IFA");
+ if (sctp_ifap == NULL) {
+#ifdef INVARIANTS
+ panic("No memory for IFA");
+#endif
+ return (NULL);
+ }
+ memset(sctp_ifap, 0, sizeof(sctp_ifap));
+ sctp_ifap->ifn_p = sctp_ifnp;
+ atomic_add_int(&sctp_ifnp->refcount, 1);
+
+ sctp_ifap->ifa = ifa;
+ memcpy(&sctp_ifap->address, addr, addr->sa_len);
+ sctp_ifap->localifa_flags = SCTP_ADDR_VALID | SCTP_ADDR_DEFER_USE;
+ sctp_ifap->flags = ifa_flags;
+ /* Set scope */
+ if (sctp_ifap->address.sa.sa_family == AF_INET) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)&sctp_ifap->address.sin;
+ if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) ||
+ (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
+ sctp_ifap->src_is_loop = 1;
+ }
+ if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+ sctp_ifap->src_is_priv = 1;
+ }
+ } else if (sctp_ifap->address.sa.sa_family == AF_INET6) {
+ /* ok to use deprecated addresses? */
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)&sctp_ifap->address.sin6;
+ if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) ||
+ (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
+ sctp_ifap->src_is_loop = 1;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ sctp_ifap->src_is_priv = 1;
+ }
+ }
+ if ((sctp_ifap->src_is_priv == 0) &&
+ (sctp_ifap->src_is_loop == 0)) {
+ sctp_ifap->src_is_glob = 1;
+ }
+ SCTP_IPI_ADDR_LOCK();
+ sctp_ifap->refcount = 1;
+ LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa);
+ sctp_ifnp->ifa_count++;
+ vrf->total_ifa_count++;
+ SCTP_IPI_ADDR_UNLOCK();
+ return (sctp_ifap);
+}
+
+struct sctp_ifa *
+sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr,
+ uint32_t ifn_index)
+{
+ struct sctp_vrf *vrf;
+ struct sctp_ifa *sctp_ifap = NULL;
+ struct sctp_ifn *sctp_ifnp = NULL;
+
+ SCTP_IPI_ADDR_LOCK();
+
+ vrf = sctp_find_vrf(vrfid);
+ if (vrf == NULL) {
+ printf("Can't find vrfid:%d\n", vrfid);
+ goto out_now;
+ }
+ sctp_ifnp = sctp_find_ifn(vrf, (void *)NULL, ifn_index);
+ if (sctp_ifnp == NULL) {
+ sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1);
+ } else {
+ sctp_ifap = sctp_find_ifa_in_ifn(sctp_ifnp, addr, 1);
+ }
+
+ if (sctp_ifap) {
+ sctp_ifap->localifa_flags &= SCTP_ADDR_VALID;
+ sctp_ifap->localifa_flags |= SCTP_BEING_DELETED;
+ sctp_ifnp->ifa_count--;
+ vrf->total_ifa_count--;
+ LIST_REMOVE(sctp_ifap, next_ifa);
+ atomic_add_int(&sctp_ifnp->refcount, -1);
+ } else {
+ printf("Del Addr-ifn:%d Could not find address:",
+ ifn_index);
+ sctp_print_address(addr);
+ }
+out_now:
+ SCTP_IPI_ADDR_UNLOCK();
+ return (sctp_ifap);
+}
/*
* Notes on locks for FreeBSD 5 and up. All association lookups that have a
@@ -177,21 +460,20 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
#endif
continue;
}
- if (laddr->ifa->ifa_addr == NULL) {
+ if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_PCB1) {
- printf("ifa with a NULL address\n");
+ printf("ifa being deleted\n");
}
#endif
continue;
}
- if (laddr->ifa->ifa_addr->sa_family ==
+ if (laddr->ifa->address.sa.sa_family ==
to->sa_family) {
/* see if it matches */
struct sockaddr_in *intf_addr, *sin;
- intf_addr = (struct sockaddr_in *)
- laddr->ifa->ifa_addr;
+ intf_addr = &laddr->ifa->address.sin;
sin = (struct sockaddr_in *)to;
if (from->sa_family == AF_INET) {
if (sin->sin_addr.s_addr ==
@@ -205,8 +487,7 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
sin6 = (struct sockaddr_in6 *)
to;
- intf_addr6 = (struct sockaddr_in6 *)
- laddr->ifa->ifa_addr;
+ intf_addr6 = &laddr->ifa->address.sin6;
if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
&intf_addr6->sin6_addr)) {
@@ -595,12 +876,13 @@ sctp_findassociation_ep_asocid(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int
static struct sctp_inpcb *
sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
- uint16_t lport)
+ uint16_t lport, uint32_t vrf_id)
{
struct sctp_inpcb *inp;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
struct sctp_laddr *laddr;
+ int fnd;
/*
* Endpoing probe expects that the INP_INFO is locked.
@@ -639,7 +921,14 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
SCTP_INP_RUNLOCK(inp);
continue;
}
+ /* does a VRF id match? */
+ fnd = 0;
+ if (inp->def_vrf_id == vrf_id)
+ fnd = 1;
+
SCTP_INP_RUNLOCK(inp);
+ if (!fnd)
+ continue;
return (inp);
}
SCTP_INP_RUNLOCK(inp);
@@ -676,6 +965,15 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
SCTP_INP_RUNLOCK(inp);
continue;
}
+ /* does a VRF id match? */
+ fnd = 0;
+ if (inp->def_vrf_id == vrf_id)
+ fnd = 1;
+
+ if (!fnd) {
+ SCTP_INP_RUNLOCK(inp);
+ continue;
+ }
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
if (laddr->ifa == NULL) {
#ifdef SCTP_DEBUG
@@ -691,20 +989,19 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
laddr->ifa);
}
#endif
- if (laddr->ifa->ifa_addr == NULL) {
+ if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_PCB1) {
- printf("Huh IFA as an ifa_addr=NULL, ");
+ printf("Huh IFA being deleted\n");
}
#endif
continue;
}
- if (laddr->ifa->ifa_addr->sa_family == nam->sa_family) {
+ if (laddr->ifa->address.sa.sa_family == nam->sa_family) {
/* possible, see if it matches */
struct sockaddr_in *intf_addr;
- intf_addr = (struct sockaddr_in *)
- laddr->ifa->ifa_addr;
+ intf_addr = &laddr->ifa->address.sin;
if (nam->sa_family == AF_INET) {
if (sin->sin_addr.s_addr ==
intf_addr->sin_addr.s_addr) {
@@ -714,8 +1011,7 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
} else if (nam->sa_family == AF_INET6) {
struct sockaddr_in6 *intf_addr6;
- intf_addr6 = (struct sockaddr_in6 *)
- laddr->ifa->ifa_addr;
+ intf_addr6 = &laddr->ifa->address.sin6;
if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
&intf_addr6->sin6_addr)) {
SCTP_INP_RUNLOCK(inp);
@@ -731,7 +1027,7 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
struct sctp_inpcb *
-sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock)
+sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock, uint32_t vrf_id)
{
/*
* First we check the hash table to see if someone has this port
@@ -765,7 +1061,7 @@ sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock)
}
head = &sctppcbinfo.sctp_ephash[SCTP_PCBHASH_ALLADDR(lport,
sctppcbinfo.hashmark)];
- inp = sctp_endpoint_probe(nam, head, lport);
+ inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
/*
* If the TCP model exists it could be that the main listening
@@ -786,7 +1082,7 @@ sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock)
*/
head = &sctppcbinfo.sctp_tcpephash[i];
if (LIST_FIRST(head)) {
- inp = sctp_endpoint_probe(nam, head, lport);
+ inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
if (inp) {
/* Found one */
break;
@@ -810,7 +1106,7 @@ sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock)
*/
struct sctp_tcb *
sctp_findassociation_addr_sa(struct sockaddr *to, struct sockaddr *from,
- struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool)
+ struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool, uint32_t vrf_id)
{
struct sctp_inpcb *inp;
struct sctp_tcb *retval;
@@ -827,7 +1123,7 @@ sctp_findassociation_addr_sa(struct sockaddr *to, struct sockaddr *from,
return (retval);
}
}
- inp = sctp_pcb_findep(to, 0, 1);
+ inp = sctp_pcb_findep(to, 0, 1, vrf_id);
if (inp_p != NULL) {
*inp_p = inp;
}
@@ -1027,8 +1323,9 @@ sctp_findassociation_addr(struct mbuf *m, int iphlen, int offset,
struct sockaddr *to = (struct sockaddr *)&to_store;
struct sockaddr *from = (struct sockaddr *)&from_store;
struct sctp_inpcb *inp;
+ uint32_t vrf_id;
-
+ vrf_id = SCTP_DEFAULT_VRFID;
iph = mtod(m, struct ip *);
if (iph->ip_v == IPVERSION) {
/* its IPv4 */
@@ -1111,11 +1408,11 @@ sctp_findassociation_addr(struct mbuf *m, int iphlen, int offset,
}
if (inp_p) {
retval = sctp_findassociation_addr_sa(to, from, inp_p, netp,
- find_tcp_pool);
+ find_tcp_pool, vrf_id);
inp = *inp_p;
} else {
retval = sctp_findassociation_addr_sa(to, from, &inp, netp,
- find_tcp_pool);
+ find_tcp_pool, vrf_id);
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_PCB1) {
@@ -1282,24 +1579,6 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int iphlen, int offset,
}
-extern int sctp_max_burst_default;
-
-extern unsigned int sctp_delayed_sack_time_default;
-extern unsigned int sctp_heartbeat_interval_default;
-extern unsigned int sctp_pmtu_raise_time_default;
-extern unsigned int sctp_shutdown_guard_time_default;
-extern unsigned int sctp_secret_lifetime_default;
-
-extern unsigned int sctp_rto_max_default;
-extern unsigned int sctp_rto_min_default;
-extern unsigned int sctp_rto_initial_default;
-extern unsigned int sctp_init_rto_max_default;
-extern unsigned int sctp_valid_cookie_life_default;
-extern unsigned int sctp_init_rtx_max_default;
-extern unsigned int sctp_assoc_rtx_max_default;
-extern unsigned int sctp_path_rtx_max_default;
-extern unsigned int sctp_nr_outgoing_streams_default;
-
/*
* allocate a sctp_inpcb and setup a temporary binding to a port/all
* addresses. This way if we don't get a bind we by default pick a ephemeral
@@ -1364,21 +1643,21 @@ sctp_inpcb_alloc(struct socket *so)
so->so_pcb = (caddr_t)inp;
- if ((so->so_type == SOCK_DGRAM) ||
- (so->so_type == SOCK_SEQPACKET)) {
+ if ((SCTP_SO_TYPE(so) == SOCK_DGRAM) ||
+ (SCTP_SO_TYPE(so) == SOCK_SEQPACKET)) {
/* UDP style socket */
inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
SCTP_PCB_FLAGS_UNBOUND);
sctp_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT);
/* Be sure it is NON-BLOCKING IO for UDP */
- /* so->so_state |= SS_NBIO; */
- } else if (so->so_type == SOCK_STREAM) {
+ /* SCTP_SET_SO_NBIO(so); */
+ } else if (SCTP_SO_TYPE(so) == SOCK_STREAM) {
/* TCP style socket */
inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE |
SCTP_PCB_FLAGS_UNBOUND);
sctp_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT);
/* Be sure we have blocking IO by default */
- so->so_state &= ~SS_NBIO;
+ SCTP_CLEAR_SO_NBIO(so);
} else {
/*
* unsupported socket type (RAW, etc)- in case we missed it
@@ -1394,6 +1673,8 @@ sctp_inpcb_alloc(struct socket *so)
SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp);
return (ENOBUFS);
}
+ inp->def_vrf_id = SCTP_DEFAULT_VRFID;
+
SCTP_INP_INFO_WLOCK();
SCTP_INP_LOCK_INIT(inp);
SCTP_INP_READ_INIT(inp);
@@ -1407,6 +1688,7 @@ sctp_inpcb_alloc(struct socket *so)
TAILQ_INIT(&inp->read_queue);
LIST_INIT(&inp->sctp_addr_list);
+
LIST_INIT(&inp->sctp_asoc_list);
#ifdef SCTP_TRACK_FREED_ASOCS
@@ -1433,6 +1715,7 @@ sctp_inpcb_alloc(struct socket *so)
m->sctp_minrto = sctp_rto_min_default;
m->initial_rto = sctp_rto_initial_default;
m->initial_init_rto_max = sctp_init_rto_max_default;
+ m->sctp_sack_freq = sctp_sack_freq_default;
m->max_open_streams_intome = MAX_SCTP_STREAMS;
@@ -1470,7 +1753,6 @@ sctp_inpcb_alloc(struct socket *so)
/* How long is a cookie good for ? */
m->def_cookie_life = sctp_valid_cookie_life_default;
-
/*
* Initialize authentication parameters
*/
@@ -1601,10 +1883,11 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp,
}
static int
-sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport)
+sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id)
{
struct sctppcbhead *head;
struct sctp_inpcb *t_inp;
+ int fnd;
head = &sctppcbinfo.sctp_ephash[SCTP_PCBHASH_ALLADDR(lport,
sctppcbinfo.hashmark)];
@@ -1613,6 +1896,13 @@ sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport)
if (t_inp->sctp_lport != lport) {
continue;
}
+ /* is it in the VRF in question */
+ fnd = 0;
+ if (t_inp->def_vrf_id == vrf_id)
+ fnd = 1;
+ if (!fnd)
+ continue;
+
/* This one is in use. */
/* check the v6/v4 binding issue */
if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
@@ -1653,6 +1943,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
int bindall;
uint16_t lport;
int error;
+ uint32_t vrf_id;
lport = 0;
error = 0;
@@ -1712,6 +2003,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
return (EAFNOSUPPORT);
}
}
+ /*
+ * Setup a vrf_id to be the default for the non-bind-all case.
+ */
+ vrf_id = inp->def_vrf_id;
+
SCTP_INP_INFO_WLOCK();
SCTP_INP_WLOCK(inp);
/* increase our count due to the unlock we do */
@@ -1724,8 +2020,10 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
/* got to be root to get at low ports */
if (ntohs(lport) < IPPORT_RESERVED) {
if (p && (error =
- priv_check(p,
- PRIV_NETINET_RESERVEDPORT)
+ priv_check_cred(p->td_ucred,
+ PRIV_NETINET_RESERVEDPORT,
+ SUSER_ALLOWJAIL
+ )
)) {
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
@@ -1740,24 +2038,44 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
return (error);
}
SCTP_INP_WUNLOCK(inp);
- inp_tmp = sctp_pcb_findep(addr, 0, 1);
- if (inp_tmp != NULL) {
- /*
- * lock guy returned and lower count note that we
- * are not bound so inp_tmp should NEVER be inp. And
- * it is this inp (inp_tmp) that gets the reference
- * bump, so we must lower it.
- */
- SCTP_INP_DECR_REF(inp_tmp);
- SCTP_INP_DECR_REF(inp);
- /* unlock info */
- SCTP_INP_INFO_WUNLOCK();
- return (EADDRNOTAVAIL);
+ if (bindall) {
+ vrf_id = inp->def_vrf_id;
+ inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id);
+ if (inp_tmp != NULL) {
+ /*
+ * lock guy returned and lower count note
+ * that we are not bound so inp_tmp should
+ * NEVER be inp. And it is this inp
+ * (inp_tmp) that gets the reference bump,
+ * so we must lower it.
+ */
+ SCTP_INP_DECR_REF(inp_tmp);
+ SCTP_INP_DECR_REF(inp);
+ /* unlock info */
+ SCTP_INP_INFO_WUNLOCK();
+ return (EADDRNOTAVAIL);
+ }
+ } else {
+ inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id);
+ if (inp_tmp != NULL) {
+ /*
+ * lock guy returned and lower count note
+ * that we are not bound so inp_tmp should
+ * NEVER be inp. And it is this inp
+ * (inp_tmp) that gets the reference bump,
+ * so we must lower it.
+ */
+ SCTP_INP_DECR_REF(inp_tmp);
+ SCTP_INP_DECR_REF(inp);
+ /* unlock info */
+ SCTP_INP_INFO_WUNLOCK();
+ return (EADDRNOTAVAIL);
+ }
}
SCTP_INP_WLOCK(inp);
if (bindall) {
/* verify that no lport is not used by a singleton */
- if (sctp_isport_inuse(inp, lport)) {
+ if (sctp_isport_inuse(inp, lport, vrf_id)) {
/* Sorry someone already has this one bound */
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
@@ -1778,6 +2096,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
uint32_t port_guess;
uint16_t port_attempt;
int not_done = 1;
+ int not_found = 1;
while (not_done) {
port_guess = sctp_select_initial_TSN(&inp->sctp_ep);
@@ -1788,8 +2107,14 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
if (port_attempt < IPPORT_RESERVED) {
port_attempt += IPPORT_RESERVED;
}
- if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+ vrf_id = inp->def_vrf_id;
+ if (sctp_isport_inuse(inp, htons(port_attempt), vrf_id) == 1) {
/* got a port we can use */
+ not_found = 0;
+ break;
+ }
+ if (not_found == 1) {
+ /* We can use this port */
not_done = 0;
continue;
}
@@ -1802,8 +2127,14 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
if (port_attempt < IPPORT_RESERVED) {
port_attempt += IPPORT_RESERVED;
}
- if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+ vrf_id = inp->def_vrf_id;
+ if (sctp_isport_inuse(inp, htons(port_attempt), vrf_id) == 1) {
/* got a port we can use */
+ not_found = 0;
+ break;
+ }
+ if (not_found == 1) {
+ /* We can use this port */
not_done = 0;
continue;
}
@@ -1818,8 +2149,14 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
if (port_attempt < IPPORT_RESERVED) {
port_attempt += IPPORT_RESERVED;
}
- if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+ vrf_id = inp->def_vrf_id;
+ if (sctp_isport_inuse(inp, htons(port_attempt), vrf_id) == 1) {
/* got a port we can use */
+ not_found = 0;
+ break;
+ }
+ if (not_found == 1) {
+ /* We can use this port */
not_done = 0;
continue;
}
@@ -1860,7 +2197,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
* well. It will also have to do the embed scope kame hack
* too (before adding).
*/
- struct ifaddr *ifa;
+ struct sctp_ifa *ifa;
struct sockaddr_storage store_sa;
memset(&store_sa, 0, sizeof(store_sa));
@@ -1882,7 +2219,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
* zero out the port to find the address! yuck! can't do
* this earlier since need port for sctp_pcb_findep()
*/
- ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa);
+ ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa, vrf_id, 0);
if (ifa == NULL) {
/* Can't find an interface with that address */
SCTP_INP_WUNLOCK(inp);
@@ -1890,16 +2227,8 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
return (EADDRNOTAVAIL);
}
if (addr->sa_family == AF_INET6) {
- struct in6_ifaddr *ifa6;
-
- ifa6 = (struct in6_ifaddr *)ifa;
- /*
- * allow binding of deprecated addresses as per RFC
- * 2462 and ipng discussion
- */
- if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
- IN6_IFF_ANYCAST |
- IN6_IFF_NOTREADY)) {
+ /* GAK, more FIXME IFA lock? */
+ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
/* Can't bind a non-existent addr. */
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_WUNLOCK();
@@ -1909,15 +2238,19 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
/* we're not bound all */
inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUNDALL;
/* set the automatic addr changes from kernel flag */
+ sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF);
if (sctp_auto_asconf == 0) {
sctp_feature_off(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
} else {
+ /*
+ * allow bindx() to send ASCONF's for binding
+ * changes
+ */
sctp_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
}
- /* allow bindx() to send ASCONF's for binding changes */
- sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF);
+
/* add this address to the endpoint list */
- error = sctp_insert_laddr(&inp->sctp_addr_list, ifa);
+ error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, 0);
if (error != 0) {
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_WUNLOCK();
@@ -1964,7 +2297,7 @@ sctp_iterator_inp_being_freed(struct sctp_inpcb *inp, struct sctp_inpcb *inp_nex
* those guys. The list of iterators should never be very big
* though.
*/
- LIST_FOREACH(it, &sctppcbinfo.iteratorhead, sctp_nxt_itr) {
+ TAILQ_FOREACH(it, &sctppcbinfo.iteratorhead, sctp_nxt_itr) {
if (it == inp->inp_starting_point_for_iterator)
/* skip this guy, he's special */
continue;
@@ -2393,9 +2726,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
for ((laddr = LIST_FIRST(&inp->sctp_addr_list)); laddr != NULL;
laddr = nladdr) {
nladdr = LIST_NEXT(laddr, sctp_nxt_addr);
- LIST_REMOVE(laddr, sctp_nxt_addr);
- SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
- SCTP_DECR_LADDR_COUNT();
+ sctp_remove_laddr(laddr);
}
#ifdef SCTP_TRACK_FREED_ASOCS
@@ -2448,48 +2779,27 @@ sctp_findnet(struct sctp_tcb *stcb, struct sockaddr *addr)
* stats of stuff.
*/
int
-sctp_is_address_on_local_host(struct sockaddr *addr)
+sctp_is_address_on_local_host(struct sockaddr *addr, uint32_t vrf_id)
{
- struct ifnet *ifn;
- struct ifaddr *ifa;
-
- TAILQ_FOREACH(ifn, &ifnet, if_list) {
- TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
- if (addr->sa_family == ifa->ifa_addr->sa_family) {
- /* same family */
- if (addr->sa_family == AF_INET) {
- struct sockaddr_in *sin, *sin_c;
-
- sin = (struct sockaddr_in *)addr;
- sin_c = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (sin->sin_addr.s_addr ==
- sin_c->sin_addr.s_addr) {
- /*
- * we are on the same
- * machine
- */
- return (1);
- }
- } else if (addr->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6, *sin_c6;
+ struct sctp_ifa *sctp_ifa;
- sin6 = (struct sockaddr_in6 *)addr;
- sin_c6 = (struct sockaddr_in6 *)
- ifa->ifa_addr;
- if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
- &sin_c6->sin6_addr)) {
- /*
- * we are on the same
- * machine
- */
- return (1);
- }
- }
- }
- }
+ sctp_ifa = sctp_find_ifa_by_addr(addr, vrf_id, 0);
+ if (sctp_ifa) {
+ return (1);
+ } else {
+ return (0);
}
- return (0);
+}
+
+void
+sctp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+ net->cwnd = min((net->mtu * 4), max((2 * net->mtu), SCTP_INITIAL_CWND));
+ /* we always get at LEAST 2 MTU's */
+ if (net->cwnd < (2 * net->mtu)) {
+ net->cwnd = 2 * net->mtu;
+ }
+ net->ssthresh = stcb->asoc.peers_rwnd;
}
int
@@ -2554,23 +2864,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
stcb->asoc.ipv4_local_scope = 1;
}
#endif /* SCTP_DONT_DO_PRIVADDR_SCOPE */
-
- if (sctp_is_address_on_local_host(newaddr)) {
- stcb->asoc.loopback_scope = 1;
- stcb->asoc.ipv4_local_scope = 1;
- stcb->asoc.local_scope = 1;
- stcb->asoc.site_scope = 1;
- }
} else {
- if (from == SCTP_ADDR_IS_CONFIRMED) {
- /* From connectx */
- if (sctp_is_address_on_local_host(newaddr)) {
- stcb->asoc.loopback_scope = 1;
- stcb->asoc.ipv4_local_scope = 1;
- stcb->asoc.local_scope = 1;
- stcb->asoc.site_scope = 1;
- }
- }
/* Validate the address is in scope */
if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) &&
(stcb->asoc.ipv4_local_scope == 0)) {
@@ -2588,9 +2882,9 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
/* assure len is set */
sin6->sin6_len = sizeof(struct sockaddr_in6);
if (set_scope) {
- if (sctp_is_address_on_local_host(newaddr)) {
+ if (sctp_is_address_on_local_host(newaddr, stcb->asoc.vrf_id)) {
stcb->asoc.loopback_scope = 1;
- stcb->asoc.local_scope = 1;
+ stcb->asoc.local_scope = 0;
stcb->asoc.ipv4_local_scope = 1;
stcb->asoc.site_scope = 1;
} else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
@@ -2612,15 +2906,6 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
stcb->asoc.site_scope = 1;
}
} else {
- if (from == SCTP_ADDR_IS_CONFIRMED) {
- /* From connectx so we check for localhost. */
- if (sctp_is_address_on_local_host(newaddr)) {
- stcb->asoc.loopback_scope = 1;
- stcb->asoc.ipv4_local_scope = 1;
- stcb->asoc.local_scope = 1;
- stcb->asoc.site_scope = 1;
- }
- }
/* Validate the address is in scope */
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) &&
(stcb->asoc.loopback_scope == 0)) {
@@ -2650,7 +2935,14 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
} else if (newaddr->sa_family == AF_INET6) {
((struct sockaddr_in6 *)&net->ro._l_addr)->sin6_port = stcb->rport;
}
- net->addr_is_local = sctp_is_address_on_local_host(newaddr);
+ net->addr_is_local = sctp_is_address_on_local_host(newaddr, stcb->asoc.vrf_id);
+ if (net->addr_is_local && ((set_scope || (from == SCTP_ADDR_IS_CONFIRMED)))) {
+ stcb->asoc.loopback_scope = 1;
+ stcb->asoc.ipv4_local_scope = 1;
+ stcb->asoc.local_scope = 0;
+ stcb->asoc.site_scope = 1;
+ addr_inscope = 1;
+ }
net->failure_threshold = stcb->asoc.def_net_failure;
if (addr_inscope == 0) {
net->dest_state = (SCTP_ADDR_REACHABLE |
@@ -2667,11 +2959,11 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
stcb->asoc.numnets++;
*(&net->ref_count) = 1;
net->tos_flowlabel = 0;
-#ifdef AF_INET
+#ifdef INET
if (newaddr->sa_family == AF_INET)
net->tos_flowlabel = stcb->asoc.default_tos;
#endif
-#ifdef AF_INET6
+#ifdef INET6
if (newaddr->sa_family == AF_INET6)
net->tos_flowlabel = stcb->asoc.default_flowlabel;
#endif
@@ -2715,13 +3007,8 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
* We take the max of the burst limit times a MTU or the
* INITIAL_CWND. We then limit this to 4 MTU's of sending.
*/
- net->cwnd = min((net->mtu * 4), max((2 * net->mtu), SCTP_INITIAL_CWND));
+ sctp_set_initial_cc_param(stcb, net);
- /* we always get at LEAST 2 MTU's */
- if (net->cwnd < (2 * net->mtu)) {
- net->cwnd = 2 * net->mtu;
- }
- net->ssthresh = stcb->asoc.peers_rwnd;
#if defined(SCTP_CWND_MONITOR) || defined(SCTP_CWND_LOGGING)
sctp_log_cwnd(stcb, net, 0, SCTP_CWND_INITIALIZATION);
@@ -2820,7 +3107,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
*/
struct sctp_tcb *
sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
- int for_a_init, int *error, uint32_t override_tag)
+ int for_a_init, int *error, uint32_t override_tag, uint32_t vrf)
{
struct sctp_tcb *stcb;
struct sctp_association *asoc;
@@ -2920,7 +3207,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
/* setup back pointer's */
stcb->sctp_ep = inp;
stcb->sctp_socket = inp->sctp_socket;
- if ((err = sctp_init_asoc(inp, asoc, for_a_init, override_tag))) {
+ if ((err = sctp_init_asoc(inp, asoc, for_a_init, override_tag, vrf))) {
/* failed */
SCTP_TCB_LOCK_DESTROY(stcb);
SCTP_TCB_SEND_LOCK_DESTROY(stcb);
@@ -3001,15 +3288,13 @@ sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net)
asoc = &stcb->asoc;
asoc->numnets--;
TAILQ_REMOVE(&asoc->nets, net, sctp_next);
- sctp_free_remote_addr(net);
if (net == asoc->primary_destination) {
/* Reset primary */
struct sctp_nets *lnet;
lnet = TAILQ_FIRST(&asoc->nets);
/* Try to find a confirmed primary */
- asoc->primary_destination = sctp_find_alternate_net(stcb, lnet,
- 0);
+ asoc->primary_destination = sctp_find_alternate_net(stcb, lnet, 0);
}
if (net == asoc->last_data_chunk_from) {
/* Reset primary */
@@ -3019,10 +3304,7 @@ sctp_remove_net(struct sctp_tcb *stcb, struct sctp_nets *net)
/* Clear net */
asoc->last_control_chunk_from = NULL;
}
-/* if (net == asoc->asconf_last_sent_to) {*/
- /* Reset primary */
-/* asoc->asconf_last_sent_to = TAILQ_FIRST(&asoc->nets);*/
-/* }*/
+ sctp_free_remote_addr(net);
}
/*
@@ -3631,13 +3913,11 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
sctp_free_remote_addr(net);
}
- /* local addresses, if any */
- while (!SCTP_LIST_EMPTY(&asoc->sctp_local_addr_list)) {
- laddr = LIST_FIRST(&asoc->sctp_local_addr_list);
- LIST_REMOVE(laddr, sctp_nxt_addr);
- SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
- SCTP_DECR_LADDR_COUNT();
+ while (!SCTP_LIST_EMPTY(&asoc->sctp_restricted_addrs)) {
+ laddr = LIST_FIRST(&asoc->sctp_restricted_addrs);
+ sctp_remove_laddr(laddr);
}
+
/* pending asconf (address) parameters */
while (!TAILQ_EMPTY(&asoc->asconf_queue)) {
aparam = TAILQ_FIRST(&asoc->asconf_queue);
@@ -3791,12 +4071,12 @@ sctp_update_ep_vflag(struct sctp_inpcb *inp)
#endif /* SCTP_DEBUG */
continue;
}
- if (laddr->ifa->ifa_addr) {
+ if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
continue;
}
- if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
+ if (laddr->ifa->address.sa.sa_family == AF_INET6) {
inp->ip_inp.inp.inp_vflag |= INP_IPV6;
- } else if (laddr->ifa->ifa_addr->sa_family == AF_INET) {
+ } else if (laddr->ifa->address.sa.sa_family == AF_INET) {
inp->ip_inp.inp.inp_vflag |= INP_IPV4;
}
}
@@ -3807,7 +4087,7 @@ sctp_update_ep_vflag(struct sctp_inpcb *inp)
* done if we are bound to all addresses
*/
int
-sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa, uint32_t action)
{
struct sctp_laddr *laddr;
int fnd, error;
@@ -3818,14 +4098,11 @@ sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
/* You are already bound to all. You have it already */
return (0);
}
- if (ifa->ifa_addr->sa_family == AF_INET6) {
- struct in6_ifaddr *ifa6;
-
- ifa6 = (struct in6_ifaddr *)ifa;
- if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
- IN6_IFF_DEPRECATED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))
- /* Can't bind a non-existent addr. */
+ if (ifa->address.sa.sa_family == AF_INET6) {
+ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
+ /* Can't bind a non-useable addr. */
return (-1);
+ }
}
/* first, is it already present? */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
@@ -3835,16 +4112,16 @@ sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
}
}
- if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) && (fnd == 0)) {
- /* Not bound to all */
- error = sctp_insert_laddr(&inp->sctp_addr_list, ifa);
+ if (fnd == 0) {
+ /* Not in the ep list */
+ error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, action);
if (error != 0)
return (error);
inp->laddr_count++;
/* update inp_vflag flags */
- if (ifa->ifa_addr->sa_family == AF_INET6) {
+ if (ifa->address.sa.sa_family == AF_INET6) {
inp->ip_inp.inp.inp_vflag |= INP_IPV6;
- } else if (ifa->ifa_addr->sa_family == AF_INET) {
+ } else if (ifa->address.sa.sa_family == AF_INET) {
inp->ip_inp.inp.inp_vflag |= INP_IPV4;
}
}
@@ -3881,7 +4158,7 @@ sctp_select_primary_destination(struct sctp_tcb *stcb)
* to be done if we are bound to all addresses
*/
int
-sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa)
{
struct sctp_laddr *laddr;
int fnd;
@@ -3901,7 +4178,7 @@ sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
/* can't delete unless there are at LEAST 2 addresses */
return (-1);
}
- if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) && (fnd)) {
+ if (fnd) {
/*
* clean up any use of this address go through our
* associations and clear any last_used_address that match
@@ -3917,29 +4194,40 @@ sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
/* clean up "last_used_address" */
LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+ struct sctp_nets *net;
+
+ SCTP_TCB_LOCK(stcb);
if (stcb->asoc.last_used_address == laddr)
/* delete this address */
stcb->asoc.last_used_address = NULL;
+ /*
+ * Now spin through all the nets and purge any ref
+ * to laddr
+ */
+ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+ if (net->ro._s_addr &&
+ (net->ro._s_addr->ifa == laddr->ifa)) {
+ /* Yep, purge src address selected */
+ struct rtentry *rt;
+
+ /* delete this address if cached */
+ rt = net->ro.ro_rt;
+ if (rt != NULL) {
+ RTFREE(rt);
+ net->ro.ro_rt = NULL;
+ }
+ sctp_free_ifa(net->ro._s_addr);
+ net->ro._s_addr = NULL;
+ net->src_addr_selected = 0;
+ }
+ }
+ SCTP_TCB_UNLOCK(stcb);
} /* for each tcb */
-
/* remove it from the ep list */
sctp_remove_laddr(laddr);
inp->laddr_count--;
/* update inp_vflag flags */
sctp_update_ep_vflag(inp);
- /* select a new primary destination if needed */
- LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
- /*
- * presume caller (sctp_asconf.c) already owns INP
- * lock
- */
- SCTP_TCB_LOCK(stcb);
- if (sctp_destination_is_reachable(stcb,
- (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr) == 0) {
- sctp_select_primary_destination(stcb);
- }
- SCTP_TCB_UNLOCK(stcb);
- } /* for each tcb */
}
return (0);
}
@@ -3951,37 +4239,35 @@ sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
* "valid" address list
*/
int
-sctp_add_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
+sctp_add_local_addr_assoc(struct sctp_tcb *stcb, struct sctp_ifa *ifa, int restricted_list)
{
struct sctp_inpcb *inp;
struct sctp_laddr *laddr;
+ struct sctpladdr *list;
int error;
/*
- * Assumes TCP is locked.. and possiblye the INP. May need to
+ * Assumes TCB is locked.. and possibly the INP. May need to
* confirm/fix that if we need it and is not the case.
*/
+ list = &stcb->asoc.sctp_restricted_addrs;
+
inp = stcb->sctp_ep;
- if (ifa->ifa_addr->sa_family == AF_INET6) {
- struct in6_ifaddr *ifa6;
-
- ifa6 = (struct in6_ifaddr *)ifa;
- if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
- /* IN6_IFF_DEPRECATED | */
- IN6_IFF_ANYCAST |
- IN6_IFF_NOTREADY))
+ if (ifa->address.sa.sa_family == AF_INET6) {
+ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
/* Can't bind a non-existent addr. */
return (-1);
+ }
}
/* does the address already exist? */
- LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+ LIST_FOREACH(laddr, list, sctp_nxt_addr) {
if (laddr->ifa == ifa) {
return (-1);
}
}
/* add to the list */
- error = sctp_insert_laddr(&stcb->asoc.sctp_local_addr_list, ifa);
+ error = sctp_insert_laddr(list, ifa, 0);
if (error != 0)
return (error);
return (0);
@@ -3991,7 +4277,7 @@ sctp_add_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
* insert an laddr entry with the given ifa for the desired list
*/
int
-sctp_insert_laddr(struct sctpladdr *list, struct ifaddr *ifa)
+sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act)
{
struct sctp_laddr *laddr;
@@ -4003,6 +4289,8 @@ sctp_insert_laddr(struct sctpladdr *list, struct ifaddr *ifa)
SCTP_INCR_LADDR_COUNT();
bzero(laddr, sizeof(*laddr));
laddr->ifa = ifa;
+ laddr->action = act;
+ atomic_add_int(&ifa->refcount, 1);
/* insert it */
LIST_INSERT_HEAD(list, laddr, sctp_nxt_addr);
@@ -4018,6 +4306,7 @@ sctp_remove_laddr(struct sctp_laddr *laddr)
/* remove from the list */
LIST_REMOVE(laddr, sctp_nxt_addr);
+ sctp_free_ifa(laddr->ifa);
SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
SCTP_DECR_LADDR_COUNT();
}
@@ -4026,7 +4315,7 @@ sctp_remove_laddr(struct sctp_laddr *laddr)
* Remove an address from the TCB local address list
*/
int
-sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
+sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct sctp_ifa *ifa)
{
struct sctp_inpcb *inp;
struct sctp_laddr *laddr;
@@ -4050,7 +4339,7 @@ sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
return (-1);
}
}
- LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+ LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) {
/* remove the address if it exists */
if (laddr->ifa == NULL)
continue;
@@ -4064,71 +4353,6 @@ sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
return (-1);
}
-/*
- * Remove an address from the TCB local address list lookup using a sockaddr
- * addr
- */
-int
-sctp_del_local_addr_assoc_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
-{
- struct sctp_inpcb *inp;
- struct sctp_laddr *laddr;
- struct sockaddr *l_sa;
-
- /*
- * This function I find does not seem to have a caller. As such we
- * NEED TO DELETE this code. If we do find a caller, the caller MUST
- * have locked the TCB at the least and probably the INP as well.
- */
- inp = stcb->sctp_ep;
- /* if subset bound and don't allow ASCONF's, can't delete last */
- if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) &&
- (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF) == 0)) {
- if (stcb->asoc.numnets < 2) {
- /* can't delete last address */
- return (-1);
- }
- }
- LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
- /* make sure the address exists */
- if (laddr->ifa == NULL)
- continue;
- if (laddr->ifa->ifa_addr == NULL)
- continue;
-
- l_sa = laddr->ifa->ifa_addr;
- if (l_sa->sa_family == AF_INET6) {
- /* IPv6 address */
- struct sockaddr_in6 *sin1, *sin2;
-
- sin1 = (struct sockaddr_in6 *)l_sa;
- sin2 = (struct sockaddr_in6 *)sa;
- if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
- sizeof(struct in6_addr)) == 0) {
- /* matched */
- sctp_remove_laddr(laddr);
- return (0);
- }
- } else if (l_sa->sa_family == AF_INET) {
- /* IPv4 address */
- struct sockaddr_in *sin1, *sin2;
-
- sin1 = (struct sockaddr_in *)l_sa;
- sin2 = (struct sockaddr_in *)sa;
- if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) {
- /* matched */
- sctp_remove_laddr(laddr);
- return (0);
- }
- } else {
- /* invalid family */
- return (-1);
- }
- } /* end foreach */
- /* address not found! */
- return (-1);
-}
-
static char sctp_pcb_initialized = 0;
/*
@@ -4159,7 +4383,7 @@ sctp_pcb_init()
LIST_INIT(&sctppcbinfo.listhead);
/* init the iterator head */
- LIST_INIT(&sctppcbinfo.iteratorhead);
+ TAILQ_INIT(&sctppcbinfo.iteratorhead);
/* init the hash table of endpoints */
TUNABLE_INT_FETCH("net.inet.sctp.tcbhashsize", &sctp_hashtblsize);
@@ -4177,6 +4401,10 @@ sctp_pcb_init()
sctppcbinfo.sctp_restarthash = SCTP_HASH_INIT(SCTP_STACK_VTAG_HASH_SIZE,
&sctppcbinfo.hashrestartmark);
+
+ sctppcbinfo.sctp_vrfhash = SCTP_HASH_INIT(SCTP_SIZE_OF_VRF_HASH,
+ &sctppcbinfo.hashvrfmark);
+
/* init the zones */
/*
* FIX ME: Should check for NULL returns, but if it does fail we are
@@ -4215,6 +4443,8 @@ sctp_pcb_init()
SCTP_IPI_COUNT_INIT();
SCTP_IPI_ADDR_INIT();
+ SCTP_IPI_ITERATOR_WQ_INIT();
+
LIST_INIT(&sctppcbinfo.addr_wq);
/* not sure if we need all the counts */
@@ -4244,6 +4474,18 @@ sctp_pcb_init()
LIST_INIT(&sctppcbinfo.vtag_timewait[i]);
}
+#if defined(SCTP_USE_THREAD_BASED_ITERATOR)
+ sctppcbinfo.iterator_running = 0;
+ sctp_startup_iterator();
+#endif
+
+ /*
+ * INIT the default VRF which for BSD is the only one, other O/S's
+ * may have more. But initially they must start with one and then
+ * add the VRF's as addresses are added.
+ */
+ sctp_init_vrf_list(SCTP_DEFAULT_VRF);
+
}
@@ -4814,8 +5056,9 @@ sctp_set_primary_addr(struct sctp_tcb *stcb, struct sockaddr *sa,
} else {
/* set the primary address */
if (net->dest_state & SCTP_ADDR_UNCONFIRMED) {
- /* Must be confirmed */
- return (-1);
+ /* Must be confirmed, so queue to set */
+ net->dest_state |= SCTP_ADDR_REQ_PRIMARY;
+ return (0);
}
stcb->asoc.primary_destination = net;
net->dest_state &= ~SCTP_ADDR_WAS_PRIMARY;
@@ -4922,89 +5165,9 @@ check_time_wait:
}
-/*
- * Delete the address from the endpoint local address list Lookup using a
- * sockaddr address (ie. not an ifaddr)
- */
-int
-sctp_del_local_addr_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa)
-{
- struct sctp_laddr *laddr;
- struct sockaddr *l_sa;
- int found = 0;
-
- /*
- * Here is another function I cannot find a caller for. As such we
- * SHOULD delete it if we have no users. If we find a user that user
- * MUST have the INP locked.
- *
- */
-
- if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
- /* You are already bound to all. You have it already */
- return (EINVAL);
- }
- LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
- /* make sure the address exists */
- if (laddr->ifa == NULL)
- continue;
- if (laddr->ifa->ifa_addr == NULL)
- continue;
-
- l_sa = laddr->ifa->ifa_addr;
- if (l_sa->sa_family == AF_INET6) {
- /* IPv6 address */
- struct sockaddr_in6 *sin1, *sin2;
-
- sin1 = (struct sockaddr_in6 *)l_sa;
- sin2 = (struct sockaddr_in6 *)sa;
- if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
- sizeof(struct in6_addr)) == 0) {
- /* matched */
- found = 1;
- break;
- }
- } else if (l_sa->sa_family == AF_INET) {
- /* IPv4 address */
- struct sockaddr_in *sin1, *sin2;
-
- sin1 = (struct sockaddr_in *)l_sa;
- sin2 = (struct sockaddr_in *)sa;
- if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) {
- /* matched */
- found = 1;
- break;
- }
- } else {
- /* invalid family */
- return (-1);
- }
- }
-
- if (found && inp->laddr_count < 2) {
- /* can't delete unless there are at LEAST 2 addresses */
- return (-1);
- }
- if (found && (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
- /*
- * remove it from the ep list, this should NOT be done until
- * its really gone from the interface list and we won't be
- * receiving more of these. Probably right away. If we do
- * allow a removal of an address from an association
- * (sub-set bind) than this should NOT be called until the
- * all ASCONF come back from this association.
- */
- sctp_remove_laddr(laddr);
- return (0);
- } else {
- return (-1);
- }
-}
-
static sctp_assoc_t reneged_asoc_ids[256];
static uint8_t reneged_at = 0;
-extern int sctp_do_drain;
static void
sctp_drain_mbufs(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
@@ -5160,6 +5323,7 @@ sctp_drain_mbufs(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
asoc->last_revoke_count = cnt;
SCTP_OS_TIMER_STOP(&stcb->asoc.dack_timer.timer);
sctp_send_sack(stcb);
+ sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_DRAIN);
reneged_asoc_ids[reneged_at] = sctp_get_associd(stcb);
reneged_at++;
}
@@ -5209,9 +5373,17 @@ sctp_drain()
* iterated through.
*/
int
-sctp_initiate_iterator(inp_func inpf, asoc_func af, uint32_t pcb_state,
- uint32_t pcb_features, uint32_t asoc_state, void *argp, uint32_t argi,
- end_func ef, struct sctp_inpcb *s_inp, uint8_t chunk_output_off)
+sctp_initiate_iterator(inp_func inpf,
+ asoc_func af,
+ inp_func inpe,
+ uint32_t pcb_state,
+ uint32_t pcb_features,
+ uint32_t asoc_state,
+ void *argp,
+ uint32_t argi,
+ end_func ef,
+ struct sctp_inpcb *s_inp,
+ uint8_t chunk_output_off)
{
struct sctp_iterator *it = NULL;
@@ -5226,12 +5398,17 @@ sctp_initiate_iterator(inp_func inpf, asoc_func af, uint32_t pcb_state,
memset(it, 0, sizeof(*it));
it->function_assoc = af;
it->function_inp = inpf;
+ if (inpf)
+ it->done_current_ep = 0;
+ else
+ it->done_current_ep = 1;
it->function_atend = ef;
it->pointer = argp;
it->val = argi;
it->pcb_flags = pcb_state;
it->pcb_features = pcb_features;
it->asoc_state = asoc_state;
+ it->function_inp_end = inpe;
it->no_chunk_output = chunk_output_off;
if (s_inp) {
it->inp = s_inp;
@@ -5239,17 +5416,29 @@ sctp_initiate_iterator(inp_func inpf, asoc_func af, uint32_t pcb_state,
} else {
SCTP_INP_INFO_RLOCK();
it->inp = LIST_FIRST(&sctppcbinfo.listhead);
+
SCTP_INP_INFO_RUNLOCK();
it->iterator_flags = SCTP_ITERATOR_DO_ALL_INP;
}
+ SCTP_IPI_ITERATOR_WQ_LOCK();
+ if (it->inp)
+ SCTP_INP_INCR_REF(it->inp);
+ TAILQ_INSERT_TAIL(&sctppcbinfo.iteratorhead, it, sctp_nxt_itr);
+#if defined(SCTP_USE_THREAD_BASED_ITERATOR)
+ if (sctppcbinfo.iterator_running == 0) {
+ sctp_wakeup_iterator();
+ }
+ SCTP_IPI_ITERATOR_WQ_UNLOCK();
+#else
+ if (it->inp)
+ SCTP_INP_DECR_REF(it->inp);
+ SCTP_IPI_ITERATOR_WQ_UNLOCK();
/* Init the timer */
SCTP_OS_TIMER_INIT(&it->tmr.timer);
/* add to the list of all iterators */
- SCTP_INP_INFO_WLOCK();
- LIST_INSERT_HEAD(&sctppcbinfo.iteratorhead, it, sctp_nxt_itr);
- SCTP_INP_INFO_WUNLOCK();
sctp_timer_start(SCTP_TIMER_TYPE_ITERATOR, (struct sctp_inpcb *)it,
NULL, NULL);
+#endif
return (0);
}
OpenPOWER on IntegriCloud