X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/9190426d73f4221282c264c447ed8cecaec8d4aa..4d8170e59e43abc00690852d097c26731af5297c:/usr/src/sys/netinet/udp_usrreq.c diff --git a/usr/src/sys/netinet/udp_usrreq.c b/usr/src/sys/netinet/udp_usrreq.c index 3927d5eeec..c957a43e02 100644 --- a/usr/src/sys/netinet/udp_usrreq.c +++ b/usr/src/sys/netinet/udp_usrreq.c @@ -1,19 +1,34 @@ -/* udp_usrreq.c 4.10 81/11/24 */ - -#include "../h/param.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/mbuf.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../h/socketvar.h" -#include "../net/inet.h" -#include "../net/inet_pcb.h" -#include "../net/inet_systm.h" -#include "../net/ip.h" -#include "../net/ip_var.h" -#include "../net/udp.h" -#include "../net/udp_var.h" +/* + * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. + * All rights reserved. + * + * %sccs.include.redist.c% + * + * @(#)udp_usrreq.c 7.31 (Berkeley) %G% + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct inpcb *udp_last_inpcb = &udb; /* * UDP protocol implementation. @@ -21,192 +36,481 @@ */ udp_init() { - udb.inp_next = udb.inp_prev = &udb; } -int udpcksum; -struct sockaddr_in udp_in = { AF_INET }; +#ifndef COMPAT_42 +int udpcksum = 1; +#else +int udpcksum = 0; /* XXX */ +#endif + +struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; -udp_input(m0) - struct mbuf *m0; +udp_input(m, iphlen) + register struct mbuf *m; + int iphlen; { - register struct udpiphdr *ui; + register struct ip *ip; + register struct udphdr *uh; register struct inpcb *inp; - register struct mbuf *m; - int len, ulen; + struct mbuf *opts = 0; + int len; + struct ip save_ip; + + udpstat.udps_ipackets++; /* - * Get ip and udp header together in first mbuf. + * Strip IP options, if any; should skip this, + * make available to user, and use on returned packets, + * but we don't yet have a way to check the checksum + * with options still present. */ - m = m0; - if (m->m_len < sizeof (struct udpiphdr) && - m_pullup(m, sizeof (struct udpiphdr)) == 0) { - udpstat.udps_hdrops++; - goto bad; + if (iphlen > sizeof (struct ip)) { + ip_stripoptions(m, (struct mbuf *)0); + iphlen = sizeof(struct ip); } - ui = mtod(m, struct udpiphdr *); - if (ui->ui_len > sizeof (struct ip)) - ip_stripoptions((struct ip *)ui, (char *)0); /* - * Make mbuf data length reflect udp length. - * If not enough data to reflect udp length, drop. + * Get IP and UDP header together in first mbuf. */ - ulen = ntohs((u_short)ui->ui_ulen); - len = sizeof (struct udpiphdr) + ulen; - if (((struct ip *)ui)->ip_len != len) { - if (len > ((struct ip *)ui)->ip_len) { + ip = mtod(m, struct ip *); + if (m->m_len < iphlen + sizeof(struct udphdr)) { + if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { + udpstat.udps_hdrops++; + return; + } + ip = mtod(m, struct ip *); + } + uh = (struct udphdr *)((caddr_t)ip + iphlen); + + /* + * Make mbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + len = ntohs((u_short)uh->uh_ulen); + if (ip->ip_len != len) { + if (len > ip->ip_len) { udpstat.udps_badlen++; goto bad; } - m_adj(m, ((struct ip *)ui)->ip_len - len); - /* (struct ip *)ui->ip_len = len; */ + m_adj(m, len - ip->ip_len); + /* ip->ip_len = len; */ } + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip = *ip; /* - * Checksum extended udp header and data. + * Checksum extended UDP header and data. */ - if (udpcksum) { - ui->ui_next = ui->ui_prev = 0; - ui->ui_x1 = 0; - ui->ui_len = htons((u_short)(sizeof (struct udpiphdr) + ulen)); - if ((ui->ui_sum = inet_cksum(m, len)) != 0xffff) { + if (udpcksum && uh->uh_sum) { + ((struct ipovly *)ip)->ih_next = 0; + ((struct ipovly *)ip)->ih_prev = 0; + ((struct ipovly *)ip)->ih_x1 = 0; + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { udpstat.udps_badsum++; - printf("udp cksum %x\n", ui->ui_sum); m_freem(m); return; } } + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || + in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) { + struct socket *last; + /* + * Deliver a multicast or broadcast datagram to *all* sockets + * for which the local and remote addresses and ports match + * those of the incoming datagram. This allows more than + * one process to receive multi/broadcasts on the same port. + * (This really ought to be done for unicast datagrams as + * well, but that would cause problems with existing + * applications that open both address-specific sockets and + * a wildcard socket listening to the same port -- they would + * end up receiving duplicates of every unicast datagram. + * Those applications open the multiple sockets to overcome an + * inadequacy of the UDP socket interface, but for backwards + * compatibility we avoid the problem here rather than + * fixing the interface. Maybe 4.5BSD will remedy this?) + */ + + /* + * Construct sockaddr format source address. + */ + udp_in.sin_port = uh->uh_sport; + udp_in.sin_addr = ip->ip_src; + m->m_len -= sizeof (struct udpiphdr); + m->m_data += sizeof (struct udpiphdr); + /* + * Locate pcb(s) for datagram. + * (Algorithm copied from raw_intr().) + */ + last = NULL; + for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) { + if (inp->inp_lport != uh->uh_dport) + continue; + if (inp->inp_laddr.s_addr != INADDR_ANY) { + if (inp->inp_laddr.s_addr != + ip->ip_dst.s_addr) + continue; + } + if (inp->inp_faddr.s_addr != INADDR_ANY) { + if (inp->inp_faddr.s_addr != + ip->ip_src.s_addr || + inp->inp_fport != uh->uh_sport) + continue; + } + + if (last != NULL) { + struct mbuf *n; + + if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { + if (sbappendaddr(&last->so_rcv, + (struct sockaddr *)&udp_in, + n, (struct mbuf *)0) == 0) { + m_freem(n); + udpstat.udps_fullsock++; + } else + sorwakeup(last); + } + } + last = inp->inp_socket; + /* + * Don't look for additional matches if this one + * does not have the SO_REUSEPORT socket option set. + * This heuristic avoids searching through all pcbs + * in the common case of a non-shared port. It + * assumes that an application will never clear + * the SO_REUSEPORT option after setting it. + */ + if ((last->so_options & SO_REUSEPORT) == 0) + break; + } + + if (last == NULL) { + /* + * No matching pcb found; discard datagram. + * (No need to send an ICMP Port Unreachable + * for a broadcast or multicast datgram.) + */ + udpstat.udps_noportbcast++; + goto bad; + } + if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in, + m, (struct mbuf *)0) == 0) { + udpstat.udps_fullsock++; + goto bad; + } + sorwakeup(last); + return; + } /* - * Convert addresses and ports to host format. * Locate pcb for datagram. */ - inp = in_pcblookup(&udb, - ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport); - if (inp == 0) - goto bad; + inp = udp_last_inpcb; + if (inp->inp_lport != uh->uh_dport || + inp->inp_fport != uh->uh_sport || + inp->inp_faddr.s_addr != ip->ip_src.s_addr || + inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { + inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, + ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); + if (inp) + udp_last_inpcb = inp; + udpstat.udpps_pcbcachemiss++; + } + if (inp == 0) { + udpstat.udps_noport++; + *ip = save_ip; + ip->ip_len += iphlen; + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); + return; + } /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ - udp_in.sin_port = ui->ui_sport; - udp_in.sin_addr = ui->ui_src; - m->m_len -= sizeof (struct udpiphdr); - m->m_off += sizeof (struct udpiphdr); - if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0) + udp_in.sin_port = uh->uh_sport; + udp_in.sin_addr = ip->ip_src; + if (inp->inp_flags & INP_CONTROLOPTS) { + struct mbuf **mp = &opts; + struct mbuf *udp_saveopt(); + + if (inp->inp_flags & INP_RECVDSTADDR) { + *mp = udp_saveopt((caddr_t) &ip->ip_dst, + sizeof(struct in_addr), IP_RECVDSTADDR); + if (*mp) + mp = &(*mp)->m_next; + } +#ifdef notyet + /* options were tossed above */ + if (inp->inp_flags & INP_RECVOPTS) { + *mp = udp_saveopt((caddr_t) opts_deleted_above, + sizeof(struct in_addr), IP_RECVOPTS); + if (*mp) + mp = &(*mp)->m_next; + } + /* ip_srcroute doesn't do what we want here, need to fix */ + if (inp->inp_flags & INP_RECVRETOPTS) { + *mp = udp_saveopt((caddr_t) ip_srcroute(), + sizeof(struct in_addr), IP_RECVRETOPTS); + if (*mp) + mp = &(*mp)->m_next; + } +#endif + } + iphlen += sizeof(struct udphdr); + m->m_len -= iphlen; + m->m_pkthdr.len -= iphlen; + m->m_data += iphlen; + if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, + m, opts) == 0) { + udpstat.udps_fullsock++; goto bad; + } sorwakeup(inp->inp_socket); return; bad: m_freem(m); + if (opts) + m_freem(opts); } -udp_ctlinput(m) - struct mbuf *m; +/* + * Create a "control" mbuf containing the specified data + * with the specified type for presentation with a datagram. + */ +struct mbuf * +udp_saveopt(p, size, type) + caddr_t p; + register int size; + int type; { + register struct cmsghdr *cp; + struct mbuf *m; - printf("udp_ctlinput\n"); - m_freem(m); + if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) + return ((struct mbuf *) NULL); + cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); + bcopy(p, (caddr_t)(cp + 1), size); + size += sizeof(*cp); + m->m_len = size; + cp->cmsg_len = size; + cp->cmsg_level = IPPROTO_IP; + cp->cmsg_type = type; + return (m); } -/*ARGSUSED*/ -udp_output(inp, m0) - struct inpcb *inp; - struct mbuf *m0; +/* + * Notify a udp user of an asynchronous error; + * just wake up so that he can collect error status. + */ +udp_notify(inp, errno) + register struct inpcb *inp; + int errno; { + inp->inp_socket->so_error = errno; + sorwakeup(inp->inp_socket); + sowwakeup(inp->inp_socket); +} + +udp_ctlinput(cmd, sa, ip) + int cmd; + struct sockaddr *sa; + register struct ip *ip; +{ + register struct udphdr *uh; + extern struct in_addr zeroin_addr; + extern u_char inetctlerrmap[]; + + if (!PRC_IS_REDIRECT(cmd) && + ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0)) + return; + if (ip) { + uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); + in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, + cmd, udp_notify); + } else + in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); +} + +udp_output(inp, m, addr, control) + register struct inpcb *inp; register struct mbuf *m; + struct mbuf *addr, *control; +{ register struct udpiphdr *ui; - register int len = 0; + register int len = m->m_pkthdr.len; + struct in_addr laddr; + int s, error = 0; + + if (control) + m_freem(control); /* XXX */ + if (addr) { + laddr = inp->inp_laddr; + if (inp->inp_faddr.s_addr != INADDR_ANY) { + error = EISCONN; + goto release; + } + /* + * Must block input while temporarily connected. + */ + s = splnet(); + error = in_pcbconnect(inp, addr); + if (error) { + splx(s); + goto release; + } + } else { + if (inp->inp_faddr.s_addr == INADDR_ANY) { + error = ENOTCONN; + goto release; + } + } /* * Calculate data length and get a mbuf - * for udp and ip headers. + * for UDP and IP headers. */ - for (m = m0; m; m = m->m_next) - len += m->m_len; - m = m_get(0); - if (m == 0) - goto bad; + M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); + if (m == 0) { + error = ENOBUFS; + goto release; + } /* - * Fill in mbuf with extended udp header + * Fill in mbuf with extended UDP header * and addresses and length put into network format. */ - m->m_off = MMAXOFF - sizeof (struct udpiphdr); - m->m_len = sizeof (struct udpiphdr); - m->m_next = m0; ui = mtod(m, struct udpiphdr *); ui->ui_next = ui->ui_prev = 0; ui->ui_x1 = 0; ui->ui_pr = IPPROTO_UDP; - ui->ui_len = sizeof (struct udpiphdr) + len; + ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); ui->ui_src = inp->inp_laddr; ui->ui_dst = inp->inp_faddr; ui->ui_sport = inp->inp_lport; ui->ui_dport = inp->inp_fport; - ui->ui_ulen = htons((u_short)len); + ui->ui_ulen = ui->ui_len; /* * Stuff checksum and output datagram. */ ui->ui_sum = 0; - ui->ui_sum = inet_cksum(m, sizeof (struct udpiphdr) + len); + if (udpcksum) { + if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) + ui->ui_sum = 0xffff; + } ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; - ((struct ip *)ui)->ip_ttl = MAXTTL; - ip_output(m); - return; -bad: + ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ + ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ + udpstat.udps_opackets++; + error = ip_output(m, inp->inp_options, &inp->inp_route, + inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST), + inp->inp_moptions); + + if (addr) { + in_pcbdisconnect(inp); + inp->inp_laddr = laddr; + splx(s); + } + return (error); + +release: m_freem(m); + return (error); } +u_long udp_sendspace = 9216; /* really max datagram size */ +u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); + /* 40 1K datagrams */ + /*ARGSUSED*/ -udp_usrreq(so, req, m, addr) +udp_usrreq(so, req, m, addr, control) struct socket *so; int req; - struct mbuf *m; - caddr_t addr; + struct mbuf *m, *addr, *control; { struct inpcb *inp = sotoinpcb(so); - int error; + int error = 0; + int s; - if (inp == 0 && req != PRU_ATTACH) - return (EINVAL); + if (req == PRU_CONTROL) + return (in_control(so, (int)m, (caddr_t)addr, + (struct ifnet *)control)); + if (inp == NULL && req != PRU_ATTACH) { + error = EINVAL; + goto release; + } + /* + * Note: need to block udp_input while changing + * the udp pcb queue and/or pcb addresses. + */ switch (req) { case PRU_ATTACH: - if (inp != 0) - return (EINVAL); - error = in_pcballoc(so, &udb, 2048, 2048, (struct sockaddr_in *)addr); + if (inp != NULL) { + error = EINVAL; + break; + } + s = splnet(); + error = in_pcballoc(so, &udb); + splx(s); + if (error) + break; + error = soreserve(so, udp_sendspace, udp_recvspace); if (error) - return (error); + break; + ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl; break; case PRU_DETACH: - if (inp == 0) - return (ENOTCONN); - in_pcbfree(inp); + udp_detach(inp); + break; + + case PRU_BIND: + s = splnet(); + error = in_pcbbind(inp, addr); + splx(s); + break; + + case PRU_LISTEN: + error = EOPNOTSUPP; break; case PRU_CONNECT: - if (inp->inp_faddr.s_addr) - return (EISCONN); - error = in_pcbsetpeer(inp, (struct sockaddr_in *)addr); - if (error) - return (error); - soisconnected(so); + if (inp->inp_faddr.s_addr != INADDR_ANY) { + error = EISCONN; + break; + } + s = splnet(); + error = in_pcbconnect(inp, addr); + splx(s); + if (error == 0) + soisconnected(so); + break; + + case PRU_CONNECT2: + error = EOPNOTSUPP; break; case PRU_ACCEPT: - return (EOPNOTSUPP); + error = EOPNOTSUPP; + break; case PRU_DISCONNECT: - if (inp->inp_faddr.s_addr == 0) - return (ENOTCONN); - inp->inp_faddr.s_addr = 0; - soisdisconnected(so); + if (inp->inp_faddr.s_addr == INADDR_ANY) { + error = ENOTCONN; + break; + } + s = splnet(); + in_pcbdisconnect(inp); + inp->inp_laddr.s_addr = INADDR_ANY; + splx(s); + so->so_state &= ~SS_ISCONNECTED; /* XXX */ break; case PRU_SHUTDOWN: @@ -214,41 +518,86 @@ udp_usrreq(so, req, m, addr) break; case PRU_SEND: - if (addr) { - if (inp->inp_faddr.s_addr) - return (EISCONN); - error = in_pcbsetpeer(inp, (struct sockaddr_in *)addr); - if (error) - return (error); - } else { - if (inp->inp_faddr.s_addr == 0) - return (ENOTCONN); - } - udp_output(inp, m); - if (addr) - inp->inp_faddr.s_addr = 0; - break; + return (udp_output(inp, m, addr, control)); case PRU_ABORT: - in_pcbfree(inp); - sofree(so); soisdisconnected(so); + udp_detach(inp); + break; + + case PRU_SOCKADDR: + in_setsockaddr(inp, addr); + break; + + case PRU_PEERADDR: + in_setpeeraddr(inp, addr); + break; + + case PRU_SENSE: + /* + * stat: don't bother with a blocksize. + */ + return (0); + + case PRU_SENDOOB: + case PRU_FASTTIMO: + case PRU_SLOWTIMO: + case PRU_PROTORCV: + case PRU_PROTOSEND: + error = EOPNOTSUPP; break; - case PRU_CONTROL: - return (EOPNOTSUPP); + case PRU_RCVD: + case PRU_RCVOOB: + return (EOPNOTSUPP); /* do not free mbuf's */ default: panic("udp_usrreq"); } - return (0); + +release: + if (control) { + printf("udp control data unexpectedly retained\n"); + m_freem(control); + } + if (m) + m_freem(m); + return (error); } -/*ARGSUSED*/ -udp_sense(m) - struct mbuf *m; +udp_detach(inp) + struct inpcb *inp; +{ + int s = splnet(); + + if (inp == udp_last_inpcb) + udp_last_inpcb = &udb; + in_pcbdetach(inp); + splx(s); +} + +/* + * Sysctl for udp variables. + */ +udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; { + extern int ip_ttl; - printf("udp_sense\n"); - return (EOPNOTSUPP); + /* all sysctl names at this level are terminal */ + if (namelen != 1) + return (ENOTDIR); + + switch (name[0]) { + case UDPCTL_CHECKSUM: + return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum)); + default: + return (ENOPROTOOPT); + } + /* NOTREACHED */ }