1*16799Skarels /* udp_usrreq.c 6.5 84/07/31 */ 24784Swnj 34784Swnj #include "../h/param.h" 44887Swnj #include "../h/dir.h" 54887Swnj #include "../h/user.h" 64784Swnj #include "../h/mbuf.h" 74805Swnj #include "../h/protosw.h" 84887Swnj #include "../h/socket.h" 94887Swnj #include "../h/socketvar.h" 1010897Ssam #include "../h/errno.h" 1110897Ssam 126584Ssam #include "../net/if.h" 136354Ssam #include "../net/route.h" 1410897Ssam 1510897Ssam #include "../netinet/in.h" 168411Swnj #include "../netinet/in_pcb.h" 178411Swnj #include "../netinet/in_systm.h" 188411Swnj #include "../netinet/ip.h" 198411Swnj #include "../netinet/ip_var.h" 208411Swnj #include "../netinet/ip_icmp.h" 218411Swnj #include "../netinet/udp.h" 228411Swnj #include "../netinet/udp_var.h" 234784Swnj 244926Swnj /* 254926Swnj * UDP protocol implementation. 264926Swnj * Per RFC 768, August, 1980. 274926Swnj */ 284805Swnj udp_init() 294805Swnj { 304805Swnj 314901Swnj udb.inp_next = udb.inp_prev = &udb; 324805Swnj } 334805Swnj 3415539Skarels int udpcksum = 0; 354926Swnj struct sockaddr_in udp_in = { AF_INET }; 364901Swnj 374926Swnj udp_input(m0) 384926Swnj struct mbuf *m0; 394784Swnj { 404901Swnj register struct udpiphdr *ui; 414887Swnj register struct inpcb *inp; 424926Swnj register struct mbuf *m; 437844Sroot int len; 444784Swnj 454926Swnj /* 465246Sroot * Get IP and UDP header together in first mbuf. 474926Swnj */ 484926Swnj m = m0; 495308Sroot if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) && 505308Sroot (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 514926Swnj udpstat.udps_hdrops++; 525308Sroot return; 534926Swnj } 545050Swnj ui = mtod(m, struct udpiphdr *); 555246Sroot if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) 565220Swnj ip_stripoptions((struct ip *)ui, (struct mbuf *)0); 574926Swnj 584926Swnj /* 595246Sroot * Make mbuf data length reflect UDP length. 605246Sroot * If not enough data to reflect UDP length, drop. 614926Swnj */ 627844Sroot len = ntohs((u_short)ui->ui_ulen); 634926Swnj if (((struct ip *)ui)->ip_len != len) { 644926Swnj if (len > ((struct ip *)ui)->ip_len) { 654926Swnj udpstat.udps_badlen++; 664926Swnj goto bad; 674926Swnj } 6815539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 694926Swnj /* (struct ip *)ui->ip_len = len; */ 704926Swnj } 714926Swnj 724926Swnj /* 735246Sroot * Checksum extended UDP header and data. 744926Swnj */ 7515539Skarels if (udpcksum && ui->ui_sum) { 764926Swnj ui->ui_next = ui->ui_prev = 0; 774926Swnj ui->ui_x1 = 0; 787844Sroot ui->ui_len = htons((u_short)len); 797844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 804926Swnj udpstat.udps_badsum++; 814901Swnj m_freem(m); 824901Swnj return; 834901Swnj } 844901Swnj } 854926Swnj 864926Swnj /* 877844Sroot * Locate pcb for datagram. 884926Swnj */ 894926Swnj inp = in_pcblookup(&udb, 906029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 916029Sroot INPLOOKUP_WILDCARD); 926584Ssam if (inp == 0) { 9310144Ssam /* don't send ICMP response for broadcast packet */ 9410144Ssam if (in_lnaof(ui->ui_dst) == INADDR_ANY) 956584Ssam goto bad; 966584Ssam icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT); 976584Ssam return; 986584Ssam } 996584Ssam 1004926Swnj /* 1014926Swnj * Construct sockaddr format source address. 1024926Swnj * Stuff source address and datagram in user buffer. 1034926Swnj */ 1044926Swnj udp_in.sin_port = ui->ui_sport; 1054926Swnj udp_in.sin_addr = ui->ui_src; 1065050Swnj m->m_len -= sizeof (struct udpiphdr); 1075050Swnj m->m_off += sizeof (struct udpiphdr); 10812767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 10912767Ssam m, (struct mbuf *)0) == 0) 1104926Swnj goto bad; 1115050Swnj sorwakeup(inp->inp_socket); 1124887Swnj return; 1134926Swnj bad: 1144887Swnj m_freem(m); 1154784Swnj } 1164784Swnj 1176584Ssam udp_abort(inp) 1186584Ssam struct inpcb *inp; 1196584Ssam { 1206584Ssam struct socket *so = inp->inp_socket; 1216584Ssam 1226584Ssam in_pcbdisconnect(inp); 1236584Ssam soisdisconnected(so); 1246584Ssam } 1256584Ssam 1266591Ssam udp_ctlinput(cmd, arg) 1276591Ssam int cmd; 1286591Ssam caddr_t arg; 1296591Ssam { 1306591Ssam struct in_addr *sin; 1316591Ssam extern u_char inetctlerrmap[]; 1326591Ssam 1336591Ssam if (cmd < 0 || cmd > PRC_NCMDS) 1346591Ssam return; 1356591Ssam switch (cmd) { 1366591Ssam 1376591Ssam case PRC_ROUTEDEAD: 1386591Ssam break; 1396591Ssam 1406591Ssam case PRC_QUENCH: 1416591Ssam break; 1426591Ssam 1436591Ssam /* these are handled by ip */ 1446591Ssam case PRC_IFDOWN: 1456591Ssam case PRC_HOSTDEAD: 1466591Ssam case PRC_HOSTUNREACH: 1476591Ssam break; 1486591Ssam 1496591Ssam default: 1506591Ssam sin = &((struct icmp *)arg)->icmp_ip.ip_dst; 1518641Sroot in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort); 1526591Ssam } 1536591Ssam } 1546591Ssam 1554955Swnj udp_output(inp, m0) 1564926Swnj struct inpcb *inp; 1574926Swnj struct mbuf *m0; 1584784Swnj { 1594926Swnj register struct mbuf *m; 1604926Swnj register struct udpiphdr *ui; 1617157Swnj register struct socket *so; 1624926Swnj register int len = 0; 16316601Ssam register struct route *ro; 1644784Swnj 1654926Swnj /* 1664926Swnj * Calculate data length and get a mbuf 1675246Sroot * for UDP and IP headers. 1684926Swnj */ 1694926Swnj for (m = m0; m; m = m->m_next) 1704926Swnj len += m->m_len; 1719644Ssam m = m_get(M_DONTWAIT, MT_HEADER); 1726507Ssam if (m == 0) { 1736507Ssam m_freem(m0); 1746507Ssam return (ENOBUFS); 1756507Ssam } 1764784Swnj 1774926Swnj /* 1785246Sroot * Fill in mbuf with extended UDP header 1794926Swnj * and addresses and length put into network format. 1804926Swnj */ 1814926Swnj m->m_off = MMAXOFF - sizeof (struct udpiphdr); 1824926Swnj m->m_len = sizeof (struct udpiphdr); 1834926Swnj m->m_next = m0; 1844926Swnj ui = mtod(m, struct udpiphdr *); 1854926Swnj ui->ui_next = ui->ui_prev = 0; 1864926Swnj ui->ui_x1 = 0; 1874926Swnj ui->ui_pr = IPPROTO_UDP; 18815226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 1895050Swnj ui->ui_src = inp->inp_laddr; 1905050Swnj ui->ui_dst = inp->inp_faddr; 1915050Swnj ui->ui_sport = inp->inp_lport; 1925050Swnj ui->ui_dport = inp->inp_fport; 19315226Ssam ui->ui_ulen = ui->ui_len; 1944784Swnj 1954926Swnj /* 1964926Swnj * Stuff checksum and output datagram. 1974926Swnj */ 1984926Swnj ui->ui_sum = 0; 19915539Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 20015539Skarels ui->ui_sum = -1; 2015050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 2025050Swnj ((struct ip *)ui)->ip_ttl = MAXTTL; 2037157Swnj so = inp->inp_socket; 20416601Ssam if (so->so_options & SO_DONTROUTE) 20516601Ssam return (ip_output(m, (struct mbuf *)0, (struct route *)0, 206*16799Skarels (so->so_state & SS_PRIV) | IP_ROUTETOIF)); 20716601Ssam /* 20816601Ssam * Use cached route for previous datagram if 20916601Ssam * this is also to the same destination. 21016601Ssam * 21116601Ssam * NB: We don't handle broadcasts because that 21216601Ssam * would require 3 subroutine calls. 21316601Ssam */ 21416601Ssam ro = &inp->inp_route; 21516601Ssam #define satosin(sa) ((struct sockaddr_in *)(sa)) 21616601Ssam if (ro->ro_rt && 21716601Ssam satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) { 21816601Ssam RTFREE(ro->ro_rt); 21916601Ssam ro->ro_rt = (struct rtentry *)0; 22016601Ssam } 22116601Ssam return (ip_output(m, (struct mbuf *)0, ro, so->so_state & SS_PRIV)); 2224784Swnj } 2234784Swnj 2248602Sroot /*ARGSUSED*/ 22512767Ssam udp_usrreq(so, req, m, nam, rights) 2264887Swnj struct socket *so; 2274784Swnj int req; 22812767Ssam struct mbuf *m, *nam, *rights; 2294784Swnj { 2304887Swnj struct inpcb *inp = sotoinpcb(so); 2316507Ssam int error = 0; 2324784Swnj 23312767Ssam if (rights && rights->m_len) { 23412767Ssam error = EINVAL; 23512767Ssam goto release; 23612767Ssam } 23711080Ssam if (inp == NULL && req != PRU_ATTACH) { 23811080Ssam error = EINVAL; 23911080Ssam goto release; 24011080Ssam } 2414784Swnj switch (req) { 2424784Swnj 2434784Swnj case PRU_ATTACH: 24411080Ssam if (inp != NULL) { 24511080Ssam error = EINVAL; 24611080Ssam break; 24711080Ssam } 2488273Sroot error = in_pcballoc(so, &udb); 2498273Sroot if (error) 2508273Sroot break; 2519032Sroot error = soreserve(so, 2048, 2048); 2528273Sroot if (error) 2538273Sroot break; 2544887Swnj break; 2554784Swnj 2564784Swnj case PRU_DETACH: 25711080Ssam if (inp == NULL) { 25811080Ssam error = ENOTCONN; 25911080Ssam break; 26011080Ssam } 2615166Swnj in_pcbdetach(inp); 2624887Swnj break; 2634784Swnj 2648273Sroot case PRU_BIND: 2658273Sroot error = in_pcbbind(inp, nam); 2668273Sroot break; 2678273Sroot 2688273Sroot case PRU_LISTEN: 2698273Sroot error = EOPNOTSUPP; 2708273Sroot break; 2718273Sroot 2724784Swnj case PRU_CONNECT: 27311080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 27411080Ssam error = EISCONN; 27511080Ssam break; 27611080Ssam } 2778273Sroot error = in_pcbconnect(inp, nam); 2786507Ssam if (error == 0) 2796507Ssam soisconnected(so); 2804887Swnj break; 2814784Swnj 28213118Ssam case PRU_CONNECT2: 28313118Ssam error = EOPNOTSUPP; 28413118Ssam break; 28513118Ssam 2864926Swnj case PRU_ACCEPT: 28711080Ssam error = EOPNOTSUPP; 28811080Ssam break; 2894926Swnj 2904784Swnj case PRU_DISCONNECT: 29111080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 29211080Ssam error = ENOTCONN; 29311080Ssam break; 29411080Ssam } 2955166Swnj in_pcbdisconnect(inp); 2964887Swnj soisdisconnected(so); 2974784Swnj break; 2984784Swnj 2994912Swnj case PRU_SHUTDOWN: 3004912Swnj socantsendmore(so); 3014912Swnj break; 3024912Swnj 3035996Swnj case PRU_SEND: { 3045996Swnj struct in_addr laddr; 305*16799Skarels int s; 3065996Swnj 3078273Sroot if (nam) { 3085996Swnj laddr = inp->inp_laddr; 30911080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 31011080Ssam error = EISCONN; 31111080Ssam break; 31211080Ssam } 313*16799Skarels /* 314*16799Skarels * Must block input while temporarily connected. 315*16799Skarels */ 316*16799Skarels s = splnet(); 3178273Sroot error = in_pcbconnect(inp, nam); 318*16799Skarels if (error) { 319*16799Skarels splx(s); 3206507Ssam break; 321*16799Skarels } 3224955Swnj } else { 32311080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 32411080Ssam error = ENOTCONN; 32511080Ssam break; 32611080Ssam } 3274955Swnj } 3286507Ssam error = udp_output(inp, m); 32911080Ssam m = NULL; 3308273Sroot if (nam) { 3315166Swnj in_pcbdisconnect(inp); 332*16799Skarels splx(s); 3335996Swnj inp->inp_laddr = laddr; 3345996Swnj } 3355996Swnj } 3364784Swnj break; 3374784Swnj 3384784Swnj case PRU_ABORT: 3395166Swnj in_pcbdetach(inp); 3404887Swnj sofree(so); 3414887Swnj soisdisconnected(so); 3424784Swnj break; 3434784Swnj 3446511Ssam case PRU_SOCKADDR: 3458273Sroot in_setsockaddr(inp, nam); 3466511Ssam break; 3476511Ssam 34814124Ssam case PRU_PEERADDR: 34914124Ssam in_setpeeraddr(inp, nam); 35014124Ssam break; 35114124Ssam 35212205Ssam case PRU_SENDOOB: 35312205Ssam case PRU_FASTTIMO: 35412205Ssam case PRU_SLOWTIMO: 35512205Ssam case PRU_PROTORCV: 35612205Ssam case PRU_PROTOSEND: 35712205Ssam error = EOPNOTSUPP; 35812205Ssam break; 35913118Ssam 360*16799Skarels case PRU_CONTROL: 361*16799Skarels case PRU_SENSE: 362*16799Skarels case PRU_RCVD: 363*16799Skarels case PRU_RCVOOB: 364*16799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 365*16799Skarels 36613118Ssam default: 36713118Ssam panic("udp_usrreq"); 3684805Swnj } 36911080Ssam release: 37011080Ssam if (m != NULL) 37111080Ssam m_freem(m); 3726507Ssam return (error); 3734784Swnj } 374