【Linux5.4】【TUN】代码学习记录(3)--tun_link_ops

本文详细解析了Linux5.4中的TUN模块中的tun_link_ops结构,包括其在初始化中的作用,以及tun_link_ops结构体的内容,如setup、validate等函数的含义。还介绍了net_device私有空间和与ethtool工具操作的相关设置。
摘要由CSDN通过智能技术生成

【Linux5.4】【TUN】代码学习记录(3)–tun_link_ops

在tun_init初始化函数中,由rtnl_link_register执行注册,将tun_link_ops加入链表link_ops中。

tun_link_ops包含内容

static struct rtnl_link_ops tun_link_ops __read_mostly = {
	.kind		= DRV_NAME,
	.priv_size	= sizeof(struct tun_struct),
	.setup		= tun_setup,
	.validate	= tun_validate,
	.get_size       = tun_get_size,
	.fill_info      = tun_fill_info,
};

tun_link_ops是rtnl_link_ops结构体对象,后者定义在<include/net/rtnetlink.h>中

struct rtnl_link_ops {
	struct list_head	list;//内部使用,实际上在rtnl_link_register中,最后调用的list_add_tail(&ops->list, &link_ops)就是它。

	const char		*kind;//标示符,用来检验link_ops是否已存在一样的ops

	size_t			priv_size;//网络设备私有空间大小
	/* setup 网络设备设置功能 */
	void			(*setup)(struct net_device *dev);

	unsigned int		maxtype;//最高的设备特有netlink属性号
	const struct nla_policy	*policy;//用于设备特定属性验证的Netlink策略
	/* validate netlink/changelink参数的可选验证功能 */
	int			(*validate)(struct nlattr *tb[],
					    struct nlattr *data[],
					    struct netlink_ext_ack *extack);
	/* newlink 配置和注册新设备 */
	int			(*newlink)(struct net *src_net,
					   struct net_device *dev,
					   struct nlattr *tb[],
					   struct nlattr *data[],
					   struct netlink_ext_ack *extack);
	/* changelink 修改已有设备参数 */
	int			(*changelink)(struct net_device *dev,
					      struct nlattr *tb[],
					      struct nlattr *data[],
					      struct netlink_ext_ack *extack);
	/* dellink 移除设备 */
	void			(*dellink)(struct net_device *dev,
					   struct list_head *head);
	/* get_size 计算转储设备特定netlink属性所需的空间 */
	size_t			(*get_size)(const struct net_device *dev);
	/* fill_info 转储设备特定的netlink属性 */
	int			(*fill_info)(struct sk_buff *skb,
					     const struct net_device *dev);
	/* get_xstats_size 计算转储设备特殊统计信息所需空间 */
	size_t			(*get_xstats_size)(const struct net_device *dev);
	/* fill_xstats 转储设备统计信息 */
	int			(*fill_xstats)(struct sk_buff *skb,
					       const struct net_device *dev);
	/* 确定要创建的传输队列的数量。 */
	unsigned int		(*get_num_tx_queues)(void);
	/* 确定要创建的接收队列的数量。 */
	unsigned int		(*get_num_rx_queues)(void);

	unsigned int		slave_maxtype;
	const struct nla_policy	*slave_policy;
	int			(*slave_changelink)(struct net_device *dev,
						    struct net_device *slave_dev,
						    struct nlattr *tb[],
						    struct nlattr *data[],
						    struct netlink_ext_ack *extack);
	size_t			(*get_slave_size)(const struct net_device *dev,
						  const struct net_device *slave_dev);
	int			(*fill_slave_info)(struct sk_buff *skb,
						   const struct net_device *dev,
						   const struct net_device *slave_dev);
	/* get_link_net 用于获取设备的i/o网络命名空间 */
	struct net		*(*get_link_net)(const struct net_device *dev);
	/* get_linkxtats_size 用于计算转储特定于设备的扩展链路状态所需的空间 */
	size_t			(*get_linkxstats_size)(const struct net_device *dev,
						       int attr);
	/* fill_linkxstats 转储设备特殊的扩展链路状态 */
	int			(*fill_linkxstats)(struct sk_buff *skb,
						   const struct net_device *dev,
						   int *prividx, int attr);
};

备注:tun_link_ops 中.priv_size = sizeof(struct tun_struct),按照priv_size定义,是网络设备私有空间大小,表明tun_struct结构体是net_device结构体私有数据结构。这在tun.c代码中有所体现

struct tun_struct *tun = netdev_priv(dev);

tun_setup

static void tun_setup(struct net_device *dev)
{
	struct tun_struct *tun = netdev_priv(dev);

	tun->owner = INVALID_UID;
	tun->group = INVALID_GID;
	tun_default_link_ksettings(dev, &tun->link_ksettings);

	dev->ethtool_ops = &tun_ethtool_ops;  //定义了一组ethtool工具的函数操作集
	/* 
	 * needs_free_netdev与priv_destructor 用于释放网络设备 
	 * needs_free_netdev标志设置true
	 * 由netdev_run_todo执行free_netdev操作
	 * priv_destructor用于释放dev中私有数据所占用内存
	 * 这样做将net_device的释放与其私有数据的释放方法进行分割
	 */
	dev->needs_free_netdev = true;
	dev->priv_destructor = tun_free_netdev;
	/* We prefer our own queue length */
	dev->tx_queue_len = TUN_READQ_SIZE;//TUN_READQ_SIZE定义500,这里当TUN设备建立后通过ifconfig可查看到该数值。
}
  1. 先看netdev_priv,定义在<include/linux/netdevice.h>
/**
 1. netdev_priv - access network device private data
 2. @dev: network device
 3.  4. Get network device private data
 */
static inline void *netdev_priv(const struct net_device *dev)
{
	return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
}

netdev_priv就是在dev首地址+32位对齐的偏移量,得到私有数据地址。

  1. 再看tun_default_link_ksettings
static void tun_default_link_ksettings(struct net_device *dev,
				       struct ethtool_link_ksettings *cmd)
{
	ethtool_link_ksettings_zero_link_mode(cmd, supported);
	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
	cmd->base.speed		= SPEED_10;  //带宽10M
	cmd->base.duplex	= DUPLEX_FULL;  //全双工
	cmd->base.port		= PORT_TP;  //双绞线
	cmd->base.phy_address	= 0;
	cmd->base.autoneg	= AUTONEG_DISABLE;  //自协商无效
}

???这里只看到对ethtool_link_ksettings 结构体对象cmd进行了设置,但没看懂跟dev的关系,暂不清楚,也可能跟dev没关系只是参数必须带dev,在此留个疑问

tun_validate

简单的netlink操作集允许删除netlink或tap设备。

/* Trivial set of netlink ops to allow deleting tun or tap
 * device with netlink.
 */
static int tun_validate(struct nlattr *tb[], struct nlattr *data[],
			struct netlink_ext_ack *extack)
{
	NL_SET_ERR_MSG(extack,
		       "tun/tap creation via rtnetlink is not supported.");
	return -EOPNOTSUPP;
}

???这个属实是没明白,该功能到底有没有实现。。。

tun_get_size

/* get_size 计算转储设备特定netlink属性所需的空间 */
static size_t tun_get_size(const struct net_device *dev)
{
	BUILD_BUG_ON(sizeof(u32) != sizeof(uid_t));
	BUILD_BUG_ON(sizeof(u32) != sizeof(gid_t));

	return nla_total_size(sizeof(uid_t)) + /* OWNER */
	       nla_total_size(sizeof(gid_t)) + /* GROUP */
	       nla_total_size(sizeof(u8)) + /* TYPE */
	       nla_total_size(sizeof(u8)) + /* PI */
	       nla_total_size(sizeof(u8)) + /* VNET_HDR */
	       nla_total_size(sizeof(u8)) + /* PERSIST */
	       nla_total_size(sizeof(u8)) + /* MULTI_QUEUE */
	       nla_total_size(sizeof(u32)) + /* NUM_QUEUES */
	       nla_total_size(sizeof(u32)) + /* NUM_DISABLED_QUEUES */
	       0;
}

tun_fill_info

tun_fill_info是要在skb中配置属性数据。

/* fill_info 转储设备特定的netlink属性 */
static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
	struct tun_struct *tun = netdev_priv(dev);

	if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK))
		goto nla_put_failure;
	if (uid_valid(tun->owner) &&
	    nla_put_u32(skb, IFLA_TUN_OWNER,
			from_kuid_munged(current_user_ns(), tun->owner)))
		goto nla_put_failure;
	if (gid_valid(tun->group) &&
	    nla_put_u32(skb, IFLA_TUN_GROUP,
			from_kgid_munged(current_user_ns(), tun->group)))
		goto nla_put_failure;
	if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI)))
		goto nla_put_failure;
	if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR)))
		goto nla_put_failure;
	if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST)))
		goto nla_put_failure;
	if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE,
		       !!(tun->flags & IFF_MULTI_QUEUE)))
		goto nla_put_failure;
	if (tun->flags & IFF_MULTI_QUEUE) {
		if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues))
			goto nla_put_failure;
		if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES,
				tun->numdisabled))
			goto nla_put_failure;
	}

	return 0;

nla_put_failure:
	return -EMSGSIZE;
}

其中nla_put_u8 和 nla_put_u32定义在<include/net/netlink.h>
作用是向skb中写入属性。

/**
 * nla_put_u8 - Add a u8 netlink attribute to a socket buffer
 * @skb: socket buffer to add attribute to
 * @attrtype: attribute type
 * @value: numeric value
 */
static inline int nla_put_u8(struct sk_buff *skb, int attrtype, u8 value)
{
	/* temporary variables to work around GCC PR81715 with asan-stack=1 */
	u8 tmp = value;

	return nla_put(skb, attrtype, sizeof(u8), &tmp);
}
/**
 * nla_put_u32 - Add a u32 netlink attribute to a socket buffer
 * @skb: socket buffer to add attribute to
 * @attrtype: attribute type
 * @value: numeric value
 */
static inline int nla_put_u32(struct sk_buff *skb, int attrtype, u32 value)
{
	u32 tmp = value;

	return nla_put(skb, attrtype, sizeof(u32), &tmp);
}

可以看到nla_put_u8 和 nla_put_u32都是对nla_put函数的封装。
nla_put实际调用__nla_put,而__nla_put首先调用__nla_reserve用于在skb中预留出要添加属性的内存空间,之后配置属性。
nla_put、__nla_put、__nla_reserve定义在<lib/nlattr.c>

/**
 * __nla_reserve - reserve room for attribute on the skb
 * @skb: socket buffer to reserve room on
 * @attrtype: attribute type
 * @attrlen: length of attribute payload
 *
 * Adds a netlink attribute header to a socket buffer and reserves
 * room for the payload but does not copy it.
 *
 * The caller is responsible to ensure that the skb provides enough
 * tailroom for the attribute header and payload.
 */
struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
{
	struct nlattr *nla;
	/* 
	 * 在skb内预留nla_total_size(attrlen)大小空间,
	 * nla_total_size(attrlen) = attrlen + NLA_HDRLEN + 4字节对齐
	 */
	nla = skb_put(skb, nla_total_size(attrlen));
	nla->nla_type = attrtype;//属性类型
	nla->nla_len = nla_attr_size(attrlen);//NLA_HDRLEN + attrlen

	memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));

	return nla;
}
EXPORT_SYMBOL(__nla_reserve);
/**
 * __nla_put - Add a netlink attribute to a socket buffer
 * @skb: socket buffer to add attribute to
 * @attrtype: attribute type
 * @attrlen: length of attribute payload
 * @data: head of attribute payload
 *
 * The caller is responsible to ensure that the skb provides enough
 * tailroom for the attribute header and payload.
 */
void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,
			     const void *data)
{
	struct nlattr *nla;

	nla = __nla_reserve(skb, attrtype, attrlen);
	memcpy(nla_data(nla), data, attrlen);
}
EXPORT_SYMBOL(__nla_put);
/**
 * nla_put - Add a netlink attribute to a socket buffer
 * @skb: socket buffer to add attribute to
 * @attrtype: attribute type
 * @attrlen: length of attribute payload
 * @data: head of attribute payload
 *
 * Returns -EMSGSIZE if the tailroom of the skb is insufficient to store
 * the attribute header and payload.
 */
int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
{
	if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen)))
		return -EMSGSIZE;
	__nla_put(skb, attrtype, attrlen, data);
	return 0;
}
EXPORT_SYMBOL(nla_put);

nla_attr_size 与nla_total_size定义在<include/net/netlink.h>

/**
 * nla_attr_size - length of attribute not including padding
 * @payload: length of payload
 */
static inline int nla_attr_size(int payload)
{
	return NLA_HDRLEN + payload;
}

/**
 * nla_total_size - total length of attribute including padding
 * @payload: length of payload
 */
static inline int nla_total_size(int payload)
{
	return NLA_ALIGN(nla_attr_size(payload));//payload + NLA_HDRLEN(属性头长度)+对齐内存
}

其中宏定义 NLA_ALIGN(x)表示对x进行4对齐并向上取整,即x=2返回4,x=5返回8

#define NLA_ALIGNTO		4
#define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值