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*51271Ssklower * @(#)udp_usrreq.c 7.22 (Berkeley) 10/02/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" 16*51271Ssklower #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; 23126426Skarels { 23226426Skarels 23344596Skarels inp->inp_socket->so_error = errno; 23426426Skarels sorwakeup(inp->inp_socket); 23526426Skarels sowwakeup(inp->inp_socket); 23626426Skarels } 23726426Skarels 23840688Skarels udp_ctlinput(cmd, sa, ip) 2396591Ssam int cmd; 24024823Skarels struct sockaddr *sa; 24140688Skarels register struct ip *ip; 2426591Ssam { 24340688Skarels register struct udphdr *uh; 24440688Skarels extern struct in_addr zeroin_addr; 2456591Ssam extern u_char inetctlerrmap[]; 2466591Ssam 24740688Skarels if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 2486591Ssam return; 24940688Skarels if (ip) { 25040688Skarels uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 25140688Skarels in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 25240688Skarels cmd, udp_notify); 25340688Skarels } else 25440688Skarels in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 2556591Ssam } 2566591Ssam 25742184Skarels udp_output(inp, m, addr, control) 25826060Skarels register struct inpcb *inp; 25935794Skarels register struct mbuf *m; 26042184Skarels struct mbuf *addr, *control; 2614784Swnj { 2624926Swnj register struct udpiphdr *ui; 26335794Skarels register int len = m->m_pkthdr.len; 26442184Skarels struct in_addr laddr; 26542184Skarels int s, error = 0; 2664784Swnj 26742184Skarels if (control) 26842184Skarels m_freem(control); /* XXX */ 26942184Skarels 27042184Skarels if (addr) { 27142184Skarels laddr = inp->inp_laddr; 27242184Skarels if (inp->inp_faddr.s_addr != INADDR_ANY) { 27342184Skarels error = EISCONN; 27442184Skarels goto release; 27542184Skarels } 27642184Skarels /* 27742184Skarels * Must block input while temporarily connected. 27842184Skarels */ 27942184Skarels s = splnet(); 28042184Skarels error = in_pcbconnect(inp, addr); 28142184Skarels if (error) { 28242184Skarels splx(s); 28342184Skarels goto release; 28442184Skarels } 28542184Skarels } else { 28642184Skarels if (inp->inp_faddr.s_addr == INADDR_ANY) { 28742184Skarels error = ENOTCONN; 28842184Skarels goto release; 28942184Skarels } 29042184Skarels } 2914926Swnj /* 2924926Swnj * Calculate data length and get a mbuf 2935246Sroot * for UDP and IP headers. 2944926Swnj */ 29550715Smckusick M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); 29650715Smckusick if (m == 0) { 29750715Smckusick error = ENOBUFS; 29850715Smckusick goto release; 29950715Smckusick } 3004784Swnj 3014926Swnj /* 3025246Sroot * Fill in mbuf with extended UDP header 3034926Swnj * and addresses and length put into network format. 3044926Swnj */ 3054926Swnj ui = mtod(m, struct udpiphdr *); 3064926Swnj ui->ui_next = ui->ui_prev = 0; 3074926Swnj ui->ui_x1 = 0; 3084926Swnj ui->ui_pr = IPPROTO_UDP; 30915226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 3105050Swnj ui->ui_src = inp->inp_laddr; 3115050Swnj ui->ui_dst = inp->inp_faddr; 3125050Swnj ui->ui_sport = inp->inp_lport; 3135050Swnj ui->ui_dport = inp->inp_fport; 31415226Ssam ui->ui_ulen = ui->ui_len; 3154784Swnj 3164926Swnj /* 3174926Swnj * Stuff checksum and output datagram. 3184926Swnj */ 3194926Swnj ui->ui_sum = 0; 32019519Skarels if (udpcksum) { 32119519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 32233715Skarels ui->ui_sum = 0xffff; 32321112Skarels } 3245050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 32544596Skarels ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 32644596Skarels ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 32740688Skarels udpstat.udps_opackets++; 32842184Skarels error = ip_output(m, inp->inp_options, &inp->inp_route, 32942184Skarels inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 33042184Skarels 33142184Skarels if (addr) { 33242184Skarels in_pcbdisconnect(inp); 33342184Skarels inp->inp_laddr = laddr; 33442184Skarels splx(s); 33542184Skarels } 33642184Skarels return (error); 33742184Skarels 33842184Skarels release: 33942184Skarels m_freem(m); 34042184Skarels return (error); 3414784Swnj } 3424784Swnj 34338354Smckusick u_long udp_sendspace = 9216; /* really max datagram size */ 34438354Smckusick u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 34538354Smckusick /* 40 1K datagrams */ 34618368Skarels 3478602Sroot /*ARGSUSED*/ 34842184Skarels udp_usrreq(so, req, m, addr, control) 3494887Swnj struct socket *so; 3504784Swnj int req; 35142184Skarels struct mbuf *m, *addr, *control; 3524784Swnj { 3534887Swnj struct inpcb *inp = sotoinpcb(so); 3546507Ssam int error = 0; 35544596Skarels int s; 3564784Swnj 35718368Skarels if (req == PRU_CONTROL) 35842184Skarels return (in_control(so, (int)m, (caddr_t)addr, 35940688Skarels (struct ifnet *)control)); 36011080Ssam if (inp == NULL && req != PRU_ATTACH) { 36111080Ssam error = EINVAL; 36211080Ssam goto release; 36311080Ssam } 36444596Skarels /* 36544968Skarels * Note: need to block udp_input while changing 36644968Skarels * the udp pcb queue and/or pcb addresses. 36744596Skarels */ 3684784Swnj switch (req) { 3694784Swnj 3704784Swnj case PRU_ATTACH: 37111080Ssam if (inp != NULL) { 37211080Ssam error = EINVAL; 37311080Ssam break; 37411080Ssam } 37544596Skarels s = splnet(); 3768273Sroot error = in_pcballoc(so, &udb); 37744596Skarels splx(s); 3788273Sroot if (error) 3798273Sroot break; 38018368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 3818273Sroot if (error) 3828273Sroot break; 38344596Skarels ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 3844887Swnj break; 3854784Swnj 3864784Swnj case PRU_DETACH: 38744968Skarels udp_detach(inp); 3884887Swnj break; 3894784Swnj 3908273Sroot case PRU_BIND: 39144968Skarels s = splnet(); 39242184Skarels error = in_pcbbind(inp, addr); 39344968Skarels splx(s); 3948273Sroot break; 3958273Sroot 3968273Sroot case PRU_LISTEN: 3978273Sroot error = EOPNOTSUPP; 3988273Sroot break; 3998273Sroot 4004784Swnj case PRU_CONNECT: 40111080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 40211080Ssam error = EISCONN; 40311080Ssam break; 40411080Ssam } 40544968Skarels s = splnet(); 40642184Skarels error = in_pcbconnect(inp, addr); 40744968Skarels splx(s); 4086507Ssam if (error == 0) 4096507Ssam soisconnected(so); 4104887Swnj break; 4114784Swnj 41213118Ssam case PRU_CONNECT2: 41313118Ssam error = EOPNOTSUPP; 41413118Ssam break; 41513118Ssam 4164926Swnj case PRU_ACCEPT: 41711080Ssam error = EOPNOTSUPP; 41811080Ssam break; 4194926Swnj 4204784Swnj case PRU_DISCONNECT: 42111080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 42211080Ssam error = ENOTCONN; 42311080Ssam break; 42411080Ssam } 42544968Skarels s = splnet(); 4265166Swnj in_pcbdisconnect(inp); 42740688Skarels inp->inp_laddr.s_addr = INADDR_ANY; 42844968Skarels splx(s); 42926404Skarels so->so_state &= ~SS_ISCONNECTED; /* XXX */ 4304784Swnj break; 4314784Swnj 4324912Swnj case PRU_SHUTDOWN: 4334912Swnj socantsendmore(so); 4344912Swnj break; 4354912Swnj 43642184Skarels case PRU_SEND: 43744596Skarels return (udp_output(inp, m, addr, control)); 4384784Swnj 4394784Swnj case PRU_ABORT: 44031750Skarels soisdisconnected(so); 44144968Skarels udp_detach(inp); 4424784Swnj break; 4434784Swnj 4446511Ssam case PRU_SOCKADDR: 44542184Skarels in_setsockaddr(inp, addr); 4466511Ssam break; 4476511Ssam 44814124Ssam case PRU_PEERADDR: 44942184Skarels in_setpeeraddr(inp, addr); 45014124Ssam break; 45114124Ssam 45216988Skarels case PRU_SENSE: 45316988Skarels /* 45416988Skarels * stat: don't bother with a blocksize. 45516988Skarels */ 45616988Skarels return (0); 45716988Skarels 45812205Ssam case PRU_SENDOOB: 45912205Ssam case PRU_FASTTIMO: 46012205Ssam case PRU_SLOWTIMO: 46112205Ssam case PRU_PROTORCV: 46212205Ssam case PRU_PROTOSEND: 46312205Ssam error = EOPNOTSUPP; 46412205Ssam break; 46513118Ssam 46616799Skarels case PRU_RCVD: 46716799Skarels case PRU_RCVOOB: 46816799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 46916799Skarels 47013118Ssam default: 47113118Ssam panic("udp_usrreq"); 4724805Swnj } 47344596Skarels 47411080Ssam release: 47542184Skarels if (control) { 47642184Skarels printf("udp control data unexpectedly retained\n"); 47742184Skarels m_freem(control); 47842184Skarels } 47942184Skarels if (m) 48011080Ssam m_freem(m); 4816507Ssam return (error); 4824784Swnj } 48344968Skarels 48444968Skarels udp_detach(inp) 48544968Skarels struct inpcb *inp; 48644968Skarels { 48744968Skarels int s = splnet(); 48844968Skarels 48944968Skarels if (inp == udp_last_inpcb) 49044968Skarels udp_last_inpcb = &udb; 49144968Skarels in_pcbdetach(inp); 49244968Skarels splx(s); 49344968Skarels } 494