#include <sys/types.h>
#include <sys/systm.h>
#include <sys/stream.h>
#include <sys/cmn_err.h>
#include <sys/strsubr.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/mib2.h>
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
void
sctp_return_heartbeat(sctp_t *sctp, sctp_chunk_hdr_t *hbcp, mblk_t *mp)
{
mblk_t *smp;
sctp_chunk_hdr_t *cp;
ipha_t *iniph;
ip6_t *inip6h;
int isv4;
in6_addr_t addr;
sctp_faddr_t *fp;
uint16_t len;
sctp_stack_t *sctps = sctp->sctp_sctps;
BUMP_LOCAL(sctp->sctp_ibchunks);
isv4 = (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION);
if (isv4) {
iniph = (ipha_t *)mp->b_rptr;
IN6_IPADDR_TO_V4MAPPED(iniph->ipha_src, &addr);
} else {
inip6h = (ip6_t *)mp->b_rptr;
addr = inip6h->ip6_src;
}
fp = sctp_lookup_faddr(sctp, &addr);
if (fp == NULL) {
dprint(1,
("sctp_return_heartbeat: %p bogus hb from %x:%x:%x:%x\n",
(void *)sctp, SCTP_PRINTADDR(addr)));
SCTP_KSTAT(sctps, sctp_return_hb_failed);
return;
}
dprint(3, ("sctp_return_heartbeat: %p got hb from %x:%x:%x:%x\n",
(void *)sctp, SCTP_PRINTADDR(addr)));
len = ntohs(hbcp->sch_len);
smp = sctp_make_mp(sctp, fp, len);
if (smp == NULL) {
SCTP_KSTAT(sctps, sctp_return_hb_failed);
return;
}
cp = (sctp_chunk_hdr_t *)smp->b_wptr;
cp->sch_id = CHUNK_HEARTBEAT_ACK;
cp->sch_flags = 0;
cp->sch_len = htons(len);
bcopy((void *)(hbcp + 1), (void *)(cp + 1), len - sizeof (*cp));
smp->b_wptr += len;
BUMP_LOCAL(sctp->sctp_obchunks);
sctp_set_iplen(sctp, smp, fp->sf_ixa);
(void) conn_ip_output(smp, fp->sf_ixa);
BUMP_LOCAL(sctp->sctp_opkts);
}
void
sctp_send_heartbeat(sctp_t *sctp, sctp_faddr_t *fp)
{
sctp_chunk_hdr_t *cp;
sctp_parm_hdr_t *hpp;
int64_t *t;
int64_t now;
in6_addr_t *a;
mblk_t *hbmp;
size_t hblen;
sctp_stack_t *sctps = sctp->sctp_sctps;
dprint(3, ("sctp_send_heartbeat: to %x:%x:%x:%x from %x:%x:%x:%x\n",
SCTP_PRINTADDR(fp->sf_faddr), SCTP_PRINTADDR(fp->sf_saddr)));
hblen = sizeof (*cp) +
sizeof (*hpp) +
sizeof (*t) +
sizeof (fp->sf_hb_secret) +
sizeof (fp->sf_faddr);
hbmp = sctp_make_mp(sctp, fp, hblen);
if (hbmp == NULL) {
SCTP_KSTAT(sctps, sctp_send_hb_failed);
return;
}
cp = (sctp_chunk_hdr_t *)hbmp->b_wptr;
cp->sch_id = CHUNK_HEARTBEAT;
cp->sch_flags = 0;
cp->sch_len = hblen;
cp->sch_len = htons(cp->sch_len);
hpp = (sctp_parm_hdr_t *)(cp + 1);
hpp->sph_type = htons(PARM_HBINFO);
hpp->sph_len = hblen - sizeof (*cp);
hpp->sph_len = htons(hpp->sph_len);
now = ddi_get_lbolt64();
t = (int64_t *)(hpp + 1);
bcopy(&now, t, sizeof (now));
t++;
bcopy(&fp->sf_hb_secret, t, sizeof (uint64_t));
a = (in6_addr_t *)(t + 1);
bcopy(&fp->sf_faddr, a, sizeof (*a));
hbmp->b_wptr += hblen;
fp->sf_lastactive = now;
fp->sf_hb_pending = B_TRUE;
BUMP_LOCAL(sctp->sctp_obchunks);
SCTPS_BUMP_MIB(sctps, sctpTimHeartBeatProbe);
sctp_set_iplen(sctp, hbmp, fp->sf_ixa);
(void) conn_ip_output(hbmp, fp->sf_ixa);
BUMP_LOCAL(sctp->sctp_opkts);
}
void
sctp_validate_peer(sctp_t *sctp)
{
sctp_faddr_t *fp;
int cnt;
int64_t now;
int64_t earliest_expiry;
sctp_stack_t *sctps = sctp->sctp_sctps;
now = ddi_get_lbolt64();
earliest_expiry = 0;
cnt = sctps->sctps_maxburst;
for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->sf_next) {
if (fp->sf_state == SCTP_FADDRS_UNREACH)
continue;
if (fp->sf_state == SCTP_FADDRS_UNCONFIRMED) {
if (cnt-- > 0) {
fp->sf_hb_expiry = now + fp->sf_rto;
sctp_send_heartbeat(sctp, fp);
} else {
fp->sf_hb_expiry = now +
(sctp->sctp_rto_initial >> 1);
}
}
if (fp->sf_hb_interval != 0 && (earliest_expiry == 0 ||
fp->sf_hb_expiry < earliest_expiry)) {
earliest_expiry = fp->sf_hb_expiry;
}
}
if (sctp->sctp_autoclose != 0) {
int64_t expire;
expire = sctp->sctp_active + sctp->sctp_autoclose;
if (earliest_expiry == 0 || expire < earliest_expiry)
earliest_expiry = expire;
}
if (earliest_expiry != 0) {
earliest_expiry -= now;
if (earliest_expiry < 0)
earliest_expiry = 1;
sctp_timer(sctp, sctp->sctp_heartbeat_mp, earliest_expiry);
}
}
void
sctp_process_heartbeat(sctp_t *sctp, sctp_chunk_hdr_t *cp)
{
int64_t *sentp, sent;
uint64_t secret;
in6_addr_t addr;
sctp_faddr_t *fp;
sctp_parm_hdr_t *hpp;
int64_t now;
BUMP_LOCAL(sctp->sctp_ibchunks);
ASSERT(OK_32PTR(cp));
if (ntohs(cp->sch_len) < (sizeof (*cp) + sizeof (*hpp) +
sizeof (sent) + sizeof (secret) + sizeof (addr))) {
dprint(2, ("sctp_process_heartbeat: malformed ack %p\n",
(void *)sctp));
return;
}
hpp = (sctp_parm_hdr_t *)(cp + 1);
if (ntohs(hpp->sph_type) != PARM_HBINFO ||
ntohs(hpp->sph_len) != (ntohs(cp->sch_len) - sizeof (*cp))) {
dprint(2,
("sctp_process_heartbeat: malformed param in ack %p\n",
(void *)sctp));
return;
}
sentp = (int64_t *)(hpp + 1);
bcopy(sentp, &sent, sizeof (sent));
bcopy(++sentp, &secret, sizeof (secret));
bcopy(++sentp, &addr, sizeof (addr));
fp = sctp_lookup_faddr(sctp, &addr);
if (fp == NULL) {
dprint(2, ("sctp_process_heartbeat: invalid faddr (sctp=%p)\n",
(void *)sctp));
return;
}
if (secret != fp->sf_hb_secret) {
dprint(2,
("sctp_process_heartbeat: invalid secret in ack %p\n",
(void *)sctp));
return;
}
sctp_faddr_alive(sctp, fp);
now = ddi_get_lbolt64();
sctp_update_rtt(sctp, fp, now - sent);
fp->sf_hb_expiry = now + SET_HB_INTVL(fp);
}