xref: /original-bsd/sys/netinet/udp_usrreq.c (revision fbed46ce)
1 /*	udp_usrreq.c	4.28	82/04/25	*/
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_abort(inp)
122 	struct inpcb *inp;
123 {
124 	struct socket *so = inp->inp_socket;
125 
126 	in_pcbdisconnect(inp);
127 	soisdisconnected(so);
128 }
129 
130 udp_ctlinput(cmd, arg)
131 	int cmd;
132 	caddr_t arg;
133 {
134 	struct in_addr *sin;
135 	extern u_char inetctlerrmap[];
136 COUNT(UDP_CTLINPUT);
137 
138 	if (cmd < 0 || cmd > PRC_NCMDS)
139 		return;
140 	switch (cmd) {
141 
142 	case PRC_ROUTEDEAD:
143 		break;
144 
145 	case PRC_QUENCH:
146 		break;
147 
148 	/* these are handled by ip */
149 	case PRC_IFDOWN:
150 	case PRC_HOSTDEAD:
151 	case PRC_HOSTUNREACH:
152 		break;
153 
154 	default:
155 		sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
156 		in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
157 	}
158 }
159 
160 udp_output(inp, m0)
161 	struct inpcb *inp;
162 	struct mbuf *m0;
163 {
164 	register struct mbuf *m;
165 	register struct udpiphdr *ui;
166 	register int len = 0;
167 
168 COUNT(UDP_OUTPUT);
169 	/*
170 	 * Calculate data length and get a mbuf
171 	 * for UDP and IP headers.
172 	 */
173 	for (m = m0; m; m = m->m_next)
174 		len += m->m_len;
175 	m = m_get(M_DONTWAIT);
176 	if (m == 0) {
177 		m_freem(m0);
178 		return (ENOBUFS);
179 	}
180 
181 	/*
182 	 * Fill in mbuf with extended UDP header
183 	 * and addresses and length put into network format.
184 	 */
185 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
186 	m->m_len = sizeof (struct udpiphdr);
187 	m->m_next = m0;
188 	ui = mtod(m, struct udpiphdr *);
189 	ui->ui_next = ui->ui_prev = 0;
190 	ui->ui_x1 = 0;
191 	ui->ui_pr = IPPROTO_UDP;
192 	ui->ui_len = sizeof (struct udpiphdr) + len;
193 	ui->ui_src = inp->inp_laddr;
194 	ui->ui_dst = inp->inp_faddr;
195 	ui->ui_sport = inp->inp_lport;
196 	ui->ui_dport = inp->inp_fport;
197 	ui->ui_ulen = htons((u_short)len);
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 	return (ip_output(m, (struct mbuf *)0, 0,
207 	    inp->inp_socket->so_state & SS_PRIV));
208 }
209 
210 udp_usrreq(so, req, m, addr)
211 	struct socket *so;
212 	int req;
213 	struct mbuf *m;
214 	caddr_t addr;
215 {
216 	struct inpcb *inp = sotoinpcb(so);
217 	int error = 0;
218 
219 COUNT(UDP_USRREQ);
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_pcbattach(so, &udb, 2048, 2048,
228 				(struct sockaddr_in *)addr);
229 		break;
230 
231 	case PRU_DETACH:
232 		if (inp == 0)
233 			return (ENOTCONN);
234 		in_pcbdetach(inp);
235 		break;
236 
237 	case PRU_CONNECT:
238 		if (inp->inp_faddr.s_addr)
239 			return (EISCONN);
240 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
241 		if (error == 0)
242 			soisconnected(so);
243 		break;
244 
245 	case PRU_ACCEPT:
246 		return (EOPNOTSUPP);
247 
248 	case PRU_DISCONNECT:
249 		if (inp->inp_faddr.s_addr == 0)
250 			return (ENOTCONN);
251 		in_pcbdisconnect(inp);
252 		soisdisconnected(so);
253 		break;
254 
255 	case PRU_SHUTDOWN:
256 		socantsendmore(so);
257 		break;
258 
259 	case PRU_SEND: {
260 		struct in_addr laddr;
261 
262 		if (addr) {
263 			laddr = inp->inp_laddr;
264 			if (inp->inp_faddr.s_addr)
265 				return (EISCONN);
266 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
267 			if (error)
268 				break;
269 		} else {
270 			if (inp->inp_faddr.s_addr == 0)
271 				return (ENOTCONN);
272 		}
273 		error = udp_output(inp, m);
274 		if (addr) {
275 			in_pcbdisconnect(inp);
276 			inp->inp_laddr = laddr;
277 		}
278 		}
279 		break;
280 
281 	case PRU_ABORT:
282 		in_pcbdetach(inp);
283 		sofree(so);
284 		soisdisconnected(so);
285 		break;
286 
287 	case PRU_CONTROL:
288 		return (EOPNOTSUPP);
289 
290 	case PRU_SOCKADDR:
291 		in_setsockaddr((struct sockaddr_in *)addr, inp);
292 		break;
293 
294 	default:
295 		panic("udp_usrreq");
296 	}
297 	return (error);
298 }
299