exit on end-of-file if file shorter than a screen, regardless
[unix-history] / usr / src / sys / netiso / tp_inet.c
CommitLineData
2f41dd0f
KS
1/***********************************************************
2 Copyright IBM Corporation 1987
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22******************************************************************/
23
24/*
25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26 */
27/*
28 * ARGO TP
29 * $Header: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $
30 * $Source: /usr/argo/sys/netiso/RCS/tp_inet.c,v $
31 *
32 * Here is where you find the inet-dependent code. We've tried
33 * keep all net-level and (primarily) address-family-dependent stuff
34 * out of the tp source, and everthing here is reached indirectly
35 * through a switch table (struct nl_protosw *) tpcb->tp_nlproto
36 * (see tp_pcb.c).
37 * The routines here are:
38 * in_getsufx: gets transport suffix out of an inpcb structure.
39 * in_putsufx: put transport suffix into an inpcb structure.
40 * in_putnetaddr: put a whole net addr into an inpcb.
41 * in_getnetaddr: get a whole net addr from an inpcb.
42 * in_recycle_suffix: clear suffix for reuse in inpcb
43 * tpip_mtu: figure out what size tpdu to use
44 * tpip_input: take a pkt from ip, strip off its ip header, give to tp
45 * tpip_output_dg: package a pkt for ip given 2 addresses & some data
46 * tpip_output: package a pkt for ip given an inpcb & some data
47 */
48
49#ifndef lint
50static char *rcsid = "$Header: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $";
51#endif lint
52
53#ifdef INET
54
55#include "types.h"
56#include "socket.h"
57#include "socketvar.h"
58#include "mbuf.h"
59#include "errno.h"
60#include "time.h"
61#include "../net/if.h"
62#include "../netiso/tp_param.h"
63#include "../netiso/argo_debug.h"
64#include "../netiso/tp_stat.h"
65#include "../netiso/tp_ip.h"
66#include "../netiso/tp_pcb.h"
67#include "../netiso/tp_trace.h"
68#include "../netiso/tp_stat.h"
69#include "../netiso/tp_tpdu.h"
70#include "../netinet/in_var.h"
71
72
73/*
74 * NAME: in_getsufx()
75
76 * CALLED FROM: pr_usrreq() on PRU_BIND,
77 * PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR
78 *
79 * FUNCTION, ARGUMENTS, and RETURN VALUE:
80 * Get a transport suffix from an inpcb structure (inp).
81 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
82 *
83 * RETURNS: internet port / transport suffix
84 * (CAST TO AN INT)
85 *
86 * SIDE EFFECTS:
87 *
88 * NOTES:
89 */
90
91int
92in_getsufx(inp, which)
93 struct inpcb *inp;
94 int which;
95{
96 switch (which) {
97 case TP_LOCAL:
98 return (int) inp->inp_lport;
99
100 case TP_FOREIGN:
101 return (int) inp->inp_fport;
102 }
103}
104
105/*
106 * NAME: in_putsufx()
107 *
108 * CALLED FROM: tp_newsocket(); i.e., when a connection
109 * is being established by an incoming CR_TPDU.
110 *
111 * FUNCTION, ARGUMENTS:
112 * Put a transport suffix (found in name) into an inpcb structure (inp).
113 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN.
114 *
115 * RETURNS: Nada
116 *
117 * SIDE EFFECTS:
118 *
119 * NOTES:
120 */
121void
122in_putsufx(inp, name, which)
123 struct inpcb *inp;
124 struct sockaddr_in *name;
125 int which;
126{
127 switch (which) {
128 case TP_LOCAL:
129 inp->inp_lport = name->sin_port;
130 break;
131 case TP_FOREIGN:
132 inp->inp_fport = name->sin_port;
133 break;
134 }
135}
136
137/*
138 * NAME: in_recycle_tsuffix()
139 *
140 * CALLED FROM: tp.trans whenever we go into REFWAIT state.
141 *
142 * FUNCTION and ARGUMENT:
143 * Called when a ref is frozen, to allow the suffix to be reused.
144 * (inp) is the net level pcb.
145 *
146 * RETURNS: Nada
147 *
148 * SIDE EFFECTS:
149 *
150 * NOTES: This really shouldn't have to be done in a NET level pcb
151 * but... for the internet world that just the way it is done in BSD...
152 * The alternative is to have the port unusable until the reference
153 * timer goes off.
154 */
155void
156in_recycle_tsuffix(inp)
157 struct inpcb *inp;
158{
159 inp->inp_fport = inp->inp_lport = 0;
160}
161
162/*
163 * NAME: in_putnetaddr()
164 *
165 * CALLED FROM:
166 * tp_newsocket(); i.e., when a connection is being established by an
167 * incoming CR_TPDU.
168 *
169 * FUNCTION and ARGUMENTS:
170 * Copy a whole net addr from a struct sockaddr (name).
171 * into an inpcb (inp).
172 * The argument (which) takes values TP_LOCAL or TP_FOREIGN
173 *
174 * RETURNS: Nada
175 *
176 * SIDE EFFECTS:
177 *
178 * NOTES:
179 */
180void
181in_putnetaddr(inp, name, which)
182 register struct inpcb *inp;
183 struct sockaddr_in *name;
184 int which;
185{
186 switch (which) {
187 case TP_LOCAL:
188 bcopy((caddr_t)&name->sin_addr,
189 (caddr_t)&inp->inp_laddr, sizeof(struct in_addr));
190 /* won't work if the dst address (name) is INADDR_ANY */
191
192 break;
193 case TP_FOREIGN:
194 if( name != (struct sockaddr_in *)0 ) {
195 bcopy((caddr_t)&name->sin_addr,
196 (caddr_t)&inp->inp_faddr, sizeof(struct in_addr));
197 }
198 }
199}
200
201/*
202 * NAME: in_getnetaddr()
203 *
204 * CALLED FROM:
205 * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR
206 * FUNCTION and ARGUMENTS:
207 * Copy a whole net addr from an inpcb (inp) into
208 * a struct sockaddr (name).
209 * The argument (which) takes values TP_LOCAL or TP_FOREIGN.
210 *
211 * RETURNS: Nada
212 *
213 * SIDE EFFECTS:
214 *
215 * NOTES:
216 */
217
218void
219in_getnetaddr( inp, name, which)
220 struct inpcb *inp;
221 struct sockaddr_in *name;
222 int which;
223{
224 switch (which) {
225 case TP_LOCAL:
226 bcopy( (caddr_t)&inp->inp_laddr, (caddr_t)&name->sin_addr,
227 sizeof(struct in_addr));
228 /* won't work if the dst address (name) is INADDR_ANY */
229 break;
230
231 case TP_FOREIGN:
232 bcopy( (caddr_t)&inp->inp_faddr, (caddr_t)&name->sin_addr,
233 sizeof(struct in_addr));
234 /* won't work if the dst address (name) is INADDR_ANY */
235 break;
236 }
237}
238
239/*
240 * NAME: tpip_mtu()
241 *
242 * CALLED FROM:
243 * tp_input() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT
244 *
245 * FUNCTION, ARGUMENTS, and RETURN VALUE:
246 *
247 * Determine the proper maximum transmission unit, i.e., MTU, to use, given
248 * a) the header size for the network protocol and the max transmission
249 * unit on the subnet interface, determined from the information in (inp),
250 * b) the max size negotiated so far (negot)
251 * c) the window size used by the tp connection (found in so),
252 *
253 * The result is put in the integer *size in its integer form and in
254 * *negot in its logarithmic form.
255 *
256 * The rules are:
257 * a) can only negotiate down from the value found in *negot.
258 * b) the MTU must be < the windowsize,
259 * c) If src and dest are on the same net,
260 * we will negotiate the closest size larger than MTU but really USE
261 * the actual device mtu - ll hdr sizes.
262 * otherwise we negotiate the closest size smaller than MTU - ll hdr sizes.
263 *
264 * SIDE EFFECTS:
265 * changes the values addressed by the arguments (size) and (negot)
266 * and
267 * when the peer is not on one of our directly connected subnets, it
268 * looks up a route, leaving the route in the inpcb addressed by (inp)
269 *
270 * NOTES:
271 */
272
273void
274tpip_mtu(so, inp, size, negot)
275 struct socket *so;
276 struct inpcb *inp;
277 int *size;
278 u_char *negot;
279{
280 register struct ifnet *ifp;
281 struct ifnet *tpip_route();
282 struct in_ifaddr *ia;
283 register int i;
284 int windowsize = so->so_rcv.sb_hiwat;
285
286 IFDEBUG(D_CONN)
287 printf("tpip_mtu(0x%x,0x%x,0x%x,0x%x)\n",
288 so, inp, size, negot);
289 printf("tpip_mtu routing to addr 0x%x\n", inp->inp_faddr);
290 ENDDEBUG
291 IFTRACE(D_CONN)
292 tptrace(TPPTmisc, "ENTER GET MTU: size negot \n",*size, *negot, 0, 0);
293 ENDTRACE
294
295 *size = 1 << *negot;
296
297 if( *size > windowsize ) {
298 *size = windowsize;
299 }
300
301 ia = in_iaonnetof(in_netof(inp->inp_faddr));
302 if ( ia == (struct in_ifaddr *)0 ) {
303 ifp = tpip_route(&inp->inp_faddr);
304 if( ifp == (struct ifnet *)0 )
305 return ;
306 } else
307 ifp = ia->ia_ifp;
308
309
310 /****************************************************************
311 * TODO - make this indirect off the socket structure to the
312 * network layer to get headersize
313 * After all, who knows what lies below the IP layer?
314 * Who knows how big the NL header will be?
315 ***************************************************************/
316
317 if( *size > ifp->if_mtu - sizeof(struct ip)) {
318 *size = ifp->if_mtu - sizeof(struct ip);
319 }
320 for(i=TP_MIN_TPDUSIZE; (i<TP_MAX_TPDUSIZE && ((1<<i)<*size)) ; i++)
321 ;
322 i--;
323
324 if (in_netof(inp->inp_laddr) != in_netof(inp->inp_faddr)) {
325 i++;
326 } else {
327 *size = 1<<i;
328 }
329 *negot = i;
330
331 IFDEBUG(D_CONN)
332 printf("GET MTU RETURNS: ifp %s size 0x%x negot 0x%x\n",
333 ifp->if_name, *size, *negot);
334 ENDDEBUG
335 IFTRACE(D_CONN)
336 tptrace(TPPTmisc, "EXIT GET MTU: tpcb size negot \n",
337 *size, *negot, 0, 0);
338 ENDTRACE
339
340}
341
342/*
343 * NAME: tpip_output()
344 *
345 * CALLED FROM: tp_emit()
346 *
347 * FUNCTION and ARGUMENTS:
348 * Take a packet(m0) from tp and package it so that ip will accept it.
349 * This means prepending space for the ip header and filling in a few
350 * of the fields.
351 * inp is the inpcb structure; datalen is the length of the data in the
352 * mbuf string m0.
353 * RETURNS:
354 * whatever (E*) is returned form the net layer output routine.
355 *
356 * SIDE EFFECTS:
357 *
358 * NOTES:
359 */
360
361int
362tpip_output(inp, m0, datalen, nochksum)
363 struct inpcb *inp;
364 struct mbuf *m0;
365 int datalen;
366 int nochksum;
367{
368 return tpip_output_dg( &inp->inp_laddr, &inp->inp_faddr, m0, datalen,
369 &inp->inp_route, nochksum);
370}
371
372/*
373 * NAME: tpip_output_dg()
374 *
375 * CALLED FROM: tp_error_emit()
376 *
377 * FUNCTION and ARGUMENTS:
378 * This is a copy of tpip_output that takes the addresses
379 * instead of a pcb. It's used by the tp_error_emit, when we
380 * don't have an in_pcb with which to call the normal output rtn.
381 *
382 * RETURNS: ENOBUFS or whatever (E*) is
383 * returned form the net layer output routine.
384 *
385 * SIDE EFFECTS:
386 *
387 * NOTES:
388 */
389
390int
391tpip_output_dg(laddr, faddr, m0, datalen, ro, nochksum)
392 struct in_addr *laddr, *faddr;
393 struct mbuf *m0;
394 int datalen;
395 struct route *ro;
396 int nochksum;
397{
398 register struct mbuf *m;
399 register struct ip *ip;
400 int error;
401
402 IFDEBUG(D_EMIT)
403 printf("tpip_output_dg datalen 0x%x m0 0x%x\n", datalen, m0);
404 ENDDEBUG
405
406
407 MGET(m, M_DONTWAIT, TPMT_IPHDR);
408 if (m == 0) {
409 error = ENOBUFS;
410 goto bad;
411 }
412 bzero(mtod(m, caddr_t), MLEN);
413 m->m_next = m0;
414 m->m_off = MMAXOFF - sizeof(struct ip);
415 m->m_len = sizeof(struct ip);
416 m->m_act = MNULL;
417
418 ip = mtod(m, struct ip *);
419
420 ip->ip_p = IPPROTO_TP;
421 ip->ip_len = sizeof(struct ip) + datalen;
422 ip->ip_ttl = MAXTTL;
423 /* don't know why you need to set ttl;
424 * overlay doesn't even make this available
425 */
426
427 ip->ip_src = *laddr;
428 ip->ip_dst = *faddr;
429
430 IncStat(ts_tpdu_sent);
431 IFDEBUG(D_EMIT)
432 dump_mbuf(m, "tpip_output_dg before ip_output\n");
433 ENDDEBUG
434
435 error = ip_output(m, (struct mbuf *)0, ro, IP_ALLOWBROADCAST);
436
437 IFDEBUG(D_EMIT)
438 printf("tpip_output_dg after ip_output\n");
439 ENDDEBUG
440
441 return error;
442
443bad:
444 m_freem(m);
445 IncStat(ts_send_drop);
446 return error;
447}
448
449/*
450 * NAME: tpip_input()
451 *
452 * CALLED FROM:
453 * ip's input routine, indirectly through the protosw.
454 *
455 * FUNCTION and ARGUMENTS:
456 * Take a packet (m) from ip, strip off the ip header and give it to tp
457 *
458 * RETURNS: No return value.
459 *
460 * SIDE EFFECTS:
461 *
462 * NOTES:
463 */
464ProtoHook
465tpip_input(m)
466 struct mbuf *m;
467{
468 typedef struct {
469 struct ip tpip_i;
470 struct tpdu tpip_d;
471 } tpiphdr;
472 register struct tpdu *hdr = mtod(m, struct tpdu *);
473 struct sockaddr_in src, dst;
474 register struct ip *ip;
475 int s = splnet();
476
477 IncStat(ts_pkt_rcvd);
478
479 /* IP layer has already pulled up the IP header */
480
481 while( m->m_len < 1 ) {
482 struct mbuf *n;
483 n = m_free(m);
484 if( n == MNULL ) {
485 splx(s);
486 return 0;
487 }
488 }
489 CHANGE_MTYPE(m, TPMT_DATA);
490
491 /*
492 * now pull up the whole tp header : we stripped all leading mbufs
493 * w/o at least one byte, so we know we can read the tpdu_li field.
494 */
495 hdr = &(mtod(m, tpiphdr *))->tpip_d;
496
497 if( m->m_len < hdr->tpdu_li + 1 + sizeof(struct ip) ) {
498 if((m = m_pullup(m, sizeof(struct ip) + (int)(hdr->tpdu_li)+1))==MNULL){
499 IFDEBUG(D_TPINPUT)
500 printf("tp_input, pullup 2!\n");
501 ENDDEBUG
502 goto discard;
503 }
504 }
505 /*
506 * cannot use tp_inputprep() here 'cause you don't
507 * have quite the same situation
508 */
509
510 IFDEBUG(D_TPINPUT)
511 dump_mbuf(m, "after tpip_input both pullups");
512 ENDDEBUG
513 /*
514 * m_pullup may have returned a different mbuf
515 */
516 ip = &(mtod(m, tpiphdr *))->tpip_i;
517
518 /*
519 * drop the ip header from the front of the mbuf
520 * this is necessary for the tp checksum
521 */
522 m->m_len -= sizeof(struct ip);
523 m->m_off += sizeof(struct ip);
524
525 src.sin_addr = *(struct in_addr *)&(ip->ip_src);
526 src.sin_family = AF_INET;
527 dst.sin_addr = *(struct in_addr *)&(ip->ip_dst);
528 dst.sin_family = AF_INET;
529
530 (void) tp_input(m, &src, &dst, 0, tpip_output_dg);
531 splx(s);
532 return 0;
533
534discard:
535 IFDEBUG(D_TPINPUT)
536 printf("tpip_input DISCARD\n");
537 ENDDEBUG
538 IFTRACE(D_TPINPUT)
539 tptrace(TPPTmisc, "tpip_input DISCARD m", m,0,0,0);
540 ENDTRACE
541 m_freem(m);
542 IncStat(ts_recv_drop);
543
544 return 0;
545}
546
547
548#include "../h/protosw.h"
549#include "../netinet/ip_icmp.h"
550
551/*
552 * NAME: tpin_quench()
553 *
554 * CALLED FROM: tpip_ctlinput()
555 *
556 * FUNCTION and ARGUMENTS: find the tpcb pointer and pass it to tp_quench
557 *
558 * RETURNS: Nada
559 *
560 * SIDE EFFECTS:
561 *
562 * NOTES:
563 */
564
565void
566tpin_quench(inp)
567 struct inpcb *inp;
568{
569 tp_quench( inp->inp_socket->so_tpcb );
570}
571
572/*
573 * NAME: tpip_ctlinput()
574 *
575 * CALLED FROM:
576 * The network layer through the protosw table.
577 *
578 * FUNCTION and ARGUMENTS:
579 * When clnp gets an ICMP msg this gets called.
580 * It either returns an error status to the user or
581 * causes all connections on this address to be aborted
582 * by calling the appropriate xx_notify() routine.
583 * (cmd) is the type of ICMP error.
584 * (sa) the address of the sender
585 *
586 * RETURNS: Nothing
587 *
588 * SIDE EFFECTS:
589 *
590 * NOTES:
591 */
592ProtoHook
593tpip_ctlinput(cmd, sin)
594 int cmd;
595 struct sockaddr_in *sin;
596{
597 extern u_char inetctlerrmap[];
598 extern ProtoHook tpin_abort();
599 extern ProtoHook in_rtchange();
600
601 if (sin->sin_family != AF_INET && sin->sin_family != AF_IMPLINK)
602 return 0;
603 if (sin->sin_addr.s_addr == INADDR_ANY)
604 return 0;
605 if (cmd < 0 || cmd > PRC_NCMDS)
606 return 0;
607 switch (cmd) {
608
609 case PRC_QUENCH:
610 in_pcbnotify(&tp_inpcb, &sin->sin_addr, 0, tp_quench);
611 break;
612
613 case PRC_ROUTEDEAD:
614 case PRC_HOSTUNREACH:
615 case PRC_UNREACH_NET:
616 case PRC_IFDOWN:
617 case PRC_HOSTDEAD:
618 in_pcbnotify(&tp_inpcb, &sin->sin_addr,
619 (int)inetctlerrmap[cmd], in_rtchange);
620 break;
621
622 default:
623 /*
624 case PRC_MSGSIZE:
625 case PRC_UNREACH_HOST:
626 case PRC_UNREACH_PROTOCOL:
627 case PRC_UNREACH_PORT:
628 case PRC_UNREACH_NEEDFRAG:
629 case PRC_UNREACH_SRCFAIL:
630 case PRC_REDIRECT_NET:
631 case PRC_REDIRECT_HOST:
632 case PRC_REDIRECT_TOSNET:
633 case PRC_REDIRECT_TOSHOST:
634 case PRC_TIMXCEED_INTRANS:
635 case PRC_TIMXCEED_REASS:
636 case PRC_PARAMPROB:
637 */
638 in_pcbnotify(&tp_inpcb, sin, (int)inetctlerrmap[cmd], tpin_abort);
639 }
640 return 0;
641}
642
643/*
644 * NAME: tpin_abort()
645 *
646 * CALLED FROM:
647 * xxx_notify() from tp_ctlinput() when
648 * net level gets some ICMP-equiv. type event.
649 *
650 * FUNCTION and ARGUMENTS:
651 * Cause the connection to be aborted with some sort of error
652 * reason indicating that the network layer caused the abort.
653 * Fakes an ER TPDU so we can go through the driver.
654 *
655 * RETURNS: Nothing
656 *
657 * SIDE EFFECTS:
658 *
659 * NOTES:
660 */
661
662ProtoHook
663tpin_abort(inp)
664 struct inpcb *inp;
665{
666 struct tp_event e;
667
668 e.ev_number = ER_TPDU;
669 e.ATTR(ER_TPDU).e_reason = ENETRESET;
670 (void) tp_driver(inp->inp_ppcb, &e);
671 return 0;
672}
673
674#ifdef ARGO_DEBUG
675dump_inaddr(addr)
676 register struct sockaddr_in *addr;
677{
678 printf("INET: port 0x%x; addr 0x%x\n", addr->sin_port, addr->sin_addr);
679}
680#endif ARGO_DEBUG
681
682/*
683 * NAME: tpip_route()
684 *
685 * CALLED FROM: tpip_mtu()
686 *
687 * FUNCTION and ARGUMENTS: given a destination addresss,
688 * find the interface that would be used to send something to this address.
689 *
690 * RETURNS: pointer to an ifnet structure
691 *
692 * SIDE EFFECTS:
693 *
694 * NOTES:
695 */
696struct ifnet *
697tpip_route(dst)
698 struct in_addr *dst;
699{
700 struct ifnet *ifp = (struct ifnet *)0;
701 struct sockaddr_in *dst_in;
702 struct route iproute;
703 struct route *ro = (struct route *)0;
704 struct in_ifaddr *ia;
705
706 IFDEBUG(D_CONN)
707 printf("tpip_route: dst is x%x\n", *dst);
708 ENDDEBUG
709
710 ro = &iproute;
711 bzero((caddr_t)ro, sizeof (*ro));
712 dst_in = (struct sockaddr_in *)&ro->ro_dst;
713 dst_in->sin_family = AF_INET;
714 dst_in->sin_addr = *dst;
715
716 ia = (struct in_ifaddr *)ifa_ifwithdstaddr(dst_in);
717 if (ia == 0)
718 ia = in_iaonnetof(in_netof(dst_in));
719 if (ia != 0) {
720 ifp = ia->ia_ifp;
721 IFDEBUG(D_CONN)
722 printf("tpip_route: ifp from ia:0x%x\n", ia);
723 ENDDEBUG
724 } else {
725 rtalloc(ro);
726 if (ro->ro_rt != 0) {
727 ifp = ro->ro_rt->rt_ifp;
728 IFDEBUG(D_CONN)
729 printf("tpip_route: ifp from route:0x%x ro_rt 0x%x\n", ro,
730 ro->ro_rt);
731 ENDDEBUG
732 rtfree(ro->ro_rt);
733 }
734 }
735 IFDEBUG(D_CONN)
736 printf("tpip_route: returning 0x%x\n", ifp);
737 if (ifp)
738 printf("tpip_route: if name %s unit 0x%x, mtu 0x%x\n",
739 ifp->if_name, ifp->if_unit, ifp->if_mtu);
740 ENDDEBUG
741 return ifp;
742}
743
744#endif INET