123199Smckusick /* 229158Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 3*32789Sbostic * All rights reserved. 423199Smckusick * 5*32789Sbostic * Redistribution and use in source and binary forms are permitted 6*32789Sbostic * provided that this notice is preserved and that due credit is given 7*32789Sbostic * to the University of California at Berkeley. The name of the University 8*32789Sbostic * may not be used to endorse or promote products derived from this 9*32789Sbostic * software without specific prior written permission. This software 10*32789Sbostic * is provided ``as is'' without express or implied warranty. 11*32789Sbostic * 12*32789Sbostic * @(#)udp_usrreq.c 7.4 (Berkeley) 12/07/87 1323199Smckusick */ 144784Swnj 1517065Sbloom #include "param.h" 1617065Sbloom #include "dir.h" 1717065Sbloom #include "user.h" 1817065Sbloom #include "mbuf.h" 1917065Sbloom #include "protosw.h" 2017065Sbloom #include "socket.h" 2117065Sbloom #include "socketvar.h" 2217065Sbloom #include "errno.h" 2317065Sbloom #include "stat.h" 2410897Ssam 256584Ssam #include "../net/if.h" 266354Ssam #include "../net/route.h" 2710897Ssam 2817065Sbloom #include "in.h" 2917065Sbloom #include "in_pcb.h" 3017065Sbloom #include "in_systm.h" 3117065Sbloom #include "ip.h" 3217065Sbloom #include "ip_var.h" 3317065Sbloom #include "ip_icmp.h" 3417065Sbloom #include "udp.h" 3517065Sbloom #include "udp_var.h" 364784Swnj 374926Swnj /* 384926Swnj * UDP protocol implementation. 394926Swnj * Per RFC 768, August, 1980. 404926Swnj */ 414805Swnj udp_init() 424805Swnj { 434805Swnj 444901Swnj udb.inp_next = udb.inp_prev = &udb; 454805Swnj } 464805Swnj 4726118Skarels #ifndef COMPAT_42 4819519Skarels int udpcksum = 1; 4926118Skarels #else 5026118Skarels int udpcksum = 0; /* XXX */ 5126118Skarels #endif 5231398Skarels int udp_ttl = UDP_TTL; 5326118Skarels 544926Swnj struct sockaddr_in udp_in = { AF_INET }; 554901Swnj 5626032Skarels udp_input(m0, ifp) 574926Swnj struct mbuf *m0; 5826032Skarels struct ifnet *ifp; 594784Swnj { 604901Swnj register struct udpiphdr *ui; 614887Swnj register struct inpcb *inp; 624926Swnj register struct mbuf *m; 637844Sroot int len; 6424823Skarels struct ip ip; 654784Swnj 664926Swnj /* 675246Sroot * Get IP and UDP header together in first mbuf. 684926Swnj */ 694926Swnj m = m0; 705308Sroot if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) && 715308Sroot (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 724926Swnj udpstat.udps_hdrops++; 735308Sroot return; 744926Swnj } 755050Swnj ui = mtod(m, struct udpiphdr *); 765246Sroot if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) 775220Swnj ip_stripoptions((struct ip *)ui, (struct mbuf *)0); 784926Swnj 794926Swnj /* 805246Sroot * Make mbuf data length reflect UDP length. 815246Sroot * If not enough data to reflect UDP length, drop. 824926Swnj */ 837844Sroot len = ntohs((u_short)ui->ui_ulen); 844926Swnj if (((struct ip *)ui)->ip_len != len) { 854926Swnj if (len > ((struct ip *)ui)->ip_len) { 864926Swnj udpstat.udps_badlen++; 874926Swnj goto bad; 884926Swnj } 8915539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 9024823Skarels /* ((struct ip *)ui)->ip_len = len; */ 914926Swnj } 9224823Skarels /* 9324823Skarels * Save a copy of the IP header in case we want restore it for ICMP. 9424823Skarels */ 9524823Skarels ip = *(struct ip*)ui; 964926Swnj 974926Swnj /* 985246Sroot * Checksum extended UDP header and data. 994926Swnj */ 10015539Skarels if (udpcksum && ui->ui_sum) { 1014926Swnj ui->ui_next = ui->ui_prev = 0; 1024926Swnj ui->ui_x1 = 0; 10321112Skarels ui->ui_len = ui->ui_ulen; 1047844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 1054926Swnj udpstat.udps_badsum++; 1064901Swnj m_freem(m); 1074901Swnj return; 1084901Swnj } 1094901Swnj } 1104926Swnj 1114926Swnj /* 1127844Sroot * Locate pcb for datagram. 1134926Swnj */ 1144926Swnj inp = in_pcblookup(&udb, 1156029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 1166029Sroot INPLOOKUP_WILDCARD); 1176584Ssam if (inp == 0) { 11810144Ssam /* don't send ICMP response for broadcast packet */ 11921112Skarels if (in_broadcast(ui->ui_dst)) 1206584Ssam goto bad; 12124823Skarels *(struct ip *)ui = ip; 12226032Skarels icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT, 12326032Skarels ifp); 1246584Ssam return; 1256584Ssam } 1266584Ssam 1274926Swnj /* 1284926Swnj * Construct sockaddr format source address. 1294926Swnj * Stuff source address and datagram in user buffer. 1304926Swnj */ 1314926Swnj udp_in.sin_port = ui->ui_sport; 1324926Swnj udp_in.sin_addr = ui->ui_src; 1335050Swnj m->m_len -= sizeof (struct udpiphdr); 1345050Swnj m->m_off += sizeof (struct udpiphdr); 13512767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 13612767Ssam m, (struct mbuf *)0) == 0) 1374926Swnj goto bad; 1385050Swnj sorwakeup(inp->inp_socket); 1394887Swnj return; 1404926Swnj bad: 1414887Swnj m_freem(m); 1424784Swnj } 1434784Swnj 14426426Skarels /* 14526426Skarels * Notify a udp user of an asynchronous error; 14626426Skarels * just wake up so that he can collect error status. 14726426Skarels */ 14826426Skarels udp_notify(inp) 14926426Skarels register struct inpcb *inp; 15026426Skarels { 15126426Skarels 15226426Skarels sorwakeup(inp->inp_socket); 15326426Skarels sowwakeup(inp->inp_socket); 15426426Skarels } 15526426Skarels 15624823Skarels udp_ctlinput(cmd, sa) 1576591Ssam int cmd; 15824823Skarels struct sockaddr *sa; 1596591Ssam { 1606591Ssam extern u_char inetctlerrmap[]; 16124823Skarels struct sockaddr_in *sin; 16221121Skarels int in_rtchange(); 1636591Ssam 16424823Skarels if ((unsigned)cmd > PRC_NCMDS) 1656591Ssam return; 16624823Skarels if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 16724823Skarels return; 16824823Skarels sin = (struct sockaddr_in *)sa; 16924823Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 17024823Skarels return; 17124823Skarels 1726591Ssam switch (cmd) { 1736591Ssam 17424823Skarels case PRC_QUENCH: 1756591Ssam break; 1766591Ssam 17724823Skarels case PRC_ROUTEDEAD: 17821121Skarels case PRC_REDIRECT_NET: 17921121Skarels case PRC_REDIRECT_HOST: 18024823Skarels case PRC_REDIRECT_TOSNET: 18124823Skarels case PRC_REDIRECT_TOSHOST: 18224823Skarels in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 1836591Ssam break; 1846591Ssam 1856591Ssam default: 18621121Skarels if (inetctlerrmap[cmd] == 0) 18721121Skarels return; /* XXX */ 18824823Skarels in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 18926426Skarels udp_notify); 1906591Ssam } 1916591Ssam } 1926591Ssam 1934955Swnj udp_output(inp, m0) 19426060Skarels register struct inpcb *inp; 1954926Swnj struct mbuf *m0; 1964784Swnj { 1974926Swnj register struct mbuf *m; 1984926Swnj register struct udpiphdr *ui; 19917165Skarels register int len = 0; 2004784Swnj 2014926Swnj /* 2024926Swnj * Calculate data length and get a mbuf 2035246Sroot * for UDP and IP headers. 2044926Swnj */ 2054926Swnj for (m = m0; m; m = m->m_next) 2064926Swnj len += m->m_len; 20721112Skarels MGET(m, M_DONTWAIT, MT_HEADER); 2086507Ssam if (m == 0) { 2096507Ssam m_freem(m0); 2106507Ssam return (ENOBUFS); 2116507Ssam } 2124784Swnj 2134926Swnj /* 2145246Sroot * Fill in mbuf with extended UDP header 2154926Swnj * and addresses and length put into network format. 2164926Swnj */ 2174926Swnj m->m_off = MMAXOFF - sizeof (struct udpiphdr); 2184926Swnj m->m_len = sizeof (struct udpiphdr); 2194926Swnj m->m_next = m0; 2204926Swnj ui = mtod(m, struct udpiphdr *); 2214926Swnj ui->ui_next = ui->ui_prev = 0; 2224926Swnj ui->ui_x1 = 0; 2234926Swnj ui->ui_pr = IPPROTO_UDP; 22415226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2255050Swnj ui->ui_src = inp->inp_laddr; 2265050Swnj ui->ui_dst = inp->inp_faddr; 2275050Swnj ui->ui_sport = inp->inp_lport; 2285050Swnj ui->ui_dport = inp->inp_fport; 22915226Ssam ui->ui_ulen = ui->ui_len; 2304784Swnj 2314926Swnj /* 2324926Swnj * Stuff checksum and output datagram. 2334926Swnj */ 2344926Swnj ui->ui_sum = 0; 23519519Skarels if (udpcksum) { 23619519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 23715539Skarels ui->ui_sum = -1; 23821112Skarels } 2395050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 24031398Skarels ((struct ip *)ui)->ip_ttl = udp_ttl; 24126060Skarels return (ip_output(m, inp->inp_options, &inp->inp_route, 24226060Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 2434784Swnj } 2444784Swnj 24518368Skarels int udp_sendspace = 2048; /* really max datagram size */ 24618368Skarels int udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 24718368Skarels 2488602Sroot /*ARGSUSED*/ 24912767Ssam udp_usrreq(so, req, m, nam, rights) 2504887Swnj struct socket *so; 2514784Swnj int req; 25212767Ssam struct mbuf *m, *nam, *rights; 2534784Swnj { 2544887Swnj struct inpcb *inp = sotoinpcb(so); 2556507Ssam int error = 0; 2564784Swnj 25718368Skarels if (req == PRU_CONTROL) 25818368Skarels return (in_control(so, (int)m, (caddr_t)nam, 25918368Skarels (struct ifnet *)rights)); 26012767Ssam if (rights && rights->m_len) { 26112767Ssam error = EINVAL; 26212767Ssam goto release; 26312767Ssam } 26411080Ssam if (inp == NULL && req != PRU_ATTACH) { 26511080Ssam error = EINVAL; 26611080Ssam goto release; 26711080Ssam } 2684784Swnj switch (req) { 2694784Swnj 2704784Swnj case PRU_ATTACH: 27111080Ssam if (inp != NULL) { 27211080Ssam error = EINVAL; 27311080Ssam break; 27411080Ssam } 2758273Sroot error = in_pcballoc(so, &udb); 2768273Sroot if (error) 2778273Sroot break; 27818368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 2798273Sroot if (error) 2808273Sroot break; 2814887Swnj break; 2824784Swnj 2834784Swnj case PRU_DETACH: 2845166Swnj in_pcbdetach(inp); 2854887Swnj break; 2864784Swnj 2878273Sroot case PRU_BIND: 2888273Sroot error = in_pcbbind(inp, nam); 2898273Sroot break; 2908273Sroot 2918273Sroot case PRU_LISTEN: 2928273Sroot error = EOPNOTSUPP; 2938273Sroot break; 2948273Sroot 2954784Swnj case PRU_CONNECT: 29611080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 29711080Ssam error = EISCONN; 29811080Ssam break; 29911080Ssam } 3008273Sroot error = in_pcbconnect(inp, nam); 3016507Ssam if (error == 0) 3026507Ssam soisconnected(so); 3034887Swnj break; 3044784Swnj 30513118Ssam case PRU_CONNECT2: 30613118Ssam error = EOPNOTSUPP; 30713118Ssam break; 30813118Ssam 3094926Swnj case PRU_ACCEPT: 31011080Ssam error = EOPNOTSUPP; 31111080Ssam break; 3124926Swnj 3134784Swnj case PRU_DISCONNECT: 31411080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 31511080Ssam error = ENOTCONN; 31611080Ssam break; 31711080Ssam } 3185166Swnj in_pcbdisconnect(inp); 31926404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 3204784Swnj break; 3214784Swnj 3224912Swnj case PRU_SHUTDOWN: 3234912Swnj socantsendmore(so); 3244912Swnj break; 3254912Swnj 3265996Swnj case PRU_SEND: { 3275996Swnj struct in_addr laddr; 32816799Skarels int s; 3295996Swnj 3308273Sroot if (nam) { 3315996Swnj laddr = inp->inp_laddr; 33211080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 33311080Ssam error = EISCONN; 33411080Ssam break; 33511080Ssam } 33616799Skarels /* 33716799Skarels * Must block input while temporarily connected. 33816799Skarels */ 33916799Skarels s = splnet(); 3408273Sroot error = in_pcbconnect(inp, nam); 34116799Skarels if (error) { 34216799Skarels splx(s); 3436507Ssam break; 34416799Skarels } 3454955Swnj } else { 34611080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 34711080Ssam error = ENOTCONN; 34811080Ssam break; 34911080Ssam } 3504955Swnj } 3516507Ssam error = udp_output(inp, m); 35211080Ssam m = NULL; 3538273Sroot if (nam) { 3545166Swnj in_pcbdisconnect(inp); 35518368Skarels inp->inp_laddr = laddr; 35616799Skarels splx(s); 3575996Swnj } 3585996Swnj } 3594784Swnj break; 3604784Swnj 3614784Swnj case PRU_ABORT: 36231750Skarels soisdisconnected(so); 3635166Swnj in_pcbdetach(inp); 3644784Swnj break; 3654784Swnj 3666511Ssam case PRU_SOCKADDR: 3678273Sroot in_setsockaddr(inp, nam); 3686511Ssam break; 3696511Ssam 37014124Ssam case PRU_PEERADDR: 37114124Ssam in_setpeeraddr(inp, nam); 37214124Ssam break; 37314124Ssam 37416988Skarels case PRU_SENSE: 37516988Skarels /* 37616988Skarels * stat: don't bother with a blocksize. 37716988Skarels */ 37816988Skarels return (0); 37916988Skarels 38012205Ssam case PRU_SENDOOB: 38112205Ssam case PRU_FASTTIMO: 38212205Ssam case PRU_SLOWTIMO: 38312205Ssam case PRU_PROTORCV: 38412205Ssam case PRU_PROTOSEND: 38512205Ssam error = EOPNOTSUPP; 38612205Ssam break; 38713118Ssam 38816799Skarels case PRU_RCVD: 38916799Skarels case PRU_RCVOOB: 39016799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 39116799Skarels 39213118Ssam default: 39313118Ssam panic("udp_usrreq"); 3944805Swnj } 39511080Ssam release: 39611080Ssam if (m != NULL) 39711080Ssam m_freem(m); 3986507Ssam return (error); 3994784Swnj } 400