linux路由表之route

1. 前言

https://blog.csdn.net/vevenlcf/article/details/48026965

描述了主机路由和网络路由的区别:https://blog.csdn.net/buhuiguowang/article/details/81026050 

2. route 命令参数

mike@ubuntu:~/workspace/DCU-LEDE$ man route > log.txt
mike@ubuntu:~/workspace/DCU-LEDE$ cat log.txt 
ROUTE(8)             Linux System Administrator's Manual             ROUTE(8)

NAME
       route - show / manipulate the IP routing table

SYNOPSIS
       route [-CFvnNee] [-A family |-4|-6]

       route  [-v]  [-A  family  |-4|-6] add [-net|-host] target [netmask Nm]
              [gw Gw] [metric N] [mss M] [window W] [irtt I]  [reject]  [mod]
              [dyn] [reinstate] [[dev] If]

       route  [-v]  [-A  family |-4|-6] del [-net|-host] target [gw Gw] [net‐
              mask Nm] [metric M] [[dev] If]

       route  [-V] [--version] [-h] [--help]

DESCRIPTION
       Route manipulates the kernel's IP routing tables.  Its primary use  is
       to set up static routes to specific hosts or networks via an interface
       after it has been configured with the ifconfig(8) program.

       When the add or del options  are  used,  route  modifies  the  routing
       tables.  Without these options, route displays the current contents of
       the routing tables.

OPTIONS
       -A family
              use the specified address family (eg `inet'). Use route  --help
              for  a full list. You can use -6 as an alias for --inet6 and -4
              as an alias for -A inet

       -F     operate on the kernel's FIB (Forwarding Information Base) rout‐
              ing table.  This is the default.

       -C     operate on the kernel's routing cache.

       -v     select verbose operation.

       -n     show  numerical  addresses  instead of trying to determine sym‐
              bolic host names. This is useful if you are trying to determine
              why the route to your nameserver has vanished.

       -e     use  netstat(8)-format  for  displaying the routing table.  -ee
              will generate a very long line with  all  parameters  from  the
              routing table.

       del    delete a route.

       add    add a new route.

       target the  destination  network or host. You can provide an addresses
              or symbolic network or host name. Optionally you can use  /pre‐
              fixlen notation instead of using the netmask option.

       -net   the target is a network.

       -host  the target is a host.

       netmask NM
              when adding a network route, the netmask to be used.

       gw GW  route packets via a gateway.
              NOTE:  The specified gateway must be reachable first. This usu‐
              ally means that you have to set up a static route to the  gate‐
              way beforehand. If you specify the address of one of your local
              interfaces, it will be used to decide about  the  interface  to
              which the packets should be routed to. This is a BSDism compat‐
              ibility hack.

       metric M
              set the metric field in the routing table (used by routing dae‐
              mons)  to  M.  If  this  option is not specified the metric for
              inet6 (IPv6) address family defaults to '1', for inet (IPv4) it
              defaults  to  '0'. You should always specify an explicit metric
              value to not rely on those defaults -  they  also  differ  from
              iproute2.

       mss M  sets  MTU  (Maximum Transmission Unit) of the route to M bytes.
              Note that the current implementation of the route command  does
              not allow the option to set the Maximum Segment Size (MSS).

       window W
              set  the  TCP  window size for connections over this route to W
              bytes. This is typically only used on AX.25 networks  and  with
              drivers unable to handle back to back frames.

       irtt I set the initial round trip time (irtt) for TCP connections over
              this route to I milliseconds (1-12000). This is typically  only
              used  on  AX.25  networks.  If  omitted the RFC 1122 default of
              300ms is used.

       reject install a blocking route, which will force a  route  lookup  to
              fail.   This  is  for  example used to mask out networks before
              using the default route. This is NOT for firewalling.

       mod, dyn, reinstate
              install a dynamic or modified route. These flags are for  diag‐
              nostic purposes, and are generally only set by routing daemons.

       dev If force  the route to be associated with the specified device, as
              the kernel will otherwise try to determine the  device  on  its
              own  (by checking already existing routes and device specifica‐
              tions, and where the route is added to). In  most  normal  net‐
              works you won't need this.

              If  dev If is the last option on the command line, the word dev
              may be omitted, as it's the default. Otherwise the order of the
              route modifiers (metric netmask gw dev) doesn't matter.

EXAMPLES
       route add -net 127.0.0.0 netmask 255.0.0.0 metric 1024 dev lo
              adds  the  normal  loopback  entry, using netmask 255.0.0.0 and
              associated with the "lo" device (assuming this device was  pre‐
              viously set up correctly with ifconfig(8)).

       route add -net 192.56.76.0 netmask 255.255.255.0 metric 1024 dev eth0
              adds  a route to the local network 192.56.76.x via "eth0".  The
              word "dev" can be omitted here.

       route del default
              deletes the current default route, which is  labeled  "default"
              or  0.0.0.0 in the destination field of the current routing ta‐
              ble.

       route del -net 192.56.76.0 netmask 255.255.255.0
              deletes the route. Since the Linux routing kernel  uses  class‐
              less  addressing,  you  pretty  much always have to specify the
              netmask that is same as as seen in 'route -n' listing.

       route add default gw mango
              adds a default route (which will be  used  if  no  other  route
              matches).   All  packets  using  this  route  will be gatewayed
              through the address of a node named "mango". The  device  which
              will  actually  be  used  for  that route depends on how we can
              reach "mango" - "mango" must be on directly reachable route.

       route add mango sl0
              Adds the route to the host named "mango" via the SLIP interface
              (assuming that "mango" is the SLIP host).

       route add -net 192.57.66.0 netmask 255.255.255.0 gw mango
              This command adds the net "192.57.66.x" to be gatewayed through
              the former route to the SLIP interface.

       route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
              This is an obscure one documented so people know how to do  it.
              This  sets  all  of the class D (multicast) IP routes to go via
              "eth0". This is the correct normal configuration  line  with  a
              multicasting kernel.

       route add -net 10.0.0.0 netmask 255.0.0.0 metric 1024 reject
              This  installs  a  rejecting  route  for  the  private  network
              "10.x.x.x."

       route -6 add 2001:0002::/48 metric 1 dev eth0
              This adds a IPv6 route with the specified metric to be directly
              reachable via eth0.

OUTPUT
       The  output  of the kernel routing table is organized in the following
       columns

       Destination
              The destination network or destination host.

       Gateway
              The gateway address or '*' if none set.

       Genmask
              The netmask for the destination net;  '255.255.255.255'  for  a
              host destination and '0.0.0.0' for the default route.

       Flags  Possible flags include
              U (route is up)
              H (target is a host)
              G (use gateway)
              R (reinstate route for dynamic routing)
              D (dynamically installed by daemon or redirect)
              M (modified from routing daemon or redirect)
              A (installed by addrconf)
              C (cache entry)
              !  (reject route)

       Metric The 'distance' to the target (usually counted in hops).

       Ref    Number of references to this route. (Not used in the Linux ker‐
              nel.)

       Use    Count of lookups for the route.  Depending on the use of -F and
              -C this will be either route cache misses (-F) or hits (-C).

       Iface  Interface to which packets for this route will be sent.

       MSS    Default  maximum  segment  size  for  TCP connections over this
              route.

       Window Default window size for TCP connections over this route.

       irtt   Initial RTT (Round Trip Time). The kernel uses  this  to  guess
              about the best TCP protocol parameters without waiting on (pos‐
              sibly slow) answers.

       HH (cached only)
              The number of ARP entries and cached routes that refer  to  the
              hardware  header cache for the cached route. This will be -1 if
              a hardware address is not  needed  for  the  interface  of  the
              cached route (e.g. lo).

       Arp (cached only)
              Whether  or not the hardware address for the cached route is up
              to date.

FILES
       /proc/net/ipv6_route
       /proc/net/route
       /proc/net/rt_cache

SEE ALSO
       ifconfig(8), netstat(8), arp(8), rarp(8), ip(8)

HISTORY
       Route for Linux  was  originally  written  by  Fred  N.   van  Kempen,
       <waltje@uwalt.nl.mugnet.org>  and then modified by Johannes Stille and
       Linus Torvalds for pl15. Alan Cox added the mss and window options for
       Linux  1.1.22.  irtt support and merged with netstat from Bernd Ecken‐
       fels.

AUTHOR
       Currently maintained by Phil Blundell <Philip.Blundell@pobox.com>  and
       Bernd Eckenfels <net-tools@lina.inka.de>.

net-tools                         2014-02-17                         ROUTE(8)

3. route源码分析之busybox

在shell窗口配置如下路由信息时

添加到主机的路由
# route add -host 192.168.1.2 dev eth0 
# route add -host 10.20.30.148 gw 10.20.30.40     #添加到10.20.30.148的网管

添加到网络的路由
# route add -net 10.20.30.40 netmask 255.255.255.248 eth0   #添加10.20.30.40的网络
# route add -net 10.20.30.48 netmask 255.255.255.248 gw 10.20.30.41 #添加10.20.30.48的网络
# route add -net 192.168.1.0/24 eth1

添加默认路由
# route add default gw 192.168.1.1

删除路由
# route del -host 192.168.1.2 dev eth0:0
# route del -host 10.20.30.148 gw 10.20.30.40
# route del -net 10.20.30.40 netmask 255.255.255.248 eth0
# route del -net 10.20.30.48 netmask 255.255.255.248 gw 10.20.30.41
# route del -net 192.168.1.0/24 eth1
# route del default gw 192.168.1.1

将调用busybox内部的源码route.c

int route_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opt;
	int what;
	char *family;
	char **p;

	/*
		route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0 //添加一条静态路由
		route add default gw 192.168.0.1 //添加默认路由
		route del -net 192.168.1.0/24 gw 192.168.0.1 //删除一条路由
		route -n //查看路由表
	*/

	/* First, remap '-net' and '-host' to avoid getopt problems. */
	p = argv;
	while (*++p) {
		if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
			p[0][0] = '#';
		}
	}

	opt = getopt32(argv, "A:ne", &family);

	if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
#if ENABLE_FEATURE_IPV6
		if (strcmp(family, "inet6") == 0) {
			opt |= ROUTE_OPT_INET6;	/* Set flag for ipv6. */
		} else
#endif
		bb_show_usage();
	}

	argv += optind;

	/* No more args means display the routing table. */
	if (!*argv) { //表示输入的命令是:route 即显示所有路由信息
		int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
#if ENABLE_FEATURE_IPV6
		if (opt & ROUTE_OPT_INET6)
			INET6_displayroutes();
		else
#endif
			bb_displayroutes(noresolve, opt & ROUTE_OPT_e);

		fflush_stdout_and_exit(EXIT_SUCCESS);
	}

	/* Check verb.  At the moment, must be add, del, or delete. */
	what = kw_lookup(tbl_verb, &argv);
	if (!what || !*argv) {		/* Unknown verb or no more args. */
		bb_show_usage();
	}

#if ENABLE_FEATURE_IPV6
	if (opt & ROUTE_OPT_INET6)
		INET6_setroute(what, argv);
	else
#endif
		INET_setroute(what, argv); //what meas: add del delete

	return EXIT_SUCCESS;
}

由于是IPV4,所以在上面的函数将调用INET_setroute()

static NOINLINE void INET_setroute(int action, char **args)
{
	/* char buffer instead of bona-fide struct avoids aliasing warning */
	char rt_buf[sizeof(struct rtentry)];
	struct rtentry *const rt = (void *)rt_buf;

	const char *netmask = NULL;
	int skfd, isnet, xflag;

    ...
    
	/* Create a socket to the INET kernel. */
	skfd = xsocket(AF_INET, SOCK_DGRAM, 0);

	if (action == RTACTION_ADD)
		xioctl(skfd, SIOCADDRT, rt);
	else
		xioctl(skfd, SIOCDELRT, rt);

	if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
}

在该函数内部主要完成 struct rtentry *const rt = (void *)rt_buf结构体的初始化,最后通过xioctl系统调用CM=SIOCADDRT、SIOCDELRT,完成对参数为struct rtentry *const rt处理。

4. route之内核源码

int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	struct sock *sk = sock->sk;
	int err = 0;
	struct net *net = sock_net(sk);

	switch (cmd) {
    ......
	case SIOCADDRT:
	case SIOCDELRT:
	case SIOCRTMSG:
		err = ip_rt_ioctl(net, cmd, (void __user *)arg);
		break;
    ......
}
int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
	struct fib_config cfg;
	struct rtentry rt;
	int err;

	switch (cmd) {
	case SIOCADDRT:		/* Add a route */
	case SIOCDELRT:		/* Delete a route */
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
			return -EPERM;

		if (copy_from_user(&rt, arg, sizeof(rt))) //强制转换为rtentry结构体成员
			return -EFAULT;

		rtnl_lock();
		err = rtentry_to_fib_config(net, cmd, &rt, &cfg); //通过rtentry结构体来解析cfg的配置
		if (err == 0) {
			struct fib_table *tb;

			if (cmd == SIOCDELRT) {
				tb = fib_get_table(net, cfg.fc_table); //cfg.fc_table = 0
				if (tb)
					err = fib_table_delete(tb, &cfg);
				else
					err = -ESRCH;
			} else {
				tb = fib_new_table(net, cfg.fc_table); //cfg.fc_table = 0
				if (tb)
					err = fib_table_insert(tb, &cfg);
				else
					err = -ENOBUFS;
			}

			/* allocated by rtentry_to_fib_config() */
			kfree(cfg.fc_mx);
		}
		rtnl_unlock();
		return err;
	}
	return -EINVAL;
}

在该函数内部主要完成以下几种功能:

a. rtentry_to_fib_config()  rtentry结构体转fib_config;

先看下struct fib_config的成员

struct fib_config {
	u8			fc_dst_len; //目的地址有效bit的个数
	u8			fc_tos;
	/* rtm_protocol */
#define RTPROT_UNSPEC	0
#define RTPROT_REDIRECT	1	/* Route installed by ICMP redirects;
				   not used by current IPv4 */
#define RTPROT_KERNEL	2	/* Route installed by kernel		*/
#define RTPROT_BOOT	3	/* Route installed during boot		*/
#define RTPROT_STATIC	4	/* Route installed by administrator	*/
	u8			fc_protocol; //见上宏定义

	enum rt_scope_t {
		RT_SCOPE_UNIVERSE=0,
	/* User defined values	*/
		RT_SCOPE_SITE=200,
		RT_SCOPE_LINK=253,
		RT_SCOPE_HOST=254,
		RT_SCOPE_NOWHERE=255
	};
	u8			fc_scope; //RT_SCOPE_HOST RT_SCOPE_LINK

	/* rtm_type */
enum {
	RTN_UNSPEC,
	RTN_UNICAST,		/* Gateway or direct route	*/
	RTN_LOCAL,		/* Accept locally		*/
	RTN_BROADCAST,		/* Accept locally as broadcast,
				   send as broadcast */
	RTN_ANYCAST,		/* Accept locally as broadcast,
				   but send as unicast */
	RTN_MULTICAST,		/* Multicast route		*/
	RTN_BLACKHOLE,		/* Drop				*/
	RTN_UNREACHABLE,	/* Destination is unreachable   */
	RTN_PROHIBIT,		/* Administratively prohibited	*/
	RTN_THROW,		/* Not in this table		*/
	RTN_NAT,		/* Translate this address	*/
	RTN_XRESOLVE,		/* Use external resolver	*/
	__RTN_MAX
};
	u8			fc_type; //fc config配置
	/* 3 bytes unused */
	u32			fc_table;
	__be32			fc_dst; //目的地址
	__be32			fc_gw; //网关地址
	int			fc_oif; // = dev->ifindex 设备接口索引编号
	u32			fc_flags;
	u32			fc_priority;
	__be32			fc_prefsrc; //  = ifa->ifa_local  即本地地址,通过net获取的设备
	struct nlattr		*fc_mx; //指向nlattr链表
	struct rtnexthop	*fc_mp; //fc_mp表示fc_mx指针的个数
	int			fc_mx_len; //表示 fc_mx 指针指向的成员字节数
	int			fc_mp_len;
	u32			fc_flow;

	/* Modifiers to NEW request */
#define NLM_F_REPLACE	0x100	/* Override existing		*/
#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
#define NLM_F_APPEND	0x800	/* Add to end of list		*/
	u32			fc_nlflags; //见上宏定义
	struct nl_info		fc_nlinfo;
 };
static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
				 struct fib_config *cfg)
{
	__be32 addr;
	int plen;

	memset(cfg, 0, sizeof(*cfg));
	cfg->fc_nlinfo.nl_net = net;

	if (rt->rt_dst.sa_family != AF_INET) //目的地址的协议族
		return -EAFNOSUPPORT;

	/*
	 * Check mask for validity:
	 * a) it must be contiguous.
	 * b) destination must have all host bits clear.
	 * c) if application forgot to set correct family (AF_INET),
	 *    reject request unless it is absolutely clear i.e.
	 *    both family and mask are zero.
	 */
	plen = 32;
	addr = sk_extract_addr(&rt->rt_dst); //提取目的地址
	if (!(rt->rt_flags & RTF_HOST)) { //不是主机路由,即是网络路由
		__be32 mask = sk_extract_addr(&rt->rt_genmask); //提取子网掩码

		if (rt->rt_genmask.sa_family != AF_INET) { //子网掩码协议族不为AF_INET	
			if (mask || rt->rt_genmask.sa_family)	//子网掩码有效 || rt->rt_genmask.sa_family有效
				return -EAFNOSUPPORT;
		}

		//到这里是网络路由,假设路由IP=192.168.1.0/24,所以addr=192.168.1.0,mask=255.255.255.0
		//在bad_mask函数内部为真,表示网络路由配置的有问题
		if (bad_mask(mask, addr)) 
			return -EINVAL;

		plen = inet_mask_len(mask); //获取mask的长度
	}

	cfg->fc_dst_len = plen; 
	cfg->fc_dst = addr;

	if (cmd != SIOCDELRT) {
		cfg->fc_nlflags = NLM_F_CREATE;
		cfg->fc_protocol = RTPROT_BOOT;
	}

	if (rt->rt_metric)
		cfg->fc_priority = rt->rt_metric - 1;

	if (rt->rt_flags & RTF_REJECT) {
		cfg->fc_scope = RT_SCOPE_HOST;
		cfg->fc_type = RTN_UNREACHABLE;
		return 0;
	}

	cfg->fc_scope = RT_SCOPE_NOWHERE;
	cfg->fc_type = RTN_UNICAST;

	if (rt->rt_dev) {
		char *colon;
		struct net_device *dev;
		char devname[IFNAMSIZ];

		if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1))
			return -EFAULT;

		devname[IFNAMSIZ-1] = 0;
		colon = strchr(devname, ':'); //colon: 冒号  如eth0:1表示eth0的别名
		if (colon)
			*colon = 0;
		dev = __dev_get_by_name(net, devname); //通过接口名称devname获取设备dev
		if (!dev)
			return -ENODEV;
		cfg->fc_oif = dev->ifindex;
		if (colon) {
			struct in_ifaddr *ifa;
			struct in_device *in_dev = __in_dev_get_rtnl(dev);
			if (!in_dev)
				return -ENODEV;
			*colon = ':';
			for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) //遍历设备下的接口
				if (strcmp(ifa->ifa_label, devname) == 0) //接口名称是否相同
					break;
			if (ifa == NULL)
				return -ENODEV;
			cfg->fc_prefsrc = ifa->ifa_local; //获取本地地址
		}
	}

	addr = sk_extract_addr(&rt->rt_gateway); //提取路由网关地址
	if (rt->rt_gateway.sa_family == AF_INET && addr) {
		cfg->fc_gw = addr; //网关地址
		if (rt->rt_flags & RTF_GATEWAY && //目的地址是网关
		    inet_addr_type(net, addr) == RTN_UNICAST)
			cfg->fc_scope = RT_SCOPE_UNIVERSE;
	}

	if (cmd == SIOCDELRT)
		return 0;

	if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw) //网关地址无效就直接退出
		return -EINVAL;

	if (cfg->fc_scope == RT_SCOPE_NOWHERE)
		cfg->fc_scope = RT_SCOPE_LINK;

	if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) { //路由标识
		struct nlattr *mx;
		int len = 0;

		mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
		if (mx == NULL)
			return -ENOMEM;

		//特别注意put_rtax函数接口,mx是函数指针,在put_rtax函数内部会执行mx的偏移
		if (rt->rt_flags & RTF_MTU)
			len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40);

		if (rt->rt_flags & RTF_WINDOW)
			len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window);

		if (rt->rt_flags & RTF_IRTT)
			len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3);

		cfg->fc_mx = mx;
		cfg->fc_mx_len = len;
	}

	return 0;
}

b. cmd == SIOCDELRT 的处理;

			if (cmd == SIOCDELRT) {
				tb = fib_get_table(net, cfg.fc_table); //cfg.fc_table = 0
				if (tb)
					err = fib_table_delete(tb, &cfg); //详见其内部的实现
//通过形参id,匹配hash链表,成功就返回tb,否则NULL
struct fib_table *fib_get_table(struct net *net, u32 id)
{
	struct fib_table *tb;
	struct hlist_head *head;
	unsigned int h;

	if (id == 0)
		id = RT_TABLE_MAIN;
	h = id & (FIB_TABLE_HASHSZ - 1); //h = id & 0xff

	rcu_read_lock();
	//关于fib_table_hash[*]的创建,详见:https://blog.csdn.net/guodong1010/article/details/52245555
	head = &net->ipv4.fib_table_hash[h]; //看下这里是什么时候赋值的,在 fib_new_table 函数内部初始化链表的
	hlist_for_each_entry_rcu(tb, head, tb_hlist) { //遍历 net->ipv4.fib_table_hash 链表,寻找匹配成功的路由表id
		if (tb->tb_id == id) {
			rcu_read_unlock();
			return tb;
		}
	}
	rcu_read_unlock();
	return NULL;
}
int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
{
	struct trie *t = (struct trie *) tb->tb_data;
	u32 key, mask;
	int plen = cfg->fc_dst_len;
	u8 tos = cfg->fc_tos;
	struct fib_alias *fa, *fa_to_delete;
	struct list_head *fa_head;
	struct leaf *l;
	struct leaf_info *li;

	if (plen > 32)
		return -EINVAL;

	key = ntohl(cfg->fc_dst); //获取目的地址
	mask = ntohl(inet_make_mask(plen)); //获取子网掩码

	if (key & ~mask) //key是网络地址?如果是网络地址,那么子网掩码为1的个数,即表示网络号,子网掩码为0的表示主机号
		return -EINVAL;

	key = key & mask; //获取网络号地址
	l = fib_find_node(t, key); //返回leaf叶子

	if (!l)
		return -ESRCH;

	li = find_leaf_info(l, plen); //通过叶子leaf上的链表,比较其有效地址长度,获取其leaf_info

	if (!li)
		return -ESRCH;

	fa_head = &li->falh; //通过leaf_info获取fib_alias链表,通过该链表获取其fib_alias别名
	fa = fib_find_alias(fa_head, tos, 0); 

	if (!fa)
		return -ESRCH;

	pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);

	fa_to_delete = NULL;
	fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); //遍历fa->fa_list链表
	list_for_each_entry_continue(fa, fa_head, fa_list) {
		struct fib_info *fi = fa->fa_info;

		if (fa->fa_tos != tos)
			break;

		if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && //路由类型
		    (cfg->fc_scope == RT_SCOPE_NOWHERE ||
		     fa->fa_info->fib_scope == cfg->fc_scope) && //路由返回,如主机、link...
		    (!cfg->fc_prefsrc ||
		     fi->fib_prefsrc == cfg->fc_prefsrc) && //源地址相等
		    (!cfg->fc_protocol ||
		     fi->fib_protocol == cfg->fc_protocol) && //路由协议类型
		    fib_nh_match(cfg, fi) == 0) {
			fa_to_delete = fa;
			break;
		}
	}

	if (!fa_to_delete)
		return -ESRCH;

	fa = fa_to_delete;
	rtmsg_fib(RTM_DELROUTE, htonl(key), fa, plen, tb->tb_id,
		  &cfg->fc_nlinfo, 0);

	list_del_rcu(&fa->fa_list);

	if (!plen)
		tb->tb_num_default--;

	if (list_empty(fa_head)) {
		hlist_del_rcu(&li->hlist);
		free_leaf_info(li);
	}

	if (hlist_empty(&l->list))
		trie_leaf_remove(t, l);

	if (fa->fa_state & FA_S_ACCESSED)
		rt_cache_flush(cfg->fc_nlinfo.nl_net);

	fib_release_info(fa->fa_info);
	alias_free_mem_rcu(fa);
	return 0;
}

c. cmd == SIOCADDRT的处理;

				tb = fib_new_table(net, cfg.fc_table); //cfg.fc_table = 0
				if (tb)
					err = fib_table_insert(tb, &cfg);
struct fib_table *fib_new_table(struct net *net, u32 id)
{
	struct fib_table *tb;
	unsigned int h;

	if (id == 0)
		id = RT_TABLE_MAIN;
	tb = fib_get_table(net, id); //检索tb是否被加入到id对应的链表(如RT_TABLE_LOCAL链表)上,被加入就直接退出,否则将执行 fib_trie_table
	if (tb)
		return tb;

	tb = fib_trie_table(id); //内存申请一个 fib_table
	if (!tb)
		return NULL;

	switch (id) {
	case RT_TABLE_LOCAL:
		net->ipv4.fib_local = tb;
		break;

	case RT_TABLE_MAIN:
		net->ipv4.fib_main = tb;
		break;

	case RT_TABLE_DEFAULT:
		net->ipv4.fib_default = tb;
		break;

	default:
		break;
	}

	h = id & (FIB_TABLE_HASHSZ - 1);
	hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);//将tb(struct fib_table *tb)添加到其链表上
	return tb;
}
int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
{
	struct trie *t = (struct trie *) tb->tb_data;
	struct fib_alias *fa, *new_fa;
	struct list_head *fa_head = NULL;
	struct fib_info *fi;
	int plen = cfg->fc_dst_len; //目的地址有效bit个数(如表示网络号的个数)
	u8 tos = cfg->fc_tos; //服务类型质量
	u32 key, mask;
	int err;
	struct leaf *l;

	if (plen > 32)
		return -EINVAL;

	key = ntohl(cfg->fc_dst); //获取目的地址

	pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen);

	mask = ntohl(inet_make_mask(plen)); //通过地址有效的个数,计算子网掩码

	if (key & ~mask) //网络号
		return -EINVAL;

	key = key & mask; //地址 & mask = 网络号

	fi = fib_create_info(cfg); //分配一个struct fib_info结构体
	if (IS_ERR(fi)) {
		err = PTR_ERR(fi);
		goto err;
	}

	l = fib_find_node(t, key);  //通过关键字key查找leaf
	fa = NULL;

	if (l) { //l为真表示叶子存在
		fa_head = get_fa_head(l, plen); //通过leaf->leaf_info->fa_alias获取其链表头
		fa = fib_find_alias(fa_head, tos, fi->fib_priority); //通过表头fa_head遍历是否存在相同的fa
	}

	/* Now fa, if non-NULL, points to the first fib alias
	 * with the same keys [prefix,tos,priority], if such key already
	 * exists or to the node before which we will insert new one.
	 *
	 * If fa is NULL, we will need to allocate a new one and
	 * insert to the head of f.
	 *
	 * If f is NULL, no fib node matched the destination key
	 * and we need to allocate a new one of those as well.
	 */

	if (fa && fa->fa_tos == tos &&
	    fa->fa_info->fib_priority == fi->fib_priority) { //表明存在相同的fa
		struct fib_alias *fa_first, *fa_match;

		err = -EEXIST;
		if (cfg->fc_nlflags & NLM_F_EXCL)
			goto out;

		/* We have 2 goals:
		 * 1. Find exact match for type, scope, fib_info to avoid
		 * duplicate routes
		 * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
		 */
		fa_match = NULL;
		fa_first = fa;
		fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
		list_for_each_entry_continue(fa, fa_head, fa_list) {
			if (fa->fa_tos != tos)
				break;
			if (fa->fa_info->fib_priority != fi->fib_priority)
				break;
			if (fa->fa_type == cfg->fc_type &&
			    fa->fa_info == fi) {
				fa_match = fa;
				break;
			}
		}

		if (cfg->fc_nlflags & NLM_F_REPLACE) { //存在,替换原来的
			struct fib_info *fi_drop;
			u8 state;

			fa = fa_first;
			if (fa_match) {
				if (fa == fa_match)
					err = 0;
				goto out; //上面匹配成功就直接退出,否则要新建一个new_fa
			}
			err = -ENOBUFS;
			new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
			if (new_fa == NULL)
				goto out;

			fi_drop = fa->fa_info;
			new_fa->fa_tos = fa->fa_tos;
			new_fa->fa_info = fi;
			new_fa->fa_type = cfg->fc_type;
			state = fa->fa_state;
			new_fa->fa_state = state & ~FA_S_ACCESSED;

			list_replace_rcu(&fa->fa_list, &new_fa->fa_list);
			alias_free_mem_rcu(fa);

			fib_release_info(fi_drop);
			if (state & FA_S_ACCESSED)
				rt_cache_flush(cfg->fc_nlinfo.nl_net);
			rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
				tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);

			goto succeeded;
		}
		/* Error if we find a perfect match which
		 * uses the same scope, type, and nexthop
		 * information.
		 */
		if (fa_match) //匹配成功就退出
			goto out;

		if (!(cfg->fc_nlflags & NLM_F_APPEND))
			fa = fa_first;
	}
	err = -ENOENT;
	if (!(cfg->fc_nlflags & NLM_F_CREATE))
		goto out;

	err = -ENOBUFS;
	new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); //到这里表明上面没有找到相同的fa,需重新申请一个新的
	if (new_fa == NULL)
		goto out;

	//初始化fib_alias结构体
	new_fa->fa_info = fi; //绑定上面分配的fi(fib_info)
	//关键字绑定
	new_fa->fa_tos = tos;
	new_fa->fa_type = cfg->fc_type;
	new_fa->fa_state = 0;
	/*
	 * Insert new entry to the list.
	 */

	if (!fa_head) { //为NULL,表明是第一次执行
		fa_head = fib_insert_node(t, key, plen); //插入一个节点,内部的实现还未理顺,待分析中,核心部分!!!
		if (unlikely(!fa_head)) {
			err = -ENOMEM;
			goto out_free_new_fa;
		}
	}

	if (!plen)
		tb->tb_num_default++;

	list_add_tail_rcu(&new_fa->fa_list,
			  (fa ? &fa->fa_list : fa_head));

	rt_cache_flush(cfg->fc_nlinfo.nl_net);
	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
		  &cfg->fc_nlinfo, 0);
succeeded:
	return 0;

out_free_new_fa:
	kmem_cache_free(fn_alias_kmem, new_fa);
out:
	fib_release_info(fi);
err:
	return err;
}

最后在该函数内部通过路由消息发送

	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
		  &cfg->fc_nlinfo, 0);

关于该路由接收的处理,这里不再赘述,详见:https://blog.csdn.net/chenliang0224/article/details/82534489 里面有描述接收部分的处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值