xref: /csrg-svn/sys/netiso/tp_inet.c (revision 36400)
1 /***********************************************************
2 		Copyright IBM Corporation 1987
3 
4                       All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
13 
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
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
50 static 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 
91 int
92 in_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  */
121 void
122 in_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  */
155 void
156 in_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  */
180 void
181 in_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 
218 void
219 in_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 
273 void
274 tpip_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 
361 int
362 tpip_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 
390 int
391 tpip_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 
443 bad:
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  */
464 ProtoHook
465 tpip_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 
534 discard:
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 
565 void
566 tpin_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  */
592 ProtoHook
593 tpip_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 
662 ProtoHook
663 tpin_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
675 dump_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  */
696 struct ifnet *
697 tpip_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
745