xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 6584)
1 /*	udp_usrreq.c	4.27	82/04/24	*/
2 
3 #include "../h/param.h"
4 #include "../h/dir.h"
5 #include "../h/user.h"
6 #include "../h/mbuf.h"
7 #include "../h/protosw.h"
8 #include "../h/socket.h"
9 #include "../h/socketvar.h"
10 #include "../net/in.h"
11 #include "../net/if.h"
12 #include "../net/route.h"
13 #include "../net/in_pcb.h"
14 #include "../net/in_systm.h"
15 #include "../net/ip.h"
16 #include "../net/ip_var.h"
17 #include "../net/ip_icmp.h"
18 #include "../net/udp.h"
19 #include "../net/udp_var.h"
20 #include <errno.h>
21 
22 /*
23  * UDP protocol implementation.
24  * Per RFC 768, August, 1980.
25  */
26 udp_init()
27 {
28 
29 COUNT(UDP_INIT);
30 	udb.inp_next = udb.inp_prev = &udb;
31 }
32 
33 int	udpcksum;
34 struct	sockaddr_in udp_in = { AF_INET };
35 
36 udp_input(m0)
37 	struct mbuf *m0;
38 {
39 	register struct udpiphdr *ui;
40 	register struct inpcb *inp;
41 	register struct mbuf *m;
42 	int len, ulen;
43 
44 COUNT(UDP_INPUT);
45 	/*
46 	 * Get IP and UDP header together in first mbuf.
47 	 */
48 	m = m0;
49 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
50 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
51 		udpstat.udps_hdrops++;
52 		return;
53 	}
54 	ui = mtod(m, struct udpiphdr *);
55 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
56 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
57 
58 	/*
59 	 * Make mbuf data length reflect UDP length.
60 	 * If not enough data to reflect UDP length, drop.
61 	 */
62 	ulen = ntohs((u_short)ui->ui_ulen);
63 	len = sizeof (struct udphdr) + ulen;
64 	if (((struct ip *)ui)->ip_len != len) {
65 		if (len > ((struct ip *)ui)->ip_len) {
66 			udpstat.udps_badlen++;
67 			goto bad;
68 		}
69 		m_adj(m, ((struct ip *)ui)->ip_len - len);
70 		/* (struct ip *)ui->ip_len = len; */
71 	}
72 
73 	/*
74 	 * Checksum extended UDP header and data.
75 	 */
76 	if (udpcksum) {
77 		ui->ui_next = ui->ui_prev = 0;
78 		ui->ui_x1 = 0;
79 		ui->ui_len = htons((u_short)(sizeof (struct udphdr) + ulen));
80 		if (ui->ui_sum = in_cksum(m, len)) {
81 			udpstat.udps_badsum++;
82 			printf("udp cksum %x\n", ui->ui_sum);
83 			m_freem(m);
84 			return;
85 		}
86 	}
87 
88 	/*
89 	 * Locate pcb for datagram.  On wildcard match, update
90 	 * control block to anchor network and host address.
91 	 */
92 	inp = in_pcblookup(&udb,
93 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
94 		INPLOOKUP_WILDCARD);
95 	if (inp == 0) {
96 		struct in_addr broadcastaddr;
97 
98 		broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
99 		if (ui->ui_dst.s_addr != broadcastaddr.s_addr)
100 			goto bad;
101 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
102 		return;
103 	}
104 
105 	/*
106 	 * Construct sockaddr format source address.
107 	 * Stuff source address and datagram in user buffer.
108 	 */
109 	udp_in.sin_port = ui->ui_sport;
110 	udp_in.sin_addr = ui->ui_src;
111 	m->m_len -= sizeof (struct udpiphdr);
112 	m->m_off += sizeof (struct udpiphdr);
113 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
114 		goto bad;
115 	sorwakeup(inp->inp_socket);
116 	return;
117 bad:
118 	m_freem(m);
119 }
120 
121 udp_ctlinput(cmd, arg)
122 	int cmd;
123 	caddr_t arg;
124 {
125 
126 COUNT(UDP_CTLINPUT);
127 }
128 
129 udp_abort(inp)
130 	struct inpcb *inp;
131 {
132 	struct socket *so = inp->inp_socket;
133 
134 	in_pcbdisconnect(inp);
135 	soisdisconnected(so);
136 }
137 
138 udp_output(inp, m0)
139 	struct inpcb *inp;
140 	struct mbuf *m0;
141 {
142 	register struct mbuf *m;
143 	register struct udpiphdr *ui;
144 	register int len = 0;
145 
146 COUNT(UDP_OUTPUT);
147 	/*
148 	 * Calculate data length and get a mbuf
149 	 * for UDP and IP headers.
150 	 */
151 	for (m = m0; m; m = m->m_next)
152 		len += m->m_len;
153 	m = m_get(M_DONTWAIT);
154 	if (m == 0) {
155 		m_freem(m0);
156 		return (ENOBUFS);
157 	}
158 
159 	/*
160 	 * Fill in mbuf with extended UDP header
161 	 * and addresses and length put into network format.
162 	 */
163 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
164 	m->m_len = sizeof (struct udpiphdr);
165 	m->m_next = m0;
166 	ui = mtod(m, struct udpiphdr *);
167 	ui->ui_next = ui->ui_prev = 0;
168 	ui->ui_x1 = 0;
169 	ui->ui_pr = IPPROTO_UDP;
170 	ui->ui_len = sizeof (struct udpiphdr) + len;
171 	ui->ui_src = inp->inp_laddr;
172 	ui->ui_dst = inp->inp_faddr;
173 	ui->ui_sport = inp->inp_lport;
174 	ui->ui_dport = inp->inp_fport;
175 	ui->ui_ulen = htons((u_short)len);
176 
177 	/*
178 	 * Stuff checksum and output datagram.
179 	 */
180 	ui->ui_sum = 0;
181 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
182 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
183 	((struct ip *)ui)->ip_ttl = MAXTTL;
184 	return (ip_output(m, (struct mbuf *)0, 0,
185 	    inp->inp_socket->so_state & SS_PRIV));
186 }
187 
188 udp_usrreq(so, req, m, addr)
189 	struct socket *so;
190 	int req;
191 	struct mbuf *m;
192 	caddr_t addr;
193 {
194 	struct inpcb *inp = sotoinpcb(so);
195 	int error = 0;
196 
197 COUNT(UDP_USRREQ);
198 	if (inp == 0 && req != PRU_ATTACH)
199 		return (EINVAL);
200 	switch (req) {
201 
202 	case PRU_ATTACH:
203 		if (inp != 0)
204 			return (EINVAL);
205 		error = in_pcbattach(so, &udb, 2048, 2048,
206 				(struct sockaddr_in *)addr);
207 		break;
208 
209 	case PRU_DETACH:
210 		if (inp == 0)
211 			return (ENOTCONN);
212 		in_pcbdetach(inp);
213 		break;
214 
215 	case PRU_CONNECT:
216 		if (inp->inp_faddr.s_addr)
217 			return (EISCONN);
218 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
219 		if (error == 0)
220 			soisconnected(so);
221 		break;
222 
223 	case PRU_ACCEPT:
224 		return (EOPNOTSUPP);
225 
226 	case PRU_DISCONNECT:
227 		if (inp->inp_faddr.s_addr == 0)
228 			return (ENOTCONN);
229 		in_pcbdisconnect(inp);
230 		soisdisconnected(so);
231 		break;
232 
233 	case PRU_SHUTDOWN:
234 		socantsendmore(so);
235 		break;
236 
237 	case PRU_SEND: {
238 		struct in_addr laddr;
239 
240 		if (addr) {
241 			laddr = inp->inp_laddr;
242 			if (inp->inp_faddr.s_addr)
243 				return (EISCONN);
244 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
245 			if (error)
246 				break;
247 		} else {
248 			if (inp->inp_faddr.s_addr == 0)
249 				return (ENOTCONN);
250 		}
251 		error = udp_output(inp, m);
252 		if (addr) {
253 			in_pcbdisconnect(inp);
254 			inp->inp_laddr = laddr;
255 		}
256 		}
257 		break;
258 
259 	case PRU_ABORT:
260 		in_pcbdetach(inp);
261 		sofree(so);
262 		soisdisconnected(so);
263 		break;
264 
265 	case PRU_CONTROL:
266 		return (EOPNOTSUPP);
267 
268 	case PRU_SOCKADDR:
269 		in_setsockaddr((struct sockaddr_in *)addr, inp);
270 		break;
271 
272 	default:
273 		panic("udp_usrreq");
274 	}
275 	return (error);
276 }
277