【Linux4.1.12源码分析】vxlan报文发送之udp_tunnel_xmit_skb

本文深入剖析Linux 4.1.12内核中的udp_tunnel_xmit_skb函数,该函数作为Open vSwitch (OVS) 2.5发送UDP报文的入口。在调用此函数前,必须预先准备好headroom空间,并已构建VXLAN头部,skb数据指针指向VXLAN头部。

udp_tunnel_xmit_skb函数是OVS2.5发送UDP报文的内核入口,在调用该函数之前,headroom空间需要准备完成,且vxlan头已经创建,skb结构体的data指向vxlan头的首地址。

1、udp_tunnel_xmit_skb函数

int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
			__be32 src, __be32 dst, __u8 tos, __u8 ttl,
			__be16 df, __be16 src_port, __be16 dst_port,
			bool xnet, bool nocheck)
{
	struct udphdr *uh;

	__skb_push(skb, sizeof(*uh));		//skb增加UDP头,skb报文的headroom大小在vxlan头封装前就完成准备,线性区的空间是充足的
	skb_reset_transport_header(skb);	//重置报文(外层报文)的transport header的偏移
	uh = udp_hdr(skb);

	uh->dest = dst_port;			//设置目的端口
	uh->source = src_port;			//设置源端端口
	uh->len = htons(skb->len);		//设置UDP头中的长度,该长度包括UDP头 + vxlan头 + 用户数据(payload)

	udp_set_csum(nocheck, skb, src, dst, skb->len);		//UDP头csum计算

	return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,	//IP层封装tunnel发送报文
			     tos, ttl, df, xnet);
}
2、udp_set_csum函数

/* Function 
补充dev_queue_xmit实现,请问哪个地方是调用vlan设备的发送函数? static int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) { struct net_device *dev = skb->dev; struct netdev_queue *txq; struct Qdisc *q; int rc = -ENOMEM; bool again = false; skb_reset_mac_header(skb); if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP)) __skb_tstamp_tx(skb, NULL, skb->sk, SCM_TSTAMP_SCHED); /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); skb_update_prio(skb); qdisc_pkt_len_init(skb); #ifdef CONFIG_NET_CLS_ACT skb->tc_at_ingress = 0; # ifdef CONFIG_NET_EGRESS if (static_branch_unlikely(&egress_needed_key)) { skb = sch_handle_egress(skb, &rc, dev); if (!skb) goto out; } # endif #endif /* If device/qdisc don't need skb->dst, release it right now while * its hot in this cpu cache. */ if (dev->priv_flags & IFF_XMIT_DST_RELEASE) skb_dst_drop(skb); else skb_dst_force(skb); txq = netdev_core_pick_tx(dev, skb, sb_dev); q = rcu_dereference_bh(txq->qdisc); trace_net_dev_queue(skb); if (q->enqueue) { rc = __dev_xmit_skb(skb, q, dev, txq); goto out; } /* The device has no queue. Common case for software devices: * loopback, all the sorts of tunnels... * Really, it is unlikely that netif_tx_lock protection is necessary * here. (f.e. loopback and IP tunnels are clean ignoring statistics * counters.) * However, it is possible, that they rely on protection * made by us here. * Check this and shot the lock. It is not prone from deadlocks. *Either shot noqueue qdisc, it is even simpler 8) */ // 确保设备已启用,如ifconfig up if (dev->flags & IFF_UP) { int cpu = smp_processor_id(); /* ok because BHs are off */ /* Other cpus might concurrently change txq->xmit_lock_owner * to -1 or to their cpu id, but not to our id. */ if (READ_ONCE(txq->xmit_lock_owner) != cpu) { if (dev_xmit_recursion()) // 检查当前线程递归深度 goto recursion_alert; // 过深则放弃发送 skb = validate_xmit_skb(skb, dev, &again); if (!skb) goto out; HARD_TX_LOCK(dev, txq, cpu); if (!netif_xmit_stopped(txq)) { dev_xmit_recursion_inc(); // 增加递归计数器 skb = dev_hard_start_xmit(skb, dev, txq, &rc); // 执行实际发送 dev_xmit_recursion_dec(); if (dev_xmit_complete(rc)) { // 发送成功完成? HARD_TX_UNLOCK(dev, txq); goto out; } } HARD_TX_UNLOCK(dev, txq); net_crit_ratelimited("Virtual device %s asks to queue packet!\n", dev->name); } else { /* Recursion is detected! It is possible, * unfortunately 若当前 CPU 已是锁所有者(txq->xmit_lock_owner == cpu),说明发生递归调用,触发死锁警告 */ recursion_alert: net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n", dev->name); } } rc = -ENETDOWN; rcu_read_unlock_bh(); atomic_long_inc(&dev->tx_dropped); kfree_skb_list(skb); return rc; out: rcu_read_unlock_bh(); return rc; }
最新发布
11-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值