udp

您所在的位置:网站首页 netfilter源码分析 udp

udp

2023-06-05 23:42| 来源: 网络整理| 查看: 265

源码分析 int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) { /*套接字的网络层表示转换成INET套接字的表示*/ struct inet_sock *inet = inet_sk(sk); /*套接字的网络层表示转换成UDP套接字的表示*/ struct udp_sock *up = udp_sk(sk); /*struct sockaddr_in * usin = ({ do { } while (0); (struct sockaddr_in *) msg->msg_name; })获取ip地址和端口*/ DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name); struct flowi4 fl4_stack; struct flowi4 *fl4; int ulen = len; struct ipcm_cookie ipc; struct rtable *rt = NULL; int free = 0; int connected = 0; __be32 daddr, faddr, saddr; __be16 dport; u8 tos; /*获取pcflag标志确定该套接字是普通的UDP套接字还是UDP轻量级套接字*/ int err, is_udplite = IS_UDPLITE(sk); /*如果 up->corkflag> 0,则将套接字标记为 UDP-Lite,后半段判断是否还需要发送更多消息*/ int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); struct sk_buff *skb; struct ip_options_data opt_copy; /*UDP数据报最长为64KB*/ if (len > 0xFFFF) return -EMSGSIZE; /* * Check the flags. */ /*UDP不支持发送带外数据,如果发送标志中设置了MSG_OOB,则返回*/ if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */ return -EOPNOTSUPP; /*udp和udplite使用不同的getfrag*/ getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; fl4 = &inet->cork.fl.u.ip4;/*struct flowi4类型,*/ if (up->pending) { /* * There are pending frames. * The socket lock must be held while it's corked. */ /*当前的sock有等待发送的数据,直接将数据追加*/ lock_sock(sk); /*likely和unlikely对程序逻辑没影响,likely提示编译器括号内的内容为真的概率更大,unlikely相反*/ if (likely(up->pending)) { if (unlikely(up->pending != AF_INET)) { /* 和lock_sock成对出现,一个加锁,一个解锁*/ release_sock(sk); return -EINVAL; } /*up->pending为AF_INET时候,直接跳转到数据发送,这里进行的了第一次数据发送后的数据发送*/ goto do_append_data; } release_sock(sk); } /*接下来的都是第一次发包时的操作,UDP数据报长度,包括UDP data + UDP header*/ ulen += sizeof(struct udphdr); /* * Get and verify the address. */ /*获取目的IP地址和端口:目的地址和端口有两个可能的来源: 1. 如果之前socket已经建立,那socket本身就存储了目标地址; 2. 地址通过msghdr传入,通常为调用sendto发送UDP数据*/ if (usin) { if (msg->msg_namelen < sizeof(*usin)) return -EINVAL; if (usin->sin_family != AF_INET) { if (usin->sin_family != AF_UNSPEC) return -EAFNOSUPPORT; } daddr = usin->sin_addr.s_addr; dport = usin->sin_port; /*目的端口不能为零*/ if (dport == 0) return -EINVAL; } else { /*msg没有目的地址的情况:通常为先调用了connect,然后调用send发送UDP数据, UDP套接字调用connetc之后,UDP传输控制块状态为TCP_ESTABLISHED*/ if (sk->sk_state != TCP_ESTABLISHED)/*即没有指明目的地址,又没有建立connect连接,则返错。*/ return -EDESTADDRREQ; daddr = inet->inet_daddr; dport = inet->inet_dport; /* Open fast path for connected socket. Route will not be used, if at least one option is set. */ /*为连接的套接字打开快速路径。如果至少设置了一个选项,则不会使用路由。*/ connected = 1; } /*获取存储在 socket 上的源地址、发送网络设备索引(device index)和时间戳选项*/ ipcm_init_sk(&ipc, inet); /*保留了当套接字被解锁时创建 UDP 标头的信息。*/ ipc.gso_size = up->gso_size; /*msg中控制信息处理*/ if (msg->msg_controllen) { /*如果msg_controllen辅助缓冲区中有数据,则消息发送*/ err = udp_cmsg_send(sk, msg, &ipc.gso_size); if (err > 0) /*调用ip_cmsg_send处理控制信息,包括IP选项等...*/ err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6); if (unlikely(err < 0)) { kfree(ipc.opt); return err; } if (ipc.opt) free = 1; /*这里表示不进行路由*/ connected = 0; } /*如果发送的数据中没有IP选项控制信息,则从正在使用的socket中获取IP选项信息*/ if (!ipc.opt) { struct ip_options_rcu *inet_opt; rcu_read_lock(); inet_opt = rcu_dereference(inet->inet_opt); if (inet_opt) { memcpy(&opt_copy, inet_opt, sizeof(*inet_opt) + inet_opt->opt.optlen); ipc.opt = &opt_copy.opt; } rcu_read_unlock(); } if (cgroup_bpf_enabled(BPF_CGROUP_UDP4_SENDMSG) && !connected) { err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk, (struct sockaddr *)usin, &ipc.addr); if (err) goto out_free; if (usin) { if (usin->sin_port == 0) { /* BPF program set invalid port. Reject it. */ /*linux中不允许目的端口为0*/ err = -EINVAL; goto out_free; } daddr = usin->sin_addr.s_addr; dport = usin->sin_port; } } saddr = ipc.addr; ipc.addr = faddr = daddr; if (ipc.opt && ipc.opt->opt.srr) { if (!daddr) { err = -EINVAL; goto out_free; } faddr = ipc.opt->opt.faddr; /*这里表示不进行路由,进行本地路由*/ connected = 0; } tos = get_rttos(&ipc, inet); if (sock_flag(sk, SOCK_LOCALROUTE) || (msg->msg_flags & MSG_DONTROUTE) || (ipc.opt && ipc.opt->opt.is_strictroute)) { tos |= RTO_ONLINK; //SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ //MSG_DONTROUTE,顾名思义,消息不路由 //ipc.opt && ipc.opt->opt.is_strictroute同时不为零 /*这里表示不进行路由,以上3种情况只进行本地路由*/ connected = 0; } //ip地址为224.*.*.*时 if (ipv4_is_multicast(daddr)) { if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) ipc.oif = inet->mc_index; if (!saddr) saddr = inet->mc_addr; connected = 0; } else if (!ipc.oif) { ipc.oif = inet->uc_index; } else if (ipv4_is_lbcast(daddr) && inet->uc_index) {/*ip地址如果为255.255.255.255*/ /* oif is set, packet is to local broadcast and * uc_index is set. oif is most likely set * by sk_bound_dev_if. If uc_index != oif check if the * oif is an L3 master and uc_index is an L3 slave. * If so, we want to allow the send using the uc_index. */ /*oif 设置,数据包到本地广播并设置 uc_index。 oif 很可能由 sk_bound_dev_if 设置。 如果 uc_index != oif 检查 oif 是否是 L3 master 并且 uc_index 是否是 L3 slave。 如果是这样,我们希望允许使用 uc_index 发送。*/ if (ipc.oif != inet->uc_index && ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk), inet->uc_index)) { ipc.oif = inet->uc_index; } } if (connected) /*目标路由检查*/ rt = (struct rtable *)sk_dst_check(sk, 0); //如果没有路由,建立一次路由 if (!rt) { struct net *net = sock_net(sk); __u8 flow_flags = inet_sk_flowi_flags(sk); fl4 = &fl4_stack; flowi4_init_output(fl4, ipc.oif, ipc.sockc.mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, flow_flags, faddr, saddr, dport, inet->inet_sport, sk->sk_uid); security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); rt = NULL; if (err == -ENETUNREACH) IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); goto out; } err = -EACCES; if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) goto out; if (connected) sk_dst_set(sk, dst_clone(&rt->dst)); } //msg_flags为确认路径有效性的话,跳转到do_confirm if (msg->msg_flags&MSG_CONFIRM) goto do_confirm; back_from_confirm: saddr = fl4->saddr; if (!ipc.addr) daddr = ipc.addr = fl4->daddr; /* Lockless fast path for the non-corking case. */ if (!corkreq) { struct inet_cork cork; skb = ip_make_skb(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, &cork, msg->msg_flags); err = PTR_ERR(skb); if (!IS_ERR_OR_NULL(skb)) err = udp_send_skb(skb, fl4, &cork); goto out; } lock_sock(sk); if (unlikely(up->pending)) { /* The socket is already corked while preparing it. */ /* ... which is an evident application bug. --ANK */ release_sock(sk); //这里表示程序错误导致阻塞,将不会发送数据 net_dbg_ratelimited("socket already corked\n"); err = -EINVAL; goto out; } /* * Now cork the socket to pend data. */ fl4 = &inet->cork.fl.u.ip4; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; fl4->fl4_sport = inet->inet_sport; up->pending = AF_INET; do_append_data: up->len += ulen; /*udp组ip包*/ err = ip_append_data(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err) udp_flush_pending_frames(sk); else if (!corkreq) err = udp_push_pending_frames(sk); else if (unlikely(skb_queue_empty(&sk->sk_write_queue))) up->pending = 0; release_sock(sk); out: ip_rt_put(rt);//等效release_sock(sk); out_free: if (free) kfree(ipc.opt);//释放内存,内部将ipc.opt==NULL if (!err) return len;//处理过程没有错误,返回已发送的字节数 /* * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting * ENOBUFS might not be good (it's not tunable per se), but otherwise * we don't have a good statistic (IpOutDiscards but it can be too many * things). We could add another new stat but at least for now that * seems like overkill. */ /*ENOBUFS = 无内核内存,SOCK_NOSPACE = 无 sndbuf 空间。 报告 ENOBUFS 可能不好(它本身不可调),但除此之外我们没有很好的统计数据(IpOutDiscards,但它可能太多了)。 我们可以添加另一个新的统计数据,但至少现在这似乎有点矫枉过正。*/ if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { UDP_INC_STATS(sock_net(sk), UDP_MIB_SNDBUFERRORS, is_udplite); } return err; do_confirm: if (msg->msg_flags & MSG_PROBE)//MSG_PROBE表示不发送,仅探测路径 dst_confirm_neigh(&rt->dst, &fl4->daddr); if (!(msg->msg_flags&MSG_PROBE) || len) goto back_from_confirm; err = 0; goto out; } EXPORT_SYMBOL(udp_sendmsg); 总结

1.参数sk:套接字的网络层表示,msg:传递有效负荷,len:数据字节长度不包含udphdr 2.首先获取到目标地址和端口,原地址和端口,目的端口不能为0,目标地址为广播地址或者开头为224操作不同。 3.虽然是面向无连接的,但是需要起始点到目的地之间的链路,发送数据必须有一条路由缓存。如果目的地址改变,则重新建立一条路由并获取缓存。 4.数据包过大时分包,第一次发送数据和第二次发送数据的逻辑不同 5.发包中没有包含校验和的操作,只含有数据拼接和发送数据前的准备工作。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3