diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index ca4a636..1360a0e 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -225,7 +225,8 @@ struct tcp_sock {
fastopen_connect:1, /* FASTOPEN_CONNECT sockopt */
fastopen_no_cookie:1, /* Allow send/recv SYN+data without a cookie */
is_sack_reneg:1, /* in recovery from loss with SACK reneg? */
- unused:2;
+ encap:1, /* TCP IKE/ESP encapsulation */
+ encap_lenhi_valid:1;
u8 nonagle : 4,/* Disable Nagle algorithm? */
thin_lto : 1,/* Use linear timeouts for thin streams */
unused1 : 1,@@ -373,6 +374,16 @@ struct tcp_sock {
*/
struct request_sock *fastopen_rsk;
u32 *saved_syn;
+
+#ifdef CONFIG_XFRM
+/* TCP ESP encapsulation */
+ struct sk_buff *encap_in;
+ struct sk_buff_head encap_out;
+ u32 encap_seq;
+ u32 encap_last;
+ u16 encap_backlog;
+ u8 encap_lenhi;
+#endif
};
enum tsq_enum {@@ -384,6 +395,7 @@ enum tsq_enum {
TCP_MTU_REDUCED_DEFERRED, /* tcp_v{4|6}_err() could not call
* tcp_v{4|6}_mtu_reduced()
*/
+ TCP_ESP_DEFERRED, /* esp_output_tcp_encap2 queued packets */
};
enum tsq_flags {@@ -393,6 +405,7 @@ enum tsq_flags {
TCPF_WRITE_TIMER_DEFERRED = (1UL << TCP_WRITE_TIMER_DEFERRED),
TCPF_DELACK_TIMER_DEFERRED = (1UL << TCP_DELACK_TIMER_DEFERRED),
TCPF_MTU_REDUCED_DEFERRED = (1UL << TCP_MTU_REDUCED_DEFERRED),
+ TCPF_ESP_DEFERRED = (1UL << TCP_ESP_DEFERRED),
};
static inline struct tcp_sock *tcp_sk(const struct sock *sk)diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6da880d..6513ae2 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -327,6 +327,7 @@ int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset,
size_t size, int flags);
ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
size_t size, int flags);
+int tcp_encap_output(struct sock *sk, struct sk_buff *skb);
void tcp_release_cb(struct sock *sk);
void tcp_wfree(struct sk_buff *skb);
void tcp_write_timer_handler(struct sock *sk);
@@ -399,6 +400,7 @@ int compat_tcp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen);
void tcp_set_keepalive(struct sock *sk, int val);
void tcp_syn_ack_timeout(const struct request_sock *req);
+void tcp_cleanup_rbuf(struct sock *sk, int copied);
int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
int flags, int *addr_len);
void tcp_parse_options(const struct net *net, const struct sk_buff *skb,
@@ -789,7 +791,8 @@ struct tcp_skb_cb {
__u8 txstamp_ack:1, /* Record TX timestamp for ack? */
eor:1, /* Is skb MSG_EOR marked? */
has_rxtstamp:1, /* SKB has a RX timestamp */
- unused:5;
+ esp_skip:1, /* SKB is pure ESP */
+ unused:4;
__u32 ack_seq; /* Sequence number ACK'd */
union {
struct {@@ -2062,4 +2065,26 @@ static inline bool tcp_bpf_ca_needs_ecn(struct sock *sk)
#if IS_ENABLED(CONFIG_SMC)
extern struct static_key_false tcp_have_smc;
#endif
+
+#ifdef CONFIG_XFRM
+DECLARE_STATIC_KEY_FALSE(tcp_encap_needed);
+
+int tcp_encap_enable(struct sock *sk);
+
+static inline bool tcp_esp_skipped(struct sk_buff *skb)
+{
+ return TCP_SKB_CB(skb)->esp_skip;
+}
+#else
+static inline int tcp_encap_enable(struct sock *sk)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline bool tcp_esp_skipped(struct sk_buff *skb)
+{
+ return false;
+}
+#endif
+
#endif /* _TCP_H */diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index b4a4f64..769cab0 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -122,6 +122,7 @@ enum {
#define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions */
#define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie) */
#define TCP_FASTOPEN_NO_COOKIE 34 /* Enable TFO without a TFO cookie */
+#define TCP_ENCAP 35 /* Set the socket to accept encapsulated packets */
struct tcp_repair_opt {
__u32 opt_code;diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h
index efb7b59..1102846 100644
--- a/include/uapi/linux/udp.h
+++ b/include/uapi/linux/udp.h
@@ -39,5 +39,6 @@ struct udphdr {
#define UDP_ENCAP_L2TPINUDP 3 /* rfc2661 */
#define UDP_ENCAP_GTP0 4 /* GSM TS 09.60 */
#define UDP_ENCAP_GTP1U 5 /* 3GPP TS 29.060 */
+#define TCP_ENCAP_ESPINTCP 6 /* Yikes, this is really xfrm encap types. */
#endif /* _UAPI_LINUX_UDP_H */diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index f08eebe..032b46c 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1545,7 +1545,7 @@ static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len)
* calculation of whether or not we must ACK for the sake of
* a window update.
*/
-static void tcp_cleanup_rbuf(struct sock *sk, int copied)
+void tcp_cleanup_rbuf(struct sock *sk, int copied)
{
struct tcp_sock *tp = tcp_sk(sk);
bool time_to_ack = false;@@ -1627,6 +1627,35 @@ static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
return NULL;
}
+#ifdef CONFIG_XFRM
+static void __tcp_esp_skip(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+ unsigned offset;
+ unsigned used;
+
+ while ((skb = tcp_recv_skb(sk, tp->copied_seq, &offset)) &&
+ tcp_esp_skipped(skb)) {
+ used = skb->len - offset;
+ tp->copied_seq += used;
+ tcp_rcv_space_adjust(sk);
+ sk_eat_skb(sk, skb);
+ }
+}
+
+static inline void tcp_esp_skip(struct sock *sk, int flags)
+{
+ if (static_branch_unlikely(&tcp_encap_needed) &&
+ tcp_sk(sk)->encap && !(flags & MSG_PEEK))
+ __tcp_esp_skip(sk);
+}
+#else
+static inline void tcp_esp_skip(struct sock *sk, int flags)
+{
+}
+#endif
+
/*
* This routine provides an alternative to tcp_recvmsg() for routines
* that would like to handle copying from skbuffs directly in 'sendfile'@@ -1650,7 +1679,9 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
if (sk->sk_state == TCP_LISTEN)
return -ENOTCONN;
while ((skb = tcp_recv_skb(sk, seq, &offset)) != NULL) {
- if (offset < skb->len) {
+ if (tcp_esp_skipped(skb))
+ seq += skb->len - offset;
+ else if (offset < skb->len) {
int used;
size_t len;
@@ -1704,6 +1735,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
/* Clean up data we have read: This will do ACK frames. */
if (copied > 0) {
tcp_recv_skb(sk, seq, &offset);
+ tcp_esp_skip(sk, 0);
tcp_cleanup_rbuf(sk, copied);
}
return copied;@@ -1946,6 +1978,13 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
found_ok_skb:
/* Ok so how much can we use? */
used = skb->len - offset;
+
+ if (tcp_esp_skipped(skb)) {
+ *seq += used;
+ urg_hole += used;
+ goto skip_copy;
+ }
+
if (len < used)
used = len;
@@ -2009,6 +2048,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
break;
} while (len > 0);
+ tcp_esp_skip(sk, flags);
+
/* According to UNIX98, msg_name/msg_namelen are ignored
* on connected socket. I was just happy when found this 8) --ANK
*/
@@ -2146,6 +2187,21 @@ bool tcp_check_oom(struct sock *sk, int shift)
return too_many_orphans || out_of_socket_memory;
}
+#ifdef CONFIG_XFRM
+static inline void tcp_encap_free(struct tcp_sock *tp)
+{
+ struct sk_buff *skb;
+
+ kfree_skb(tp->encap_in);
+ while ((skb = __skb_dequeue(&tp->encap_out)) != NULL)
+ __kfree_skb(skb);
+}
+#else
+static inline void tcp_encap_free(struct tcp_sock *tp)
+{
+}
+#endif
+
void tcp_close(struct sock *sk, long timeout)
{
struct sk_buff *skb;@@ -2177,6 +2233,8 @@ void tcp_close(struct sock *sk, long timeout)
__kfree_skb(skb);
}
+ tcp_encap_free(tcp_sk(sk));
+
sk_mem_reclaim(sk);
/* If socket has been already reset (e.g. in tcp_reset()) - kill it. */
@@ -2583,6 +2641,12 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
return tcp_fastopen_reset_cipher(net, sk, key, sizeof(key));
}
+ case TCP_ENCAP:
+ if (sk->sk_state == TCP_ESTABLISHED)
+ return tcp_encap_enable(sk);
+ else
+ return -ENOTCONN;
+ break;
default:
/* fallthru */
break;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9550cc4..22c9f70 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -72,12 +72,14 @@
#include <linux/prefetch.h>
#include <net/dst.h>
#include <net/tcp.h>
+#include <net/xfrm.h>
#include <net/inet_common.h>
#include <linux/ipsec.h>
#include <asm/unaligned.h>
#include <linux/errqueue.h>
#include <trace/events/tcp.h>
#include <linux/static_key.h>
+#include <uapi/linux/udp.h>
int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
@@ -110,6 +112,10 @@
#define REXMIT_LOST 1 /* retransmit packets marked lost */
#define REXMIT_NEW 2 /* FRTO-style transmit of unsent/new packets */
+#ifdef CONFIG_XFRM
+DEFINE_STATIC_KEY_FALSE(tcp_encap_needed);
+#endif
+
static void tcp_gro_dev_warn(struct sock *sk, const struct sk_buff *skb,
unsigned int len)
{@@ -4294,6 +4300,314 @@ static void tcp_drop(struct sock *sk, struct sk_buff *skb)
__kfree_skb(skb);
}
+#ifdef CONFIG_XFRM
+static void tcp_set_encap_seq(struct tcp_sock *tp, struct sk_buff *skb,
+ unsigned offset, __be16 len)
+{
+ while ((offset += min(be16_to_cpu(len), 2)) + 1 < skb->len)
+ skb_copy_bits(skb, offset, &len, 2);
+
+ if (skb->len <= offset) {
+ tp->encap_seq = TCP_SKB_CB(skb)->seq + offset;
+ return;
+ }
+
+ skb_copy_bits(skb, offset, &tp->encap_lenhi, 1);
+ tp->encap_lenhi_valid = true;
+}
+
+static void tcp_encap_error(struct tcp_sock *tp, struct sk_buff *skb,
+ unsigned offset)
+{
+ struct sk_buff *prev = tp->encap_in;
+ union {
+ u8 bytes[2];
+ __be16 len;
+ } hdr;
+
+ if (!prev) {
+ tcp_set_encap_seq(tp, skb, offset - 2, 0);
+ return;
+ }
+
+ if (prev->len == 1) {
+ skb_copy_bits(prev, 0, &hdr.bytes[0], 1);
+ skb_copy_bits(skb, offset, &hdr.bytes[1], 1);
+ tcp_set_encap_seq(tp, skb, offset - 1, hdr.len);
+ }
+
+ __kfree_skb(prev);
+ tp->encap_in = NULL;
+}
+
+static void tcp_encap_error_free(struct tcp_sock *tp, struct sk_buff *skb,
+ unsigned offset)
+{
+ tcp_encap_error(tp, skb, offset);
+ __kfree_skb(skb);
+}
+
+static int tcp_decap_skb(struct tcp_sock *tp, struct sk_buff *skb)
+{
+ /* Get rid of length field to get pure ESP. */
+ if (!__pskb_pull(skb, 2))
+ return -ENOMEM;
+ skb_reset_transport_header(skb);
+
+ rcu_read_lock();
+ skb->dev = dev_get_by_index_rcu(sock_net((struct sock *)tp),
+ skb->skb_iif);
+ if (skb->dev)
+ xfrm4_rcv_encap(skb, IPPROTO_ESP, 0, TCP_ENCAP_ESPINTCP);
+ rcu_read_unlock();
+ return 0;
+}
+
+static bool __tcp_encap_process(struct tcp_sock *tp, struct sk_buff *skb)
+{
+ struct sock *sk = (void *)tp;
+ struct {
+ union {
+ u8 bytes[2];
+ __be16 len;
+ };
+ __be32 spi;
+ } hdr;
+ struct sk_buff *prev;
+ bool eaten = false;
+ unsigned headlen;
+ unsigned offset2;
+ unsigned offset;
+ bool fragstolen;
+ int delta;
+
+ offset = tp->encap_last - TCP_SKB_CB(skb)->seq;
+ if (unlikely(skb->len <= offset))
+ return false;
+
+ tp->encap_last = TCP_SKB_CB(skb)->seq + skb->len;
+
+ if (unlikely(tp->encap_lenhi_valid)) {
+ tp->encap_lenhi_valid = false;
+ hdr.bytes[0] = tp->encap_lenhi;
+ skb_copy_bits(skb, offset, &hdr.bytes[1], 1);
+ tcp_set_encap_seq(tp, skb, offset - 1, hdr.len);
+ return false;
+ }
+
+ if (unlikely(tp->urg_data))
+ goto slow_path;
+
+ if (unlikely(tp->encap_in))
+ goto slow_path;
+
+ offset = tp->encap_seq - TCP_SKB_CB(skb)->seq;
+ if (unlikely(skb->len <= offset))
+ return false;
+
+ if (unlikely(offset))
+ goto slow_path;
+
+ if (unlikely(skb_has_frag_list(skb)))
+ goto slow_path;
+
+ offset2 = 0;
+
+ do {
+ if (unlikely(skb->len < sizeof(hdr)))
+ goto slow_path;
+
+ skb_copy_bits(skb, offset2, &hdr, sizeof(hdr));
+ offset2 += be16_to_cpu(hdr.len);
+ if (skb->len < offset2)
+ goto slow_path;
+
+ if (!hdr.spi)
+ goto slow_path;
+ } while (skb->len > offset2);
+
+ if (offset2 != be16_to_cpu(hdr.len))
+ goto slow_path;
+
+ tp->encap_seq = TCP_SKB_CB(skb)->seq + skb->len;
+
+ if (!skb_peek_tail(&sk->sk_receive_queue) &&
+ !(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) {
+ tp->copied_seq = tp->encap_seq;
+ tcp_rcv_space_adjust(sk);
+ tcp_cleanup_rbuf(sk, skb->len);
+ eaten = true;
+ }
+
+ TCP_SKB_CB(skb)->esp_skip = 1;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return eaten;
+
+ if (unlikely(tcp_decap_skb(tp, skb)))
+ __kfree_skb(skb);
+
+ return eaten;
+
+slow_path:
+ headlen = -(skb_mac_header_was_set(skb) ? skb_mac_offset(skb) :
+ skb_network_offset(skb));
+ __skb_push(skb, headlen);
+ prev = skb;
+
+ skb = pskb_copy(prev, GFP_ATOMIC);
+ __skb_pull(prev, headlen);
+
+ if (!skb) {
+ tcp_encap_error(tp, prev, offset);
+ return false;
+ }
+
+ __skb_pull(skb, headlen);
+ skb->mac_len = prev->mac_len;
+
+ if (!__pskb_pull(skb, offset)) {
+ tcp_encap_error_free(tp, skb, offset);
+ return false;
+ }
+
+ TCP_SKB_CB(skb)->seq += offset;
+ prev = tp->encap_in;
+ tp->encap_in = NULL;
+
+ if (!prev)
+ prev = skb;
+ else if (skb_try_coalesce(prev, skb, &fragstolen, &delta))
+ kfree_skb_partial(skb, fragstolen);
+ else {
+ skb_shinfo(prev)->frag_list = skb;
+ prev->data_len += skb->len;
+ prev->len += skb->len;
+ prev->truesize += skb->truesize;
+ }
+
+ /* We could do a list instead of linearising, but that would
+ * open the door to abuses such as a stream of single-byte
+ * datagrams up to 64K.
+ */
+ if (skb_has_frag_list(prev) && __skb_linearize(prev)) {
+ tcp_encap_error_free(tp, prev, 0);
+ return false;
+ }
+
+ headlen = -(skb_mac_header_was_set(prev) ? skb_mac_offset(prev) :
+ skb_network_offset(prev));
+
+ while (prev->len >= sizeof(hdr.len)) {
+ skb_copy_bits(prev, 0, &hdr,
+ min((unsigned)sizeof(hdr), prev->len));
+
+ offset = be16_to_cpu(hdr.len);
+ tp->encap_seq = TCP_SKB_CB(prev)->seq + offset;
+
+ if (prev->len < offset)
+ break;
+
+ skb = prev;
+ if (prev->len > offset) {
+ int nsize = skb_headlen(skb) - offset;
+
+ if (nsize < 0)
+ nsize = 0;
+
+ prev = alloc_skb(nsize + headlen, GFP_ATOMIC);
+ if (!prev) {
+ tcp_encap_error_free(tp, skb, offset);
+ return false;
+ }
+
+ /* Slap on a header on each message. */
+ if (skb_mac_header_was_set(skb)) {
+ skb_reset_mac_header(prev);
+ skb_set_network_header(
+ prev, skb_mac_header_len(skb));
+ prev->mac_len = skb->mac_len;
+ } else
+ skb_reset_network_header(prev);
+ memcpy(__skb_put(prev, headlen),
+ skb->data - headlen, headlen);
+ __skb_pull(prev, headlen);
+
+ nsize = skb->len - offset - nsize;
+
+ skb_split(skb, prev, offset);
+ skb->truesize -= nsize;
+ prev->truesize += nsize;
+ prev->skb_iif = skb->skb_iif;
+ TCP_SKB_CB(prev)->seq = TCP_SKB_CB(skb)->seq + offset;
+ }
+
+ if (!hdr.spi || tcp_decap_skb(tp, skb))
+ __kfree_skb(skb);
+
+ if (prev == skb)
+ return eaten;
+ }
+
+ tp->encap_in = prev;
+
+ return false;
+}
+
+int tcp_encap_enable(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+
+ lock_sock(sk);
+
+ if (tp->encap)
+ goto out;
+
+ __skb_queue_head_init(&tp->encap_out);
+
+ tp->encap_last = tp->encap_seq = tp->copied_seq;
+
+ skb_queue_walk(&sk->sk_receive_queue, skb)
+ __tcp_encap_process(tp, skb);
+
+ tp->encap = 1;
+ static_branch_enable(&tcp_encap_needed);
+
+out:
+ release_sock(sk);
+
+ return 0;
+}
+
+static inline bool tcp_encap_process(struct tcp_sock *tp, struct sk_buff *skb)
+{
+ if (static_branch_unlikely(&tcp_encap_needed) && tp->encap)
+ return __tcp_encap_process(tp, skb);
+
+ return false;
+}
+#else
+static inline bool tcp_encap_process(struct tcp_sock *tp, struct sk_buff *skb)
+{
+ return false;
+}
+#endif
+
+static bool tcp_eat_skb(struct sock *sk, struct sk_buff *skb, bool *fragstolen)
+{
+ struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
+ struct tcp_sock *tp = tcp_sk(sk);
+ bool eaten;
+
+ eaten = tcp_encap_process(tp, skb) ||
+ (tail && tcp_try_coalesce(sk, tail, skb, fragstolen));
+ tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
+
+ return eaten;
+}
+
/* This one checks to see if we can put data from the
* out_of_order queue into the receive_queue.
*/@@ -4302,7 +4616,7 @@ static void tcp_ofo_queue(struct sock *sk)
struct tcp_sock *tp = tcp_sk(sk);
__u32 dsack_high = tp->rcv_nxt;
bool fin, fragstolen, eaten;
- struct sk_buff *skb, *tail;
+ struct sk_buff *skb;
struct rb_node *p;
p = rb_first(&tp->out_of_order_queue);
@@ -4329,9 +4643,7 @@ static void tcp_ofo_queue(struct sock *sk)
tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
TCP_SKB_CB(skb)->end_seq);
- tail = skb_peek_tail(&sk->sk_receive_queue);
- eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen);
- tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
+ eaten = tcp_eat_skb(sk, skb, &fragstolen);
fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN;
if (!eaten)
__skb_queue_tail(&sk->sk_receive_queue, skb);
@@ -4508,13 +4820,9 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
bool *fragstolen)
{
int eaten;
- struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
__skb_pull(skb, hdrlen);
- eaten = (tail &&
- tcp_try_coalesce(sk, tail,
- skb, fragstolen)) ? 1 : 0;
- tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq);
+ eaten = tcp_eat_skb(sk, skb, fragstolen);
if (!eaten) {
__skb_queue_tail(&sk->sk_receive_queue, skb);
skb_set_owner_r(skb, sk);diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 77ea45d..a613ff4 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1617,6 +1617,7 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph,
TCP_SKB_CB(skb)->sacked = 0;
TCP_SKB_CB(skb)->has_rxtstamp =
skb->tstamp || skb_hwtstamps(skb)->hwtstamp;
+ TCP_SKB_CB(skb)->esp_skip = 0;
}
/*
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a4d214c..66e1121 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -830,10 +830,53 @@ static void tcp_tasklet_func(unsigned long data)
}
}
+#ifdef CONFIG_XFRM
+int tcp_encap_output(struct sock *sk, struct sk_buff *skb)
+{
+ int offset;
+ unsigned len;
+
+ if (sk->sk_state != TCP_ESTABLISHED)
+ return -ECONNRESET;
+
+ offset = skb_transport_offset(skb);
+ len = skb->len - offset;
+
+ *(__be16 *)skb_transport_header(skb) = cpu_to_be16(len);
+
+ offset = skb_send_sock_locked(sk, skb, offset, len);
+ if (offset >= 0) {
+ __kfree_skb(skb);
+ offset = 0;
+ }
+
+ return offset;
+}
+EXPORT_SYMBOL(tcp_encap_output);
+
+static void tcp_process_encap(struct sock *sk)
+{
+ struct sk_buff_head queue;
+ struct sk_buff *skb;
+
+ __skb_queue_head_init(&queue);
+ skb_queue_splice_init(&tcp_sk(sk)->encap_out, &queue);
+
+ while ((skb = __skb_dequeue(&queue)))
+ if (tcp_encap_output(sk, skb))
+ __kfree_skb(skb);
+}
+#else
+static inline void tcp_process_encap(struct sock *sk)
+{
+}
+#endif
+
#define TCP_DEFERRED_ALL (TCPF_TSQ_DEFERRED | \
TCPF_WRITE_TIMER_DEFERRED | \
TCPF_DELACK_TIMER_DEFERRED | \
- TCPF_MTU_REDUCED_DEFERRED)
+ TCPF_MTU_REDUCED_DEFERRED | \
+ TCPF_ESP_DEFERRED)
/**
* tcp_release_cb - tcp release_sock() callback
* @sk: socket@@ -879,6 +922,8 @@ void tcp_release_cb(struct sock *sk)
inet_csk(sk)->icsk_af_ops->mtu_reduced(sk);
__sock_put(sk);
}
+ if (flags & TCPF_ESP_DEFERRED)
+ tcp_process_encap(sk);
}
EXPORT_SYMBOL(tcp_release_cb);
@@ -1609,6 +1654,7 @@ unsigned int tcp_current_mss(struct sock *sk)
return mss_now;
}
+EXPORT_SYMBOL(tcp_current_mss);
/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
* As additional protections, we do not touch cwnd in retransmission phases,