当调用sendto或其他应用层发送函数,向SOCK_DGRAM类套接字写数据,将引发内核调用udp_sendmsg函数,下面是阅读2.6内核相应部分后的一些笔记:
// 参数iocb是和内核I/O依赖的数据结构int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len)
{
struct inet_opt *inet = inet_sk(sk); // 将sk强制转化成struct inet_sock类型
struct udp_opt *up = udp_sk(sk); //将sk强制转化成struct udp_sock类型
int ulen = len;
struct ipcm_cookie ipc;
struct rtable *rt = NULL;
int free = 0;
int connected = 0;
u32 daddr, faddr, saddr;
u16 dport;
u8 tos;
int err;
//cork传递给ip_append_data,用于指出是否应该使用缓冲区机制。
//msg_flags其值即为传入的参数flags。
// MSG_MORE标志表明还将发送更多的参数
int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
//检查产度是否超过最大地址长度
if (len > 0xFFFF)
return -EMSGSIZE;
// MSG_OOB用来接受外带数据
if (msg->msg_flags&MSG_OOB)
return -EOPNOTSUPP;
ipc.opt = NULL;
//判断是否有数据需要发送。 如果该socket有等待发送的数据,那么直接将数据追加。如果没有就ulen加上udp首部的长度。
if (up->pending) {
//调用lock_sock函数锁定该套接字
lock_sock(sk);
//if(likely(value))和if(unlikely(value))都是宏函数,都是将"分支"信息提供给编译器以对代码进行优化,提高执行效率,带likely里的代码执行的机会更大些。
if (likely(up->pending)) {
if (unlikely(up->pending != AF_INET)) {
release_sock(sk);
return -EINVAL;
}
goto do_append_data;
}
release_sock(sk);
}
//ulen加上udp首部的长度。
ulen = sizeof(struct udphdr);
//msg-> msg_name 套接字名字
if (msg->msg_name) {
struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
if (msg->msg_namelen < sizeof(*usin))
return -EINVAL;
//因为这是udp_sendmsg函数,当然使用TCP/IP协议,所以判断协议是否为AF_INET协议族,如果不是,继续判断是否为AF_UNSPEC,如果不是,则返回不可用的地址
if (usin->sin_family != AF_INET) {
if (usin->sin_family != AF_UNSPEC)
return -EINVAL;
}
daddr = usin->sin_addr.s_addr;
dport = usin->sin_port;
if (dport == 0)
return -EINVAL;
} else {
//即使目的地址为空,但如果套接字处于连接状态,则仍然认为目的地址合法,允许继续传送数据
if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ;
daddr = inet->daddr;
dport = inet->dport;
connected = 1;
}
ipc.addr = inet->saddr;
ipc.oif = sk->sk_bound_dev_if;
//如果是控制报文,通过ip_cmsg_send处理控制报文
if (msg->msg_controllen) {
err = ip_cmsg_send(msg, &ipc);
if (err)
return err;
if (ipc.opt)
free = 1;
connected = 0;
}
if (!ipc.opt)
ipc.opt = inet->opt;
saddr = ipc.addr;
ipc.addr = faddr = daddr;
if (ipc.opt && ipc.opt->srr) {
if (!daddr)
return -EINVAL;
faddr = ipc.opt->faddr;
connected = 0;
}
tos = RT_TOS(inet->tos);
//确定是否需要路由信息
if (sk->sk_localroute || (msg->msg_flags & MSG_DONTROUTE) ||
(ipc.opt && ipc.opt->is_strictroute)) {
tos |= RTO_ONLINK;
connected = 0;
}
if (MULTICAST(daddr)) {
if (!ipc.oif)
ipc.oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
connected = 0;
}
//如果已经建立了套接字连接,则不需要重新查询路由
//直接从套接字的管理结构中返回路由表信息 ,并记录到rt 中
if (connected)
rt = (struct rtable*)sk_dst_check(sk, 0);
//如果rt为空,即无路由信息,则先用struct flowi结构记录查找路由表的索引信息,再调用 ip_route_output_flow函数查询路由表,得到路由信息。
if (rt == NULL) {
struct flowi fl = { .oif = ipc.oif,
.nl_u = { .ip4_u =
{ .daddr = faddr,
.saddr = saddr,
.tos = tos } },
.proto = IPPROTO_UDP,
.uli_u = { .ports =
{ .sport = inet->sport,
.dport = dport } } };
err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
if (err)
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->u.dst));
}
if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm;
back_from_confirm:
saddr = rt->rt_src;
if (!ipc.addr)
daddr = ipc.addr = rt->rt_dst;
lock_sock(sk);
if (unlikely(up->pending)) {
release_sock(sk);
err = -EINVAL;
goto out;
}
inet->cork.fl.fl4_dst = daddr;
inet->cork.fl.fl_ip_dport = dport;
inet->cork.fl.fl4_src = saddr;
inet->cork.fl.fl_ip_sport = inet->sport;
up->pending = AF_INET;
do_append_data:
up->len = ulen;
//对UDP数据包进行分片处理,为IP层分片处理做好准备
err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
sizeof(struct udphdr), &ipc, rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err)
udp_flush_pending_frames(sk);
//上层应用指定flags为MSG_MORE时,corkreq为1
else if (!corkreq)
err = udp_push_pending_frames(sk, up);
release_sock(sk);
out:
ip_rt_put(rt);
if (free)
kfree(ipc.opt);
if (!err) {
UDP_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
return len;
}
return err;
do_confirm:
dst_confirm(&rt->u.dst);
if (!(msg->msg_flags&MSG_PROBE) || len)
goto back_from_confirm;
err = 0;
goto out;
}
阅读(583) | 评论(0) | 转发(0) |