123199Smckusick /* 244596Skarels * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. 332789Sbostic * All rights reserved. 423199Smckusick * 544642Sbostic * %sccs.include.redist.c% 632789Sbostic * 7*50715Smckusick * @(#)udp_usrreq.c 7.21 (Berkeley) 07/31/91 823199Smckusick */ 94784Swnj 1017065Sbloom #include "param.h" 1135794Skarels #include "malloc.h" 1217065Sbloom #include "mbuf.h" 1317065Sbloom #include "protosw.h" 1417065Sbloom #include "socket.h" 1517065Sbloom #include "socketvar.h" 1617065Sbloom #include "stat.h" 1710897Ssam 186584Ssam #include "../net/if.h" 196354Ssam #include "../net/route.h" 2010897Ssam 2117065Sbloom #include "in.h" 2217065Sbloom #include "in_systm.h" 2317065Sbloom #include "ip.h" 2440688Skarels #include "in_pcb.h" 2517065Sbloom #include "ip_var.h" 2617065Sbloom #include "ip_icmp.h" 2717065Sbloom #include "udp.h" 2817065Sbloom #include "udp_var.h" 294784Swnj 3044596Skarels struct inpcb *udp_last_inpcb = &udb; 3144596Skarels 324926Swnj /* 334926Swnj * UDP protocol implementation. 344926Swnj * Per RFC 768, August, 1980. 354926Swnj */ 364805Swnj udp_init() 374805Swnj { 384805Swnj 394901Swnj udb.inp_next = udb.inp_prev = &udb; 404805Swnj } 414805Swnj 4226118Skarels #ifndef COMPAT_42 4319519Skarels int udpcksum = 1; 4426118Skarels #else 4526118Skarels int udpcksum = 0; /* XXX */ 4626118Skarels #endif 4731398Skarels int udp_ttl = UDP_TTL; 4826118Skarels 4937324Skarels struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 504901Swnj 5135794Skarels udp_input(m, iphlen) 5235794Skarels register struct mbuf *m; 5335794Skarels int iphlen; 544784Swnj { 5540688Skarels register struct ip *ip; 5640688Skarels register struct udphdr *uh; 574887Swnj register struct inpcb *inp; 5844596Skarels struct mbuf *opts = 0; 597844Sroot int len; 6040688Skarels struct ip save_ip; 614784Swnj 6244596Skarels udpstat.udps_ipackets++; 6344828Skarels 6444828Skarels /* 6544828Skarels * Strip IP options, if any; should skip this, 6644828Skarels * make available to user, and use on returned packets, 6744828Skarels * but we don't yet have a way to check the checksum 6844828Skarels * with options still present. 6944828Skarels */ 7044828Skarels if (iphlen > sizeof (struct ip)) { 7140688Skarels ip_stripoptions(m, (struct mbuf *)0); 7244828Skarels iphlen = sizeof(struct ip); 7344828Skarels } 7444828Skarels 754926Swnj /* 765246Sroot * Get IP and UDP header together in first mbuf. 774926Swnj */ 7840688Skarels ip = mtod(m, struct ip *); 7940688Skarels if (m->m_len < iphlen + sizeof(struct udphdr)) { 8040688Skarels if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 8135288Skarels udpstat.udps_hdrops++; 8235288Skarels return; 8335288Skarels } 8440688Skarels ip = mtod(m, struct ip *); 8535288Skarels } 8640688Skarels uh = (struct udphdr *)((caddr_t)ip + iphlen); 874926Swnj 884926Swnj /* 895246Sroot * Make mbuf data length reflect UDP length. 905246Sroot * If not enough data to reflect UDP length, drop. 914926Swnj */ 9240688Skarels len = ntohs((u_short)uh->uh_ulen); 9340688Skarels if (ip->ip_len != len) { 9440688Skarels if (len > ip->ip_len) { 954926Swnj udpstat.udps_badlen++; 964926Swnj goto bad; 974926Swnj } 9840688Skarels m_adj(m, len - ip->ip_len); 9940688Skarels /* ip->ip_len = len; */ 1004926Swnj } 10124823Skarels /* 10240688Skarels * Save a copy of the IP header in case we want restore it 10340688Skarels * for sending an ICMP error message in response. 10424823Skarels */ 10540688Skarels save_ip = *ip; 1064926Swnj 1074926Swnj /* 1085246Sroot * Checksum extended UDP header and data. 1094926Swnj */ 11040688Skarels if (udpcksum && uh->uh_sum) { 11140688Skarels ((struct ipovly *)ip)->ih_next = 0; 11240688Skarels ((struct ipovly *)ip)->ih_prev = 0; 11340688Skarels ((struct ipovly *)ip)->ih_x1 = 0; 11440688Skarels ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 11540688Skarels if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { 1164926Swnj udpstat.udps_badsum++; 1174901Swnj m_freem(m); 1184901Swnj return; 1194901Swnj } 1204901Swnj } 1214926Swnj 1224926Swnj /* 1237844Sroot * Locate pcb for datagram. 1244926Swnj */ 12544596Skarels inp = udp_last_inpcb; 12644596Skarels if (inp->inp_lport != uh->uh_dport || 12744596Skarels inp->inp_fport != uh->uh_sport || 12844596Skarels inp->inp_faddr.s_addr != ip->ip_src.s_addr || 12944596Skarels inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 13044596Skarels inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 13144596Skarels ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 13244596Skarels if (inp) 13344596Skarels udp_last_inpcb = inp; 13444596Skarels udpstat.udpps_pcbcachemiss++; 13544596Skarels } 1366584Ssam if (inp == 0) { 13710144Ssam /* don't send ICMP response for broadcast packet */ 13840688Skarels udpstat.udps_noport++; 13944596Skarels if (m->m_flags & M_BCAST) { 14044596Skarels udpstat.udps_noportbcast++; 1416584Ssam goto bad; 14244596Skarels } 14340688Skarels *ip = save_ip; 14444828Skarels ip->ip_len += iphlen; 14535794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1466584Ssam return; 1476584Ssam } 1486584Ssam 1494926Swnj /* 1504926Swnj * Construct sockaddr format source address. 1514926Swnj * Stuff source address and datagram in user buffer. 1524926Swnj */ 15340688Skarels udp_in.sin_port = uh->uh_sport; 15440688Skarels udp_in.sin_addr = ip->ip_src; 15544596Skarels if (inp->inp_flags & INP_CONTROLOPTS) { 15644596Skarels struct mbuf **mp = &opts; 15744596Skarels struct mbuf *udp_saveopt(); 15844596Skarels 15944596Skarels if (inp->inp_flags & INP_RECVDSTADDR) { 16044596Skarels *mp = udp_saveopt((caddr_t) &ip->ip_dst, 16144596Skarels sizeof(struct in_addr), IP_RECVDSTADDR); 16244596Skarels if (*mp) 16344596Skarels mp = &(*mp)->m_next; 16444596Skarels } 16544596Skarels #ifdef notyet 16644596Skarels /* options were tossed above */ 16744596Skarels if (inp->inp_flags & INP_RECVOPTS) { 16844596Skarels *mp = udp_saveopt((caddr_t) opts_deleted_above, 16944596Skarels sizeof(struct in_addr), IP_RECVOPTS); 17044596Skarels if (*mp) 17144596Skarels mp = &(*mp)->m_next; 17244596Skarels } 17344596Skarels /* ip_srcroute doesn't do what we want here, need to fix */ 17444596Skarels if (inp->inp_flags & INP_RECVRETOPTS) { 17544596Skarels *mp = udp_saveopt((caddr_t) ip_srcroute(), 17644596Skarels sizeof(struct in_addr), IP_RECVRETOPTS); 17744596Skarels if (*mp) 17844596Skarels mp = &(*mp)->m_next; 17944596Skarels } 18044596Skarels #endif 18144596Skarels } 18240688Skarels iphlen += sizeof(struct udphdr); 18340688Skarels m->m_len -= iphlen; 18440688Skarels m->m_pkthdr.len -= iphlen; 18540688Skarels m->m_data += iphlen; 18612767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 18744596Skarels m, opts) == 0) { 18844596Skarels udpstat.udps_fullsock++; 1894926Swnj goto bad; 19044596Skarels } 1915050Swnj sorwakeup(inp->inp_socket); 1924887Swnj return; 1934926Swnj bad: 1944887Swnj m_freem(m); 19544596Skarels if (opts) 19644596Skarels m_freem(opts); 1974784Swnj } 1984784Swnj 19926426Skarels /* 20044596Skarels * Create a "control" mbuf containing the specified data 20144596Skarels * with the specified type for presentation with a datagram. 20244596Skarels */ 20344596Skarels struct mbuf * 20444596Skarels udp_saveopt(p, size, type) 20544596Skarels caddr_t p; 20644596Skarels register int size; 20744596Skarels int type; 20844596Skarels { 20944596Skarels register struct cmsghdr *cp; 21044596Skarels struct mbuf *m; 21144596Skarels 21244596Skarels if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 21344596Skarels return ((struct mbuf *) NULL); 21444596Skarels cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 21545645Ssklower bcopy(p, (caddr_t)(cp + 1), size); 21644596Skarels size += sizeof(*cp); 21744596Skarels m->m_len = size; 21844596Skarels cp->cmsg_len = size; 21944596Skarels cp->cmsg_level = IPPROTO_IP; 22044596Skarels cp->cmsg_type = type; 22144596Skarels return (m); 22244596Skarels } 22344596Skarels 22444596Skarels /* 22526426Skarels * Notify a udp user of an asynchronous error; 22626426Skarels * just wake up so that he can collect error status. 22726426Skarels */ 22844596Skarels udp_notify(inp, errno) 22926426Skarels register struct inpcb *inp; 23026426Skarels { 23126426Skarels 23244596Skarels inp->inp_socket->so_error = errno; 23326426Skarels sorwakeup(inp->inp_socket); 23426426Skarels sowwakeup(inp->inp_socket); 23526426Skarels } 23626426Skarels 23740688Skarels udp_ctlinput(cmd, sa, ip) 2386591Ssam int cmd; 23924823Skarels struct sockaddr *sa; 24040688Skarels register struct ip *ip; 2416591Ssam { 24240688Skarels register struct udphdr *uh; 24340688Skarels extern struct in_addr zeroin_addr; 2446591Ssam extern u_char inetctlerrmap[]; 2456591Ssam 24640688Skarels if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 2476591Ssam return; 24840688Skarels if (ip) { 24940688Skarels uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 25040688Skarels in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 25140688Skarels cmd, udp_notify); 25240688Skarels } else 25340688Skarels in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 2546591Ssam } 2556591Ssam 25642184Skarels udp_output(inp, m, addr, control) 25726060Skarels register struct inpcb *inp; 25835794Skarels register struct mbuf *m; 25942184Skarels struct mbuf *addr, *control; 2604784Swnj { 2614926Swnj register struct udpiphdr *ui; 26235794Skarels register int len = m->m_pkthdr.len; 26342184Skarels struct in_addr laddr; 26442184Skarels int s, error = 0; 2654784Swnj 26642184Skarels if (control) 26742184Skarels m_freem(control); /* XXX */ 26842184Skarels 26942184Skarels if (addr) { 27042184Skarels laddr = inp->inp_laddr; 27142184Skarels if (inp->inp_faddr.s_addr != INADDR_ANY) { 27242184Skarels error = EISCONN; 27342184Skarels goto release; 27442184Skarels } 27542184Skarels /* 27642184Skarels * Must block input while temporarily connected. 27742184Skarels */ 27842184Skarels s = splnet(); 27942184Skarels error = in_pcbconnect(inp, addr); 28042184Skarels if (error) { 28142184Skarels splx(s); 28242184Skarels goto release; 28342184Skarels } 28442184Skarels } else { 28542184Skarels if (inp->inp_faddr.s_addr == INADDR_ANY) { 28642184Skarels error = ENOTCONN; 28742184Skarels goto release; 28842184Skarels } 28942184Skarels } 2904926Swnj /* 2914926Swnj * Calculate data length and get a mbuf 2925246Sroot * for UDP and IP headers. 2934926Swnj */ 294*50715Smckusick M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); 295*50715Smckusick if (m == 0) { 296*50715Smckusick error = ENOBUFS; 297*50715Smckusick goto release; 298*50715Smckusick } 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; 32444596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 32544596Skarels ((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; 35444596Skarels 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 } 36344596Skarels /* 36444968Skarels * Note: need to block udp_input while changing 36544968Skarels * the udp pcb queue and/or pcb addresses. 36644596Skarels */ 3674784Swnj switch (req) { 3684784Swnj 3694784Swnj case PRU_ATTACH: 37011080Ssam if (inp != NULL) { 37111080Ssam error = EINVAL; 37211080Ssam break; 37311080Ssam } 37444596Skarels s = splnet(); 3758273Sroot error = in_pcballoc(so, &udb); 37644596Skarels splx(s); 3778273Sroot if (error) 3788273Sroot break; 37918368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3808273Sroot if (error) 3818273Sroot break; 38244596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3834887Swnj break; 3844784Swnj 3854784Swnj case PRU_DETACH: 38644968Skarels udp_detach(inp); 3874887Swnj break; 3884784Swnj 3898273Sroot case PRU_BIND: 39044968Skarels s = splnet(); 39142184Skarels error = in_pcbbind(inp, addr); 39244968Skarels splx(s); 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 } 40444968Skarels s = splnet(); 40542184Skarels error = in_pcbconnect(inp, addr); 40644968Skarels splx(s); 4076507Ssam if (error == 0) 4086507Ssam soisconnected(so); 4094887Swnj break; 4104784Swnj 41113118Ssam case PRU_CONNECT2: 41213118Ssam error = EOPNOTSUPP; 41313118Ssam break; 41413118Ssam 4154926Swnj case PRU_ACCEPT: 41611080Ssam error = EOPNOTSUPP; 41711080Ssam break; 4184926Swnj 4194784Swnj case PRU_DISCONNECT: 42011080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 42111080Ssam error = ENOTCONN; 42211080Ssam break; 42311080Ssam } 42444968Skarels s = splnet(); 4255166Swnj in_pcbdisconnect(inp); 42640688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 42744968Skarels splx(s); 42826404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4294784Swnj break; 4304784Swnj 4314912Swnj case PRU_SHUTDOWN: 4324912Swnj socantsendmore(so); 4334912Swnj break; 4344912Swnj 43542184Skarels case PRU_SEND: 43644596Skarels return (udp_output(inp, m, addr, control)); 4374784Swnj 4384784Swnj case PRU_ABORT: 43931750Skarels soisdisconnected(so); 44044968Skarels udp_detach(inp); 4414784Swnj break; 4424784Swnj 4436511Ssam case PRU_SOCKADDR: 44442184Skarels in_setsockaddr(inp, addr); 4456511Ssam break; 4466511Ssam 44714124Ssam case PRU_PEERADDR: 44842184Skarels in_setpeeraddr(inp, addr); 44914124Ssam break; 45014124Ssam 45116988Skarels case PRU_SENSE: 45216988Skarels /* 45316988Skarels * stat: don't bother with a blocksize. 45416988Skarels */ 45516988Skarels return (0); 45616988Skarels 45712205Ssam case PRU_SENDOOB: 45812205Ssam case PRU_FASTTIMO: 45912205Ssam case PRU_SLOWTIMO: 46012205Ssam case PRU_PROTORCV: 46112205Ssam case PRU_PROTOSEND: 46212205Ssam error = EOPNOTSUPP; 46312205Ssam break; 46413118Ssam 46516799Skarels case PRU_RCVD: 46616799Skarels case PRU_RCVOOB: 46716799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 46816799Skarels 46913118Ssam default: 47013118Ssam panic("udp_usrreq"); 4714805Swnj } 47244596Skarels 47311080Ssam release: 47442184Skarels if (control) { 47542184Skarels printf("udp control data unexpectedly retained\n"); 47642184Skarels m_freem(control); 47742184Skarels } 47842184Skarels if (m) 47911080Ssam m_freem(m); 4806507Ssam return (error); 4814784Swnj } 48244968Skarels 48344968Skarels udp_detach(inp) 48444968Skarels struct inpcb *inp; 48544968Skarels { 48644968Skarels int s = splnet(); 48744968Skarels 48844968Skarels if (inp == udp_last_inpcb) 48944968Skarels udp_last_inpcb = &udb; 49044968Skarels in_pcbdetach(inp); 49144968Skarels splx(s); 49244968Skarels } 493