#include <sys/systm.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_utun.h>
#include <sys/mbuf.h>
#include <net/if_utun_crypto.h>
#include <net/if_utun_crypto_ipsec.h>
#include <net/if_utun_crypto_dtls.h>
void
utun_ctl_init_crypto (void)
{
utun_ctl_init_crypto_dtls();
}
void
utun_cleanup_crypto (struct utun_pcb *pcb)
{
#if IPSEC
utun_cleanup_all_crypto_ipsec(pcb);
#endif
utun_cleanup_all_crypto_dtls(pcb);
pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO;
}
errno_t
utun_ctl_enable_crypto (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
utun_crypto_ctx_t *crypto_ctx;
if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
return EINVAL;
}
if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
return EINVAL;
}
if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
#if IPSEC
if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
utun_ctl_enable_crypto_ipsec(pcb, crypto_args);
} else
#endif
if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
utun_ctl_enable_crypto_dtls(pcb, crypto_args);
} else {
return EPROTONOSUPPORT;
}
for (idx = 0; idx < UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_MAX); idx++) {
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (crypto_ctx->valid) {
return EBADF;
}
crypto_ctx->type = crypto_args->type;
LIST_INIT(&crypto_ctx->keys_listhead);
LIST_INIT(&crypto_ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(UTUN_CRYPTO_INNER_TYPE_IPv4)]);
LIST_INIT(&crypto_ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(UTUN_CRYPTO_INNER_TYPE_IPv6)]);
crypto_ctx->valid = 1;
printf("%s: initialized framer lists\n", __FUNCTION__);
}
pcb->utun_flags |= (UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
return 0;
}
}
errno_t
utun_ctl_disable_crypto (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
return EINVAL;
}
if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
return EINVAL;
}
if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
#if IPSEC
if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
utun_ctl_disable_crypto_ipsec(pcb);
} else
#endif
if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
utun_ctl_disable_crypto_dtls(pcb);
} else {
return EPROTONOSUPPORT;
}
}
pcb->utun_flags &= ~(UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
return 0;
}
errno_t
utun_ctl_config_crypto_keys (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
utun_crypto_ctx_t *crypto_ctx;
utun_crypto_keys_t *crypto_keys = NULL;
if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
return EINVAL;
}
if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
return EINVAL;
}
if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
return EINVAL;
}
idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (!crypto_ctx->valid) {
return EBADF;
}
if (crypto_keys_args->type != crypto_ctx->type) {
return ENOENT;
}
crypto_keys = utun_alloc(sizeof(*crypto_keys));
if (!crypto_keys) {
return ENOBUFS;
}
bzero(crypto_keys, sizeof(*crypto_keys));
if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
#if IPSEC
if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
errno_t err;
if ((err = utun_ctl_config_crypto_keys_ipsec(pcb, crypto_keys_args, crypto_keys))) {
utun_free(crypto_keys);
return err;
}
} else
#endif
{
utun_free(crypto_keys);
return EPROTONOSUPPORT;
}
crypto_keys->type = crypto_keys_args->type;
LIST_INSERT_HEAD(&crypto_ctx->keys_listhead, crypto_keys, chain);
crypto_keys->valid = 1;
}
return 0;
}
errno_t
utun_ctl_unconfig_crypto_keys (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
utun_crypto_ctx_t *crypto_ctx;
utun_crypto_keys_t *cur_crypto_keys, *nxt_crypto_keys;
if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
return EINVAL;
}
if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
return EINVAL;
}
if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
return EINVAL;
}
idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (!crypto_ctx->valid) {
return EBADF;
}
if (crypto_keys_args->type != crypto_ctx->type) {
return ENOENT;
}
if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
for (cur_crypto_keys = (__typeof__(cur_crypto_keys))LIST_FIRST(&crypto_ctx->keys_listhead);
cur_crypto_keys != NULL;
cur_crypto_keys = nxt_crypto_keys) {
nxt_crypto_keys = (__typeof__(nxt_crypto_keys))LIST_NEXT(cur_crypto_keys, chain);
#if IPSEC
if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
if (crypto_keys_args->u.ipsec_v1.spi == cur_crypto_keys->state.u.ipsec.spi) {
errno_t err;
if ((err = utun_ctl_unconfig_crypto_keys_ipsec(crypto_keys_args, cur_crypto_keys))) {
return err;
}
LIST_REMOVE(cur_crypto_keys, chain);
bzero(cur_crypto_keys, sizeof(*cur_crypto_keys));
utun_free(cur_crypto_keys);
return 0;
}
} else
#endif
{
return EPROTONOSUPPORT;
}
}
}
return 0;
}
errno_t
utun_ctl_config_crypto_framer (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_FRAMER_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_framer_args_t *framer_args = (__typeof__(framer_args))data;
utun_crypto_ctx_t *crypto_ctx;
if (framer_args->ver == 0 || framer_args->ver >= UTUN_CRYPTO_FRAMER_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, (int)framer_args->ver);
return EINVAL;
}
if (framer_args->dir == 0 || framer_args->dir >= UTUN_CRYPTO_DIR_MAX) {
printf("%s: dir check failed %d\n", __FUNCTION__, (int)framer_args->dir);
return EINVAL;
}
if (framer_args->type == 0 || framer_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, (int)framer_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args));
return EINVAL;
}
idx = UTUN_CRYPTO_DIR_TO_IDX(framer_args->dir);
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (!crypto_ctx->valid) {
return EBADF;
}
if (framer_args->type != crypto_ctx->type) {
return ENOENT;
}
if (framer_args->args_ulen != sizeof(framer_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
if (framer_args->type == UTUN_CRYPTO_TYPE_DTLS) {
errno_t err;
if ((err = utun_ctl_config_crypto_dtls_framer(crypto_ctx, framer_args))) {
return err;
}
} else {
return EPROTONOSUPPORT;
}
}
return 0;
}
errno_t
utun_ctl_unconfig_crypto_framer (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_FRAMER_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_framer_args_t *framer_args = (__typeof__(framer_args))data;
utun_crypto_ctx_t *crypto_ctx;
if (framer_args->ver == 0 || framer_args->ver >= UTUN_CRYPTO_FRAMER_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, (int)framer_args->ver);
return EINVAL;
}
if (framer_args->dir == 0 || framer_args->dir >= UTUN_CRYPTO_DIR_MAX) {
printf("%s: dir check failed %d\n", __FUNCTION__, (int)framer_args->dir);
return EINVAL;
}
if (framer_args->type == 0 || framer_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, (int)framer_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args));
return EINVAL;
}
idx = UTUN_CRYPTO_DIR_TO_IDX(framer_args->dir);
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (!crypto_ctx->valid) {
return EBADF;
}
if (framer_args->type != crypto_ctx->type) {
return ENOENT;
}
if (framer_args->args_ulen != sizeof(framer_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
if (framer_args->type == UTUN_CRYPTO_TYPE_DTLS) {
errno_t err;
if ((err = utun_ctl_unconfig_crypto_dtls_framer(crypto_ctx, framer_args))) {
return err;
}
} else {
return EPROTONOSUPPORT;
}
}
return 0;
}
errno_t
utun_ctl_generate_crypto_keys_idx (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t *len)
{
struct utun_pcb *pcb = unitinfo;
if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
int idx;
utun_crypto_keys_idx_args_t *crypto_keys_idx_args = (__typeof__(crypto_keys_idx_args))data;
utun_crypto_ctx_t *crypto_ctx;
if (crypto_keys_idx_args->ver == 0 || crypto_keys_idx_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_idx_args->ver);
return EINVAL;
}
if (crypto_keys_idx_args->dir == 0 || crypto_keys_idx_args->dir >= UTUN_CRYPTO_DIR_MAX) {
printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_idx_args->dir);
return EINVAL;
}
if (crypto_keys_idx_args->type == 0 || crypto_keys_idx_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_idx_args->type);
return EINVAL;
}
if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)*len, (int)UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args));
return EINVAL;
}
idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_idx_args->dir);
crypto_ctx = &pcb->utun_crypto_ctx[idx];
if (!crypto_ctx->valid) {
return EBADF;
}
if (crypto_keys_idx_args->type != crypto_ctx->type) {
return ENOENT;
}
if (crypto_keys_idx_args->args_ulen != sizeof(crypto_keys_idx_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
#if IPSEC
if (crypto_keys_idx_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
errno_t err;
if ((err = utun_ctl_generate_crypto_keys_idx_ipsec(crypto_keys_idx_args))) {
return err;
}
} else
#endif
{
return EPROTONOSUPPORT;
}
}
return 0;
}
errno_t
utun_ctl_stop_crypto_data_traffic (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
return EINVAL;
}
if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
return EINVAL;
}
if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
printf("%s: crypto is already disabled\n", __FUNCTION__);
return EINVAL;
}
if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
} else if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
utun_ctl_stop_datatraffic_crypto_dtls(pcb);
} else {
return EPROTONOSUPPORT;
}
}
pcb->utun_flags |= UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
return 0;
}
errno_t
utun_ctl_start_crypto_data_traffic (__unused kern_ctl_ref kctlref,
__unused u_int32_t unit,
__unused void *unitinfo,
__unused int opt,
void *data,
size_t len)
{
struct utun_pcb *pcb = unitinfo;
if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
return EMSGSIZE;
} else {
utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
return EINVAL;
}
if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
return EINVAL;
}
if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
(int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
return EINVAL;
}
if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
printf("%s: compatibility mode\n", __FUNCTION__);
}
if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
printf("%s: crypto is already disabled\n", __FUNCTION__);
return EINVAL;
}
if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
} else if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
utun_ctl_start_datatraffic_crypto_dtls(pcb);
} else {
return EPROTONOSUPPORT;
}
}
pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
return 0;
}
int
utun_pkt_crypto_output (struct utun_pcb *pcb, mbuf_t *m)
{
int idx = UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT);
if (!pcb->utun_crypto_ctx[idx].valid) {
printf("%s: context is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].valid);
return -1;
}
#if IPSEC
if (pcb->utun_crypto_ctx[idx].type == UTUN_CRYPTO_TYPE_IPSEC) {
return(utun_pkt_ipsec_output(pcb, m));
} else
#endif
if (pcb->utun_crypto_ctx[idx].type == UTUN_CRYPTO_TYPE_DTLS) {
return(utun_pkt_dtls_output(pcb, m));
} else {
printf("%s: type is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].type);
}
return -1;
}