123199Smckusick /* 235794Skarels * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 332789Sbostic * All rights reserved. 423199Smckusick * 532789Sbostic * Redistribution and use in source and binary forms are permitted 634855Sbostic * provided that the above copyright notice and this paragraph are 734855Sbostic * duplicated in all such forms and that any documentation, 834855Sbostic * advertising materials, and other materials related to such 934855Sbostic * distribution and use acknowledge that the software was developed 1034855Sbostic * by the University of California, Berkeley. The name of the 1134855Sbostic * University may not be used to endorse or promote products derived 1234855Sbostic * from this software without specific prior written permission. 1334855Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434855Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534855Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1632789Sbostic * 17*37324Skarels * @(#)udp_usrreq.c 7.10 (Berkeley) 04/08/89 1823199Smckusick */ 194784Swnj 2017065Sbloom #include "param.h" 2117065Sbloom #include "dir.h" 2217065Sbloom #include "user.h" 2335794Skarels #include "malloc.h" 2417065Sbloom #include "mbuf.h" 2517065Sbloom #include "protosw.h" 2617065Sbloom #include "socket.h" 2717065Sbloom #include "socketvar.h" 2817065Sbloom #include "errno.h" 2917065Sbloom #include "stat.h" 3010897Ssam 316584Ssam #include "../net/if.h" 326354Ssam #include "../net/route.h" 3310897Ssam 3417065Sbloom #include "in.h" 3517065Sbloom #include "in_pcb.h" 3617065Sbloom #include "in_systm.h" 3717065Sbloom #include "ip.h" 3817065Sbloom #include "ip_var.h" 3917065Sbloom #include "ip_icmp.h" 4017065Sbloom #include "udp.h" 4117065Sbloom #include "udp_var.h" 424784Swnj 434926Swnj /* 444926Swnj * UDP protocol implementation. 454926Swnj * Per RFC 768, August, 1980. 464926Swnj */ 474805Swnj udp_init() 484805Swnj { 494805Swnj 504901Swnj udb.inp_next = udb.inp_prev = &udb; 514805Swnj } 524805Swnj 5326118Skarels #ifndef COMPAT_42 5419519Skarels int udpcksum = 1; 5526118Skarels #else 5626118Skarels int udpcksum = 0; /* XXX */ 5726118Skarels #endif 5831398Skarels int udp_ttl = UDP_TTL; 5926118Skarels 60*37324Skarels struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 614901Swnj 6235794Skarels udp_input(m, iphlen) 6335794Skarels register struct mbuf *m; 6435794Skarels int iphlen; 654784Swnj { 664901Swnj register struct udpiphdr *ui; 674887Swnj register struct inpcb *inp; 687844Sroot int len; 6924823Skarels struct ip ip; 704784Swnj 714926Swnj /* 725246Sroot * Get IP and UDP header together in first mbuf. 7335288Skarels * Note: IP leaves IP header in first mbuf. 744926Swnj */ 755050Swnj ui = mtod(m, struct udpiphdr *); 7635794Skarels if (iphlen > sizeof (struct ip)) 7735794Skarels ip_stripoptions(m, (struct mbuf *)0); 7835794Skarels if (m->m_len < sizeof (struct udpiphdr)) { 7935288Skarels if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 8035288Skarels udpstat.udps_hdrops++; 8135288Skarels return; 8235288Skarels } 8335288Skarels ui = mtod(m, struct udpiphdr *); 8435288Skarels } 854926Swnj 864926Swnj /* 875246Sroot * Make mbuf data length reflect UDP length. 885246Sroot * If not enough data to reflect UDP length, drop. 894926Swnj */ 907844Sroot len = ntohs((u_short)ui->ui_ulen); 914926Swnj if (((struct ip *)ui)->ip_len != len) { 924926Swnj if (len > ((struct ip *)ui)->ip_len) { 934926Swnj udpstat.udps_badlen++; 944926Swnj goto bad; 954926Swnj } 9615539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 9724823Skarels /* ((struct ip *)ui)->ip_len = len; */ 984926Swnj } 9924823Skarels /* 10024823Skarels * Save a copy of the IP header in case we want restore it for ICMP. 10124823Skarels */ 10235794Skarels ip = *(struct ip *)ui; 1034926Swnj 1044926Swnj /* 1055246Sroot * Checksum extended UDP header and data. 1064926Swnj */ 10715539Skarels if (udpcksum && ui->ui_sum) { 1084926Swnj ui->ui_next = ui->ui_prev = 0; 1094926Swnj ui->ui_x1 = 0; 11021112Skarels ui->ui_len = ui->ui_ulen; 1117844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 1124926Swnj udpstat.udps_badsum++; 1134901Swnj m_freem(m); 1144901Swnj return; 1154901Swnj } 1164901Swnj } 1174926Swnj 1184926Swnj /* 1197844Sroot * Locate pcb for datagram. 1204926Swnj */ 1214926Swnj inp = in_pcblookup(&udb, 1226029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 1236029Sroot INPLOOKUP_WILDCARD); 1246584Ssam if (inp == 0) { 12510144Ssam /* don't send ICMP response for broadcast packet */ 12635794Skarels if (m->m_flags & M_BCAST) 1276584Ssam goto bad; 12824823Skarels *(struct ip *)ui = ip; 12935794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1306584Ssam return; 1316584Ssam } 1326584Ssam 1334926Swnj /* 1344926Swnj * Construct sockaddr format source address. 1354926Swnj * Stuff source address and datagram in user buffer. 1364926Swnj */ 1374926Swnj udp_in.sin_port = ui->ui_sport; 1384926Swnj udp_in.sin_addr = ui->ui_src; 1395050Swnj m->m_len -= sizeof (struct udpiphdr); 14035794Skarels m->m_pkthdr.len -= sizeof (struct udpiphdr); 14135794Skarels m->m_data += sizeof (struct udpiphdr); 14212767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 14312767Ssam m, (struct mbuf *)0) == 0) 1444926Swnj goto bad; 1455050Swnj sorwakeup(inp->inp_socket); 1464887Swnj return; 1474926Swnj bad: 1484887Swnj m_freem(m); 1494784Swnj } 1504784Swnj 15126426Skarels /* 15226426Skarels * Notify a udp user of an asynchronous error; 15326426Skarels * just wake up so that he can collect error status. 15426426Skarels */ 15526426Skarels udp_notify(inp) 15626426Skarels register struct inpcb *inp; 15726426Skarels { 15826426Skarels 15926426Skarels sorwakeup(inp->inp_socket); 16026426Skarels sowwakeup(inp->inp_socket); 16126426Skarels } 16226426Skarels 16324823Skarels udp_ctlinput(cmd, sa) 1646591Ssam int cmd; 16524823Skarels struct sockaddr *sa; 1666591Ssam { 1676591Ssam extern u_char inetctlerrmap[]; 16824823Skarels struct sockaddr_in *sin; 16921121Skarels int in_rtchange(); 1706591Ssam 17124823Skarels if ((unsigned)cmd > PRC_NCMDS) 1726591Ssam return; 17324823Skarels if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 17424823Skarels return; 17524823Skarels sin = (struct sockaddr_in *)sa; 17624823Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 17724823Skarels return; 17824823Skarels 1796591Ssam switch (cmd) { 1806591Ssam 18124823Skarels case PRC_QUENCH: 1826591Ssam break; 1836591Ssam 18424823Skarels case PRC_ROUTEDEAD: 18521121Skarels case PRC_REDIRECT_NET: 18621121Skarels case PRC_REDIRECT_HOST: 18724823Skarels case PRC_REDIRECT_TOSNET: 18824823Skarels case PRC_REDIRECT_TOSHOST: 18924823Skarels in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 1906591Ssam break; 1916591Ssam 1926591Ssam default: 19321121Skarels if (inetctlerrmap[cmd] == 0) 19421121Skarels return; /* XXX */ 19524823Skarels in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 19626426Skarels udp_notify); 1976591Ssam } 1986591Ssam } 1996591Ssam 20035794Skarels udp_output(inp, m) 20126060Skarels register struct inpcb *inp; 20235794Skarels register struct mbuf *m; 2034784Swnj { 2044926Swnj register struct udpiphdr *ui; 20535794Skarels register int len = m->m_pkthdr.len; 2064784Swnj 2074926Swnj /* 2084926Swnj * Calculate data length and get a mbuf 2095246Sroot * for UDP and IP headers. 2104926Swnj */ 21135794Skarels M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 2124784Swnj 2134926Swnj /* 2145246Sroot * Fill in mbuf with extended UDP header 2154926Swnj * and addresses and length put into network format. 2164926Swnj */ 2174926Swnj ui = mtod(m, struct udpiphdr *); 2184926Swnj ui->ui_next = ui->ui_prev = 0; 2194926Swnj ui->ui_x1 = 0; 2204926Swnj ui->ui_pr = IPPROTO_UDP; 22115226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2225050Swnj ui->ui_src = inp->inp_laddr; 2235050Swnj ui->ui_dst = inp->inp_faddr; 2245050Swnj ui->ui_sport = inp->inp_lport; 2255050Swnj ui->ui_dport = inp->inp_fport; 22615226Ssam ui->ui_ulen = ui->ui_len; 2274784Swnj 2284926Swnj /* 2294926Swnj * Stuff checksum and output datagram. 2304926Swnj */ 2314926Swnj ui->ui_sum = 0; 23219519Skarels if (udpcksum) { 23319519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 23433715Skarels ui->ui_sum = 0xffff; 23521112Skarels } 2365050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 23731398Skarels ((struct ip *)ui)->ip_ttl = udp_ttl; 23826060Skarels return (ip_output(m, inp->inp_options, &inp->inp_route, 23926060Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 2404784Swnj } 2414784Swnj 24234500Skarels u_long udp_sendspace = 2048; /* really max datagram size */ 24334500Skarels u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 24418368Skarels 2458602Sroot /*ARGSUSED*/ 24635794Skarels udp_usrreq(so, req, m, nam, rights, control) 2474887Swnj struct socket *so; 2484784Swnj int req; 24935794Skarels struct mbuf *m, *nam, *rights, *control; 2504784Swnj { 2514887Swnj struct inpcb *inp = sotoinpcb(so); 2526507Ssam int error = 0; 2534784Swnj 25418368Skarels if (req == PRU_CONTROL) 25518368Skarels return (in_control(so, (int)m, (caddr_t)nam, 25618368Skarels (struct ifnet *)rights)); 25712767Ssam if (rights && rights->m_len) { 25812767Ssam error = EINVAL; 25912767Ssam goto release; 26012767Ssam } 26111080Ssam if (inp == NULL && req != PRU_ATTACH) { 26211080Ssam error = EINVAL; 26311080Ssam goto release; 26411080Ssam } 2654784Swnj switch (req) { 2664784Swnj 2674784Swnj case PRU_ATTACH: 26811080Ssam if (inp != NULL) { 26911080Ssam error = EINVAL; 27011080Ssam break; 27111080Ssam } 2728273Sroot error = in_pcballoc(so, &udb); 2738273Sroot if (error) 2748273Sroot break; 27518368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 2768273Sroot if (error) 2778273Sroot break; 2784887Swnj break; 2794784Swnj 2804784Swnj case PRU_DETACH: 2815166Swnj in_pcbdetach(inp); 2824887Swnj break; 2834784Swnj 2848273Sroot case PRU_BIND: 2858273Sroot error = in_pcbbind(inp, nam); 2868273Sroot break; 2878273Sroot 2888273Sroot case PRU_LISTEN: 2898273Sroot error = EOPNOTSUPP; 2908273Sroot break; 2918273Sroot 2924784Swnj case PRU_CONNECT: 29311080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 29411080Ssam error = EISCONN; 29511080Ssam break; 29611080Ssam } 2978273Sroot error = in_pcbconnect(inp, nam); 2986507Ssam if (error == 0) 2996507Ssam soisconnected(so); 3004887Swnj break; 3014784Swnj 30213118Ssam case PRU_CONNECT2: 30313118Ssam error = EOPNOTSUPP; 30413118Ssam break; 30513118Ssam 3064926Swnj case PRU_ACCEPT: 30711080Ssam error = EOPNOTSUPP; 30811080Ssam break; 3094926Swnj 3104784Swnj case PRU_DISCONNECT: 31111080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 31211080Ssam error = ENOTCONN; 31311080Ssam break; 31411080Ssam } 3155166Swnj in_pcbdisconnect(inp); 31626404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 3174784Swnj break; 3184784Swnj 3194912Swnj case PRU_SHUTDOWN: 3204912Swnj socantsendmore(so); 3214912Swnj break; 3224912Swnj 3235996Swnj case PRU_SEND: { 3245996Swnj struct in_addr laddr; 32516799Skarels int s; 3265996Swnj 3278273Sroot if (nam) { 3285996Swnj laddr = inp->inp_laddr; 32911080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 33011080Ssam error = EISCONN; 33111080Ssam break; 33211080Ssam } 33316799Skarels /* 33416799Skarels * Must block input while temporarily connected. 33516799Skarels */ 33616799Skarels s = splnet(); 3378273Sroot error = in_pcbconnect(inp, nam); 33816799Skarels if (error) { 33916799Skarels splx(s); 3406507Ssam break; 34116799Skarels } 3424955Swnj } else { 34311080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 34411080Ssam error = ENOTCONN; 34511080Ssam break; 34611080Ssam } 3474955Swnj } 3486507Ssam error = udp_output(inp, m); 34911080Ssam m = NULL; 3508273Sroot if (nam) { 3515166Swnj in_pcbdisconnect(inp); 35218368Skarels inp->inp_laddr = laddr; 35316799Skarels splx(s); 3545996Swnj } 3555996Swnj } 3564784Swnj break; 3574784Swnj 3584784Swnj case PRU_ABORT: 35931750Skarels soisdisconnected(so); 3605166Swnj in_pcbdetach(inp); 3614784Swnj break; 3624784Swnj 3636511Ssam case PRU_SOCKADDR: 3648273Sroot in_setsockaddr(inp, nam); 3656511Ssam break; 3666511Ssam 36714124Ssam case PRU_PEERADDR: 36814124Ssam in_setpeeraddr(inp, nam); 36914124Ssam break; 37014124Ssam 37116988Skarels case PRU_SENSE: 37216988Skarels /* 37316988Skarels * stat: don't bother with a blocksize. 37416988Skarels */ 37516988Skarels return (0); 37616988Skarels 37712205Ssam case PRU_SENDOOB: 37812205Ssam case PRU_FASTTIMO: 37912205Ssam case PRU_SLOWTIMO: 38012205Ssam case PRU_PROTORCV: 38112205Ssam case PRU_PROTOSEND: 38212205Ssam error = EOPNOTSUPP; 38312205Ssam break; 38413118Ssam 38516799Skarels case PRU_RCVD: 38616799Skarels case PRU_RCVOOB: 38716799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 38816799Skarels 38913118Ssam default: 39013118Ssam panic("udp_usrreq"); 3914805Swnj } 39211080Ssam release: 39311080Ssam if (m != NULL) 39411080Ssam m_freem(m); 3956507Ssam return (error); 3964784Swnj } 397