网络子系统81_inet协议族-SOCK_RAW(二)

//	struct sock->sk_prot字段
//		struct proto为插口层到传输层的接口
4.1 struct proto raw_prot = {
	.name		   = "RAW",
	.owner		   = THIS_MODULE,
	.close		   = raw_close,
	.destroy	   = raw_destroy,
	.connect	   = ip4_datagram_connect,
	.disconnect	   = udp_disconnect,
	.ioctl		   = raw_ioctl,
	.init		   = raw_init,
	.setsockopt	   = raw_setsockopt,
	.getsockopt	   = raw_getsockopt,
	.sendmsg	   = raw_sendmsg,
	.recvmsg	   = raw_recvmsg,
	.bind		   = raw_bind,
	.backlog_rcv   = raw_rcv_skb,
	.release_cb	   = ip4_datagram_release_cb,
	.hash		   = raw_hash_sk,
	.unhash		   = raw_unhash_sk,
	.obj_size	   = sizeof(struct raw_sock),
	.h.raw_hash	   = &raw_v4_hashinfo,
};

//	raw sock绑定地址到socket
4.1 static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	struct inet_sock *inet = inet_sk(sk);
	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
	int ret = -EINVAL;
	int chk_addr_ret;

	//sk->sk_state!=TCP_CLOSE说明连接已经建立
	if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_in))
		goto out;
	//检查绑定的地址类型是否合法
	chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
	ret = -EADDRNOTAVAIL;
	//合法的绑定地址类型:本地地址,多播地址,广播地址
	if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
	    chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
		goto out;

	//多播,广播地址,源地址使用接口设备的地址
	inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
	if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
		inet->inet_saddr = 0;
	sk_dst_reset(sk);
	ret = 0;
out:	return ret;
}

//	建立连接
//	步骤:
//		1.建立路由信息
//		2.更新sock->sk_state=TCP_ESTABLISHED表示建立了连接
4.2 int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	struct inet_sock *inet = inet_sk(sk);
	struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
	struct flowi4 *fl4;
	struct rtable *rt;
	__be32 saddr;
	int oif;
	int err;

	//重置inet的路由信息
	sk_dst_reset(sk);

	lock_sock(sk);
	//sock绑定的设备(可通过setsockopt建立)
	oif = sk->sk_bound_dev_if;
	saddr = inet->inet_saddr;
	fl4 = &inet->cork.fl.u.ip4;
	//建立路由信息
	rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr,
			      RT_CONN_FLAGS(sk), oif,
			      sk->sk_protocol,
			      inet->inet_sport, usin->sin_port, sk, true);

	...

	inet->inet_daddr = fl4->daddr;
	inet->inet_dport = usin->sin_port;
	//更新sk->sk_state表示建立了连接
	sk->sk_state = TCP_ESTABLISHED;
	inet->inet_id = jiffies;
	//设置sock路由缓存
	sk_dst_set(sk, &rt->dst);
	err = 0;
out:
	release_sock(sk);
	return err;
}

//	发送数据
//	步骤:
//		1.获取目的地址
//			1.1 通过msghdr或inet_sock
//		2.初始化flow4结构体
//		3.通过flow4查找路由表,获取路由信息
//		4.将数据报添加到sk->sk_write_queue
//		5.没有设置MSG_MORE,通知ip层进程数据传输
4.3 static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		       size_t len)
{
	struct inet_sock *inet = inet_sk(sk);
	struct ipcm_cookie ipc;
	struct rtable *rt = NULL;
	struct flowi4 fl4;
	int free = 0;
	__be32 daddr;
	__be32 saddr;
	u8  tos;
	int err;
	struct ip_options_data opt_copy;

	...

	ipc.addr = inet->inet_saddr;
	ipc.opt = NULL;
	ipc.tx_flags = 0;
	//使用bound的出口设备
	ipc.oif = sk->sk_bound_dev_if;

	saddr = ipc.addr;
	ipc.addr = daddr;

	tos = RT_CONN_FLAGS(sk);

	//初始化flowi4结构
	flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
			   RT_SCOPE_UNIVERSE,
			   inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
			   inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP,
			   daddr, saddr, 0, 0);

	//查找路由表,获得路由信息
	rt = ip_route_output_flow(sock_net(sk), &fl4, sk);

	err = -EACCES;
	//路由结果为广播,但是套接字不允许发送广播报文,直接返回
	if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST))
		goto done;

	//目的地址
	if (!ipc.addr)
		ipc.addr = fl4.daddr;
	lock_sock(sk);
	//添加数据报到sock->sk_write_queue
	err = ip_append_data(sk, &fl4, ip_generic_getfrag, msg->msg_iov, len, 0, &ipc, &rt, msg->msg_flags);
	//出现错误,丢弃所有已经pending的ip报文
	if (err)
		ip_flush_pending_frames(sk);
	//MSG_MORE表示接下来还有msg
	else if (!(msg->msg_flags & MSG_MORE)) {
		//将sk_write_queue上的数据发送出去
		err = ip_push_pending_frames(sk, &fl4);
		if (err == -ENOBUFS && !inet->recverr)
			err = 0;
		release_sock(sk);
	}
done:
	if (free)
		kfree(ipc.opt);
	//递减路由信息的引用计数
	ip_rt_put(rt);

out:
	if (err < 0)
		return err;
	return len;

}

//	接收数据
//	步骤:
//		1.接收数据报
//		2.将数据报拷贝到iovec中
4.4 static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		       size_t len, int noblock, int flags, int *addr_len)
{
	struct inet_sock *inet = inet_sk(sk);
	size_t copied = 0;
	int err = -EOPNOTSUPP;

	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
	struct sk_buff *skb;

	//接收数据报
	skb = skb_recv_datagram(sk, flags, noblock, &err);
	if (!skb)
		goto out;

	//将数据从skb拷贝到iovec
	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
	if (err)
		goto done;
	//拷贝skb中的源地址
	if (sin) {
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
		sin->sin_port = 0;
		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
	}

done:
	skb_free_datagram(sk, skb);
out:
	if (err)
		return err;
	return copied;
}
//	接收数据报
//	步骤:
//		1.获取允许的阻塞时间
//		2.如果sock->sk_receive_queue有接收到的skb,返回skb
//		3.如果可阻塞等待,则阻塞,否则直接返回null
4.5 struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
				    int *peeked, int *off, int *err)
{
	struct sk_buff *skb;
	long timeo;

	//计算等待到期时间
	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);

	do {
		//取下sock->sk_receive_queue接收到的skb
		unsigned long cpu_flags;
		struct sk_buff_head *queue = &sk->sk_receive_queue;

		spin_lock_irqsave(&queue->lock, cpu_flags);
		skb_queue_walk(queue, skb) {
			__skb_unlink(skb, queue);
			spin_unlock_irqrestore(&queue->lock, cpu_flags);
			return skb;
		}
		spin_unlock_irqrestore(&queue->lock, cpu_flags);

		error = -EAGAIN;
		if (!timeo)
			goto no_packet;
	//等待数据到来
	} while (!wait_for_packet(sk, err, &timeo));

	return NULL;

no_packet:
	*err = error;
	return NULL;
}

//	在sock上等待数据到来
//		1. 创建wait块,指定wait_func
//		2. 将wait块添加到sock->sk_wq上
//		3. 通过schedule_timeout阻塞
//	注:
//		创建的wait块添加到sock->sk_wq用于在有事件发生时,唤醒阻塞线程,
//		但是阻塞是通过schedule_timeout执行,而非wait块
4.6 static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
{
	int error;

	//创建wait块
	DEFINE_WAIT_FUNC(wait, receiver_wake_function);

	//将wait块添加到sock->sk_wq上
	prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);

	...

	error = 0;
	//重调度
	*timeo_p = schedule_timeout(*timeo_p);
out:
	finish_wait(sk_sleep(sk), &wait);
	return error;
}
时间: 2024-11-30 07:01:38

网络子系统81_inet协议族-SOCK_RAW(二)的相关文章

网络子系统80_inet协议族-SOCK_RAW(一)

// SOCK_RAW 1.1 static struct inet_protosw inetsw_array[] = { ... { .type = SOCK_RAW, .protocol = IPPROTO_IP, /* 通配符 */ .prot = &raw_prot, /* Networking protocol blocks attached to sockets */ .ops = &inet_sockraw_ops, .no_check = UDP_CSUM_DEFAULT,

网络子系统82_inet协议族-SOCK_RAW(三)

// 向raw sock传递skb // 步骤: // 1.根据协议号获取监听指定protocol的sock // 2.向raw sock传递skb 1.1 int raw_local_deliver(struct sk_buff *skb, int protocol) { int hash; struct sock *raw_sk; //获取监听指定协议的raw sock hash = protocol & (RAW_HTABLE_SIZE - 1); raw_sk = sk_head(&

网络子系统79_inet协议族

// inet协议族默认支持的协议类型 // 在inet_init中,通过inet_register_protosw注册到inetsw邻接表 1.1 static struct inet_protosw inetsw_array[] = { //流类型 { .type = SOCK_STREAM, .protocol = IPPROTO_TCP, .prot = &tcp_prot, .ops = &inet_stream_ops, .no_check = 0, .flags = INET

网络子系统85_inet协议族-l3向上

// ip数据报向本地传递 // 调用路径:ip_rcv->dst_input->...->ip_local_deliver 1.1 int ip_local_deliver(struct sk_buff *skb) { //如果ip数据报被分片,则重组 if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } //通过hook点,向上传递 return NF_H

网络子系统86_inet协议族-l4向下(一)

// l4数据向下l3传递 // 步骤: // 1.如果sock->sk_write_queue为空,初始化corking // 1.1 corking信息用于帮助ip层对数据进行分片 1.1 int ip_append_data(struct sock *sk, struct flowi4 *fl4, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from,

网络子系统48_ip协议数据帧的发送

//ip协议与l4协议接口,l4通过此接口向下l3传递数据帧 //函数主要任务: // 1.通过路由子系统路由封包 // 2.填充l3报头 // 3.ip分片 // 4.计算校验和 // 5.衔接邻居子系统,向下层传送封包. 1.1 int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); struct ip_opt

网络子系统42_ip协议数据帧的接收

//参考 深入理解linux网络技术内幕                                      // 注册l3协议 // ptype_all链表,链接所有ETH_P_ALL类型的l3协议 // ptype_base哈希表,非ETH_P_ALL类型的l3协议 // 注:l3协议可以使用相同的协议号 1.1 void dev_add_pack(struct packet_type *pt) { int hash; //ptype_all ptype_base共用一把锁 ptype

网络子系统36_BPDU协议格式

//参考 深入理解linux网络技术内幕 //通过以太网帧的目的地址,判断一个数据帧是否为bpdu数据帧 //1.BPDU封装格式:                                          //2.区分BPDU类型为CONFIG还是TCN,CONFIG类型的BPDU封包长38字节,TCN类型的BPDU封包长度为7字节 //由上图,越过以太网帧头6+6+2+3(llc头),之后为bpdu协议内容,偏移量为3的一个字节,指示封包为CONFIG或TCN            

网络子系统55_ip协议分片重组_加入ipq

//ip分片加入到正确的ipq结构 //调用路径:ip_defrag->ip_frag_queue // 处理过程: // 1.正在被释放的ipq,不处理新加入的分片(ipq正在被释放由last_in设置COMPLETE指出) // 2.处理分片的合法性 // 2.1当该封包为最后一个分片时 // 2.1.1如果之前没有接收到最后一个分片,则该分片在总有效载荷中的结尾位置需要大于等于以推测出的最大长度 // 2.1.2如果之前已经接收到最后一个分片,则该分片在总有效载荷中的结尾位置需要等于之前接