tkernel: net: add toa support
Upstream: no The private TCP option for support Taobao LVS full-NAT feature This option saves the original IP address and source port of a TCP segment after LVS performed NAT on it. So far, this module only supports IPv4 and IPv6 mapped IPv4. cdb need it. Signed-off-by: Xiaoming Gao <newtongao@tencent.com> Signed-off-by: katrinzhou <katrinzhou@tencent.com> Signed-off-by: Kairui Song <kasong@tencent.com>
This commit is contained in:
parent
7dbd79cf59
commit
7384d821f2
|
@ -8,7 +8,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
extern const struct proto_ops inet_stream_ops;
|
||||
extern struct proto_ops inet_stream_ops;
|
||||
extern const struct proto_ops inet_dgram_ops;
|
||||
|
||||
/*
|
||||
|
|
|
@ -1238,7 +1238,7 @@ int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
|||
/*
|
||||
* reassembly.c
|
||||
*/
|
||||
extern const struct proto_ops inet6_stream_ops;
|
||||
extern struct proto_ops inet6_stream_ops;
|
||||
extern const struct proto_ops inet6_dgram_ops;
|
||||
extern const struct proto_ops inet6_sockraw_ops;
|
||||
|
||||
|
@ -1290,6 +1290,12 @@ int ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex,
|
|||
const struct in6_addr *addr, unsigned int mode);
|
||||
int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
|
||||
const struct in6_addr *addr);
|
||||
/* public func in tcp_ipv6.c */
|
||||
extern struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
bool *own_req);
|
||||
|
||||
static inline int ip6_sock_set_v6only(struct sock *sk)
|
||||
{
|
||||
|
|
|
@ -909,7 +909,7 @@ struct tcp_skb_cb {
|
|||
|
||||
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
|
||||
|
||||
extern const struct inet_connection_sock_af_ops ipv4_specific;
|
||||
extern struct inet_connection_sock_af_ops ipv4_specific;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/* This is the variant of inet6_iif() that must be used by TCP,
|
||||
|
@ -937,7 +937,7 @@ static inline int tcp_v6_sdif(const struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern const struct inet_connection_sock_af_ops ipv6_specific;
|
||||
extern struct inet_connection_sock_af_ops ipv6_specific;
|
||||
|
||||
INDIRECT_CALLABLE_DECLARE(void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb));
|
||||
INDIRECT_CALLABLE_DECLARE(int tcp_v6_rcv(struct sk_buff *skb));
|
||||
|
|
|
@ -72,6 +72,7 @@ source "net/xfrm/Kconfig"
|
|||
source "net/iucv/Kconfig"
|
||||
source "net/smc/Kconfig"
|
||||
source "net/xdp/Kconfig"
|
||||
source "net/toa/Kconfig"
|
||||
|
||||
config NET_HANDSHAKE
|
||||
bool
|
||||
|
|
|
@ -80,3 +80,4 @@ obj-$(CONFIG_XDP_SOCKETS) += xdp/
|
|||
obj-$(CONFIG_MPTCP) += mptcp/
|
||||
obj-$(CONFIG_MCTP) += mctp/
|
||||
obj-$(CONFIG_NET_HANDSHAKE) += handshake/
|
||||
obj-$(CONFIG_TOA) += toa/
|
||||
|
|
|
@ -3326,6 +3326,7 @@ void sock_def_readable(struct sock *sk)
|
|||
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(sock_def_readable);
|
||||
|
||||
static void sock_def_write_space(struct sock *sk)
|
||||
{
|
||||
|
|
|
@ -1045,7 +1045,7 @@ static int inet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned lon
|
|||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
const struct proto_ops inet_stream_ops = {
|
||||
struct proto_ops inet_stream_ops = {
|
||||
.family = PF_INET,
|
||||
.owner = THIS_MODULE,
|
||||
.release = inet_release,
|
||||
|
|
|
@ -2252,7 +2252,7 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
|
|||
}
|
||||
EXPORT_SYMBOL(inet_sk_rx_dst_set);
|
||||
|
||||
const struct inet_connection_sock_af_ops ipv4_specific = {
|
||||
struct inet_connection_sock_af_ops ipv4_specific = {
|
||||
.queue_xmit = ip_queue_xmit,
|
||||
.send_check = tcp_v4_send_check,
|
||||
.rebuild_header = inet_sk_rebuild_header,
|
||||
|
|
|
@ -679,7 +679,7 @@ int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
|||
return err;
|
||||
}
|
||||
|
||||
const struct proto_ops inet6_stream_ops = {
|
||||
struct proto_ops inet6_stream_ops = {
|
||||
.family = PF_INET6,
|
||||
.owner = THIS_MODULE,
|
||||
.release = inet6_release,
|
||||
|
@ -711,6 +711,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,
|
||||
|
|
|
@ -75,7 +75,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
|
|||
INDIRECT_CALLABLE_SCOPE 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_specific;
|
||||
struct inet_connection_sock_af_ops ipv6_specific;
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;
|
||||
static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
|
||||
|
@ -1191,7 +1191,7 @@ static void tcp_v6_restore_cb(struct sk_buff *skb)
|
|||
sizeof(struct inet6_skb_parm));
|
||||
}
|
||||
|
||||
static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
|
||||
struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
|
||||
struct request_sock *req,
|
||||
struct dst_entry *dst,
|
||||
struct request_sock *req_unhash,
|
||||
|
@ -1413,6 +1413,7 @@ out:
|
|||
tcp_listendrop(sk);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_v6_syn_recv_sock);
|
||||
|
||||
INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *,
|
||||
u32));
|
||||
|
@ -1887,7 +1888,7 @@ INDIRECT_CALLABLE_SCOPE void tcp_v6_send_check(struct sock *sk, struct sk_buff *
|
|||
__tcp_v6_send_check(skb, &sk->sk_v6_rcv_saddr, &sk->sk_v6_daddr);
|
||||
}
|
||||
|
||||
const struct inet_connection_sock_af_ops ipv6_specific = {
|
||||
struct inet_connection_sock_af_ops ipv6_specific = {
|
||||
.queue_xmit = inet6_csk_xmit,
|
||||
.send_check = tcp_v6_send_check,
|
||||
.rebuild_header = inet6_sk_rebuild_header,
|
||||
|
@ -1902,6 +1903,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 = {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
config TOA
|
||||
tristate "The private TCP option for support Taobao LVS full-NAT feature"
|
||||
default m
|
||||
help
|
||||
This option saves the original IP address and source port of a TCP segment
|
||||
after LVS performed NAT on it. So far, this module only supports IPv4 and
|
||||
IPv6 mapped IPv4.
|
||||
|
||||
Say m if unsure.
|
|
@ -0,0 +1,4 @@
|
|||
#
|
||||
# Makefile for TOA module.
|
||||
#
|
||||
obj-$(CONFIG_TOA) += toa.o
|
|
@ -0,0 +1,413 @@
|
|||
#include "toa.h"
|
||||
|
||||
/*
|
||||
* TOA a new Tcp Option as Address,
|
||||
* here address including IP and Port.
|
||||
* the real {IP,Port} can be added into option field of TCP header,
|
||||
* with LVS FULLNAT model, the realservice are still able to receive real {IP,Port} info.
|
||||
* So far, this module only supports IPv4 and IPv6 mapped IPv4.
|
||||
*
|
||||
* Authors:
|
||||
* Wen Li <steel.mental@gmail.com>
|
||||
* Yan Tian <tianyan.7c00@gmail.com>
|
||||
* Jiaming Wu <pukong.wjm@taobao.com>
|
||||
* Jiajun Chen <mofan.cjj@taobao.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Statistics of toa in proc /proc/net/toa_stats
|
||||
*/
|
||||
|
||||
struct toa_stats_entry toa_stats[] = {
|
||||
TOA_STAT_ITEM("syn_recv_sock_toa", SYN_RECV_SOCK_TOA_CNT),
|
||||
TOA_STAT_ITEM("syn_recv_sock_no_toa", SYN_RECV_SOCK_NO_TOA_CNT),
|
||||
TOA_STAT_ITEM("getname_toa_ok", GETNAME_TOA_OK_CNT),
|
||||
TOA_STAT_ITEM("getname_toa_mismatch", GETNAME_TOA_MISMATCH_CNT),
|
||||
TOA_STAT_ITEM("getname_toa_bypass", GETNAME_TOA_BYPASS_CNT),
|
||||
TOA_STAT_ITEM("getname_toa_empty", GETNAME_TOA_EMPTY_CNT),
|
||||
TOA_STAT_END
|
||||
};
|
||||
|
||||
DEFINE_TOA_STAT(struct toa_stat_mib, ext_stats);
|
||||
|
||||
/*
|
||||
* Funcs for toa hooks
|
||||
*/
|
||||
|
||||
/* Parse TCP options in skb, try to get client ip, port
|
||||
* @param skb [in] received skb, it should be a ack/get-ack packet.
|
||||
* @return NULL if we don't get client ip/port;
|
||||
* value of toa_data in ret_ptr if we get client ip/port.
|
||||
*/
|
||||
static void *get_toa_data(struct sk_buff *skb)
|
||||
{
|
||||
struct tcphdr *th;
|
||||
int length;
|
||||
unsigned char *ptr;
|
||||
|
||||
struct toa_data tdata;
|
||||
|
||||
void *ret_ptr = NULL;
|
||||
|
||||
//TOA_DBG("get_toa_data called\n");
|
||||
|
||||
if (NULL != skb) {
|
||||
th = tcp_hdr(skb);
|
||||
length = (th->doff * 4) - sizeof (struct tcphdr);
|
||||
ptr = (unsigned char *)(th + 1);
|
||||
|
||||
while (length > 0) {
|
||||
int opcode = *ptr++;
|
||||
int opsize;
|
||||
switch (opcode) {
|
||||
case TCPOPT_EOL:
|
||||
return NULL;
|
||||
case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
|
||||
length--;
|
||||
continue;
|
||||
default:
|
||||
opsize = *ptr++;
|
||||
if (opsize < 2) /* "silly options" */
|
||||
return NULL;
|
||||
if (opsize > length)
|
||||
return NULL; /* don't parse partial options */
|
||||
if (TCPOPT_TOA == opcode && TCPOLEN_TOA == opsize) {
|
||||
memcpy(&tdata, ptr - 2, sizeof (tdata));
|
||||
//TOA_DBG("find toa data: ip = %u.%u.%u.%u, port = %u\n", NIPQUAD(tdata.ip),
|
||||
//ntohs(tdata.port));
|
||||
memcpy(&ret_ptr, &tdata, sizeof (ret_ptr));
|
||||
//TOA_DBG("coded toa data: %p\n", ret_ptr);
|
||||
return ret_ptr;
|
||||
}
|
||||
ptr += opsize - 2;
|
||||
length -= opsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get client ip from socket
|
||||
* @param sock [in] the socket to getpeername() or getsockname()
|
||||
* @param uaddr [out] the place to put client ip, port
|
||||
* @param uaddr_len [out] lenth of @uaddr
|
||||
* @peer [in] if(peer), try to get remote address; if(!peer), try to get local address
|
||||
* @return return what the original inet_getname() returns.
|
||||
*/
|
||||
static int
|
||||
inet_getname_toa(struct socket *sock, struct sockaddr *uaddr, int peer)
|
||||
{
|
||||
int retval = 0;
|
||||
struct sock *sk = sock->sk;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
|
||||
struct toa_data tdata;
|
||||
|
||||
//TOA_DBG("inet_getname_toa called, sk->sk_user_data is %p\n", sk->sk_user_data);
|
||||
|
||||
/* call orginal one */
|
||||
retval = inet_getname(sock, uaddr, peer);
|
||||
|
||||
/* set our value if need */
|
||||
if (retval == 0 && NULL != sk->sk_user_data && peer) {
|
||||
if (sock_def_readable == sk->sk_data_ready) {
|
||||
memcpy(&tdata, &sk->sk_user_data, sizeof (tdata));
|
||||
if (TCPOPT_TOA == tdata.opcode && TCPOLEN_TOA == tdata.opsize) {
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_OK_CNT);
|
||||
//TOA_DBG("inet_getname_toa: set new sockaddr, ip %u.%u.%u.%u -> %u.%u.%u.%u, port %u -> %u\n",
|
||||
// NIPQUAD(sin->sin_addr.s_addr), NIPQUAD(tdata.ip), ntohs(sin->sin_port),
|
||||
// ntohs(tdata.port));
|
||||
sin->sin_port = tdata.port;
|
||||
sin->sin_addr.s_addr = tdata.ip;
|
||||
} else { /* sk_user_data doesn't belong to us */
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_MISMATCH_CNT);
|
||||
//TOA_DBG("inet_getname_toa: invalid toa data, ip %u.%u.%u.%u port %u opcode %u opsize %u\n",
|
||||
// NIPQUAD(tdata.ip), ntohs(tdata.port), tdata.opcode, tdata.opsize);
|
||||
}
|
||||
} else {
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_BYPASS_CNT);
|
||||
}
|
||||
} else { /* no need to get client ip */
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_EMPTY_CNT);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
static int
|
||||
inet6_getname_toa(struct socket *sock, struct sockaddr *uaddr, int peer)
|
||||
{
|
||||
int retval = 0;
|
||||
struct sock *sk = sock->sk;
|
||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *) uaddr;
|
||||
struct toa_data tdata;
|
||||
|
||||
//TOA_DBG("inet6_getname_toa called, sk->sk_user_data is %p\n", sk->sk_user_data);
|
||||
|
||||
/* call orginal one */
|
||||
retval = inet6_getname(sock, uaddr, peer);
|
||||
|
||||
/* set our value if need */
|
||||
if (retval == 0 && NULL != sk->sk_user_data && peer) {
|
||||
if (sock_def_readable == sk->sk_data_ready) {
|
||||
memcpy(&tdata, &sk->sk_user_data, sizeof (tdata));
|
||||
if (TCPOPT_TOA == tdata.opcode && TCPOLEN_TOA == tdata.opsize) {
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_OK_CNT);
|
||||
sin->sin6_port = tdata.port;
|
||||
ipv6_addr_set(&sin->sin6_addr, 0, 0, htonl(0x0000FFFF), tdata.ip);
|
||||
} else { /* sk_user_data doesn't belong to us */
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_MISMATCH_CNT);
|
||||
}
|
||||
} else {
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_BYPASS_CNT);
|
||||
}
|
||||
} else { /* no need to get client ip */
|
||||
TOA_INC_STATS(ext_stats, GETNAME_TOA_EMPTY_CNT);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The three way handshake has completed - we got a valid synack -
|
||||
* now create the new socket.
|
||||
* We need to save toa data into the new socket.
|
||||
* @param sk [out] the socket
|
||||
* @param skb [in] the ack/ack-get packet
|
||||
* @param req [in] the open request for this connection
|
||||
* @param dst [out] route cache entry
|
||||
* @return NULL if fail new socket if succeed.
|
||||
*/
|
||||
static struct sock *
|
||||
tcp_v4_syn_recv_sock_toa(const 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 *newsock = NULL;
|
||||
|
||||
//TOA_DBG("tcp_v4_syn_recv_sock_toa called\n");
|
||||
|
||||
/* call orginal one */
|
||||
newsock = tcp_v4_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req);
|
||||
|
||||
/* set our value if need */
|
||||
if (NULL != newsock && NULL == newsock->sk_user_data) {
|
||||
newsock->sk_user_data = get_toa_data(skb);
|
||||
if (NULL != newsock->sk_user_data) {
|
||||
TOA_INC_STATS(ext_stats, SYN_RECV_SOCK_TOA_CNT);
|
||||
} else {
|
||||
TOA_INC_STATS(ext_stats, SYN_RECV_SOCK_NO_TOA_CNT);
|
||||
}
|
||||
//TOA_DBG("tcp_v4_syn_recv_sock_toa: set sk->sk_user_data to %p\n", newsock->sk_user_data);
|
||||
}
|
||||
return newsock;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
static struct sock *
|
||||
tcp_v6_syn_recv_sock_toa(const 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 *newsock = NULL;
|
||||
|
||||
//TOA_DBG("tcp_v4_syn_recv_sock_toa called\n");
|
||||
|
||||
/* call orginal one */
|
||||
newsock = tcp_v6_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req);
|
||||
|
||||
/* set our value if need */
|
||||
if (NULL != newsock && NULL == newsock->sk_user_data) {
|
||||
newsock->sk_user_data = get_toa_data(skb);
|
||||
if (NULL != newsock->sk_user_data) {
|
||||
TOA_INC_STATS(ext_stats, SYN_RECV_SOCK_TOA_CNT);
|
||||
} else {
|
||||
TOA_INC_STATS(ext_stats, SYN_RECV_SOCK_NO_TOA_CNT);
|
||||
}
|
||||
}
|
||||
return newsock;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HOOK FUNCS
|
||||
*/
|
||||
|
||||
/* replace the functions with our functions */
|
||||
static inline int hook_toa_functions(void)
|
||||
{
|
||||
struct proto_ops *inet_stream_ops_p;
|
||||
struct proto_ops *inet6_stream_ops_p;
|
||||
struct inet_connection_sock_af_ops *ipv4_specific_p;
|
||||
struct inet_connection_sock_af_ops *ipv6_specific_p;
|
||||
|
||||
/* hook inet_getname for ipv4 */
|
||||
inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops;
|
||||
inet_stream_ops_p->getname = inet_getname_toa;
|
||||
TOA_INFO("CPU [%u] hooked inet_getname <%p> --> <%p>\n", smp_processor_id(), inet_getname,
|
||||
inet_stream_ops_p->getname);
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/* hook inet6_getname for ipv6 */
|
||||
inet6_stream_ops_p = (struct proto_ops *)&inet6_stream_ops;
|
||||
inet6_stream_ops_p->getname = inet6_getname_toa;
|
||||
TOA_INFO("CPU [%u] hooked inet6_getname <%p> --> <%p>\n", smp_processor_id(), inet6_getname,
|
||||
inet6_stream_ops_p->getname);
|
||||
#endif
|
||||
|
||||
/* hook tcp_v4_syn_recv_sock for ipv4 */
|
||||
ipv4_specific_p = (struct inet_connection_sock_af_ops *)&ipv4_specific;
|
||||
ipv4_specific_p->syn_recv_sock = tcp_v4_syn_recv_sock_toa;
|
||||
TOA_INFO("CPU [%u] hooked tcp_v4_syn_recv_sock <%p> --> <%p>\n", smp_processor_id(), tcp_v4_syn_recv_sock,
|
||||
ipv4_specific_p->syn_recv_sock);
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/* hook tcp_v6_syn_recv_sock for ipv6 */
|
||||
ipv6_specific_p = (struct inet_connection_sock_af_ops *)&ipv6_specific;
|
||||
ipv6_specific_p->syn_recv_sock = tcp_v6_syn_recv_sock_toa;
|
||||
TOA_INFO("CPU [%u] hooked tcp_v6_syn_recv_sock <%p> --> <%p>\n", smp_processor_id(), tcp_v6_syn_recv_sock,
|
||||
ipv6_specific_p->syn_recv_sock);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* replace the functions to original ones */
|
||||
static int
|
||||
unhook_toa_functions(void)
|
||||
{
|
||||
struct proto_ops *inet_stream_ops_p;
|
||||
struct proto_ops *inet6_stream_ops_p;
|
||||
struct inet_connection_sock_af_ops *ipv4_specific_p;
|
||||
struct inet_connection_sock_af_ops *ipv6_specific_p;
|
||||
|
||||
/* unhook inet_getname for ipv4 */
|
||||
inet_stream_ops_p = (struct proto_ops *)&inet_stream_ops;
|
||||
inet_stream_ops_p->getname = inet_getname;
|
||||
TOA_INFO("CPU [%u] unhooked inet_getname\n", smp_processor_id());
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/* unhook inet6_getname for ipv6 */
|
||||
inet6_stream_ops_p = (struct proto_ops *)&inet6_stream_ops;
|
||||
inet6_stream_ops_p->getname = inet6_getname;
|
||||
TOA_INFO("CPU [%u] unhooked inet6_getname\n", smp_processor_id());
|
||||
#endif
|
||||
|
||||
/* unhook tcp_v4_syn_recv_sock for ipv4 */
|
||||
ipv4_specific_p = (struct inet_connection_sock_af_ops *)&ipv4_specific;
|
||||
ipv4_specific_p->syn_recv_sock = tcp_v4_syn_recv_sock;
|
||||
TOA_INFO("CPU [%u] unhooked tcp_v4_syn_recv_sock\n", smp_processor_id());
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/* unhook tcp_v6_syn_recv_sock for ipv6 */
|
||||
ipv6_specific_p = (struct inet_connection_sock_af_ops *)&ipv6_specific;
|
||||
ipv6_specific_p->syn_recv_sock = tcp_v6_syn_recv_sock;
|
||||
TOA_INFO("CPU [%u] unhooked tcp_v6_syn_recv_sock\n", smp_processor_id());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Statistics of toa in proc /proc/net/toa_stats
|
||||
*/
|
||||
static int toa_stats_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* print CPU first */
|
||||
seq_printf(seq, " ");
|
||||
for (i = 0; i < NR_CPUS; i++)
|
||||
if (cpu_online(i))
|
||||
seq_printf(seq, "CPU%d ", i);
|
||||
seq_putc(seq, '\n');
|
||||
|
||||
i = 0;
|
||||
while (NULL != toa_stats[i].name) {
|
||||
seq_printf(seq, "%-25s:", toa_stats[i].name);
|
||||
for (j = 0; j < NR_CPUS; j++) {
|
||||
if (cpu_online(j)) {
|
||||
seq_printf(seq, "%10lu ",
|
||||
*(((unsigned long *) per_cpu_ptr(ext_stats, j)) + toa_stats[i].entry));
|
||||
}
|
||||
}
|
||||
seq_putc(seq, '\n');
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int toa_stats_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, toa_stats_show, NULL);
|
||||
}
|
||||
|
||||
static const struct proc_ops toa_stats_ops = {
|
||||
.proc_open = toa_stats_seq_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* TOA module init and destory
|
||||
*/
|
||||
|
||||
/* module init */
|
||||
static int __init toa_init(void)
|
||||
{
|
||||
|
||||
TOA_INFO("TOA " TOA_VERSION " by pukong.wjm\n");
|
||||
|
||||
/* alloc statistics array for toa */
|
||||
ext_stats = alloc_percpu(struct toa_stat_mib);
|
||||
if (NULL == ext_stats)
|
||||
return 1;
|
||||
proc_create("toa_stats", 0, init_net.proc_net, &toa_stats_ops);
|
||||
|
||||
/* hook funcs for parse and get toa */
|
||||
if (!hook_toa_functions())
|
||||
goto err;
|
||||
|
||||
TOA_INFO("toa loaded\n");
|
||||
return 0;
|
||||
|
||||
err:
|
||||
remove_proc_entry("toa_stats", init_net.proc_net);
|
||||
if (NULL != ext_stats) {
|
||||
free_percpu(ext_stats);
|
||||
ext_stats = NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* module cleanup*/
|
||||
static void __exit toa_exit(void)
|
||||
{
|
||||
unhook_toa_functions();
|
||||
synchronize_net();
|
||||
|
||||
remove_proc_entry("toa_stats", init_net.proc_net);
|
||||
if (NULL != ext_stats) {
|
||||
free_percpu(ext_stats);
|
||||
ext_stats = NULL;
|
||||
}
|
||||
TOA_INFO("toa unloaded\n");
|
||||
}
|
||||
|
||||
module_init(toa_init);
|
||||
module_exit(toa_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef __NET__TOA_H__
|
||||
#define __NET__TOA_H__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/inet_common.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/transp_v6.h>
|
||||
|
||||
#define TOA_VERSION "1.0.0.0"
|
||||
|
||||
#define TOA_DBG(msg...) \
|
||||
do { \
|
||||
printk(KERN_DEBUG "[DEBUG] TOA: " msg); \
|
||||
} while (0)
|
||||
|
||||
#define TOA_INFO(msg...) \
|
||||
do { \
|
||||
if (net_ratelimit()) \
|
||||
printk(KERN_INFO "TOA: " msg);\
|
||||
} while (0)
|
||||
|
||||
#define TCPOPT_TOA 200
|
||||
|
||||
/* MUST be 4n !!!! */
|
||||
#define TCPOLEN_TOA 8 /* |opcode|size|ip+port| = 1 + 1 + 6 */
|
||||
|
||||
/* MUST be 4 bytes alignment */
|
||||
struct toa_data {
|
||||
__u8 opcode;
|
||||
__u8 opsize;
|
||||
__u16 port;
|
||||
__u32 ip;
|
||||
};
|
||||
|
||||
/* statistics about toa in proc /proc/net/toa_stat */
|
||||
enum {
|
||||
SYN_RECV_SOCK_TOA_CNT = 1,
|
||||
SYN_RECV_SOCK_NO_TOA_CNT,
|
||||
GETNAME_TOA_OK_CNT,
|
||||
GETNAME_TOA_MISMATCH_CNT,
|
||||
GETNAME_TOA_BYPASS_CNT,
|
||||
GETNAME_TOA_EMPTY_CNT,
|
||||
TOA_STAT_LAST
|
||||
};
|
||||
|
||||
struct toa_stats_entry {
|
||||
char *name;
|
||||
int entry;
|
||||
};
|
||||
|
||||
#define TOA_STAT_ITEM(_name, _entry) { \
|
||||
.name = _name, \
|
||||
.entry = _entry, \
|
||||
}
|
||||
|
||||
#define TOA_STAT_END { \
|
||||
NULL, \
|
||||
0, \
|
||||
}
|
||||
|
||||
struct toa_stat_mib {
|
||||
unsigned long mibs[TOA_STAT_LAST];
|
||||
};
|
||||
|
||||
#define DEFINE_TOA_STAT(type, name) \
|
||||
__typeof__(type)(*name)
|
||||
#define TOA_INC_STATS(mib, field) \
|
||||
(per_cpu_ptr(mib, smp_processor_id())->mibs[field]++)
|
||||
#endif
|
Loading…
Reference in New Issue