📄 udp.c
字号:
skb->len, proto, 0); /* Probably, we should checksum udp header (it should be in cache * in any case) and data in tiny packets (< rx copybreak). */ return 0;}/* * All we need to do is get the socket, and then do a checksum. */int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], int proto){ struct sock *sk; struct udphdr *uh = udp_hdr(skb); unsigned short ulen; struct rtable *rt = (struct rtable*)skb->dst; __be32 saddr = ip_hdr(skb)->saddr; __be32 daddr = ip_hdr(skb)->daddr; /* * Validate the packet. */ if (!pskb_may_pull(skb, sizeof(struct udphdr))) goto drop; /* No space for header. */ ulen = ntohs(uh->len); if (ulen > skb->len) goto short_packet; if (proto == IPPROTO_UDP) { /* UDP validates ulen. */ if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) goto short_packet; uh = udp_hdr(skb); } if (udp4_csum_init(skb, uh, proto)) goto csum_error; if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); sk = __udp4_lib_lookup(saddr, uh->source, daddr, uh->dest, inet_iif(skb), udptable); if (sk != NULL) { int ret = udp_queue_rcv_skb(sk, skb); sock_put(sk); /* a return value > 0 means to resubmit the input, but * it wants the return to be -protocol, or 0 */ if (ret > 0) return -ret; return 0; } if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; nf_reset(skb); /* No socket. Drop packet silently, if checksum is wrong */ if (udp_lib_checksum_complete(skb)) goto csum_error; UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); /* * Hmm. We got an UDP packet to a port to which we * don't wanna listen. Ignore it. */ kfree_skb(skb); return 0;short_packet: LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n", proto == IPPROTO_UDPLITE ? "-Lite" : "", NIPQUAD(saddr), ntohs(uh->source), ulen, skb->len, NIPQUAD(daddr), ntohs(uh->dest)); goto drop;csum_error: /* * RFC1122: OK. Discards the bad packet silently (as far as * the network is concerned, anyway) as per 4.1.3.4 (MUST). */ LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n", proto == IPPROTO_UDPLITE ? "-Lite" : "", NIPQUAD(saddr), ntohs(uh->source), NIPQUAD(daddr), ntohs(uh->dest), ulen);drop: UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); kfree_skb(skb); return 0;}int udp_rcv(struct sk_buff *skb){ return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);}int udp_destroy_sock(struct sock *sk){ lock_sock(sk); udp_flush_pending_frames(sk); release_sock(sk); return 0;}/* * Socket option code for UDP */int udp_lib_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen, int (*push_pending_frames)(struct sock *)){ struct udp_sock *up = udp_sk(sk); int val; int err = 0; if (optlen<sizeof(int)) return -EINVAL; if (get_user(val, (int __user *)optval)) return -EFAULT; switch (optname) { case UDP_CORK: if (val != 0) { up->corkflag = 1; } else { up->corkflag = 0; lock_sock(sk); (*push_pending_frames)(sk); release_sock(sk); } break; case UDP_ENCAP: switch (val) { case 0: case UDP_ENCAP_ESPINUDP: case UDP_ENCAP_ESPINUDP_NON_IKE: up->encap_rcv = xfrm4_udp_encap_rcv; /* FALLTHROUGH */ case UDP_ENCAP_L2TPINUDP: up->encap_type = val; break; default: err = -ENOPROTOOPT; break; } break; /* * UDP-Lite's partial checksum coverage (RFC 3828). */ /* The sender sets actual checksum coverage length via this option. * The case coverage > packet length is handled by send module. */ case UDPLITE_SEND_CSCOV: if (!up->pcflag) /* Disable the option on UDP sockets */ return -ENOPROTOOPT; if (val != 0 && val < 8) /* Illegal coverage: use default (8) */ val = 8; up->pcslen = val; up->pcflag |= UDPLITE_SEND_CC; break; /* The receiver specifies a minimum checksum coverage value. To make * sense, this should be set to at least 8 (as done below). If zero is * used, this again means full checksum coverage. */ case UDPLITE_RECV_CSCOV: if (!up->pcflag) /* Disable the option on UDP sockets */ return -ENOPROTOOPT; if (val != 0 && val < 8) /* Avoid silly minimal values. */ val = 8; up->pcrlen = val; up->pcflag |= UDPLITE_RECV_CC; break; default: err = -ENOPROTOOPT; break; } return err;}int udp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen){ if (level == SOL_UDP || level == SOL_UDPLITE) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_push_pending_frames); return ip_setsockopt(sk, level, optname, optval, optlen);}#ifdef CONFIG_COMPATint compat_udp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen){ if (level == SOL_UDP || level == SOL_UDPLITE) return udp_lib_setsockopt(sk, level, optname, optval, optlen, udp_push_pending_frames); return compat_ip_setsockopt(sk, level, optname, optval, optlen);}#endifint udp_lib_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen){ struct udp_sock *up = udp_sk(sk); int val, len; if (get_user(len,optlen)) return -EFAULT; len = min_t(unsigned int, len, sizeof(int)); if (len < 0) return -EINVAL; switch (optname) { case UDP_CORK: val = up->corkflag; break; case UDP_ENCAP: val = up->encap_type; break; /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: val = up->pcslen; break; case UDPLITE_RECV_CSCOV: val = up->pcrlen; break; default: return -ENOPROTOOPT; } if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val,len)) return -EFAULT; return 0;}int udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen){ if (level == SOL_UDP || level == SOL_UDPLITE) return udp_lib_getsockopt(sk, level, optname, optval, optlen); return ip_getsockopt(sk, level, optname, optval, optlen);}#ifdef CONFIG_COMPATint compat_udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen){ if (level == SOL_UDP || level == SOL_UDPLITE) return udp_lib_getsockopt(sk, level, optname, optval, optlen); return compat_ip_getsockopt(sk, level, optname, optval, optlen);}#endif/** * udp_poll - wait for a UDP event. * @file - file struct * @sock - socket * @wait - poll table * * This is same as datagram poll, except for the special case of * blocking sockets. If application is using a blocking fd * and a packet with checksum error is in the queue; * then it could get return from select indicating data available * but then block when reading it. Add special case code * to work around these arguably broken applications. */unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait){ unsigned int mask = datagram_poll(file, sock, wait); struct sock *sk = sock->sk; int is_lite = IS_UDPLITE(sk); /* Check for false positives due to checksum errors */ if ( (mask & POLLRDNORM) && !(file->f_flags & O_NONBLOCK) && !(sk->sk_shutdown & RCV_SHUTDOWN)){ struct sk_buff_head *rcvq = &sk->sk_receive_queue; struct sk_buff *skb; spin_lock_bh(&rcvq->lock); while ((skb = skb_peek(rcvq)) != NULL && udp_lib_checksum_complete(skb)) { UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_lite); __skb_unlink(skb, rcvq); kfree_skb(skb); } spin_unlock_bh(&rcvq->lock); /* nothing to see, move along */ if (skb == NULL) mask &= ~(POLLIN | POLLRDNORM); } return mask;}DEFINE_PROTO_INUSE(udp)struct proto udp_prot = { .name = "UDP", .owner = THIS_MODULE, .close = udp_lib_close, .connect = ip4_datagram_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, .destroy = udp_destroy_sock, .setsockopt = udp_setsockopt, .getsockopt = udp_getsockopt, .sendmsg = udp_sendmsg, .recvmsg = udp_recvmsg, .sendpage = udp_sendpage, .backlog_rcv = udp_queue_rcv_skb, .hash = udp_lib_hash, .unhash = udp_lib_unhash, .get_port = udp_v4_get_port, .obj_size = sizeof(struct udp_sock),#ifdef CONFIG_COMPAT .compat_setsockopt = compat_udp_setsockopt, .compat_getsockopt = compat_udp_getsockopt,#endif REF_PROTO_INUSE(udp)};/* ------------------------------------------------------------------------ */#ifdef CONFIG_PROC_FSstatic struct sock *udp_get_first(struct seq_file *seq){ struct sock *sk; struct udp_iter_state *state = seq->private; for (state->bucket = 0; state->bucket < UDP_HTABLE_SIZE; ++state->bucket) { struct hlist_node *node; sk_for_each(sk, node, state->hashtable + state->bucket) { if (sk->sk_family == state->family) goto found; } } sk = NULL;found: return sk;}static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk){ struct udp_iter_state *state = seq->private; do { sk = sk_next(sk);try_again: ; } while (sk && sk->sk_family != state->family); if (!sk && ++state->bucket < UDP_HTABLE_SIZE) { sk = sk_head(state->hashtable + state->bucket); goto try_again; } return sk;}static struct sock *udp_get_idx(struct seq_file *seq, loff_t pos){ struct sock *sk = udp_get_first(seq); if (sk) while (pos && (sk = udp_get_next(seq, sk)) != NULL) --pos; return pos ? NULL : sk;}static void *udp_seq_start(struct seq_file *seq, loff_t *pos){ read_lock(&udp_hash_lock); return *pos ? udp_get_idx(seq, *pos-1) : (void *)1;}static void *udp_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct sock *sk; if (v == (void *)1) sk = udp_get_idx(seq, 0); else sk = udp_get_next(seq, v); ++*pos; return sk;}static void udp_seq_stop(struct seq_file *seq, void *v){ read_unlock(&udp_hash_lock);}static int udp_seq_open(struct inode *inode, struct file *file){ struct udp_seq_afinfo *afinfo = PDE(inode)->data; struct seq_file *seq; int rc = -ENOMEM; struct udp_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) goto out; s->family = afinfo->family; s->hashtable = afinfo->hashtable; s->seq_ops.start = udp_seq_start; s->seq_ops.next = udp_seq_next; s->seq_ops.show = afinfo->seq_show; s->seq_ops.stop = udp_seq_stop; rc = seq_open(file, &s->seq_ops); if (rc) goto out_kfree; seq = file->private_data; seq->private = s;out: return rc;out_kfree: kfree(s); goto out;}/* ------------------------------------------------------------------------ */int udp_proc_register(struct udp_seq_afinfo *afinfo){ struct proc_dir_entry *p; int rc = 0; if (!afinfo) return -EINVAL; afinfo->seq_fops->owner = afinfo->owner; afinfo->seq_fops->open = udp_seq_open; afinfo->seq_fops->read = seq_read; afinfo->seq_fops->llseek = seq_lseek; afinfo->seq_fops->release = seq_release_private; p = proc_net_fops_create(&init_net, afinfo->name, S_IRUGO, afinfo->seq_fops); if (p) p->data = afinfo; else rc = -ENOMEM; return rc;}void udp_proc_unregister(struct udp_seq_afinfo *afinfo){ if (!afinfo) return; proc_net_remove(&init_net, afinfo->name); memset(afinfo->seq_fops, 0, sizeof(*afinfo->seq_fops));}/* ------------------------------------------------------------------------ */static void udp4_format_sock(struct sock *sp, char *tmpbuf, int bucket){ struct inet_sock *inet = inet_sk(sp); __be32 dest = inet->daddr; __be32 src = inet->rcv_saddr; __u16 destp = ntohs(inet->dport); __u16 srcp = ntohs(inet->sport); sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p", bucket, src, srcp, dest, destp, sp->sk_state, atomic_read(&sp->sk_wmem_alloc), atomic_read(&sp->sk_rmem_alloc), 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp);}int udp4_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) seq_printf(seq, "%-127s\n", " sl local_address rem_address st tx_queue " "rx_queue tr tm->when retrnsmt uid timeout " "inode"); else { char tmpbuf[129]; struct udp_iter_state *state = seq->private; udp4_format_sock(v, tmpbuf, state->bucket); seq_printf(seq, "%-127s\n", tmpbuf); } return 0;}/* ------------------------------------------------------------------------ */static struct file_operations udp4_seq_fops;static struct udp_seq_afinfo udp4_seq_afinfo = { .owner = THIS_MODULE, .name = "udp", .family = AF_INET, .hashtable = udp_hash, .seq_show = udp4_seq_show, .seq_fops = &udp4_seq_fops,};int __init udp4_proc_init(void){ return udp_proc_register(&udp4_seq_afinfo);}void udp4_proc_exit(void){ udp_proc_unregister(&udp4_seq_afinfo);}#endif /* CONFIG_PROC_FS */EXPORT_SYMBOL(udp_disconnect);EXPORT_SYMBOL(udp_hash);EXPORT_SYMBOL(udp_hash_lock);EXPORT_SYMBOL(udp_ioctl);EXPORT_SYMBOL(udp_get_port);EXPORT_SYMBOL(udp_prot);EXPORT_SYMBOL(udp_sendmsg);EXPORT_SYMBOL(udp_lib_getsockopt);EXPORT_SYMBOL(udp_lib_setsockopt);EXPORT_SYMBOL(udp_poll);#ifdef CONFIG_PROC_FSEXPORT_SYMBOL(udp_proc_register);EXPORT_SYMBOL(udp_proc_unregister);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -