网络子系统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, }; // 发送数据 // 步骤: // 1.获取目的地址 // 1.1 通过msghdr或inet_sock // 2.初始化flow4结构体 // 3.通过flow4查找路由表,获取路由信息 // 4.将数据报添加到sk->sk_write_queue // 5.没有设置MSG_MORE,通过ip层进程数据传输 4.2 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; //获取目的地址 // 通过msghdr或inet_sock提供 if (msg->msg_namelen) { struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name; err = -EINVAL; if (msg->msg_namelen < sizeof(*usin)) goto out; if (usin->sin_family != AF_INET) { pr_info_once("%s: %s forgot to set AF_INET. Fix it!\n", __func__, current->comm); err = -EAFNOSUPPORT; if (usin->sin_family) goto out; } daddr = usin->sin_addr.s_addr; } else { err = -EDESTADDRREQ; //连接没有建立 if (sk->sk_state != TCP_ESTABLISHED) goto out; daddr = inet->inet_daddr; } ipc.addr = inet->inet_saddr; ipc.opt = NULL; ipc.tx_flags = 0; 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.3 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.4 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; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。