在餐馆吃饭时,连接无线网络后访问某网页会自动弹出一个认证页面,我想大家都经历过。。。。。  

  其网络拓扑如下:

  sta-------------网络设备--------------公网

比如sta 终端ip 是100.100.100.100  访问百度,但是按道理应该返回百度啊?那怎么返回的是 认真页面呢?这里面涉及到报文拦截 。

当sta 的报文经过网络设备时,网络设备会将报文copy到网络设备自身处理,而不是转发出去。但是报文的目的ip 指向的是baidu.com ,到了网络设备cpu 处理时,其协议栈也会forward啊?

所以为了防止报文forward出去,协议栈会读报文进行双向nat处理。

比如100.100.100.100-------->baidu.com -------------经过perrouting---dnat-------local_in---- sant 后会变成 3.3.3.1----------------->3.3.3.2 此时3.3.3.2 正好是网络设备本机接口ip ,所以本机设备就会收取次报文然后回复http 报文

但是内核协议栈的nat 模块有个奇怪的地方就是:

local_out出去的报文经过dnat后,又会重新路由,这就会导致网络设备回复报文时,出接口错误。可以看到如下代码:可能调用ip_route_me_harder  重新路由

nf_nat_local_fn(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
unsigned int ret; /* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT; ret = nf_nat_fn(hooknum, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); if (ct->tuplehash[dir].tuple.dst.u3.ip !=
ct->tuplehash[!dir].tuple.src.u3.ip) {
if (ip_route_me_harder(skb, RTN_UNSPEC))
ret = NF_DROP;
} }
return ret;
}

所以一般 portal 认证的双向nat 不会用内核协议栈本身的逻辑实现。一般都会自身写一个conn-nat----也就是直接full nat

一般都会使用conntrack  的扩展功能实现。具体就不讲了

/* 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_test_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_test_nat_out,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK + 1,
},
};

也就是在PRE_ROUTING 直接替换 源ip 目的ip 将报文直接送到本机出来

     iph->saddr =  htonl(3.3);
iph->daddr = htonl(3.1);
tcph->source = htons(auth_info->alloc_port); ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip = htonl(3.1);
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip = htonl(3.3);
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port = htons(auth_info->alloc_port);
original方向在 conntrack 完成
/* 将数据包转换成tuple */
/* 由skb得出一个original方向的tuple,赋值给tuple,这是一个struct nf_conntrack_tuple结构,这里给其所有成员都赋值了,
以TCP包为例:
tuple->src.l3num = l3num;
tuple->src.u3.ip = srcip;
tuple->dst.u3.ip = dstip;
tuple->dst.protonum = protonum;
tuple->dst.dir = IP_CT_DIR_ORIGINAL;
tuple->src.u.tcp.port = srcport;
tuple->dst.u.tcp.port = destport;
调用L3proto->pkt_to_tuple() 以及 L4proto->pkt_to_tuple() 设置L3 L4信息
*/

在LOCAL_OUT直接反向替换 源ip 目的ip  然后将报文dev_xmit到设备

auth_info = nf_ct_ext_find(ct, NF_CT_EXT_TEST);
if (auth_info == NULL ){
return NF_ACCEPT;
}
--------------
iph->saddr = auth_info->org_dip;
iph->daddr = auth_info->org_sip; tcph->dest = auth_info->org_port;
--------------------------
nat_output_with_info()

最新文章

  1. java8中的map和reduce
  2. Delphi 精选文章地址
  3. Object.create()兼容实现方法
  4. 【MySQL】MySQL忘记root密码解决方案
  5. discuz xplus 模板 没解析的问题
  6. js中给函数传参函数时,函数加括号与不加括号的区别
  7. PL/SQL编程要点和注意点
  8. java邮件发送(以163邮箱为例)
  9. 【CSS】整屏大背景
  10. 【BZOJ4003】【JLOI2015】城池攻占(左偏树)
  11. 19 主线程向子线程发送信息(handler)
  12. BZOJ_4320_ShangHai2006 Homework_分块
  13. F. Multicolored Markers(数学思维)
  14. 【CSS】Sticky Footer 布局
  15. python 小练习 8
  16. node爬虫gbk中文乱码问题
  17. flask应用中取得config的配置
  18. java第六天
  19. 20181009-6 选题 Scrum立会报告+燃尽图 05
  20. 弹性布局解决ios输入框遮挡input

热门文章

  1. 霍夫曼编码(Huffman)
  2. MeteoInfoLab脚本示例:闪电位置图
  3. Linux给特定进程单独指定DNS
  4. go 继承
  5. html的keywords标签
  6. 探索ParNew和CMS垃圾回收器
  7. C# 获取页面Post过来的数据
  8. .net core2.2 HealthChecks记录
  9. linux ssh自动输入密码,expect使用
  10. Python合集之Python开发环境在Windows系统里面搭建