free queued segments carefully, runs at splnet, not splimp
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
CommitLineData
0e3f761f 1/* udp_usrreq.c 4.47 83/05/12 */
1bd53abe
BJ
2
3#include "../h/param.h"
53a5409e
BJ
4#include "../h/dir.h"
5#include "../h/user.h"
1bd53abe 6#include "../h/mbuf.h"
d52566dd 7#include "../h/protosw.h"
53a5409e
BJ
8#include "../h/socket.h"
9#include "../h/socketvar.h"
f4d55810
SL
10#include "../h/errno.h"
11
72e4f44e 12#include "../net/if.h"
c124e997 13#include "../net/route.h"
f4d55810
SL
14
15#include "../netinet/in.h"
bf6ab6ff
BJ
16#include "../netinet/in_pcb.h"
17#include "../netinet/in_systm.h"
18#include "../netinet/ip.h"
19#include "../netinet/ip_var.h"
20#include "../netinet/ip_icmp.h"
21#include "../netinet/udp.h"
22#include "../netinet/udp_var.h"
d52566dd 23
2b4b57cd
BJ
24/*
25 * UDP protocol implementation.
26 * Per RFC 768, August, 1980.
27 */
d52566dd
BJ
28udp_init()
29{
30
eb44bfb2 31 udb.inp_next = udb.inp_prev = &udb;
d52566dd 32}
1bd53abe 33
eb44bfb2 34int udpcksum;
2b4b57cd 35struct sockaddr_in udp_in = { AF_INET };
eb44bfb2 36
2b4b57cd
BJ
37udp_input(m0)
38 struct mbuf *m0;
1bd53abe 39{
eb44bfb2 40 register struct udpiphdr *ui;
53a5409e 41 register struct inpcb *inp;
2b4b57cd 42 register struct mbuf *m;
e199a8a5 43 int len;
eb44bfb2 44
2b4b57cd 45 /*
4aed14e3 46 * Get IP and UDP header together in first mbuf.
2b4b57cd
BJ
47 */
48 m = m0;
89925e89
BJ
49 if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
50 (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
2b4b57cd 51 udpstat.udps_hdrops++;
89925e89 52 return;
2b4b57cd 53 }
4e60622a 54 ui = mtod(m, struct udpiphdr *);
4aed14e3 55 if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
cca1853f 56 ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
2b4b57cd
BJ
57
58 /*
4aed14e3
BJ
59 * Make mbuf data length reflect UDP length.
60 * If not enough data to reflect UDP length, drop.
2b4b57cd 61 */
e199a8a5 62 len = ntohs((u_short)ui->ui_ulen);
2b4b57cd
BJ
63 if (((struct ip *)ui)->ip_len != len) {
64 if (len > ((struct ip *)ui)->ip_len) {
65 udpstat.udps_badlen++;
66 goto bad;
67 }
68 m_adj(m, ((struct ip *)ui)->ip_len - len);
69 /* (struct ip *)ui->ip_len = len; */
70 }
71
72 /*
4aed14e3 73 * Checksum extended UDP header and data.
2b4b57cd 74 */
eb44bfb2 75 if (udpcksum) {
2b4b57cd
BJ
76 ui->ui_next = ui->ui_prev = 0;
77 ui->ui_x1 = 0;
e199a8a5
BJ
78 ui->ui_len = htons((u_short)len);
79 if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
2b4b57cd 80 udpstat.udps_badsum++;
eb44bfb2
BJ
81 m_freem(m);
82 return;
83 }
84 }
2b4b57cd
BJ
85
86 /*
e199a8a5 87 * Locate pcb for datagram.
2b4b57cd 88 */
2b4b57cd 89 inp = in_pcblookup(&udb,
ebcadd38
BJ
90 ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
91 INPLOOKUP_WILDCARD);
72e4f44e 92 if (inp == 0) {
5cdc4d65
SL
93 /* don't send ICMP response for broadcast packet */
94 if (in_lnaof(ui->ui_dst) == INADDR_ANY)
72e4f44e
SL
95 goto bad;
96 icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
97 return;
98 }
2b4b57cd
BJ
99
100 /*
101 * Construct sockaddr format source address.
102 * Stuff source address and datagram in user buffer.
103 */
104 udp_in.sin_port = ui->ui_sport;
105 udp_in.sin_addr = ui->ui_src;
4e60622a
BJ
106 m->m_len -= sizeof (struct udpiphdr);
107 m->m_off += sizeof (struct udpiphdr);
108 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
2b4b57cd 109 goto bad;
4e60622a 110 sorwakeup(inp->inp_socket);
53a5409e 111 return;
2b4b57cd 112bad:
53a5409e 113 m_freem(m);
1bd53abe
BJ
114}
115
72e4f44e
SL
116udp_abort(inp)
117 struct inpcb *inp;
118{
119 struct socket *so = inp->inp_socket;
120
121 in_pcbdisconnect(inp);
122 soisdisconnected(so);
1bd53abe
BJ
123}
124
39674d5f
SL
125udp_ctlinput(cmd, arg)
126 int cmd;
127 caddr_t arg;
128{
129 struct in_addr *sin;
130 extern u_char inetctlerrmap[];
39674d5f
SL
131
132 if (cmd < 0 || cmd > PRC_NCMDS)
133 return;
134 switch (cmd) {
135
136 case PRC_ROUTEDEAD:
137 break;
138
139 case PRC_QUENCH:
140 break;
141
142 /* these are handled by ip */
143 case PRC_IFDOWN:
144 case PRC_HOSTDEAD:
145 case PRC_HOSTUNREACH:
146 break;
147
148 default:
149 sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
a1edc12b 150 in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
39674d5f
SL
151 }
152}
153
4ad99bae 154udp_output(inp, m0)
2b4b57cd 155 struct inpcb *inp;
2b4b57cd 156 struct mbuf *m0;
1bd53abe 157{
2b4b57cd
BJ
158 register struct mbuf *m;
159 register struct udpiphdr *ui;
a13c006d 160 register struct socket *so;
2b4b57cd 161 register int len = 0;
0e3f761f 162 int flags;
2b4b57cd
BJ
163
164 /*
165 * Calculate data length and get a mbuf
4aed14e3 166 * for UDP and IP headers.
2b4b57cd
BJ
167 */
168 for (m = m0; m; m = m->m_next)
169 len += m->m_len;
cce93e4b 170 m = m_get(M_DONTWAIT, MT_HEADER);
8a2f82db
SL
171 if (m == 0) {
172 m_freem(m0);
173 return (ENOBUFS);
174 }
2b4b57cd
BJ
175
176 /*
4aed14e3 177 * Fill in mbuf with extended UDP header
2b4b57cd
BJ
178 * and addresses and length put into network format.
179 */
180 m->m_off = MMAXOFF - sizeof (struct udpiphdr);
181 m->m_len = sizeof (struct udpiphdr);
182 m->m_next = m0;
183 ui = mtod(m, struct udpiphdr *);
184 ui->ui_next = ui->ui_prev = 0;
185 ui->ui_x1 = 0;
186 ui->ui_pr = IPPROTO_UDP;
e199a8a5 187 ui->ui_len = len + sizeof (struct udphdr);
4e60622a
BJ
188 ui->ui_src = inp->inp_laddr;
189 ui->ui_dst = inp->inp_faddr;
190 ui->ui_sport = inp->inp_lport;
191 ui->ui_dport = inp->inp_fport;
e199a8a5 192 ui->ui_ulen = htons((u_short)ui->ui_len);
2b4b57cd
BJ
193
194 /*
195 * Stuff checksum and output datagram.
196 */
197 ui->ui_sum = 0;
ce605ce8 198 ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
4e60622a
BJ
199 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
200 ((struct ip *)ui)->ip_ttl = MAXTTL;
a13c006d 201 so = inp->inp_socket;
0e3f761f
SL
202 flags = (so->so_options & SO_DONTROUTE) | (so->so_state & SS_PRIV);
203 return (ip_output(m, (struct mbuf *)0, (struct route *)0, flags));
1bd53abe
BJ
204}
205
a8d3bf7f 206/*ARGSUSED*/
bc031e39 207udp_usrreq(so, req, m, nam)
53a5409e 208 struct socket *so;
1bd53abe 209 int req;
8075bb0e 210 struct mbuf *m, *nam;
1bd53abe 211{
53a5409e 212 struct inpcb *inp = sotoinpcb(so);
8a2f82db 213 int error = 0;
1bd53abe 214
a3fa431a
SL
215 if (inp == NULL && req != PRU_ATTACH) {
216 error = EINVAL;
217 goto release;
218 }
1bd53abe
BJ
219 switch (req) {
220
221 case PRU_ATTACH:
a3fa431a
SL
222 if (inp != NULL) {
223 error = EINVAL;
224 break;
225 }
8075bb0e
BJ
226 error = in_pcballoc(so, &udb);
227 if (error)
228 break;
59965020 229 error = soreserve(so, 2048, 2048);
8075bb0e
BJ
230 if (error)
231 break;
53a5409e 232 break;
1bd53abe
BJ
233
234 case PRU_DETACH:
a3fa431a
SL
235 if (inp == NULL) {
236 error = ENOTCONN;
237 break;
238 }
405c9168 239 in_pcbdetach(inp);
53a5409e 240 break;
1bd53abe 241
8075bb0e
BJ
242 case PRU_BIND:
243 error = in_pcbbind(inp, nam);
244 break;
245
246 case PRU_LISTEN:
247 error = EOPNOTSUPP;
248 break;
249
1bd53abe 250 case PRU_CONNECT:
a3fa431a
SL
251 if (inp->inp_faddr.s_addr != INADDR_ANY) {
252 error = EISCONN;
253 break;
254 }
8075bb0e 255 error = in_pcbconnect(inp, nam);
8a2f82db
SL
256 if (error == 0)
257 soisconnected(so);
1bd53abe
BJ
258 break;
259
2b4b57cd 260 case PRU_ACCEPT:
a3fa431a
SL
261 error = EOPNOTSUPP;
262 break;
2b4b57cd 263
53a5409e 264 case PRU_DISCONNECT:
a3fa431a
SL
265 if (inp->inp_faddr.s_addr == INADDR_ANY) {
266 error = ENOTCONN;
267 break;
268 }
405c9168 269 in_pcbdisconnect(inp);
53a5409e 270 soisdisconnected(so);
1bd53abe
BJ
271 break;
272
cdad2eb1
BJ
273 case PRU_SHUTDOWN:
274 socantsendmore(so);
275 break;
276
1aa87517
BJ
277 case PRU_SEND: {
278 struct in_addr laddr;
279
8075bb0e 280 if (nam) {
1aa87517 281 laddr = inp->inp_laddr;
a3fa431a
SL
282 if (inp->inp_faddr.s_addr != INADDR_ANY) {
283 error = EISCONN;
284 break;
285 }
8075bb0e 286 error = in_pcbconnect(inp, nam);
02bcdb4e 287 if (error)
8a2f82db 288 break;
4ad99bae 289 } else {
a3fa431a
SL
290 if (inp->inp_faddr.s_addr == INADDR_ANY) {
291 error = ENOTCONN;
292 break;
293 }
4ad99bae 294 }
8a2f82db 295 error = udp_output(inp, m);
a3fa431a 296 m = NULL;
8075bb0e 297 if (nam) {
405c9168 298 in_pcbdisconnect(inp);
1aa87517
BJ
299 inp->inp_laddr = laddr;
300 }
301 }
1bd53abe
BJ
302 break;
303
304 case PRU_ABORT:
405c9168 305 in_pcbdetach(inp);
53a5409e
BJ
306 sofree(so);
307 soisdisconnected(so);
1bd53abe
BJ
308 break;
309
126472ab 310 case PRU_SOCKADDR:
8075bb0e 311 in_setsockaddr(inp, nam);
126472ab
SL
312 break;
313
1bd53abe 314 default:
11d2a668 315 printf("request %d\n", req);
1bd53abe 316 panic("udp_usrreq");
11d2a668
SL
317
318 case PRU_RCVD:
319 case PRU_CONTROL:
320 case PRU_SENSE:
321 case PRU_RCVOOB:
322 case PRU_SENDOOB:
323 case PRU_FASTTIMO:
324 case PRU_SLOWTIMO:
325 case PRU_PROTORCV:
326 case PRU_PROTOSEND:
327 error = EOPNOTSUPP;
328 break;
d52566dd 329 }
a3fa431a
SL
330release:
331 if (m != NULL)
332 m_freem(m);
8a2f82db 333 return (error);
d52566dd 334}