linux内核发送udp,linux内核中的udp_sendmsg函数分析

当调用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;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值