目录

文章目录

  • 目录
  • Linux 作为一个路由器
  • 路由表项的类型
  • route 指令
  • ip route 指令
    • 添加默认路由
    • 添加静态路由
    • 删除静态路由
  • 操作示例
  • Linux Kernel 路由子系统
    • 路由表
    • 发送数据时进行路由选择(选路)
    • 接收数据时进行路由选择(选路)
    • 路由查找

Linux 作为一个路由器

Linux 作为一个路由器只需要打开 ipv4.forward 特性即可。

$ vim /etc/sysctl.conf
...
net.ipv4.ip_forward=1$ sysctl -p# 开启 NAT
$ iptables -t nat -A POSTROUTING -j MASQUERADE

开启了路由功能之后的 Linux 服务器就相当于一个 Router,Linux 服务器的路由表就相当于 Router 的路由表,Linux 服务器上的网卡就相当于 Router Interface。数据包会根据路由表规则在这些 “网卡” 中选择下一跳。

同时要注意 iptables 规则是否可能会造成混乱,对于 Router 所有数据包都是 SNAT,所以不需要配置 DNAT 规则,建议清理干净。

路由表项的类型

根据子网掩码的特征,可以将路由分为 3 种类型:

  1. 默认路由(0.0.0.0):当主机不能在路由表中查找到目标主机的 IP 地址或网络路由记录时,数据包就被发送到默认路由(默认网关)上。Flags 字段为 G。e.g. 默认路由是 IP 地址为 192.168.1.1 的路由器。
Destination    Gateway       Genmask Flags     Metric    Ref    Use    Iface
-----------    -------     ------- -----      ------    ---    ---    -----
default       192.168.1.1     0.0.0.0    UG       0        0     0    eth0
  1. 主机路由(255.255.255.255):是路由表项中指向单个 IP 地址或主机名的记录。Flags 字段为 H。e.g. 本地主机通过 IP 地址 192.168.1.1 的路由器到达 IP 地址为 10.0.0.10 的主机。
Destination    Gateway       Genmask Flags     Metric    Ref    Use    Iface
-----------    -------     -------            -----     ------    ---    ---    -----
10.0.0.10     192.168.1.1    255.255.255.255   UH       0    0      0    eth0
  1. 网络路由(特定的网段,e.g. 255.255.255.0):代表主机可以到达的网络。Flags 字段为 N。e.g. 本地主机将发送到网络 192.19.12 的数据包转发到 IP 地址的 192.168.1.1 的路由器。
Destination    Gateway       Genmask Flags    Metric    Ref     Use    Iface
-----------    -------     -------         -----    -----   ---    ---    -----
192.19.12     192.168.1.1    255.255.255.0      UN      0       0     0    eth0

route 指令

命令行下输入 route -nnetstat -rn,就可以打印本机的 main 路由表:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.18.22.1     0.0.0.0         UG    0      0        0 br-ex
172.18.22.0     0.0.0.0         255.255.255.0   U     0      0        0 br-ex
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 o-hm0
192.168.122.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr0
字段 说明
Destination 目的网段(或目的主机),0.0.0.0(或 default)表示默认路由
Gateway 网关地址,0.0.0.0 或 * 表示当前路由记录对应的 Destination 跟本机处于同一个网段,通信时不需要经过网关
Genmask 地址掩码,当路由项为默认路由时会设置为 0.0.0.0,当路由项为主机路由时会设置为 255.255.255.255
Flags 标志,含义参考表格后面的解释
Metric 跃点,到达指定网络所需的中转数(不在 Linux Kernel 中使用)
Ref 引用,路由项引用次数(不在 Linux Kernel 中使用)
Use 使用,路由项被路由软件查找的次数(不在 Linux Kernel 中使用)
Iface 接口

Flags 含义:

  • U - 有效路由
  • H - 主机路由
  • G - 网关路由
  • N - 网段路由
  • R - 恢复动态路由产生的表项
  • D - 由路由的后台程序动态地安装
  • M - 由路由的后台程序修改
  • ! - 拒绝路由

ip route 指令

使用 ip route show 查看 main 路由表:

$ ip route show | column -t
default           via  172.18.22.1  dev    br-ex
172.18.22.0/24    dev  br-ex        proto  kernel  scope  link  src  172.18.22.200
192.168.0.0/24    dev  o-hm0        proto  kernel  scope  link  src  192.168.0.96
192.168.122.0/24  dev  virbr0       proto  kernel  scope  link  src  192.168.122.1
  • 默认路由,没有匹配路由项的数据包全部通过设备 br-ex 转发至网关 172.18.22.1
default via 172.18.22.1 dev br-ex
  • 目的网段为 172.18.22.0/24 的数据包通过设备 br-ex 转发,该路由项由内核自动创建,直连,数据包源 IP 为 172.18.22.200,不需要经过网关(主机就处于该网段)。
172.18.22.0/24 dev br-ex proto kernel scope link src 172.18.22.200
  • 目的网段为 10.15.150.0/24 的数据包通过设备 enp0s3 转发至网关 192.168.150.253,该路由项为 static 静态路由,由手动创建,到达指定网络需要的转发的跳数为 1。
10.15.150.0/24  via  192.168.150.253  dev  enp0s3  proto  static  metric  1

添加默认路由

ip route add default via 172.18.22.1 dev br-ex

NOTE:当 Linux 上物理网卡(e.g. eth0)挂载到了 Linux Bridge(e.g. br-ex)上之后,默认路由的设备应该为 Bridge 设备,因为此时的物理网卡 IP 无效了。

添加静态路由

ip route add [目的网段] via [网关] dev [接口] [table <table_number>]

e.g. 目标网段为 10.15.150.0/24 的数据包通过接口 enp0s3 转发到网关 192.168.150.253。

ip route add 10.15.150.0/24 via 192.168.150.253 dev enp0s3

删除静态路由

ip route del [目标网段]

e.g.

ip route del 10.15.150.0/24

操作示例

  1. 要实现全网通信,也就是网络中任意两个节点都可以通信,就要求每个路由器的路由表中必须有到所有网段的路由。

  2. 对于路由器来说,它只知道与自己直接相连的网段,对于没有直连的网络,则需要人工添加到这些网段的路由。如 R1 路由器直接与 A、B 两个网段相连,R1 会自动生成 A、B 网段的路由表,所以 R1 天然就知道数据包如何在在 A、B 网段之间进行转发。但 R1 没有与直接 C、D 网段相连,A、B 网段发出的数据包无法被 R1 转发至 C、D 网段,此时就需要手动添加 C、D 网段的路由到 R1(如上图所示)。此时 A 网段(192.168.0.2/24)访问 C 网段(172.16.1.2/24),R1 就知道该数据包应该被转发至 R2 的网关 172.16.0.2/24,R2 在根据目标地址(172.16.1.2/24)转发至 R3。

  3. “下一跳” 指的是数据包下一步转发给哪个路由器,应该为目标路由器的入口地址。对于点到点链路而言,“下一跳” 地址可以被写成目标网络的出口(如 serial 2/0),因为 PPP 协议,数据帧从一端发出,接收端只有一个;但对于路由器而言,因为路由器之间是一个以太网连接,这种情况下添加路由器,只能写下一跳地址,而不能写路由器的出口,因为路由器的出口连的是以太网,它无法知道该将数据包发给该以太网中的哪个路由器。

  4. 路由器只关心到某个 网段 如何转发数据包,所以添加路由时一定要是某个网段的地址,而不能是某个特定地址的路由,即一定要确保 IP 地址的主机位全是 0。如果要让路由器转发到一个 IP 地址的路由,子网掩码要写成 4 个 255。如:R1(config)#ip route 192.168.1.3 255.255.255.255 172.16.0.2

简单来说,每个 Router 都会连接多个 LAN,如果当前 Router 没有这个 LAN,那么 Router 就应该将数据包 下一跳 到连接这个 LAN 的 Router 上,之间可能隔着多个 Routers。

NOTE:需要注意的是,路由器功能生效的前提是连接一个 LAN 的路由网关接口必须配置了同一网段的网关 IP 地址。笔者经历过使用 DPDK 用户态数据面转发软件的场景,就是因为没有配置网管 IP 所以没有触发数据包的转发动作。无论是传统的路由交换组网场景,还是基于用户态内核旁路技术的自定义网络的组网场景,路由网关都是必要需要存在的。

Linux Kernel 路由子系统

Linux 在发送数据时和接收数据时都会涉及到路由子系统的工作。

路由表

Routing Table(路由表)在 Kernel 中的另外一个别名是 FIB(Forwarding Information Base,转发信息库)。所以,在 Kernel 中看到的 fib 开头的定义基本上就是和路由表相关的功能。

在默认情况下,Linux 只有 local 和 main 两个路由表,如果内核编译时支持策略路由,那么最多可以配置 255 个独立的路由表。查看是否开启了 CONFIG_IP_MULTIPLE_TABLES 多路由表支持。

$ cat /boot/config-3.10.0-693.el7.x86_64
...
CONFIG_IP_MULTIPLE_TABLES=y

若开启了,此时 Linux 自身会维护 4 个路由表:

$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0   unspec
#
# local
#
#1  inr.ruhep
  • 0 unspec table: 系统保留表。
  • 253 defulte table:没特别指定的默认路由都放在该表。
  • 254 main table:没指明路由表的所有路由都放在该表。
  • 255 locale table:保存本地接口地址,广播地址、NAT 地址。该由系统维护,用户不得更改。

查看指定的路由表项目:

$ ip route list table {table_number}
# e.g.
$ ip route list table local

同时,Linux 支持 Network namespace,如果存在多个 Network namespace 的话,就会存在多套路由表。路由表在整个内核数据结构中的关联关系如下图所示。

发送数据时进行路由选择(选路)

Linux 在发送数据包的时候需要进行路由选路,因为服务器上可能会存在多张网卡设备。当待发送的数据包进入 IP 层的路由子系统时,就要进行路由选择(选路),以决定使用哪张网卡设备把数据包送出去。

IP 网络层数据包发送的入口函数是 ip_queue_xmit(),其中调用的 ip_route_output_ports() 函数实现了路由项的查找,继而完成路由选择,在路由表中进行匹配,然后决定使用哪个网卡发送出去。

// net/ipv4/ip_output.cint ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
{// 路由选择过程// 选择完后记录路由信息到 skb 上rt = (struct rtable *)__sk_dst_check(sk, 0);if (rt == NULL) {// 没有缓存则查找路由项rt = ip_route_output_ports(...);sk_setup_caps(sk, &rt->dst);}skb_dst_set_noref(skb, &rt->dst);...//发送ip_local_out(skb);
}

接收数据时进行路由选择(选路)

因为 Linux 提供了路由器的功能,可以像路由器一样工作,将接收到的数据包通过合适的网卡将其转发出去。

IP 网络层数据包接收的入口函数是 ip_rcv(),执行后调用到 ip_rcv_finish(),在这里展开路由选择。

  • 如果匹配确实就是本设备的网络包,那么就通过 ip_local_deliver() 送到更上层的 TCP 层进行处理。

  • 如果匹配发现是非本设备的网络包,那就进入到 ip_forward() 进行转发,最后通过 ip_output() 发送出去。

// net/ipv4/ip_input.c
static int ip_rcv_finish(struct sk_buff *skb){...if (!skb_dst(skb)) {int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,iph->tos, skb->dev);...}...return dst_input(skb);
}// net/ipv4/route.c
// 进行路由查找
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,u8 tos, struct net_device *dev)
{...res = ip_route_input_slow(skb, daddr, saddr, tos, dev);return res;
}

路由查找

上述内容提到,数据包发送过程中调用了 ip_route_output_ports() 来查找路由,数据包接收过程中调用了 ip_route_input_slow() 来查找。其实这两个函数最终都会调用到 fib_lookup() 这个核心函数。

fib_lookup() 函数依次到 local 和 main 表中进行匹配,匹配到后就返回。并且 local 表的优先级要高于 main 表,如果 local 表中找到了规则,则路由过程就结束了。

根据这个实现,当我们 ping 本地 IP 地址的时,eth0 上是抓不到包的。因为所有命中 local 表的包,都会被送往 loopback 设备,而不是 eth0。

// net/ipv4/route.cstruct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
{...// 进入 fib_lookupif (fib_lookup(net, fl4, &res)) {}
}// net/ipv4/route.c
static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,u8 tos, struct net_device *dev)
{...// 进入 fib_lookuperr = fib_lookup(net, &fl4, &res);
}// include/net/ip_fib.h
static inline int fib_lookup(struct net *net, const struct flowi4 *flp,struct fib_result *res)
{struct fib_table *table;table = fib_get_table(net, RT_TABLE_LOCAL);if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))return 0;table = fib_get_table(net, RT_TABLE_MAIN);if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))return 0;return -ENETUNREACH;
}

Linux Kernel TCP/IP Stack — L3 Layer — 路由器子系统相关推荐

  1. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架

    目录 文章目录 目录 netfilter 框架 netfilter 的组成模块 netfilter 的 Hook 机制实现 netfilter 的工作原理 规则(Rules) 链(Chains) 表( ...

  2. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — iptables NAPT 网络地址/端口转换

    目录 文章目录 目录 网络地址转换(NAT) SNAT DNAT 端口映射(PAT) 网络地址转换(NAT) IP 网络有公网与私网的区分,通常内网使用私网 IP,Internet 使用公网 IP,而 ...

  3. Linux Kernel TCP/IP Stack — L3 Layer

    目录 文章目录 目录 L3 Layer 框架 IP Handler L3 Layer 框架 IP Handler 实现了以下功能: IP 数据包的分片和重组. IP 协议的字段处理,例如:IP 选项, ...

  4. Linux Kernel TCP/IP Stack — L3 Layer — netfilter/iptables 防火墙

    目录 文章目录 目录 iptables/netfilter 框架 iptables-service iptables 指令应用 查看规则 添加规则 删除规则 修改规则 保存和加载规则 常规初始化配置 ...

  5. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — conntrack(CT,连接跟踪)

    目录 文章目录 目录 CT CT CT(conntrack,connection tracking,连接跟踪),顾名思义,就是跟踪(并记录)连接的状态,是许多网络应用的基础.例如:iptables.L ...

  6. Linux Kernel TCP/IP Stack — L3 Layer — 邻居发现子系统

    目录 文章目录 目录 邻居发现子系统 邻居发现子系统 提供以下功能: ARP 协议(IPV4) NDP 协议(IPV6) 邻居表新建,更新,老化机制,状态转化等.

  7. Linux Kernel TCP/IP Stack — L2 Layer — Linux Bridge(虚拟网桥)的基本操作

    目录 文章目录 目录 Linux bridge 的基本操作 创建 Bridge 将 veth pair 连上 Bridge 为 Bridge 配置 IP 地址 将物理网卡接口设备挂靠 Bridge L ...

  8. Linux Kernel TCP/IP Stack — L7 Layer — Application Socket I/O 接口类型

    目录 文章目录 目录 基本概念 同步与异步 阻塞与非阻塞 I/O 操作的执行流程 Socket I/O 接口类型 阻塞 IO 缺点 非阻塞 IO 缺点 阻塞 IO 与非阻塞 IO 的区别 IO 多路复 ...

  9. Linux Kernel TCP/IP Stack — L1 Layer — 多队列网卡

    目录 文章目录 目录 多队列网卡 Intel 82575 的多队列硬件实现 Intel 82575 的多队列软件驱动实现 多队列网卡识别 多队列网卡 多队列网卡,是一种用来解决网络 I/O QoS 问 ...

最新文章

  1. ACM寒假训练第二周总结
  2. python getattr_Python中的getattr()函数详解
  3. Oracle 10g中对resumable session的增强
  4. JAVA标识符的命名规则和规范
  5. 抵御物联网DDoS军团
  6. P4091-[HEOI2016/TJOI2016]求和【斯特林数,NTT】
  7. Linux-正则表达式的POSIX规范及流派
  8. 【转载】H264编码原理以及I帧、B帧、P帧
  9. Log4jdbc-log4j2打印mybatis语句
  10. 存储大师新作,三星存储四大新品面世!
  11. 关于FPGA软件quartus仿真出现cannot launch the modelsim software问题的解决
  12. 雷达存在感应器技术,实时感知控制应用,雷达人体探测方案
  13. Himall商城普通帮助类(一)
  14. ubuntu20.04.1下安装qt4相关依赖库
  15. 前端开发:Mac电脑修改hosts文件的方法
  16. 如何换ionic里面的图标
  17. redis连接出错 ERR AUTH <password> called without any password configured for the default user.
  18. IDC机房动力环境设备维护
  19. 一个模仿水滴筹的项目(只是一个半成品)
  20. C++20中的协程(Coroutine)

热门文章

  1. 分享Kali Linux 2016.2第49周镜像文件
  2. Xamarin基础命名空间Microsoft.SqlServer.Server
  3. jira 审批流程_博兴县行政审批服务局推暖心服务工程 企业开办实现“全程网办”_博兴新闻...
  4. python6翻_洗礼灵魂,修炼python(6)--活起来的代码+列表
  5. java01背包问题算法_经典动态规划--01背包问题
  6. 深度神经网络对基于EEG的情绪识别的关键频带和通道的研究
  7. 没听说过“羽毛球VR”计划?嗯?
  8. c语言从键盘输入千米数,第二章 C语言编程基础.ppt
  9. 阿里达摩院百万大奖评选开启!这次人人都能给青年科学家当伯乐
  10. 科学家们竟用乐高观察细胞,网友:万万没想到啊