1*23199Smckusick /* 2*23199Smckusick * Copyright (c) 1982 Regents of the University of California. 3*23199Smckusick * All rights reserved. The Berkeley software License Agreement 4*23199Smckusick * specifies the terms and conditions for redistribution. 5*23199Smckusick * 6*23199Smckusick * @(#)udp_usrreq.c 6.14 (Berkeley) 06/08/85 7*23199Smckusick */ 84784Swnj 917065Sbloom #include "param.h" 1017065Sbloom #include "dir.h" 1117065Sbloom #include "user.h" 1217065Sbloom #include "mbuf.h" 1317065Sbloom #include "protosw.h" 1417065Sbloom #include "socket.h" 1517065Sbloom #include "socketvar.h" 1617065Sbloom #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_pcb.h" 2417065Sbloom #include "in_systm.h" 2517065Sbloom #include "ip.h" 2617065Sbloom #include "ip_var.h" 2717065Sbloom #include "ip_icmp.h" 2817065Sbloom #include "udp.h" 2917065Sbloom #include "udp_var.h" 304784Swnj 314926Swnj /* 324926Swnj * UDP protocol implementation. 334926Swnj * Per RFC 768, August, 1980. 344926Swnj */ 354805Swnj udp_init() 364805Swnj { 374805Swnj 384901Swnj udb.inp_next = udb.inp_prev = &udb; 394805Swnj } 404805Swnj 4119519Skarels int udpcksum = 1; 424926Swnj struct sockaddr_in udp_in = { AF_INET }; 434901Swnj 444926Swnj udp_input(m0) 454926Swnj struct mbuf *m0; 464784Swnj { 474901Swnj register struct udpiphdr *ui; 484887Swnj register struct inpcb *inp; 494926Swnj register struct mbuf *m; 507844Sroot int len; 514784Swnj 524926Swnj /* 535246Sroot * Get IP and UDP header together in first mbuf. 544926Swnj */ 554926Swnj m = m0; 565308Sroot if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) && 575308Sroot (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 584926Swnj udpstat.udps_hdrops++; 595308Sroot return; 604926Swnj } 615050Swnj ui = mtod(m, struct udpiphdr *); 625246Sroot if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2)) 635220Swnj ip_stripoptions((struct ip *)ui, (struct mbuf *)0); 644926Swnj 654926Swnj /* 665246Sroot * Make mbuf data length reflect UDP length. 675246Sroot * If not enough data to reflect UDP length, drop. 684926Swnj */ 697844Sroot len = ntohs((u_short)ui->ui_ulen); 704926Swnj if (((struct ip *)ui)->ip_len != len) { 714926Swnj if (len > ((struct ip *)ui)->ip_len) { 724926Swnj udpstat.udps_badlen++; 734926Swnj goto bad; 744926Swnj } 7515539Skarels m_adj(m, len - ((struct ip *)ui)->ip_len); 764926Swnj /* (struct ip *)ui->ip_len = len; */ 774926Swnj } 784926Swnj 794926Swnj /* 805246Sroot * Checksum extended UDP header and data. 814926Swnj */ 8215539Skarels if (udpcksum && ui->ui_sum) { 834926Swnj ui->ui_next = ui->ui_prev = 0; 844926Swnj ui->ui_x1 = 0; 8521112Skarels ui->ui_len = ui->ui_ulen; 867844Sroot if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 874926Swnj udpstat.udps_badsum++; 884901Swnj m_freem(m); 894901Swnj return; 904901Swnj } 914901Swnj } 924926Swnj 934926Swnj /* 947844Sroot * Locate pcb for datagram. 954926Swnj */ 964926Swnj inp = in_pcblookup(&udb, 976029Sroot ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 986029Sroot INPLOOKUP_WILDCARD); 996584Ssam if (inp == 0) { 10010144Ssam /* don't send ICMP response for broadcast packet */ 10121112Skarels if (in_broadcast(ui->ui_dst)) 1026584Ssam goto bad; 1036584Ssam icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT); 1046584Ssam return; 1056584Ssam } 1066584Ssam 1074926Swnj /* 1084926Swnj * Construct sockaddr format source address. 1094926Swnj * Stuff source address and datagram in user buffer. 1104926Swnj */ 1114926Swnj udp_in.sin_port = ui->ui_sport; 1124926Swnj udp_in.sin_addr = ui->ui_src; 1135050Swnj m->m_len -= sizeof (struct udpiphdr); 1145050Swnj m->m_off += sizeof (struct udpiphdr); 11512767Ssam if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 11612767Ssam m, (struct mbuf *)0) == 0) 1174926Swnj goto bad; 1185050Swnj sorwakeup(inp->inp_socket); 1194887Swnj return; 1204926Swnj bad: 1214887Swnj m_freem(m); 1224784Swnj } 1234784Swnj 1246584Ssam udp_abort(inp) 1256584Ssam struct inpcb *inp; 1266584Ssam { 1276584Ssam struct socket *so = inp->inp_socket; 1286584Ssam 1296584Ssam in_pcbdisconnect(inp); 1306584Ssam soisdisconnected(so); 1316584Ssam } 1326584Ssam 1336591Ssam udp_ctlinput(cmd, arg) 1346591Ssam int cmd; 1356591Ssam caddr_t arg; 1366591Ssam { 13721121Skarels struct in_addr *in; 1386591Ssam extern u_char inetctlerrmap[]; 13921121Skarels int in_rtchange(); 1406591Ssam 1416591Ssam if (cmd < 0 || cmd > PRC_NCMDS) 1426591Ssam return; 1436591Ssam switch (cmd) { 1446591Ssam 1456591Ssam case PRC_ROUTEDEAD: 1466591Ssam break; 1476591Ssam 14821121Skarels case PRC_REDIRECT_NET: 14921121Skarels case PRC_REDIRECT_HOST: 15021121Skarels in = &((struct icmp *)arg)->icmp_ip.ip_dst; 15121121Skarels in_pcbnotify(&udb, in, 0, in_rtchange); 1526591Ssam break; 1536591Ssam 1546591Ssam case PRC_IFDOWN: 15521121Skarels in = &((struct sockaddr_in *)arg)->sin_addr; 15621121Skarels goto notify; 15721121Skarels 1586591Ssam case PRC_HOSTDEAD: 1596591Ssam case PRC_HOSTUNREACH: 16021121Skarels in = (struct in_addr *)arg; 16121121Skarels goto notify; 1626591Ssam 1636591Ssam default: 16421121Skarels if (inetctlerrmap[cmd] == 0) 16521121Skarels return; /* XXX */ 16621121Skarels in = &((struct icmp *)arg)->icmp_ip.ip_dst; 16721121Skarels notify: 16821121Skarels in_pcbnotify(&udb, in, (int)inetctlerrmap[cmd], udp_abort); 1696591Ssam } 1706591Ssam } 1716591Ssam 1724955Swnj udp_output(inp, m0) 1734926Swnj struct inpcb *inp; 1744926Swnj struct mbuf *m0; 1754784Swnj { 1764926Swnj register struct mbuf *m; 1774926Swnj register struct udpiphdr *ui; 17817165Skarels register struct socket *so; 17917165Skarels register int len = 0; 18016601Ssam register struct route *ro; 1814784Swnj 1824926Swnj /* 1834926Swnj * Calculate data length and get a mbuf 1845246Sroot * for UDP and IP headers. 1854926Swnj */ 1864926Swnj for (m = m0; m; m = m->m_next) 1874926Swnj len += m->m_len; 18821112Skarels MGET(m, M_DONTWAIT, MT_HEADER); 1896507Ssam if (m == 0) { 1906507Ssam m_freem(m0); 1916507Ssam return (ENOBUFS); 1926507Ssam } 1934784Swnj 1944926Swnj /* 1955246Sroot * Fill in mbuf with extended UDP header 1964926Swnj * and addresses and length put into network format. 1974926Swnj */ 1984926Swnj m->m_off = MMAXOFF - sizeof (struct udpiphdr); 1994926Swnj m->m_len = sizeof (struct udpiphdr); 2004926Swnj m->m_next = m0; 2014926Swnj ui = mtod(m, struct udpiphdr *); 2024926Swnj ui->ui_next = ui->ui_prev = 0; 2034926Swnj ui->ui_x1 = 0; 2044926Swnj ui->ui_pr = IPPROTO_UDP; 20515226Ssam ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 2065050Swnj ui->ui_src = inp->inp_laddr; 2075050Swnj ui->ui_dst = inp->inp_faddr; 2085050Swnj ui->ui_sport = inp->inp_lport; 2095050Swnj ui->ui_dport = inp->inp_fport; 21015226Ssam ui->ui_ulen = ui->ui_len; 2114784Swnj 2124926Swnj /* 2134926Swnj * Stuff checksum and output datagram. 2144926Swnj */ 2154926Swnj ui->ui_sum = 0; 21619519Skarels if (udpcksum) { 21719519Skarels if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 21815539Skarels ui->ui_sum = -1; 21921112Skarels } 2205050Swnj ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 2215050Swnj ((struct ip *)ui)->ip_ttl = MAXTTL; 22217165Skarels so = inp->inp_socket; 22317165Skarels if (so->so_options & SO_DONTROUTE) 22417165Skarels return (ip_output(m, (struct mbuf *)0, (struct route *)0, 22517165Skarels (so->so_options & SO_BROADCAST) | IP_ROUTETOIF)); 22617165Skarels /* 22717165Skarels * Use cached route for previous datagram if 22817165Skarels * this is also to the same destination. 22917165Skarels * 23017165Skarels * NB: We don't handle broadcasts because that 23117165Skarels * would require 3 subroutine calls. 23217165Skarels */ 23317165Skarels ro = &inp->inp_route; 23416601Ssam #define satosin(sa) ((struct sockaddr_in *)(sa)) 23517165Skarels if (ro->ro_rt && 23617165Skarels satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) { 23717165Skarels RTFREE(ro->ro_rt); 23817165Skarels ro->ro_rt = (struct rtentry *)0; 23917165Skarels } 24017165Skarels return (ip_output(m, (struct mbuf *)0, ro, 24117165Skarels so->so_options & SO_BROADCAST)); 2424784Swnj } 2434784Swnj 24418368Skarels int udp_sendspace = 2048; /* really max datagram size */ 24518368Skarels int udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 24618368Skarels 2478602Sroot /*ARGSUSED*/ 24812767Ssam udp_usrreq(so, req, m, nam, rights) 2494887Swnj struct socket *so; 2504784Swnj int req; 25112767Ssam struct mbuf *m, *nam, *rights; 2524784Swnj { 2534887Swnj struct inpcb *inp = sotoinpcb(so); 2546507Ssam int error = 0; 2554784Swnj 25618368Skarels if (req == PRU_CONTROL) 25718368Skarels return (in_control(so, (int)m, (caddr_t)nam, 25818368Skarels (struct ifnet *)rights)); 25912767Ssam if (rights && rights->m_len) { 26012767Ssam error = EINVAL; 26112767Ssam goto release; 26212767Ssam } 26311080Ssam if (inp == NULL && req != PRU_ATTACH) { 26411080Ssam error = EINVAL; 26511080Ssam goto release; 26611080Ssam } 2674784Swnj switch (req) { 2684784Swnj 2694784Swnj case PRU_ATTACH: 27011080Ssam if (inp != NULL) { 27111080Ssam error = EINVAL; 27211080Ssam break; 27311080Ssam } 2748273Sroot error = in_pcballoc(so, &udb); 2758273Sroot if (error) 2768273Sroot break; 27718368Skarels error = soreserve(so, udp_sendspace, udp_recvspace); 2788273Sroot if (error) 2798273Sroot break; 2804887Swnj break; 2814784Swnj 2824784Swnj case PRU_DETACH: 28311080Ssam if (inp == NULL) { 28411080Ssam error = ENOTCONN; 28511080Ssam break; 28611080Ssam } 2875166Swnj in_pcbdetach(inp); 2884887Swnj break; 2894784Swnj 2908273Sroot case PRU_BIND: 2918273Sroot error = in_pcbbind(inp, nam); 2928273Sroot break; 2938273Sroot 2948273Sroot case PRU_LISTEN: 2958273Sroot error = EOPNOTSUPP; 2968273Sroot break; 2978273Sroot 2984784Swnj case PRU_CONNECT: 29911080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 30011080Ssam error = EISCONN; 30111080Ssam break; 30211080Ssam } 3038273Sroot error = in_pcbconnect(inp, nam); 3046507Ssam if (error == 0) 3056507Ssam soisconnected(so); 3064887Swnj break; 3074784Swnj 30813118Ssam case PRU_CONNECT2: 30913118Ssam error = EOPNOTSUPP; 31013118Ssam break; 31113118Ssam 3124926Swnj case PRU_ACCEPT: 31311080Ssam error = EOPNOTSUPP; 31411080Ssam break; 3154926Swnj 3164784Swnj case PRU_DISCONNECT: 31711080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 31811080Ssam error = ENOTCONN; 31911080Ssam break; 32011080Ssam } 3215166Swnj in_pcbdisconnect(inp); 3224887Swnj soisdisconnected(so); 3234784Swnj break; 3244784Swnj 3254912Swnj case PRU_SHUTDOWN: 3264912Swnj socantsendmore(so); 3274912Swnj break; 3284912Swnj 3295996Swnj case PRU_SEND: { 3305996Swnj struct in_addr laddr; 33116799Skarels int s; 3325996Swnj 3338273Sroot if (nam) { 3345996Swnj laddr = inp->inp_laddr; 33511080Ssam if (inp->inp_faddr.s_addr != INADDR_ANY) { 33611080Ssam error = EISCONN; 33711080Ssam break; 33811080Ssam } 33916799Skarels /* 34016799Skarels * Must block input while temporarily connected. 34116799Skarels */ 34216799Skarels s = splnet(); 3438273Sroot error = in_pcbconnect(inp, nam); 34416799Skarels if (error) { 34516799Skarels splx(s); 3466507Ssam break; 34716799Skarels } 3484955Swnj } else { 34911080Ssam if (inp->inp_faddr.s_addr == INADDR_ANY) { 35011080Ssam error = ENOTCONN; 35111080Ssam break; 35211080Ssam } 3534955Swnj } 3546507Ssam error = udp_output(inp, m); 35511080Ssam m = NULL; 3568273Sroot if (nam) { 3575166Swnj in_pcbdisconnect(inp); 35818368Skarels inp->inp_laddr = laddr; 35916799Skarels splx(s); 3605996Swnj } 3615996Swnj } 3624784Swnj break; 3634784Swnj 3644784Swnj case PRU_ABORT: 3655166Swnj in_pcbdetach(inp); 3664887Swnj sofree(so); 3674887Swnj soisdisconnected(so); 3684784Swnj break; 3694784Swnj 3706511Ssam case PRU_SOCKADDR: 3718273Sroot in_setsockaddr(inp, nam); 3726511Ssam break; 3736511Ssam 37414124Ssam case PRU_PEERADDR: 37514124Ssam in_setpeeraddr(inp, nam); 37614124Ssam break; 37714124Ssam 37816988Skarels case PRU_SENSE: 37916988Skarels /* 38016988Skarels * stat: don't bother with a blocksize. 38116988Skarels */ 38216988Skarels return (0); 38316988Skarels 38412205Ssam case PRU_SENDOOB: 38512205Ssam case PRU_FASTTIMO: 38612205Ssam case PRU_SLOWTIMO: 38712205Ssam case PRU_PROTORCV: 38812205Ssam case PRU_PROTOSEND: 38912205Ssam error = EOPNOTSUPP; 39012205Ssam break; 39113118Ssam 39216799Skarels case PRU_RCVD: 39316799Skarels case PRU_RCVOOB: 39416799Skarels return (EOPNOTSUPP); /* do not free mbuf's */ 39516799Skarels 39613118Ssam default: 39713118Ssam panic("udp_usrreq"); 3984805Swnj } 39911080Ssam release: 40011080Ssam if (m != NULL) 40111080Ssam m_freem(m); 4026507Ssam return (error); 4034784Swnj } 404