VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/tcp_output.c@ 1033

Last change on this file since 1033 was 1033, checked in by vboxsync, 18 years ago

Big change to make slirp fully instantiatable (replace all global
variables with local ones, passing a reference to the state/config
structure to all places which are interested). You can now have as many
cards in the guest configured for NAT networking as you want.

  • Property svn:eol-style set to native
File size: 17.8 KB
Line 
1/*
2 * Copyright (c) 1982, 1986, 1988, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
34 * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
35 */
36
37/*
38 * Changes and additions relating to SLiRP
39 * Copyright (c) 1995 Danny Gasparovski.
40 *
41 * Please read the file COPYRIGHT for the
42 * terms and conditions of the copyright.
43 */
44
45#include <slirp.h>
46
47/*
48 * Since this is only used in "stats socket", we give meaning
49 * names instead of the REAL names
50 */
51#ifdef VBOX
52const char * const tcpstates[] = {
53#else /* !VBOX */
54char *tcpstates[] = {
55#endif /* !VBOX */
56/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */
57 "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD",
58 "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
59 "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
60};
61
62#ifdef VBOX
63static const u_char tcp_outflags[TCP_NSTATES] = {
64#else /* !VBOX */
65u_char tcp_outflags[TCP_NSTATES] = {
66#endif /* !VBOX */
67 TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
68 TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
69 TH_FIN|TH_ACK, TH_ACK, TH_ACK,
70};
71
72
73#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
74
75/*
76 * Tcp output routine: figure out what should be sent and send it.
77 */
78int
79#ifdef VBOX
80tcp_output(PNATState pData, register struct tcpcb *tp)
81#else /* !VBOX */
82tcp_output(tp)
83 register struct tcpcb *tp;
84#endif /* !VBOX */
85{
86 register struct socket *so = tp->t_socket;
87 register long len, win;
88 int off, flags, error;
89 register struct mbuf *m;
90 register struct tcpiphdr *ti;
91 u_char opt[MAX_TCPOPTLEN];
92 unsigned optlen, hdrlen;
93 int idle, sendalot;
94
95 DEBUG_CALL("tcp_output");
96 DEBUG_ARG("tp = %lx", (long )tp);
97
98 /*
99 * Determine length of data that should be transmitted,
100 * and flags that will be used.
101 * If there is some data or critical controls (SYN, RST)
102 * to send, then transmit; otherwise, investigate further.
103 */
104 idle = (tp->snd_max == tp->snd_una);
105 if (idle && tp->t_idle >= tp->t_rxtcur)
106 /*
107 * We have been idle for "a while" and no acks are
108 * expected to clock out any data we send --
109 * slow start to get ack "clock" running again.
110 */
111 tp->snd_cwnd = tp->t_maxseg;
112again:
113 sendalot = 0;
114 off = tp->snd_nxt - tp->snd_una;
115 win = min(tp->snd_wnd, tp->snd_cwnd);
116
117 flags = tcp_outflags[tp->t_state];
118
119 DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
120
121 /*
122 * If in persist timeout with window of 0, send 1 byte.
123 * Otherwise, if window is small but nonzero
124 * and timer expired, we will send what we can
125 * and go to transmit state.
126 */
127 if (tp->t_force) {
128 if (win == 0) {
129 /*
130 * If we still have some data to send, then
131 * clear the FIN bit. Usually this would
132 * happen below when it realizes that we
133 * aren't sending all the data. However,
134 * if we have exactly 1 byte of unset data,
135 * then it won't clear the FIN bit below,
136 * and if we are in persist state, we wind
137 * up sending the packet without recording
138 * that we sent the FIN bit.
139 *
140 * We can't just blindly clear the FIN bit,
141 * because if we don't have any more data
142 * to send then the probe will be the FIN
143 * itself.
144 */
145 if (off < so->so_snd.sb_cc)
146 flags &= ~TH_FIN;
147 win = 1;
148 } else {
149 tp->t_timer[TCPT_PERSIST] = 0;
150 tp->t_rxtshift = 0;
151 }
152 }
153
154 len = min(so->so_snd.sb_cc, win) - off;
155
156 if (len < 0) {
157 /*
158 * If FIN has been sent but not acked,
159 * but we haven't been called to retransmit,
160 * len will be -1. Otherwise, window shrank
161 * after we sent into it. If window shrank to 0,
162 * cancel pending retransmit and pull snd_nxt
163 * back to (closed) window. We will enter persist
164 * state below. If the window didn't close completely,
165 * just wait for an ACK.
166 */
167 len = 0;
168 if (win == 0) {
169 tp->t_timer[TCPT_REXMT] = 0;
170 tp->snd_nxt = tp->snd_una;
171 }
172 }
173
174 if (len > tp->t_maxseg) {
175 len = tp->t_maxseg;
176 sendalot = 1;
177 }
178 if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
179 flags &= ~TH_FIN;
180
181 win = sbspace(&so->so_rcv);
182
183 /*
184 * Sender silly window avoidance. If connection is idle
185 * and can send all data, a maximum segment,
186 * at least a maximum default-size segment do it,
187 * or are forced, do it; otherwise don't bother.
188 * If peer's buffer is tiny, then send
189 * when window is at least half open.
190 * If retransmitting (possibly after persist timer forced us
191 * to send into a small window), then must resend.
192 */
193 if (len) {
194 if (len == tp->t_maxseg)
195 goto send;
196 if ((1 || idle || tp->t_flags & TF_NODELAY) &&
197 len + off >= so->so_snd.sb_cc)
198 goto send;
199 if (tp->t_force)
200 goto send;
201 if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
202 goto send;
203 if (SEQ_LT(tp->snd_nxt, tp->snd_max))
204 goto send;
205 }
206
207 /*
208 * Compare available window to amount of window
209 * known to peer (as advertised window less
210 * next expected input). If the difference is at least two
211 * max size segments, or at least 50% of the maximum possible
212 * window, then want to send a window update to peer.
213 */
214 if (win > 0) {
215 /*
216 * "adv" is the amount we can increase the window,
217 * taking into account that we are limited by
218 * TCP_MAXWIN << tp->rcv_scale.
219 */
220 long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
221 (tp->rcv_adv - tp->rcv_nxt);
222
223 if (adv >= (long) (2 * tp->t_maxseg))
224 goto send;
225 if (2 * adv >= (long) so->so_rcv.sb_datalen)
226 goto send;
227 }
228
229 /*
230 * Send if we owe peer an ACK.
231 */
232 if (tp->t_flags & TF_ACKNOW)
233 goto send;
234 if (flags & (TH_SYN|TH_RST))
235 goto send;
236 if (SEQ_GT(tp->snd_up, tp->snd_una))
237 goto send;
238 /*
239 * If our state indicates that FIN should be sent
240 * and we have not yet done so, or we're retransmitting the FIN,
241 * then we need to send.
242 */
243 if (flags & TH_FIN &&
244 ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
245 goto send;
246
247 /*
248 * TCP window updates are not reliable, rather a polling protocol
249 * using ``persist'' packets is used to insure receipt of window
250 * updates. The three ``states'' for the output side are:
251 * idle not doing retransmits or persists
252 * persisting to move a small or zero window
253 * (re)transmitting and thereby not persisting
254 *
255 * tp->t_timer[TCPT_PERSIST]
256 * is set when we are in persist state.
257 * tp->t_force
258 * is set when we are called to send a persist packet.
259 * tp->t_timer[TCPT_REXMT]
260 * is set when we are retransmitting
261 * The output side is idle when both timers are zero.
262 *
263 * If send window is too small, there is data to transmit, and no
264 * retransmit or persist is pending, then go to persist state.
265 * If nothing happens soon, send when timer expires:
266 * if window is nonzero, transmit what we can,
267 * otherwise force out a byte.
268 */
269 if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
270 tp->t_timer[TCPT_PERSIST] == 0) {
271 tp->t_rxtshift = 0;
272 tcp_setpersist(tp);
273 }
274
275 /*
276 * No reason to send a segment, just return.
277 */
278 tcpstat.tcps_didnuttin++;
279
280 return (0);
281
282send:
283 /*
284 * Before ESTABLISHED, force sending of initial options
285 * unless TCP set not to do any options.
286 * NOTE: we assume that the IP/TCP header plus TCP options
287 * always fit in a single mbuf, leaving room for a maximum
288 * link header, i.e.
289 * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
290 */
291 optlen = 0;
292 hdrlen = sizeof (struct tcpiphdr);
293 if (flags & TH_SYN) {
294 tp->snd_nxt = tp->iss;
295 if ((tp->t_flags & TF_NOOPT) == 0) {
296 u_int16_t mss;
297
298 opt[0] = TCPOPT_MAXSEG;
299 opt[1] = 4;
300#ifdef VBOX
301 mss = htons((u_int16_t) tcp_mss(pData, tp, 0));
302#else /* !VBOX */
303 mss = htons((u_int16_t) tcp_mss(tp, 0));
304#endif /* !VBOX */
305 memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
306 optlen = 4;
307
308/* if ((tp->t_flags & TF_REQ_SCALE) &&
309 * ((flags & TH_ACK) == 0 ||
310 * (tp->t_flags & TF_RCVD_SCALE))) {
311 * *((u_int32_t *) (opt + optlen)) = htonl(
312 * TCPOPT_NOP << 24 |
313 * TCPOPT_WINDOW << 16 |
314 * TCPOLEN_WINDOW << 8 |
315 * tp->request_r_scale);
316 * optlen += 4;
317 * }
318 */
319 }
320 }
321
322 /*
323 * Send a timestamp and echo-reply if this is a SYN and our side
324 * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
325 * and our peer have sent timestamps in our SYN's.
326 */
327/* if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
328 * (flags & TH_RST) == 0 &&
329 * ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
330 * (tp->t_flags & TF_RCVD_TSTMP))) {
331 * u_int32_t *lp = (u_int32_t *)(opt + optlen);
332 *
333 * / * Form timestamp option as shown in appendix A of RFC 1323. * /
334 * *lp++ = htonl(TCPOPT_TSTAMP_HDR);
335 * *lp++ = htonl(tcp_now);
336 * *lp = htonl(tp->ts_recent);
337 * optlen += TCPOLEN_TSTAMP_APPA;
338 * }
339 */
340 hdrlen += optlen;
341
342 /*
343 * Adjust data length if insertion of options will
344 * bump the packet length beyond the t_maxseg length.
345 */
346 if (len > tp->t_maxseg - optlen) {
347 len = tp->t_maxseg - optlen;
348 sendalot = 1;
349 }
350
351 /*
352 * Grab a header mbuf, attaching a copy of data to
353 * be transmitted, and initialize the header from
354 * the template for sends on this connection.
355 */
356 if (len) {
357 if (tp->t_force && len == 1)
358 tcpstat.tcps_sndprobe++;
359 else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
360 tcpstat.tcps_sndrexmitpack++;
361 tcpstat.tcps_sndrexmitbyte += len;
362 } else {
363 tcpstat.tcps_sndpack++;
364 tcpstat.tcps_sndbyte += len;
365 }
366
367#ifdef VBOX
368 m = m_get(pData);
369#else /* !VBOX */
370 m = m_get();
371#endif /* !VBOX */
372 if (m == NULL) {
373/* error = ENOBUFS; */
374 error = 1;
375 goto out;
376 }
377 m->m_data += if_maxlinkhdr;
378 m->m_len = hdrlen;
379
380 /*
381 * This will always succeed, since we make sure our mbufs
382 * are big enough to hold one MSS packet + header + ... etc.
383 */
384/* if (len <= MHLEN - hdrlen - max_linkhdr) { */
385
386 sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
387 m->m_len += len;
388
389/* } else {
390 * m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
391 * if (m->m_next == 0)
392 * len = 0;
393 * }
394 */
395 /*
396 * If we're sending everything we've got, set PUSH.
397 * (This will keep happy those implementations which only
398 * give data to the user when a buffer fills or
399 * a PUSH comes in.)
400 */
401 if (off + len == so->so_snd.sb_cc)
402 flags |= TH_PUSH;
403 } else {
404 if (tp->t_flags & TF_ACKNOW)
405 tcpstat.tcps_sndacks++;
406 else if (flags & (TH_SYN|TH_FIN|TH_RST))
407 tcpstat.tcps_sndctrl++;
408 else if (SEQ_GT(tp->snd_up, tp->snd_una))
409 tcpstat.tcps_sndurg++;
410 else
411 tcpstat.tcps_sndwinup++;
412
413#ifdef VBOX
414 m = m_get(pData);
415#else /* !VBOX */
416 m = m_get();
417#endif /* !VBOX */
418 if (m == NULL) {
419/* error = ENOBUFS; */
420 error = 1;
421 goto out;
422 }
423 m->m_data += if_maxlinkhdr;
424 m->m_len = hdrlen;
425 }
426
427 ti = mtod(m, struct tcpiphdr *);
428
429 memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
430
431 /*
432 * Fill in fields, remembering maximum advertised
433 * window for use in delaying messages about window sizes.
434 * If resending a FIN, be sure not to use a new sequence number.
435 */
436 if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
437 tp->snd_nxt == tp->snd_max)
438 tp->snd_nxt--;
439 /*
440 * If we are doing retransmissions, then snd_nxt will
441 * not reflect the first unsent octet. For ACK only
442 * packets, we do not want the sequence number of the
443 * retransmitted packet, we want the sequence number
444 * of the next unsent octet. So, if there is no data
445 * (and no SYN or FIN), use snd_max instead of snd_nxt
446 * when filling in ti_seq. But if we are in persist
447 * state, snd_max might reflect one byte beyond the
448 * right edge of the window, so use snd_nxt in that
449 * case, since we know we aren't doing a retransmission.
450 * (retransmit and persist are mutually exclusive...)
451 */
452 if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
453 ti->ti_seq = htonl(tp->snd_nxt);
454 else
455 ti->ti_seq = htonl(tp->snd_max);
456 ti->ti_ack = htonl(tp->rcv_nxt);
457 if (optlen) {
458 memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
459 ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
460 }
461 ti->ti_flags = flags;
462 /*
463 * Calculate receive window. Don't shrink window,
464 * but avoid silly window syndrome.
465 */
466 if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
467 win = 0;
468 if (win > (long)TCP_MAXWIN << tp->rcv_scale)
469 win = (long)TCP_MAXWIN << tp->rcv_scale;
470 if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
471 win = (long)(tp->rcv_adv - tp->rcv_nxt);
472 ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale));
473
474 if (SEQ_GT(tp->snd_up, tp->snd_una)) {
475 ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
476#ifdef notdef
477 if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
478 ti->ti_urp = htons((u_int16_t)(tp->snd_up - tp->snd_nxt));
479#endif
480 ti->ti_flags |= TH_URG;
481 } else
482 /*
483 * If no urgent pointer to send, then we pull
484 * the urgent pointer to the left edge of the send window
485 * so that it doesn't drift into the send window on sequence
486 * number wraparound.
487 */
488 tp->snd_up = tp->snd_una; /* drag it along */
489
490 /*
491 * Put TCP length in extended header, and then
492 * checksum extended header and data.
493 */
494 if (len + optlen)
495 ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) +
496 optlen + len));
497 ti->ti_sum = cksum(m, (int)(hdrlen + len));
498
499 /*
500 * In transmit state, time the transmission and arrange for
501 * the retransmit. In persist state, just set snd_max.
502 */
503 if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
504 tcp_seq startseq = tp->snd_nxt;
505
506 /*
507 * Advance snd_nxt over sequence space of this segment.
508 */
509 if (flags & (TH_SYN|TH_FIN)) {
510 if (flags & TH_SYN)
511 tp->snd_nxt++;
512 if (flags & TH_FIN) {
513 tp->snd_nxt++;
514 tp->t_flags |= TF_SENTFIN;
515 }
516 }
517 tp->snd_nxt += len;
518 if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
519 tp->snd_max = tp->snd_nxt;
520 /*
521 * Time this transmission if not a retransmission and
522 * not currently timing anything.
523 */
524 if (tp->t_rtt == 0) {
525 tp->t_rtt = 1;
526 tp->t_rtseq = startseq;
527 tcpstat.tcps_segstimed++;
528 }
529 }
530
531 /*
532 * Set retransmit timer if not currently set,
533 * and not doing an ack or a keep-alive probe.
534 * Initial value for retransmit timer is smoothed
535 * round-trip time + 2 * round-trip time variance.
536 * Initialize shift counter which is used for backoff
537 * of retransmit time.
538 */
539 if (tp->t_timer[TCPT_REXMT] == 0 &&
540 tp->snd_nxt != tp->snd_una) {
541 tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
542 if (tp->t_timer[TCPT_PERSIST]) {
543 tp->t_timer[TCPT_PERSIST] = 0;
544 tp->t_rxtshift = 0;
545 }
546 }
547 } else
548 if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
549 tp->snd_max = tp->snd_nxt + len;
550
551 /*
552 * Fill in IP length and desired time to live and
553 * send to IP level. There should be a better way
554 * to handle ttl and tos; we could keep them in
555 * the template, but need a way to checksum without them.
556 */
557 m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
558
559 {
560
561 ((struct ip *)ti)->ip_len = m->m_len;
562
563 ((struct ip *)ti)->ip_ttl = ip_defttl;
564 ((struct ip *)ti)->ip_tos = so->so_iptos;
565
566/* #if BSD >= 43 */
567 /* Don't do IP options... */
568/* error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
569 * so->so_options & SO_DONTROUTE, 0);
570 */
571#ifdef VBOX
572 error = ip_output(pData, so, m);
573#else /* !VBOX */
574 error = ip_output(so, m);
575#endif /* !VBOX */
576
577/* #else
578 * error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route,
579 * so->so_options & SO_DONTROUTE);
580 * #endif
581 */
582 }
583 if (error) {
584out:
585/* if (error == ENOBUFS) {
586 * tcp_quench(tp->t_inpcb, 0);
587 * return (0);
588 * }
589 */
590/* if ((error == EHOSTUNREACH || error == ENETDOWN)
591 * && TCPS_HAVERCVDSYN(tp->t_state)) {
592 * tp->t_softerror = error;
593 * return (0);
594 * }
595 */
596 return (error);
597 }
598 tcpstat.tcps_sndtotal++;
599
600 /*
601 * Data sent (as far as we can tell).
602 * If this advertises a larger window than any other segment,
603 * then remember the size of the advertised window.
604 * Any pending ACK has now been sent.
605 */
606 if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
607 tp->rcv_adv = tp->rcv_nxt + win;
608 tp->last_ack_sent = tp->rcv_nxt;
609 tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
610 if (sendalot)
611 goto again;
612
613 return (0);
614}
615
616void
617tcp_setpersist(tp)
618 register struct tcpcb *tp;
619{
620 int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
621
622/* if (tp->t_timer[TCPT_REXMT])
623 * panic("tcp_output REXMT");
624 */
625 /*
626 * Start/restart persistence timer.
627 */
628 TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
629 t * tcp_backoff[tp->t_rxtshift],
630 TCPTV_PERSMIN, TCPTV_PERSMAX);
631 if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
632 tp->t_rxtshift++;
633}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette