xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 7246)
1 /*	udp_usrreq.c	4.30	82/06/20	*/
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 	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, ulen;
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 	ulen = ntohs((u_short)ui->ui_ulen);
61 	len = sizeof (struct udphdr) + ulen;
62 	if (((struct ip *)ui)->ip_len != len) {
63 		if (len > ((struct ip *)ui)->ip_len) {
64 			udpstat.udps_badlen++;
65 			goto bad;
66 		}
67 		m_adj(m, ((struct ip *)ui)->ip_len - len);
68 		/* (struct ip *)ui->ip_len = len; */
69 	}
70 
71 	/*
72 	 * Checksum extended UDP header and data.
73 	 */
74 	if (udpcksum) {
75 		ui->ui_next = ui->ui_prev = 0;
76 		ui->ui_x1 = 0;
77 		ui->ui_len = htons((u_short)(sizeof (struct udphdr) + ulen));
78 		if (ui->ui_sum = in_cksum(m, len)) {
79 			udpstat.udps_badsum++;
80 			printf("udp cksum %x\n", ui->ui_sum);
81 			m_freem(m);
82 			return;
83 		}
84 	}
85 
86 	/*
87 	 * Locate pcb for datagram.  On wildcard match, update
88 	 * control block to anchor network and host address.
89 	 */
90 	inp = in_pcblookup(&udb,
91 	    ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
92 		INPLOOKUP_WILDCARD);
93 	if (inp == 0) {
94 		struct in_addr broadcastaddr;
95 
96 		broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
97 		if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
98 			goto bad;
99 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
100 		return;
101 	}
102 
103 	/*
104 	 * Construct sockaddr format source address.
105 	 * Stuff source address and datagram in user buffer.
106 	 */
107 	udp_in.sin_port = ui->ui_sport;
108 	udp_in.sin_addr = ui->ui_src;
109 	m->m_len -= sizeof (struct udpiphdr);
110 	m->m_off += sizeof (struct udpiphdr);
111 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
112 		goto bad;
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 = sizeof (struct udpiphdr) + len;
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 = len;
195 #if vax
196 	ui->ui_ulen = htons(ui->ui_ulen);
197 #endif
198 
199 	/*
200 	 * Stuff checksum and output datagram.
201 	 */
202 	ui->ui_sum = 0;
203 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
204 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
205 	((struct ip *)ui)->ip_ttl = MAXTTL;
206 	so = inp->inp_socket;
207 	return (ip_output(m, (struct mbuf *)0,
208 	    (so->so_options & SO_DONTROUTE) ? &routetoif : (struct route *)0,
209 	    so->so_state & SS_PRIV));
210 }
211 
212 udp_usrreq(so, req, m, addr)
213 	struct socket *so;
214 	int req;
215 	struct mbuf *m;
216 	caddr_t addr;
217 {
218 	struct inpcb *inp = sotoinpcb(so);
219 	int error = 0;
220 
221 	if (inp == 0 && req != PRU_ATTACH)
222 		return (EINVAL);
223 	switch (req) {
224 
225 	case PRU_ATTACH:
226 		if (inp != 0)
227 			return (EINVAL);
228 		error = in_pcbattach(so, &udb, 2048, 2048,
229 				(struct sockaddr_in *)addr);
230 		break;
231 
232 	case PRU_DETACH:
233 		if (inp == 0)
234 			return (ENOTCONN);
235 		in_pcbdetach(inp);
236 		break;
237 
238 	case PRU_CONNECT:
239 		if (inp->inp_faddr.s_addr)
240 			return (EISCONN);
241 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
242 		if (error == 0)
243 			soisconnected(so);
244 		break;
245 
246 	case PRU_ACCEPT:
247 		return (EOPNOTSUPP);
248 
249 	case PRU_DISCONNECT:
250 		if (inp->inp_faddr.s_addr == 0)
251 			return (ENOTCONN);
252 		in_pcbdisconnect(inp);
253 		soisdisconnected(so);
254 		break;
255 
256 	case PRU_SHUTDOWN:
257 		socantsendmore(so);
258 		break;
259 
260 	case PRU_SEND: {
261 		struct in_addr laddr;
262 
263 		if (addr) {
264 			laddr = inp->inp_laddr;
265 			if (inp->inp_faddr.s_addr)
266 				return (EISCONN);
267 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
268 			if (error)
269 				break;
270 		} else {
271 			if (inp->inp_faddr.s_addr == 0)
272 				return (ENOTCONN);
273 		}
274 		error = udp_output(inp, m);
275 		if (addr) {
276 			in_pcbdisconnect(inp);
277 			inp->inp_laddr = laddr;
278 		}
279 		}
280 		break;
281 
282 	case PRU_ABORT:
283 		in_pcbdetach(inp);
284 		sofree(so);
285 		soisdisconnected(so);
286 		break;
287 
288 	case PRU_CONTROL:
289 		return (EOPNOTSUPP);
290 
291 	case PRU_SOCKADDR:
292 		in_setsockaddr((struct sockaddr_in *)addr, inp);
293 		break;
294 
295 	default:
296 		panic("udp_usrreq");
297 	}
298 	return (error);
299 }
300