xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 8551)
1 /*	udp_usrreq.c	4.34	82/10/16	*/
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 "../netinet/in.h"
11 #include "../net/if.h"
12 #include "../net/route.h"
13 #include "../netinet/in_pcb.h"
14 #include "../netinet/in_systm.h"
15 #include "../netinet/ip.h"
16 #include "../netinet/ip_var.h"
17 #include "../netinet/ip_icmp.h"
18 #include "../netinet/udp.h"
19 #include "../netinet/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 	udb.inp_next = udb.inp_prev = &udb;
30 }
31 
32 int	udpcksum;
33 struct	sockaddr_in udp_in = { AF_INET };
34 
35 udp_input(m0)
36 	struct mbuf *m0;
37 {
38 	register struct udpiphdr *ui;
39 	register struct inpcb *inp;
40 	register struct mbuf *m;
41 	int len;
42 
43 	/*
44 	 * Get IP and UDP header together in first mbuf.
45 	 */
46 	m = m0;
47 	if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
48 	    (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
49 		udpstat.udps_hdrops++;
50 		return;
51 	}
52 	ui = mtod(m, struct udpiphdr *);
53 	if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
54 		ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
55 
56 	/*
57 	 * Make mbuf data length reflect UDP length.
58 	 * If not enough data to reflect UDP length, drop.
59 	 */
60 	len = ntohs((u_short)ui->ui_ulen);
61 	if (((struct ip *)ui)->ip_len != len) {
62 		if (len > ((struct ip *)ui)->ip_len) {
63 			udpstat.udps_badlen++;
64 			goto bad;
65 		}
66 		m_adj(m, ((struct ip *)ui)->ip_len - len);
67 		/* (struct ip *)ui->ip_len = len; */
68 	}
69 
70 	/*
71 	 * Checksum extended UDP header and data.
72 	 */
73 	if (udpcksum) {
74 		ui->ui_next = ui->ui_prev = 0;
75 		ui->ui_x1 = 0;
76 		ui->ui_len = htons((u_short)len);
77 		if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
78 			udpstat.udps_badsum++;
79 			printf("udp cksum %x\n", ui->ui_sum);
80 			m_freem(m);
81 			return;
82 		}
83 	}
84 
85 	/*
86 	 * Locate pcb for datagram.
87 	 */
88 	inp = in_pcblookup(&udb,
89 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
90 		INPLOOKUP_WILDCARD);
91 	if (inp == 0) {
92 		struct in_addr broadcastaddr;
93 
94 		broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
95 		if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
96 			goto bad;
97 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
98 		return;
99 	}
100 
101 	/*
102 	 * Construct sockaddr format source address.
103 	 * Stuff source address and datagram in user buffer.
104 	 */
105 	udp_in.sin_port = ui->ui_sport;
106 	udp_in.sin_addr = ui->ui_src;
107 	m->m_len -= sizeof (struct udpiphdr);
108 	m->m_off += sizeof (struct udpiphdr);
109 SBCHECK(&inp->inp_socket->so_rcv, "udpinput before");
110 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
111 		goto bad;
112 SBCHECK(&inp->inp_socket->so_rcv, "udpinput after");
113 	sorwakeup(inp->inp_socket);
114 	return;
115 bad:
116 	m_freem(m);
117 }
118 
119 udp_abort(inp)
120 	struct inpcb *inp;
121 {
122 	struct socket *so = inp->inp_socket;
123 
124 	in_pcbdisconnect(inp);
125 	soisdisconnected(so);
126 }
127 
128 udp_ctlinput(cmd, arg)
129 	int cmd;
130 	caddr_t arg;
131 {
132 	struct in_addr *sin;
133 	extern u_char inetctlerrmap[];
134 
135 	if (cmd < 0 || cmd > PRC_NCMDS)
136 		return;
137 	switch (cmd) {
138 
139 	case PRC_ROUTEDEAD:
140 		break;
141 
142 	case PRC_QUENCH:
143 		break;
144 
145 	/* these are handled by ip */
146 	case PRC_IFDOWN:
147 	case PRC_HOSTDEAD:
148 	case PRC_HOSTUNREACH:
149 		break;
150 
151 	default:
152 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
153 		in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
154 	}
155 }
156 
157 udp_output(inp, m0)
158 	struct inpcb *inp;
159 	struct mbuf *m0;
160 {
161 	register struct mbuf *m;
162 	register struct udpiphdr *ui;
163 	register struct socket *so;
164 	register int len = 0;
165 
166 	/*
167 	 * Calculate data length and get a mbuf
168 	 * for UDP and IP headers.
169 	 */
170 	for (m = m0; m; m = m->m_next)
171 		len += m->m_len;
172 	m = m_get(M_DONTWAIT);
173 	if (m == 0) {
174 		m_freem(m0);
175 		return (ENOBUFS);
176 	}
177 
178 	/*
179 	 * Fill in mbuf with extended UDP header
180 	 * and addresses and length put into network format.
181 	 */
182 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
183 	m->m_len = sizeof (struct udpiphdr);
184 	m->m_next = m0;
185 	ui = mtod(m, struct udpiphdr *);
186 	ui->ui_next = ui->ui_prev = 0;
187 	ui->ui_x1 = 0;
188 	ui->ui_pr = IPPROTO_UDP;
189 	ui->ui_len = len + sizeof (struct udphdr);
190 	ui->ui_src = inp->inp_laddr;
191 	ui->ui_dst = inp->inp_faddr;
192 	ui->ui_sport = inp->inp_lport;
193 	ui->ui_dport = inp->inp_fport;
194 	ui->ui_ulen = htons((u_short)ui->ui_len);
195 
196 	/*
197 	 * Stuff checksum and output datagram.
198 	 */
199 	ui->ui_sum = 0;
200 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
201 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
202 	((struct ip *)ui)->ip_ttl = MAXTTL;
203 	so = inp->inp_socket;
204 	return (ip_output(m, (struct mbuf *)0,
205 	    (so->so_options & SO_DONTROUTE) ? &routetoif : (struct route *)0,
206 	    so->so_state & SS_PRIV));
207 }
208 
209 udp_usrreq(so, req, m, nam, opt)
210 	struct socket *so;
211 	int req;
212 	struct mbuf *m, *nam;
213 	struct socketopt *opt;
214 {
215 	struct inpcb *inp = sotoinpcb(so);
216 	int error = 0;
217 
218 	if (inp == 0 && req != PRU_ATTACH)
219 		return (EINVAL);
220 	switch (req) {
221 
222 	case PRU_ATTACH:
223 		if (inp != 0)
224 			return (EINVAL);
225 		error = in_pcballoc(so, &udb);
226 		if (error)
227 			break;
228 		error = in_pcbreserve(so, 2048, 2048);
229 		if (error)
230 			break;
231 		break;
232 
233 	case PRU_DETACH:
234 		if (inp == 0)
235 			return (ENOTCONN);
236 		in_pcbdetach(inp);
237 		break;
238 
239 	case PRU_BIND:
240 		error = in_pcbbind(inp, nam);
241 		break;
242 
243 	case PRU_LISTEN:
244 		error = EOPNOTSUPP;
245 		break;
246 
247 	case PRU_CONNECT:
248 		if (inp->inp_faddr.s_addr)
249 			return (EISCONN);
250 		error = in_pcbconnect(inp, nam);
251 		if (error == 0)
252 			soisconnected(so);
253 		break;
254 
255 	case PRU_ACCEPT:
256 		return (EOPNOTSUPP);
257 
258 	case PRU_DISCONNECT:
259 		if (inp->inp_faddr.s_addr == 0)
260 			return (ENOTCONN);
261 		in_pcbdisconnect(inp);
262 		soisdisconnected(so);
263 		break;
264 
265 	case PRU_SHUTDOWN:
266 		socantsendmore(so);
267 		break;
268 
269 	case PRU_SEND: {
270 		struct in_addr laddr;
271 
272 		if (nam) {
273 			laddr = inp->inp_laddr;
274 			if (inp->inp_faddr.s_addr)
275 				return (EISCONN);
276 			error = in_pcbconnect(inp, nam);
277 			if (error)
278 				break;
279 		} else {
280 			if (inp->inp_faddr.s_addr == 0)
281 				return (ENOTCONN);
282 		}
283 		error = udp_output(inp, m);
284 		if (nam) {
285 			in_pcbdisconnect(inp);
286 			inp->inp_laddr = laddr;
287 		}
288 		}
289 		break;
290 
291 	case PRU_ABORT:
292 		in_pcbdetach(inp);
293 		sofree(so);
294 		soisdisconnected(so);
295 		break;
296 
297 	case PRU_CONTROL:
298 		return (EOPNOTSUPP);
299 
300 	case PRU_SOCKADDR:
301 		in_setsockaddr(inp, nam);
302 		break;
303 
304 	default:
305 		panic("udp_usrreq");
306 	}
307 	return (error);
308 }
309