xref: /original-bsd/sys/netinet/udp_usrreq.c (revision f0fd5f8a)
1 /*	udp_usrreq.c	4.41	82/12/14	*/
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 =
95 		    if_makeaddr(in_netof(ui->ui_dst), INADDR_ANY);
96 		if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
97 			goto bad;
98 		icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
99 		return;
100 	}
101 
102 	/*
103 	 * Construct sockaddr format source address.
104 	 * Stuff source address and datagram in user buffer.
105 	 */
106 	udp_in.sin_port = ui->ui_sport;
107 	udp_in.sin_addr = ui->ui_src;
108 	m->m_len -= sizeof (struct udpiphdr);
109 	m->m_off += sizeof (struct udpiphdr);
110 SBCHECK(&inp->inp_socket->so_rcv, "udpinput before");
111 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
112 		goto bad;
113 SBCHECK(&inp->inp_socket->so_rcv, "udpinput after");
114 	sorwakeup(inp->inp_socket);
115 	return;
116 bad:
117 	m_freem(m);
118 }
119 
120 udp_abort(inp)
121 	struct inpcb *inp;
122 {
123 	struct socket *so = inp->inp_socket;
124 
125 	in_pcbdisconnect(inp);
126 	soisdisconnected(so);
127 }
128 
129 udp_ctlinput(cmd, arg)
130 	int cmd;
131 	caddr_t arg;
132 {
133 	struct in_addr *sin;
134 	extern u_char inetctlerrmap[];
135 
136 	if (cmd < 0 || cmd > PRC_NCMDS)
137 		return;
138 	switch (cmd) {
139 
140 	case PRC_ROUTEDEAD:
141 		break;
142 
143 	case PRC_QUENCH:
144 		break;
145 
146 	/* these are handled by ip */
147 	case PRC_IFDOWN:
148 	case PRC_HOSTDEAD:
149 	case PRC_HOSTUNREACH:
150 		break;
151 
152 	default:
153 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
154 		in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
155 	}
156 }
157 
158 udp_output(inp, m0)
159 	struct inpcb *inp;
160 	struct mbuf *m0;
161 {
162 	register struct mbuf *m;
163 	register struct udpiphdr *ui;
164 	register struct socket *so;
165 	register int len = 0;
166 
167 	/*
168 	 * Calculate data length and get a mbuf
169 	 * for UDP and IP headers.
170 	 */
171 	for (m = m0; m; m = m->m_next)
172 		len += m->m_len;
173 	m = m_get(M_DONTWAIT, MT_HEADER);
174 	if (m == 0) {
175 		m_freem(m0);
176 		return (ENOBUFS);
177 	}
178 
179 	/*
180 	 * Fill in mbuf with extended UDP header
181 	 * and addresses and length put into network format.
182 	 */
183 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
184 	m->m_len = sizeof (struct udpiphdr);
185 	m->m_next = m0;
186 	ui = mtod(m, struct udpiphdr *);
187 	ui->ui_next = ui->ui_prev = 0;
188 	ui->ui_x1 = 0;
189 	ui->ui_pr = IPPROTO_UDP;
190 	ui->ui_len = len + sizeof (struct udphdr);
191 	ui->ui_src = inp->inp_laddr;
192 	ui->ui_dst = inp->inp_faddr;
193 	ui->ui_sport = inp->inp_lport;
194 	ui->ui_dport = inp->inp_fport;
195 	ui->ui_ulen = htons((u_short)ui->ui_len);
196 
197 	/*
198 	 * Stuff checksum and output datagram.
199 	 */
200 	ui->ui_sum = 0;
201 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
202 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
203 	((struct ip *)ui)->ip_ttl = MAXTTL;
204 	so = inp->inp_socket;
205 	return (ip_output(m, (struct mbuf *)0,
206 	    (so->so_options & SO_DONTROUTE) ? &routetoif : (struct route *)0,
207 	    so->so_state & SS_PRIV));
208 }
209 
210 /*ARGSUSED*/
211 udp_usrreq(so, req, m, nam, opt)
212 	struct socket *so;
213 	int req;
214 	struct mbuf *m, *nam;
215 	struct socketopt *opt;
216 {
217 	struct inpcb *inp = sotoinpcb(so);
218 	int error = 0;
219 
220 	if (inp == 0 && req != PRU_ATTACH)
221 		return (EINVAL);
222 	switch (req) {
223 
224 	case PRU_ATTACH:
225 		if (inp != 0)
226 			return (EINVAL);
227 		error = in_pcballoc(so, &udb);
228 		if (error)
229 			break;
230 		error = soreserve(so, 2048, 2048);
231 		if (error)
232 			break;
233 		break;
234 
235 	case PRU_DETACH:
236 		if (inp == 0)
237 			return (ENOTCONN);
238 		in_pcbdetach(inp);
239 		break;
240 
241 	case PRU_BIND:
242 		error = in_pcbbind(inp, nam);
243 		break;
244 
245 	case PRU_LISTEN:
246 		error = EOPNOTSUPP;
247 		break;
248 
249 	case PRU_CONNECT:
250 		if (inp->inp_faddr.s_addr)
251 			return (EISCONN);
252 		error = in_pcbconnect(inp, nam);
253 		if (error == 0)
254 			soisconnected(so);
255 		break;
256 
257 	case PRU_ACCEPT:
258 		return (EOPNOTSUPP);
259 
260 	case PRU_DISCONNECT:
261 		if (inp->inp_faddr.s_addr == 0)
262 			return (ENOTCONN);
263 		in_pcbdisconnect(inp);
264 		soisdisconnected(so);
265 		break;
266 
267 	case PRU_SHUTDOWN:
268 		socantsendmore(so);
269 		break;
270 
271 	case PRU_SEND: {
272 		struct in_addr laddr;
273 
274 		if (nam) {
275 			laddr = inp->inp_laddr;
276 			if (inp->inp_faddr.s_addr)
277 				return (EISCONN);
278 			error = in_pcbconnect(inp, nam);
279 			if (error)
280 				break;
281 		} else {
282 			if (inp->inp_faddr.s_addr == 0)
283 				return (ENOTCONN);
284 		}
285 		error = udp_output(inp, m);
286 		if (nam) {
287 			in_pcbdisconnect(inp);
288 			inp->inp_laddr = laddr;
289 		}
290 		}
291 		break;
292 
293 	case PRU_ABORT:
294 		in_pcbdetach(inp);
295 		sofree(so);
296 		soisdisconnected(so);
297 		break;
298 
299 	case PRU_CONTROL:
300 		return (EOPNOTSUPP);
301 
302 	case PRU_SOCKADDR:
303 		in_setsockaddr(inp, nam);
304 		break;
305 
306 	default:
307 		panic("udp_usrreq");
308 	}
309 	return (error);
310 }
311