From 7384d821f268ce4c725e0f6649838794368ca071 Mon Sep 17 00:00:00 2001 From: katrinzhou Date: Fri, 15 Apr 2022 13:02:18 +0800 Subject: [PATCH] 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 Signed-off-by: katrinzhou Signed-off-by: Kairui Song --- include/net/inet_common.h | 2 +- include/net/ipv6.h | 8 +- include/net/tcp.h | 4 +- net/Kconfig | 1 + net/Makefile | 1 + net/core/sock.c | 1 + net/ipv4/af_inet.c | 2 +- net/ipv4/datagram.c | 2 +- net/ipv4/raw.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/af_inet6.c | 3 +- net/ipv6/tcp_ipv6.c | 8 +- net/toa/Kconfig | 9 + net/toa/Makefile | 4 + net/toa/toa.c | 413 ++++++++++++++++++++++++++++++++++++++ net/toa/toa.h | 83 ++++++++ 16 files changed, 533 insertions(+), 12 deletions(-) create mode 100644 net/toa/Kconfig create mode 100644 net/toa/Makefile create mode 100644 net/toa/toa.c create mode 100644 net/toa/toa.h diff --git a/include/net/inet_common.h b/include/net/inet_common.h index f50a644d87a9..e8367761b88b 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -8,7 +8,7 @@ #include #include -extern const struct proto_ops inet_stream_ops; +extern struct proto_ops inet_stream_ops; extern const struct proto_ops inet_dgram_ops; /* diff --git a/include/net/ipv6.h b/include/net/ipv6.h index c6932d1a3fa8..a3fea746c732 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -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) { diff --git a/include/net/tcp.h b/include/net/tcp.h index 0239e815edf7..7fb35976b333 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -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)); diff --git a/net/Kconfig b/net/Kconfig index d532ec33f1fe..29e46de3a306 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -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 diff --git a/net/Makefile b/net/Makefile index 4c4dc535453d..ad7f22c39e3c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -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/ diff --git a/net/core/sock.c b/net/core/sock.c index bfaf47b3f3c7..08c94c43ee99 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -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) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index dffb3b1e2bfb..6c7ef3a69f8a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -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, diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index cb5dbee9e018..c85dd03b9a7a 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -19,7 +19,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct inet_sock *inet = inet_sk(sk); - struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; + struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; struct flowi4 *fl4; struct rtable *rt; __be32 saddr; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 4b5db5d1edc2..93430bff5937 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -688,7 +688,7 @@ static void raw_destroy(struct sock *sk) static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct inet_sock *inet = inet_sk(sk); - struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; struct net *net = sock_net(sk); u32 tb_id = RT_TABLE_LOCAL; int ret = -EINVAL; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4167e8a48b60..8187e169782e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -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, diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index b87d292cde66..dcc19e07f7c9 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -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, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 44b6949d72b2..44d5ca742218 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -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 = { diff --git a/net/toa/Kconfig b/net/toa/Kconfig new file mode 100644 index 000000000000..51a088e8dab7 --- /dev/null +++ b/net/toa/Kconfig @@ -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. diff --git a/net/toa/Makefile b/net/toa/Makefile new file mode 100644 index 000000000000..a0e3620ded48 --- /dev/null +++ b/net/toa/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for TOA module. +# +obj-$(CONFIG_TOA) += toa.o diff --git a/net/toa/toa.c b/net/toa/toa.c new file mode 100644 index 000000000000..b4d86889d405 --- /dev/null +++ b/net/toa/toa.c @@ -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 + * Yan Tian + * Jiaming Wu + * Jiajun Chen + * + * 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"); + diff --git a/net/toa/toa.h b/net/toa/toa.h new file mode 100644 index 000000000000..6e68d54f4402 --- /dev/null +++ b/net/toa/toa.h @@ -0,0 +1,83 @@ +#ifndef __NET__TOA_H__ +#define __NET__TOA_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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