内核的DNAT与ARP

2023-12-19 10:48
文章标签 arp 内核 dnat

本文主要是介绍内核的DNAT与ARP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

Linux内核在实现多对一或者多对多的DNAT时,可指定多个目的IP地址,这就造成一个问题,如果其中有的地址并没有配置在接口上,那么这些地址对于外部设备而言,ARP就是不可达,无法进行通信。

                                                +------------+|  Internal  ||  Server 1  ||------------|+----------+              +-----------+              |  10.10.0.2/24| External |         eth0 |   Linux   | 10.10.0.1    ||  Device  |--------------|  Gateway  |--------------++----------+              +-----------+ eth1         |20.20.20.20     20.20.20.1                     |  10.10.0.3/24+------------+|  Internal  ||  Server 2  |+------------+

如上图拓扑所示,LINUX网关的左侧网卡连接外部网络,此处是IP地址为20.20.20.2的设备;网关的右侧网卡连接在内部的10.10.0.0/24的内部网络中。假如在内网中服务器10.10.0.2对外提供服务,可使用如下iptables命令配置DNAT将访问流量映射进来:

iptables -t nat -A PREROUTING -d 20.20.20.1 -j DNAT --to-destination 10.10.0.1  


这样iptables将在PREROUTING hook点检查所有目的地址为20.20.20.1的流量,然后全部映射到内部10.10.0.1的服务器上。如果我们另有一个公网IP:20.20.20.2,需要将其映射到内部的第二台服务器上,IP地址为10.10.0.3,与以上iptables命令类似,使用如下命令进行映射:

iptables -t nat -A PREROUTING -d 20.20.20.2 -j DNAT --to-destination 10.10.0.2 

另外,需要将公网IP地址20.20.20.2配置在Linux网关的eth0接口上,否则,外部设备(20.20.20.20)将不知道20.20.20.2地址的存在,即ARP请求其IP地址,将得不到回应。如果有多个公网IP需要映射,都需要配置在外部网卡eth0上,IP地址可以是不同网段。

$ sudo ip addr add 20.20.20.2/24 dev eth0 
$ sudo ip addr add 20.20.20.3/24 dev eth0 

查看配置的IP地址:

$ ip addr show
3: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default qlen 1000link/ether 00:0c:29:74:7f:0e brd ff:ff:ff:ff:ff:ffinet 20.20.20.1/24 scope global eth0valid_lft forever preferred_lft foreverinet 20.20.20.2/24 scope global secondary eth0valid_lft forever preferred_lft foreverinet 20.20.20.3/24 scope global secondary eth0valid_lft forever preferred_lft forever

内核对ARP处理

Linux内核中arp_process函数处理接收到的ARP报文。在判断是一个ARP请求报文之后,查询路由表,确认ARP请求的目标地址tip为本机地址后,才有可能需要回复ARP请求。路由查询结果rt_type需要为RTN_LOCAL类型。

static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{if (arp->ar_op == htons(ARPOP_REQUEST) &&ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {rt = skb_rtable(skb);addr_type = rt->rt_type;if (addr_type == RTN_LOCAL) {dont_send = arp_ignore(in_dev, sip, tip);if (!dont_send && IN_DEV_ARPFILTER(in_dev))dont_send = arp_filter(sip, tip, dev);if (!dont_send) {n = neigh_event_ns(&arp_tbl, sha, &sip, dev);if (n) {arp_send_dst(ARPOP_REPLY, ETH_P_ARP,sip, dev, tip, sha, dev->dev_addr, sha, reply_dst);
}

另外一个判断条件,是在arp_ignore的子函数confirm_addr_indev中,需要确认目标IP地址是否配置在出接口上。毫无疑问,以上两个条件都成立,对于外部IP地址的ARP请求都会得到相应。


ARP的修改

清楚了以上的ARP处理过程,可以避免在外部网卡eth0上配置多个IP地址,直接修改内核代码,对ARP相应。只需要两个地方的改动。第一在arp_process中查询路由之后(ip_route_input_noref函数),如果目的IP不是指向本地地址,再次查询内核的DNAT规则中配置的目的IP,如果匹配到,即认为这是本地地址;第二在arp_ignore中在接口IP列表中找不目标IP后,也需要在此查询DNAT规则中的目的IP,如果有认为条件成立,回复ARP响应。二层地址使用入口设备的MAC地址。

 

SNAT

对于SNAT,问题是一样的,如果配置了多个映射的源IP,需要在外出接口上配置这些IP地址,以响应对端的ARP地址请求。

sudo iptables -t nat -A POSTROUTING -s 20.20.2.0/24 -j SNAT -o ens38 --to-source 9.9.9.1-9.9.9.20


内核版本

Linux-4.15

 

这篇关于内核的DNAT与ARP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511895

相关文章

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

Ubuntu22.04回退系统内核

文章目录 起因回退操作卸载内核禁止内核升级 起因 最近因为系统内核自动升级,导致显卡驱动检测不到,炼丹环境被破坏。无奈只能重装驱动,于是跟着手册操作发现驱动要求的是内核版本是5.15.0-25-generic,而我通过uname -r发现这时候的内核版本是6.8.0-40-generic,看来只能回退了。 我搜索了网上很多的文章,没有一篇文章能够完全解决这个问题,所以在我多次尝

跟我一起玩《linux内核设计的艺术》第1章(四)——from setup.s to head.s,这回一定让main滚出来!(已解封)

看到书上1.3的大标题,以为马上就要见着main了,其实啊,还早着呢,光看setup.s和head.s的代码量就知道,跟bootsect.s没有可比性,真多……这确实需要包括我在内的大家多一些耐心,相信见着main后,大家的信心和干劲会上一个台阶,加油! 既然上篇已经玩转gdb,接下来的讲解肯定是边调试边分析书上的内容,纯理论讲解其实我并不在行。 setup.s: 目标:争取把setup.

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

hijack_modprobe_path篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermod

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

hijack_prctl篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统

linux 内核提权总结(demo+exp分析) -- 任意读写(二)

hijack_vdso篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     vdso: 内核实现的一个动态库,存在于内核,然后映射到用户态空间,可由用户态直接调用 内核中的vdso如果被修改

linux 内核提权总结(demo+exp分析) -- 任意读写(一)

cred篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   每个线程在内核中都对应一个线程结构块thread_infothread_info中存在task_struct类型结构体 struct t