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
in_putsufx(inp,sufxloc,sufxlen,which)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
in_recycle_tsuffix(inp)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
in_putnetaddr(inp,name,which)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 */
in_cmpnetaddr(inp,name,which)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
in_getnetaddr(inp,name,which)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
tpip_mtu(tpcb)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
tpip_output(inp,m0,datalen,nochksum)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
tpip_output_dg(laddr,faddr,m0,datalen,ro,nochksum)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
tpip_input(m,iplen)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
tpin_quench(inp)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
tpip_ctlinput(cmd,sin)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
tpin_abort(inp)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
dump_inaddr(addr)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