mirror of
https://github.com/opnsense/src.git
synced 2026-04-29 01:59:38 -04:00
KTLS adds support for in-kernel framing and encryption of Transport Layer Security (1.0-1.2) data on TCP sockets. KTLS only supports offload of TLS for transmitted data. Key negotation must still be performed in userland. Once completed, transmit session keys for a connection are provided to the kernel via a new TCP_TXTLS_ENABLE socket option. All subsequent data transmitted on the socket is placed into TLS frames and encrypted using the supplied keys. Any data written to a KTLS-enabled socket via write(2), aio_write(2), or sendfile(2) is assumed to be application data and is encoded in TLS frames with an application data type. Individual records can be sent with a custom type (e.g. handshake messages) via sendmsg(2) with a new control message (TLS_SET_RECORD_TYPE) specifying the record type. At present, rekeying is not supported though the in-kernel framework should support rekeying. KTLS makes use of the recently added unmapped mbufs to store TLS frames in the socket buffer. Each TLS frame is described by a single ext_pgs mbuf. The ext_pgs structure contains the header of the TLS record (and trailer for encrypted records) as well as references to the associated TLS session. KTLS supports two primary methods of encrypting TLS frames: software TLS and ifnet TLS. Software TLS marks mbufs holding socket data as not ready via M_NOTREADY similar to sendfile(2) when TLS framing information is added to an unmapped mbuf in ktls_frame(). ktls_enqueue() is then called to schedule TLS frames for encryption. In the case of sendfile_iodone() calls ktls_enqueue() instead of pru_ready() leaving the mbufs marked M_NOTREADY until encryption is completed. For other writes (vn_sendfile when pages are available, write(2), etc.), the PRUS_NOTREADY is set when invoking pru_send() along with invoking ktls_enqueue(). A pool of worker threads (the "KTLS" kernel process) encrypts TLS frames queued via ktls_enqueue(). Each TLS frame is temporarily mapped using the direct map and passed to a software encryption backend to perform the actual encryption. (Note: The use of PHYS_TO_DMAP could be replaced with sf_bufs if someone wished to make this work on architectures without a direct map.) KTLS supports pluggable software encryption backends. Internally, Netflix uses proprietary pure-software backends. This commit includes a simple backend in a new ktls_ocf.ko module that uses the kernel's OpenCrypto framework to provide AES-GCM encryption of TLS frames. As a result, software TLS is now a bit of a misnomer as it can make use of hardware crypto accelerators. Once software encryption has finished, the TLS frame mbufs are marked ready via pru_ready(). At this point, the encrypted data appears as regular payload to the TCP stack stored in unmapped mbufs. ifnet TLS permits a NIC to offload the TLS encryption and TCP segmentation. In this mode, a new send tag type (IF_SND_TAG_TYPE_TLS) is allocated on the interface a socket is routed over and associated with a TLS session. TLS records for a TLS session using ifnet TLS are not marked M_NOTREADY but are passed down the stack unencrypted. The ip_output_send() and ip6_output_send() helper functions that apply send tags to outbound IP packets verify that the send tag of the TLS record matches the outbound interface. If so, the packet is tagged with the TLS send tag and sent to the interface. The NIC device driver must recognize packets with the TLS send tag and schedule them for TLS encryption and TCP segmentation. If the the outbound interface does not match the interface in the TLS send tag, the packet is dropped. In addition, a task is scheduled to refresh the TLS send tag for the TLS session. If a new TLS send tag cannot be allocated, the connection is dropped. If a new TLS send tag is allocated, however, subsequent packets will be tagged with the correct TLS send tag. (This latter case has been tested by configuring both ports of a Chelsio T6 in a lagg and failing over from one port to another. As the connections migrated to the new port, new TLS send tags were allocated for the new port and connections resumed without being dropped.) ifnet TLS can be enabled and disabled on supported network interfaces via new '[-]txtls[46]' options to ifconfig(8). ifnet TLS is supported across both vlan devices and lagg interfaces using failover, lacp with flowid enabled, or lacp with flowid enabled. Applications may request the current KTLS mode of a connection via a new TCP_TXTLS_MODE socket option. They can also use this socket option to toggle between software and ifnet TLS modes. In addition, a testing tool is available in tools/tools/switch_tls. This is modeled on tcpdrop and uses similar syntax. However, instead of dropping connections, -s is used to force KTLS connections to switch to software TLS and -i is used to switch to ifnet TLS. Various sysctls and counters are available under the kern.ipc.tls sysctl node. The kern.ipc.tls.enable node must be set to true to enable KTLS (it is off by default). The use of unmapped mbufs must also be enabled via kern.ipc.mb_use_ext_pgs to enable KTLS. KTLS is enabled via the KERN_TLS kernel option. This patch is the culmination of years of work by several folks including Scott Long and Randall Stewart for the original design and implementation; Drew Gallatin for several optimizations including the use of ext_pgs mbufs, the M_NOTREADY mechanism for TLS records awaiting software encryption, and pluggable software crypto backends; and John Baldwin for modifications to support hardware TLS offload. Reviewed by: gallatin, hselasky, rrs Obtained from: Netflix Sponsored by: Netflix, Chelsio Communications Differential Revision: https://reviews.freebsd.org/D21277
256 lines
9.4 KiB
C
256 lines
9.4 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (c) 1982, 1986, 1990, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)socketvar.h 8.3 (Berkeley) 2/19/95
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
#ifndef _SYS_SOCKBUF_H_
|
|
#define _SYS_SOCKBUF_H_
|
|
|
|
/*
|
|
* Constants for sb_flags field of struct sockbuf/xsockbuf.
|
|
*/
|
|
#define SB_WAIT 0x04 /* someone is waiting for data/space */
|
|
#define SB_SEL 0x08 /* someone is selecting */
|
|
#define SB_ASYNC 0x10 /* ASYNC I/O, need signals */
|
|
#define SB_UPCALL 0x20 /* someone wants an upcall */
|
|
#define SB_NOINTR 0x40 /* operations not interruptible */
|
|
#define SB_AIO 0x80 /* AIO operations queued */
|
|
#define SB_KNOTE 0x100 /* kernel note attached */
|
|
#define SB_NOCOALESCE 0x200 /* don't coalesce new data into existing mbufs */
|
|
#define SB_IN_TOE 0x400 /* socket buffer is in the middle of an operation */
|
|
#define SB_AUTOSIZE 0x800 /* automatically size socket buffer */
|
|
#define SB_STOP 0x1000 /* backpressure indicator */
|
|
#define SB_AIO_RUNNING 0x2000 /* AIO operation running */
|
|
#define SB_TLS_IFNET 0x4000 /* has used / is using ifnet KTLS */
|
|
|
|
#define SBS_CANTSENDMORE 0x0010 /* can't send more data to peer */
|
|
#define SBS_CANTRCVMORE 0x0020 /* can't receive more data from peer */
|
|
#define SBS_RCVATMARK 0x0040 /* at mark on input */
|
|
|
|
#if defined(_KERNEL) || defined(_WANT_SOCKET)
|
|
#include <sys/_lock.h>
|
|
#include <sys/_mutex.h>
|
|
#include <sys/_sx.h>
|
|
#include <sys/_task.h>
|
|
|
|
#define SB_MAX (2*1024*1024) /* default for max chars in sockbuf */
|
|
|
|
struct ktls_session;
|
|
struct mbuf;
|
|
struct sockaddr;
|
|
struct socket;
|
|
struct thread;
|
|
struct selinfo;
|
|
|
|
/*
|
|
* Variables for socket buffering.
|
|
*
|
|
* Locking key to struct sockbuf:
|
|
* (a) locked by SOCKBUF_LOCK().
|
|
* (b) locked by sblock()
|
|
*/
|
|
struct sockbuf {
|
|
struct mtx sb_mtx; /* sockbuf lock */
|
|
struct sx sb_sx; /* prevent I/O interlacing */
|
|
struct selinfo *sb_sel; /* process selecting read/write */
|
|
short sb_state; /* (a) socket state on sockbuf */
|
|
#define sb_startzero sb_mb
|
|
struct mbuf *sb_mb; /* (a) the mbuf chain */
|
|
struct mbuf *sb_mbtail; /* (a) the last mbuf in the chain */
|
|
struct mbuf *sb_lastrecord; /* (a) first mbuf of last
|
|
* record in socket buffer */
|
|
struct mbuf *sb_sndptr; /* (a) pointer into mbuf chain */
|
|
struct mbuf *sb_fnrdy; /* (a) pointer to first not ready buffer */
|
|
u_int sb_sndptroff; /* (a) byte offset of ptr into chain */
|
|
u_int sb_acc; /* (a) available chars in buffer */
|
|
u_int sb_ccc; /* (a) claimed chars in buffer */
|
|
u_int sb_hiwat; /* (a) max actual char count */
|
|
u_int sb_mbcnt; /* (a) chars of mbufs used */
|
|
u_int sb_mcnt; /* (a) number of mbufs in buffer */
|
|
u_int sb_ccnt; /* (a) number of clusters in buffer */
|
|
u_int sb_mbmax; /* (a) max chars of mbufs to use */
|
|
u_int sb_ctl; /* (a) non-data chars in buffer */
|
|
int sb_lowat; /* (a) low water mark */
|
|
sbintime_t sb_timeo; /* (a) timeout for read/write */
|
|
uint64_t sb_tls_seqno; /* (a) TLS seqno */
|
|
struct ktls_session *sb_tls_info; /* (a + b) TLS state */
|
|
short sb_flags; /* (a) flags, see above */
|
|
int (*sb_upcall)(struct socket *, void *, int); /* (a) */
|
|
void *sb_upcallarg; /* (a) */
|
|
TAILQ_HEAD(, kaiocb) sb_aiojobq; /* (a) pending AIO ops */
|
|
struct task sb_aiotask; /* AIO task */
|
|
};
|
|
|
|
#endif /* defined(_KERNEL) || defined(_WANT_SOCKET) */
|
|
#ifdef _KERNEL
|
|
|
|
/*
|
|
* Per-socket buffer mutex used to protect most fields in the socket
|
|
* buffer.
|
|
*/
|
|
#define SOCKBUF_MTX(_sb) (&(_sb)->sb_mtx)
|
|
#define SOCKBUF_LOCK_INIT(_sb, _name) \
|
|
mtx_init(SOCKBUF_MTX(_sb), _name, NULL, MTX_DEF)
|
|
#define SOCKBUF_LOCK_DESTROY(_sb) mtx_destroy(SOCKBUF_MTX(_sb))
|
|
#define SOCKBUF_LOCK(_sb) mtx_lock(SOCKBUF_MTX(_sb))
|
|
#define SOCKBUF_OWNED(_sb) mtx_owned(SOCKBUF_MTX(_sb))
|
|
#define SOCKBUF_UNLOCK(_sb) mtx_unlock(SOCKBUF_MTX(_sb))
|
|
#define SOCKBUF_LOCK_ASSERT(_sb) mtx_assert(SOCKBUF_MTX(_sb), MA_OWNED)
|
|
#define SOCKBUF_UNLOCK_ASSERT(_sb) mtx_assert(SOCKBUF_MTX(_sb), MA_NOTOWNED)
|
|
|
|
/*
|
|
* Socket buffer private mbuf(9) flags.
|
|
*/
|
|
#define M_NOTREADY M_PROTO1 /* m_data not populated yet */
|
|
#define M_BLOCKED M_PROTO2 /* M_NOTREADY in front of m */
|
|
#define M_NOTAVAIL (M_NOTREADY | M_BLOCKED)
|
|
|
|
void sbappend(struct sockbuf *sb, struct mbuf *m, int flags);
|
|
void sbappend_locked(struct sockbuf *sb, struct mbuf *m, int flags);
|
|
void sbappendstream(struct sockbuf *sb, struct mbuf *m, int flags);
|
|
void sbappendstream_locked(struct sockbuf *sb, struct mbuf *m, int flags);
|
|
int sbappendaddr(struct sockbuf *sb, const struct sockaddr *asa,
|
|
struct mbuf *m0, struct mbuf *control);
|
|
int sbappendaddr_locked(struct sockbuf *sb, const struct sockaddr *asa,
|
|
struct mbuf *m0, struct mbuf *control);
|
|
int sbappendaddr_nospacecheck_locked(struct sockbuf *sb,
|
|
const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control);
|
|
void sbappendcontrol(struct sockbuf *sb, struct mbuf *m0,
|
|
struct mbuf *control);
|
|
void sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0,
|
|
struct mbuf *control);
|
|
void sbappendrecord(struct sockbuf *sb, struct mbuf *m0);
|
|
void sbappendrecord_locked(struct sockbuf *sb, struct mbuf *m0);
|
|
void sbcompress(struct sockbuf *sb, struct mbuf *m, struct mbuf *n);
|
|
struct mbuf *
|
|
sbcreatecontrol(caddr_t p, int size, int type, int level);
|
|
void sbdestroy(struct sockbuf *sb, struct socket *so);
|
|
void sbdrop(struct sockbuf *sb, int len);
|
|
void sbdrop_locked(struct sockbuf *sb, int len);
|
|
struct mbuf *
|
|
sbcut_locked(struct sockbuf *sb, int len);
|
|
void sbdroprecord(struct sockbuf *sb);
|
|
void sbdroprecord_locked(struct sockbuf *sb);
|
|
void sbflush(struct sockbuf *sb);
|
|
void sbflush_locked(struct sockbuf *sb);
|
|
void sbrelease(struct sockbuf *sb, struct socket *so);
|
|
void sbrelease_internal(struct sockbuf *sb, struct socket *so);
|
|
void sbrelease_locked(struct sockbuf *sb, struct socket *so);
|
|
int sbsetopt(struct socket *so, int cmd, u_long cc);
|
|
int sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so,
|
|
struct thread *td);
|
|
void sbsndptr_adv(struct sockbuf *sb, struct mbuf *mb, u_int len);
|
|
struct mbuf *
|
|
sbsndptr_noadv(struct sockbuf *sb, u_int off, u_int *moff);
|
|
struct mbuf *
|
|
sbsndmbuf(struct sockbuf *sb, u_int off, u_int *moff);
|
|
int sbwait(struct sockbuf *sb);
|
|
int sblock(struct sockbuf *sb, int flags);
|
|
void sbunlock(struct sockbuf *sb);
|
|
void sballoc(struct sockbuf *, struct mbuf *);
|
|
void sbfree(struct sockbuf *, struct mbuf *);
|
|
int sbready(struct sockbuf *, struct mbuf *, int);
|
|
|
|
/*
|
|
* Return how much data is available to be taken out of socket
|
|
* buffer right now.
|
|
*/
|
|
static inline u_int
|
|
sbavail(struct sockbuf *sb)
|
|
{
|
|
|
|
#if 0
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
#endif
|
|
return (sb->sb_acc);
|
|
}
|
|
|
|
/*
|
|
* Return how much data sits there in the socket buffer
|
|
* It might be that some data is not yet ready to be read.
|
|
*/
|
|
static inline u_int
|
|
sbused(struct sockbuf *sb)
|
|
{
|
|
|
|
#if 0
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
#endif
|
|
return (sb->sb_ccc);
|
|
}
|
|
|
|
/*
|
|
* How much space is there in a socket buffer (so->so_snd or so->so_rcv)?
|
|
* This is problematical if the fields are unsigned, as the space might
|
|
* still be negative (ccc > hiwat or mbcnt > mbmax).
|
|
*/
|
|
static inline long
|
|
sbspace(struct sockbuf *sb)
|
|
{
|
|
int bleft, mleft; /* size should match sockbuf fields */
|
|
|
|
#if 0
|
|
SOCKBUF_LOCK_ASSERT(sb);
|
|
#endif
|
|
|
|
if (sb->sb_flags & SB_STOP)
|
|
return(0);
|
|
|
|
bleft = sb->sb_hiwat - sb->sb_ccc;
|
|
mleft = sb->sb_mbmax - sb->sb_mbcnt;
|
|
|
|
return ((bleft < mleft) ? bleft : mleft);
|
|
}
|
|
|
|
#define SB_EMPTY_FIXUP(sb) do { \
|
|
if ((sb)->sb_mb == NULL) { \
|
|
(sb)->sb_mbtail = NULL; \
|
|
(sb)->sb_lastrecord = NULL; \
|
|
} \
|
|
} while (/*CONSTCOND*/0)
|
|
|
|
#ifdef SOCKBUF_DEBUG
|
|
void sblastrecordchk(struct sockbuf *, const char *, int);
|
|
void sblastmbufchk(struct sockbuf *, const char *, int);
|
|
void sbcheck(struct sockbuf *, const char *, int);
|
|
#define SBLASTRECORDCHK(sb) sblastrecordchk((sb), __FILE__, __LINE__)
|
|
#define SBLASTMBUFCHK(sb) sblastmbufchk((sb), __FILE__, __LINE__)
|
|
#define SBCHECK(sb) sbcheck((sb), __FILE__, __LINE__)
|
|
#else
|
|
#define SBLASTRECORDCHK(sb) do {} while (0)
|
|
#define SBLASTMBUFCHK(sb) do {} while (0)
|
|
#define SBCHECK(sb) do {} while (0)
|
|
#endif /* SOCKBUF_DEBUG */
|
|
|
|
#endif /* _KERNEL */
|
|
|
|
#endif /* _SYS_SOCKBUF_H_ */
|