if_utun_crypto_dtls.c [plain text]
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_utun.h>
#include <sys/mbuf.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_var.h>
#include <net/if_utun.h>
#include <net/if_utun_crypto_dtls.h>
#include <net/bpf.h>
extern errno_t utun_pkt_input (struct utun_pcb *pcb, mbuf_t m);
static UInt32 dtls_kpi_callbacks_inited = FALSE;
static unsigned int dtls_kpi_flags = 0;
static utun_crypto_kpi_connect_func dtls_kpi_connect = (__typeof__(dtls_kpi_connect))NULL;
static utun_crypto_kpi_send_func dtls_kpi_send = (__typeof__(dtls_kpi_send))NULL;
static UInt32 dtls_ctl_mutex_inited = FALSE;
static lck_grp_t *dtls_ctl_mutex_grp = NULL;
static lck_grp_attr_t *dtls_ctl_mutex_grp_attr = NULL;
static lck_attr_t *dtls_ctl_mutex_attr = NULL;
static lck_mtx_t dtls_ctl_mutex;
#define utun_ctl_get_first_framer(ctx, inner_type) (utun_crypto_framer_t *)LIST_FIRST(&ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(inner_type)])
#define utun_get_framer_listhead(ctx, inner_type) &ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(inner_type)]
static void
utun_ctl_clr_dtls_framer (utun_crypto_framer_t *rem_framer)
{
if (!rem_framer) return;
LIST_REMOVE(rem_framer, framer_chain); if (rem_framer->dir == UTUN_CRYPTO_DIR_IN) {
if (utun_crypto_framer_state_dtls_in(rem_framer).in_pattern) {
utun_free(utun_crypto_framer_state_dtls_in(rem_framer).in_pattern);
}
if (utun_crypto_framer_state_dtls_in(rem_framer).in_pattern_mask) {
utun_free(utun_crypto_framer_state_dtls_in(rem_framer).in_pattern_mask);
}
if (utun_crypto_framer_state_dtls_in(rem_framer).in_pattern_masked) {
utun_free(utun_crypto_framer_state_dtls_in(rem_framer).in_pattern_masked);
}
} else {
if (utun_crypto_framer_state_dtls_out(rem_framer).out_pattern) {
utun_free(utun_crypto_framer_state_dtls_out(rem_framer).out_pattern);
}
}
utun_free(rem_framer);
return;
}
static void
utun_ctl_clr_dtls_framers (utun_crypto_framer_t *first_framer)
{
utun_crypto_framer_t *cur_framer, *nxt_framer;
for (cur_framer = first_framer;
cur_framer != NULL;
cur_framer = nxt_framer) {
nxt_framer = (__typeof__(nxt_framer))LIST_NEXT(cur_framer, framer_chain);
utun_ctl_clr_dtls_framer(cur_framer);
}
return;
}
static void
utun_ctl_clr_dtls_all_framers (utun_crypto_ctx_t *crypto_ctx)
{
utun_ctl_clr_dtls_framers(utun_ctl_get_first_framer(crypto_ctx, UTUN_CRYPTO_INNER_TYPE_IPv4));
utun_ctl_clr_dtls_framers(utun_ctl_get_first_framer(crypto_ctx, UTUN_CRYPTO_INNER_TYPE_IPv6));
crypto_ctx->num_framers = 0;
}
static void
utun_ctl_restart_dtls_framers (utun_crypto_framer_t *first_framer)
{
utun_crypto_framer_t *cur_framer;
for (cur_framer = first_framer;
cur_framer != NULL;
cur_framer = (__typeof__(cur_framer))LIST_NEXT(cur_framer, framer_chain)) {
utun_crypto_framer_state_dtls_out(cur_framer).sequence_field = utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_initval;
}
return;
}
static void
utun_ctl_restart_dtls_all_framers (utun_crypto_ctx_t *crypto_ctx)
{
utun_ctl_restart_dtls_framers(utun_ctl_get_first_framer(crypto_ctx, UTUN_CRYPTO_INNER_TYPE_IPv4));
utun_ctl_restart_dtls_framers(utun_ctl_get_first_framer(crypto_ctx, UTUN_CRYPTO_INNER_TYPE_IPv6));
}
static int
is_pattern_all_zeroes (u_int8_t *pattern,
int pattern_len)
{
int i;
if (!pattern || !pattern_len) return FALSE;
for (i = 0; i < pattern_len; i++) {
if (pattern[i] != 0) return FALSE;
}
return TRUE;
}
static int
is_pattern_masked_all_zeroes (u_int8_t *pattern,
u_int8_t *pattern_mask,
int pattern_len)
{
int i;
if (!pattern || !pattern_mask || !pattern_len) return FALSE;
for (i = 0; i < pattern_len; i++) {
if ((pattern[i] & pattern_mask[i])) return FALSE;
}
return TRUE;
}
static void
utun_ctl_calc_dtls_framer_pattern_and_mask (u_int8_t *pattern_masked, u_int8_t *pattern, u_int8_t *mask, int len)
{
int i;
for (i = 0; i < len; i++) {
pattern_masked[i] = (pattern[i] & mask[i]);
}
}
static Boolean
utun_ctl_did_dtls_framer_pattern_match (u_int8_t *input, u_int8_t *pattern_masked, int len)
{
int i;
for (i = 0; i < len; i++) {
if ((input[i] & pattern_masked[i]) != pattern_masked[i]) return FALSE;
}
return TRUE;
}
static Boolean
utun_pkt_dtls_input_frame_is_data(utun_crypto_ctx_t *crypto_ctx,
mbuf_t *pkt,
protocol_family_t family,
int *striplen)
{
u_int8_t *p;
utun_crypto_framer_t *cur_framer;
p = mtod(*pkt, __typeof__(p));
for (cur_framer = utun_ctl_get_first_framer(crypto_ctx, utun_crypto_framer_protocol_family_to_inner_type(family));
cur_framer != NULL;
cur_framer = (__typeof__(cur_framer))LIST_NEXT(cur_framer, framer_chain)) {
if (m_pktlen(*pkt) < utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len) {
continue;
}
if ((*pkt)->m_len < utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len) {
*pkt = m_pullup(*pkt, utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len);
if (!*pkt ||
(*pkt)->m_len < utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len) {
return FALSE;
}
p = mtod(*pkt, __typeof__(p));
}
if (utun_ctl_did_dtls_framer_pattern_match(p,
utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_masked,
utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len)) {
*striplen = utun_crypto_framer_state_dtls_in(cur_framer).in_data_offset;
return TRUE;
}
}
return FALSE;
}
#define GETLONG(l, cp) { \
(l) = *(cp)++ << 8; \
(l) |= *(cp)++; (l) <<= 8; \
(l) |= *(cp)++; (l) <<= 8; \
(l) |= *(cp)++; \
}
#define PUTLONG(l, cp) { \
*(cp)++ = (u_char) ((l) >> 24); \
*(cp)++ = (u_char) ((l) >> 16); \
*(cp)++ = (u_char) ((l) >> 8); \
*(cp)++ = (u_char) (l); \
}
static int
utun_pkt_dtls_output_frame_encapsulate (utun_crypto_ctx_t *crypto_ctx,
mbuf_t *pkt,
protocol_family_t proto)
{
u_int8_t *p;
utun_crypto_framer_t *cur_framer;
u_int32_t pkt_len;
if (!crypto_ctx->num_framers) {
return 0;
}
if (proto != AF_INET && proto != AF_INET6) {
printf("%s: unsupported proto %d\n", __FUNCTION__, proto);
return EINVAL;
}
for (cur_framer = utun_ctl_get_first_framer(crypto_ctx, utun_crypto_framer_protocol_family_to_inner_type(proto));
cur_framer != NULL && !utun_crypto_framer_state_dtls_out(cur_framer).out_pattern;
cur_framer = (__typeof__(cur_framer))LIST_NEXT(cur_framer, framer_chain));
if (!cur_framer ||
!utun_crypto_framer_state_dtls_out(cur_framer).out_pattern_len) {
return 0;
}
pkt_len = m_pktlen(*pkt);
if (mbuf_prepend(pkt, utun_crypto_framer_state_dtls_out(cur_framer).out_pattern_len, MBUF_DONTWAIT) != 0) {
printf("%s - ifnet_output prepend failed\n", __FUNCTION__);
return ENOBUFS;
}
p = mtod(*pkt, __typeof__(p));
memcpy(p,
utun_crypto_framer_state_dtls_out(cur_framer).out_pattern,
utun_crypto_framer_state_dtls_out(cur_framer).out_pattern_len);
if (utun_crypto_framer_state_dtls_out(cur_framer).len_field_mask) {
u_int32_t tmp;
u_int8_t *q = p + utun_crypto_framer_state_dtls_out(cur_framer).len_field_offset;
GETLONG(tmp, q);
tmp &= ((pkt_len + utun_crypto_framer_state_dtls_out(cur_framer).len_field_extra) & utun_crypto_framer_state_dtls_out(cur_framer).len_field_mask);
q = p + utun_crypto_framer_state_dtls_out(cur_framer).len_field_offset;
PUTLONG(tmp, q);
}
if (utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_mask) {
u_int32_t tmp = (utun_crypto_framer_state_dtls_out(cur_framer).sequence_field & utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_mask);
u_int8_t *q = p + utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_offset;
GETLONG(tmp, q);
tmp &= (utun_crypto_framer_state_dtls_out(cur_framer).sequence_field & utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_mask);
q = p + utun_crypto_framer_state_dtls_out(cur_framer).sequence_field_offset;
PUTLONG(tmp, q);
utun_crypto_framer_state_dtls_out(cur_framer).sequence_field++;
}
return 0;
}
void
utun_ctl_init_crypto_dtls (void)
{
if (OSCompareAndSwap(FALSE, TRUE, &dtls_ctl_mutex_inited)) {
if (!dtls_ctl_mutex_grp_attr)
dtls_ctl_mutex_grp_attr = lck_grp_attr_alloc_init();
if (!dtls_ctl_mutex_grp)
dtls_ctl_mutex_grp = lck_grp_alloc_init("utun-crypto", dtls_ctl_mutex_grp_attr);
if (!dtls_ctl_mutex_attr)
dtls_ctl_mutex_attr = lck_attr_alloc_init();
lck_mtx_init(&dtls_ctl_mutex, dtls_ctl_mutex_grp, dtls_ctl_mutex_attr);
}
}
errno_t
utun_ctl_register_dtls (utun_crypto_kpi_reg_t *reg)
{
if (!reg) return EINVAL;
if (reg->crypto_kpi_type != UTUN_CRYPTO_TYPE_DTLS) {
return EINVAL;
}
if (!reg->crypto_kpi_connect) {
return EINVAL;
}
if (!reg->crypto_kpi_send) {
return EINVAL;
}
if (OSCompareAndSwap(FALSE, TRUE, &dtls_kpi_callbacks_inited)) {
dtls_kpi_flags = reg->crypto_kpi_flags;
dtls_kpi_connect = reg->crypto_kpi_connect;
dtls_kpi_send = reg->crypto_kpi_send;
}
return 0;
}
void
utun_ctl_enable_crypto_dtls(struct utun_pcb *pcb, utun_crypto_args_t *args)
{
utun_crypto_ctx_t *crypto_ctx;
lck_mtx_lock(&dtls_ctl_mutex);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_IN)];
if (crypto_ctx->valid) {
printf("%s: dtls already enabled (prev %u, now %u)\n", __FUNCTION__,
crypto_ctx->kpi_handle, args->u.dtls_v1.kpi_handle);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT)];
if (!crypto_ctx->valid) {
crypto_ctx->kpi_handle = args->u.dtls_v1.kpi_handle;
} else {
printf("%s: dtls already enabled for egress (prev %u, now %u)\n", __FUNCTION__,
crypto_ctx->kpi_handle, args->u.dtls_v1.kpi_handle);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
void
utun_ctl_disable_crypto_dtls(struct utun_pcb *pcb)
{
utun_crypto_ctx_t *crypto_ctx;
lck_mtx_lock(&dtls_ctl_mutex);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_IN)];
if (crypto_ctx->valid &&
crypto_ctx->type == UTUN_CRYPTO_TYPE_DTLS) {
utun_ctl_clr_dtls_all_framers(crypto_ctx);
}
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT)];
if (!crypto_ctx->valid ||
crypto_ctx->type != UTUN_CRYPTO_TYPE_DTLS) {
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
if (crypto_ctx->kpi_ref) {
if (dtls_kpi_connect) {
(void)dtls_kpi_connect(crypto_ctx->kpi_handle, NULL);
if (--crypto_ctx->kpi_refcnt == 0) {
crypto_ctx->kpi_ref = (__typeof__(crypto_ctx->kpi_ref))NULL;
crypto_ctx->kpi_handle = UTUN_CRYPTO_DTLS_HANDLE_INVALID;
} else {
}
} else {
printf("%s: ### dtls_ctl_connect unavailable\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
} else {
if (crypto_ctx->kpi_handle < 0) {
printf("%s: dtls already disabled\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
crypto_ctx->kpi_handle = UTUN_CRYPTO_DTLS_HANDLE_INVALID;
}
utun_ctl_clr_dtls_all_framers(crypto_ctx);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
static utun_crypto_framer_t *
utun_ctl_get_dtls_in_framer (utun_crypto_framer_t *first_framer,
u_int8_t *in_pattern,
int in_pattern_len,
u_int8_t *in_pattern_mask,
int in_pattern_mask_len)
{
utun_crypto_framer_t *cur_framer;
for (cur_framer = first_framer;
cur_framer != NULL;
cur_framer = (__typeof__(cur_framer))LIST_NEXT(cur_framer, framer_chain)) {
if (utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len == in_pattern_len &&
memcmp(utun_crypto_framer_state_dtls_in(cur_framer).in_pattern,
in_pattern,
in_pattern_len) == 0 &&
utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_len == in_pattern_mask_len &&
memcmp(utun_crypto_framer_state_dtls_in(cur_framer).in_pattern_mask,
in_pattern_mask,
in_pattern_mask_len) == 0) {
return cur_framer;
}
}
return NULL;
}
errno_t
utun_ctl_config_crypto_dtls_framer (utun_crypto_ctx_t *crypto_ctx,
utun_crypto_framer_args_t *args)
{
utun_crypto_framer_t *framer, *new_framer = NULL, *dup_framer;
if (args->ver != UTUN_CRYPTO_DTLS_VER_1) {
return EINVAL;
}
if (!args->type || args->type >= UTUN_CRYPTO_INNER_TYPE_MAX) {
return EINVAL;
}
lck_mtx_lock(&dtls_ctl_mutex);
if (args->dir == UTUN_CRYPTO_DIR_IN) {
if (!utun_crypto_framer_args_dtls_in(args).in_pattern_len ||
!utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: invalid dtls in-pattern %d mask %d\n", __FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_len,
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len);
return EINVAL;
}
if (utun_crypto_framer_args_dtls_in(args).in_pattern_len != utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: inconsistent dtls in-pattern %d mask %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_len,
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len);
return EINVAL;
}
if ((u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_len + (u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len != args->varargs_buflen) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: inconsistent dtls in-pattern %d mask %d, total %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_len,
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len,
args->varargs_buflen);
return EINVAL;
}
if (is_pattern_all_zeroes(&args->varargs_buf[0],
utun_crypto_framer_args_dtls_in(args).in_pattern_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: in-pattern is all zeros, len %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_len);
return EINVAL;
}
if (is_pattern_all_zeroes(&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: in-pattern-mask is all zeros, len %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len);
return EINVAL;
}
if (is_pattern_masked_all_zeroes(&args->varargs_buf[0],
&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: in-pattern-masked is all zeros, len %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len);
return EINVAL;
}
if (!(framer = utun_ctl_get_first_framer(crypto_ctx, args->inner_type))) {
if (!(framer = utun_alloc(sizeof(*framer)))) {
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
bzero(framer, sizeof(*framer));
} else {
if ((dup_framer = utun_ctl_get_dtls_in_framer(framer ,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_in(args).in_pattern_len,
&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len))) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: ignoring duplicate framer for type %d\n",__FUNCTION__,
args->inner_type);
return 0;
}
if (!(new_framer = utun_alloc(sizeof(*new_framer)))) {
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
bzero(new_framer, sizeof(*new_framer));
framer = new_framer;
}
LIST_INSERT_HEAD(utun_get_framer_listhead(crypto_ctx, args->inner_type),
new_framer,
framer_chain);
framer->inner_type = args->inner_type;
framer->inner_protocol_family = utun_crypto_framer_inner_type_to_protocol_family(args->inner_type);
if (!(utun_crypto_framer_state_dtls_in(framer).in_pattern = utun_alloc(utun_crypto_framer_args_dtls_in(args).in_pattern_len))) {
utun_ctl_clr_dtls_framer(framer);
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
memcpy(utun_crypto_framer_state_dtls_in(framer).in_pattern,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_in(args).in_pattern_len);
utun_crypto_framer_state_dtls_in(framer).in_pattern_len = utun_crypto_framer_args_dtls_in(args).in_pattern_len;
if (!(utun_crypto_framer_state_dtls_in(framer).in_pattern_mask = utun_alloc(utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len))) {
utun_ctl_clr_dtls_framer(framer);
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
memcpy(utun_crypto_framer_state_dtls_in(framer).in_pattern_mask,
&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len);
utun_crypto_framer_state_dtls_in(framer).in_data_offset = utun_crypto_framer_args_dtls_in(args).in_data_offset;
if (!(utun_crypto_framer_state_dtls_in(framer).in_pattern_masked = utun_alloc(utun_crypto_framer_args_dtls_in(args).in_pattern_len))) {
utun_ctl_clr_dtls_framer(framer);
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
utun_ctl_calc_dtls_framer_pattern_and_mask(utun_crypto_framer_state_dtls_in(framer).in_pattern_masked,
utun_crypto_framer_state_dtls_in(framer).in_pattern,
utun_crypto_framer_state_dtls_in(framer).in_pattern_mask,
utun_crypto_framer_state_dtls_in(framer).in_pattern_len);
crypto_ctx->num_framers++;
} else {
if (!utun_crypto_framer_args_dtls_out(args).out_pattern_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: invalid output framer, len %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_out(args).out_pattern_len);
return EINVAL;
}
if (is_pattern_all_zeroes(&args->varargs_buf[0],
utun_crypto_framer_args_dtls_out(args).out_pattern_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: zeroed output framer, len %d\n",__FUNCTION__,
utun_crypto_framer_args_dtls_out(args).out_pattern_len);
return EINVAL;
}
if ((utun_crypto_framer_args_dtls_out(args).len_field_offset || utun_crypto_framer_args_dtls_out(args).len_field_extra) && !utun_crypto_framer_args_dtls_out(args).len_field_mask) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: output framer has invalid length-field %d,%d,%x\n",__FUNCTION__,
(int)utun_crypto_framer_args_dtls_out(args).len_field_offset,
(int)utun_crypto_framer_args_dtls_out(args).len_field_extra,
utun_crypto_framer_args_dtls_out(args).len_field_mask);
return EINVAL;
}
if (utun_crypto_framer_args_dtls_out(args).len_field_offset >= utun_crypto_framer_args_dtls_out(args).out_pattern_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if ((utun_crypto_framer_args_dtls_out(args).sequence_field || utun_crypto_framer_args_dtls_out(args).sequence_field_offset) && !utun_crypto_framer_args_dtls_out(args).sequence_field_mask) {
lck_mtx_unlock(&dtls_ctl_mutex);
printf("%s: output framer has invalid sequence-field %d,%d,%x\n",__FUNCTION__,
(int)utun_crypto_framer_args_dtls_out(args).sequence_field,
(int)utun_crypto_framer_args_dtls_out(args).sequence_field_offset,
utun_crypto_framer_args_dtls_out(args).sequence_field_mask);
return EINVAL;
}
if (utun_crypto_framer_args_dtls_out(args).sequence_field_offset >= utun_crypto_framer_args_dtls_out(args).out_pattern_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if ((u_int32_t)utun_crypto_framer_args_dtls_out(args).out_pattern_len != args->varargs_buflen) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if (!(framer = utun_ctl_get_first_framer(crypto_ctx, args->inner_type))) {
if (!(framer = utun_alloc(sizeof(*framer)))) {
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
bzero(framer, sizeof(*framer));
LIST_INSERT_HEAD(utun_get_framer_listhead(crypto_ctx, args->inner_type),
new_framer,
framer_chain);
} else {
if (framer->inner_type == args->inner_type &&
utun_crypto_framer_state_dtls_out(framer).out_pattern_len == utun_crypto_framer_args_dtls_out(args).out_pattern_len &&
utun_crypto_framer_state_dtls_out(framer).out_pattern &&
memcmp(utun_crypto_framer_state_dtls_out(framer).out_pattern,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_out(args).out_pattern_len) == 0) {
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
if (utun_crypto_framer_state_dtls_out(framer).out_pattern) {
utun_free(utun_crypto_framer_state_dtls_out(framer).out_pattern);
}
}
framer->inner_type = args->inner_type;
framer->inner_protocol_family = utun_crypto_framer_inner_type_to_protocol_family(args->inner_type);
if (!(utun_crypto_framer_state_dtls_out(framer).out_pattern = utun_alloc(utun_crypto_framer_args_dtls_out(args).out_pattern_len))) {
utun_ctl_clr_dtls_framer(framer);
lck_mtx_unlock(&dtls_ctl_mutex);
return ENOBUFS;
}
memcpy(utun_crypto_framer_state_dtls_out(framer).out_pattern,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_out(args).out_pattern_len);
utun_crypto_framer_state_dtls_out(framer).out_pattern_len = utun_crypto_framer_args_dtls_out(args).out_pattern_len;
utun_crypto_framer_state_dtls_out(framer).len_field_mask = utun_crypto_framer_args_dtls_out(args).len_field_mask;
utun_crypto_framer_state_dtls_out(framer).len_field_offset = utun_crypto_framer_args_dtls_out(args).len_field_offset;
utun_crypto_framer_state_dtls_out(framer).len_field_extra = utun_crypto_framer_args_dtls_out(args).len_field_extra;
utun_crypto_framer_state_dtls_out(framer).sequence_field_initval = utun_crypto_framer_args_dtls_out(args).sequence_field;
utun_crypto_framer_state_dtls_out(framer).sequence_field_mask = utun_crypto_framer_args_dtls_out(args).sequence_field_mask;
utun_crypto_framer_state_dtls_out(framer).sequence_field_offset = utun_crypto_framer_args_dtls_out(args).sequence_field_offset;
crypto_ctx->num_framers = 1;
}
framer->type = args->type;
framer->dir = args->dir;
framer->valid = 1;
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
int
utun_ctl_unconfig_crypto_dtls_framer (utun_crypto_ctx_t *crypto_ctx,
utun_crypto_framer_args_t *args)
{
utun_crypto_framer_t *framer, *rem_framer;
if (args->ver != UTUN_CRYPTO_DTLS_VER_1) {
return EINVAL;
}
if (!args->type || args->type >= UTUN_CRYPTO_INNER_TYPE_MAX) {
return EINVAL;
}
lck_mtx_lock(&dtls_ctl_mutex);
if (args->dir == UTUN_CRYPTO_DIR_IN) {
if (!utun_crypto_framer_args_dtls_in(args).in_pattern_len) {
utun_ctl_clr_dtls_framers(utun_ctl_get_first_framer(crypto_ctx, args->inner_type));
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
if (utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len &&
utun_crypto_framer_args_dtls_in(args).in_pattern_len != utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if ((u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_len + (u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len != args->varargs_buflen) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if (is_pattern_all_zeroes(&args->varargs_buf[0],
utun_crypto_framer_args_dtls_in(args).in_pattern_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if (utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len &&
is_pattern_all_zeroes(&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if (is_pattern_masked_all_zeroes(&args->varargs_buf[0],
&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_len)) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if ((u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_len + (u_int32_t)utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len != args->varargs_buflen) {
lck_mtx_unlock(&dtls_ctl_mutex);
return EINVAL;
}
if (!(framer = utun_ctl_get_first_framer(crypto_ctx, args->inner_type))) {
printf("%s: no framers configured\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
} else {
if ((rem_framer = utun_ctl_get_dtls_in_framer(framer,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_in(args).in_pattern_len,
&args->varargs_buf[utun_crypto_framer_args_dtls_in(args).in_pattern_len],
utun_crypto_framer_args_dtls_in(args).in_pattern_mask_len))) {
utun_ctl_clr_dtls_framer(rem_framer);
if (crypto_ctx->num_framers) crypto_ctx->num_framers--;
} else {
printf("%s: no matching ingress framer\n", __FUNCTION__);
}
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
} else {
framer = utun_ctl_get_first_framer(crypto_ctx, args->inner_type);
if (framer) {
if (framer->inner_type != args->inner_type ||
(utun_crypto_framer_args_dtls_out(args).out_pattern_len &&
utun_crypto_framer_state_dtls_out(framer).out_pattern_len != utun_crypto_framer_args_dtls_out(args).out_pattern_len) ||
(utun_crypto_framer_args_dtls_out(args).out_pattern_len &&
memcmp(utun_crypto_framer_state_dtls_out(framer).out_pattern,
&args->varargs_buf[0],
utun_crypto_framer_args_dtls_out(args).out_pattern_len))) {
printf("%s: no matching egress framer\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return EBADF;
}
utun_ctl_clr_dtls_framer(framer);
if (crypto_ctx->num_framers) crypto_ctx->num_framers--;
}
}
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
void
utun_ctl_start_datatraffic_crypto_dtls(struct utun_pcb *pcb)
{
utun_crypto_ctx_t *crypto_ctx;
lck_mtx_lock(&dtls_ctl_mutex);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT)];
if (crypto_ctx->kpi_handle < 0) {
printf("%s: dtls disabled\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
if (!crypto_ctx->kpi_ref) {
if (dtls_kpi_connect) {
crypto_ctx->kpi_ref = dtls_kpi_connect(crypto_ctx->kpi_handle, pcb);
if (!crypto_ctx->kpi_ref) {
printf("%s: ### dtls_kpi_connect failed\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
crypto_ctx->kpi_refcnt++;
} else {
printf("%s: ### dtls_kpi_connect unavailable\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
} else {
printf("%s: dtls already stitched\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
utun_ctl_restart_dtls_all_framers(crypto_ctx);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
void
utun_ctl_stop_datatraffic_crypto_dtls(struct utun_pcb *pcb)
{
utun_crypto_ctx_t *crypto_ctx;
lck_mtx_lock(&dtls_ctl_mutex);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT)];
if (crypto_ctx->kpi_ref) {
if (dtls_kpi_connect) {
(void)dtls_kpi_connect(crypto_ctx->kpi_handle, NULL);
if (--crypto_ctx->kpi_refcnt == 0) {
crypto_ctx->kpi_ref = (__typeof__(crypto_ctx->kpi_ref))NULL;
crypto_ctx->kpi_handle = UTUN_CRYPTO_DTLS_HANDLE_INVALID;
} else {
}
} else {
printf("%s: dtls_kpi_connect unavailable\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
} else {
printf("%s: dtls already not-stitched\n", __FUNCTION__);
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
lck_mtx_unlock(&dtls_ctl_mutex);
return;
}
#define utun_pkt_dtls_prepend_proto(pkt, pf) do { \
if (mbuf_prepend(pkt, sizeof(protocol_family_t), MBUF_DONTWAIT) != 0) { \
printf("%s - ifnet_output prepend failed\n", __FUNCTION__); \
lck_mtx_unlock(&dtls_ctl_mutex); \
return EBADF; \
} \
*(protocol_family_t *)mbuf_data(*pkt) = pf; \
} while(0);
#define utun_pkt_dtls_puntup(pcb, pkt, errstr, rc) do { \
*(protocol_family_t *)mbuf_data(*pkt) = htonl(*(protocol_family_t *)mbuf_data(*pkt)); \
rc = ctl_enqueuembuf(pcb->utun_ctlref, pcb->utun_unit, *pkt, CTL_DATA_EOR); \
if (rc != 0) { \
printf("%s: - ctl_enqueuembuf failed (rc %d) for %s:\n", __FUNCTION__, rc, (char *)errstr); \
mbuf_freem(*pkt); \
ifnet_stat_increment_out(pcb->utun_ifp, 0, 0, 1); \
lck_mtx_unlock(&dtls_ctl_mutex); \
return 0; \
} \
*pkt = NULL; \
} while(0);
int
utun_pkt_dtls_output(struct utun_pcb *pcb, mbuf_t *pkt)
{
errno_t rc = ENETUNREACH;
int len;
utun_crypto_ctx_t *crypto_ctx;
protocol_family_t proto;
if (!(pcb->utun_flags & UTUN_FLAGS_CRYPTO)) {
printf("%s - crypto disabled\n", __FUNCTION__);
return EINVAL;
}
if (!pcb->utun_ifp) {
printf("%s - utun ifp cleared\n", __FUNCTION__);
return EINVAL;
}
proto = *(mtod(*pkt, protocol_family_t *));
lck_mtx_lock(&dtls_ctl_mutex);
len = mbuf_pkthdr_len(*pkt);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT)];
if (dtls_kpi_send && (crypto_ctx->kpi_handle >= 0) && crypto_ctx->kpi_ref) {
m_adj(*pkt, sizeof(protocol_family_t));
if (!(rc = utun_pkt_dtls_output_frame_encapsulate(crypto_ctx, pkt, proto))) {
rc = dtls_kpi_send(crypto_ctx->kpi_ref, pkt);
if (rc) {
printf("%s: DTLS failed to send pkt %d\n", __FUNCTION__, rc);
ifnet_stat_increment_out(pcb->utun_ifp, 0, 0, 1); lck_mtx_unlock(&dtls_ctl_mutex);
return 0; }
} else if (rc == EINVAL) {
utun_pkt_dtls_prepend_proto(pkt, proto);
utun_pkt_dtls_puntup(pcb, pkt, (char *)"unsupported proto", rc);
} else {
printf("%s: failed to encrypsulate and send pkt %d\n", __FUNCTION__,rc);
ifnet_stat_increment_out(pcb->utun_ifp, 0, 0, 1); lck_mtx_unlock(&dtls_ctl_mutex);
return 0; }
} else {
utun_pkt_dtls_puntup(pcb, pkt, (char *)"slowpath", rc);
}
if (!rc)
ifnet_stat_increment_out(pcb->utun_ifp, 1, len, 0);
lck_mtx_unlock(&dtls_ctl_mutex);
return rc;
}
int
utun_pkt_dtls_input(struct utun_pcb *pcb, mbuf_t *pkt, __unused protocol_family_t family)
{
utun_crypto_ctx_t *crypto_ctx;
int striplen = 0;
if (!(pcb->utun_flags & UTUN_FLAGS_CRYPTO)) {
printf("%s - crypto disabled\n", __FUNCTION__);
return EINVAL;
}
if (!pcb->utun_ifp) {
printf("%s - utun ifp cleared\n", __FUNCTION__);
return EINVAL;
}
lck_mtx_lock(&dtls_ctl_mutex);
crypto_ctx = &pcb->utun_crypto_ctx[UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_IN)];
if (crypto_ctx->num_framers &&
!utun_pkt_dtls_input_frame_is_data(crypto_ctx, pkt, AF_INET, &striplen) &&
!utun_pkt_dtls_input_frame_is_data(crypto_ctx, pkt, AF_INET6, &striplen)) {
errno_t rc;
utun_pkt_dtls_prepend_proto(pkt, family);
*(protocol_family_t *)mbuf_data(*pkt) = htonl(*(protocol_family_t *)mbuf_data(*pkt));
rc = ctl_enqueuembuf(pcb->utun_ctlref, pcb->utun_unit, *pkt, CTL_DATA_EOR);
if (rc != 0) {
printf("%s: - ctl_enqueuembuf failed: %d\n", __FUNCTION__, rc);
mbuf_freem(*pkt);
lck_mtx_unlock(&dtls_ctl_mutex);
return rc;
}
printf("%s: - ctl_enqueuembuf punted a packet up to UTUN ctrl sock: %d\n", __FUNCTION__, rc);
ifnet_stat_increment_in(pcb->utun_ifp, 1, mbuf_pkthdr_len(*pkt), 0);
*pkt = NULL;
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}
if (striplen) {
m_adj(*pkt, striplen);
}
utun_pkt_dtls_prepend_proto(pkt, family);
ifnet_stat_increment_in(pcb->utun_ifp, 1, mbuf_pkthdr_len(*pkt), 0);
(void)utun_pkt_input(pcb, *pkt);
lck_mtx_unlock(&dtls_ctl_mutex);
return 0;
}