BSD协议栈:UDP输入
UDP输入
输入函数的目标是把UDP数据报放到合适的插口缓存内,然后唤醒该插口上因输入阻塞的所有进程或线程(唤醒需要依靠操作系统的IPC机制)。
分为三个步骤:
1.确认消息并简单处理输入的数据
2.处理目的地址是单播类型的数据报:提交给单个socket即可
3.处理目的地址是广播或多播类型的数据报:需要找到所有需要提交的socket
处理输入的代码如下:
代码主要是验证数据报长度,有两个参数:ip_len 与 uh_ulen,这两个参数都表示数据报长度,正常情况下,它们应该是相等的。
正常情况:
uh_ulen:UDP首部加UDP数据长度
ip_len:数据报内容长度
但我们都知道网络结构是分层的,下一层对上一层来说就是内容,所以有:
ip_len 大于 uh_ulen:代码相信小的那个,也就是uh_ulen,此时调用m_adj丢弃mbuf后面多出来的部分,在校验和检验时会丢弃该数据报。
ip_len 小于 uh_ulen:长度出现严重错误,数据报必须立即被丢弃。
后面就是填写字段并计算校验和,在前文讲过了,就不过多赘述了。
void udp_input(m, iphlen)
register struct mbuf *m;
int iphlen;
{
register struct ip *ip;
register struct udphdr *uh;
register struct inpcb *inp;
struct mbuf *opts = 0;
int len;
struct ip save_ip;
udpstat.udps_ipackets++;
//此时还没有实现备份IP选项,因此需要丢弃
if (iphlen > sizeof (struct ip)) {
ip_stripoptions(m, (struct mbuf *)0);
iphlen = sizeof(struct ip);
}
//如果IP/UDP长度不合理,那么重新安排mbuf链,使第一个mbuf至少有28个字节
ip = mtod(m, struct ip *);
if (m->m_len < iphlen + sizeof(struct udphdr)) {
if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
udpstat.udps_hdrops++;//状态标志位,这些代码不影响理解
return;
}
ip = mtod(m, struct ip *);
}
uh = (struct udphdr *)((caddr_t)ip + iphlen);
/*
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
len = ntohs((u_short)uh->uh_ulen);
if (ip->ip_len != len) {
if (len > ip->ip_len) {
udpstat.udps_badlen++;
goto bad;
}
m_adj(m, len - ip->ip_len);
/* ip->ip_len = len; */
}
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip = *ip;
/*
* Checksum extended UDP header and data.
*/
if (udpcksum && uh->uh_sum) {
((struct ipovly *)ip)->ih_next = 0;
((struct ipovly *)ip)->ih_prev = 0;
((struct ipovly *)ip)->ih_x1 = 0;
((struct ipovly *)ip)->ih_len = uh->uh_ulen;
if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
udpstat.udps_badsum++;
m_freem(m);
return;
}
}
后面的程序依次是:
分用多播和广播数据报
分用单播数据报
生成ICMP端口不可达差错