123199Smckusick /* 2*44596Skarels * Copyright (c) 1982, 1986, 1988, 1990 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*44596Skarels * @(#)udp_usrreq.c 7.15 (Berkeley) 06/29/90 1823199Smckusick */ 194784Swnj 2017065Sbloom #include "param.h" 2117065Sbloom #include "user.h" 2235794Skarels #include "malloc.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_systm.h" 3517065Sbloom #include "ip.h" 3640688Skarels #include "in_pcb.h" 3717065Sbloom #include "ip_var.h" 3817065Sbloom #include "ip_icmp.h" 3917065Sbloom #include "udp.h" 4017065Sbloom #include "udp_var.h" 414784Swnj 42*44596Skarels struct inpcb *udp_last_inpcb = &udb; 43*44596Skarels 444926Swnj /* 454926Swnj * UDP protocol implementation. 464926Swnj * Per RFC 768, August, 1980. 474926Swnj */ 484805Swnj udp_init() 494805Swnj { 504805Swnj 514901Swnj udb.inp_next = udb.inp_prev = &udb; 524805Swnj } 534805Swnj 5426118Skarels #ifndef COMPAT_42 5519519Skarels int udpcksum = 1; 5626118Skarels #else 5726118Skarels int udpcksum = 0; /* XXX */ 5826118Skarels #endif 5931398Skarels int udp_ttl = UDP_TTL; 6026118Skarels 6137324Skarels struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 624901Swnj 6335794Skarels udp_input(m, iphlen) 6435794Skarels register struct mbuf *m; 6535794Skarels int iphlen; 664784Swnj { 6740688Skarels register struct ip *ip; 6840688Skarels register struct udphdr *uh; 694887Swnj register struct inpcb *inp; 70*44596Skarels struct mbuf *opts = 0; 717844Sroot int len; 7240688Skarels struct ip save_ip; 734784Swnj 74*44596Skarels udpstat.udps_ipackets++; 75*44596Skarels #ifndef notyet /* should skip this, make available & use on returned packets */ 7640688Skarels if (iphlen > sizeof (struct ip)) 7740688Skarels ip_stripoptions(m, (struct mbuf *)0); 7840688Skarels #endif 794926Swnj /* 805246Sroot * Get IP and UDP header together in first mbuf. 814926Swnj */ 8240688Skarels ip = mtod(m, struct ip *); 8340688Skarels if (m->m_len < iphlen + sizeof(struct udphdr)) { 8440688Skarels if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 8535288Skarels udpstat.udps_hdrops++; 8635288Skarels return; 8735288Skarels } 8840688Skarels ip = mtod(m, struct ip *); 8935288Skarels } 9040688Skarels uh = (struct udphdr *)((caddr_t)ip + iphlen); 914926Swnj 924926Swnj /* 935246Sroot * Make mbuf data length reflect UDP length. 945246Sroot * If not enough data to reflect UDP length, drop. 954926Swnj */ 9640688Skarels len = ntohs((u_short)uh->uh_ulen); 9740688Skarels if (ip->ip_len != len) { 9840688Skarels if (len > ip->ip_len) { 994926Swnj udpstat.udps_badlen++; 1004926Swnj goto bad; 1014926Swnj } 10240688Skarels m_adj(m, len - ip->ip_len); 10340688Skarels /* ip->ip_len = len; */ 1044926Swnj } 10524823Skarels /* 10640688Skarels * Save a copy of the IP header in case we want restore it 10740688Skarels * for sending an ICMP error message in response. 10824823Skarels */ 10940688Skarels save_ip = *ip; 1104926Swnj 1114926Swnj /* 1125246Sroot * Checksum extended UDP header and data. 1134926Swnj */ 11440688Skarels if (udpcksum && uh->uh_sum) { 11540688Skarels ((struct ipovly *)ip)->ih_next = 0; 11640688Skarels ((struct ipovly *)ip)->ih_prev = 0; 11740688Skarels ((struct ipovly *)ip)->ih_x1 = 0; 11840688Skarels ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 11940688Skarels if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { 1204926Swnj udpstat.udps_badsum++; 1214901Swnj m_freem(m); 1224901Swnj return; 1234901Swnj } 1244901Swnj } 1254926Swnj 1264926Swnj /* 1277844Sroot * Locate pcb for datagram. 1284926Swnj */ 129*44596Skarels inp = udp_last_inpcb; 130*44596Skarels if (inp->inp_lport != uh->uh_dport || 131*44596Skarels inp->inp_fport != uh->uh_sport || 132*44596Skarels inp->inp_faddr.s_addr != ip->ip_src.s_addr || 133*44596Skarels inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 134*44596Skarels inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 135*44596Skarels ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 136*44596Skarels if (inp) 137*44596Skarels udp_last_inpcb = inp; 138*44596Skarels udpstat.udpps_pcbcachemiss++; 139*44596Skarels } 1406584Ssam if (inp == 0) { 14110144Ssam /* don't send ICMP response for broadcast packet */ 14240688Skarels udpstat.udps_noport++; 143*44596Skarels if (m->m_flags & M_BCAST) { 144*44596Skarels udpstat.udps_noportbcast++; 1456584Ssam goto bad; 146*44596Skarels } 14740688Skarels *ip = save_ip; 14835794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1496584Ssam return; 1506584Ssam } 1516584Ssam 1524926Swnj /* 1534926Swnj * Construct sockaddr format source address. 1544926Swnj * Stuff source address and datagram in user buffer. 1554926Swnj */ 15640688Skarels udp_in.sin_port = uh->uh_sport; 15740688Skarels udp_in.sin_addr = ip->ip_src; 158*44596Skarels if (inp->inp_flags & INP_CONTROLOPTS) { 159*44596Skarels struct mbuf **mp = &opts; 160*44596Skarels struct mbuf *udp_saveopt(); 161*44596Skarels 162*44596Skarels if (inp->inp_flags & INP_RECVDSTADDR) { 163*44596Skarels *mp = udp_saveopt((caddr_t) &ip->ip_dst, 164*44596Skarels sizeof(struct in_addr), IP_RECVDSTADDR); 165*44596Skarels if (*mp) 166*44596Skarels mp = &(*mp)->m_next; 167*44596Skarels } 168*44596Skarels #ifdef notyet 169*44596Skarels /* options were tossed above */ 170*44596Skarels if (inp->inp_flags & INP_RECVOPTS) { 171*44596Skarels *mp = udp_saveopt((caddr_t) opts_deleted_above, 172*44596Skarels sizeof(struct in_addr), IP_RECVOPTS); 173*44596Skarels if (*mp) 174*44596Skarels mp = &(*mp)->m_next; 175*44596Skarels } 176*44596Skarels /* ip_srcroute doesn't do what we want here, need to fix */ 177*44596Skarels if (inp->inp_flags & INP_RECVRETOPTS) { 178*44596Skarels *mp = udp_saveopt((caddr_t) ip_srcroute(), 179*44596Skarels sizeof(struct in_addr), IP_RECVRETOPTS); 180*44596Skarels if (*mp) 181*44596Skarels mp = &(*mp)->m_next; 182*44596Skarels } 183*44596Skarels #endif 184*44596Skarels } 18540688Skarels iphlen = sizeof(struct ip); 18640688Skarels iphlen += sizeof(struct udphdr); 18740688Skarels m->m_len -= iphlen; 18840688Skarels m->m_pkthdr.len -= iphlen; 18940688Skarels m->m_data += iphlen; 19012767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 191*44596Skarels m, opts) == 0) { 192*44596Skarels udpstat.udps_fullsock++; 1934926Swnj goto bad; 194*44596Skarels } 1955050Swnj sorwakeup(inp->inp_socket); 1964887Swnj return; 1974926Swnj bad: 1984887Swnj m_freem(m); 199*44596Skarels if (opts) 200*44596Skarels m_freem(opts); 2014784Swnj } 2024784Swnj 20326426Skarels /* 204*44596Skarels * Create a "control" mbuf containing the specified data 205*44596Skarels * with the specified type for presentation with a datagram. 206*44596Skarels */ 207*44596Skarels struct mbuf * 208*44596Skarels udp_saveopt(p, size, type) 209*44596Skarels caddr_t p; 210*44596Skarels register int size; 211*44596Skarels int type; 212*44596Skarels { 213*44596Skarels register struct cmsghdr *cp; 214*44596Skarels struct mbuf *m; 215*44596Skarels 216*44596Skarels if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 217*44596Skarels return ((struct mbuf *) NULL); 218*44596Skarels cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 219*44596Skarels bcopy(p, (caddr_t)cp + size, size); 220*44596Skarels size += sizeof(*cp); 221*44596Skarels m->m_len = size; 222*44596Skarels cp->cmsg_len = size; 223*44596Skarels cp->cmsg_level = IPPROTO_IP; 224*44596Skarels cp->cmsg_type = type; 225*44596Skarels return (m); 226*44596Skarels } 227*44596Skarels 228*44596Skarels /* 22926426Skarels * Notify a udp user of an asynchronous error; 23026426Skarels * just wake up so that he can collect error status. 23126426Skarels */ 232*44596Skarels udp_notify(inp, errno) 23326426Skarels register struct inpcb *inp; 23426426Skarels { 23526426Skarels 236*44596Skarels inp->inp_socket->so_error = errno; 23726426Skarels sorwakeup(inp->inp_socket); 23826426Skarels sowwakeup(inp->inp_socket); 23926426Skarels } 24026426Skarels 24140688Skarels udp_ctlinput(cmd, sa, ip) 2426591Ssam int cmd; 24324823Skarels struct sockaddr *sa; 24440688Skarels register struct ip *ip; 2456591Ssam { 24640688Skarels register struct udphdr *uh; 24740688Skarels extern struct in_addr zeroin_addr; 2486591Ssam extern u_char inetctlerrmap[]; 2496591Ssam 25040688Skarels if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 2516591Ssam return; 25240688Skarels if (ip) { 25340688Skarels uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 25440688Skarels in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 25540688Skarels cmd, udp_notify); 25640688Skarels } else 25740688Skarels in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 2586591Ssam } 2596591Ssam 26042184Skarels udp_output(inp, m, addr, control) 26126060Skarels register struct inpcb *inp; 26235794Skarels register struct mbuf *m; 26342184Skarels struct mbuf *addr, *control; 2644784Swnj { 2654926Swnj register struct udpiphdr *ui; 26635794Skarels register int len = m->m_pkthdr.len; 26742184Skarels struct in_addr laddr; 26842184Skarels int s, error = 0; 2694784Swnj 27042184Skarels if (control) 27142184Skarels m_freem(control); /* XXX */ 27242184Skarels 27342184Skarels if (addr) { 27442184Skarels laddr = inp->inp_laddr; 27542184Skarels if (inp->inp_faddr.s_addr != INADDR_ANY) { 27642184Skarels error = EISCONN; 27742184Skarels goto release; 27842184Skarels } 27942184Skarels /* 28042184Skarels * Must block input while temporarily connected. 28142184Skarels */ 28242184Skarels s = splnet(); 28342184Skarels error = in_pcbconnect(inp, addr); 28442184Skarels if (error) { 28542184Skarels splx(s); 28642184Skarels goto release; 28742184Skarels } 28842184Skarels } else { 28942184Skarels if (inp->inp_faddr.s_addr == INADDR_ANY) { 29042184Skarels error = ENOTCONN; 29142184Skarels goto release; 29242184Skarels } 29342184Skarels } 2944926Swnj /* 2954926Swnj * Calculate data length and get a mbuf 2965246Sroot * for UDP and IP headers. 2974926Swnj */ 29835794Skarels M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 2994784Swnj 3004926Swnj /* 3015246Sroot * Fill in mbuf with extended UDP header 3024926Swnj * and addresses and length put into network format. 3034926Swnj */ 3044926Swnj ui = mtod(m, struct udpiphdr *); 3054926Swnj ui->ui_next = ui->ui_prev = 0; 3064926Swnj ui->ui_x1 = 0; 3074926Swnj ui->ui_pr = IPPROTO_UDP; 30815226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 3095050Swnj ui->ui_src = inp->inp_laddr; 3105050Swnj ui->ui_dst = inp->inp_faddr; 3115050Swnj ui->ui_sport = inp->inp_lport; 3125050Swnj ui->ui_dport = inp->inp_fport; 31315226Ssam ui->ui_ulen = ui->ui_len; 3144784Swnj 3154926Swnj /* 3164926Swnj * Stuff checksum and output datagram. 3174926Swnj */ 3184926Swnj ui->ui_sum = 0; 31919519Skarels if (udpcksum) { 32019519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 32133715Skarels ui->ui_sum = 0xffff; 32221112Skarels } 3235050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 324*44596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 325*44596Skarels ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 32640688Skarels udpstat.udps_opackets++; 32742184Skarels error = ip_output(m, inp->inp_options, &inp->inp_route, 32842184Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 32942184Skarels 33042184Skarels if (addr) { 33142184Skarels in_pcbdisconnect(inp); 33242184Skarels inp->inp_laddr = laddr; 33342184Skarels splx(s); 33442184Skarels } 33542184Skarels return (error); 33642184Skarels 33742184Skarels release: 33842184Skarels m_freem(m); 33942184Skarels return (error); 3404784Swnj } 3414784Swnj 34238354Smckusick u_long udp_sendspace = 9216; /* really max datagram size */ 34338354Smckusick u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 34438354Smckusick /* 40 1K datagrams */ 34518368Skarels 3468602Sroot /*ARGSUSED*/ 34742184Skarels udp_usrreq(so, req, m, addr, control) 3484887Swnj struct socket *so; 3494784Swnj int req; 35042184Skarels struct mbuf *m, *addr, *control; 3514784Swnj { 3524887Swnj struct inpcb *inp = sotoinpcb(so); 3536507Ssam int error = 0; 354*44596Skarels int s; 3554784Swnj 35618368Skarels if (req == PRU_CONTROL) 35742184Skarels return (in_control(so, (int)m, (caddr_t)addr, 35840688Skarels (struct ifnet *)control)); 35911080Ssam if (inp == NULL && req != PRU_ATTACH) { 36011080Ssam error = EINVAL; 36111080Ssam goto release; 36211080Ssam } 363*44596Skarels /* 364*44596Skarels * block udp_input while changing udp pcb queue, 365*44596Skarels * addresses; should be done for individual cases, 366*44596Skarels * but it's not worth it. 367*44596Skarels */ 368*44596Skarels s = splnet(); 3694784Swnj switch (req) { 3704784Swnj 3714784Swnj case PRU_ATTACH: 37211080Ssam if (inp != NULL) { 37311080Ssam error = EINVAL; 37411080Ssam break; 37511080Ssam } 376*44596Skarels s = splnet(); 3778273Sroot error = in_pcballoc(so, &udb); 378*44596Skarels splx(s); 3798273Sroot if (error) 3808273Sroot break; 38118368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3828273Sroot if (error) 3838273Sroot break; 384*44596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3854887Swnj break; 3864784Swnj 3874784Swnj case PRU_DETACH: 3885166Swnj in_pcbdetach(inp); 3894887Swnj break; 3904784Swnj 3918273Sroot case PRU_BIND: 39242184Skarels error = in_pcbbind(inp, addr); 3938273Sroot break; 3948273Sroot 3958273Sroot case PRU_LISTEN: 3968273Sroot error = EOPNOTSUPP; 3978273Sroot break; 3988273Sroot 3994784Swnj case PRU_CONNECT: 40011080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 40111080Ssam error = EISCONN; 40211080Ssam break; 40311080Ssam } 40442184Skarels error = in_pcbconnect(inp, addr); 4056507Ssam if (error == 0) 4066507Ssam soisconnected(so); 4074887Swnj break; 4084784Swnj 40913118Ssam case PRU_CONNECT2: 41013118Ssam error = EOPNOTSUPP; 41113118Ssam break; 41213118Ssam 4134926Swnj case PRU_ACCEPT: 41411080Ssam error = EOPNOTSUPP; 41511080Ssam break; 4164926Swnj 4174784Swnj case PRU_DISCONNECT: 41811080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 41911080Ssam error = ENOTCONN; 42011080Ssam break; 42111080Ssam } 4225166Swnj in_pcbdisconnect(inp); 42340688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 42426404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4254784Swnj break; 4264784Swnj 4274912Swnj case PRU_SHUTDOWN: 4284912Swnj socantsendmore(so); 4294912Swnj break; 4304912Swnj 43142184Skarels case PRU_SEND: 432*44596Skarels splx(s); 433*44596Skarels return (udp_output(inp, m, addr, control)); 4344784Swnj 4354784Swnj case PRU_ABORT: 43631750Skarels soisdisconnected(so); 4375166Swnj in_pcbdetach(inp); 4384784Swnj break; 4394784Swnj 4406511Ssam case PRU_SOCKADDR: 44142184Skarels in_setsockaddr(inp, addr); 4426511Ssam break; 4436511Ssam 44414124Ssam case PRU_PEERADDR: 44542184Skarels in_setpeeraddr(inp, addr); 44614124Ssam break; 44714124Ssam 44816988Skarels case PRU_SENSE: 44916988Skarels /* 45016988Skarels * stat: don't bother with a blocksize. 45116988Skarels */ 45216988Skarels return (0); 45316988Skarels 45412205Ssam case PRU_SENDOOB: 45512205Ssam case PRU_FASTTIMO: 45612205Ssam case PRU_SLOWTIMO: 45712205Ssam case PRU_PROTORCV: 45812205Ssam case PRU_PROTOSEND: 45912205Ssam error = EOPNOTSUPP; 46012205Ssam break; 46113118Ssam 46216799Skarels case PRU_RCVD: 46316799Skarels case PRU_RCVOOB: 46416799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 46516799Skarels 46613118Ssam default: 46713118Ssam panic("udp_usrreq"); 4684805Swnj } 469*44596Skarels splx(s); 470*44596Skarels 47111080Ssam release: 47242184Skarels if (control) { 47342184Skarels printf("udp control data unexpectedly retained\n"); 47442184Skarels m_freem(control); 47542184Skarels } 47642184Skarels if (m) 47711080Ssam m_freem(m); 4786507Ssam return (error); 4794784Swnj } 480