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*45645Ssklower * @(#)udp_usrreq.c 7.19 (Berkeley) 11/27/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++; 6544828Skarels 6644828Skarels /* 6744828Skarels * Strip IP options, if any; should skip this, 6844828Skarels * make available to user, and use on returned packets, 6944828Skarels * but we don't yet have a way to check the checksum 7044828Skarels * with options still present. 7144828Skarels */ 7244828Skarels if (iphlen > sizeof (struct ip)) { 7340688Skarels ip_stripoptions(m, (struct mbuf *)0); 7444828Skarels iphlen = sizeof(struct ip); 7544828Skarels } 7644828Skarels 774926Swnj /* 785246Sroot * Get IP and UDP header together in first mbuf. 794926Swnj */ 8040688Skarels ip = mtod(m, struct ip *); 8140688Skarels if (m->m_len < iphlen + sizeof(struct udphdr)) { 8240688Skarels if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 8335288Skarels udpstat.udps_hdrops++; 8435288Skarels return; 8535288Skarels } 8640688Skarels ip = mtod(m, struct ip *); 8735288Skarels } 8840688Skarels uh = (struct udphdr *)((caddr_t)ip + iphlen); 894926Swnj 904926Swnj /* 915246Sroot * Make mbuf data length reflect UDP length. 925246Sroot * If not enough data to reflect UDP length, drop. 934926Swnj */ 9440688Skarels len = ntohs((u_short)uh->uh_ulen); 9540688Skarels if (ip->ip_len != len) { 9640688Skarels if (len > ip->ip_len) { 974926Swnj udpstat.udps_badlen++; 984926Swnj goto bad; 994926Swnj } 10040688Skarels m_adj(m, len - ip->ip_len); 10140688Skarels /* ip->ip_len = len; */ 1024926Swnj } 10324823Skarels /* 10440688Skarels * Save a copy of the IP header in case we want restore it 10540688Skarels * for sending an ICMP error message in response. 10624823Skarels */ 10740688Skarels save_ip = *ip; 1084926Swnj 1094926Swnj /* 1105246Sroot * Checksum extended UDP header and data. 1114926Swnj */ 11240688Skarels if (udpcksum && uh->uh_sum) { 11340688Skarels ((struct ipovly *)ip)->ih_next = 0; 11440688Skarels ((struct ipovly *)ip)->ih_prev = 0; 11540688Skarels ((struct ipovly *)ip)->ih_x1 = 0; 11640688Skarels ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 11740688Skarels if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { 1184926Swnj udpstat.udps_badsum++; 1194901Swnj m_freem(m); 1204901Swnj return; 1214901Swnj } 1224901Swnj } 1234926Swnj 1244926Swnj /* 1257844Sroot * Locate pcb for datagram. 1264926Swnj */ 12744596Skarels inp = udp_last_inpcb; 12844596Skarels if (inp->inp_lport != uh->uh_dport || 12944596Skarels inp->inp_fport != uh->uh_sport || 13044596Skarels inp->inp_faddr.s_addr != ip->ip_src.s_addr || 13144596Skarels inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 13244596Skarels inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 13344596Skarels ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 13444596Skarels if (inp) 13544596Skarels udp_last_inpcb = inp; 13644596Skarels udpstat.udpps_pcbcachemiss++; 13744596Skarels } 1386584Ssam if (inp == 0) { 13910144Ssam /* don't send ICMP response for broadcast packet */ 14040688Skarels udpstat.udps_noport++; 14144596Skarels if (m->m_flags & M_BCAST) { 14244596Skarels udpstat.udps_noportbcast++; 1436584Ssam goto bad; 14444596Skarels } 14540688Skarels *ip = save_ip; 14644828Skarels ip->ip_len += iphlen; 14735794Skarels icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 1486584Ssam return; 1496584Ssam } 1506584Ssam 1514926Swnj /* 1524926Swnj * Construct sockaddr format source address. 1534926Swnj * Stuff source address and datagram in user buffer. 1544926Swnj */ 15540688Skarels udp_in.sin_port = uh->uh_sport; 15640688Skarels udp_in.sin_addr = ip->ip_src; 15744596Skarels if (inp->inp_flags & INP_CONTROLOPTS) { 15844596Skarels struct mbuf **mp = &opts; 15944596Skarels struct mbuf *udp_saveopt(); 16044596Skarels 16144596Skarels if (inp->inp_flags & INP_RECVDSTADDR) { 16244596Skarels *mp = udp_saveopt((caddr_t) &ip->ip_dst, 16344596Skarels sizeof(struct in_addr), IP_RECVDSTADDR); 16444596Skarels if (*mp) 16544596Skarels mp = &(*mp)->m_next; 16644596Skarels } 16744596Skarels #ifdef notyet 16844596Skarels /* options were tossed above */ 16944596Skarels if (inp->inp_flags & INP_RECVOPTS) { 17044596Skarels *mp = udp_saveopt((caddr_t) opts_deleted_above, 17144596Skarels sizeof(struct in_addr), IP_RECVOPTS); 17244596Skarels if (*mp) 17344596Skarels mp = &(*mp)->m_next; 17444596Skarels } 17544596Skarels /* ip_srcroute doesn't do what we want here, need to fix */ 17644596Skarels if (inp->inp_flags & INP_RECVRETOPTS) { 17744596Skarels *mp = udp_saveopt((caddr_t) ip_srcroute(), 17844596Skarels sizeof(struct in_addr), IP_RECVRETOPTS); 17944596Skarels if (*mp) 18044596Skarels mp = &(*mp)->m_next; 18144596Skarels } 18244596Skarels #endif 18344596Skarels } 18440688Skarels iphlen += sizeof(struct udphdr); 18540688Skarels m->m_len -= iphlen; 18640688Skarels m->m_pkthdr.len -= iphlen; 18740688Skarels m->m_data += iphlen; 18812767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 18944596Skarels m, opts) == 0) { 19044596Skarels udpstat.udps_fullsock++; 1914926Swnj goto bad; 19244596Skarels } 1935050Swnj sorwakeup(inp->inp_socket); 1944887Swnj return; 1954926Swnj bad: 1964887Swnj m_freem(m); 19744596Skarels if (opts) 19844596Skarels m_freem(opts); 1994784Swnj } 2004784Swnj 20126426Skarels /* 20244596Skarels * Create a "control" mbuf containing the specified data 20344596Skarels * with the specified type for presentation with a datagram. 20444596Skarels */ 20544596Skarels struct mbuf * 20644596Skarels udp_saveopt(p, size, type) 20744596Skarels caddr_t p; 20844596Skarels register int size; 20944596Skarels int type; 21044596Skarels { 21144596Skarels register struct cmsghdr *cp; 21244596Skarels struct mbuf *m; 21344596Skarels 21444596Skarels if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 21544596Skarels return ((struct mbuf *) NULL); 21644596Skarels cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 217*45645Ssklower bcopy(p, (caddr_t)(cp + 1), size); 21844596Skarels size += sizeof(*cp); 21944596Skarels m->m_len = size; 22044596Skarels cp->cmsg_len = size; 22144596Skarels cp->cmsg_level = IPPROTO_IP; 22244596Skarels cp->cmsg_type = type; 22344596Skarels return (m); 22444596Skarels } 22544596Skarels 22644596Skarels /* 22726426Skarels * Notify a udp user of an asynchronous error; 22826426Skarels * just wake up so that he can collect error status. 22926426Skarels */ 23044596Skarels udp_notify(inp, errno) 23126426Skarels register struct inpcb *inp; 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 */ 29635794Skarels M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 2974784Swnj 2984926Swnj /* 2995246Sroot * Fill in mbuf with extended UDP header 3004926Swnj * and addresses and length put into network format. 3014926Swnj */ 3024926Swnj ui = mtod(m, struct udpiphdr *); 3034926Swnj ui->ui_next = ui->ui_prev = 0; 3044926Swnj ui->ui_x1 = 0; 3054926Swnj ui->ui_pr = IPPROTO_UDP; 30615226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 3075050Swnj ui->ui_src = inp->inp_laddr; 3085050Swnj ui->ui_dst = inp->inp_faddr; 3095050Swnj ui->ui_sport = inp->inp_lport; 3105050Swnj ui->ui_dport = inp->inp_fport; 31115226Ssam ui->ui_ulen = ui->ui_len; 3124784Swnj 3134926Swnj /* 3144926Swnj * Stuff checksum and output datagram. 3154926Swnj */ 3164926Swnj ui->ui_sum = 0; 31719519Skarels if (udpcksum) { 31819519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 31933715Skarels ui->ui_sum = 0xffff; 32021112Skarels } 3215050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 32244596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 32344596Skarels ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 32440688Skarels udpstat.udps_opackets++; 32542184Skarels error = ip_output(m, inp->inp_options, &inp->inp_route, 32642184Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 32742184Skarels 32842184Skarels if (addr) { 32942184Skarels in_pcbdisconnect(inp); 33042184Skarels inp->inp_laddr = laddr; 33142184Skarels splx(s); 33242184Skarels } 33342184Skarels return (error); 33442184Skarels 33542184Skarels release: 33642184Skarels m_freem(m); 33742184Skarels return (error); 3384784Swnj } 3394784Swnj 34038354Smckusick u_long udp_sendspace = 9216; /* really max datagram size */ 34138354Smckusick u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 34238354Smckusick /* 40 1K datagrams */ 34318368Skarels 3448602Sroot /*ARGSUSED*/ 34542184Skarels udp_usrreq(so, req, m, addr, control) 3464887Swnj struct socket *so; 3474784Swnj int req; 34842184Skarels struct mbuf *m, *addr, *control; 3494784Swnj { 3504887Swnj struct inpcb *inp = sotoinpcb(so); 3516507Ssam int error = 0; 35244596Skarels int s; 3534784Swnj 35418368Skarels if (req == PRU_CONTROL) 35542184Skarels return (in_control(so, (int)m, (caddr_t)addr, 35640688Skarels (struct ifnet *)control)); 35711080Ssam if (inp == NULL && req != PRU_ATTACH) { 35811080Ssam error = EINVAL; 35911080Ssam goto release; 36011080Ssam } 36144596Skarels /* 36244968Skarels * Note: need to block udp_input while changing 36344968Skarels * the udp pcb queue and/or pcb addresses. 36444596Skarels */ 3654784Swnj switch (req) { 3664784Swnj 3674784Swnj case PRU_ATTACH: 36811080Ssam if (inp != NULL) { 36911080Ssam error = EINVAL; 37011080Ssam break; 37111080Ssam } 37244596Skarels s = splnet(); 3738273Sroot error = in_pcballoc(so, &udb); 37444596Skarels splx(s); 3758273Sroot if (error) 3768273Sroot break; 37718368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3788273Sroot if (error) 3798273Sroot break; 38044596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3814887Swnj break; 3824784Swnj 3834784Swnj case PRU_DETACH: 38444968Skarels udp_detach(inp); 3854887Swnj break; 3864784Swnj 3878273Sroot case PRU_BIND: 38844968Skarels s = splnet(); 38942184Skarels error = in_pcbbind(inp, addr); 39044968Skarels splx(s); 3918273Sroot break; 3928273Sroot 3938273Sroot case PRU_LISTEN: 3948273Sroot error = EOPNOTSUPP; 3958273Sroot break; 3968273Sroot 3974784Swnj case PRU_CONNECT: 39811080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 39911080Ssam error = EISCONN; 40011080Ssam break; 40111080Ssam } 40244968Skarels s = splnet(); 40342184Skarels error = in_pcbconnect(inp, addr); 40444968Skarels splx(s); 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 } 42244968Skarels s = splnet(); 4235166Swnj in_pcbdisconnect(inp); 42440688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 42544968Skarels splx(s); 42626404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4274784Swnj break; 4284784Swnj 4294912Swnj case PRU_SHUTDOWN: 4304912Swnj socantsendmore(so); 4314912Swnj break; 4324912Swnj 43342184Skarels case PRU_SEND: 43444596Skarels return (udp_output(inp, m, addr, control)); 4354784Swnj 4364784Swnj case PRU_ABORT: 43731750Skarels soisdisconnected(so); 43844968Skarels udp_detach(inp); 4394784Swnj break; 4404784Swnj 4416511Ssam case PRU_SOCKADDR: 44242184Skarels in_setsockaddr(inp, addr); 4436511Ssam break; 4446511Ssam 44514124Ssam case PRU_PEERADDR: 44642184Skarels in_setpeeraddr(inp, addr); 44714124Ssam break; 44814124Ssam 44916988Skarels case PRU_SENSE: 45016988Skarels /* 45116988Skarels * stat: don't bother with a blocksize. 45216988Skarels */ 45316988Skarels return (0); 45416988Skarels 45512205Ssam case PRU_SENDOOB: 45612205Ssam case PRU_FASTTIMO: 45712205Ssam case PRU_SLOWTIMO: 45812205Ssam case PRU_PROTORCV: 45912205Ssam case PRU_PROTOSEND: 46012205Ssam error = EOPNOTSUPP; 46112205Ssam break; 46213118Ssam 46316799Skarels case PRU_RCVD: 46416799Skarels case PRU_RCVOOB: 46516799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 46616799Skarels 46713118Ssam default: 46813118Ssam panic("udp_usrreq"); 4694805Swnj } 47044596Skarels 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 } 48044968Skarels 48144968Skarels udp_detach(inp) 48244968Skarels struct inpcb *inp; 48344968Skarels { 48444968Skarels int s = splnet(); 48544968Skarels 48644968Skarels if (inp == udp_last_inpcb) 48744968Skarels udp_last_inpcb = &udb; 48844968Skarels in_pcbdetach(inp); 48944968Skarels splx(s); 49044968Skarels } 491