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 6*34855Sbostic * provided that the above copyright notice and this paragraph are 7*34855Sbostic * duplicated in all such forms and that any documentation, 8*34855Sbostic * advertising materials, and other materials related to such 9*34855Sbostic * distribution and use acknowledge that the software was developed 10*34855Sbostic * by the University of California, Berkeley. The name of the 11*34855Sbostic * University may not be used to endorse or promote products derived 12*34855Sbostic * from this software without specific prior written permission. 13*34855Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34855Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34855Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1632789Sbostic * 17*34855Sbostic * @(#)udp_usrreq.c 7.7 (Berkeley) 06/29/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. 734926Swnj */ 744926Swnj m = m0; 755308Sroot if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) && 765308Sroot (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 774926Swnj udpstat.udps_hdrops++; 785308Sroot return; 794926Swnj } 805050Swnj ui = mtod(m, struct udpiphdr *); 815246Sroot if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) 825220Swnj ip_stripoptions((struct ip *)ui, (struct mbuf *)0); 834926Swnj 844926Swnj /* 855246Sroot * Make mbuf data length reflect UDP length. 865246Sroot * If not enough data to reflect UDP length, drop. 874926Swnj */ 887844Sroot len = ntohs((u_short)ui->ui_ulen); 894926Swnj if (((struct ip *)ui)->ip_len != len) { 904926Swnj if (len > ((struct ip *)ui)->ip_len) { 914926Swnj udpstat.udps_badlen++; 924926Swnj goto bad; 934926Swnj } 9415539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 9524823Skarels /* ((struct ip *)ui)->ip_len = len; */ 964926Swnj } 9724823Skarels /* 9824823Skarels * Save a copy of the IP header in case we want restore it for ICMP. 9924823Skarels */ 10024823Skarels ip = *(struct ip*)ui; 1014926Swnj 1024926Swnj /* 1035246Sroot * Checksum extended UDP header and data. 1044926Swnj */ 10515539Skarels if (udpcksum && ui->ui_sum) { 1064926Swnj ui->ui_next = ui->ui_prev = 0; 1074926Swnj ui->ui_x1 = 0; 10821112Skarels ui->ui_len = ui->ui_ulen; 1097844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 1104926Swnj udpstat.udps_badsum++; 1114901Swnj m_freem(m); 1124901Swnj return; 1134901Swnj } 1144901Swnj } 1154926Swnj 1164926Swnj /* 1177844Sroot * Locate pcb for datagram. 1184926Swnj */ 1194926Swnj inp = in_pcblookup(&udb, 1206029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 1216029Sroot INPLOOKUP_WILDCARD); 1226584Ssam if (inp == 0) { 12310144Ssam /* don't send ICMP response for broadcast packet */ 12421112Skarels if (in_broadcast(ui->ui_dst)) 1256584Ssam goto bad; 12624823Skarels *(struct ip *)ui = ip; 12726032Skarels icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT, 12826032Skarels ifp); 1296584Ssam return; 1306584Ssam } 1316584Ssam 1324926Swnj /* 1334926Swnj * Construct sockaddr format source address. 1344926Swnj * Stuff source address and datagram in user buffer. 1354926Swnj */ 1364926Swnj udp_in.sin_port = ui->ui_sport; 1374926Swnj udp_in.sin_addr = ui->ui_src; 1385050Swnj m->m_len -= sizeof (struct udpiphdr); 1395050Swnj m->m_off += sizeof (struct udpiphdr); 14012767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 14112767Ssam m, (struct mbuf *)0) == 0) 1424926Swnj goto bad; 1435050Swnj sorwakeup(inp->inp_socket); 1444887Swnj return; 1454926Swnj bad: 1464887Swnj m_freem(m); 1474784Swnj } 1484784Swnj 14926426Skarels /* 15026426Skarels * Notify a udp user of an asynchronous error; 15126426Skarels * just wake up so that he can collect error status. 15226426Skarels */ 15326426Skarels udp_notify(inp) 15426426Skarels register struct inpcb *inp; 15526426Skarels { 15626426Skarels 15726426Skarels sorwakeup(inp->inp_socket); 15826426Skarels sowwakeup(inp->inp_socket); 15926426Skarels } 16026426Skarels 16124823Skarels udp_ctlinput(cmd, sa) 1626591Ssam int cmd; 16324823Skarels struct sockaddr *sa; 1646591Ssam { 1656591Ssam extern u_char inetctlerrmap[]; 16624823Skarels struct sockaddr_in *sin; 16721121Skarels int in_rtchange(); 1686591Ssam 16924823Skarels if ((unsigned)cmd > PRC_NCMDS) 1706591Ssam return; 17124823Skarels if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 17224823Skarels return; 17324823Skarels sin = (struct sockaddr_in *)sa; 17424823Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 17524823Skarels return; 17624823Skarels 1776591Ssam switch (cmd) { 1786591Ssam 17924823Skarels case PRC_QUENCH: 1806591Ssam break; 1816591Ssam 18224823Skarels case PRC_ROUTEDEAD: 18321121Skarels case PRC_REDIRECT_NET: 18421121Skarels case PRC_REDIRECT_HOST: 18524823Skarels case PRC_REDIRECT_TOSNET: 18624823Skarels case PRC_REDIRECT_TOSHOST: 18724823Skarels in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 1886591Ssam break; 1896591Ssam 1906591Ssam default: 19121121Skarels if (inetctlerrmap[cmd] == 0) 19221121Skarels return; /* XXX */ 19324823Skarels in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 19426426Skarels udp_notify); 1956591Ssam } 1966591Ssam } 1976591Ssam 1984955Swnj udp_output(inp, m0) 19926060Skarels register struct inpcb *inp; 2004926Swnj struct mbuf *m0; 2014784Swnj { 2024926Swnj register struct mbuf *m; 2034926Swnj register struct udpiphdr *ui; 20417165Skarels register int len = 0; 2054784Swnj 2064926Swnj /* 2074926Swnj * Calculate data length and get a mbuf 2085246Sroot * for UDP and IP headers. 2094926Swnj */ 2104926Swnj for (m = m0; m; m = m->m_next) 2114926Swnj len += m->m_len; 21221112Skarels MGET(m, M_DONTWAIT, MT_HEADER); 2136507Ssam if (m == 0) { 2146507Ssam m_freem(m0); 2156507Ssam return (ENOBUFS); 2166507Ssam } 2174784Swnj 2184926Swnj /* 2195246Sroot * Fill in mbuf with extended UDP header 2204926Swnj * and addresses and length put into network format. 2214926Swnj */ 2224926Swnj m->m_off = MMAXOFF - sizeof (struct udpiphdr); 2234926Swnj m->m_len = sizeof (struct udpiphdr); 2244926Swnj m->m_next = m0; 2254926Swnj ui = mtod(m, struct udpiphdr *); 2264926Swnj ui->ui_next = ui->ui_prev = 0; 2274926Swnj ui->ui_x1 = 0; 2284926Swnj ui->ui_pr = IPPROTO_UDP; 22915226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2305050Swnj ui->ui_src = inp->inp_laddr; 2315050Swnj ui->ui_dst = inp->inp_faddr; 2325050Swnj ui->ui_sport = inp->inp_lport; 2335050Swnj ui->ui_dport = inp->inp_fport; 23415226Ssam ui->ui_ulen = ui->ui_len; 2354784Swnj 2364926Swnj /* 2374926Swnj * Stuff checksum and output datagram. 2384926Swnj */ 2394926Swnj ui->ui_sum = 0; 24019519Skarels if (udpcksum) { 24119519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 24233715Skarels ui->ui_sum = 0xffff; 24321112Skarels } 2445050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 24531398Skarels ((struct ip *)ui)->ip_ttl = udp_ttl; 24626060Skarels return (ip_output(m, inp->inp_options, &inp->inp_route, 24726060Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 2484784Swnj } 2494784Swnj 25034500Skarels u_long udp_sendspace = 2048; /* really max datagram size */ 25134500Skarels u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 25218368Skarels 2538602Sroot /*ARGSUSED*/ 25412767Ssam udp_usrreq(so, req, m, nam, rights) 2554887Swnj struct socket *so; 2564784Swnj int req; 25712767Ssam struct mbuf *m, *nam, *rights; 2584784Swnj { 2594887Swnj struct inpcb *inp = sotoinpcb(so); 2606507Ssam int error = 0; 2614784Swnj 26218368Skarels if (req == PRU_CONTROL) 26318368Skarels return (in_control(so, (int)m, (caddr_t)nam, 26418368Skarels (struct ifnet *)rights)); 26512767Ssam if (rights && rights->m_len) { 26612767Ssam error = EINVAL; 26712767Ssam goto release; 26812767Ssam } 26911080Ssam if (inp == NULL && req != PRU_ATTACH) { 27011080Ssam error = EINVAL; 27111080Ssam goto release; 27211080Ssam } 2734784Swnj switch (req) { 2744784Swnj 2754784Swnj case PRU_ATTACH: 27611080Ssam if (inp != NULL) { 27711080Ssam error = EINVAL; 27811080Ssam break; 27911080Ssam } 2808273Sroot error = in_pcballoc(so, &udb); 2818273Sroot if (error) 2828273Sroot break; 28318368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 2848273Sroot if (error) 2858273Sroot break; 2864887Swnj break; 2874784Swnj 2884784Swnj case PRU_DETACH: 2895166Swnj in_pcbdetach(inp); 2904887Swnj break; 2914784Swnj 2928273Sroot case PRU_BIND: 2938273Sroot error = in_pcbbind(inp, nam); 2948273Sroot break; 2958273Sroot 2968273Sroot case PRU_LISTEN: 2978273Sroot error = EOPNOTSUPP; 2988273Sroot break; 2998273Sroot 3004784Swnj case PRU_CONNECT: 30111080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 30211080Ssam error = EISCONN; 30311080Ssam break; 30411080Ssam } 3058273Sroot error = in_pcbconnect(inp, nam); 3066507Ssam if (error == 0) 3076507Ssam soisconnected(so); 3084887Swnj break; 3094784Swnj 31013118Ssam case PRU_CONNECT2: 31113118Ssam error = EOPNOTSUPP; 31213118Ssam break; 31313118Ssam 3144926Swnj case PRU_ACCEPT: 31511080Ssam error = EOPNOTSUPP; 31611080Ssam break; 3174926Swnj 3184784Swnj case PRU_DISCONNECT: 31911080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 32011080Ssam error = ENOTCONN; 32111080Ssam break; 32211080Ssam } 3235166Swnj in_pcbdisconnect(inp); 32426404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 3254784Swnj break; 3264784Swnj 3274912Swnj case PRU_SHUTDOWN: 3284912Swnj socantsendmore(so); 3294912Swnj break; 3304912Swnj 3315996Swnj case PRU_SEND: { 3325996Swnj struct in_addr laddr; 33316799Skarels int s; 3345996Swnj 3358273Sroot if (nam) { 3365996Swnj laddr = inp->inp_laddr; 33711080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 33811080Ssam error = EISCONN; 33911080Ssam break; 34011080Ssam } 34116799Skarels /* 34216799Skarels * Must block input while temporarily connected. 34316799Skarels */ 34416799Skarels s = splnet(); 3458273Sroot error = in_pcbconnect(inp, nam); 34616799Skarels if (error) { 34716799Skarels splx(s); 3486507Ssam break; 34916799Skarels } 3504955Swnj } else { 35111080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 35211080Ssam error = ENOTCONN; 35311080Ssam break; 35411080Ssam } 3554955Swnj } 3566507Ssam error = udp_output(inp, m); 35711080Ssam m = NULL; 3588273Sroot if (nam) { 3595166Swnj in_pcbdisconnect(inp); 36018368Skarels inp->inp_laddr = laddr; 36116799Skarels splx(s); 3625996Swnj } 3635996Swnj } 3644784Swnj break; 3654784Swnj 3664784Swnj case PRU_ABORT: 36731750Skarels soisdisconnected(so); 3685166Swnj in_pcbdetach(inp); 3694784Swnj break; 3704784Swnj 3716511Ssam case PRU_SOCKADDR: 3728273Sroot in_setsockaddr(inp, nam); 3736511Ssam break; 3746511Ssam 37514124Ssam case PRU_PEERADDR: 37614124Ssam in_setpeeraddr(inp, nam); 37714124Ssam break; 37814124Ssam 37916988Skarels case PRU_SENSE: 38016988Skarels /* 38116988Skarels * stat: don't bother with a blocksize. 38216988Skarels */ 38316988Skarels return (0); 38416988Skarels 38512205Ssam case PRU_SENDOOB: 38612205Ssam case PRU_FASTTIMO: 38712205Ssam case PRU_SLOWTIMO: 38812205Ssam case PRU_PROTORCV: 38912205Ssam case PRU_PROTOSEND: 39012205Ssam error = EOPNOTSUPP; 39112205Ssam break; 39213118Ssam 39316799Skarels case PRU_RCVD: 39416799Skarels case PRU_RCVOOB: 39516799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 39616799Skarels 39713118Ssam default: 39813118Ssam panic("udp_usrreq"); 3994805Swnj } 40011080Ssam release: 40111080Ssam if (m != NULL) 40211080Ssam m_freem(m); 4036507Ssam return (error); 4044784Swnj } 405