📄 udp.c
字号:
/* * Get and verify the address. */ if (msg->msg_name) { struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name; if (msg->msg_namelen < sizeof(*usin)) return(-EINVAL); if (usin->sin_family != AF_INET) { static int complained; if (!complained++) printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm); if (usin->sin_family) return -EINVAL; } ufh.daddr = usin->sin_addr.s_addr; ufh.uh.dest = usin->sin_port; if (ufh.uh.dest == 0) return -EINVAL; } else { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; ufh.daddr = sk->daddr; ufh.uh.dest = sk->dport; /* Open fast path for connected socket. Route will not be used, if at least one option is set. */ connected = 1; }#ifdef CONFIG_IP_TRANSPARENT_PROXY if (msg->msg_flags&MSG_PROXY) { /* * We map the first 8 bytes of a second sockaddr_in * into the last 8 (unused) bytes of a sockaddr_in. */ struct sockaddr_in *from = (struct sockaddr_in *)msg->msg_name; from = (struct sockaddr_in *)&from->sin_zero; if (from->sin_family != AF_INET) return -EINVAL; ipc.addr = from->sin_addr.s_addr; ufh.uh.source = from->sin_port; if (ipc.addr == 0) ipc.addr = sk->saddr; connected = 0; } else#endif { ipc.addr = sk->saddr; ufh.uh.source = sk->sport; } ipc.opt = NULL; ipc.oif = sk->bound_dev_if; 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 = sk->opt; ufh.saddr = ipc.addr; ipc.addr = daddr = ufh.daddr; if (ipc.opt && ipc.opt->srr) { if (!daddr) return -EINVAL; daddr = ipc.opt->faddr; connected = 0; } tos = RT_TOS(sk->ip_tos); if (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 = sk->ip_mc_index; if (!ufh.saddr) ufh.saddr = sk->ip_mc_addr; connected = 0; } if (connected && sk->dst_cache) { rt = (struct rtable*)sk->dst_cache; if (rt->u.dst.obsolete) { sk->dst_cache = NULL; dst_release(&rt->u.dst); rt = NULL; } else dst_clone(&rt->u.dst); } if (rt == NULL) { err = ip_route_output(&rt, daddr, ufh.saddr,#ifdef CONFIG_IP_TRANSPARENT_PROXY (msg->msg_flags&MSG_PROXY ? RTO_TPROXY : 0) |#endif tos, ipc.oif); if (err) goto out; err = -EACCES; if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) goto out; if (connected && sk->dst_cache == NULL) sk->dst_cache = dst_clone(&rt->u.dst); } ufh.saddr = rt->rt_src; if (!ipc.addr) ufh.daddr = ipc.addr = rt->rt_dst; ufh.uh.len = htons(ulen); ufh.uh.check = 0; ufh.iov = msg->msg_iov; ufh.wcheck = 0; /* RFC1122: OK. Provides the checksumming facility (MUST) as per */ /* 4.1.3.4. It's configurable by the application via setsockopt() */ /* (MAY) and it defaults to on (MUST). */ err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag, &ufh, ulen, &ipc, rt, msg->msg_flags);out: ip_rt_put(rt); if (free) kfree(ipc.opt); if (!err) { udp_statistics.UdpOutDatagrams++; return len; } return err;}#ifdef _HURD_#define udp_ioctl 0#else/* * IOCTL requests applicable to the UDP protocol */int udp_ioctl(struct sock *sk, int cmd, unsigned long arg){ switch(cmd) { case TIOCOUTQ: { unsigned long amount; amount = sock_wspace(sk); return put_user(amount, (int *)arg); } case TIOCINQ: { struct sk_buff *skb; unsigned long amount; amount = 0; /* N.B. Is this interrupt safe?? -> Yes. Interrupts do not remove skbs. --ANK (980725) */ skb = skb_peek(&sk->receive_queue); if (skb != NULL) { /* * We will only return the amount * of this packet since that is all * that will be read. */ amount = skb->len - sizeof(struct udphdr); } return put_user(amount, (int *)arg); } default: return(-ENOIOCTLCMD); } return(0);}#endif#ifndef HAVE_CSUM_COPY_USER#undef CONFIG_UDP_DELAY_CSUM#endif/* * This should be easy, if there is something there we * return it, otherwise we block. */int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len){ struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; struct sk_buff *skb; int copied, err; if (flags & MSG_ERRQUEUE) return ip_recv_error(sk, msg, len); /* * From here the generic datagram does a lot of the work. Come * the finished NET3, it will do _ALL_ the work! */ skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; copied = skb->len - sizeof(struct udphdr); if (copied > len) { copied = len; msg->msg_flags |= MSG_TRUNC; }#ifndef CONFIG_UDP_DELAY_CSUM err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied);#else if (skb->ip_summed==CHECKSUM_UNNECESSARY) { err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) { if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) goto csum_copy_err; err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); } else { unsigned int csum; err = 0; csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum); csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err); if (err) goto out_free; if ((unsigned short)csum_fold(csum)) goto csum_copy_err; }#endif if (err) goto out_free; sk->stamp=skb->stamp; /* Copy the address. */ if (sin) { /* * Check any passed addresses */ if (addr_len) *addr_len=sizeof(*sin); sin->sin_family = AF_INET; sin->sin_port = skb->h.uh->source; sin->sin_addr.s_addr = skb->nh.iph->saddr;#ifdef CONFIG_IP_TRANSPARENT_PROXY if (flags&MSG_PROXY) { /* * We map the first 8 bytes of a second sockaddr_in * into the last 8 (unused) bytes of a sockaddr_in. * This _is_ ugly, but it's the only way to do it * easily, without adding system calls. */ struct sockaddr_in *sinto = (struct sockaddr_in *) sin->sin_zero; sinto->sin_family = AF_INET; sinto->sin_port = skb->h.uh->dest; sinto->sin_addr.s_addr = skb->nh.iph->daddr; }#endif } if (sk->ip_cmsg_flags) ip_cmsg_recv(msg, skb); err = copied;out_free: skb_free_datagram(sk, skb);out: return err;#ifdef CONFIG_UDP_DELAY_CSUMcsum_copy_err: udp_statistics.UdpInErrors++; skb_free_datagram(sk, skb); /* * Error for blocking case is chosen to masquerade * as some normal condition. */ return (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;#endif}int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len){ struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; struct rtable *rt; int err; if (addr_len < sizeof(*usin)) return(-EINVAL); /* * 1003.1g - break association. */ if (usin->sin_family==AF_UNSPEC) { sk->saddr=INADDR_ANY; sk->rcv_saddr=INADDR_ANY; sk->daddr=INADDR_ANY; sk->state = TCP_CLOSE; if(uh_cache_sk == sk) uh_cache_sk = NULL; return 0; } if (usin->sin_family && usin->sin_family != AF_INET) return(-EAFNOSUPPORT); dst_release(xchg(&sk->dst_cache, NULL)); err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr, sk->ip_tos|sk->localroute, sk->bound_dev_if); if (err) return err; if ((rt->rt_flags&RTCF_BROADCAST) && !sk->broadcast) { ip_rt_put(rt); return -EACCES; } if(!sk->saddr) sk->saddr = rt->rt_src; /* Update source address */ if(!sk->rcv_saddr) sk->rcv_saddr = rt->rt_src; sk->daddr = rt->rt_dst; sk->dport = usin->sin_port; sk->state = TCP_ESTABLISHED; if(uh_cache_sk == sk) uh_cache_sk = NULL; sk->dst_cache = &rt->u.dst; return(0);}static void udp_close(struct sock *sk, long timeout){ /* See for explanation: raw_close in ipv4/raw.c */ sk->state = TCP_CLOSE; udp_v4_unhash(sk); sk->dead = 1; destroy_sock(sk);}static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb){ /* * Charge it to the socket, dropping if the queue is full. */#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM) if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) { if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) { udp_statistics.UdpInErrors++; ip_statistics.IpInDiscards++; ip_statistics.IpInDelivers--; kfree_skb(skb); return -1; } skb->ip_summed = CHECKSUM_UNNECESSARY; }#endif if (sock_queue_rcv_skb(sk,skb)<0) { udp_statistics.UdpInErrors++; ip_statistics.IpInDiscards++; ip_statistics.IpInDelivers--; kfree_skb(skb); return -1; } udp_statistics.UdpInDatagrams++; return 0;}static inline void udp_deliver(struct sock *sk, struct sk_buff *skb){ udp_queue_rcv_skb(sk, skb);}/* * Multicasts and broadcasts go to each listener. * * Note: called only from the BH handler context, * so we don't need to lock the hashes. */static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, u32 saddr, u32 daddr){ struct sock *sk; int dif; sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]; dif = skb->dev->ifindex; sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, dif); if (sk) { struct sock *sknext = NULL; do { struct sk_buff *skb1 = skb; sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr, uh->source, daddr, dif); if(sknext) skb1 = skb_clone(skb, GFP_ATOMIC); if(skb1) udp_deliver(sk, skb1); sk = sknext; } while(sknext); } else kfree_skb(skb); return 0;}#ifdef CONFIG_IP_TRANSPARENT_PROXY/* * Check whether a received UDP packet might be for one of our * sockets. */int udp_chkaddr(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4); struct sock *sk; sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev->ifindex); if (!sk) return 0; /* 0 means accept all LOCAL addresses here, not all the world... */ if (sk->rcv_saddr == 0) return 0; return 1;}#endif/* * All we need to do is get the socket, and then do a checksum. */int udp_rcv(struct sk_buff *skb, unsigned short len){ struct sock *sk; struct udphdr *uh; unsigned short ulen; struct rtable *rt = (struct rtable*)skb->dst; u32 saddr = skb->nh.iph->saddr; u32 daddr = skb->nh.iph->daddr; /* * First time through the loop.. Do all the setup stuff * (including finding out the socket we go to etc) */ /* * Get the header. */ uh = skb->h.uh; __skb_pull(skb, skb->h.raw - skb->data); ip_statistics.IpInDelivers++; /* * Validate the packet and the UDP length. */ ulen = ntohs(uh->len); if (ulen > len || ulen < sizeof(*uh)) { NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len)); udp_statistics.UdpInErrors++; kfree_skb(skb); return(0); } skb_trim(skb, ulen);#ifndef CONFIG_UDP_DELAY_CSUM if (uh->check && (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) || ((skb->ip_summed==CHECKSUM_NONE) && (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) goto csum_error;#else if (uh->check==0) skb->ip_summed = CHECKSUM_UNNECESSARY; else if (skb->ip_summed==CHECKSUM_HW) { if (udp_check(uh,ulen,saddr,daddr,skb->csum)) goto csum_error; skb->ip_summed = CHECKSUM_UNNECESSARY; } else if (skb->ip_summed != CHECKSUM_UNNECESSARY) skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);#endif if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) return udp_v4_mcast_deliver(skb, uh, saddr, daddr);#ifdef CONFIG_IP_TRANSPARENT_PROXY if (IPCB(skb)->redirport) sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source, daddr, skb->dev, IPCB(skb)->redirport, skb->dev->ifindex); else#endif sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex); if (sk == NULL) {#ifdef CONFIG_UDP_DELAY_CSUM if (skb->ip_summed != CHECKSUM_UNNECESSARY && (unsigned short)csum_fold(csum_partial((char*)uh, ulen, skb->csum))) goto csum_error;#endif udp_statistics.UdpNoPorts++; icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); /* * Hmm. We got an UDP broadcast to a port to which we * don't wanna listen. Ignore it. */ kfree_skb(skb); return(0); } udp_deliver(sk, skb); return 0;csum_error: /* * RFC1122: OK. Discards the bad packet silently (as far as * the network is concerned, anyway) as per 4.1.3.4 (MUST). */ NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n", NIPQUAD(saddr), ntohs(uh->source), NIPQUAD(daddr), ntohs(uh->dest), ulen)); udp_statistics.UdpInErrors++; kfree_skb(skb); return(0);}struct proto udp_prot = { (struct sock *)&udp_prot, /* sklist_next */ (struct sock *)&udp_prot, /* sklist_prev */ udp_close, /* close */ udp_connect, /* connect */ NULL, /* accept */ NULL, /* retransmit */ NULL, /* write_wakeup */ NULL, /* read_wakeup */ datagram_poll, /* poll */ udp_ioctl, /* ioctl */ NULL, /* init */ NULL, /* destroy */ NULL, /* shutdown */ ip_setsockopt, /* setsockopt */ ip_getsockopt, /* getsockopt */ udp_sendmsg, /* sendmsg */ udp_recvmsg, /* recvmsg */ NULL, /* bind */ udp_queue_rcv_skb, /* backlog_rcv */ udp_v4_hash, /* hash */ udp_v4_unhash, /* unhash */ udp_v4_get_port, /* good_socknum */ 128, /* max_header */ 0, /* retransmits */ "UDP", /* name */ 0, /* inuse */ 0 /* highestinuse */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -