root/vic/trunk/net-ip.cc @ 734

Revision 734, 11.0 KB (checked in by ucackha, 16 years ago)

Added registry code from RAT + tidying up. Kris

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*-
2 * Copyright (c) 1993-1994 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and the Network Research Group at
17 *      Lawrence Berkeley Laboratory.
18 * 4. Neither the name of the University nor of the Laboratory may be used
19 *    to endorse or promote products derived from this software without
20 *    specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34static const char rcsid[] =
35    "@(#) $Header$ (LBL)";
36
37#include <stdio.h>
38#include <errno.h>
39#include <string.h>
40#ifdef WIN32
41#include <io.h>
42#define close closesocket
43#else
44#include <sys/param.h>
45#include <sys/socket.h>
46#include <sys/ioctl.h>
47#endif
48#if defined(sun) && defined(__svr4__)
49#include <sys/systeminfo.h>
50#endif
51
52#include "config.h"
53#include "net.h"
54#include "Tcl.h"
55
56class IPNetwork : public Network {
57    public:
58        virtual int command(int argc, const char*const* argv);
59        virtual void reset();
60    protected:
61        int open(u_int32_t addr, int port, int ttl);
62        int close();
63        void localname(sockaddr_in*);
64        int openssock(u_int32_t addr, u_short port, int ttl);
65        int openrsock(u_int32_t addr, u_short port,
66                      const struct sockaddr_in& local);
67        time_t last_reset_;
68};
69
70static class IPNetworkMatcher : public Matcher {
71    public:
72        IPNetworkMatcher() : Matcher("network") {}
73        TclObject* match(const char* id) {
74                if (strcasecmp(id, "ip") == 0)
75                        return (new IPNetwork);
76                else
77                        return (0);
78        }
79} nm_ip;
80
81int IPNetwork::command(int argc, const char*const* argv)
82{
83        Tcl& tcl = Tcl::instance();
84        if (argc == 2) {
85                if (strcmp(argv[1], "close") == 0) {
86                        close();
87                        return (TCL_OK);
88                }
89                char* cp = tcl.result();
90                if (strcmp(argv[1], "addr") == 0) {
91                        strcpy(cp, intoa(addr_));
92                        return (TCL_OK);
93                }
94                if (strcmp(argv[1], "interface") == 0) {
95                        strcpy(cp, intoa(local_));
96                        return (TCL_OK);
97                }
98                if (strcmp(argv[1], "port") == 0) {
99                        sprintf(cp, "%d", ntohs(port_));
100                        return (TCL_OK);
101                }
102                if (strcmp(argv[1], "localport") == 0) {
103                        sprintf(cp, "%d", ntohs(lport_));
104                        return (TCL_OK);
105                }
106                if (strcmp(argv[1], "ttl") == 0) {
107                        sprintf(cp, "%d", ttl_);
108                        return (TCL_OK);
109                }
110                if (strcmp(argv[1], "ismulticast") == 0) {
111                        tcl.result(IN_CLASSD(ntohl(addr_))? "1" : "0");
112                        return (TCL_OK);
113                }
114        } else if (argc == 3) {
115                if (strcmp(argv[1], "loopback") == 0) {
116                        char c = atoi(argv[2]);
117                        if (setsockopt(ssock_, IPPROTO_IP, IP_MULTICAST_LOOP,
118                                       &c, 1) < 0) {
119                                /*
120                                 * If we cannot turn off loopback (Like on the
121                                 * Microsoft TCP/IP stack), then declare this
122                                 * option broken so that our packets can be
123                                 * filtered on the recv path.
124                                 */
125                                if (c == 0)
126                                        noloopback_broken_ = 1;
127                        }
128                        return (TCL_OK);
129                }
130        } else if (argc == 5) {
131                if (strcmp(argv[1], "open") == 0) {
132                        u_int32_t addr = LookupHostAddr(argv[2]);
133                        int port = htons(atoi(argv[3]));
134                        int ttl = atoi(argv[4]);
135                        if (open(addr, port, ttl) < 0)
136                                tcl.result("0");
137                        else
138                                tcl.result("1");
139                        return (TCL_OK);
140                }
141        }
142        return (Network::command(argc, argv));
143}
144
145int IPNetwork::open(u_int32_t addr, int port, int ttl)
146{
147        addr_ = addr;
148        port_ = port;
149        ttl_ = ttl;
150
151        ssock_ = openssock(addr, port, ttl);
152        if (ssock_ < 0)
153                return (-1);
154        /*
155         * Connecting the send socket also bound the local address.
156         * On a multihomed host we need to bind the receive socket
157         * to the same local address the kernel has chosen to send on.
158         */
159        sockaddr_in local;
160        localname(&local);
161        rsock_ = openrsock(addr, port, local);
162        if (rsock_ < 0) {
163                (void)::close(ssock_);
164                return (-1);
165        }
166        local_ = local.sin_addr.s_addr;
167#if defined(sun) && defined(__svr4__)
168        /*
169         * gethostname on solaris prior to 2.6 always returns 0 for
170         * udp sockets.  do a horrible kluge that often fails on
171         * multihomed hosts to get the local address (we don't use
172         * this to open the recv sock, only for the 'interface'
173         * tcl command).
174         */
175        if (local_ == 0) {
176                char myhostname[1024];
177                int error;
178
179                error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
180                if (error == -1) {
181                        perror("Getting my hostname");
182                        exit(-1);
183                }
184                local_ = LookupHostAddr(myhostname);
185        }
186#endif
187        lport_ = local.sin_port;
188        last_reset_ = 0;
189        return (0);
190}
191
192int IPNetwork::close()
193{
194        if (ssock_ >= 0) {
195                ::close(ssock_);
196                ::close(rsock_);
197                ssock_ = rsock_ = -1;
198        }
199        return (0);
200}
201
202void IPNetwork::localname(sockaddr_in* p)
203{
204        memset((char *)p, 0, sizeof(*p));
205        p->sin_family = AF_INET;
206        int len = sizeof(*p);
207        if (getsockname(ssock_, (struct sockaddr *)p, &len) < 0) {
208                perror("getsockname");
209                p->sin_addr.s_addr = 0;
210                p->sin_port = 0;
211        }
212#ifdef WIN32
213        if (p->sin_addr.s_addr == 0) {
214                char hostname[80];
215                struct hostent *hp;
216
217                if (gethostname(hostname, sizeof(hostname)) >= 0) {
218                        if ((hp = gethostbyname(hostname)) >= 0) {
219                                p->sin_addr.s_addr = ((struct in_addr *)hp->h_addr)->s_addr;
220                        }
221                }
222        }
223#endif 
224}
225
226void IPNetwork::reset()
227{
228        time_t t = time(0);
229        int d = int(t - last_reset_);
230        if (d > 3) {
231                last_reset_ = t;
232                (void)::close(ssock_);
233                ssock_ = openssock(addr_, port_, ttl_);
234        }
235}
236
237int IPNetwork::openrsock(u_int32_t addr, u_short port,
238                            const struct sockaddr_in& local)
239{
240        int fd;
241        struct sockaddr_in sin;
242
243        fd = socket(AF_INET, SOCK_DGRAM, 0);
244        if (fd < 0) {
245                perror("socket");
246                exit(1);
247        }
248        nonblock(fd);
249        int on = 1;
250        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) {
251                perror("SO_REUSEADDR");
252        }
253#ifdef SO_REUSEPORT
254        on = 1;
255        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof(on)) < 0) {
256                perror("SO_REUSEPORT");
257                exit(1);
258        }
259#endif
260        memset((char *)&sin, 0, sizeof(sin));
261        sin.sin_family = AF_INET;
262        sin.sin_port   = port;
263        if (IN_CLASSD(ntohl(addr))) {
264                /*
265                 * Try to bind the multicast address as the socket
266                 * dest address.  On many systems this won't work
267                 * so fall back to a destination of INADDR_ANY if
268                 * the first bind fails.
269                 */
270                sin.sin_addr.s_addr = addr;
271                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
272                        sin.sin_addr.s_addr = INADDR_ANY;
273                        if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
274                                perror("bind (INADDR_ANY)");
275                                abort();
276                        } else {
277                                /*printf("bound to INADDR_ANY\n");*/
278                        }
279                }
280                /*
281                 * XXX This is bogus multicast setup that really
282                 * shouldn't have to be done (group membership should be
283                 * implicit in the IP class D address, route should contain
284                 * ttl & no loopback flag, etc.).  Steve Deering has promised
285                 * to fix this for the 4.4bsd release.  We're all waiting
286                 * with bated breath.
287                 */
288                struct ip_mreq mr;
289
290                mr.imr_multiaddr.s_addr = addr;
291                mr.imr_interface.s_addr = INADDR_ANY;
292                if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mr, sizeof(mr)) < 0) {
293                        perror("IP_ADD_MEMBERSHIP");
294                        exit(1);
295                }
296        } else {
297                /*
298                 * bind the local host's address to this socket.  If that
299                 * fails, another vic probably has the addresses bound so
300                 * just exit.
301                 */
302                sin.sin_addr.s_addr = local.sin_addr.s_addr;
303                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
304                        perror("bind");
305                        exit(1);
306                }
307                /*
308                 * Despite several attempts on our part to get this fixed,
309                 * Microsoft Windows isn't complient with the Internet Host
310                 * Requirements standard (RFC-1122) and won't let us include
311                 * the source address in the receive socket demux state.
312                 * (The consequence of this is that all conversations have
313                 * to be assigned a unique local port so the vat 'side
314                 * conversation' (middle click on site name) function is
315                 * essentially useless under windows.)
316                 */
317#ifndef WIN32
318                /*
319                 * (try to) connect the foreign host's address to this socket.
320                 */
321                sin.sin_port = 0;
322                sin.sin_addr.s_addr = addr;
323                connect(fd, (struct sockaddr *)&sin, sizeof(sin));
324#endif
325        }
326        /*
327         * XXX don't need this for the session socket.
328         */     
329        int bufsize = 80 * 1024;
330        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) {
331                bufsize = 32 * 1024;
332                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) {
333                        perror("SO_RCVBUF");
334                }
335        }
336        return fd;
337}
338
339int IPNetwork::openssock(u_int32_t addr, u_short port, int ttl)
340{
341        int fd;
342        struct sockaddr_in sin;
343
344        fd = socket(AF_INET, SOCK_DGRAM, 0);
345        if (fd < 0) {
346                perror("socket");
347                exit(1);
348        }
349        nonblock(fd);
350
351#ifdef WIN32
352        memset((char *)&sin, 0, sizeof(sin));
353        sin.sin_family = AF_INET;
354        sin.sin_port = 0;
355        sin.sin_addr.s_addr = INADDR_ANY;
356        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
357                perror("bind");
358                exit(1);
359        }
360#endif
361        memset((char *)&sin, 0, sizeof(sin));
362        sin.sin_family = AF_INET;
363        sin.sin_port = port;
364        sin.sin_addr.s_addr = addr;
365        if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
366                perror("connect");
367                exit(1);
368        }
369        if (IN_CLASSD(ntohl(addr))) {
370#ifdef IP_ADD_MEMBERSHIP
371                char c;
372
373                /* turn off loopback */
374                c = 0;
375                if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &c, 1) < 0) {
376                        /*
377                         * If we cannot turn off loopback (Like on the
378                         * Microsoft TCP/IP stack), then declare this
379                         * option broken so that our packets can be
380                         * filtered on the recv path.
381                         */
382                        if (c == 0)
383                                noloopback_broken_ = 1;
384                }
385                /* set the multicast TTL */
386#ifdef WIN32
387                u_int t;
388#else
389                u_char t;
390#endif
391                t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl;
392                if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
393                               (char*)&t, sizeof(t)) < 0) {
394                        perror("IP_MULTICAST_TTL");
395                        exit(1);
396                }
397#else
398                fprintf(stderr, "\
399not compiled with support for IP multicast\n\
400you must specify a unicast destination\n");
401                exit(1);
402#endif
403        }
404        /*
405         * XXX don't need this for the session socket.
406         */
407        int bufsize = 80 * 1024;
408        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
409                       sizeof(bufsize)) < 0) {
410                bufsize = 48 * 1024;
411                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
412                               sizeof(bufsize)) < 0)
413                        perror("SO_SNDBUF");
414        }
415        return (fd);
416}
Note: See TracBrowser for help on using the browser.