-/* udp_usrreq.c 4.3 81/11/14 */
-
-#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_host.h"
-#include "../net/inet_pcb.h"
-#include "../net/inet_systm.h"
-#include "../net/udp.h"
-#include "../net/udp_var.h"
+/*
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)udp_usrreq.c 7.9 (Berkeley) %G%
+ */
+#include "param.h"
+#include "dir.h"
+#include "user.h"
+#include "malloc.h"
+#include "mbuf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "errno.h"
+#include "stat.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "in.h"
+#include "in_pcb.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "ip_var.h"
+#include "ip_icmp.h"
+#include "udp.h"
+#include "udp_var.h"
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
udp_init()
{
- udb.inp_next = udb.inp_prev = &udp;
+ udb.inp_next = udb.inp_prev = &udb;
}
-udp_input(m)
- struct mbuf *m;
+#ifndef COMPAT_42
+int udpcksum = 1;
+#else
+int udpcksum = 0; /* XXX */
+#endif
+int udp_ttl = UDP_TTL;
+
+struct sockaddr_in udp_in = { AF_INET };
+
+udp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
{
+ register struct udpiphdr *ui;
register struct inpcb *inp;
- int raddr, rport;
- int addr, port;
+ int len;
+ struct ip ip;
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ if (iphlen > sizeof (struct ip))
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < sizeof (struct udpiphdr)) {
+ if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
+ udpstat.udps_hdrops++;
+ return;
+ }
+ ui = mtod(m, struct udpiphdr *);
+ }
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((u_short)ui->ui_ulen);
+ if (((struct ip *)ui)->ip_len != len) {
+ if (len > ((struct ip *)ui)->ip_len) {
+ udpstat.udps_badlen++;
+ goto bad;
+ }
+ m_adj(m, len - ((struct ip *)ui)->ip_len);
+ /* ((struct ip *)ui)->ip_len = len; */
+ }
+ /*
+ * Save a copy of the IP header in case we want restore it for ICMP.
+ */
+ ip = *(struct ip *)ui;
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (udpcksum && ui->ui_sum) {
+ ui->ui_next = ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_len = ui->ui_ulen;
+ if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
+ udpstat.udps_badsum++;
+ m_freem(m);
+ return;
+ }
+ }
+
+ /*
+ * Locate pcb for datagram.
+ */
+ inp = in_pcblookup(&udb,
+ ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
+ INPLOOKUP_WILDCARD);
+ if (inp == 0) {
+ /* don't send ICMP response for broadcast packet */
+ if (m->m_flags & M_BCAST)
+ goto bad;
+ *(struct ip *)ui = ip;
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
+ return;
+ }
- inp = inpcb_lookup(&udb, addr, port);
- if (inp == 0)
+ /*
+ * 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_pkthdr.len -= sizeof (struct udpiphdr);
+ m->m_data += sizeof (struct udpiphdr);
+ if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
+ m, (struct mbuf *)0) == 0)
goto bad;
- /* sostuff(inp->inp_socket, m, raddr, rport); */
+ sorwakeup(inp->inp_socket);
return;
bad:
m_freem(m);
- /* gen icmp? */
}
-udp_ctlinput(m)
- struct mbuf *m;
+/*
+ * Notify a udp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+udp_notify(inp)
+ register struct inpcb *inp;
{
- m_freem(m);
+ sorwakeup(inp->inp_socket);
+ sowwakeup(inp->inp_socket);
}
-udp_advise(m)
- struct mbuf *m;
+udp_ctlinput(cmd, sa)
+ int cmd;
+ struct sockaddr *sa;
{
+ extern u_char inetctlerrmap[];
+ struct sockaddr_in *sin;
+ int in_rtchange();
- m_freem(m);
+ if ((unsigned)cmd > PRC_NCMDS)
+ return;
+ if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
+ return;
+ sin = (struct sockaddr_in *)sa;
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ return;
+
+ switch (cmd) {
+
+ case PRC_QUENCH:
+ break;
+
+ case PRC_ROUTEDEAD:
+ case PRC_REDIRECT_NET:
+ case PRC_REDIRECT_HOST:
+ case PRC_REDIRECT_TOSNET:
+ case PRC_REDIRECT_TOSHOST:
+ in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
+ break;
+
+ default:
+ if (inetctlerrmap[cmd] == 0)
+ return; /* XXX */
+ in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
+ udp_notify);
+ }
}
-udp_output(raddr, rport, m)
- int raddr, rport;
- struct mbuf *m;
+udp_output(inp, m)
+ register struct inpcb *inp;
+ register struct mbuf *m;
{
+ register struct udpiphdr *ui;
+ register int len = m->m_pkthdr.len;
+
+ /*
+ * Calculate data length and get a mbuf
+ * for UDP and IP headers.
+ */
+ M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ ui->ui_next = ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ 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 = ui->ui_len;
- /* setup header */
- ip_output(m);
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ 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 = udp_ttl;
+ return (ip_output(m, inp->inp_options, &inp->inp_route,
+ inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
}
-udp_usrreq(so, req, m, addr)
+u_long udp_sendspace = 2048; /* really max datagram size */
+u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
+
+/*ARGSUSED*/
+udp_usrreq(so, req, m, nam, rights, control)
struct socket *so;
int req;
- struct mbuf *m;
- struct in_addr *addr;
+ struct mbuf *m, *nam, *rights, *control;
{
struct inpcb *inp = sotoinpcb(so);
- int error;
+ int error = 0;
+ if (req == PRU_CONTROL)
+ return (in_control(so, (int)m, (caddr_t)nam,
+ (struct ifnet *)rights));
+ if (rights && rights->m_len) {
+ error = EINVAL;
+ goto release;
+ }
+ if (inp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
switch (req) {
case PRU_ATTACH:
- if (inp != 0)
- return (EINVAL);
- inp = in_pcballoc();
- if (inp == NULL)
- return (ENOBUFS);
- so->so_pcb = (caddr_t)inp;
+ if (inp != NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = in_pcballoc(so, &udb);
+ if (error)
+ break;
+ error = soreserve(so, udp_sendspace, udp_recvspace);
+ if (error)
+ break;
break;
case PRU_DETACH:
- if (inp == 0)
- return (ENOTCONN);
- sofree(inp->inp_socket);
- udp_detach(inp);
+ in_pcbdetach(inp);
+ break;
+
+ case PRU_BIND:
+ error = in_pcbbind(inp, nam);
+ break;
+
+ case PRU_LISTEN:
+ error = EOPNOTSUPP;
break;
case PRU_CONNECT:
- if (inp->inp_fhost)
- return (EISCONN);
- inp->inp_fhost = in_hmake((struct in_addr *)addr, &error);
- if (inp->inp_fhost == 0)
- return (error);
- soisconnected(so);
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ break;
+ }
+ error = in_pcbconnect(inp, nam);
+ if (error == 0)
+ soisconnected(so);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
break;
case PRU_DISCONNECT:
- if (inp->inp_fhost == 0)
- return (ENOTCONN);
- h_free(inp->inp_fhost);
- inp->inp_fhost = 0;
- soisdisconnected(so);
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ break;
+ }
+ in_pcbdisconnect(inp);
+ so->so_state &= ~SS_ISCONNECTED; /* XXX */
break;
- case PRU_SEND:
-#if 0
- if (addr) {
- if (inp->inp_fhost)
- return (EISCONN);
- udp_output(addr->in_fhost, addr->in_fport, m);
- } else
- udp_output(inp->inp_fhost->h_addr, ip->inp_fport, m);
-#endif
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND: {
+ struct in_addr laddr;
+ int s;
+
+ if (nam) {
+ laddr = inp->inp_laddr;
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ break;
+ }
+ /*
+ * Must block input while temporarily connected.
+ */
+ s = splnet();
+ error = in_pcbconnect(inp, nam);
+ if (error) {
+ splx(s);
+ break;
+ }
+ } else {
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ break;
+ }
+ }
+ error = udp_output(inp, m);
+ m = NULL;
+ if (nam) {
+ in_pcbdisconnect(inp);
+ inp->inp_laddr = laddr;
+ splx(s);
+ }
+ }
break;
case PRU_ABORT:
- in_pcbfree(inp);
- sofree(so);
soisdisconnected(so);
+ in_pcbdetach(inp);
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, nam);
break;
- case PRU_CONTROL:
- return (EOPNOTSUPP);
+ 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_RCVD:
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP); /* do not free mbuf's */
default:
panic("udp_usrreq");
}
- return (0);
-}
-
-udp_sense()
-{
-
+release:
+ if (m != NULL)
+ m_freem(m);
+ return (error);
}