ck: net: kernel hookers service for toa module
fix #32251972 LVS fullnat will replace network traffic's source ip with its local ip, and thus the backend servers cannot obtain the real client ip. To solve this, LVS has introduced the tcp option address (TOA) to store the essential ip address information in the last tcp ack packet of the 3-way handshake, and the backend servers need to retrieve it from the packet header. In this patch, we have introduced the sk_toa_data member in the sock structure to hold the TOA information. There used to be an in-tree module for TOA managing, whereas it has now been maintained as an standalone module. In this case, the toa module should register its hook function(s) using the provided interfaces in the hookers module. TOA in sock structure: __be32 sk_toa_data[16]; The hookers module only provides the sk_toa_data placeholder, and the toa module can use this variable through the layout it needs. Hook interfaces: The hookers module replaces the kernel's syn_recv_sock and getname handler with a stub that chains the toa module's hook function(s) to the original handling function. The hookers module allows hook functions to be installed and uninstalled in any order. toa module: The external toa module will be provided in separate RPM package. Reviewed-by: Caspar Zhang <caspar@linux.alibaba.com> Signed-off-by: George Zhang <georgezhang@linux.alibaba.com> Signed-off-by: Xu Yu <xuyu@linux.alibaba.com> Signed-off-by: Xin Hao <xhao@linux.alibaba.com> Reviewed-by: Tony Lu <tonylu@linux.alibaba.com>
This commit is contained in:
parent
4f01df9f66
commit
cf0dd0a267
|
@ -1053,6 +1053,7 @@ CONFIG_NET_KEY_MIGRATE=y
|
|||
# CONFIG_SMC is not set
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=m
|
||||
CONFIG_HOOKERS=m
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
|
|
|
@ -1034,6 +1034,7 @@ CONFIG_NET_KEY_MIGRATE=y
|
|||
# CONFIG_SMC is not set
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=m
|
||||
CONFIG_HOOKERS=m
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
|
|
|
@ -1062,6 +1062,7 @@ CONFIG_NET_KEY_MIGRATE=y
|
|||
# CONFIG_SMC is not set
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=m
|
||||
CONFIG_HOOKERS=m
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
|
|
|
@ -1063,6 +1063,7 @@ CONFIG_NET_KEY_MIGRATE=y
|
|||
# CONFIG_SMC is not set
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=m
|
||||
CONFIG_HOOKERS=m
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2019 Alibaba Group Holding Limited. All Rights Reserved.
|
||||
*
|
||||
* Changes: Li Yu
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_HOOKER_H_
|
||||
#define _LINUX_HOOKER_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct hooked_place;
|
||||
|
||||
struct hooker {
|
||||
struct hooked_place *hplace;
|
||||
void *func; /* the installed hooker function pointer */
|
||||
struct list_head chain;
|
||||
};
|
||||
|
||||
/*
|
||||
* Install the hooker function at specified address.
|
||||
* This function may sleep.
|
||||
*
|
||||
* Parameters:
|
||||
* place - the address that saves function pointer
|
||||
* hooker - the hooker to install, the caller must fill
|
||||
* its func member first
|
||||
*
|
||||
* Return:
|
||||
* 0 - All OK, please note that hooker func may be called before
|
||||
* this return
|
||||
* < 0 - any error, e.g. out of memory, existing same installed hooker
|
||||
*/
|
||||
|
||||
extern int hooker_install(const void *place, struct hooker *hooker);
|
||||
|
||||
/*
|
||||
* Remove the installed hooker function that saved in hooker->func.
|
||||
* This function may sleep.
|
||||
*
|
||||
* Parameters:
|
||||
* place - the address that saves function pointer
|
||||
* hooker - the installed hooker struct
|
||||
*/
|
||||
|
||||
extern void hooker_uninstall(struct hooker *hooker);
|
||||
|
||||
#endif
|
|
@ -343,6 +343,7 @@ struct bpf_local_storage;
|
|||
* @sk_txtime_deadline_mode: set deadline mode for SO_TXTIME
|
||||
* @sk_txtime_report_errors: set report errors mode for SO_TXTIME
|
||||
* @sk_txtime_unused: unused txtime flags
|
||||
* @sk_toa_data: tcp option address (toa) data
|
||||
*/
|
||||
struct sock {
|
||||
/*
|
||||
|
@ -518,6 +519,7 @@ struct sock {
|
|||
#endif
|
||||
void (*sk_destruct)(struct sock *sk);
|
||||
struct sock_reuseport __rcu *sk_reuseport_cb;
|
||||
__be32 sk_toa_data[16];
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
struct bpf_local_storage __rcu *sk_bpf_storage;
|
||||
#endif
|
||||
|
|
|
@ -56,6 +56,9 @@ ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp, __u16 srcp,
|
|||
|
||||
#define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006)
|
||||
|
||||
extern const struct inet_connection_sock_af_ops ipv6_specific;
|
||||
extern const struct inet_connection_sock_af_ops ipv6_mapped;
|
||||
|
||||
void inet6_destroy_sock(struct sock *sk);
|
||||
|
||||
#define IPV6_SEQ_DGRAM_HEADER \
|
||||
|
|
|
@ -67,6 +67,7 @@ source "net/xfrm/Kconfig"
|
|||
source "net/iucv/Kconfig"
|
||||
source "net/smc/Kconfig"
|
||||
source "net/xdp/Kconfig"
|
||||
source "net/hookers/Kconfig"
|
||||
|
||||
config INET
|
||||
bool "TCP/IP networking"
|
||||
|
|
|
@ -87,4 +87,5 @@ endif
|
|||
obj-$(CONFIG_QRTR) += qrtr/
|
||||
obj-$(CONFIG_NET_NCSI) += ncsi/
|
||||
obj-$(CONFIG_XDP_SOCKETS) += xdp/
|
||||
obj-$(CONFIG_HOOKERS) += hookers/
|
||||
obj-$(CONFIG_MPTCP) += mptcp/
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
config HOOKERS
|
||||
tristate "Hooker service"
|
||||
default m
|
||||
depends on X86
|
||||
help
|
||||
Allow replacing and restore the function pointer in any order.
|
||||
See include/linux/hookers.h for details.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#
|
||||
# Makefile for hookers module.
|
||||
#
|
||||
obj-$(CONFIG_HOOKERS) += hookers.o
|
|
@ -0,0 +1,363 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2019 Alibaba Group Holding Limited. All Rights Reserved. */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/transp_v6.h>
|
||||
#include <net/inet_common.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/inet.h>
|
||||
|
||||
#include <linux/hookers.h>
|
||||
|
||||
struct hooked_place {
|
||||
const char *name; /* position information shown in procfs */
|
||||
void *place; /* the kernel address to be hook */
|
||||
void *orig; /* original content at hooked place */
|
||||
void *stub; /* hooker function stub */
|
||||
int nr_hookers; /* how many hookers are linked at below chain */
|
||||
struct list_head chain; /* hookers chain */
|
||||
};
|
||||
|
||||
static spinlock_t hookers_lock;
|
||||
|
||||
static struct sock *
|
||||
ipv4_specific_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req);
|
||||
static int
|
||||
inet_stream_ops_getname_stub(struct socket *sock,
|
||||
struct sockaddr *uaddr, int peer);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static struct sock *
|
||||
ipv6_specific_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req);
|
||||
static struct sock *
|
||||
ipv6_mapped_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req);
|
||||
static int
|
||||
inet6_stream_ops_getname_stub(struct socket *sock,
|
||||
struct sockaddr *uaddr, int peer);
|
||||
#endif
|
||||
|
||||
enum pt_types {
|
||||
IPV4_SPECIFIC_SYN_RECV_SOCK = 0,
|
||||
INET_STREAM_OPS_GETNAME,
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
IPV6_SPECIFIC_SYN_RECV_SOCK,
|
||||
IPV6_MAPPED_SYN_RECV_SOCK,
|
||||
INET6_STREAM_OPS_GETNAME,
|
||||
#endif
|
||||
PLACE_TABLE_SZ
|
||||
};
|
||||
|
||||
static struct hooked_place place_table[] = {
|
||||
{
|
||||
.name = "ipv4_specific.syn_recv_sock",
|
||||
.place = (void *)&ipv4_specific.syn_recv_sock,
|
||||
.stub = ipv4_specific_syn_recv_sock_stub,
|
||||
},
|
||||
|
||||
{
|
||||
.name = "inet_stream_ops.getname",
|
||||
.place = (void *)&inet_stream_ops.getname,
|
||||
.stub = inet_stream_ops_getname_stub,
|
||||
},
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
{
|
||||
.name = "ipv6_specific.syn_recv_sock",
|
||||
.place = (void *)&ipv6_specific.syn_recv_sock,
|
||||
.stub = ipv6_specific_syn_recv_sock_stub,
|
||||
},
|
||||
|
||||
{
|
||||
.name = "ipv6_mapped.syn_recv_sock",
|
||||
.place = (void *)&ipv6_mapped.syn_recv_sock,
|
||||
.stub = ipv6_mapped_syn_recv_sock_stub,
|
||||
},
|
||||
|
||||
{
|
||||
.name = "inet6_stream_ops.getname",
|
||||
.place = (void *)&inet6_stream_ops.getname,
|
||||
.stub = inet6_stream_ops_getname_stub,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct sock *
|
||||
__syn_recv_sock_hstub(struct hooked_place *place,
|
||||
struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req, struct dst_entry *dst,
|
||||
struct request_sock *req_unhash, bool *own_req)
|
||||
{
|
||||
struct hooker *iter;
|
||||
struct sock *(*hooker_func)(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req,
|
||||
struct sock **ret);
|
||||
struct sock *(*orig_func)(struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req);
|
||||
struct sock *ret;
|
||||
|
||||
orig_func = place->orig;
|
||||
ret = orig_func(sk, skb, req, dst, req_unhash, own_req);
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(iter, &place->chain, chain) {
|
||||
hooker_func = iter->func;
|
||||
hooker_func(sk, skb, req, dst, req_unhash, own_req, &ret);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __getname_hstub(struct hooked_place *place,
|
||||
struct socket *sock, struct sockaddr *uaddr,
|
||||
int peer)
|
||||
{
|
||||
struct hooker *iter;
|
||||
int (*hooker_func)(struct socket *sock, struct sockaddr *uaddr,
|
||||
int peer, int *ret);
|
||||
int (*orig_func)(struct socket *sock, struct sockaddr *uaddr,
|
||||
int peer);
|
||||
int ret;
|
||||
|
||||
orig_func = place->orig;
|
||||
ret = orig_func(sock, uaddr, peer);
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(iter, &place->chain, chain) {
|
||||
hooker_func = iter->func;
|
||||
hooker_func(sock, uaddr, peer, &ret);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sock *
|
||||
ipv4_specific_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req)
|
||||
{
|
||||
return __syn_recv_sock_hstub(&place_table[IPV4_SPECIFIC_SYN_RECV_SOCK],
|
||||
sk, skb, req, dst, req_unhash, own_req);
|
||||
}
|
||||
|
||||
static int
|
||||
inet_stream_ops_getname_stub(struct socket *sock,
|
||||
struct sockaddr *uaddr, int peer)
|
||||
{
|
||||
return __getname_hstub(&place_table[INET_STREAM_OPS_GETNAME], sock,
|
||||
uaddr, peer);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static struct sock *
|
||||
ipv6_specific_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req)
|
||||
{
|
||||
return __syn_recv_sock_hstub(&place_table[IPV6_SPECIFIC_SYN_RECV_SOCK],
|
||||
sk, skb, req, dst, req_unhash, own_req);
|
||||
}
|
||||
|
||||
static struct sock *
|
||||
ipv6_mapped_syn_recv_sock_stub(struct sock *sk,
|
||||
struct sk_buff *skb, struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req)
|
||||
{
|
||||
return __syn_recv_sock_hstub(&place_table[IPV6_MAPPED_SYN_RECV_SOCK],
|
||||
sk, skb, req, dst, req_unhash, own_req);
|
||||
}
|
||||
|
||||
static int
|
||||
inet6_stream_ops_getname_stub(struct socket *sock,
|
||||
struct sockaddr *uaddr, int peer)
|
||||
{
|
||||
return __getname_hstub(&place_table[INET6_STREAM_OPS_GETNAME], sock,
|
||||
uaddr, peer);
|
||||
}
|
||||
#endif
|
||||
|
||||
int hooker_install(const void *place, struct hooker *h)
|
||||
{
|
||||
enum pt_types i;
|
||||
struct hooked_place *hplace;
|
||||
|
||||
/* synchronize_rcu() */
|
||||
might_sleep();
|
||||
|
||||
if (!place || !h || !h->func)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < PLACE_TABLE_SZ; i++) {
|
||||
hplace = &place_table[i];
|
||||
if (hplace->place == place) {
|
||||
INIT_LIST_HEAD(&h->chain);
|
||||
spin_lock(&hookers_lock);
|
||||
hplace->nr_hookers++;
|
||||
h->hplace = hplace;
|
||||
list_add_tail_rcu(&h->chain, &place_table[i].chain);
|
||||
spin_unlock(&hookers_lock);
|
||||
synchronize_rcu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (i >= PLACE_TABLE_SZ) ? -EINVAL : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hooker_install);
|
||||
|
||||
void hooker_uninstall(struct hooker *h)
|
||||
{
|
||||
/* synchronize_rcu(); */
|
||||
might_sleep();
|
||||
|
||||
spin_lock(&hookers_lock);
|
||||
list_del_rcu(&h->chain);
|
||||
h->hplace->nr_hookers--;
|
||||
h->hplace = NULL;
|
||||
spin_unlock(&hookers_lock);
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hooker_uninstall);
|
||||
|
||||
static inline unsigned int hookers_clear_cr0(void)
|
||||
{
|
||||
unsigned int cr0 = read_cr0();
|
||||
|
||||
write_cr0(cr0 & 0xfffeffff);
|
||||
return cr0;
|
||||
}
|
||||
|
||||
static inline void hookers_restore_cr0(unsigned int val)
|
||||
{
|
||||
write_cr0(val);
|
||||
}
|
||||
|
||||
static void *hookers_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos < PLACE_TABLE_SZ)
|
||||
return &place_table[*pos];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *hookers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
if (++(*pos) >= PLACE_TABLE_SZ)
|
||||
return NULL;
|
||||
|
||||
return (void *)&place_table[*pos];
|
||||
}
|
||||
|
||||
static void hookers_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int hookers_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct hooked_place *hplace = (struct hooked_place *)v;
|
||||
|
||||
seq_printf(seq, "name:%-24s addr:0x%p hookers:%-10d\n",
|
||||
hplace->name, hplace->place, hplace->nr_hookers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations hookers_seq_ops = {
|
||||
.start = hookers_seq_start,
|
||||
.next = hookers_seq_next,
|
||||
.stop = hookers_seq_stop,
|
||||
.show = hookers_seq_show,
|
||||
};
|
||||
|
||||
static int hookers_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &hookers_seq_ops);
|
||||
}
|
||||
|
||||
static const struct proc_ops hookers_seq_fops = {
|
||||
.proc_open = hookers_seq_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release,
|
||||
};
|
||||
|
||||
static int hookers_init(void)
|
||||
{
|
||||
enum pt_types i;
|
||||
|
||||
if (!proc_create("hookers", 0444, NULL, &hookers_seq_fops))
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_init(&hookers_lock);
|
||||
for (i = 0; i < PLACE_TABLE_SZ; i++) {
|
||||
unsigned int cr0;
|
||||
void **place = place_table[i].place;
|
||||
|
||||
place_table[i].orig = *place;
|
||||
if (!place_table[i].stub)
|
||||
break;
|
||||
INIT_LIST_HEAD(&place_table[i].chain);
|
||||
get_online_cpus();
|
||||
cr0 = hookers_clear_cr0();
|
||||
*place = place_table[i].stub;
|
||||
hookers_restore_cr0(cr0);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hookers_exit(void)
|
||||
{
|
||||
enum pt_types i;
|
||||
|
||||
remove_proc_entry("hookers", NULL);
|
||||
|
||||
for (i = 0; i < PLACE_TABLE_SZ; i++) {
|
||||
unsigned int cr0;
|
||||
void **place = place_table[i].place;
|
||||
|
||||
get_online_cpus();
|
||||
cr0 = hookers_clear_cr0();
|
||||
*place = place_table[i].orig;
|
||||
hookers_restore_cr0(cr0);
|
||||
put_online_cpus();
|
||||
}
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
module_init(hookers_init);
|
||||
module_exit(hookers_exit);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -692,6 +692,7 @@ const struct proto_ops inet6_stream_ops = {
|
|||
#endif
|
||||
.set_rcvlowat = tcp_set_rcvlowat,
|
||||
};
|
||||
EXPORT_SYMBOL(inet6_stream_ops);
|
||||
|
||||
const struct proto_ops inet6_dgram_ops = {
|
||||
.family = PF_INET6,
|
||||
|
|
|
@ -74,7 +74,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
|
|||
|
||||
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
static const struct inet_connection_sock_af_ops ipv6_mapped;
|
||||
const struct inet_connection_sock_af_ops ipv6_mapped;
|
||||
const struct inet_connection_sock_af_ops ipv6_specific;
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;
|
||||
|
@ -1881,6 +1881,7 @@ const struct inet_connection_sock_af_ops ipv6_specific = {
|
|||
.sockaddr_len = sizeof(struct sockaddr_in6),
|
||||
.mtu_reduced = tcp_v6_mtu_reduced,
|
||||
};
|
||||
EXPORT_SYMBOL(ipv6_specific);
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
|
||||
|
@ -1893,7 +1894,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
|
|||
/*
|
||||
* TCP over IPv4 via INET6 API
|
||||
*/
|
||||
static const struct inet_connection_sock_af_ops ipv6_mapped = {
|
||||
const struct inet_connection_sock_af_ops ipv6_mapped = {
|
||||
.queue_xmit = ip_queue_xmit,
|
||||
.send_check = tcp_v4_send_check,
|
||||
.rebuild_header = inet_sk_rebuild_header,
|
||||
|
@ -1907,6 +1908,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {
|
|||
.sockaddr_len = sizeof(struct sockaddr_in6),
|
||||
.mtu_reduced = tcp_v4_mtu_reduced,
|
||||
};
|
||||
EXPORT_SYMBOL(ipv6_mapped);
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
|
||||
|
|
Loading…
Reference in New Issue