int udp_chkaddr(struct sk_buff *skb) { struct iphdr *iph = skb->h.iph; struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4); struct sock *sk; sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev); if (!sk) return 0; /* 0 means accept all LOCAL addresses here, not all the world... */ if (sk->rcv_saddr == 0) return 0; return 1; }
int icmp_chkaddr(struct sk_buff *skb) { struct icmphdr *icmph=(struct icmphdr *)(skb->h.raw + skb->h.iph->ihl*4); struct iphdr *iph = (struct iphdr *) (icmph + 1); void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len) = icmp_pointers[icmph->type].handler; if (handler == icmp_unreach || handler == icmp_redirect) { struct sock *sk; switch (iph->protocol) { case IPPROTO_TCP: { struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr) return 0; if (sk->daddr != iph->daddr) return 0; if (sk->dummy_th.dest != th->dest) return 0; /* * This packet came from us. */ return 1; } case IPPROTO_UDP: { struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR) return 0; /* * This packet may have come from us. * Assume it did. */ return 1; } } } return 0; }
int icmp_chkaddr(struct sk_buff *skb) { struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4); struct iphdr *iph = (struct iphdr *) (icmph + 1); void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len) = icmp_pointers[icmph->type].handler; if (handler == icmp_unreach || handler == icmp_redirect) { struct sock *sk; switch (iph->protocol) { case IPPROTO_TCP: { struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex); if (!sk || (sk->state == TCP_LISTEN)) return 0; /* * This packet came from us. */ return 1; } case IPPROTO_UDP: { struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex); if (!sk) return 0; if (sk->saddr != iph->saddr && inet_addr_type(iph->saddr) != RTN_LOCAL) return 0; /* * This packet may have come from us. * Assume it did. */ return 1; } } } return 0; }
int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, __u32 daddr, unsigned short len, __u32 saddr, int redo, struct inet_protocol *protocol) { struct sock *sk; struct udphdr *uh; unsigned short ulen; int addr_type; /* * If we're doing a "redo" (the socket was busy last time * around), we can just queue the packet now.. */ if (redo) { udp_queue_rcv_skb(skb->sk, skb); return 0; } /* * First time through the loop.. Do all the setup stuff * (including finding out the socket we go to etc) */ addr_type = IS_MYADDR; if(!dev || dev->pa_addr!=daddr) addr_type=ip_chk_addr(daddr); /* * Get the header. */ uh = (struct udphdr *) skb->h.uh; ip_statistics.IpInDelivers++; /* * Validate the packet and the UDP length. */ ulen = ntohs(uh->len); if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) { NETDEBUG(printk("UDP: short packet: %d/%d\n", ulen, len)); udp_statistics.UdpInErrors++; kfree_skb(skb, FREE_WRITE); return(0); } /* RFC1122 warning: According to 4.1.3.6, we MUST discard any */ /* datagram which has an invalid source address, either here or */ /* in IP. */ /* Right now, IP isn't doing it, and neither is UDP. It's on the */ /* FIXME list for IP, though, so I wouldn't worry about it. */ /* (That's the Right Place to do it, IMHO.) -- MS */ if (uh->check && ( ( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) || ( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0))) /* skip if CHECKSUM_UNNECESSARY */ ) ) { /* <*****@*****.**> wants to know, who sent it, to go and stomp on the garbage sender... */ /* RFC1122: OK. Discards the bad packet silently (as far as */ /* the network is concerned, anyway) as per 4.1.3.4 (MUST). */ NETDEBUG(printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n", ntohl(saddr),ntohs(uh->source), ntohl(daddr),ntohs(uh->dest), ulen)); udp_statistics.UdpInErrors++; kfree_skb(skb, FREE_WRITE); return(0); } /* * These are supposed to be switched. */ skb->daddr = saddr; skb->saddr = daddr; len=ulen; skb->dev = dev; skb_trim(skb,len); #ifdef CONFIG_IP_MULTICAST if (addr_type==IS_BROADCAST || addr_type==IS_MULTICAST) return udp_v4_mcast_deliver(skb, uh, saddr, daddr); #endif #ifdef CONFIG_IP_TRANSPARENT_PROXY if(skb->redirport) sk = udp_v4_proxy_lookup(saddr, uh->source, daddr, uh->dest, dev->pa_addr, skb->redirport, dev); else #endif sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev); if (sk == NULL) { udp_statistics.UdpNoPorts++; if (addr_type != IS_BROADCAST && addr_type != IS_MULTICAST) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); } /* * Hmm. We got an UDP broadcast to a port to which we * don't wanna listen. Ignore it. */ skb->sk = NULL; kfree_skb(skb, FREE_WRITE); return(0); } udp_deliver(sk, skb); return 0; }
/* Shared by v4/v6 tcp. */ unsigned short udp_good_socknum(void) { int result; static int start = 0; int i, best, best_size_so_far; SOCKHASH_LOCK(); /* Select initial not-so-random "best" */ best = PROT_SOCK + 1 + (start & 1023); best_size_so_far = 32767; /* "big" num */ result = best; for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { struct sock *sk; int size; sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; /* No clashes - take it */ if (!sk) goto out; /* Is this one better than our best so far? */ size = 0; do { if(++size >= best_size_so_far) goto next; } while((sk = sk->next) != NULL); best_size_so_far = size; best = result; next: } while (udp_lport_inuse(best)) best += UDP_HTABLE_SIZE; result = best; out: start = result; SOCKHASH_UNLOCK(); return result; } static void udp_v4_hash(struct sock *sk) { struct sock **skp; int num = sk->num; num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; SOCKHASH_LOCK(); sk->next = *skp; *skp = sk; sk->hashent = num; SOCKHASH_UNLOCK(); } static void udp_v4_unhash(struct sock *sk) { struct sock **skp; int num = sk->num; num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; SOCKHASH_LOCK(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; break; } skp = &((*skp)->next); } SOCKHASH_UNLOCK(); } static void udp_v4_rehash(struct sock *sk) { struct sock **skp; int num = sk->num; int oldnum = sk->hashent; num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[oldnum]; SOCKHASH_LOCK(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; break; } skp = &((*skp)->next); } sk->next = udp_hash[num]; udp_hash[num] = sk; sk->hashent = num; SOCKHASH_UNLOCK(); } /* UDP is nearly always wildcards out the wazoo, it makes no sense to try * harder than this. -DaveM */ __inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, struct device *dev) { struct sock *sk, *result = NULL; unsigned short hnum = ntohs(dport); int badness = -1; for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) { if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) { int score = 0; if(sk->rcv_saddr) { if(sk->rcv_saddr != daddr) continue; score++; } if(sk->daddr) { if(sk->daddr != saddr) continue; score++; } if(sk->dummy_th.dest) { if(sk->dummy_th.dest != sport) continue; score++; } /* If this socket is bound to a particular interface, * did the packet come in on it? */ if (sk->bound_device) { if (dev == sk->bound_device) score++; else continue; /* mismatch--not this sock */ } if(score == 4) { result = sk; break; } else if(score > badness) { result = sk; badness = score; } } } return result; } #ifdef CONFIG_IP_TRANSPARENT_PROXY struct sock *udp_v4_proxy_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, u32 paddr, u16 rport, struct device *dev) { struct sock *hh[3], *sk, *result = NULL; int i; int badness = -1; unsigned short hnum = ntohs(dport); unsigned short hpnum = ntohs(rport); SOCKHASH_LOCK(); hh[0] = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; hh[1] = udp_hash[hpnum & (UDP_HTABLE_SIZE - 1)]; for (i = 0; i < 2; i++) { for(sk = hh[i]; sk != NULL; sk = sk->next) { if(sk->num == hnum || sk->num == hpnum) { int score = 0; if(sk->dead && (sk->state == TCP_CLOSE)) continue; if(sk->rcv_saddr) { if((sk->num != hpnum || sk->rcv_saddr != paddr) && (sk->num != hnum || sk->rcv_saddr != daddr)) continue; score++; } if(sk->daddr) { if(sk->daddr != saddr) continue; score++; } if(sk->dummy_th.dest) { if(sk->dummy_th.dest != sport) continue; score++; } /* If this socket is bound to a particular interface, * did the packet come in on it? */ if(sk->bound_device) { if (sk->bound_device != dev) continue; score++; } if(score == 4 && sk->num == hnum) { result = sk; break; } else if(score > badness && (sk->num == hpnum || sk->rcv_saddr)) { result = sk; badness = score; } } } } SOCKHASH_UNLOCK(); return result; } #endif static inline struct sock *udp_v4_mcast_next(struct sock *sk, unsigned short num, unsigned long raddr, unsigned short rnum, unsigned long laddr, struct device *dev) { struct sock *s = sk; unsigned short hnum = ntohs(num); for(; s; s = s->next) { if ((s->num != hnum) || (s->dead && (s->state == TCP_CLOSE)) || (s->daddr && s->daddr!=raddr) || (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) || ((s->bound_device) && (s->bound_device!=dev)) || (s->rcv_saddr && s->rcv_saddr != laddr)) continue; break; } return s; } #define min(a,b) ((a)<(b)?(a):(b)) /* * This routine is called by the ICMP module when it gets some * sort of error condition. If err < 0 then the socket should * be closed and the error returned to the user. If err > 0 * it's just the icmp type << 8 | icmp code. * Header points to the ip header of the error packet. We move * on past this. Then (as it used to claim before adjustment) * header points to the first 8 bytes of the udp header. We need * to find the appropriate port. */ void udp_err(int type, int code, unsigned char *header, __u32 daddr, __u32 saddr, struct inet_protocol *protocol, int len) { struct udphdr *uh; struct sock *sk; /* * Find the 8 bytes of post IP header ICMP included for us */ if(len<sizeof(struct udphdr)) return; uh = (struct udphdr *)header; sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL); if (sk == NULL) return; /* No socket for error */ if (type == ICMP_SOURCE_QUENCH) { /* Slow down! */ if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2; return; } if (type == ICMP_PARAMETERPROB) { sk->err = EPROTO; sk->error_report(sk); return; } /* * Various people wanted BSD UDP semantics. Well they've come * back out because they slow down response to stuff like dead * or unreachable name servers and they screw term users something * chronic. Oh and it violates RFC1122. So basically fix your * client code people. */ /* RFC1122: OK. Passes ICMP errors back to application, as per */ /* 4.1.3.3. */ /* After the comment above, that should be no surprise. */ if(code<=NR_ICMP_UNREACH && icmp_err_convert[code].fatal) { /* * 4.x BSD compatibility item. Break RFC1122 to * get BSD socket semantics. */ if(sk->bsdism && sk->state!=TCP_ESTABLISHED) return; sk->err = icmp_err_convert[code].errno; sk->error_report(sk); } } static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, unsigned long base) { return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base)); } struct udpfakehdr { struct udphdr uh; __u32 daddr; __u32 other; const char *from; __u32 wcheck; }; /* * Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to * get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode * for direct user->board I/O transfers. That one will be fun. */ static void udp_getfrag(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) { struct udpfakehdr *ufh = (struct udpfakehdr *)p; const char *src; char *dst; unsigned int len; if (offset) { len = fraglen; src = ufh->from+(offset-sizeof(struct udphdr)); dst = to; } else { len = fraglen-sizeof(struct udphdr); src = ufh->from; dst = to+sizeof(struct udphdr); } ufh->wcheck = csum_partial_copy_fromuser(src, dst, len, ufh->wcheck); if (offset == 0) { ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr), ufh->wcheck); ufh->uh.check = csum_tcpudp_magic(saddr, ufh->daddr, ntohs(ufh->uh.len), IPPROTO_UDP, ufh->wcheck); if (ufh->uh.check == 0) ufh->uh.check = -1; memcpy(to, ufh, sizeof(struct udphdr)); } } /* * Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing * that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET * set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if * this is a valid decision. */ static void udp_getfrag_nosum(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) { struct udpfakehdr *ufh = (struct udpfakehdr *)p; const char *src; char *dst; unsigned int len; if (offset) { len = fraglen; src = ufh->from+(offset-sizeof(struct udphdr)); dst = to; } else { len = fraglen-sizeof(struct udphdr); src = ufh->from; dst = to+sizeof(struct udphdr); } memcpy_fromfs(dst,src,len); if (offset == 0) memcpy(to, ufh, sizeof(struct udphdr)); } /* * Send UDP frames. */ static int udp_send(struct sock *sk, struct sockaddr_in *sin, const unsigned char *from, int len, int rt, __u32 saddr, int noblock) { int ulen = len + sizeof(struct udphdr); int a; struct udpfakehdr ufh; if(ulen>65535-sizeof(struct iphdr)) return -EMSGSIZE; ufh.uh.source = sk->dummy_th.source; ufh.uh.dest = sin->sin_port; ufh.uh.len = htons(ulen); ufh.uh.check = 0; ufh.daddr = sin->sin_addr.s_addr; ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256; ufh.from = from; ufh.wcheck = 0; #ifdef CONFIG_IP_TRANSPARENT_PROXY if (rt&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 *sinfrom = (struct sockaddr_in *) sin->sin_zero; if (!suser()) return(-EPERM); if (sinfrom->sin_family && sinfrom->sin_family != AF_INET) return(-EINVAL); if (sinfrom->sin_port == 0) return(-EINVAL); saddr = sinfrom->sin_addr.s_addr; ufh.uh.source = sinfrom->sin_port; } #endif /* 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). Almost makes up for the */ /* violation above. -- MS */ if(sk->no_check) a = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock); else a = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock); if(a<0) return a; udp_statistics.UdpOutDatagrams++; return len; }
void udp_err(struct sk_buff *skb, u32 info) { struct iphdr *iph = (struct iphdr*)skb->data; struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2)); int type = skb->h.icmph->type; int code = skb->h.icmph->code; struct sock *sk; int harderr; int err; sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex); if (sk == NULL) { ICMP_INC_STATS_BH(IcmpInErrors); return; /* No socket for error */ } err = 0; harderr = 0; switch (type) { default: case ICMP_TIME_EXCEEDED: err = EHOSTUNREACH; break; case ICMP_SOURCE_QUENCH: goto out; case ICMP_PARAMETERPROB: err = EPROTO; harderr = 1; break; case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ if (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; break; } goto out; } err = EHOSTUNREACH; if (code <= NR_ICMP_UNREACH) { harderr = icmp_err_convert[code].fatal; err = icmp_err_convert[code].errno; } break; } /* * RFC1122: OK. Passes ICMP errors back to application, as per * 4.1.3.3. */ if (!sk->protinfo.af_inet.recverr) { if (!harderr || sk->state != TCP_ESTABLISHED) goto out; } else { ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1)); } sk->err = err; sk->error_report(sk); out: sock_put(sk); }