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