xref: /csrg-svn/sys/netinet/udp_usrreq.c (revision 6511)
1 /*	udp_usrreq.c	4.26	82/04/10	*/
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/route.h"
12 #include "../net/in_pcb.h"
13 #include "../net/in_systm.h"
14 #include "../net/ip.h"
15 #include "../net/ip_var.h"
16 #include "../net/udp.h"
17 #include "../net/udp_var.h"
18 #include <errno.h>
19 
20 /*
21  * UDP protocol implementation.
22  * Per RFC 768, August, 1980.
23  */
24 udp_init()
25 {
26 
27 COUNT(UDP_INIT);
28 	udb.inp_next = udb.inp_prev = &udb;
29 }
30 
31 int	udpcksum;
32 struct	sockaddr_in udp_in = { AF_INET };
33 
34 udp_input(m0)
35 	struct mbuf *m0;
36 {
37 	register struct udpiphdr *ui;
38 	register struct inpcb *inp;
39 	register struct mbuf *m;
40 	int len, ulen;
41 
42 COUNT(UDP_INPUT);
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 		goto bad;
95 
96 	/*
97 	 * Construct sockaddr format source address.
98 	 * Stuff source address and datagram in user buffer.
99 	 */
100 	udp_in.sin_port = ui->ui_sport;
101 	udp_in.sin_addr = ui->ui_src;
102 	m->m_len -= sizeof (struct udpiphdr);
103 	m->m_off += sizeof (struct udpiphdr);
104 	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
105 		goto bad;
106 	sorwakeup(inp->inp_socket);
107 	return;
108 bad:
109 	m_freem(m);
110 }
111 
112 udp_ctlinput(m)
113 	struct mbuf *m;
114 {
115 
116 COUNT(UDP_CTLINPUT);
117 	m_freem(m);
118 }
119 
120 udp_output(inp, m0)
121 	struct inpcb *inp;
122 	struct mbuf *m0;
123 {
124 	register struct mbuf *m;
125 	register struct udpiphdr *ui;
126 	register int len = 0;
127 
128 COUNT(UDP_OUTPUT);
129 	/*
130 	 * Calculate data length and get a mbuf
131 	 * for UDP and IP headers.
132 	 */
133 	for (m = m0; m; m = m->m_next)
134 		len += m->m_len;
135 	m = m_get(M_DONTWAIT);
136 	if (m == 0) {
137 		m_freem(m0);
138 		return (ENOBUFS);
139 	}
140 
141 	/*
142 	 * Fill in mbuf with extended UDP header
143 	 * and addresses and length put into network format.
144 	 */
145 	m->m_off = MMAXOFF - sizeof (struct udpiphdr);
146 	m->m_len = sizeof (struct udpiphdr);
147 	m->m_next = m0;
148 	ui = mtod(m, struct udpiphdr *);
149 	ui->ui_next = ui->ui_prev = 0;
150 	ui->ui_x1 = 0;
151 	ui->ui_pr = IPPROTO_UDP;
152 	ui->ui_len = sizeof (struct udpiphdr) + len;
153 	ui->ui_src = inp->inp_laddr;
154 	ui->ui_dst = inp->inp_faddr;
155 	ui->ui_sport = inp->inp_lport;
156 	ui->ui_dport = inp->inp_fport;
157 	ui->ui_ulen = htons((u_short)len);
158 
159 	/*
160 	 * Stuff checksum and output datagram.
161 	 */
162 	ui->ui_sum = 0;
163 	ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
164 	((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
165 	((struct ip *)ui)->ip_ttl = MAXTTL;
166 	return (ip_output(m, (struct mbuf *)0, 0,
167 	    inp->inp_socket->so_state & SS_PRIV));
168 }
169 
170 udp_usrreq(so, req, m, addr)
171 	struct socket *so;
172 	int req;
173 	struct mbuf *m;
174 	caddr_t addr;
175 {
176 	struct inpcb *inp = sotoinpcb(so);
177 	int error = 0;
178 
179 COUNT(UDP_USRREQ);
180 	if (inp == 0 && req != PRU_ATTACH)
181 		return (EINVAL);
182 	switch (req) {
183 
184 	case PRU_ATTACH:
185 		if (inp != 0)
186 			return (EINVAL);
187 		error = in_pcbattach(so, &udb, 2048, 2048,
188 				(struct sockaddr_in *)addr);
189 		break;
190 
191 	case PRU_DETACH:
192 		if (inp == 0)
193 			return (ENOTCONN);
194 		in_pcbdetach(inp);
195 		break;
196 
197 	case PRU_CONNECT:
198 		if (inp->inp_faddr.s_addr)
199 			return (EISCONN);
200 		error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
201 		if (error == 0)
202 			soisconnected(so);
203 		break;
204 
205 	case PRU_ACCEPT:
206 		return (EOPNOTSUPP);
207 
208 	case PRU_DISCONNECT:
209 		if (inp->inp_faddr.s_addr == 0)
210 			return (ENOTCONN);
211 		in_pcbdisconnect(inp);
212 		soisdisconnected(so);
213 		break;
214 
215 	case PRU_SHUTDOWN:
216 		socantsendmore(so);
217 		break;
218 
219 	case PRU_SEND: {
220 		struct in_addr laddr;
221 
222 		if (addr) {
223 			laddr = inp->inp_laddr;
224 			if (inp->inp_faddr.s_addr)
225 				return (EISCONN);
226 			error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
227 			if (error)
228 				break;
229 		} else {
230 			if (inp->inp_faddr.s_addr == 0)
231 				return (ENOTCONN);
232 		}
233 		error = udp_output(inp, m);
234 		if (addr) {
235 			in_pcbdisconnect(inp);
236 			inp->inp_laddr = laddr;
237 		}
238 		}
239 		break;
240 
241 	case PRU_ABORT:
242 		in_pcbdetach(inp);
243 		sofree(so);
244 		soisdisconnected(so);
245 		break;
246 
247 	case PRU_CONTROL:
248 		return (EOPNOTSUPP);
249 
250 	case PRU_SOCKADDR:
251 		in_setsockaddr((struct sockaddr_in *)addr, inp);
252 		break;
253 
254 	default:
255 		panic("udp_usrreq");
256 	}
257 	return (error);
258 }
259