声明:本人也是初学者,错误在所难免,希望大家发现后能指出,不胜感激。愿共同进步
480 int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len)
482 {
//结构体的初始化,以及一些变量的定义
//将sk转为udp内部使用的类型指针,在TCP/IP中充斥了这样的转换。主要原因是因为对于上层来说,需要一个统一的类型,而到了底层的具体实现时,都会将上层抽象的数据类型,转为自己所需的类型。
483 struct inet_opt *inet = inet_sk(sk);
484 struct udp_opt *up = udp_sk(sk);
485 int ulen = len;
486 struct ipcm_cookie ipc;
rt变量被初始化为NULL,此处为bug,详情http://blog.csdn.net/abc78400123/article/details/6695161
487 struct rtable *rt = NULL;
488 int free = 0;
489 int connected = 0;
490 u32 daddr, faddr, saddr;
491 u16 dport;
492 u8 tos;
493 int err;
//cork传递给ip_append_data,用于指出是否应该使用缓冲区机制。msg_flags其值即为传入的参数flags。 MSG_MORE标志表明还将发送更多的参数
494 int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
495
//检查产度是否超过最大地址长度
496 if (len > 0xFFFF)
497 return -EMSGSIZE;//Message too long
499 /*
500 * Check the flags.
501 */
//检查是否含有带外标志,如果有的话,返回操作不被支持
503 if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */
504 return -EOPNOTSUPP;//Operation not supported on transport endpoint
506 ipc.opt = NULL;
//linux udp协议允许多个udp数据包合并成一个发送出去,提高发送效率。
//判断是否有更多的数据需要发送。 如果该socket有等待发送的数据,那么直接将数据追加。如果没有就ulen加上udp首部的长度。
508 if (up->pending) {
509 /*
510 * There are pending frames.
511 * The socket lock must be held while it's corked.
512 */
//调用lock_sock函数锁定该套接字
513 lock_sock(sk);
514 if (likely(up->pending)) {
515 if (unlikely(up->pending != AF_INET)) {
516 release_sock(sk);
517 return -EINVAL;
518 }
//将数据发送出去
519 goto do_append_data;
520 }
//释放上锁的套接字时
521 release_sock(sk);
522 }
//ulen加上udp首部的长度。
523 ulen += sizeof(struct udphdr);
524
525 /*
526 * Get and verify the address.
527 */
//检查struct msghdr结构的msg_name字段,确定目的地址是否合法。
struct msghdr结构详见http://blog.csdn.net/abc78400123/article/details/6695276
//判断目的地址是否为空
528 if (msg->msg_name) {
529 struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
//?估计是判断强制转换后赋值是否成功?
530 if (msg->msg_namelen < sizeof(*usin))
531 return -EINVAL;
//,因为这是udp_sendmsg函数,当然使用TCP/IP协议,所以判断协议是否为AF_INET协议族,如果不是,继续判断是否为AF_UNSPEC,如果不是,则返回不可用的地址
532 if (usin->sin_family != AF_INET) {
533 if (usin->sin_family != AF_UNSPEC)
534 return -EINVAL;
535 }
536
//如果上述语句执行顺利,则把上层应用程序的套接字地址赋给本函数。
537 daddr = usin->sin_addr.s_addr;
538 dport = usin->sin_port;
539 if (dport == 0)
540 return -EINVAL;
541 } else {
//即使目的地址为空,但如果套接字处于连接状态,则仍然认为目的地址合法,允许继续传送数据
542 if (sk->sk_state != TCP_ESTABLISHED)
543 return -EDESTADDRREQ;
//接下来两句赋值语句,隐藏内部数据的写法,C的指针擅长描述这种结构,C语言的不带方法的类继承实现。这个函数是附带在sock结构后面的内部数据,inet_sk(sk)负责起出这个数据,只在内部使用
544 daddr = inet->daddr;
545 dport = inet->dport;
546 /* Open fast path for connected socket.
547 Route will not be used, if at least one option is set.
548 */
549 connected = 1;
550 }
//接下来的一句不明白,求指点
551 ipc.addr = inet->saddr;
//ipc.oif设置为socket bind的interface
553 ipc.oif = sk->sk_bound_dev_if;
//如果是控制报文,则调用ip_cmsg_send函数处理报文。从函数名字上看,好像是要发送cmsg,然而实际上却没有任何数据发送。
554 if (msg->msg_controllen) {
555 err = ip_cmsg_send(msg, &ipc);
556 if (err)
557 return err;
//下面不明白,有空再想
558 if (ipc.opt)
559 free = 1;
560 connected = 0;
561 }
562 if (!ipc.opt)
563 ipc.opt = inet->opt;
564
565 saddr = ipc.addr;
566 ipc.addr = faddr = daddr;
567
568 if (ipc.opt && ipc.opt->srr) {
569 if (!daddr)
570 return -EINVAL;
571 faddr = ipc.opt->faddr;
572 connected = 0;
573 }
574 tos = RT_TOS(inet->tos);
//确定是否需要路由信息
575 if (sk->sk_localroute || (msg->msg_flags & MSG_DONTROUTE) ||
576 (ipc.opt && ipc.opt->is_strictroute)) {
577 tos |= RTO_ONLINK;
578 connected = 0;
579 }
580
581 if (MULTICAST(daddr)) {
582 if (!ipc.oif)
583 ipc.oif = inet->mc_index;
584 if (!saddr)
585 saddr = inet->mc_addr;
586 connected = 0;
587 }
//如果已经建立了套接字连接,则不需要重新查询路由
//直接从套接字的管理结构中返回路由表信息 ,并记录到rt 中
589 if (connected)
590 rt = (struct rtable*)sk_dst_check(sk, 0);
//如果rt为空,即无路由信息,则先用struct flowi结构记录查找路由表的索引信息,再调用 ip_route_output_flow函数查询路由表,得到路由信息。
592 if (rt == NULL) {
593 struct flowi fl = { .oif = ipc.oif,
594 .nl_u = { .ip4_u =
595 { .daddr = faddr,
596 .saddr = saddr,
597 .tos = tos } },
598 .proto = IPPROTO_UDP,
599 .uli_u = { .ports =
600 { .sport = inet->sport,
601 .dport = dport } } };
602 err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
603 if (err)
604 goto out;
605
606 err = -EACCES;
607 if ((rt->rt_flags & RTCF_BROADCAST) &&
608 !sock_flag(sk, SOCK_BROADCAST))
609 goto out;
610 if (connected)
611 sk_dst_set(sk, dst_clone(&rt->u.dst));
612 }
613
614 if (msg->msg_flags&MSG_CONFIRM)
615 goto do_confirm;
616 back_from_confirm:
617
618 saddr = rt->rt_src;
619 if (!ipc.addr)
620 daddr = ipc.addr = rt->rt_dst;
621
622 lock_sock(sk);
623 if (unlikely(up->pending)) {
624 /* The socket is already corked while preparing it. */
625 /* ... which is an evident application bug. --ANK */
626 release_sock(sk);
627
628 NETDEBUG(if (net_ratelimit()) printk(KERN_DEBUG "udp cork app bug 2
\n"));
629 err = -EINVAL;
630 goto out;
631 }
632 /*
633 * Now cork the socket to pend data.
634 */
635 inet->cork.fl.fl4_dst = daddr;
636 inet->cork.fl.fl_ip_dport = dport;
637 inet->cork.fl.fl4_src = saddr;
638 inet->cork.fl.fl_ip_sport = inet->sport;
639 up->pending = AF_INET;
640
641 do_append_data:
642 up->len += ulen;
//对UDP数据包进行分片处理,为IP层分片处理做好准备
643 err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
644 sizeof(struct udphdr), &ipc, rt,
645 corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
646 if (err)
647 udp_flush_pending_frames(sk);
//上层应用指定flags为MSG_MORE时,corkreq为1
648 else if (!corkreq)
649 err = udp_push_pending_frames(sk, up);
650 release_sock(sk);
651
652 out:
653 ip_rt_put(rt);
654 if (free)
655 kfree(ipc.opt);
656 if (!err) {
657 UDP_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
658 return len;
659 }
660 return err;
661
662 do_confirm:
663 dst_confirm(&rt->u.dst);
664 if (!(msg->msg_flags&MSG_PROBE) || len)
665 goto back_from_confirm;
666 err = 0;
667 goto out;
668 }
669