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