Linux使用TPROXY进行UDP的透明代理

1.为什么使用TPROXY才能代理UDP

在进行TCP的代理时,只要在NET表上无脑进行REDIRECT就好了。例如使用ss-redir,你只要把tcp的流量redirect到ss-redir监听的端口上就OK了。但是当你使用这种方法的时候,就会不正常,因为对于UDP进行redirect之后,原始的目的地址和端口就找不到了。

这是为什么呢?
ss-redir的原理很简单:使用iptables对PREROUTING与OUTPUT的TCP/UDP流量进行REDIRECT(REDIRECT是DNAT的特例),ss—redir在捕获网络流量后,通过一些技术手段获取REDIRECT之前的目的地址(dst)与端口(port),连同网络流量一起转发至远程服务器。
针对TCP连接,的确是因为Linux Kernel连接跟踪机制的实现才使获取数据包原本的dst和port成为可能,但这种连接跟踪机制并非只存在于TCP连接中,UDP连接同样存在,conntrack -p udp便能看到UDP的连接跟踪记录。内核中有关TCP与UDP的NAT源码/net/netfilter/nf_nat_proto_tcp.c和/net/netfilter/nf_nat_proto_udp.c几乎一模一样,都是根据NAT的类型做SNAT或DNAT。

那这究竟是怎么一回事?为什么对于UDP连接就失效了呢?
回过头来看看ss-redir有关获取TCP原本的dst和port的源码,核心函数是getdestaddr:

static int
getdestaddr(int fd, struct sockaddr_storage *destaddr)
{
    socklen_t socklen = sizeof(*destaddr);
    int error         = 0;

    error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen);
    if (error) { // Didn't find a proper way to detect IP version.
        error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen);
        if (error) {
            return -1;
        }
    }
    return 0;
}

在内核源码中搜了下有关SO_ORIGINAL_DST的东西,看到了getorigdst:

static int
getorigdst(struct sock *sk, int optval, void __user *user, int *len)
{
    const struct inet_sock *inet = inet_sk(sk);
    const struct nf_conntrack_tuple_hash *h;
    struct nf_conntrack_tuple tuple;

    memset(&tuple, 0, sizeof(tuple));

    lock_sock(sk);
    tuple.src.u3.ip = inet->inet_rcv_saddr;
    tuple.src.u.tcp.port = inet->inet_sport;
    tuple.dst.u3.ip = inet->inet_daddr;
    tuple.dst.u.tcp.port = inet->inet_dport;
    tuple.src.l3num = PF_INET;
    tuple.dst.protonum = sk->sk_protocol;
    release_sock(sk);

    /* We only do TCP and SCTP at the moment: is there a better way? */
    if (tuple.dst.protonum != IPPROTO_TCP &&
        tuple.dst.protonum != IPPROTO_SCTP) {
        pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
        return -ENOPROTOOPT;
    }

We only do TCP and SCTP at the moment。Oh,shit!只针对TCP与SCTP才能这么做,并非技术上不可行,只是人为地阻止罢了。

2.TPROXY

为了在redirect UDP后还能够获取原本的dst和port,ss-redir采用了TPROXY。Linux系统有关TPROXY的设置是以下三条命令:

ip rule add fwmark 0x2333/0x2333 pref 100 table 100
ip route add local default dev lo table 100
iptables -t mangle -A PREROUTING -p udp -j TPROXY --tproxy-mark 0x2333/0x2333 --on-ip 127.0.0.1 --on-port 1080

大意就是在mangle表的PREROUTING中为每个UDP数据包打上0x2333/0x2333标志,之后在路由选择中将具有0x2333/0x2333标志的数据包投递到本地环回设备上的1080端口;对监听0.0.0.0地址的1080端口的socket启用IP_TRANSPARENT标志,使IPv4路由能够将非本机的数据报投递到传输层,传递给监听1080端口的ss-redir。IP_RECVORIGDSTADDR与IPV6_RECVORIGDSTADDR则表示获取送达数据包的dst与port。
可问题来了:要知道mangle表并不会修改数据包,那么TPROXY是如何做到在不修改数据包的前提下将非本机dst的数据包投递到换回设备上的1080端口呢?
这个问题在内核中时如何实现的,还待研究,但是确定是TPROXY做了某些工作。

TPROXY主要功能:

    1. 重定向一部分经过路由选择的流量到本地路由进程(类似NAT中的REDIRECT)
    1. 在非本地IP上起监听。监听后就可以转发了(神奇吧)

TPROXY要解决的两个重要的问题

  • 1.套接字如何监听到非本地IP地址。
    先用setsockopt函数为套接字设置IP_TRANSPARENT标识,再去监听0.0.0.0地址这样的方式来实现监听任意IP。
  • 2.如何获取的原始目标的端口 。
    先调用setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &n, sizeof(int))函数为套接字设置IP_RECVORIGDSTADDR标识,然后通过recvmsg函数从tproxy那边接受发过来的msghdr结构体信息,并循环遍历cmsghdr成员最终获取到原始目标的地址和端口,也就是说tproxy会向msghdr(附属数据结构)填入原始目标ip和端口信息,再通过sendmsg函数发送给代理应用。

参考:
https://blog.csdn.net/ts__cf/article/details/78942294
https://vvl.me/2018/06/09/from-ss-redir-to-linux-nat/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容