rx: Remove needless braces
[openafs.git] / src / rx / rx_user.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* rx_user.c contains routines specific to the user space UNIX implementation of rx */
11
12 /* rxi_syscall is currently not prototyped */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18
19 #include <afs/opr.h>
20
21 #ifdef AFS_NT40_ENV
22 # include <WINNT/syscfg.h>
23 #else
24 # include <net/if.h>
25 #endif
26 #if !defined(AFS_AIX_ENV) && !defined(AFS_NT40_ENV)
27 # include <sys/syscall.h>
28 #endif
29 #include <afs/afs_args.h>
30 #include <afs/afsutil.h>
31
32 #ifndef IPPORT_USERRESERVED
33 /* If in.h doesn't define this, define it anyway.  Unfortunately, defining
34    this doesn't put the code into the kernel to restrict kernel assigned
35    port numbers to numbers below IPPORT_USERRESERVED...  */
36 #define IPPORT_USERRESERVED 5000
37 # endif
38
39 #if defined(HAVE_LINUX_ERRQUEUE_H) && defined(ADAPT_PMTU)
40 #include <linux/types.h>
41 #include <linux/errqueue.h>
42 #ifndef IP_MTU
43 #define IP_MTU 14
44 #endif
45 #endif
46
47 #include "rx.h"
48 #include "rx_atomic.h"
49 #include "rx_globals.h"
50 #include "rx_stats.h"
51 #include "rx_packet.h"
52
53 #ifdef AFS_PTHREAD_ENV
54
55 /*
56  * The rx_if_init_mutex mutex protects the following global variables:
57  * Inited
58  */
59
60 afs_kmutex_t rx_if_init_mutex;
61 #define LOCK_IF_INIT MUTEX_ENTER(&rx_if_init_mutex)
62 #define UNLOCK_IF_INIT MUTEX_EXIT(&rx_if_init_mutex)
63
64 /*
65  * The rx_if_mutex mutex protects the following global variables:
66  * myNetFlags
67  * myNetMTUs
68  * myNetMasks
69  */
70
71 afs_kmutex_t rx_if_mutex;
72 #define LOCK_IF MUTEX_ENTER(&rx_if_mutex)
73 #define UNLOCK_IF MUTEX_EXIT(&rx_if_mutex)
74 #else
75 #define LOCK_IF_INIT
76 #define UNLOCK_IF_INIT
77 #define LOCK_IF
78 #define UNLOCK_IF
79 #endif /* AFS_PTHREAD_ENV */
80
81
82 /*
83  * Make a socket for receiving/sending IP packets.  Set it into non-blocking
84  * and large buffering modes.  If port isn't specified, the kernel will pick
85  * one.  Returns the socket (>= 0) on success.  Returns OSI_NULLSOCKET on
86  * failure. Port must be in network byte order.
87  */
88 osi_socket
89 rxi_GetHostUDPSocket(u_int ahost, u_short port)
90 {
91     int binds, code = 0;
92     osi_socket socketFd = OSI_NULLSOCKET;
93     struct sockaddr_in taddr;
94     char *name = "rxi_GetUDPSocket: ";
95 #ifdef AFS_LINUX22_ENV
96 #if defined(ADAPT_PMTU)
97     int pmtu=IP_PMTUDISC_WANT;
98     int recverr=1;
99 #else
100     int pmtu=IP_PMTUDISC_DONT;
101 #endif
102 #endif
103
104 #if !defined(AFS_NT40_ENV)
105     if (ntohs(port) >= IPPORT_RESERVED && ntohs(port) < IPPORT_USERRESERVED) {
106 /*      (osi_Msg "%s*WARNING* port number %d is not a reserved port number.  Use port numbers above %d\n", name, port, IPPORT_USERRESERVED);
107 */ ;
108     }
109     if (ntohs(port) > 0 && ntohs(port) < IPPORT_RESERVED && geteuid() != 0) {
110         (osi_Msg
111          "%sport number %d is a reserved port number which may only be used by root.  Use port numbers above %d\n",
112          name, ntohs(port), IPPORT_USERRESERVED);
113         goto error;
114     }
115 #endif
116     socketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
117
118     if (socketFd == OSI_NULLSOCKET) {
119 #ifdef AFS_NT40_ENV
120         fprintf(stderr, "socket() failed with error %u\n", WSAGetLastError());
121 #else
122         perror("socket");
123 #endif
124         goto error;
125     }
126
127 #ifdef AFS_NT40_ENV
128     rxi_xmit_init(socketFd);
129 #endif /* AFS_NT40_ENV */
130
131     taddr.sin_addr.s_addr = ahost;
132     taddr.sin_family = AF_INET;
133     taddr.sin_port = (u_short) port;
134 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
135     taddr.sin_len = sizeof(struct sockaddr_in);
136 #endif
137 #define MAX_RX_BINDS 10
138     for (binds = 0; binds < MAX_RX_BINDS; binds++) {
139         if (binds)
140             rxi_Delay(10);
141         code = bind(socketFd, (struct sockaddr *)&taddr, sizeof(taddr));
142         break;
143     }
144     if (code) {
145         (osi_Msg "%sbind failed\n", name);
146         goto error;
147     }
148 #if !defined(AFS_NT40_ENV)
149     /*
150      * Set close-on-exec on rx socket
151      */
152     fcntl(socketFd, F_SETFD, 1);
153 #endif
154
155     /* Use one of three different ways of getting a socket buffer expanded to
156      * a reasonable size.
157      */
158     {
159         int greedy = 0;
160         int len1, len2;
161
162         len1 = 32766;
163         len2 = rx_UdpBufSize;
164
165         /* find the size closest to rx_UdpBufSize that will be accepted */
166         while (!greedy && len2 > len1) {
167             greedy =
168                 (setsockopt
169                   (socketFd, SOL_SOCKET, SO_RCVBUF, (char *)&len2,
170                    sizeof(len2)) >= 0);
171             if (!greedy)
172                 len2 /= 2;
173         }
174
175         /* but do not let it get smaller than 32K */
176         if (len2 < len1)
177             len2 = len1;
178
179         if (len1 < len2)
180             len1 = len2;
181
182
183         greedy =
184             (setsockopt
185              (socketFd, SOL_SOCKET, SO_SNDBUF, (char *)&len1,
186               sizeof(len1)) >= 0)
187             &&
188             (setsockopt
189              (socketFd, SOL_SOCKET, SO_RCVBUF, (char *)&len2,
190               sizeof(len2)) >= 0);
191         if (!greedy)
192             (osi_Msg "%s*WARNING* Unable to increase buffering on socket\n",
193              name);
194         if (rx_stats_active)
195             rx_atomic_set(&rx_stats.socketGreedy, greedy);
196     }
197
198 #ifdef AFS_LINUX22_ENV
199     setsockopt(socketFd, SOL_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
200 #if defined(ADAPT_PMTU)
201     setsockopt(socketFd, SOL_IP, IP_RECVERR, &recverr, sizeof(recverr));
202 #endif
203 #endif
204     if (rxi_Listen(socketFd) < 0) {
205         goto error;
206     }
207
208     return socketFd;
209
210   error:
211 #ifdef AFS_NT40_ENV
212     if (socketFd != OSI_NULLSOCKET)
213         closesocket(socketFd);
214 #else
215     if (socketFd != OSI_NULLSOCKET)
216         close(socketFd);
217 #endif
218
219     return OSI_NULLSOCKET;
220 }
221
222 osi_socket
223 rxi_GetUDPSocket(u_short port)
224 {
225     return rxi_GetHostUDPSocket(htonl(INADDR_ANY), port);
226 }
227
228 void
229 osi_Panic(char *msg, ...)
230 {
231     va_list ap;
232     va_start(ap, msg);
233     (osi_Msg "Fatal Rx error: ");
234     (osi_VMsg msg, ap);
235     va_end(ap);
236     fflush(stderr);
237     fflush(stdout);
238     opr_abort();
239 }
240
241 /*
242  * osi_AssertFailU() -- used by the osi_Assert() macro.
243  */
244
245 void
246 osi_AssertFailU(const char *expr, const char *file, int line)
247 {
248     osi_Panic("assertion failed: %s, file: %s, line: %d\n", expr,
249               file, line);
250 }
251
252 #if defined(AFS_AIX32_ENV) && !defined(KERNEL)
253 #ifndef osi_Alloc
254 static const char memZero;
255 void *
256 osi_Alloc(afs_int32 x)
257 {
258     /*
259      * 0-length allocs may return NULL ptr from malloc, so we special-case
260      * things so that NULL returned iff an error occurred
261      */
262     if (x == 0)
263         return (void *)&memZero;
264     return(malloc(x));
265 }
266
267 void
268 osi_Free(void *x, afs_int32 size)
269 {
270     if (x == &memZero)
271         return;
272     free(x);
273 }
274 #endif
275 #endif /* defined(AFS_AIX32_ENV) && !defined(KERNEL) */
276
277 #define ADDRSPERSITE    16
278
279
280 static afs_uint32 rxi_NetAddrs[ADDRSPERSITE];   /* host order */
281 static int myNetMTUs[ADDRSPERSITE];
282 static int myNetMasks[ADDRSPERSITE];
283 static int myNetFlags[ADDRSPERSITE];
284 static u_int rxi_numNetAddrs;
285 static int Inited = 0;
286
287 #if defined(AFS_NT40_ENV)
288 int
289 rxi_getaddr(void)
290 {
291     /* The IP address list can change so we must query for it */
292     rx_GetIFInfo();
293
294     /* we don't want to use the loopback adapter which is first */
295     /* this is a bad bad hack */
296     if (rxi_numNetAddrs > 1)
297         return htonl(rxi_NetAddrs[1]);
298     else if (rxi_numNetAddrs > 0)
299         return htonl(rxi_NetAddrs[0]);
300     else
301         return 0;
302 }
303
304 /*
305 ** return number of addresses
306 ** and the addresses themselves in the buffer
307 ** maxSize - max number of interfaces to return.
308 */
309 int
310 rx_getAllAddr(afs_uint32 * buffer, int maxSize)
311 {
312     int count = 0, offset = 0;
313
314     /* The IP address list can change so we must query for it */
315     rx_GetIFInfo();
316
317     for (count = 0; offset < rxi_numNetAddrs && maxSize > 0;
318          count++, offset++, maxSize--)
319         buffer[count] = htonl(rxi_NetAddrs[offset]);
320
321     return count;
322 }
323
324 /* this function returns the total number of interface addresses
325  * the buffer has to be passed in by the caller. It also returns
326  * the matching interface mask and mtu.  All values are returned
327  * in network byte order.
328  */
329 int
330 rx_getAllAddrMaskMtu(afs_uint32 addrBuffer[], afs_uint32 maskBuffer[],
331                      afs_uint32 mtuBuffer[], int maxSize)
332 {
333     int count = 0, offset = 0;
334
335     /* The IP address list can change so we must query for it */
336     rx_GetIFInfo();
337
338     for (count = 0;
339          offset < rxi_numNetAddrs && maxSize > 0;
340          count++, offset++, maxSize--) {
341         addrBuffer[count] = htonl(rxi_NetAddrs[offset]);
342         maskBuffer[count] = htonl(myNetMasks[offset]);
343         mtuBuffer[count]  = htonl(myNetMTUs[offset]);
344     }
345     return count;
346 }
347 #endif
348
349 #ifdef AFS_NT40_ENV
350 extern int rxinit_status;
351 void
352 rxi_InitMorePackets(void) {
353     int npackets, ncbufs;
354
355     ncbufs = (rx_maxJumboRecvSize - RX_FIRSTBUFFERSIZE);
356     if (ncbufs > 0) {
357         ncbufs = ncbufs / RX_CBUFFERSIZE;
358         npackets = rx_initSendWindow - 1;
359         rxi_MorePackets(npackets * (ncbufs + 1));
360     }
361 }
362 void
363 rx_GetIFInfo(void)
364 {
365     u_int maxsize;
366     u_int rxsize;
367     afs_uint32 i;
368
369     LOCK_IF_INIT;
370     if (Inited) {
371         if (Inited < 2 && rxinit_status == 0) {
372             /* We couldn't initialize more packets earlier.
373              * Do it now. */
374             rxi_InitMorePackets();
375             Inited = 2;
376         }
377         UNLOCK_IF_INIT;
378         return;
379     }
380     Inited = 1;
381     UNLOCK_IF_INIT;
382
383     LOCK_IF;
384     rxi_numNetAddrs = ADDRSPERSITE;
385     (void)syscfg_GetIFInfo(&rxi_numNetAddrs, rxi_NetAddrs,
386                            myNetMasks, myNetMTUs, myNetFlags);
387
388     for (i = 0; i < rxi_numNetAddrs; i++) {
389         rxsize = rxi_AdjustIfMTU(myNetMTUs[i] - RX_IPUDP_SIZE);
390         maxsize =
391             rxi_nRecvFrags * rxsize + (rxi_nRecvFrags - 1) * UDP_HDR_SIZE;
392         maxsize = rxi_AdjustMaxMTU(rxsize, maxsize);
393         if (rx_maxReceiveSize > maxsize) {
394             rx_maxReceiveSize = MIN(RX_MAX_PACKET_SIZE, maxsize);
395             rx_maxReceiveSize =
396                 MIN(rx_maxReceiveSize, rx_maxReceiveSizeUser);
397         }
398         if (rx_MyMaxSendSize > maxsize) {
399             rx_MyMaxSendSize = MIN(RX_MAX_PACKET_SIZE, maxsize);
400         }
401     }
402     UNLOCK_IF;
403
404     /*
405      * If rxinit_status is still set, rx_InitHost() has yet to be called
406      * and we therefore do not have any mutex locks initialized.  As a
407      * result we cannot call rxi_MorePackets() without crashing.
408      */
409     if (rxinit_status)
410         return;
411
412     rxi_InitMorePackets();
413 }
414 #endif
415
416 static afs_uint32
417 fudge_netmask(afs_uint32 addr)
418 {
419     afs_uint32 msk;
420
421     if (IN_CLASSA(addr))
422         msk = IN_CLASSA_NET;
423     else if (IN_CLASSB(addr))
424         msk = IN_CLASSB_NET;
425     else if (IN_CLASSC(addr))
426         msk = IN_CLASSC_NET;
427     else
428         msk = 0;
429
430     return msk;
431 }
432
433
434
435 #if !defined(AFS_AIX_ENV) && !defined(AFS_NT40_ENV) && !defined(AFS_LINUX20_ENV)
436 int
437 rxi_syscall(afs_uint32 a3, afs_uint32 a4, void *a5)
438 {
439     afs_uint32 rcode;
440     void (*old) (int);
441
442     old = signal(SIGSYS, SIG_IGN);
443
444 #if defined(AFS_SGI_ENV)
445     rcode = afs_syscall(AFS_SYSCALL, 28, a3, a4, a5);
446 #else
447     rcode = syscall(AFS_SYSCALL, 28 /* AFSCALL_CALL */ , a3, a4, a5);
448 #endif /* AFS_SGI_ENV */
449
450     signal(SIGSYS, old);
451
452     return rcode;
453 }
454 #endif /* AFS_AIX_ENV */
455
456 #ifndef AFS_NT40_ENV
457 void
458 rx_GetIFInfo(void)
459 {
460     int s;
461     int i, j, len, res;
462     struct ifconf ifc;
463     struct ifreq ifs[ADDRSPERSITE];
464     struct ifreq *ifr;
465 #ifdef  AFS_AIX41_ENV
466     char buf[BUFSIZ], *cp, *cplim;
467 #endif
468     struct sockaddr_in *a;
469
470     LOCK_IF_INIT;
471     if (Inited) {
472         UNLOCK_IF_INIT;
473         return;
474     }
475     Inited = 1;
476     UNLOCK_IF_INIT;
477     LOCK_IF;
478     rxi_numNetAddrs = 0;
479     memset(rxi_NetAddrs, 0, sizeof(rxi_NetAddrs));
480     memset(myNetFlags, 0, sizeof(myNetFlags));
481     memset(myNetMTUs, 0, sizeof(myNetMTUs));
482     memset(myNetMasks, 0, sizeof(myNetMasks));
483     UNLOCK_IF;
484     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
485     if (s == OSI_NULLSOCKET)
486         return;
487 #ifdef  AFS_AIX41_ENV
488     ifc.ifc_len = sizeof(buf);
489     ifc.ifc_buf = buf;
490     ifr = ifc.ifc_req;
491 #else
492     ifc.ifc_len = sizeof(ifs);
493     ifc.ifc_buf = (caddr_t) & ifs[0];
494     memset(&ifs[0], 0, sizeof(ifs));
495 #endif
496     res = ioctl(s, SIOCGIFCONF, &ifc);
497     if (res < 0) {
498         /* fputs(stderr, "ioctl error IFCONF\n"); */
499         close(s);
500         return;
501     }
502
503     LOCK_IF;
504 #ifdef  AFS_AIX41_ENV
505 #define size(p) MAX((p).sa_len, sizeof(p))
506     cplim = buf + ifc.ifc_len;  /*skip over if's with big ifr_addr's */
507     for (cp = buf; cp < cplim;
508          cp += sizeof(ifr->ifr_name) + MAX(a->sin_len, sizeof(*a))) {
509         if (rxi_numNetAddrs >= ADDRSPERSITE)
510             break;
511
512         ifr = (struct ifreq *)cp;
513 #else
514     len = ifc.ifc_len / sizeof(struct ifreq);
515     if (len > ADDRSPERSITE)
516         len = ADDRSPERSITE;
517
518     for (i = 0; i < len; ++i) {
519         ifr = &ifs[i];
520         res = ioctl(s, SIOCGIFADDR, ifr);
521 #endif
522         if (res < 0) {
523             /* fputs(stderr, "ioctl error IFADDR\n");
524              * perror(ifr->ifr_name);   */
525             continue;
526         }
527         a = (struct sockaddr_in *)&ifr->ifr_addr;
528         if (a->sin_family != AF_INET)
529             continue;
530         rxi_NetAddrs[rxi_numNetAddrs] = ntohl(a->sin_addr.s_addr);
531         if (rx_IsLoopbackAddr(rxi_NetAddrs[rxi_numNetAddrs])) {
532             /* we don't really care about "localhost" */
533             continue;
534         }
535         for (j = 0; j < rxi_numNetAddrs; j++) {
536             if (rxi_NetAddrs[j] == rxi_NetAddrs[rxi_numNetAddrs])
537                 break;
538         }
539         if (j < rxi_numNetAddrs)
540             continue;
541
542         /* fprintf(stderr, "if %s addr=%x\n", ifr->ifr_name,
543          * rxi_NetAddrs[rxi_numNetAddrs]); */
544
545 #ifdef SIOCGIFFLAGS
546         res = ioctl(s, SIOCGIFFLAGS, ifr);
547         if (res == 0) {
548             myNetFlags[rxi_numNetAddrs] = ifr->ifr_flags;
549 #ifdef IFF_LOOPBACK
550             /* Handle aliased loopbacks as well. */
551             if (ifr->ifr_flags & IFF_LOOPBACK)
552                 continue;
553 #endif
554             /* fprintf(stderr, "if %s flags=%x\n",
555              * ifr->ifr_name, ifr->ifr_flags); */
556         } else {                /*
557                                  * fputs(stderr, "ioctl error IFFLAGS\n");
558                                  * perror(ifr->ifr_name); */
559         }
560 #endif /* SIOCGIFFLAGS */
561
562 #if !defined(AFS_AIX_ENV)  && !defined(AFS_LINUX20_ENV)
563         /* this won't run on an AIX system w/o a cache manager */
564         rxi_syscallp = rxi_syscall;
565 #endif
566
567         /* If I refer to kernel extensions that aren't loaded on AIX, the
568          * program refuses to load and run, so I simply can't include the
569          * following code.  Fortunately, AIX is the one operating system in
570          * which the subsequent ioctl works reliably. */
571         if (rxi_syscallp) {
572             if ((*rxi_syscallp) (20 /*AFSOP_GETMTU */ ,
573                                  htonl(rxi_NetAddrs[rxi_numNetAddrs]),
574                                  &(myNetMTUs[rxi_numNetAddrs]))) {
575                 /* fputs(stderr, "syscall error GETMTU\n");
576                  * perror(ifr->ifr_name); */
577                 myNetMTUs[rxi_numNetAddrs] = 0;
578             }
579             if ((*rxi_syscallp) (42 /*AFSOP_GETMASK */ ,
580                                  htonl(rxi_NetAddrs[rxi_numNetAddrs]),
581                                  &(myNetMasks[rxi_numNetAddrs]))) {
582                 /* fputs(stderr, "syscall error GETMASK\n");
583                  * perror(ifr->ifr_name); */
584                 myNetMasks[rxi_numNetAddrs] = 0;
585             } else
586                 myNetMasks[rxi_numNetAddrs] =
587                     ntohl(myNetMasks[rxi_numNetAddrs]);
588             /* fprintf(stderr, "if %s mask=0x%x\n",
589              * ifr->ifr_name, myNetMasks[rxi_numNetAddrs]); */
590         }
591
592         if (myNetMTUs[rxi_numNetAddrs] == 0) {
593             myNetMTUs[rxi_numNetAddrs] = OLD_MAX_PACKET_SIZE + RX_IPUDP_SIZE;
594 #ifdef SIOCGIFMTU
595             res = ioctl(s, SIOCGIFMTU, ifr);
596             if ((res == 0) && (ifr->ifr_metric > 128)) {        /* sanity check */
597                 myNetMTUs[rxi_numNetAddrs] = ifr->ifr_metric;
598                 /* fprintf(stderr, "if %s mtu=%d\n",
599                  * ifr->ifr_name, ifr->ifr_metric); */
600             } else {
601                 /* fputs(stderr, "ioctl error IFMTU\n");
602                  * perror(ifr->ifr_name); */
603             }
604 #endif
605         }
606
607         if (myNetMasks[rxi_numNetAddrs] == 0) {
608             myNetMasks[rxi_numNetAddrs] =
609                 fudge_netmask(rxi_NetAddrs[rxi_numNetAddrs]);
610 #ifdef SIOCGIFNETMASK
611             res = ioctl(s, SIOCGIFNETMASK, ifr);
612             if (res == 0) {
613                 a = (struct sockaddr_in *)&ifr->ifr_addr;
614                 myNetMasks[rxi_numNetAddrs] = ntohl(a->sin_addr.s_addr);
615                 /* fprintf(stderr, "if %s subnetmask=0x%x\n",
616                  * ifr->ifr_name, myNetMasks[rxi_numNetAddrs]); */
617             } else {
618                 /* fputs(stderr, "ioctl error IFMASK\n");
619                  * perror(ifr->ifr_name); */
620             }
621 #endif
622         }
623
624         if (!rx_IsLoopbackAddr(rxi_NetAddrs[rxi_numNetAddrs])) {        /* ignore lo0 */
625             int maxsize;
626             maxsize =
627                 rxi_nRecvFrags * (myNetMTUs[rxi_numNetAddrs] - RX_IP_SIZE);
628             maxsize -= UDP_HDR_SIZE;    /* only the first frag has a UDP hdr */
629             if (rx_maxReceiveSize < maxsize)
630                 rx_maxReceiveSize = MIN(RX_MAX_PACKET_SIZE, maxsize);
631             ++rxi_numNetAddrs;
632         }
633     }
634     UNLOCK_IF;
635     close(s);
636
637     /* have to allocate at least enough to allow a single packet to reach its
638      * maximum size, so ReadPacket will work.  Allocate enough for a couple
639      * of packets to do so, for good measure */
640     {
641         int npackets, ncbufs;
642
643         rx_maxJumboRecvSize =
644             RX_HEADER_SIZE + rxi_nDgramPackets * RX_JUMBOBUFFERSIZE +
645             (rxi_nDgramPackets - 1) * RX_JUMBOHEADERSIZE;
646         rx_maxJumboRecvSize = MAX(rx_maxJumboRecvSize, rx_maxReceiveSize);
647         ncbufs = (rx_maxJumboRecvSize - RX_FIRSTBUFFERSIZE);
648         if (ncbufs > 0) {
649             ncbufs = ncbufs / RX_CBUFFERSIZE;
650             npackets = rx_initSendWindow - 1;
651             rxi_MorePackets(npackets * (ncbufs + 1));
652         }
653     }
654 }
655 #endif /* AFS_NT40_ENV */
656
657 /* Called from rxi_FindPeer, when initializing a clear rx_peer structure,
658  * to get interesting information.
659  * Curiously enough, the rx_peerHashTable_lock currently protects the
660  * Inited variable (and hence rx_GetIFInfo). When the fs suite uses
661  * pthreads, this issue will need to be revisited.
662  */
663
664 void
665 rxi_InitPeerParams(struct rx_peer *pp)
666 {
667     afs_uint32 ppaddr;
668     u_short rxmtu;
669     int ix;
670 #if defined(ADAPT_PMTU) && defined(IP_MTU)
671     int sock;
672     struct sockaddr_in addr;
673 #endif
674
675     LOCK_IF_INIT;
676     if (!Inited) {
677         UNLOCK_IF_INIT;
678         /*
679          * there's a race here since more than one thread could call
680          * rx_GetIFInfo.  The race stops in rx_GetIFInfo.
681          */
682         rx_GetIFInfo();
683     } else {
684         UNLOCK_IF_INIT;
685     }
686
687 #ifdef ADAPT_MTU
688     /* try to second-guess IP, and identify which link is most likely to
689      * be used for traffic to/from this host. */
690     ppaddr = ntohl(pp->host);
691
692     pp->ifMTU = 0;
693     rx_rto_setPeerTimeoutSecs(pp, 2);
694     /* I don't initialize these, because I presume they are bzero'd...
695      * pp->burstSize pp->burst pp->burstWait.sec pp->burstWait.usec
696      */
697
698     LOCK_IF;
699     for (ix = 0; ix < rxi_numNetAddrs; ++ix) {
700         if ((rxi_NetAddrs[ix] & myNetMasks[ix]) == (ppaddr & myNetMasks[ix])) {
701 #ifdef IFF_POINTOPOINT
702             if (myNetFlags[ix] & IFF_POINTOPOINT)
703                 rx_rto_setPeerTimeoutSecs(pp, 4);
704 #endif /* IFF_POINTOPOINT */
705
706             rxmtu = myNetMTUs[ix] - RX_IPUDP_SIZE;
707             if (rxmtu < RX_MIN_PACKET_SIZE)
708                 rxmtu = RX_MIN_PACKET_SIZE;
709             if (pp->ifMTU < rxmtu)
710                 pp->ifMTU = MIN(rx_MyMaxSendSize, rxmtu);
711         }
712     }
713     UNLOCK_IF;
714     if (!pp->ifMTU) {           /* not local */
715         rx_rto_setPeerTimeoutSecs(pp, 3);
716         pp->ifMTU = MIN(rx_MyMaxSendSize, RX_REMOTE_PACKET_SIZE);
717     }
718 #else /* ADAPT_MTU */
719     rx_rto_setPeerTimeoutSecs(pp, 2);
720     pp->ifMTU = MIN(rx_MyMaxSendSize, OLD_MAX_PACKET_SIZE);
721 #endif /* ADAPT_MTU */
722 #if defined(ADAPT_PMTU) && defined(IP_MTU)
723     sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
724     if (sock != OSI_NULLSOCKET) {
725         addr.sin_family = AF_INET;
726         addr.sin_addr.s_addr = pp->host;
727         addr.sin_port = pp->port;
728         if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
729             int mtu=0;
730             socklen_t s = sizeof(mtu);
731             if (getsockopt(sock, SOL_IP, IP_MTU, &mtu, &s)== 0) {
732                 pp->ifMTU = MIN(mtu - RX_IPUDP_SIZE, pp->ifMTU);
733             }
734         }
735 #ifdef AFS_NT40_ENV
736         closesocket(sock);
737 #else
738         close(sock);
739 #endif
740     }
741 #endif
742     pp->ifMTU = rxi_AdjustIfMTU(pp->ifMTU);
743     pp->maxMTU = OLD_MAX_PACKET_SIZE;   /* for compatibility with old guys */
744     pp->natMTU = MIN((int)pp->ifMTU, OLD_MAX_PACKET_SIZE);
745     pp->maxDgramPackets =
746         MIN(rxi_nDgramPackets,
747             rxi_AdjustDgramPackets(rxi_nSendFrags, pp->ifMTU));
748     pp->ifDgramPackets =
749         MIN(rxi_nDgramPackets,
750             rxi_AdjustDgramPackets(rxi_nSendFrags, pp->ifMTU));
751     pp->maxDgramPackets = 1;
752     /* Initialize slow start parameters */
753     pp->MTU = MIN(pp->natMTU, pp->maxMTU);
754     pp->cwind = 1;
755     pp->nDgramPackets = 1;
756     pp->congestSeq = 0;
757 }
758
759 /* Don't expose jumobgram internals. */
760 void
761 rx_SetNoJumbo(void)
762 {
763     rx_maxReceiveSize = OLD_MAX_PACKET_SIZE;
764     rxi_nSendFrags = rxi_nRecvFrags = 1;
765 }
766
767 /* Override max MTU.  If rx_SetNoJumbo is called, it must be
768    called before calling rx_SetMaxMTU since SetNoJumbo clobbers rx_maxReceiveSize */
769 int
770 rx_SetMaxMTU(int mtu)
771 {
772     if (mtu < RX_MIN_PACKET_SIZE || mtu > RX_MAX_PACKET_DATA_SIZE)
773         return EINVAL;
774
775     rx_MyMaxSendSize = rx_maxReceiveSizeUser = rx_maxReceiveSize = mtu;
776
777     return 0;
778 }
779
780 #if defined(ADAPT_PMTU)
781 int
782 rxi_HandleSocketError(int socket)
783 {
784     int ret=0;
785 #if defined(HAVE_LINUX_ERRQUEUE_H)
786     struct msghdr msg;
787     struct cmsghdr *cmsg;
788     struct sock_extended_err *err;
789     struct sockaddr_in addr;
790     char controlmsgbuf[256];
791     int code;
792
793     msg.msg_name = &addr;
794     msg.msg_namelen = sizeof(addr);
795     msg.msg_iov = NULL;
796     msg.msg_iovlen = 0;
797     msg.msg_control = controlmsgbuf;
798     msg.msg_controllen = 256;
799     msg.msg_flags = 0;
800     code = recvmsg(socket, &msg, MSG_ERRQUEUE|MSG_DONTWAIT|MSG_TRUNC);
801
802     if (code < 0 || !(msg.msg_flags & MSG_ERRQUEUE))
803         goto out;
804
805     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
806        if ((char *)cmsg - controlmsgbuf > msg.msg_controllen - CMSG_SPACE(0) ||
807            (char *)cmsg - controlmsgbuf > msg.msg_controllen - CMSG_SPACE(cmsg->cmsg_len) ||
808            cmsg->cmsg_len == 0) {
809            cmsg = 0;
810            break;
811         }
812         if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
813             break;
814     }
815     if (!cmsg)
816         goto out;
817     ret=1;
818     err =(struct sock_extended_err *) CMSG_DATA(cmsg);
819
820     if (err->ee_errno == EMSGSIZE && err->ee_info >= 68) {
821         rxi_SetPeerMtu(NULL, addr.sin_addr.s_addr, addr.sin_port,
822                        err->ee_info - RX_IPUDP_SIZE);
823     }
824     /* other DEST_UNREACH's and TIME_EXCEEDED should be dealt with too */
825
826 out:
827 #endif
828     return ret;
829 }
830 #endif