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*52949Storek * @(#)udp_usrreq.c 7.23 (Berkeley) 03/15/92 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" 1651271Ssklower #include "errno.h" 1717065Sbloom #include "stat.h" 1810897Ssam 196584Ssam #include "../net/if.h" 206354Ssam #include "../net/route.h" 2110897Ssam 2217065Sbloom #include "in.h" 2317065Sbloom #include "in_systm.h" 2417065Sbloom #include "ip.h" 2540688Skarels #include "in_pcb.h" 2617065Sbloom #include "ip_var.h" 2717065Sbloom #include "ip_icmp.h" 2817065Sbloom #include "udp.h" 2917065Sbloom #include "udp_var.h" 304784Swnj 3144596Skarels struct inpcb *udp_last_inpcb = &udb; 3244596Skarels 334926Swnj /* 344926Swnj * UDP protocol implementation. 354926Swnj * Per RFC 768, August, 1980. 364926Swnj */ 374805Swnj udp_init() 384805Swnj { 394805Swnj 404901Swnj udb.inp_next = udb.inp_prev = &udb; 414805Swnj } 424805Swnj 4326118Skarels #ifndef COMPAT_42 4419519Skarels int udpcksum = 1; 4526118Skarels #else 4626118Skarels int udpcksum = 0; /* XXX */ 4726118Skarels #endif 4831398Skarels int udp_ttl = UDP_TTL; 4926118Skarels 5037324Skarels struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 514901Swnj 5235794Skarels udp_input(m, iphlen) 5335794Skarels register struct mbuf *m; 5435794Skarels int iphlen; 554784Swnj { 5640688Skarels register struct ip *ip; 5740688Skarels register struct udphdr *uh; 584887Swnj register struct inpcb *inp; 5944596Skarels struct mbuf *opts = 0; 607844Sroot int len; 6140688Skarels struct ip save_ip; 624784Swnj 6344596Skarels udpstat.udps_ipackets++; 6444828Skarels 6544828Skarels /* 6644828Skarels * Strip IP options, if any; should skip this, 6744828Skarels * make available to user, and use on returned packets, 6844828Skarels * but we don't yet have a way to check the checksum 6944828Skarels * with options still present. 7044828Skarels */ 7144828Skarels if (iphlen > sizeof (struct ip)) { 7240688Skarels ip_stripoptions(m, (struct mbuf *)0); 7344828Skarels iphlen = sizeof(struct ip); 7444828Skarels } 7544828Skarels 764926Swnj /* 775246Sroot * Get IP and UDP header together in first mbuf. 784926Swnj */ 7940688Skarels ip = mtod(m, struct ip *); 8040688Skarels if (m->m_len < iphlen + sizeof(struct udphdr)) { 8140688Skarels if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 8235288Skarels udpstat.udps_hdrops++; 8335288Skarels return; 8435288Skarels } 8540688Skarels ip = mtod(m, struct ip *); 8635288Skarels } 8740688Skarels uh = (struct udphdr *)((caddr_t)ip + iphlen); 884926Swnj 894926Swnj /* 905246Sroot * Make mbuf data length reflect UDP length. 915246Sroot * If not enough data to reflect UDP length, drop. 924926Swnj */ 9340688Skarels len = ntohs((u_short)uh->uh_ulen); 9440688Skarels if (ip->ip_len != len) { 9540688Skarels if (len > ip->ip_len) { 964926Swnj udpstat.udps_badlen++; 974926Swnj goto bad; 984926Swnj } 9940688Skarels m_adj(m, len - ip->ip_len); 10040688Skarels /* ip->ip_len = len; */ 1014926Swnj } 10224823Skarels /* 10340688Skarels * Save a copy of the IP header in case we want restore it 10440688Skarels * for sending an ICMP error message in response. 10524823Skarels */ 10640688Skarels save_ip = *ip; 1074926Swnj 1084926Swnj /* 1095246Sroot * Checksum extended UDP header and data. 1104926Swnj */ 11140688Skarels if (udpcksum && uh->uh_sum) { 11240688Skarels ((struct ipovly *)ip)->ih_next = 0; 11340688Skarels ((struct ipovly *)ip)->ih_prev = 0; 11440688Skarels ((struct ipovly *)ip)->ih_x1 = 0; 11540688Skarels ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 11640688Skarels if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { 1174926Swnj udpstat.udps_badsum++; 1184901Swnj m_freem(m); 1194901Swnj return; 1204901Swnj } 1214901Swnj } 1224926Swnj 1234926Swnj /* 1247844Sroot * Locate pcb for datagram. 1254926Swnj */ 12644596Skarels inp = udp_last_inpcb; 12744596Skarels if (inp->inp_lport != uh->uh_dport || 12844596Skarels inp->inp_fport != uh->uh_sport || 12944596Skarels inp->inp_faddr.s_addr != ip->ip_src.s_addr || 13044596Skarels inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 13144596Skarels inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 13244596Skarels ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 13344596Skarels if (inp) 13444596Skarels udp_last_inpcb = inp; 13544596Skarels udpstat.udpps_pcbcachemiss++; 13644596Skarels } 1376584Ssam if (inp == 0) { 13810144Ssam /* don't send ICMP response for broadcast packet */ 13940688Skarels udpstat.udps_noport++; 14044596Skarels if (m->m_flags & M_BCAST) { 14144596Skarels udpstat.udps_noportbcast++; 1426584Ssam goto bad; 14344596Skarels } 14440688Skarels *ip = save_ip; 14544828Skarels ip->ip_len += iphlen; 14635794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1476584Ssam return; 1486584Ssam } 1496584Ssam 1504926Swnj /* 1514926Swnj * Construct sockaddr format source address. 1524926Swnj * Stuff source address and datagram in user buffer. 1534926Swnj */ 15440688Skarels udp_in.sin_port = uh->uh_sport; 15540688Skarels udp_in.sin_addr = ip->ip_src; 15644596Skarels if (inp->inp_flags & INP_CONTROLOPTS) { 15744596Skarels struct mbuf **mp = &opts; 15844596Skarels struct mbuf *udp_saveopt(); 15944596Skarels 16044596Skarels if (inp->inp_flags & INP_RECVDSTADDR) { 16144596Skarels *mp = udp_saveopt((caddr_t) &ip->ip_dst, 16244596Skarels sizeof(struct in_addr), IP_RECVDSTADDR); 16344596Skarels if (*mp) 16444596Skarels mp = &(*mp)->m_next; 16544596Skarels } 16644596Skarels #ifdef notyet 16744596Skarels /* options were tossed above */ 16844596Skarels if (inp->inp_flags & INP_RECVOPTS) { 16944596Skarels *mp = udp_saveopt((caddr_t) opts_deleted_above, 17044596Skarels sizeof(struct in_addr), IP_RECVOPTS); 17144596Skarels if (*mp) 17244596Skarels mp = &(*mp)->m_next; 17344596Skarels } 17444596Skarels /* ip_srcroute doesn't do what we want here, need to fix */ 17544596Skarels if (inp->inp_flags & INP_RECVRETOPTS) { 17644596Skarels *mp = udp_saveopt((caddr_t) ip_srcroute(), 17744596Skarels sizeof(struct in_addr), IP_RECVRETOPTS); 17844596Skarels if (*mp) 17944596Skarels mp = &(*mp)->m_next; 18044596Skarels } 18144596Skarels #endif 18244596Skarels } 18340688Skarels iphlen += sizeof(struct udphdr); 18440688Skarels m->m_len -= iphlen; 18540688Skarels m->m_pkthdr.len -= iphlen; 18640688Skarels m->m_data += iphlen; 18712767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 18844596Skarels m, opts) == 0) { 18944596Skarels udpstat.udps_fullsock++; 1904926Swnj goto bad; 19144596Skarels } 1925050Swnj sorwakeup(inp->inp_socket); 1934887Swnj return; 1944926Swnj bad: 1954887Swnj m_freem(m); 19644596Skarels if (opts) 19744596Skarels m_freem(opts); 1984784Swnj } 1994784Swnj 20026426Skarels /* 20144596Skarels * Create a "control" mbuf containing the specified data 20244596Skarels * with the specified type for presentation with a datagram. 20344596Skarels */ 20444596Skarels struct mbuf * 20544596Skarels udp_saveopt(p, size, type) 20644596Skarels caddr_t p; 20744596Skarels register int size; 20844596Skarels int type; 20944596Skarels { 21044596Skarels register struct cmsghdr *cp; 21144596Skarels struct mbuf *m; 21244596Skarels 21344596Skarels if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 21444596Skarels return ((struct mbuf *) NULL); 21544596Skarels cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 21645645Ssklower bcopy(p, (caddr_t)(cp + 1), size); 21744596Skarels size += sizeof(*cp); 21844596Skarels m->m_len = size; 21944596Skarels cp->cmsg_len = size; 22044596Skarels cp->cmsg_level = IPPROTO_IP; 22144596Skarels cp->cmsg_type = type; 22244596Skarels return (m); 22344596Skarels } 22444596Skarels 22544596Skarels /* 22626426Skarels * Notify a udp user of an asynchronous error; 22726426Skarels * just wake up so that he can collect error status. 22826426Skarels */ 22944596Skarels udp_notify(inp, errno) 23026426Skarels register struct inpcb *inp; 231*52949Storek int errno; 23226426Skarels { 23326426Skarels 23444596Skarels inp->inp_socket->so_error = errno; 23526426Skarels sorwakeup(inp->inp_socket); 23626426Skarels sowwakeup(inp->inp_socket); 23726426Skarels } 23826426Skarels 23940688Skarels udp_ctlinput(cmd, sa, ip) 2406591Ssam int cmd; 24124823Skarels struct sockaddr *sa; 24240688Skarels register struct ip *ip; 2436591Ssam { 24440688Skarels register struct udphdr *uh; 24540688Skarels extern struct in_addr zeroin_addr; 2466591Ssam extern u_char inetctlerrmap[]; 2476591Ssam 24840688Skarels if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 2496591Ssam return; 25040688Skarels if (ip) { 25140688Skarels uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 25240688Skarels in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 25340688Skarels cmd, udp_notify); 25440688Skarels } else 25540688Skarels in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 2566591Ssam } 2576591Ssam 25842184Skarels udp_output(inp, m, addr, control) 25926060Skarels register struct inpcb *inp; 26035794Skarels register struct mbuf *m; 26142184Skarels struct mbuf *addr, *control; 2624784Swnj { 2634926Swnj register struct udpiphdr *ui; 26435794Skarels register int len = m->m_pkthdr.len; 26542184Skarels struct in_addr laddr; 26642184Skarels int s, error = 0; 2674784Swnj 26842184Skarels if (control) 26942184Skarels m_freem(control); /* XXX */ 27042184Skarels 27142184Skarels if (addr) { 27242184Skarels laddr = inp->inp_laddr; 27342184Skarels if (inp->inp_faddr.s_addr != INADDR_ANY) { 27442184Skarels error = EISCONN; 27542184Skarels goto release; 27642184Skarels } 27742184Skarels /* 27842184Skarels * Must block input while temporarily connected. 27942184Skarels */ 28042184Skarels s = splnet(); 28142184Skarels error = in_pcbconnect(inp, addr); 28242184Skarels if (error) { 28342184Skarels splx(s); 28442184Skarels goto release; 28542184Skarels } 28642184Skarels } else { 28742184Skarels if (inp->inp_faddr.s_addr == INADDR_ANY) { 28842184Skarels error = ENOTCONN; 28942184Skarels goto release; 29042184Skarels } 29142184Skarels } 2924926Swnj /* 2934926Swnj * Calculate data length and get a mbuf 2945246Sroot * for UDP and IP headers. 2954926Swnj */ 29650715Smckusick M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); 29750715Smckusick if (m == 0) { 29850715Smckusick error = ENOBUFS; 29950715Smckusick goto release; 30050715Smckusick } 3014784Swnj 3024926Swnj /* 3035246Sroot * Fill in mbuf with extended UDP header 3044926Swnj * and addresses and length put into network format. 3054926Swnj */ 3064926Swnj ui = mtod(m, struct udpiphdr *); 3074926Swnj ui->ui_next = ui->ui_prev = 0; 3084926Swnj ui->ui_x1 = 0; 3094926Swnj ui->ui_pr = IPPROTO_UDP; 31015226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 3115050Swnj ui->ui_src = inp->inp_laddr; 3125050Swnj ui->ui_dst = inp->inp_faddr; 3135050Swnj ui->ui_sport = inp->inp_lport; 3145050Swnj ui->ui_dport = inp->inp_fport; 31515226Ssam ui->ui_ulen = ui->ui_len; 3164784Swnj 3174926Swnj /* 3184926Swnj * Stuff checksum and output datagram. 3194926Swnj */ 3204926Swnj ui->ui_sum = 0; 32119519Skarels if (udpcksum) { 32219519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 32333715Skarels ui->ui_sum = 0xffff; 32421112Skarels } 3255050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 32644596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 32744596Skarels ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 32840688Skarels udpstat.udps_opackets++; 32942184Skarels error = ip_output(m, inp->inp_options, &inp->inp_route, 33042184Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 33142184Skarels 33242184Skarels if (addr) { 33342184Skarels in_pcbdisconnect(inp); 33442184Skarels inp->inp_laddr = laddr; 33542184Skarels splx(s); 33642184Skarels } 33742184Skarels return (error); 33842184Skarels 33942184Skarels release: 34042184Skarels m_freem(m); 34142184Skarels return (error); 3424784Swnj } 3434784Swnj 34438354Smckusick u_long udp_sendspace = 9216; /* really max datagram size */ 34538354Smckusick u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 34638354Smckusick /* 40 1K datagrams */ 34718368Skarels 3488602Sroot /*ARGSUSED*/ 34942184Skarels udp_usrreq(so, req, m, addr, control) 3504887Swnj struct socket *so; 3514784Swnj int req; 35242184Skarels struct mbuf *m, *addr, *control; 3534784Swnj { 3544887Swnj struct inpcb *inp = sotoinpcb(so); 3556507Ssam int error = 0; 35644596Skarels int s; 3574784Swnj 35818368Skarels if (req == PRU_CONTROL) 35942184Skarels return (in_control(so, (int)m, (caddr_t)addr, 36040688Skarels (struct ifnet *)control)); 36111080Ssam if (inp == NULL && req != PRU_ATTACH) { 36211080Ssam error = EINVAL; 36311080Ssam goto release; 36411080Ssam } 36544596Skarels /* 36644968Skarels * Note: need to block udp_input while changing 36744968Skarels * the udp pcb queue and/or pcb addresses. 36844596Skarels */ 3694784Swnj switch (req) { 3704784Swnj 3714784Swnj case PRU_ATTACH: 37211080Ssam if (inp != NULL) { 37311080Ssam error = EINVAL; 37411080Ssam break; 37511080Ssam } 37644596Skarels s = splnet(); 3778273Sroot error = in_pcballoc(so, &udb); 37844596Skarels splx(s); 3798273Sroot if (error) 3808273Sroot break; 38118368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3828273Sroot if (error) 3838273Sroot break; 38444596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3854887Swnj break; 3864784Swnj 3874784Swnj case PRU_DETACH: 38844968Skarels udp_detach(inp); 3894887Swnj break; 3904784Swnj 3918273Sroot case PRU_BIND: 39244968Skarels s = splnet(); 39342184Skarels error = in_pcbbind(inp, addr); 39444968Skarels splx(s); 3958273Sroot break; 3968273Sroot 3978273Sroot case PRU_LISTEN: 3988273Sroot error = EOPNOTSUPP; 3998273Sroot break; 4008273Sroot 4014784Swnj case PRU_CONNECT: 40211080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 40311080Ssam error = EISCONN; 40411080Ssam break; 40511080Ssam } 40644968Skarels s = splnet(); 40742184Skarels error = in_pcbconnect(inp, addr); 40844968Skarels splx(s); 4096507Ssam if (error == 0) 4106507Ssam soisconnected(so); 4114887Swnj break; 4124784Swnj 41313118Ssam case PRU_CONNECT2: 41413118Ssam error = EOPNOTSUPP; 41513118Ssam break; 41613118Ssam 4174926Swnj case PRU_ACCEPT: 41811080Ssam error = EOPNOTSUPP; 41911080Ssam break; 4204926Swnj 4214784Swnj case PRU_DISCONNECT: 42211080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 42311080Ssam error = ENOTCONN; 42411080Ssam break; 42511080Ssam } 42644968Skarels s = splnet(); 4275166Swnj in_pcbdisconnect(inp); 42840688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 42944968Skarels splx(s); 43026404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4314784Swnj break; 4324784Swnj 4334912Swnj case PRU_SHUTDOWN: 4344912Swnj socantsendmore(so); 4354912Swnj break; 4364912Swnj 43742184Skarels case PRU_SEND: 43844596Skarels return (udp_output(inp, m, addr, control)); 4394784Swnj 4404784Swnj case PRU_ABORT: 44131750Skarels soisdisconnected(so); 44244968Skarels udp_detach(inp); 4434784Swnj break; 4444784Swnj 4456511Ssam case PRU_SOCKADDR: 44642184Skarels in_setsockaddr(inp, addr); 4476511Ssam break; 4486511Ssam 44914124Ssam case PRU_PEERADDR: 45042184Skarels in_setpeeraddr(inp, addr); 45114124Ssam break; 45214124Ssam 45316988Skarels case PRU_SENSE: 45416988Skarels /* 45516988Skarels * stat: don't bother with a blocksize. 45616988Skarels */ 45716988Skarels return (0); 45816988Skarels 45912205Ssam case PRU_SENDOOB: 46012205Ssam case PRU_FASTTIMO: 46112205Ssam case PRU_SLOWTIMO: 46212205Ssam case PRU_PROTORCV: 46312205Ssam case PRU_PROTOSEND: 46412205Ssam error = EOPNOTSUPP; 46512205Ssam break; 46613118Ssam 46716799Skarels case PRU_RCVD: 46816799Skarels case PRU_RCVOOB: 46916799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 47016799Skarels 47113118Ssam default: 47213118Ssam panic("udp_usrreq"); 4734805Swnj } 47444596Skarels 47511080Ssam release: 47642184Skarels if (control) { 47742184Skarels printf("udp control data unexpectedly retained\n"); 47842184Skarels m_freem(control); 47942184Skarels } 48042184Skarels if (m) 48111080Ssam m_freem(m); 4826507Ssam return (error); 4834784Swnj } 48444968Skarels 48544968Skarels udp_detach(inp) 48644968Skarels struct inpcb *inp; 48744968Skarels { 48844968Skarels int s = splnet(); 48944968Skarels 49044968Skarels if (inp == udp_last_inpcb) 49144968Skarels udp_last_inpcb = &udb; 49244968Skarels in_pcbdetach(inp); 49344968Skarels splx(s); 49444968Skarels } 495