#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sctputil.c,v 1.23 2025/11/19 22:16:48 andvar Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
#include "opt_ipsec.h"
#include "opt_sctp.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/cprng.h>
#include <sys/callout.h>
#include <net/route.h>
#ifdef INET6
#include <sys/domain.h>
#endif
#include <machine/limits.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
#include <netinet6/in6_pcb.h>
#endif
#include <netinet/sctp_pcb.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/key.h>
#endif
#include <netinet/sctputil.h>
#include <netinet/sctp_var.h>
#ifdef INET6
#include <netinet6/sctp6_var.h>
#endif
#include <netinet/sctp_header.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_hashdriver.h>
#include <netinet/sctp_uio.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_crc32.h>
#include <netinet/sctp_indata.h>
#define NUMBER_OF_MTU_SIZES 18
#ifdef SCTP_DEBUG
extern u_int32_t sctp_debug_on;
#endif
#ifdef SCTP_STAT_LOGGING
int sctp_cwnd_log_at=0;
int sctp_cwnd_log_rolled=0;
struct sctp_cwnd_log sctp_clog[SCTP_STAT_LOG_SIZE];
void sctp_clr_stat_log(void)
{
sctp_cwnd_log_at=0;
sctp_cwnd_log_rolled=0;
}
void
sctp_log_strm_del_alt(u_int32_t tsn, u_int16_t sseq, int from)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_STRM;
sctp_clog[sctp_cwnd_log_at].x.strlog.n_tsn = tsn;
sctp_clog[sctp_cwnd_log_at].x.strlog.n_sseq = sseq;
sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn = 0;
sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq = 0;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_map(uint32_t map, uint32_t cum, uint32_t high, int from)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MAP;
sctp_clog[sctp_cwnd_log_at].x.map.base = map;
sctp_clog[sctp_cwnd_log_at].x.map.cum = cum;
sctp_clog[sctp_cwnd_log_at].x.map.high = high;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_fr(uint32_t biggest_tsn, uint32_t biggest_new_tsn, uint32_t tsn,
int from)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_FR;
sctp_clog[sctp_cwnd_log_at].x.fr.largest_tsn = biggest_tsn;
sctp_clog[sctp_cwnd_log_at].x.fr.largest_new_tsn = biggest_new_tsn;
sctp_clog[sctp_cwnd_log_at].x.fr.tsn = tsn;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_strm_del(struct sctp_tmit_chunk *chk, struct sctp_tmit_chunk *poschk,
int from)
{
if (chk == NULL) {
printf("Gak log of NULL?\n");
return;
}
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_STRM;
sctp_clog[sctp_cwnd_log_at].x.strlog.n_tsn = chk->rec.data.TSN_seq;
sctp_clog[sctp_cwnd_log_at].x.strlog.n_sseq = chk->rec.data.stream_seq;
if (poschk != NULL) {
sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn =
poschk->rec.data.TSN_seq;
sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq =
poschk->rec.data.stream_seq;
} else {
sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn = 0;
sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq = 0;
}
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_cwnd(struct sctp_nets *net, int augment, uint8_t from)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_CWND;
sctp_clog[sctp_cwnd_log_at].x.cwnd.net = net;
sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_new_value = net->cwnd;
sctp_clog[sctp_cwnd_log_at].x.cwnd.inflight = net->flight_size;
sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_augment = augment;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_maxburst(struct sctp_nets *net, int error, int burst, uint8_t from)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MAXBURST;
sctp_clog[sctp_cwnd_log_at].x.cwnd.net = net;
sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_new_value = error;
sctp_clog[sctp_cwnd_log_at].x.cwnd.inflight = net->flight_size;
sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_augment = burst;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_rwnd(uint8_t from, u_int32_t peers_rwnd , u_int32_t snd_size, u_int32_t overhead)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_RWND;
sctp_clog[sctp_cwnd_log_at].x.rwnd.rwnd = peers_rwnd;
sctp_clog[sctp_cwnd_log_at].x.rwnd.send_size = snd_size;
sctp_clog[sctp_cwnd_log_at].x.rwnd.overhead = overhead;
sctp_clog[sctp_cwnd_log_at].x.rwnd.new_rwnd = 0;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_rwnd_set(uint8_t from, u_int32_t peers_rwnd , u_int32_t flight_size, u_int32_t overhead, u_int32_t a_rwndval)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_RWND;
sctp_clog[sctp_cwnd_log_at].x.rwnd.rwnd = peers_rwnd;
sctp_clog[sctp_cwnd_log_at].x.rwnd.send_size = flight_size;
sctp_clog[sctp_cwnd_log_at].x.rwnd.overhead = overhead;
sctp_clog[sctp_cwnd_log_at].x.rwnd.new_rwnd = a_rwndval;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_mbcnt(uint8_t from, u_int32_t total_oq , u_int32_t book, u_int32_t total_mbcnt_q, u_int32_t mbcnt)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MBCNT;
sctp_clog[sctp_cwnd_log_at].x.mbcnt.total_queue_size = total_oq;
sctp_clog[sctp_cwnd_log_at].x.mbcnt.size_change = book;
sctp_clog[sctp_cwnd_log_at].x.mbcnt.total_queue_mb_size = total_mbcnt_q;
sctp_clog[sctp_cwnd_log_at].x.mbcnt.mbcnt_change = mbcnt;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
void
sctp_log_block(uint8_t from, struct socket *so, struct sctp_association *asoc)
{
sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_BLOCK;
sctp_clog[sctp_cwnd_log_at].x.blk.maxmb = (u_int16_t)(so->so_snd.sb_mbmax/1024);
sctp_clog[sctp_cwnd_log_at].x.blk.onmb = asoc->total_output_mbuf_queue_size;
sctp_clog[sctp_cwnd_log_at].x.blk.maxsb = (u_int16_t)(so->so_snd.sb_hiwat/1024);
sctp_clog[sctp_cwnd_log_at].x.blk.onsb = asoc->total_output_queue_size;
sctp_clog[sctp_cwnd_log_at].x.blk.send_sent_qcnt = (u_int16_t)(asoc->send_queue_cnt + asoc->sent_queue_cnt);
sctp_clog[sctp_cwnd_log_at].x.blk.stream_qcnt = (u_int16_t)asoc->stream_queue_cnt;
sctp_cwnd_log_at++;
if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
sctp_cwnd_log_at = 0;
sctp_cwnd_log_rolled = 1;
}
}
int
sctp_fill_stat_log(struct mbuf *m)
{
struct sctp_cwnd_log_req *req;
int size_limit, num, i, at, cnt_out=0;
if (m == NULL)
return (EINVAL);
size_limit = (m->m_len - sizeof(struct sctp_cwnd_log_req));
if (size_limit < sizeof(struct sctp_cwnd_log)) {
return (EINVAL);
}
req = mtod(m, struct sctp_cwnd_log_req *);
num = size_limit/sizeof(struct sctp_cwnd_log);
if (sctp_cwnd_log_rolled) {
req->num_in_log = SCTP_STAT_LOG_SIZE;
} else {
req->num_in_log = sctp_cwnd_log_at;
if (req->end_at > sctp_cwnd_log_at) {
req->end_at = sctp_cwnd_log_at;
}
}
if ((num < SCTP_STAT_LOG_SIZE) &&
((sctp_cwnd_log_rolled) || (sctp_cwnd_log_at > num))) {
if (((req->start_at == 0) && (req->end_at == 0)) ||
(req->start_at >= SCTP_STAT_LOG_SIZE) ||
(req->end_at >= SCTP_STAT_LOG_SIZE)) {
req->num_ret = num;
req->end_at = sctp_cwnd_log_at - 1;
if ((sctp_cwnd_log_at - num) < 0) {
int cc;
cc = num - sctp_cwnd_log_at;
req->start_at = SCTP_STAT_LOG_SIZE - cc;
} else {
req->start_at = sctp_cwnd_log_at - num;
}
} else {
int cc;
if (req->start_at > req->end_at) {
cc = (SCTP_STAT_LOG_SIZE - req->start_at) +
(req->end_at + 1);
} else {
cc = req->end_at - req->start_at;
}
if (cc < num) {
num = cc;
}
req->num_ret = num;
}
} else {
req->start_at = 0;
req->end_at = sctp_cwnd_log_at - 1;
req->num_ret = sctp_cwnd_log_at;
}
for (i = 0, at = req->start_at; i < req->num_ret; i++) {
req->log[i] = sctp_clog[at];
cnt_out++;
at++;
if (at >= SCTP_STAT_LOG_SIZE)
at = 0;
}
m->m_len = (cnt_out * sizeof(struct sctp_cwnd_log_req)) + sizeof(struct sctp_cwnd_log_req);
return (0);
}
#endif
#ifdef SCTP_AUDITING_ENABLED
u_int8_t sctp_audit_data[SCTP_AUDIT_SIZE][2];
static int sctp_audit_indx = 0;
static
void sctp_print_audit_report(void)
{
int i;
int cnt;
cnt = 0;
for (i=sctp_audit_indx;i<SCTP_AUDIT_SIZE;i++) {
if ((sctp_audit_data[i][0] == 0xe0) &&
(sctp_audit_data[i][1] == 0x01)) {
cnt = 0;
printf("\n");
} else if (sctp_audit_data[i][0] == 0xf0) {
cnt = 0;
printf("\n");
} else if ((sctp_audit_data[i][0] == 0xc0) &&
(sctp_audit_data[i][1] == 0x01)) {
printf("\n");
cnt = 0;
}
printf("%2.2x%2.2x ", (uint32_t)sctp_audit_data[i][0],
(uint32_t)sctp_audit_data[i][1]);
cnt++;
if ((cnt % 14) == 0)
printf("\n");
}
for (i=0;i<sctp_audit_indx;i++) {
if ((sctp_audit_data[i][0] == 0xe0) &&
(sctp_audit_data[i][1] == 0x01)) {
cnt = 0;
printf("\n");
} else if (sctp_audit_data[i][0] == 0xf0) {
cnt = 0;
printf("\n");
} else if ((sctp_audit_data[i][0] == 0xc0) &&
(sctp_audit_data[i][1] == 0x01)) {
printf("\n");
cnt = 0;
}
printf("%2.2x%2.2x ", (uint32_t)sctp_audit_data[i][0],
(uint32_t)sctp_audit_data[i][1]);
cnt++;
if ((cnt % 14) == 0)
printf("\n");
}
printf("\n");
}
void sctp_auditing(int from, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
int resend_cnt, tot_out, rep, tot_book_cnt;
struct sctp_nets *lnet;
struct sctp_tmit_chunk *chk;
sctp_audit_data[sctp_audit_indx][0] = 0xAA;
sctp_audit_data[sctp_audit_indx][1] = 0x000000ff & from;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
if (inp == NULL) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0x01;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
return;
}
if (stcb == NULL) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0x02;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
return;
}
sctp_audit_data[sctp_audit_indx][0] = 0xA1;
sctp_audit_data[sctp_audit_indx][1] =
(0x000000ff & stcb->asoc.sent_queue_retran_cnt);
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 0;
tot_book_cnt = 0;
resend_cnt = tot_out = 0;
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if (chk->sent == SCTP_DATAGRAM_RESEND) {
resend_cnt++;
} else if (chk->sent < SCTP_DATAGRAM_RESEND) {
tot_out += chk->book_size;
tot_book_cnt++;
}
}
if (resend_cnt != stcb->asoc.sent_queue_retran_cnt) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA1;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
printf("resend_cnt:%d asoc-tot:%d\n",
resend_cnt, stcb->asoc.sent_queue_retran_cnt);
rep = 1;
stcb->asoc.sent_queue_retran_cnt = resend_cnt;
sctp_audit_data[sctp_audit_indx][0] = 0xA2;
sctp_audit_data[sctp_audit_indx][1] =
(0x000000ff & stcb->asoc.sent_queue_retran_cnt);
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
}
if (tot_out != stcb->asoc.total_flight) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA2;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
printf("tot_flt:%d asoc_tot:%d\n", tot_out,
(int)stcb->asoc.total_flight);
stcb->asoc.total_flight = tot_out;
}
if (tot_book_cnt != stcb->asoc.total_flight_count) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA5;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
printf("tot_flt_book:%d\n", tot_book);
stcb->asoc.total_flight_count = tot_book_cnt;
}
tot_out = 0;
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
tot_out += lnet->flight_size;
}
if (tot_out != stcb->asoc.total_flight) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA3;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
printf("real flight:%d net total was %d\n",
stcb->asoc.total_flight, tot_out);
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
tot_out = 0;
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if ((chk->whoTo == lnet) &&
(chk->sent < SCTP_DATAGRAM_RESEND)) {
tot_out += chk->book_size;
}
}
if (lnet->flight_size != tot_out) {
printf("net:%x flight was %d corrected to %d\n",
(uint32_t)lnet, lnet->flight_size, tot_out);
lnet->flight_size = tot_out;
}
}
}
if (rep) {
sctp_print_audit_report();
}
}
void
sctp_audit_log(u_int8_t ev, u_int8_t fd)
{
sctp_audit_data[sctp_audit_indx][0] = ev;
sctp_audit_data[sctp_audit_indx][1] = fd;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
}
#endif
static int sctp_mtu_sizes[] = {
68,
296,
508,
512,
544,
576,
1006,
1492,
1500,
1536,
2002,
2048,
4352,
4464,
8166,
17914,
32000,
65535
};
int
find_next_best_mtu(int totsz)
{
int i, perfer;
perfer = 0;
for (i = 0; i < NUMBER_OF_MTU_SIZES; i++) {
if (totsz < sctp_mtu_sizes[i]) {
perfer = i - 1;
if (perfer < 0)
perfer = 0;
break;
}
}
return (sctp_mtu_sizes[perfer]);
}
uint32_t
sctp_select_initial_TSN(struct sctp_pcb *m)
{
return cprng_strong32();
}
u_int32_t sctp_select_a_tag(struct sctp_inpcb *m)
{
u_long x, not_done;
struct timeval now;
SCTP_GETTIME_TIMEVAL(&now);
not_done = 1;
while (not_done) {
x = sctp_select_initial_TSN(&m->sctp_ep);
if (x == 0) {
continue;
}
if (sctp_is_vtag_good(m, x, &now)) {
not_done = 0;
}
}
return (x);
}
int
sctp_init_asoc(struct sctp_inpcb *m, struct sctp_association *asoc,
int for_a_init, uint32_t override_tag )
{
int i;
asoc->state = SCTP_STATE_INUSE;
asoc->max_burst = m->sctp_ep.max_burst;
asoc->heart_beat_delay = m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT];
asoc->cookie_life = m->sctp_ep.def_cookie_life;
if (override_tag) {
asoc->my_vtag = override_tag;
} else {
asoc->my_vtag = sctp_select_a_tag(m);
}
asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number = asoc->sending_seq =
sctp_select_initial_TSN(&m->sctp_ep);
asoc->t3timeout_highest_marked = asoc->asconf_seq_out;
asoc->peer_supports_asconf = 1;
asoc->peer_supports_asconf_setprim = 1;
asoc->peer_supports_pktdrop = 1;
asoc->sent_queue_retran_cnt = 0;
asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->advanced_peer_ack_point = asoc->last_acked_seq;
asoc->asconf_seq_in = asoc->last_acked_seq;
asoc->str_reset_seq_in = asoc->last_acked_seq + 1;
asoc->initial_init_rto_max = m->sctp_ep.initial_init_rto_max;
asoc->initial_rto = m->sctp_ep.initial_rto;
asoc->max_init_times = m->sctp_ep.max_init_times;
asoc->max_send_times = m->sctp_ep.max_send_times;
asoc->def_net_failure = m->sctp_ep.def_net_failure;
asoc->ecn_nonce_allowed = 0;
asoc->receiver_nonce_sum = 1;
asoc->nonce_sum_expect_base = 1;
asoc->nonce_sum_check = 1;
asoc->nonce_resync_tsn = 0;
asoc->nonce_wait_for_ecne = 0;
asoc->nonce_wait_tsn = 0;
if (m->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
struct in6pcb *inp6;
inp6 = (struct in6pcb *)m;
asoc->ipv6_addr_legal = 1;
if (
#if defined(__OpenBSD__)
(0)
#elif defined (__NetBSD__)
(inp6->in6p_flags & IN6P_IPV6_V6ONLY)
#else
(inp6->inp_flags & IN6P_IPV6_V6ONLY)
#endif
== 0) {
asoc->ipv4_addr_legal = 1;
} else {
asoc->ipv4_addr_legal = 0;
}
} else {
asoc->ipv4_addr_legal = 1;
asoc->ipv6_addr_legal = 0;
}
asoc->my_rwnd = uimax(m->sctp_socket->so_rcv.sb_hiwat, SCTP_MINIMAL_RWND);
asoc->peers_rwnd = m->sctp_socket->so_rcv.sb_hiwat;
asoc->smallest_mtu = m->sctp_frag_point;
asoc->minrto = m->sctp_ep.sctp_minrto;
asoc->maxrto = m->sctp_ep.sctp_maxrto;
LIST_INIT(&asoc->sctp_local_addr_list);
TAILQ_INIT(&asoc->nets);
TAILQ_INIT(&asoc->pending_reply_queue);
asoc->last_asconf_ack_sent = NULL;
asoc->hb_random_idx = 4;
asoc->sctp_autoclose_ticks = m->sctp_ep.auto_close_time;
asoc->streamoutcnt = asoc->pre_open_streams =
m->sctp_ep.pre_open_stream_count;
asoc->strmout = malloc(asoc->streamoutcnt *
sizeof(struct sctp_stream_out), M_PCB, M_NOWAIT);
if (asoc->strmout == NULL) {
return (ENOMEM);
}
for (i = 0; i < asoc->streamoutcnt; i++) {
asoc->strmout[i].next_sequence_sent = 0x0;
TAILQ_INIT(&asoc->strmout[i].outqueue);
asoc->strmout[i].stream_no = i;
asoc->strmout[i].next_spoke.tqe_next = 0;
asoc->strmout[i].next_spoke.tqe_prev = 0;
}
asoc->mapping_array_size = SCTP_INITIAL_MAPPING_ARRAY;
asoc->mapping_array = malloc(asoc->mapping_array_size,
M_PCB, M_NOWAIT);
if (asoc->mapping_array == NULL) {
free(asoc->strmout, M_PCB);
return (ENOMEM);
}
memset(asoc->mapping_array, 0, asoc->mapping_array_size);
TAILQ_INIT(&asoc->out_wheel);
TAILQ_INIT(&asoc->control_send_queue);
TAILQ_INIT(&asoc->send_queue);
TAILQ_INIT(&asoc->sent_queue);
TAILQ_INIT(&asoc->reasmqueue);
TAILQ_INIT(&asoc->delivery_queue);
asoc->max_inbound_streams = m->sctp_ep.max_open_streams_intome;
TAILQ_INIT(&asoc->asconf_queue);
return (0);
}
int
sctp_expand_mapping_array(struct sctp_association *asoc)
{
u_int8_t *new_array;
uint16_t new_size, old_size;
old_size = asoc->mapping_array_size;
new_size = old_size + SCTP_MAPPING_ARRAY_INCR;
new_array = malloc(new_size, M_PCB, M_NOWAIT);
if (new_array == NULL) {
printf("No memory for expansion of SCTP mapping array %d\n",
new_size);
return (-1);
}
memcpy(new_array, asoc->mapping_array, old_size);
memset(new_array + old_size, 0, SCTP_MAPPING_ARRAY_INCR);
free(asoc->mapping_array, M_PCB);
asoc->mapping_array = new_array;
asoc->mapping_array_size = new_size;
return (0);
}
static void
sctp_timeout_handler(void *t)
{
struct sctp_inpcb *inp;
struct sctp_tcb *stcb;
struct sctp_nets *net;
struct sctp_timer *tmr;
int did_output;
mutex_enter(softnet_lock);
tmr = (struct sctp_timer *)t;
inp = (struct sctp_inpcb *)tmr->ep;
stcb = (struct sctp_tcb *)tmr->tcb;
net = (struct sctp_nets *)tmr->net;
did_output = 1;
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xF0, (u_int8_t)tmr->type);
sctp_auditing(3, inp, stcb, net);
#endif
sctp_pegs[SCTP_TIMERS_EXP]++;
if (inp == NULL) {
return;
}
SCTP_INP_WLOCK(inp);
if (inp->sctp_socket == 0) {
mutex_exit(softnet_lock);
SCTP_INP_WUNLOCK(inp);
return;
}
if (stcb) {
if (stcb->asoc.state == 0) {
mutex_exit(softnet_lock);
SCTP_INP_WUNLOCK(inp);
return;
}
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("Timer type %d goes off\n", tmr->type);
}
#endif
#ifndef __NetBSD__
if (!callout_active(&tmr->timer)) {
SCTP_INP_WUNLOCK(inp);
return;
}
#endif
if (stcb) {
SCTP_TCB_LOCK(stcb);
}
SCTP_INP_INCR_REF(inp);
SCTP_INP_WUNLOCK(inp);
switch (tmr->type) {
case SCTP_TIMER_TYPE_ITERATOR:
{
struct sctp_iterator *it;
it = (struct sctp_iterator *)inp;
sctp_iterator_timer(it);
}
break;
case SCTP_TIMER_TYPE_SEND:
sctp_pegs[SCTP_TMIT_TIMER]++;
stcb->asoc.num_send_timers_up--;
if (stcb->asoc.num_send_timers_up < 0) {
stcb->asoc.num_send_timers_up = 0;
}
if (sctp_t3rxt_timer(inp, stcb, net)) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 1);
if ((stcb->asoc.num_send_timers_up == 0) &&
(stcb->asoc.sent_queue_cnt > 0)
) {
struct sctp_tmit_chunk *chk;
sctp_pegs[SCTP_T3_SAFEGRD]++;
chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb,
chk->whoTo);
}
break;
case SCTP_TIMER_TYPE_INIT:
if (sctp_t1init_timer(inp, stcb, net)) {
goto out_decr;
}
did_output = 0;
break;
case SCTP_TIMER_TYPE_RECV:
sctp_pegs[SCTP_RECV_TIMER]++;
sctp_send_sack(stcb);
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 4);
break;
case SCTP_TIMER_TYPE_SHUTDOWN:
if (sctp_shutdown_timer(inp, stcb, net) ) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 5);
break;
case SCTP_TIMER_TYPE_HEARTBEAT:
if (sctp_heartbeat_timer(inp, stcb, net)) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 6);
break;
case SCTP_TIMER_TYPE_COOKIE:
if (sctp_cookie_timer(inp, stcb, net)) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 1);
break;
case SCTP_TIMER_TYPE_NEWCOOKIE:
{
struct timeval tv;
int i, secret;
SCTP_GETTIME_TIMEVAL(&tv);
SCTP_INP_WLOCK(inp);
inp->sctp_ep.time_of_secret_change = tv.tv_sec;
inp->sctp_ep.last_secret_number =
inp->sctp_ep.current_secret_number;
inp->sctp_ep.current_secret_number++;
if (inp->sctp_ep.current_secret_number >=
SCTP_HOW_MANY_SECRETS) {
inp->sctp_ep.current_secret_number = 0;
}
secret = (int)inp->sctp_ep.current_secret_number;
for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) {
inp->sctp_ep.secret_key[secret][i] =
sctp_select_initial_TSN(&inp->sctp_ep);
}
SCTP_INP_WUNLOCK(inp);
sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, stcb, net);
}
did_output = 0;
break;
case SCTP_TIMER_TYPE_PATHMTURAISE:
sctp_pathmtu_timer(inp, stcb, net);
did_output = 0;
break;
case SCTP_TIMER_TYPE_SHUTDOWNACK:
if (sctp_shutdownack_timer(inp, stcb, net)) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 7);
break;
case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
sctp_abort_an_association(inp, stcb,
SCTP_SHUTDOWN_GUARD_EXPIRES, NULL);
goto out_decr;
break;
case SCTP_TIMER_TYPE_STRRESET:
if (sctp_strreset_timer(inp, stcb, net)) {
goto out_decr;
}
sctp_chunk_output(inp, stcb, 9);
break;
case SCTP_TIMER_TYPE_ASCONF:
if (sctp_asconf_timer(inp, stcb, net)) {
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, 8);
break;
case SCTP_TIMER_TYPE_AUTOCLOSE:
sctp_autoclose_timer(inp, stcb, net);
sctp_chunk_output(inp, stcb, 10);
did_output = 0;
break;
case SCTP_TIMER_TYPE_INPKILL:
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
sctp_timer_stop(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL);
sctp_inpcb_free(inp, 1);
goto out_no_decr;
break;
default:
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("sctp_timeout_handler:unknown timer %d\n",
tmr->type);
}
#endif
break;
};
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xF1, (u_int8_t)tmr->type);
sctp_auditing(5, inp, stcb, net);
#endif
if (did_output) {
sctp_fix_ecn_echo(&stcb->asoc);
}
if (stcb) {
SCTP_TCB_UNLOCK(stcb);
}
out_decr:
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
out_no_decr:
mutex_exit(softnet_lock);
}
int
sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
int to_ticks;
struct sctp_timer *tmr;
if (inp == NULL)
return (EFAULT);
to_ticks = 0;
tmr = NULL;
switch (t_type) {
case SCTP_TIMER_TYPE_ITERATOR:
{
struct sctp_iterator *it;
it = (struct sctp_iterator *)inp;
tmr = &it->tmr;
to_ticks = SCTP_ITERATOR_TICKS;
}
break;
case SCTP_TIMER_TYPE_SEND:
{
int rto_val;
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
rto_val = stcb->asoc.initial_rto;
} else {
rto_val = net->RTO;
}
to_ticks = MSEC_TO_TICKS(rto_val);
}
break;
case SCTP_TIMER_TYPE_INIT:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
break;
case SCTP_TIMER_TYPE_RECV:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.dack_timer;
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV];
break;
case SCTP_TIMER_TYPE_SHUTDOWN:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_HEARTBEAT:
if (stcb == NULL) {
return (EFAULT);
}
{
uint32_t rndval;
uint8_t this_random;
int cnt_of_unconf=0;
struct sctp_nets *lnet;
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
if (lnet->dest_state & SCTP_ADDR_UNCONFIRMED) {
cnt_of_unconf++;
}
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("HB timer to start unconfirmed:%d hb_delay:%d\n",
cnt_of_unconf, stcb->asoc.heart_beat_delay);
}
#endif
if (stcb->asoc.hb_random_idx > 3) {
rndval = sctp_select_initial_TSN(&inp->sctp_ep);
memcpy(stcb->asoc.hb_random_values, &rndval,
sizeof(stcb->asoc.hb_random_values));
this_random = stcb->asoc.hb_random_values[0];
stcb->asoc.hb_random_idx = 0;
stcb->asoc.hb_ect_randombit = 0;
} else {
this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx];
stcb->asoc.hb_random_idx++;
stcb->asoc.hb_ect_randombit = 0;
}
if ((stcb->asoc.heart_beat_delay == 0) &&
(cnt_of_unconf == 0)) {
return (0);
}
if (net) {
int delay;
delay = stcb->asoc.heart_beat_delay;
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) &&
((lnet->dest_state & SCTP_ADDR_OUT_OF_SCOPE) == 0) &&
(lnet->dest_state & SCTP_ADDR_REACHABLE)) {
delay = 0;
}
}
if (net->RTO == 0) {
to_ticks = this_random + stcb->asoc.initial_rto + delay;
} else {
to_ticks = delay + net->RTO + this_random;
}
} else {
if (cnt_of_unconf) {
to_ticks = this_random + stcb->asoc.initial_rto;
} else {
to_ticks = stcb->asoc.heart_beat_delay + this_random + stcb->asoc.initial_rto;
}
}
to_ticks *= hz;
to_ticks /= 1000;
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("Timer to expire in %d ticks\n", to_ticks);
}
#endif
tmr = &stcb->asoc.hb_timer;
}
break;
case SCTP_TIMER_TYPE_COOKIE:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_NEWCOOKIE:
tmr = &inp->sctp_ep.signature_change;
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_SIGNATURE];
break;
case SCTP_TIMER_TYPE_INPKILL:
tmr = &inp->sctp_ep.signature_change;
to_ticks = (SCTP_INP_KILL_TIMEOUT * hz) / 1000;
break;
case SCTP_TIMER_TYPE_PATHMTURAISE:
if (stcb == NULL) {
return (EFAULT);
}
if (net == NULL) {
return (EFAULT);
}
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_PMTU];
tmr = &net->pmtu_timer;
break;
case SCTP_TIMER_TYPE_SHUTDOWNACK:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
if (stcb == NULL) {
return (EFAULT);
}
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN];
tmr = &stcb->asoc.shut_guard_timer;
break;
case SCTP_TIMER_TYPE_STRRESET:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
tmr = &stcb->asoc.strreset_timer;
break;
case SCTP_TIMER_TYPE_ASCONF:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
if (net->RTO == 0) {
to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
} else {
to_ticks = MSEC_TO_TICKS(net->RTO);
}
tmr = &stcb->asoc.asconf_timer;
break;
case SCTP_TIMER_TYPE_AUTOCLOSE:
if (stcb == NULL) {
return (EFAULT);
}
if (stcb->asoc.sctp_autoclose_ticks == 0) {
return (0);
}
to_ticks = stcb->asoc.sctp_autoclose_ticks;
tmr = &stcb->asoc.autoclose_timer;
break;
default:
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("sctp_timer_start:Unknown timer type %d\n",
t_type);
}
#endif
return (EFAULT);
break;
};
if ((to_ticks <= 0) || (tmr == NULL)) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("sctp_timer_start:%d:software error to_ticks:%d tmr:%p not set ??\n",
t_type, to_ticks, tmr);
}
#endif
return (EFAULT);
}
if (callout_pending(&tmr->timer)) {
return (EALREADY);
}
if (t_type == SCTP_TIMER_TYPE_SEND) {
stcb->asoc.num_send_timers_up++;
}
tmr->type = t_type;
tmr->ep = (void *)inp;
tmr->tcb = (void *)stcb;
tmr->net = (void *)net;
callout_reset(&tmr->timer, to_ticks, sctp_timeout_handler, tmr);
return (0);
}
int
sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
struct sctp_timer *tmr;
if (inp == NULL)
return (EFAULT);
tmr = NULL;
switch (t_type) {
case SCTP_TIMER_TYPE_ITERATOR:
{
struct sctp_iterator *it;
it = (struct sctp_iterator *)inp;
tmr = &it->tmr;
}
break;
case SCTP_TIMER_TYPE_SEND:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_INIT:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_RECV:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.dack_timer;
break;
case SCTP_TIMER_TYPE_SHUTDOWN:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_HEARTBEAT:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.hb_timer;
break;
case SCTP_TIMER_TYPE_COOKIE:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_NEWCOOKIE:
tmr = &inp->sctp_ep.signature_change;
break;
case SCTP_TIMER_TYPE_INPKILL:
tmr = &inp->sctp_ep.signature_change;
break;
case SCTP_TIMER_TYPE_PATHMTURAISE:
if (stcb == NULL) {
return (EFAULT);
}
if (net == NULL) {
return (EFAULT);
}
tmr = &net->pmtu_timer;
break;
case SCTP_TIMER_TYPE_SHUTDOWNACK:
if ((stcb == NULL) || (net == NULL)) {
return (EFAULT);
}
tmr = &net->rxt_timer;
break;
case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.shut_guard_timer;
break;
case SCTP_TIMER_TYPE_STRRESET:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.strreset_timer;
break;
case SCTP_TIMER_TYPE_ASCONF:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.asconf_timer;
break;
case SCTP_TIMER_TYPE_AUTOCLOSE:
if (stcb == NULL) {
return (EFAULT);
}
tmr = &stcb->asoc.autoclose_timer;
break;
default:
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
printf("sctp_timer_stop:Unknown timer type %d\n",
t_type);
}
#endif
break;
};
if (tmr == NULL)
return (EFAULT);
if ((tmr->type != t_type) && tmr->type) {
return (0);
}
if (t_type == SCTP_TIMER_TYPE_SEND) {
stcb->asoc.num_send_timers_up--;
if (stcb->asoc.num_send_timers_up < 0) {
stcb->asoc.num_send_timers_up = 0;
}
}
callout_stop(&tmr->timer);
return (0);
}
u_int32_t
sctp_calculate_len(struct mbuf *m)
{
u_int32_t tlen=0;
struct mbuf *at;
at = m;
while (at) {
tlen += at->m_len;
at = at->m_next;
}
return (tlen);
}
uint32_t
sctp_calculate_sum(struct mbuf *m, int32_t *pktlen, uint32_t offset)
{
int32_t tlen=0;
uint32_t base = 0xffffffff;
struct mbuf *at;
at = m;
while ((at != NULL) && (offset > (uint32_t)at->m_len)) {
offset -= at->m_len;
at = at->m_next;
}
while (at != NULL) {
base = update_crc32(base, at->m_data + offset,
at->m_len - offset);
tlen += at->m_len - offset;
if (offset) {
offset = 0;
}
at = at->m_next;
}
if (pktlen != NULL) {
*pktlen = tlen;
}
base = sctp_csum_finalize(base);
return (base);
}
void
sctp_mtu_size_reset(struct sctp_inpcb *inp,
struct sctp_association *asoc, u_long mtu)
{
struct sctp_tmit_chunk *chk;
struct sctp_stream_out *strm;
unsigned int eff_mtu, ovh;
asoc->smallest_mtu = mtu;
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
ovh = SCTP_MIN_OVERHEAD;
} else {
ovh = SCTP_MIN_V4_OVERHEAD;
}
eff_mtu = mtu - ovh;
TAILQ_FOREACH(strm, &asoc->out_wheel, next_spoke) {
TAILQ_FOREACH(chk, &strm->outqueue, sctp_next) {
if (chk->send_size > eff_mtu) {
chk->flags &= SCTP_DONT_FRAGMENT;
chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
}
}
}
TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
if (chk->send_size > eff_mtu) {
chk->flags &= SCTP_DONT_FRAGMENT;
chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
}
}
TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
if (chk->send_size > eff_mtu) {
chk->flags &= SCTP_DONT_FRAGMENT;
chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
}
}
}
u_int32_t
sctp_calculate_rto(struct sctp_tcb *stcb,
struct sctp_association *asoc,
struct sctp_nets *net,
struct timeval *old)
{
int calc_time = 0;
unsigned int new_rto = 0;
int first_measure = 0;
struct timeval now;
SCTP_GETTIME_TIMEVAL(&now);
if ((u_long)now.tv_sec > (u_long)old->tv_sec) {
calc_time = ((u_long)now.tv_sec - (u_long)old->tv_sec) * 1000;
if ((u_long)now.tv_usec > (u_long)old->tv_usec) {
calc_time += (((u_long)now.tv_usec -
(u_long)old->tv_usec)/1000);
} else if ((u_long)now.tv_usec < (u_long)old->tv_usec) {
calc_time -= 1000;
calc_time += ((int)now.tv_usec/1000);
calc_time += (((int)1000000 - (int)old->tv_usec)/1000);
}
} else if ((u_long)now.tv_sec == (u_long)old->tv_sec) {
if ((u_long)now.tv_usec > (u_long)old->tv_usec) {
calc_time = ((u_long)now.tv_usec -
(u_long)old->tv_usec)/1000;
} else if ((u_long)now.tv_usec < (u_long)old->tv_usec) {
return (((net->lastsa >> 2) + net->lastsv) >> 1);
} else {
return (((net->lastsa >> 2) + net->lastsv) >> 1);
}
} else {
return (((net->lastsa >> 2) + net->lastsv) >> 1);
}
#if 0
#endif
if (net->RTO) {
calc_time -= (net->lastsa >> 3);
net->lastsa += calc_time;
if (calc_time < 0) {
calc_time = -calc_time;
}
calc_time -= (net->lastsv >> 2);
net->lastsv += calc_time;
if (net->lastsv == 0) {
net->lastsv = SCTP_CLOCK_GRANULARITY;
}
} else {
net->lastsa = calc_time;
net->lastsv = calc_time >> 1;
first_measure = 1;
}
new_rto = ((net->lastsa >> 2) + net->lastsv) >> 1;
if ((new_rto > SCTP_SAT_NETWORK_MIN) &&
(stcb->asoc.sat_network_lockout == 0)) {
stcb->asoc.sat_network = 1;
} else if ((!first_measure) && stcb->asoc.sat_network) {
stcb->asoc.sat_network = 0;
stcb->asoc.sat_network_lockout = 1;
}
if (new_rto < stcb->asoc.minrto) {
new_rto = stcb->asoc.minrto;
}
if (new_rto > stcb->asoc.maxrto) {
new_rto = stcb->asoc.maxrto;
}
return ((u_int32_t)new_rto);
}
void *
sctp_m_getptr(struct mbuf *m, int off, int len, u_int8_t *in_ptr)
{
uint32_t count;
uint8_t *ptr;
ptr = in_ptr;
if ((off < 0) || (len <= 0))
return (NULL);
while ((m != NULL) && (off > 0)) {
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
if (m == NULL)
return (NULL);
if ((m->m_len - off) >= len) {
return ((void *)(mtod(m, vaddr_t) + off));
} else {
while ((m != NULL) && (len > 0)) {
count = uimin(m->m_len - off, len);
memcpy(ptr, (void *)(mtod(m, vaddr_t) + off), count);
len -= count;
ptr += count;
off = 0;
m = m->m_next;
}
if ((m == NULL) && (len > 0))
return (NULL);
else
return ((void *)in_ptr);
}
}
struct sctp_paramhdr *
sctp_get_next_param(struct mbuf *m,
int offset,
struct sctp_paramhdr *pull,
int pull_limit)
{
return ((struct sctp_paramhdr *)sctp_m_getptr(m, offset, pull_limit,
(u_int8_t *)pull));
}
int
sctp_add_pad_tombuf(struct mbuf *m, int padlen)
{
u_int8_t *dp;
int i;
if (padlen > 3) {
return (ENOBUFS);
}
if (M_TRAILINGSPACE(m)) {
dp = (u_int8_t *)(mtod(m, vaddr_t) + m->m_len);
m->m_len += padlen;
} else {
struct mbuf *tmp;
MGET(tmp, M_DONTWAIT, MT_DATA);
if (tmp == NULL) {
return (ENOSPC);
}
tmp->m_next = m->m_next;
tmp->m_len = padlen;
m->m_next = tmp;
dp = mtod(tmp, u_int8_t *);
}
for (i= 0; i < padlen; i++) {
*dp = 0;
dp++;
}
return (0);
}
int
sctp_pad_lastmbuf(struct mbuf *m, int padval)
{
struct mbuf *m_at;
m_at = m;
while (m_at) {
if (m_at->m_next == NULL) {
return (sctp_add_pad_tombuf(m_at, padval));
}
m_at = m_at->m_next;
}
return (EFAULT);
}
static void
sctp_notify_assoc_change(u_int32_t event, struct sctp_tcb *stcb,
u_int32_t error)
{
struct mbuf *m_notify;
struct sctp_assoc_change *sac;
const struct sockaddr *to;
struct sockaddr_in6 sin6, lsa6;
#ifdef SCTP_DEBUG
printf("notify: %d\n", event);
#endif
if ((event == SCTP_SHUTDOWN_COMP) || (event == SCTP_COMM_LOST)) {
sctp_deliver_data(stcb, &stcb->asoc, NULL, 0);
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
(event == SCTP_COMM_LOST)) {
stcb->sctp_socket->so_error = ECONNRESET;
sowwakeup(stcb->sctp_socket);
sorwakeup(stcb->sctp_socket);
}
#if 0
if ((event == SCTP_COMM_UP) &&
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
soisconnected(stcb->sctp_socket);
}
#endif
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVASSOCEVNT)) {
return;
}
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
sac = mtod(m_notify, struct sctp_assoc_change *);
sac->sac_type = SCTP_ASSOC_CHANGE;
sac->sac_flags = 0;
sac->sac_length = sizeof(struct sctp_assoc_change);
sac->sac_state = event;
sac->sac_error = error;
sac->sac_outbound_streams = stcb->asoc.streamoutcnt;
sac->sac_inbound_streams = stcb->asoc.streamincnt;
sac->sac_assoc_id = sctp_get_associd(stcb);
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = sizeof(struct sctp_assoc_change);
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_assoc_change);
m_notify->m_next = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
to->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = (const struct sockaddr_in *)to;
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv,
to, m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_peer_addr_change(struct sctp_tcb *stcb, uint32_t state,
const struct sockaddr *sa, uint32_t error)
{
struct mbuf *m_notify;
struct sctp_paddr_change *spc;
const struct sockaddr *to;
struct sockaddr_in6 sin6, lsa6;
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVPADDREVNT))
return;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
MCLGET(m_notify, M_DONTWAIT);
if ((m_notify->m_flags & M_EXT) != M_EXT) {
sctp_m_freem(m_notify);
return;
}
spc = mtod(m_notify, struct sctp_paddr_change *);
spc->spc_type = SCTP_PEER_ADDR_CHANGE;
spc->spc_flags = 0;
spc->spc_length = sizeof(struct sctp_paddr_change);
if (sa->sa_family == AF_INET) {
memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in));
} else {
memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in6));
}
spc->spc_state = state;
spc->spc_error = error;
spc->spc_assoc_id = sctp_get_associd(stcb);
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = sizeof(struct sctp_paddr_change);
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_paddr_change);
m_notify->m_next = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
to->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = (const struct sockaddr_in *)to;
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_send_failed(struct sctp_tcb *stcb, u_int32_t error,
struct sctp_tmit_chunk *chk)
{
struct mbuf *m_notify;
struct sctp_send_failed *ssf;
struct sockaddr_in6 sin6, lsa6;
const struct sockaddr *to;
int length;
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVSENDFAILEVNT))
return;
length = sizeof(struct sctp_send_failed) + chk->send_size;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
ssf = mtod(m_notify, struct sctp_send_failed *);
ssf->ssf_type = SCTP_SEND_FAILED;
if (error == SCTP_NOTIFY_DATAGRAM_UNSENT)
ssf->ssf_flags = SCTP_DATA_UNSENT;
else
ssf->ssf_flags = SCTP_DATA_SENT;
ssf->ssf_length = length;
ssf->ssf_error = error;
ssf->ssf_info.sinfo_stream = chk->rec.data.stream_number;
ssf->ssf_info.sinfo_ssn = chk->rec.data.stream_seq;
ssf->ssf_info.sinfo_flags = chk->rec.data.rcv_flags;
ssf->ssf_info.sinfo_ppid = chk->rec.data.payloadtype;
ssf->ssf_info.sinfo_context = chk->rec.data.context;
ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
ssf->ssf_assoc_id = sctp_get_associd(stcb);
m_notify->m_next = chk->data;
if (m_notify->m_next == NULL)
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
else {
struct mbuf *m;
m_notify->m_flags |= M_NOTIFICATION;
m = m_notify;
while (m->m_next != NULL)
m = m->m_next;
m->m_flags |= M_EOR;
}
m_notify->m_pkthdr.len = length;
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_send_failed);
chk->data = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
to->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = satocsin(to);
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_adaption_layer(struct sctp_tcb *stcb,
u_int32_t error)
{
struct mbuf *m_notify;
struct sctp_adaption_event *sai;
struct sockaddr_in6 sin6, lsa6;
const struct sockaddr *to;
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ADAPTIONEVNT))
return;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
sai = mtod(m_notify, struct sctp_adaption_event *);
sai->sai_type = SCTP_ADAPTION_INDICATION;
sai->sai_flags = 0;
sai->sai_length = sizeof(struct sctp_adaption_event);
sai->sai_adaption_ind = error;
sai->sai_assoc_id = sctp_get_associd(stcb);
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = sizeof(struct sctp_adaption_event);
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_adaption_event);
m_notify->m_next = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
(to->sa_family == AF_INET)) {
const struct sockaddr_in *sin;
sin = satocsin(to);
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb,
u_int32_t error)
{
struct mbuf *m_notify;
struct sctp_pdapi_event *pdapi;
struct sockaddr_in6 sin6, lsa6;
const struct sockaddr *to;
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_PDAPIEVNT))
return;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
pdapi = mtod(m_notify, struct sctp_pdapi_event *);
pdapi->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
pdapi->pdapi_flags = 0;
pdapi->pdapi_length = sizeof(struct sctp_pdapi_event);
pdapi->pdapi_indication = error;
pdapi->pdapi_assoc_id = sctp_get_associd(stcb);
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = sizeof(struct sctp_pdapi_event);
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_pdapi_event);
m_notify->m_next = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
(to->sa_family == AF_INET)) {
const struct sockaddr_in *sin;
sin = satocsin(to);
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_shutdown_event(struct sctp_tcb *stcb)
{
struct mbuf *m_notify;
struct sctp_shutdown_event *sse;
struct sockaddr_in6 sin6, lsa6;
const struct sockaddr *to;
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
socantrcvmore(stcb->sctp_socket);
socantsendmore(stcb->sctp_socket);
}
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT))
return;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
sse = mtod(m_notify, struct sctp_shutdown_event *);
sse->sse_type = SCTP_SHUTDOWN_EVENT;
sse->sse_flags = 0;
sse->sse_length = sizeof(struct sctp_shutdown_event);
sse->sse_assoc_id = sctp_get_associd(stcb);
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = sizeof(struct sctp_shutdown_event);
m_reset_rcvif(m_notify);
m_notify->m_len = sizeof(struct sctp_shutdown_event);
m_notify->m_next = NULL;
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
to->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = satocsin(to);
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *)sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
static void
sctp_notify_stream_reset(struct sctp_tcb *stcb,
int number_entries, uint16_t *list, int flag)
{
struct mbuf *m_notify;
struct sctp_stream_reset_event *strreset;
struct sockaddr_in6 sin6, lsa6;
const struct sockaddr *to;
int len;
if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_STREAM_RESETEVNT))
return;
MGETHDR(m_notify, M_DONTWAIT, MT_DATA);
if (m_notify == NULL)
return;
m_notify->m_len = 0;
len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t));
if (len > M_TRAILINGSPACE(m_notify)) {
MCLGET(m_notify, M_WAIT);
}
if (m_notify == NULL)
return;
if (len > M_TRAILINGSPACE(m_notify)) {
m_freem(m_notify);
return;
}
strreset = mtod(m_notify, struct sctp_stream_reset_event *);
strreset->strreset_type = SCTP_STREAM_RESET_EVENT;
if (number_entries == 0) {
strreset->strreset_flags = flag | SCTP_STRRESET_ALL_STREAMS;
} else {
strreset->strreset_flags = flag | SCTP_STRRESET_STREAM_LIST;
}
strreset->strreset_length = len;
strreset->strreset_assoc_id = sctp_get_associd(stcb);
if (number_entries) {
int i;
for (i=0; i<number_entries; i++) {
strreset->strreset_list[i] = list[i];
}
}
m_notify->m_flags |= M_EOR | M_NOTIFICATION;
m_notify->m_pkthdr.len = len;
m_reset_rcvif(m_notify);
m_notify->m_len = len;
m_notify->m_next = NULL;
if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
sctp_m_freem(m_notify);
return;
}
to = rtcache_getdst(&stcb->asoc.primary_destination->ro);
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
to->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = satocsin(to);
in6_sin_2_v4mapsin6(sin, &sin6);
to = (struct sockaddr *)&sin6;
}
to = (const struct sockaddr *) sctp_recover_scope((const struct sockaddr_in6 *)to,
&lsa6);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
sctp_m_freem(m_notify);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
return;
}
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
} else {
stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
}
SCTP_INP_WUNLOCK(stcb->sctp_ep);
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
void
sctp_ulp_notify(u_int32_t notification, struct sctp_tcb *stcb,
u_int32_t error, void *data)
{
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
return;
}
if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
return;
}
switch (notification) {
case SCTP_NOTIFY_ASSOC_UP:
sctp_notify_assoc_change(SCTP_COMM_UP, stcb, error);
break;
case SCTP_NOTIFY_ASSOC_DOWN:
sctp_notify_assoc_change(SCTP_SHUTDOWN_COMP, stcb, error);
break;
case SCTP_NOTIFY_INTERFACE_DOWN:
{
struct sctp_nets *net;
net = (struct sctp_nets *)data;
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_UNREACHABLE,
rtcache_getdst(&net->ro), error);
break;
}
case SCTP_NOTIFY_INTERFACE_UP:
{
struct sctp_nets *net;
net = (struct sctp_nets *)data;
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_AVAILABLE,
rtcache_getdst(&net->ro), error);
break;
}
case SCTP_NOTIFY_INTERFACE_CONFIRMED:
{
struct sctp_nets *net;
net = (struct sctp_nets *)data;
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_CONFIRMED,
rtcache_getdst(&net->ro), error);
break;
}
case SCTP_NOTIFY_DG_FAIL:
sctp_notify_send_failed(stcb, error,
(struct sctp_tmit_chunk *)data);
break;
case SCTP_NOTIFY_ADAPTION_INDICATION:
sctp_notify_adaption_layer(stcb, error);
break;
case SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION:
sctp_notify_partial_delivery_indication(stcb, error);
break;
case SCTP_NOTIFY_STRDATA_ERR:
break;
case SCTP_NOTIFY_ASSOC_ABORTED:
sctp_notify_assoc_change(SCTP_COMM_LOST, stcb, error);
break;
case SCTP_NOTIFY_PEER_OPENED_STREAM:
break;
case SCTP_NOTIFY_STREAM_OPENED_OK:
break;
case SCTP_NOTIFY_ASSOC_RESTART:
sctp_notify_assoc_change(SCTP_RESTART, stcb, error);
break;
case SCTP_NOTIFY_HB_RESP:
break;
case SCTP_NOTIFY_STR_RESET_SEND:
sctp_notify_stream_reset(stcb, error, ((uint16_t *)data), SCTP_STRRESET_OUTBOUND_STR);
break;
case SCTP_NOTIFY_STR_RESET_RECV:
sctp_notify_stream_reset(stcb, error, ((uint16_t *)data), SCTP_STRRESET_INBOUND_STR);
break;
case SCTP_NOTIFY_ASCONF_ADD_IP:
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_ADDED, data,
error);
break;
case SCTP_NOTIFY_ASCONF_DELETE_IP:
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_REMOVED, data,
error);
break;
case SCTP_NOTIFY_ASCONF_SET_PRIMARY:
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_MADE_PRIM, data,
error);
break;
case SCTP_NOTIFY_ASCONF_SUCCESS:
break;
case SCTP_NOTIFY_ASCONF_FAILED:
break;
case SCTP_NOTIFY_PEER_SHUTDOWN:
sctp_notify_shutdown_event(stcb);
break;
default:
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_UTIL1) {
printf("NOTIFY: unknown notification %xh (%u)\n",
notification, notification);
}
#endif
break;
}
}
void
sctp_report_all_outbound(struct sctp_tcb *stcb)
{
struct sctp_association *asoc;
struct sctp_stream_out *outs;
struct sctp_tmit_chunk *chk;
asoc = &stcb->asoc;
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
return;
}
TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
chk = TAILQ_FIRST(&outs->outqueue);
while (chk) {
stcb->asoc.stream_queue_cnt--;
TAILQ_REMOVE(&outs->outqueue, chk, sctp_next);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
SCTP_NOTIFY_DATAGRAM_UNSENT, chk);
sctp_m_freem(chk->data);
chk->data = NULL;
if (chk->whoTo)
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
chk->asoc = NULL;
SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
sctppcbinfo.ipi_count_chunk--;
if ((int)sctppcbinfo.ipi_count_chunk < 0) {
panic("Chunk count is negative");
}
sctppcbinfo.ipi_gencnt_chunk++;
chk = TAILQ_FIRST(&outs->outqueue);
}
}
if (!TAILQ_EMPTY(&asoc->send_queue)) {
chk = TAILQ_FIRST(&asoc->send_queue);
while (chk) {
TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, chk);
sctp_m_freem(chk->data);
chk->data = NULL;
if (chk->whoTo)
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
sctppcbinfo.ipi_count_chunk--;
if ((int)sctppcbinfo.ipi_count_chunk < 0) {
panic("Chunk count is negative");
}
sctppcbinfo.ipi_gencnt_chunk++;
chk = TAILQ_FIRST(&asoc->send_queue);
}
}
if (!TAILQ_EMPTY(&asoc->sent_queue)) {
chk = TAILQ_FIRST(&asoc->sent_queue);
while (chk) {
TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
SCTP_NOTIFY_DATAGRAM_SENT, chk);
sctp_m_freem(chk->data);
chk->data = NULL;
if (chk->whoTo)
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
sctppcbinfo.ipi_count_chunk--;
if ((int)sctppcbinfo.ipi_count_chunk < 0) {
panic("Chunk count is negative");
}
sctppcbinfo.ipi_gencnt_chunk++;
chk = TAILQ_FIRST(&asoc->sent_queue);
}
}
}
void
sctp_abort_notification(struct sctp_tcb *stcb, int error)
{
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
return;
}
sctp_report_all_outbound(stcb);
sctp_ulp_notify(SCTP_NOTIFY_ASSOC_ABORTED, stcb, error, NULL);
}
void
sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err)
{
u_int32_t vtag;
vtag = 0;
if (stcb != NULL) {
vtag = stcb->asoc.peer_vtag;
sctp_abort_notification(stcb, 0);
}
sctp_send_abort(m, iphlen, sh, vtag, op_err);
if (stcb != NULL) {
sctp_free_assoc(inp, stcb);
} else {
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
sctp_inpcb_free(inp, 1);
}
}
}
}
void
sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
int error, struct mbuf *op_err)
{
if (stcb == NULL) {
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
sctp_inpcb_free(inp, 1);
}
}
return;
}
if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0)
sctp_abort_notification(stcb, error);
sctp_send_abort_tcb(stcb, op_err);
sctp_free_assoc(inp, stcb);
}
void
sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
struct sctp_inpcb *inp, struct mbuf *op_err)
{
struct sctp_chunkhdr *ch, chunk_buf;
unsigned int chk_length;
if (inp && (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
sctp_inpcb_free(inp, 1);
}
}
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
sizeof(*ch), (u_int8_t *)&chunk_buf);
while (ch != NULL) {
chk_length = ntohs(ch->chunk_length);
if (chk_length < sizeof(*ch)) {
break;
}
switch (ch->chunk_type) {
case SCTP_PACKET_DROPPED:
return;
case SCTP_ABORT_ASSOCIATION:
return;
case SCTP_SHUTDOWN_COMPLETE:
return;
case SCTP_SHUTDOWN_ACK:
sctp_send_shutdown_complete2(m, iphlen, sh);
return;
default:
break;
}
offset += SCTP_SIZE32(chk_length);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
sizeof(*ch), (u_int8_t *)&chunk_buf);
}
sctp_send_abort(m, iphlen, sh, 0, op_err);
}
int
sctp_is_there_an_abort_here(struct mbuf *m, int iphlen, int *vtagfill)
{
struct sctp_chunkhdr *ch;
struct sctp_init_chunk *init_chk, chunk_buf;
int offset;
unsigned int chk_length;
offset = iphlen + sizeof(struct sctphdr);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch),
(u_int8_t *)&chunk_buf);
while (ch != NULL) {
chk_length = ntohs(ch->chunk_length);
if (chk_length < sizeof(*ch)) {
break;
}
if (ch->chunk_type == SCTP_ABORT_ASSOCIATION) {
return (1);
}
if (ch->chunk_type == SCTP_INITIATION) {
init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
offset, sizeof(*init_chk), (u_int8_t *)&chunk_buf);
if (init_chk != NULL) {
*vtagfill = ntohl(init_chk->init.initiate_tag);
}
}
offset += SCTP_SIZE32(chk_length);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
sizeof(*ch), (u_int8_t *)&chunk_buf);
}
return (0);
}
uint32_t
sctp_is_same_scope(const struct sockaddr_in6 *addr1, const struct sockaddr_in6 *addr2)
{
struct sockaddr_in6 a, b;
a = *addr1;
b = *addr2;
if (a.sin6_scope_id == 0)
if (sa6_recoverscope(&a)) {
return (0);
}
if (b.sin6_scope_id == 0)
if (sa6_recoverscope(&b)) {
return (0);
}
if (a.sin6_scope_id != b.sin6_scope_id)
return (0);
return (1);
}
const struct sockaddr_in6 *
sctp_recover_scope(const struct sockaddr_in6 *addr, struct sockaddr_in6 *store)
{
const struct sockaddr_in6 *newaddr;
newaddr = addr;
if (addr->sin6_family == AF_INET6) {
if (IN6_IS_SCOPE_LINKLOCAL(&addr->sin6_addr)) {
if (addr->sin6_scope_id == 0) {
*store = *addr;
if (sa6_recoverscope(store) == 0) {
newaddr = store;
}
}
}
}
return (newaddr);
}
int
sctp_cmpaddr(const struct sockaddr *sa1, const struct sockaddr *sa2)
{
if (sa1 == NULL || sa2 == NULL)
return (0);
if (sa1->sa_family != sa2->sa_family)
return (0);
if (sa1->sa_family == AF_INET6) {
const struct sockaddr_in6 *sin6_1, *sin6_2;
sin6_1 = (const struct sockaddr_in6 *)sa1;
sin6_2 = (const struct sockaddr_in6 *)sa2;
return (SCTP6_ARE_ADDR_EQUAL(&sin6_1->sin6_addr,
&sin6_2->sin6_addr));
} else if (sa1->sa_family == AF_INET) {
const struct sockaddr_in *sin_1, *sin_2;
sin_1 = (const struct sockaddr_in *)sa1;
sin_2 = (const struct sockaddr_in *)sa2;
return (sin_1->sin_addr.s_addr == sin_2->sin_addr.s_addr);
} else {
return (0);
}
}
void
sctp_print_address(const struct sockaddr *sa)
{
char ip6buf[INET6_ADDRSTRLEN];
if (sa->sa_family == AF_INET6) {
const struct sockaddr_in6 *sin6;
sin6 = (const struct sockaddr_in6 *)sa;
printf("IPv6 address: %s:%d scope:%u\n",
IN6_PRINT(ip6buf, &sin6->sin6_addr), ntohs(sin6->sin6_port),
sin6->sin6_scope_id);
} else if (sa->sa_family == AF_INET) {
const struct sockaddr_in *sin;
sin = (const struct sockaddr_in *)sa;
printf("IPv4 address: %s:%d\n", inet_ntoa(sin->sin_addr),
ntohs(sin->sin_port));
} else {
printf("?\n");
}
}
void
sctp_print_address_pkt(struct ip *iph, struct sctphdr *sh)
{
if (iph->ip_v == IPVERSION) {
struct sockaddr_in lsa, fsa;
memset(&lsa, 0, sizeof(lsa));
lsa.sin_len = sizeof(lsa);
lsa.sin_family = AF_INET;
lsa.sin_addr = iph->ip_src;
lsa.sin_port = sh->src_port;
memset(&fsa, 0, sizeof(fsa));
fsa.sin_len = sizeof(fsa);
fsa.sin_family = AF_INET;
fsa.sin_addr = iph->ip_dst;
fsa.sin_port = sh->dest_port;
printf("src: ");
sctp_print_address((struct sockaddr *)&lsa);
printf("dest: ");
sctp_print_address((struct sockaddr *)&fsa);
} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
struct ip6_hdr *ip6;
struct sockaddr_in6 lsa6, fsa6;
ip6 = (struct ip6_hdr *)iph;
memset(&lsa6, 0, sizeof(lsa6));
lsa6.sin6_len = sizeof(lsa6);
lsa6.sin6_family = AF_INET6;
lsa6.sin6_addr = ip6->ip6_src;
lsa6.sin6_port = sh->src_port;
memset(&fsa6, 0, sizeof(fsa6));
fsa6.sin6_len = sizeof(fsa6);
fsa6.sin6_family = AF_INET6;
fsa6.sin6_addr = ip6->ip6_dst;
fsa6.sin6_port = sh->dest_port;
printf("src: ");
sctp_print_address((struct sockaddr *)&lsa6);
printf("dest: ");
sctp_print_address((struct sockaddr *)&fsa6);
}
}
#if defined(__FreeBSD__) || defined(__APPLE__)
#define SCTP_SBLINKRECORD(sb, m0) do { \
if ((sb)->sb_lastrecord != NULL) \
(sb)->sb_lastrecord->m_nextpkt = (m0); \
else \
(sb)->sb_mb = (m0); \
(sb)->sb_lastrecord = (m0); \
} while (0)
#endif
int
sbappendaddr_nocheck(struct sockbuf *sb, const struct sockaddr *asa,
struct mbuf *m0, struct mbuf *control,
u_int32_t tag, struct sctp_inpcb *inp)
{
#ifdef __NetBSD__
struct mbuf *m, *n;
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr_nocheck");
m0->m_pkthdr.csum_data = (int)tag;
for (n = control; n; n = n->m_next) {
if (n->m_next == 0)
break;
}
if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
MGETHDR(m, M_DONTWAIT, MT_SONAME);
if (m == 0)
return (0);
m->m_len = asa->sa_len;
memcpy(mtod(m, void *), (const void *)asa, asa->sa_len);
} else {
m = NULL;
}
if (n) {
n->m_next = m0;
} else {
control = m0;
}
if (m)
m->m_next = control;
else
m = control;
m->m_pkthdr.csum_data = tag;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
if ((n = sb->sb_mb) != NULL) {
if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
inp->sb_last_mpkt = NULL;
}
if (inp->sb_last_mpkt)
inp->sb_last_mpkt->m_nextpkt = m;
else {
while (n->m_nextpkt) {
n = n->m_nextpkt;
}
n->m_nextpkt = m;
}
inp->sb_last_mpkt = m;
} else {
inp->sb_last_mpkt = sb->sb_mb = m;
inp->sctp_vtag_first = tag;
}
return (1);
#endif
#if defined(__FreeBSD__) || defined(__APPLE__)
struct mbuf *m, *n, *nlast;
int cnt=0;
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr_nocheck");
for (n = control; n; n = n->m_next) {
if (n->m_next == 0)
break;
}
if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
if (asa->sa_len > MHLEN)
return (0);
try_again:
MGETHDR(m, M_DONTWAIT, MT_SONAME);
if (m == 0)
return (0);
m->m_len = 0;
if (m == m0) {
printf("Duplicate mbuf allocated %p in and mget returned %p?\n",
m0, m);
if (cnt) {
panic("more than once");
}
cnt++;
goto try_again;
}
m->m_len = asa->sa_len;
bcopy((void *)asa, mtod(m, void *), asa->sa_len);
}
else {
m = NULL;
}
if (n)
n->m_next = m0;
else
control = m0;
if (m)
m->m_next = control;
else
m = control;
m->m_pkthdr.csum_data = (int)tag;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
nlast = n;
if (sb->sb_mb == NULL) {
inp->sctp_vtag_first = tag;
}
#ifdef __FREEBSD__
if (sb->sb_mb == NULL)
inp->sctp_vtag_first = tag;
SCTP_SBLINKRECORD(sb, m);
sb->sb_mbtail = nlast;
#else
if ((n = sb->sb_mb) != NULL) {
if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
inp->sb_last_mpkt = NULL;
}
if (inp->sb_last_mpkt)
inp->sb_last_mpkt->m_nextpkt = m;
else {
while (n->m_nextpkt) {
n = n->m_nextpkt;
}
n->m_nextpkt = m;
}
inp->sb_last_mpkt = m;
} else {
inp->sb_last_mpkt = sb->sb_mb = m;
inp->sctp_vtag_first = tag;
}
#endif
return (1);
#endif
#ifdef __OpenBSD__
struct mbuf *m, *n;
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr_nocheck");
m0->m_pkthdr.csum = (int)tag;
for (n = control; n; n = n->m_next) {
if (n->m_next == 0)
break;
}
if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
if (asa->sa_len > MHLEN)
return (0);
MGETHDR(m, M_DONTWAIT, MT_SONAME);
if (m == 0)
return (0);
m->m_len = asa->sa_len;
bcopy((void *)asa, mtod(m, void *), asa->sa_len);
} else {
m = NULL;
}
if (n)
n->m_next = m0;
else
control = m0;
m->m_pkthdr.csum = (int)tag;
m->m_next = control;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
if ((n = sb->sb_mb) != NULL) {
if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
inp->sb_last_mpkt = NULL;
}
if (inp->sb_last_mpkt)
inp->sb_last_mpkt->m_nextpkt = m;
else {
while (n->m_nextpkt) {
n = n->m_nextpkt;
}
n->m_nextpkt = m;
}
inp->sb_last_mpkt = m;
} else {
inp->sb_last_mpkt = sb->sb_mb = m;
inp->sctp_vtag_first = tag;
}
return (1);
#endif
}
struct mbuf *
sctp_generate_invmanparam(int err)
{
struct mbuf *m;
MGET(m, M_DONTWAIT, MT_DATA);
if (m) {
struct sctp_paramhdr *ph;
m->m_len = sizeof(struct sctp_paramhdr);
ph = mtod(m, struct sctp_paramhdr *);
ph->param_length = htons(sizeof(struct sctp_paramhdr));
ph->param_type = htons(err);
}
return (m);
}
static int
sctp_should_be_moved(struct mbuf *this, struct sctp_association *asoc)
{
struct mbuf *m;
m = this;
while (m) {
if (m->m_flags & M_PKTHDR) {
#if defined(__OpenBSD__)
if ((u_int32_t)m->m_pkthdr.csum == asoc->my_vtag)
#else
if ((u_int32_t)m->m_pkthdr.csum_data == asoc->my_vtag)
#endif
{
return (1);
}
}
m = m->m_next;
}
return (0);
}
u_int32_t
sctp_get_first_vtag_from_sb(struct socket *so)
{
struct mbuf *this, *at;
u_int32_t retval;
retval = 0;
if (so->so_rcv.sb_mb) {
this = so->so_rcv.sb_mb;
while (this) {
at = this;
while (at) {
if (at->m_flags & M_PKTHDR)
break;
else {
at = at->m_next;
}
}
if (at && (at->m_flags & M_PKTHDR)) {
#if defined(__OpenBSD__)
if ((u_int32_t)at->m_pkthdr.csum != 0)
#else
if ((u_int32_t)at->m_pkthdr.csum_data != 0)
#endif
{
#if defined(__OpenBSD__)
retval = (u_int32_t)at->m_pkthdr.csum;
#else
retval =
(u_int32_t)at->m_pkthdr.csum_data;
#endif
break;
}
}
this = this->m_nextpkt;
}
}
return (retval);
}
void
sctp_grub_through_socket_buffer(struct sctp_inpcb *inp, struct socket *old,
struct socket *new, struct sctp_tcb *stcb)
{
struct mbuf **put, **take, *next, *this;
struct sockbuf *old_sb, *new_sb;
struct sctp_association *asoc;
int moved_top = 0;
asoc = &stcb->asoc;
old_sb = &old->so_rcv;
new_sb = &new->so_rcv;
if (old_sb->sb_mb == NULL) {
return;
}
if (inp->sctp_vtag_first == asoc->my_vtag) {
struct mbuf *mm;
for (mm = old_sb->sb_mb; mm; mm = mm->m_next) {
sbfree(old_sb, mm);
sballoc(new_sb, mm);
}
new_sb->sb_mb = old_sb->sb_mb;
old_sb->sb_mb = new_sb->sb_mb->m_nextpkt;
new_sb->sb_mb->m_nextpkt = NULL;
put = &new_sb->sb_mb->m_nextpkt;
moved_top = 1;
} else {
put = &new_sb->sb_mb;
}
take = &old_sb->sb_mb;
next = old_sb->sb_mb;
while (next) {
this = next;
next = this->m_nextpkt;
if (sctp_should_be_moved(this, asoc)) {
struct mbuf *mm;
*take = this->m_nextpkt;
this->m_nextpkt = NULL;
*put = this;
for (mm = this; mm; mm = mm->m_next) {
sbfree(old_sb, mm);
sballoc(new_sb, mm);
}
put = &this->m_nextpkt;
} else {
take = &this->m_nextpkt;
}
}
if (moved_top) {
inp->sctp_vtag_first = sctp_get_first_vtag_from_sb(old);
}
}
void
sctp_free_bufspace(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_tmit_chunk *tp1)
{
if (tp1->data == NULL) {
return;
}
#ifdef SCTP_MBCNT_LOGGING
sctp_log_mbcnt(SCTP_LOG_MBCNT_DECREASE,
asoc->total_output_queue_size,
tp1->book_size,
asoc->total_output_mbuf_queue_size,
tp1->mbcnt);
#endif
if (asoc->total_output_queue_size >= tp1->book_size) {
asoc->total_output_queue_size -= tp1->book_size;
} else {
asoc->total_output_queue_size = 0;
}
if (asoc->total_output_mbuf_queue_size >= tp1->mbcnt) {
asoc->total_output_mbuf_queue_size -= tp1->mbcnt;
} else {
asoc->total_output_mbuf_queue_size = 0;
}
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
if (stcb->sctp_socket->so_snd.sb_cc >= tp1->book_size) {
stcb->sctp_socket->so_snd.sb_cc -= tp1->book_size;
} else {
stcb->sctp_socket->so_snd.sb_cc = 0;
}
if (stcb->sctp_socket->so_snd.sb_mbcnt >= tp1->mbcnt) {
stcb->sctp_socket->so_snd.sb_mbcnt -= tp1->mbcnt;
} else {
stcb->sctp_socket->so_snd.sb_mbcnt = 0;
}
}
}
int
sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1,
int reason, struct sctpchunk_listhead *queue)
{
int ret_sz = 0;
int notdone;
uint8_t foundeom = 0;
do {
ret_sz += tp1->book_size;
tp1->sent = SCTP_FORWARD_TSN_SKIP;
if (tp1->data) {
sctp_free_bufspace(stcb, &stcb->asoc, tp1);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1);
sctp_m_freem(tp1->data);
tp1->data = NULL;
sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
}
if (tp1->flags & SCTP_PR_SCTP_BUFFER) {
stcb->asoc.sent_queue_cnt_removeable--;
}
if (queue == &stcb->asoc.send_queue) {
TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next);
TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1,
sctp_next);
stcb->asoc.sent_queue_cnt++;
}
if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) ==
SCTP_DATA_NOT_FRAG) {
notdone = 0;
foundeom = 1;
} else if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
notdone = 0;
foundeom = 1;
} else {
notdone = 1;
tp1 = TAILQ_NEXT(tp1, sctp_next);
}
} while (tp1 && notdone);
if ((foundeom == 0) && (queue == &stcb->asoc.sent_queue)) {
tp1 = TAILQ_FIRST(&stcb->asoc.send_queue);
if (tp1) {
ret_sz += sctp_release_pr_sctp_chunk(stcb, tp1, reason,
&stcb->asoc.send_queue);
} else {
printf("hmm, nothing on the send queue and no EOM?\n");
}
}
return (ret_sz);
}
struct ifaddr *
sctp_find_ifa_by_addr(struct sockaddr *sa)
{
struct ifnet *ifn;
struct ifaddr *ifa;
int s;
s = pserialize_read_enter();
IFNET_READER_FOREACH(ifn) {
IFADDR_READER_FOREACH(ifa, ifn) {
if (ifa->ifa_addr->sa_family != sa->sa_family)
continue;
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin1, *sin2, sin6_tmp;
sin1 = (struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_SCOPE_LINKLOCAL(&sin1->sin6_addr)) {
memcpy(&sin6_tmp, sin1,
sizeof(struct sockaddr_in6));
sin1 = &sin6_tmp;
in6_clearscope(&sin1->sin6_addr);
}
sin2 = (struct sockaddr_in6 *)sa;
if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
sizeof(struct in6_addr)) == 0) {
pserialize_read_exit(s);
return (ifa);
}
} else
#endif
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *sin1, *sin2;
sin1 = (struct sockaddr_in *)ifa->ifa_addr;
sin2 = (struct sockaddr_in *)sa;
if (sin1->sin_addr.s_addr ==
sin2->sin_addr.s_addr) {
pserialize_read_exit(s);
return (ifa);
}
}
}
}
pserialize_read_exit(s);
return (NULL);
}
#ifdef __APPLE__
static void
sctp_pkthdr_fix(struct mbuf *m)
{
struct mbuf *m_nxt;
if ((m->m_flags & M_PKTHDR) == 0) {
return;
}
if (m->m_len != 0) {
return;
}
m_nxt = m->m_next;
if (m_nxt == NULL) {
return;
}
if ((size_t)m_nxt->m_len > sizeof(long)) {
bcopy(mtod(m_nxt, void *), mtod(m, void *), sizeof(long));
m->m_len += sizeof(long);
m_nxt->m_data += sizeof(long);
m_nxt->m_len -= sizeof(long);
}
}
inline struct mbuf *
sctp_m_copym(struct mbuf *m, int off, int len, int wait)
{
sctp_pkthdr_fix(m);
return (m_copym(m, off, len, wait));
}
#endif