123199Smckusick /* 244596Skarels * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. 332789Sbostic * All rights reserved. 423199Smckusick * 5*44642Sbostic * %sccs.include.redist.c% 632789Sbostic * 7*44642Sbostic * @(#)udp_usrreq.c 7.16 (Berkeley) 06/29/90 823199Smckusick */ 94784Swnj 1017065Sbloom #include "param.h" 1117065Sbloom #include "user.h" 1235794Skarels #include "malloc.h" 1317065Sbloom #include "mbuf.h" 1417065Sbloom #include "protosw.h" 1517065Sbloom #include "socket.h" 1617065Sbloom #include "socketvar.h" 1717065Sbloom #include "errno.h" 1817065Sbloom #include "stat.h" 1910897Ssam 206584Ssam #include "../net/if.h" 216354Ssam #include "../net/route.h" 2210897Ssam 2317065Sbloom #include "in.h" 2417065Sbloom #include "in_systm.h" 2517065Sbloom #include "ip.h" 2640688Skarels #include "in_pcb.h" 2717065Sbloom #include "ip_var.h" 2817065Sbloom #include "ip_icmp.h" 2917065Sbloom #include "udp.h" 3017065Sbloom #include "udp_var.h" 314784Swnj 3244596Skarels struct inpcb *udp_last_inpcb = &udb; 3344596Skarels 344926Swnj /* 354926Swnj * UDP protocol implementation. 364926Swnj * Per RFC 768, August, 1980. 374926Swnj */ 384805Swnj udp_init() 394805Swnj { 404805Swnj 414901Swnj udb.inp_next = udb.inp_prev = &udb; 424805Swnj } 434805Swnj 4426118Skarels #ifndef COMPAT_42 4519519Skarels int udpcksum = 1; 4626118Skarels #else 4726118Skarels int udpcksum = 0; /* XXX */ 4826118Skarels #endif 4931398Skarels int udp_ttl = UDP_TTL; 5026118Skarels 5137324Skarels struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 524901Swnj 5335794Skarels udp_input(m, iphlen) 5435794Skarels register struct mbuf *m; 5535794Skarels int iphlen; 564784Swnj { 5740688Skarels register struct ip *ip; 5840688Skarels register struct udphdr *uh; 594887Swnj register struct inpcb *inp; 6044596Skarels struct mbuf *opts = 0; 617844Sroot int len; 6240688Skarels struct ip save_ip; 634784Swnj 6444596Skarels udpstat.udps_ipackets++; 6544596Skarels #ifndef notyet /* should skip this, make available & use on returned packets */ 6640688Skarels if (iphlen > sizeof (struct ip)) 6740688Skarels ip_stripoptions(m, (struct mbuf *)0); 6840688Skarels #endif 694926Swnj /* 705246Sroot * Get IP and UDP header together in first mbuf. 714926Swnj */ 7240688Skarels ip = mtod(m, struct ip *); 7340688Skarels if (m->m_len < iphlen + sizeof(struct udphdr)) { 7440688Skarels if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 7535288Skarels udpstat.udps_hdrops++; 7635288Skarels return; 7735288Skarels } 7840688Skarels ip = mtod(m, struct ip *); 7935288Skarels } 8040688Skarels uh = (struct udphdr *)((caddr_t)ip + iphlen); 814926Swnj 824926Swnj /* 835246Sroot * Make mbuf data length reflect UDP length. 845246Sroot * If not enough data to reflect UDP length, drop. 854926Swnj */ 8640688Skarels len = ntohs((u_short)uh->uh_ulen); 8740688Skarels if (ip->ip_len != len) { 8840688Skarels if (len > ip->ip_len) { 894926Swnj udpstat.udps_badlen++; 904926Swnj goto bad; 914926Swnj } 9240688Skarels m_adj(m, len - ip->ip_len); 9340688Skarels /* ip->ip_len = len; */ 944926Swnj } 9524823Skarels /* 9640688Skarels * Save a copy of the IP header in case we want restore it 9740688Skarels * for sending an ICMP error message in response. 9824823Skarels */ 9940688Skarels save_ip = *ip; 1004926Swnj 1014926Swnj /* 1025246Sroot * Checksum extended UDP header and data. 1034926Swnj */ 10440688Skarels if (udpcksum && uh->uh_sum) { 10540688Skarels ((struct ipovly *)ip)->ih_next = 0; 10640688Skarels ((struct ipovly *)ip)->ih_prev = 0; 10740688Skarels ((struct ipovly *)ip)->ih_x1 = 0; 10840688Skarels ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 10940688Skarels if (uh->uh_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 */ 11944596Skarels inp = udp_last_inpcb; 12044596Skarels if (inp->inp_lport != uh->uh_dport || 12144596Skarels inp->inp_fport != uh->uh_sport || 12244596Skarels inp->inp_faddr.s_addr != ip->ip_src.s_addr || 12344596Skarels inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 12444596Skarels inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 12544596Skarels ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 12644596Skarels if (inp) 12744596Skarels udp_last_inpcb = inp; 12844596Skarels udpstat.udpps_pcbcachemiss++; 12944596Skarels } 1306584Ssam if (inp == 0) { 13110144Ssam /* don't send ICMP response for broadcast packet */ 13240688Skarels udpstat.udps_noport++; 13344596Skarels if (m->m_flags & M_BCAST) { 13444596Skarels udpstat.udps_noportbcast++; 1356584Ssam goto bad; 13644596Skarels } 13740688Skarels *ip = save_ip; 13835794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1396584Ssam return; 1406584Ssam } 1416584Ssam 1424926Swnj /* 1434926Swnj * Construct sockaddr format source address. 1444926Swnj * Stuff source address and datagram in user buffer. 1454926Swnj */ 14640688Skarels udp_in.sin_port = uh->uh_sport; 14740688Skarels udp_in.sin_addr = ip->ip_src; 14844596Skarels if (inp->inp_flags & INP_CONTROLOPTS) { 14944596Skarels struct mbuf **mp = &opts; 15044596Skarels struct mbuf *udp_saveopt(); 15144596Skarels 15244596Skarels if (inp->inp_flags & INP_RECVDSTADDR) { 15344596Skarels *mp = udp_saveopt((caddr_t) &ip->ip_dst, 15444596Skarels sizeof(struct in_addr), IP_RECVDSTADDR); 15544596Skarels if (*mp) 15644596Skarels mp = &(*mp)->m_next; 15744596Skarels } 15844596Skarels #ifdef notyet 15944596Skarels /* options were tossed above */ 16044596Skarels if (inp->inp_flags & INP_RECVOPTS) { 16144596Skarels *mp = udp_saveopt((caddr_t) opts_deleted_above, 16244596Skarels sizeof(struct in_addr), IP_RECVOPTS); 16344596Skarels if (*mp) 16444596Skarels mp = &(*mp)->m_next; 16544596Skarels } 16644596Skarels /* ip_srcroute doesn't do what we want here, need to fix */ 16744596Skarels if (inp->inp_flags & INP_RECVRETOPTS) { 16844596Skarels *mp = udp_saveopt((caddr_t) ip_srcroute(), 16944596Skarels sizeof(struct in_addr), IP_RECVRETOPTS); 17044596Skarels if (*mp) 17144596Skarels mp = &(*mp)->m_next; 17244596Skarels } 17344596Skarels #endif 17444596Skarels } 17540688Skarels iphlen = sizeof(struct ip); 17640688Skarels iphlen += sizeof(struct udphdr); 17740688Skarels m->m_len -= iphlen; 17840688Skarels m->m_pkthdr.len -= iphlen; 17940688Skarels m->m_data += iphlen; 18012767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 18144596Skarels m, opts) == 0) { 18244596Skarels udpstat.udps_fullsock++; 1834926Swnj goto bad; 18444596Skarels } 1855050Swnj sorwakeup(inp->inp_socket); 1864887Swnj return; 1874926Swnj bad: 1884887Swnj m_freem(m); 18944596Skarels if (opts) 19044596Skarels m_freem(opts); 1914784Swnj } 1924784Swnj 19326426Skarels /* 19444596Skarels * Create a "control" mbuf containing the specified data 19544596Skarels * with the specified type for presentation with a datagram. 19644596Skarels */ 19744596Skarels struct mbuf * 19844596Skarels udp_saveopt(p, size, type) 19944596Skarels caddr_t p; 20044596Skarels register int size; 20144596Skarels int type; 20244596Skarels { 20344596Skarels register struct cmsghdr *cp; 20444596Skarels struct mbuf *m; 20544596Skarels 20644596Skarels if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 20744596Skarels return ((struct mbuf *) NULL); 20844596Skarels cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 20944596Skarels bcopy(p, (caddr_t)cp + size, size); 21044596Skarels size += sizeof(*cp); 21144596Skarels m->m_len = size; 21244596Skarels cp->cmsg_len = size; 21344596Skarels cp->cmsg_level = IPPROTO_IP; 21444596Skarels cp->cmsg_type = type; 21544596Skarels return (m); 21644596Skarels } 21744596Skarels 21844596Skarels /* 21926426Skarels * Notify a udp user of an asynchronous error; 22026426Skarels * just wake up so that he can collect error status. 22126426Skarels */ 22244596Skarels udp_notify(inp, errno) 22326426Skarels register struct inpcb *inp; 22426426Skarels { 22526426Skarels 22644596Skarels inp->inp_socket->so_error = errno; 22726426Skarels sorwakeup(inp->inp_socket); 22826426Skarels sowwakeup(inp->inp_socket); 22926426Skarels } 23026426Skarels 23140688Skarels udp_ctlinput(cmd, sa, ip) 2326591Ssam int cmd; 23324823Skarels struct sockaddr *sa; 23440688Skarels register struct ip *ip; 2356591Ssam { 23640688Skarels register struct udphdr *uh; 23740688Skarels extern struct in_addr zeroin_addr; 2386591Ssam extern u_char inetctlerrmap[]; 2396591Ssam 24040688Skarels if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 2416591Ssam return; 24240688Skarels if (ip) { 24340688Skarels uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 24440688Skarels in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 24540688Skarels cmd, udp_notify); 24640688Skarels } else 24740688Skarels in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 2486591Ssam } 2496591Ssam 25042184Skarels udp_output(inp, m, addr, control) 25126060Skarels register struct inpcb *inp; 25235794Skarels register struct mbuf *m; 25342184Skarels struct mbuf *addr, *control; 2544784Swnj { 2554926Swnj register struct udpiphdr *ui; 25635794Skarels register int len = m->m_pkthdr.len; 25742184Skarels struct in_addr laddr; 25842184Skarels int s, error = 0; 2594784Swnj 26042184Skarels if (control) 26142184Skarels m_freem(control); /* XXX */ 26242184Skarels 26342184Skarels if (addr) { 26442184Skarels laddr = inp->inp_laddr; 26542184Skarels if (inp->inp_faddr.s_addr != INADDR_ANY) { 26642184Skarels error = EISCONN; 26742184Skarels goto release; 26842184Skarels } 26942184Skarels /* 27042184Skarels * Must block input while temporarily connected. 27142184Skarels */ 27242184Skarels s = splnet(); 27342184Skarels error = in_pcbconnect(inp, addr); 27442184Skarels if (error) { 27542184Skarels splx(s); 27642184Skarels goto release; 27742184Skarels } 27842184Skarels } else { 27942184Skarels if (inp->inp_faddr.s_addr == INADDR_ANY) { 28042184Skarels error = ENOTCONN; 28142184Skarels goto release; 28242184Skarels } 28342184Skarels } 2844926Swnj /* 2854926Swnj * Calculate data length and get a mbuf 2865246Sroot * for UDP and IP headers. 2874926Swnj */ 28835794Skarels M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 2894784Swnj 2904926Swnj /* 2915246Sroot * Fill in mbuf with extended UDP header 2924926Swnj * and addresses and length put into network format. 2934926Swnj */ 2944926Swnj ui = mtod(m, struct udpiphdr *); 2954926Swnj ui->ui_next = ui->ui_prev = 0; 2964926Swnj ui->ui_x1 = 0; 2974926Swnj ui->ui_pr = IPPROTO_UDP; 29815226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2995050Swnj ui->ui_src = inp->inp_laddr; 3005050Swnj ui->ui_dst = inp->inp_faddr; 3015050Swnj ui->ui_sport = inp->inp_lport; 3025050Swnj ui->ui_dport = inp->inp_fport; 30315226Ssam ui->ui_ulen = ui->ui_len; 3044784Swnj 3054926Swnj /* 3064926Swnj * Stuff checksum and output datagram. 3074926Swnj */ 3084926Swnj ui->ui_sum = 0; 30919519Skarels if (udpcksum) { 31019519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 31133715Skarels ui->ui_sum = 0xffff; 31221112Skarels } 3135050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 31444596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 31544596Skarels ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 31640688Skarels udpstat.udps_opackets++; 31742184Skarels error = ip_output(m, inp->inp_options, &inp->inp_route, 31842184Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 31942184Skarels 32042184Skarels if (addr) { 32142184Skarels in_pcbdisconnect(inp); 32242184Skarels inp->inp_laddr = laddr; 32342184Skarels splx(s); 32442184Skarels } 32542184Skarels return (error); 32642184Skarels 32742184Skarels release: 32842184Skarels m_freem(m); 32942184Skarels return (error); 3304784Swnj } 3314784Swnj 33238354Smckusick u_long udp_sendspace = 9216; /* really max datagram size */ 33338354Smckusick u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 33438354Smckusick /* 40 1K datagrams */ 33518368Skarels 3368602Sroot /*ARGSUSED*/ 33742184Skarels udp_usrreq(so, req, m, addr, control) 3384887Swnj struct socket *so; 3394784Swnj int req; 34042184Skarels struct mbuf *m, *addr, *control; 3414784Swnj { 3424887Swnj struct inpcb *inp = sotoinpcb(so); 3436507Ssam int error = 0; 34444596Skarels int s; 3454784Swnj 34618368Skarels if (req == PRU_CONTROL) 34742184Skarels return (in_control(so, (int)m, (caddr_t)addr, 34840688Skarels (struct ifnet *)control)); 34911080Ssam if (inp == NULL && req != PRU_ATTACH) { 35011080Ssam error = EINVAL; 35111080Ssam goto release; 35211080Ssam } 35344596Skarels /* 35444596Skarels * block udp_input while changing udp pcb queue, 35544596Skarels * addresses; should be done for individual cases, 35644596Skarels * but it's not worth it. 35744596Skarels */ 35844596Skarels s = splnet(); 3594784Swnj switch (req) { 3604784Swnj 3614784Swnj case PRU_ATTACH: 36211080Ssam if (inp != NULL) { 36311080Ssam error = EINVAL; 36411080Ssam break; 36511080Ssam } 36644596Skarels s = splnet(); 3678273Sroot error = in_pcballoc(so, &udb); 36844596Skarels splx(s); 3698273Sroot if (error) 3708273Sroot break; 37118368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3728273Sroot if (error) 3738273Sroot break; 37444596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3754887Swnj break; 3764784Swnj 3774784Swnj case PRU_DETACH: 3785166Swnj in_pcbdetach(inp); 3794887Swnj break; 3804784Swnj 3818273Sroot case PRU_BIND: 38242184Skarels error = in_pcbbind(inp, addr); 3838273Sroot break; 3848273Sroot 3858273Sroot case PRU_LISTEN: 3868273Sroot error = EOPNOTSUPP; 3878273Sroot break; 3888273Sroot 3894784Swnj case PRU_CONNECT: 39011080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 39111080Ssam error = EISCONN; 39211080Ssam break; 39311080Ssam } 39442184Skarels error = in_pcbconnect(inp, addr); 3956507Ssam if (error == 0) 3966507Ssam soisconnected(so); 3974887Swnj break; 3984784Swnj 39913118Ssam case PRU_CONNECT2: 40013118Ssam error = EOPNOTSUPP; 40113118Ssam break; 40213118Ssam 4034926Swnj case PRU_ACCEPT: 40411080Ssam error = EOPNOTSUPP; 40511080Ssam break; 4064926Swnj 4074784Swnj case PRU_DISCONNECT: 40811080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 40911080Ssam error = ENOTCONN; 41011080Ssam break; 41111080Ssam } 4125166Swnj in_pcbdisconnect(inp); 41340688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 41426404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4154784Swnj break; 4164784Swnj 4174912Swnj case PRU_SHUTDOWN: 4184912Swnj socantsendmore(so); 4194912Swnj break; 4204912Swnj 42142184Skarels case PRU_SEND: 42244596Skarels splx(s); 42344596Skarels return (udp_output(inp, m, addr, control)); 4244784Swnj 4254784Swnj case PRU_ABORT: 42631750Skarels soisdisconnected(so); 4275166Swnj in_pcbdetach(inp); 4284784Swnj break; 4294784Swnj 4306511Ssam case PRU_SOCKADDR: 43142184Skarels in_setsockaddr(inp, addr); 4326511Ssam break; 4336511Ssam 43414124Ssam case PRU_PEERADDR: 43542184Skarels in_setpeeraddr(inp, addr); 43614124Ssam break; 43714124Ssam 43816988Skarels case PRU_SENSE: 43916988Skarels /* 44016988Skarels * stat: don't bother with a blocksize. 44116988Skarels */ 44216988Skarels return (0); 44316988Skarels 44412205Ssam case PRU_SENDOOB: 44512205Ssam case PRU_FASTTIMO: 44612205Ssam case PRU_SLOWTIMO: 44712205Ssam case PRU_PROTORCV: 44812205Ssam case PRU_PROTOSEND: 44912205Ssam error = EOPNOTSUPP; 45012205Ssam break; 45113118Ssam 45216799Skarels case PRU_RCVD: 45316799Skarels case PRU_RCVOOB: 45416799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 45516799Skarels 45613118Ssam default: 45713118Ssam panic("udp_usrreq"); 4584805Swnj } 45944596Skarels splx(s); 46044596Skarels 46111080Ssam release: 46242184Skarels if (control) { 46342184Skarels printf("udp control data unexpectedly retained\n"); 46442184Skarels m_freem(control); 46542184Skarels } 46642184Skarels if (m) 46711080Ssam m_freem(m); 4686507Ssam return (error); 4694784Swnj } 470