123199Smckusick /* 229158Smckusick * Copyright (c) 1982, 1986 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*35288Skarels * @(#)udp_usrreq.c 7.8 (Berkeley) 07/28/88 1823199Smckusick */ 194784Swnj 2017065Sbloom #include "param.h" 2117065Sbloom #include "dir.h" 2217065Sbloom #include "user.h" 2317065Sbloom #include "mbuf.h" 2417065Sbloom #include "protosw.h" 2517065Sbloom #include "socket.h" 2617065Sbloom #include "socketvar.h" 2717065Sbloom #include "errno.h" 2817065Sbloom #include "stat.h" 2910897Ssam 306584Ssam #include "../net/if.h" 316354Ssam #include "../net/route.h" 3210897Ssam 3317065Sbloom #include "in.h" 3417065Sbloom #include "in_pcb.h" 3517065Sbloom #include "in_systm.h" 3617065Sbloom #include "ip.h" 3717065Sbloom #include "ip_var.h" 3817065Sbloom #include "ip_icmp.h" 3917065Sbloom #include "udp.h" 4017065Sbloom #include "udp_var.h" 414784Swnj 424926Swnj /* 434926Swnj * UDP protocol implementation. 444926Swnj * Per RFC 768, August, 1980. 454926Swnj */ 464805Swnj udp_init() 474805Swnj { 484805Swnj 494901Swnj udb.inp_next = udb.inp_prev = &udb; 504805Swnj } 514805Swnj 5226118Skarels #ifndef COMPAT_42 5319519Skarels int udpcksum = 1; 5426118Skarels #else 5526118Skarels int udpcksum = 0; /* XXX */ 5626118Skarels #endif 5731398Skarels int udp_ttl = UDP_TTL; 5826118Skarels 594926Swnj struct sockaddr_in udp_in = { AF_INET }; 604901Swnj 6126032Skarels udp_input(m0, ifp) 624926Swnj struct mbuf *m0; 6326032Skarels struct ifnet *ifp; 644784Swnj { 654901Swnj register struct udpiphdr *ui; 664887Swnj register struct inpcb *inp; 674926Swnj register struct mbuf *m; 687844Sroot int len; 6924823Skarels struct ip ip; 704784Swnj 714926Swnj /* 725246Sroot * Get IP and UDP header together in first mbuf. 73*35288Skarels * Note: IP leaves IP header in first mbuf. 744926Swnj */ 754926Swnj m = m0; 765050Swnj ui = mtod(m, struct udpiphdr *); 775246Sroot if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) 785220Swnj ip_stripoptions((struct ip *)ui, (struct mbuf *)0); 79*35288Skarels if (m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) { 80*35288Skarels if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 81*35288Skarels udpstat.udps_hdrops++; 82*35288Skarels return; 83*35288Skarels } 84*35288Skarels ui = mtod(m, struct udpiphdr *); 85*35288Skarels } 864926Swnj 874926Swnj /* 885246Sroot * Make mbuf data length reflect UDP length. 895246Sroot * If not enough data to reflect UDP length, drop. 904926Swnj */ 917844Sroot len = ntohs((u_short)ui->ui_ulen); 924926Swnj if (((struct ip *)ui)->ip_len != len) { 934926Swnj if (len > ((struct ip *)ui)->ip_len) { 944926Swnj udpstat.udps_badlen++; 954926Swnj goto bad; 964926Swnj } 9715539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 9824823Skarels /* ((struct ip *)ui)->ip_len = len; */ 994926Swnj } 10024823Skarels /* 10124823Skarels * Save a copy of the IP header in case we want restore it for ICMP. 10224823Skarels */ 10324823Skarels ip = *(struct ip*)ui; 1044926Swnj 1054926Swnj /* 1065246Sroot * Checksum extended UDP header and data. 1074926Swnj */ 10815539Skarels if (udpcksum && ui->ui_sum) { 1094926Swnj ui->ui_next = ui->ui_prev = 0; 1104926Swnj ui->ui_x1 = 0; 11121112Skarels ui->ui_len = ui->ui_ulen; 1127844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 1134926Swnj udpstat.udps_badsum++; 1144901Swnj m_freem(m); 1154901Swnj return; 1164901Swnj } 1174901Swnj } 1184926Swnj 1194926Swnj /* 1207844Sroot * Locate pcb for datagram. 1214926Swnj */ 1224926Swnj inp = in_pcblookup(&udb, 1236029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 1246029Sroot INPLOOKUP_WILDCARD); 1256584Ssam if (inp == 0) { 12610144Ssam /* don't send ICMP response for broadcast packet */ 12721112Skarels if (in_broadcast(ui->ui_dst)) 1286584Ssam goto bad; 12924823Skarels *(struct ip *)ui = ip; 13026032Skarels icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT, 13126032Skarels ifp); 1326584Ssam return; 1336584Ssam } 1346584Ssam 1354926Swnj /* 1364926Swnj * Construct sockaddr format source address. 1374926Swnj * Stuff source address and datagram in user buffer. 1384926Swnj */ 1394926Swnj udp_in.sin_port = ui->ui_sport; 1404926Swnj udp_in.sin_addr = ui->ui_src; 1415050Swnj m->m_len -= sizeof (struct udpiphdr); 1425050Swnj m->m_off += sizeof (struct udpiphdr); 14312767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 14412767Ssam m, (struct mbuf *)0) == 0) 1454926Swnj goto bad; 1465050Swnj sorwakeup(inp->inp_socket); 1474887Swnj return; 1484926Swnj bad: 1494887Swnj m_freem(m); 1504784Swnj } 1514784Swnj 15226426Skarels /* 15326426Skarels * Notify a udp user of an asynchronous error; 15426426Skarels * just wake up so that he can collect error status. 15526426Skarels */ 15626426Skarels udp_notify(inp) 15726426Skarels register struct inpcb *inp; 15826426Skarels { 15926426Skarels 16026426Skarels sorwakeup(inp->inp_socket); 16126426Skarels sowwakeup(inp->inp_socket); 16226426Skarels } 16326426Skarels 16424823Skarels udp_ctlinput(cmd, sa) 1656591Ssam int cmd; 16624823Skarels struct sockaddr *sa; 1676591Ssam { 1686591Ssam extern u_char inetctlerrmap[]; 16924823Skarels struct sockaddr_in *sin; 17021121Skarels int in_rtchange(); 1716591Ssam 17224823Skarels if ((unsigned)cmd > PRC_NCMDS) 1736591Ssam return; 17424823Skarels if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 17524823Skarels return; 17624823Skarels sin = (struct sockaddr_in *)sa; 17724823Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 17824823Skarels return; 17924823Skarels 1806591Ssam switch (cmd) { 1816591Ssam 18224823Skarels case PRC_QUENCH: 1836591Ssam break; 1846591Ssam 18524823Skarels case PRC_ROUTEDEAD: 18621121Skarels case PRC_REDIRECT_NET: 18721121Skarels case PRC_REDIRECT_HOST: 18824823Skarels case PRC_REDIRECT_TOSNET: 18924823Skarels case PRC_REDIRECT_TOSHOST: 19024823Skarels in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 1916591Ssam break; 1926591Ssam 1936591Ssam default: 19421121Skarels if (inetctlerrmap[cmd] == 0) 19521121Skarels return; /* XXX */ 19624823Skarels in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 19726426Skarels udp_notify); 1986591Ssam } 1996591Ssam } 2006591Ssam 2014955Swnj udp_output(inp, m0) 20226060Skarels register struct inpcb *inp; 2034926Swnj struct mbuf *m0; 2044784Swnj { 2054926Swnj register struct mbuf *m; 2064926Swnj register struct udpiphdr *ui; 20717165Skarels register int len = 0; 2084784Swnj 2094926Swnj /* 2104926Swnj * Calculate data length and get a mbuf 2115246Sroot * for UDP and IP headers. 2124926Swnj */ 2134926Swnj for (m = m0; m; m = m->m_next) 2144926Swnj len += m->m_len; 21521112Skarels MGET(m, M_DONTWAIT, MT_HEADER); 2166507Ssam if (m == 0) { 2176507Ssam m_freem(m0); 2186507Ssam return (ENOBUFS); 2196507Ssam } 2204784Swnj 2214926Swnj /* 2225246Sroot * Fill in mbuf with extended UDP header 2234926Swnj * and addresses and length put into network format. 2244926Swnj */ 2254926Swnj m->m_off = MMAXOFF - sizeof (struct udpiphdr); 2264926Swnj m->m_len = sizeof (struct udpiphdr); 2274926Swnj m->m_next = m0; 2284926Swnj ui = mtod(m, struct udpiphdr *); 2294926Swnj ui->ui_next = ui->ui_prev = 0; 2304926Swnj ui->ui_x1 = 0; 2314926Swnj ui->ui_pr = IPPROTO_UDP; 23215226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2335050Swnj ui->ui_src = inp->inp_laddr; 2345050Swnj ui->ui_dst = inp->inp_faddr; 2355050Swnj ui->ui_sport = inp->inp_lport; 2365050Swnj ui->ui_dport = inp->inp_fport; 23715226Ssam ui->ui_ulen = ui->ui_len; 2384784Swnj 2394926Swnj /* 2404926Swnj * Stuff checksum and output datagram. 2414926Swnj */ 2424926Swnj ui->ui_sum = 0; 24319519Skarels if (udpcksum) { 24419519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 24533715Skarels ui->ui_sum = 0xffff; 24621112Skarels } 2475050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 24831398Skarels ((struct ip *)ui)->ip_ttl = udp_ttl; 24926060Skarels return (ip_output(m, inp->inp_options, &inp->inp_route, 25026060Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 2514784Swnj } 2524784Swnj 25334500Skarels u_long udp_sendspace = 2048; /* really max datagram size */ 25434500Skarels u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 25518368Skarels 2568602Sroot /*ARGSUSED*/ 25712767Ssam udp_usrreq(so, req, m, nam, rights) 2584887Swnj struct socket *so; 2594784Swnj int req; 26012767Ssam struct mbuf *m, *nam, *rights; 2614784Swnj { 2624887Swnj struct inpcb *inp = sotoinpcb(so); 2636507Ssam int error = 0; 2644784Swnj 26518368Skarels if (req == PRU_CONTROL) 26618368Skarels return (in_control(so, (int)m, (caddr_t)nam, 26718368Skarels (struct ifnet *)rights)); 26812767Ssam if (rights && rights->m_len) { 26912767Ssam error = EINVAL; 27012767Ssam goto release; 27112767Ssam } 27211080Ssam if (inp == NULL && req != PRU_ATTACH) { 27311080Ssam error = EINVAL; 27411080Ssam goto release; 27511080Ssam } 2764784Swnj switch (req) { 2774784Swnj 2784784Swnj case PRU_ATTACH: 27911080Ssam if (inp != NULL) { 28011080Ssam error = EINVAL; 28111080Ssam break; 28211080Ssam } 2838273Sroot error = in_pcballoc(so, &udb); 2848273Sroot if (error) 2858273Sroot break; 28618368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 2878273Sroot if (error) 2888273Sroot break; 2894887Swnj break; 2904784Swnj 2914784Swnj case PRU_DETACH: 2925166Swnj in_pcbdetach(inp); 2934887Swnj break; 2944784Swnj 2958273Sroot case PRU_BIND: 2968273Sroot error = in_pcbbind(inp, nam); 2978273Sroot break; 2988273Sroot 2998273Sroot case PRU_LISTEN: 3008273Sroot error = EOPNOTSUPP; 3018273Sroot break; 3028273Sroot 3034784Swnj case PRU_CONNECT: 30411080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 30511080Ssam error = EISCONN; 30611080Ssam break; 30711080Ssam } 3088273Sroot error = in_pcbconnect(inp, nam); 3096507Ssam if (error == 0) 3106507Ssam soisconnected(so); 3114887Swnj break; 3124784Swnj 31313118Ssam case PRU_CONNECT2: 31413118Ssam error = EOPNOTSUPP; 31513118Ssam break; 31613118Ssam 3174926Swnj case PRU_ACCEPT: 31811080Ssam error = EOPNOTSUPP; 31911080Ssam break; 3204926Swnj 3214784Swnj case PRU_DISCONNECT: 32211080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 32311080Ssam error = ENOTCONN; 32411080Ssam break; 32511080Ssam } 3265166Swnj in_pcbdisconnect(inp); 32726404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 3284784Swnj break; 3294784Swnj 3304912Swnj case PRU_SHUTDOWN: 3314912Swnj socantsendmore(so); 3324912Swnj break; 3334912Swnj 3345996Swnj case PRU_SEND: { 3355996Swnj struct in_addr laddr; 33616799Skarels int s; 3375996Swnj 3388273Sroot if (nam) { 3395996Swnj laddr = inp->inp_laddr; 34011080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 34111080Ssam error = EISCONN; 34211080Ssam break; 34311080Ssam } 34416799Skarels /* 34516799Skarels * Must block input while temporarily connected. 34616799Skarels */ 34716799Skarels s = splnet(); 3488273Sroot error = in_pcbconnect(inp, nam); 34916799Skarels if (error) { 35016799Skarels splx(s); 3516507Ssam break; 35216799Skarels } 3534955Swnj } else { 35411080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 35511080Ssam error = ENOTCONN; 35611080Ssam break; 35711080Ssam } 3584955Swnj } 3596507Ssam error = udp_output(inp, m); 36011080Ssam m = NULL; 3618273Sroot if (nam) { 3625166Swnj in_pcbdisconnect(inp); 36318368Skarels inp->inp_laddr = laddr; 36416799Skarels splx(s); 3655996Swnj } 3665996Swnj } 3674784Swnj break; 3684784Swnj 3694784Swnj case PRU_ABORT: 37031750Skarels soisdisconnected(so); 3715166Swnj in_pcbdetach(inp); 3724784Swnj break; 3734784Swnj 3746511Ssam case PRU_SOCKADDR: 3758273Sroot in_setsockaddr(inp, nam); 3766511Ssam break; 3776511Ssam 37814124Ssam case PRU_PEERADDR: 37914124Ssam in_setpeeraddr(inp, nam); 38014124Ssam break; 38114124Ssam 38216988Skarels case PRU_SENSE: 38316988Skarels /* 38416988Skarels * stat: don't bother with a blocksize. 38516988Skarels */ 38616988Skarels return (0); 38716988Skarels 38812205Ssam case PRU_SENDOOB: 38912205Ssam case PRU_FASTTIMO: 39012205Ssam case PRU_SLOWTIMO: 39112205Ssam case PRU_PROTORCV: 39212205Ssam case PRU_PROTOSEND: 39312205Ssam error = EOPNOTSUPP; 39412205Ssam break; 39513118Ssam 39616799Skarels case PRU_RCVD: 39716799Skarels case PRU_RCVOOB: 39816799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 39916799Skarels 40013118Ssam default: 40113118Ssam panic("udp_usrreq"); 4024805Swnj } 40311080Ssam release: 40411080Ssam if (m != NULL) 40511080Ssam m_freem(m); 4066507Ssam return (error); 4074784Swnj } 408