/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * The User Datagram Protocol (UDP). * * Version: @(#)udp.c 1.0.13 06/02/93 * * Authors: Ross Biro, * Fred N. van Kempen, * * Fixes: * Alan Cox : verify_area() calls * Alan Cox : stopped close while in use off icmp * messages. Not a fix but a botch that * for udp at least is 'valid'. * Alan Cox : Fixed icmp handling properly * Alan Cox : Correct error for oversized datagrams * Alan Cox : Tidied select() semantics. * Alan Cox : udp_err() fixed properly, also now * select and read wake correctly on errors * Alan Cox : udp_send verify_area moved to avoid mem leak * Alan Cox : UDP can count its memory * Alan Cox : send to an uknown connection causes * an ECONNREFUSED off the icmp, but * does NOT close. * Alan Cox : Switched to new sk_buff handlers. No more backlog! * Alan Cox : Using generic datagram code. Even smaller and the PEEK * bug no longer crashes it. * Fred Van Kempen : Net2e support for sk->broadcast. * Alan Cox : Uses skb_free_datagram * Alan Cox : Added get/set sockopt support. * Alan Cox : Broadcasting without option set returns EACCES. * Alan Cox : No wakeup calls. Instead we now use the callbacks. * Alan Cox : Use ip_tos and ip_ttl * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "inet.h" #include "dev.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" #include "sock.h" #include "udp.h" #include "icmp.h" #define min(a,b) ((a)<(b)?(a):(b)) static void print_udp(struct udphdr *uh) { if (inet_debug != DBG_UDP) return; if (uh == NULL) { printk("(NULL)\n"); return; } printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest)); printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check)); } /* * 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 err, unsigned char *header, unsigned long daddr, unsigned long saddr, struct inet_protocol *protocol) { struct udphdr *th; struct sock *sk; struct iphdr *ip=(struct iphdr *)header; header += 4*ip->ihl; th = (struct udphdr *)header; DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest)); sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr); if (sk == NULL) return; /* No socket for error */ if (err < 0) /* As per the calling spec */ { sk->err = -err; sk->error_report(sk); /* User process wakes to see error */ return; } if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) { /* Slow down! */ if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2; return; } sk->err = icmp_err_convert[err & 0xff].errno; /* It's only fatal if we have connected to them. */ if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) { sk->err=ECONNREFUSED; } sk->error_report(sk); } static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr) { unsigned long sum; DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n", uh, len, saddr, daddr)); print_udp(uh); __asm__("\t addl %%ecx,%%ebx\n" "\t adcl %%edx,%%ebx\n" "\t adcl $0, %%ebx\n" : "=b"(sum) : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256) : "cx","bx","dx" ); if (len > 3) { __asm__("\tclc\n" "1:\n" "\t lodsl\n" "\t adcl %%eax, %%ebx\n" "\t loop 1b\n" "\t adcl $0, %%ebx\n" : "=b"(sum) , "=S"(uh) : "0"(sum), "c"(len/4) ,"1"(uh) : "ax", "cx", "bx", "si" ); } /* Convert from 32 bits to 16 bits. */ __asm__("\t movl %%ebx, %%ecx\n" "\t shrl $16,%%ecx\n" "\t addw %%cx, %%bx\n" "\t adcw $0, %%bx\n" : "=b"(sum) : "0"(sum) : "bx", "cx"); /* Check for an extra word. */ if ((len & 2) != 0) { __asm__("\t lodsw\n" "\t addw %%ax,%%bx\n" "\t adcw $0, %%bx\n" : "=b"(sum), "=S"(uh) : "0"(sum) ,"1"(uh) : "si", "ax", "bx"); } /* Now check for the extra byte. */ if ((len & 1) != 0) { __asm__("\t lodsb\n" "\t movb $0,%%ah\n" "\t addw %%ax,%%bx\n" "\t adcw $0, %%bx\n" : "=b"(sum) : "0"(sum) ,"S"(uh) : "si", "ax", "bx"); } /* We only want the bottom 16 bits, but we never cleared the top 16. */ return((~sum) & 0xffff); } static void udp_send_check(struct udphdr *uh, unsigned long saddr, unsigned long daddr, int len, struct sock *sk) { uh->check = 0; if (sk && sk->no_check) return; uh->check = udp_check(uh, len, saddr, daddr); if (uh->check == 0) uh->check = 0xffff; } static int udp_send(struct sock *sk, struct sockaddr_in *sin, unsigned char *from, int len) { struct sk_buff *skb; struct device *dev; struct udphdr *uh; unsigned char *buff; unsigned long saddr; int size, tmp; int err; DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n", in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port), from, len)); err=verify_area(VERIFY_READ, from, len); if(err) return(err); /* Allocate a copy of the packet. */ size = sizeof(struct sk_buff) + sk->prot->max_header + len; skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL); if (skb == NULL) return(-ENOMEM); skb->mem_addr = skb; skb->mem_len = size; skb->sk = NULL; /* to avoid changing sk->saddr */ skb->free = 1; skb->arp = 0; /* Now build the IP and MAC header. */ buff = skb->data; saddr = 0; dev = NULL; DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n", saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len)); tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr, &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl); skb->sk=sk; /* So memory is freed correctly */ if (tmp < 0 ) { sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); return(tmp); } buff += tmp; saddr = dev->pa_addr; DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp)); skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */ skb->dev = dev; #ifdef OLD /* * This code used to hack in some form of fragmentation. * I removed that, since it didn't work anyway, and it made the * code a bad thing to read and understand. -FvK */ if (len > dev->mtu) { #else if (skb->len > 4095) { #endif printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu); sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); return(-EMSGSIZE); } /* Fill in the UDP header. */ uh = (struct udphdr *) buff; uh->len = htons(len + sizeof(struct udphdr)); uh->source = sk->dummy_th.source; uh->dest = sin->sin_port; buff = (unsigned char *) (uh + 1); /* Copy the user data. */ memcpy_fromfs(buff, from, len); /* Set up the UDP checksum. */ udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk); /* Send the datagram to the interface. */ sk->prot->queue_xmit(sk, dev, skb, 1); return(len); } static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { struct sockaddr_in sin; int tmp; int err; DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags)); /* Check the flags. */ if (flags) return(-EINVAL); if (len < 0) return(-EINVAL); if (len == 0) return(0); /* Get and verify the address. */ if (usin) { if (addr_len < sizeof(sin)) return(-EINVAL); err=verify_area(VERIFY_READ, usin, sizeof(sin)); if(err) return err; memcpy_fromfs(&sin, usin, sizeof(sin)); if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL); if (sin.sin_port == 0) return(-EINVAL); } else { if (sk->state != TCP_ESTABLISHED) return(-EINVAL); sin.sin_family = AF_INET; sin.sin_port = sk->dummy_th.dest; sin.sin_addr.s_addr = sk->daddr; } if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ sk->inuse = 1; /* Send the packet. */ tmp = udp_send(sk, &sin, from, len); /* The datagram has been sent off. Release the socket. */ release_sock(sk); return(tmp); } static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0)); } int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) { int err; switch(cmd) { case DDIOCSDBG: { int val; if (!suser()) return(-EPERM); err=verify_area(VERIFY_READ, (void *)arg, sizeof(int)); if(err) return err; val = get_fs_long((int *)arg); switch(val) { case 0: inet_debug = 0; break; case 1: inet_debug = DBG_UDP; break; default: return(-EINVAL); } } break; case TIOCOUTQ: { unsigned long amount; if (sk->state == TCP_LISTEN) return(-EINVAL); amount = sk->prot->wspace(sk)/*/2*/; err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); if(err) return(err); put_fs_long(amount,(unsigned long *)arg); return(0); } case TIOCINQ: { struct sk_buff *skb; unsigned long amount; if (sk->state == TCP_LISTEN) return(-EINVAL); amount = 0; skb = sk->rqueue; if (skb != NULL) { /* * We will only return the amount * of this packet since that is all * that will be read. */ amount = skb->len; } err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); if(err) return(err); put_fs_long(amount,(unsigned long *)arg); return(0); } default: return(-EINVAL); } return(0); } /* * This should be easy, if there is something there we\ * return it, otherwise we block. */ int udp_recvfrom(struct sock *sk, unsigned char *to, int len, int noblock, unsigned flags, struct sockaddr_in *sin, int *addr_len) { int copied = 0; struct sk_buff *skb; int er; /* * This will pick up errors that occured while the program * was doing something else. */ if (sk->err) { int err; err = -sk->err; sk->err = 0; return(err); } if (len == 0) return(0); if (len < 0) return(-EINVAL); if (addr_len) { er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); if(er) return(er); put_fs_long(sizeof(*sin), addr_len); } if(sin) { er=verify_area(VERIFY_WRITE, sin, sizeof(*sin)); if(er) return(er); } er=verify_area(VERIFY_WRITE,to,len); if(er) return er; skb=skb_recv_datagram(sk,flags,noblock,&er); if(skb==NULL) return er; copied = min(len, skb->len); /* FIXME : should use udp header size info value */ skb_copy_datagram(skb,sizeof(struct udphdr),to,copied); /* Copy the address. */ if (sin) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = skb->h.uh->source; addr.sin_addr.s_addr = skb->daddr; memcpy_tofs(sin, &addr, sizeof(*sin)); } skb_free_datagram(skb); release_sock(sk); return(copied); } int udp_read(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); } int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) { struct sockaddr_in sin; int er; if (addr_len < sizeof(sin)) return(-EINVAL); er=verify_area(VERIFY_READ, usin, sizeof(sin)); if(er) return er; memcpy_fromfs(&sin, usin, sizeof(sin)); if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT); if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ sk->daddr = sin.sin_addr.s_addr; sk->dummy_th.dest = sin.sin_port; sk->state = TCP_ESTABLISHED; return(0); } static void udp_close(struct sock *sk, int timeout) { sk->inuse = 1; sk->state = TCP_CLOSE; if (sk->dead) destroy_sock(sk); else release_sock(sk); } /* All we need to do is get the socket, and then do a checksum. */ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol) { struct sock *sk; struct udphdr *uh; uh = (struct udphdr *) skb->h.uh; sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); if (sk == NULL) { if (chk_addr(daddr) == IS_MYADDR) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev); } /* * Hmm. We got an UDP broadcast to a port to which we * don't wanna listen. The only thing we can do now is * to ignore the packet... -FvK */ skb->sk = NULL; kfree_skb(skb, FREE_WRITE); return(0); } if (uh->check && udp_check(uh, len, saddr, daddr)) { DPRINTF((DBG_UDP, "UDP: bad checksum\n")); skb->sk = NULL; kfree_skb(skb, FREE_WRITE); return(0); } skb->sk = sk; skb->dev = dev; skb->len = len; /* These are supposed to be switched. */ skb->daddr = saddr; skb->saddr = daddr; /* Charge it to the socket. */ if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) { skb->sk = NULL; kfree_skb(skb, FREE_WRITE); release_sock(sk); return(0); } sk->rmem_alloc += skb->mem_len; /* At this point we should print the thing out. */ DPRINTF((DBG_UDP, "<< \n")); print_udp(uh); /* Now add it to the data chain and wake things up. */ skb_queue_tail(&sk->rqueue,skb); skb->len = len - sizeof(*uh); if (!sk->dead) sk->data_ready(sk,skb->len); release_sock(sk); return(0); } struct proto udp_prot = { sock_wmalloc, sock_rmalloc, sock_wfree, sock_rfree, sock_rspace, sock_wspace, udp_close, udp_read, udp_write, udp_sendto, udp_recvfrom, ip_build_header, udp_connect, NULL, ip_queue_xmit, ip_retransmit, NULL, NULL, udp_rcv, datagram_select, udp_ioctl, NULL, NULL, ip_setsockopt, ip_getsockopt, 128, 0, {NULL,}, "UDP" };