nat与路由

NF_INET_PRE_ROUTING 做DNAT (做了DNAT后在做路由决定是local_in还是forwarding)数据包进入路由表之前

NF_INET_POST_ROUTING 做SNAT (离开协议栈时,做源ip替换)发送到网卡接口之前

NF_INET_LOCAL_OUT:本机发包时,做DNAT,由于发包时,在local_out之前已经做了路由选择,所以做了DNAT后如果发现目的ip做了替换,则需要重新路由!!

NF_INET_LOCAL_IN:通过路由表后目的地为本机,做SNAT,报文送往本机,被nginx等服务读取之前做源ip替换。

原因如下: 报文pkt进入内核协议栈时,还没有查找路由,由于路由选择是在PREROUTEING 和 FORWARD之间,所以先替换目的ip 后在选择路由,

如果先做了路由结果为forwarding,但是去做dnat可能去本机,这样就冲突了,需要重新路由来矫正。所以还是需要先dnat在做路由。

  之前存在一个问题:在内核使用双向nat,local_out替换目的ip后,就会重新路由,导致出口有问题,所以为此重新设计了一套代码。 请参考之前的博客:双向nat   

具体逻辑就是:

/* We must be after connection tracking and before packet filtering. */

static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
    /* Before packet filtering, change destination */
    {
        .hook        = nf_portal_nat_in,
        .owner        = THIS_MODULE,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_PRE_ROUTING,
        .priority    = NF_IP_PRI_CONNTRACK + 1,
    },
    /* After packet filtering, change source */
    {
        .hook        = nf_portal_nat_out,
        .owner        = THIS_MODULE,
        .pf        = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_OUT,
        .priority    = NF_IP_PRI_CONNTRACK + 1,
    },
};

主要逻辑就是:client ----sw ------server;

第一步:client 和sw 之间做双线nat, 此时收包pre_routeing 以及反方向 output 做source dst ip同时替换。

第二步:client知道需要访问的真实ip,此时在sw上做源ip替换, sw做代理,访问server获取相关信息。

      auth_info = nf_ct_ext_add(ct, NF_CT_EXT_PORTAL, GFP_ATOMIC);

        -------------------------------
        //保存源mac,原ip, 原端口
        memcpy(auth_info->org_mac, eth->h_source,sizeof(auth_info->org_mac));
        auth_info->org_sip = iph->saddr;
        auth_info->org_dip = iph->daddr;
        auth_info->org_port = tcph->source;

        //分配源端口
        auth_info->alloc_port = sw_alloc_snat_port();
-----------------------------------
     ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip = htonl(DP_PORTAL_IP);
        ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip = htonl(DP_PORTAL_SRC_XLATE_IP);
        //ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.tcp.port;
        ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port = htons(auth_info->alloc_port);

    return NF_ACCEPT;
//此时做了sip dip替换后,路由转发到local in。

对应local_out就调用ct保留的信息复原即可。

详细情况就不说了。

 

posted @ 2023-03-08 17:26  codestacklinuxer  阅读(94)  评论(0编辑  收藏  举报