root/vic/trunk/net/net-ipv6.cpp @ 1020

Revision 1020, 15.2 KB (checked in by piers, 14 years ago)

orrected IPPROTO_IP used in net-ipv6 loopback command to use IPPROTO_IPV6

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * net-ipv6.cc -- IPv6 Network  (based upon ip-net.cc)
3 */
4
5/*-
6 * Copyright (c) 1993-1994 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by the University of
20 *      California, Berkeley and the Network Research Group at
21 *      Lawrence Berkeley Laboratory.
22 * 4. Neither the name of the University nor of the Laboratory may be used
23 *    to endorse or promote products derived from this software without
24 *    specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39static const char rcsid[] =
40    "@(#) $Header$";
41
42#ifdef HAVE_IPV6
43
44#include <stdio.h>
45#include <errno.h>
46#include <string.h>
47#ifdef WIN32
48#include <io.h>
49#define close closesocket
50#else
51#include <sys/param.h>
52#include <sys/socket.h>
53#include <sys/ioctl.h>
54#endif
55#if defined(sun) && defined(__svr4__)
56#include <sys/systeminfo.h>
57#endif
58
59#include "config.h"
60#include "net.h"
61#include "vic_tcl.h"
62
63#include "inet6.h"
64#include "net-addr.h"
65
66/* This is pretty nasty but it's the simplest way to get round */
67/* the Detexis bug that means their MUSICA IPv6 stack uses     */
68/* IPPROTO_IP instead of IPPROTO_IPV6 in setsockopt calls      */
69/* We also need to define in6addr_any */
70#ifdef  MUSICA_IPV6
71#define IPPROTO_IPV6    IPPROTO_IP
72struct  in6_addr                in6addr_any = {IN6ADDR_ANY_INIT};
73#endif
74
75#if defined(FreeBSD)
76#ifndef IPV6_ADD_MEMBERSHIP
77#define IPV6_ADD_MEMBERSHIP     IPV6_JOIN_GROUP
78#endif
79#endif
80
81#ifndef INET6_ADDRSTRLEN
82#define INET6_ADDRSTRLEN (46)
83#endif
84
85class IP6Address : public Address {
86  public:
87        IP6Address() { text_ = new char[INET6_ADDRSTRLEN]; text_[0]='\0';};
88        virtual int operator=(const char*);
89        int operator=(const struct in6_addr& addr);
90
91        virtual Address* copy() const;
92        virtual size_t length() const { return sizeof(addr_);}
93        virtual operator const void*() const { return &addr_;}
94
95        // conversion from IP6Address to type struct in6_addr
96        operator struct in6_addr() const { return addr_; }
97        operator const struct in6_addr&() const { return addr_; }
98
99  protected:
100        struct in6_addr addr_;
101};
102
103
104static class IP6AddressType : public AddressType {
105public:
106  virtual Address* resolve(const char* name) {
107  struct in6_addr addr;
108    IP6Address * result = 0;
109    if (inet6_LookupHostAddr(&addr, name) >= 0) {
110      result = new IP6Address;
111      *result = addr;
112    } else {
113      result = 0;
114    }
115    return (result);
116  }
117} ip6_address_type;
118
119
120class IP6Network : public Network {
121  public:
122          IP6Network() : Network(*(new IP6Address), *(new IP6Address))  {;}
123        virtual int command(int argc, const char*const* argv);
124        virtual void reset();
125        virtual Address* alloc(const char* name) {
126                struct in6_addr addr;
127                IP6Address * result = 0;
128                if (inet6_LookupHostAddr(&addr, name) >= 0) {
129                  result = new IP6Address;
130                  *result = addr;
131                } else {
132                  result = 0;
133                }
134                return (result);
135        }
136  protected:
137        virtual int dorecv(u_char* buf, int len, Address& from, int fd);
138        int open(const char * host, int port, int ttl);
139        int close();
140        int localname(sockaddr_in6*);
141        int openssock(Address & addr, u_short port, int ttl);
142        int openrsock(Address & addr, u_short port, Address & local);
143        time_t last_reset_;
144        unsigned int flowLabel_;        /* Flowlabel for all traffic */
145        int ifIndex_;           /* Interface index to bind to on all layers */
146};
147
148static class IP6NetworkMatcher : public Matcher {
149    public:
150        IP6NetworkMatcher() : Matcher("network") {}
151        TclObject* match(const char* id) {
152                if (strcasecmp(id, "ip6") == 0)
153                        return (new IP6Network);
154                else
155                        return (0);
156        }
157} nm_ip6;
158
159int IP6Address::operator=(const char* text)  {
160  if (!inet6_LookupHostAddr(&addr_, text))
161        return ((inet_ntop(AF_INET6, &addr_, text_, INET6_ADDRSTRLEN) != 0) ?
162          (0) : (1));
163  else {
164        fprintf(stderr,"Error looking up: %s\n",text);
165        exit(1);
166  }
167}
168
169Address * IP6Address::copy() const {
170  IP6Address * result = new IP6Address;
171  *result = addr_;
172  return (result);
173}
174
175int IP6Address::operator=(const struct in6_addr& addr) {
176  memcpy(&addr_, &addr, sizeof(addr));
177  return ((inet_ntop(AF_INET6, &addr_, text_, INET6_ADDRSTRLEN) != 0) ?
178          (0) : (1));
179}
180
181
182int IP6Network::command(int argc, const char*const* argv)
183{
184        Tcl& tcl = Tcl::instance();
185        if (argc == 2) {
186                if (strcmp(argv[1], "close") == 0) {
187                        close();
188                        return (TCL_OK);
189                }
190                char* cp = tcl.result();
191                if (strcmp(argv[1], "addr") == 0) {
192/* __IPV6 use Address */
193                        strcpy(cp, addr_);
194                        return (TCL_OK);
195                }
196                if (strcmp(argv[1], "interface") == 0) {
197/* __IPV6 use Address */
198                        strcpy(cp, local_);
199                        return (TCL_OK);
200                }
201                if (strcmp(argv[1], "port") == 0) {
202                        sprintf(cp, "%d", ntohs(port_));
203                        return (TCL_OK);
204                }
205                if (strcmp(argv[1], "localport") == 0) {
206                        sprintf(cp, "%d", ntohs(lport_));
207                        return (TCL_OK);
208                }
209                if (strcmp(argv[1], "ttl") == 0) {
210                        sprintf(cp, "%d", ttl_);
211                        return (TCL_OK);
212                }
213/* __IPV6 use IN6_IS_ADDR */
214                if (strcmp(argv[1], "ismulticast") == 0) {
215                        const in6_addr & addr = (IP6Address&)addr_;
216                        tcl.result(IN6_IS_ADDR_MULTICAST(&addr)? "1" : "0");
217                        return (TCL_OK);
218/* __IPV6 user IPV6_MULTICAST_LOOP */
219                }
220        } else if (argc == 3) {
221                if (strcmp(argv[1], "loopback") == 0) {
222                        char c = atoi(argv[2]);
223                        if (setsockopt(ssock_, IPPROTO_IPV6,
224                                       IPV6_MULTICAST_LOOP, &c, 1) < 0) {
225                                /*
226                                 * If we cannot turn off loopback (Like on the
227                                 * Microsoft TCP/IP stack), then declare this
228                                 * option broken so that our packets can be
229                                 * filtered on the recv path.
230                                 */
231                                if (c == 0)
232                                        noloopback_broken_ = 1;
233                        }
234                        return (TCL_OK);
235                }
236        } else if (argc == 5) {
237                if (strcmp(argv[1], "open") == 0) {
238/* __IPV6 use v6 lookup */
239                        const char * host = argv[2];
240                        int port = htons(atoi(argv[3]));
241                        int ttl = atoi(argv[4]);
242                        flowLabel_ = htonl(atoi(tcl.attr("flowLabel")));
243                        ifIndex_ = atoi(tcl.attr("ifIndex"));
244                        if (strlen(tcl.attr("ifAddr"))>1)
245                                (IP6Address&)local_ = tcl.attr("ifAddr");
246                        if (open(host, port, ttl) < 0)
247                                tcl.result("0");
248                        else
249                                tcl.result("1");
250                        return (TCL_OK);
251                }
252        }
253        return (Network::command(argc, argv));
254}
255
256int IP6Network::open(const char * host, int port, int ttl)
257{
258        addr_ = host;
259        port_ = port;
260        ttl_ = ttl;
261
262        ssock_ = openssock(addr_, port, ttl);
263        if (ssock_ < 0)
264                return (-1);
265        /*
266         * Connecting the send socket also bound the local address.
267         * On a multihomed host we need to bind the receive socket
268         * to the same local address the kernel has chosen to send on.
269         */
270        struct sockaddr_in6 local;
271        if (localname(&local) < 0) {
272                return (-1);
273        }
274        (IP6Address&)local_ = local.sin6_addr;
275
276        rsock_ = openrsock(addr_, port, local_);
277        if (rsock_ < 0) {
278                (void)::close(ssock_);
279                return (-1);
280        }
281        lport_ = local.sin6_port;
282        last_reset_ = 0;
283        return (0);
284}
285
286int IP6Network::close()
287{
288        if (ssock_ >= 0) {
289                ::close(ssock_);
290                ::close(rsock_);
291                ssock_ = rsock_ = -1;
292        }
293        return (0);
294}
295
296int IP6Network::localname(sockaddr_in6* p) {
297
298  memset((char *)p, 0, sizeof(*p));
299  p->sin6_family = AF_INET6;
300  int len = sizeof(*p), result = 0;
301
302  if ((result = getsockname(ssock_, (struct sockaddr *)p, &len)) < 0) {
303    perror("getsockname");
304    p->sin6_addr = in6addr_any;
305    p->sin6_port = 0;
306  }
307  /* This doesn't yield anything useful
308  len = sizeof(p->sin6_addr);
309  if (!getsockopt(ssock_, IPPROTO_IPV6, IP_MULTICAST_IF, (char *)&p->sin6_addr,&len))
310                        return (0);
311  */
312
313        // Use Local name if already set via command line
314        // But use port derived from getsockname
315        if (((const char*)local_)[0]!='\0') {
316                p->sin6_addr=(IP6Address&)local_;
317                return (result);
318        }
319
320#ifdef MUSICA_IPV6
321  if (IS_SAME_IN6_ADDR(p->sin6_addr, in6addr_any)) {
322#else 
323  if (IN6_ARE_ADDR_EQUAL(&(p->sin6_addr), &in6addr_any)) {
324#endif
325    result = inet6_LookupLocalAddr(&p->sin6_addr);
326  }
327
328  return (result);
329
330}
331
332void IP6Network::reset()
333{
334        time_t t = time(0);
335        int d = int(t - last_reset_);
336        if (d > 3) {
337                last_reset_ = t;
338                (void)::close(ssock_);
339                ssock_ = openssock(addr_, port_, ttl_);
340        }
341}
342
343int IP6Network::openrsock(Address & addr, u_short port, Address & local)
344{
345        int fd;
346        struct sockaddr_in6 sin;
347
348        fd = socket(AF_INET6, SOCK_DGRAM, 0);
349        if (fd < 0) {
350                perror("socket");
351                exit(1);
352        }
353        nonblock(fd);
354        int on = 1;
355        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
356                        sizeof(on)) < 0) {
357                perror("SO_REUSEADDR");
358        }
359#ifdef SO_REUSEPORT
360        on = 1;
361        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on,
362                       sizeof(on)) < 0) {
363                perror("SO_REUSEPORT");
364                exit(1);
365        }
366#endif
367        memset((char *)&sin, 0, sizeof(sin));
368        sin.sin6_family = AF_INET6;
369        sin.sin6_addr = (IP6Address&)addr;
370        sin.sin6_port = port;
371#ifdef IPV6_ADD_MEMBERSHIP
372        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
373                /*
374                 * Try to bind the multicast address as the socket
375                 * dest address.  On many systems this won't work
376                 * so fall back to a destination of INADDR_ANY if
377                 * the first bind fails.
378                 */
379/* __IPV6 memcopy address */
380                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
381                        sin.sin6_addr = in6addr_any;
382                        if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
383                                perror("bind");
384                                exit(1);
385                        }
386                }
387                /*
388                 * XXX This is bogus multicast setup that really
389                 * shouldn't have to be done (group membership should be
390                 * implicit in the IP class D address, route should contain
391                 * ttl & no loopback flag, etc.).  Steve Deering has promised
392                 * to fix this for the 4.4bsd release.  We're all waiting
393                 * with bated breath.
394                 */
395                struct ipv6_mreq mr;
396
397/* __IPV6 memcopy address */
398#ifdef MUSICA_IPV6
399                mr.i6mr_interface = (ifIndex_<0)?0:ifIndex_;
400                mr.i6mr_multiaddr = (IP6Address&)addr;
401#else
402                mr.ipv6mr_interface = (ifIndex_<0)?0:ifIndex_;
403                mr.ipv6mr_multiaddr = (IP6Address&)addr;
404#endif
405
406                if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
407                               (char *)&mr, sizeof(mr)) < 0) {
408                        perror("IPV6_ADD_MEMBERSHIP");
409                        exit(1);
410                }
411        } else
412#endif
413        {
414                /*
415                 * bind the local host's address to this socket.  If that
416                 * fails, another vic probably has the addresses bound so
417                 * just exit.
418                 */
419/* __IPV6 memcopy address */
420                sin.sin6_addr = (IP6Address&)local;
421                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
422                        perror("bind");
423                        exit(1);
424                }
425                /*
426                 * Despite several attempts on our part to get this fixed,
427                 * Microsoft Windows isn't complient with the Internet Host
428                 * Requirements standard (RFC-1122) and won't let us include
429                 * the source address in the receive socket demux state.
430                 * (The consequence of this is that all conversations have
431                 * to be assigned a unique local port so the vat 'side
432                 * conversation' (middle click on site name) function is
433                 * essentially useless under windows.)
434                 */
435#ifndef WIN32
436                /*
437                 * (try to) connect the foreign host's address to this socket.
438                 */
439                sin.sin6_port = 0;
440/* __IPV6 memcopy address */
441                sin.sin6_addr = (IP6Address&)addr;
442                connect(fd, (struct sockaddr *)&sin, sizeof(sin));
443#endif
444        }
445
446        /*
447         * XXX don't need this for the session socket.
448         */     
449        int bufsize = 80 * 1024;
450        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
451                        sizeof(bufsize)) < 0) {
452                bufsize = 32 * 1024;
453                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
454                                sizeof(bufsize)) < 0)
455                        perror("SO_RCVBUF");
456        }
457        return (fd);
458}       
459
460int IP6Network::openssock(Address & addr, u_short port, int ttl)
461{
462        int fd;
463        struct sockaddr_in6 sin;
464
465
466        fd = socket(AF_INET6, SOCK_DGRAM, 0);
467        if (fd < 0) {
468                perror("socket");
469                exit(1);
470        }
471        nonblock(fd);
472
473        memset((char *)&sin, 0, sizeof(sin));
474        sin.sin6_family = AF_INET6;
475        sin.sin6_port = 0;
476        sin.sin6_flowinfo = flowLabel_;
477/* __IPV6 memcopy address */
478        sin.sin6_addr = in6addr_any;
479        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
480                perror("bind");
481                exit(1);
482        }
483
484        memset((char *)&sin, 0, sizeof(sin));
485        sin.sin6_family = AF_INET6;
486/* __IPV6 memcopy address */
487        sin.sin6_addr = (IP6Address&)addr;
488        sin.sin6_port = port;
489        sin.sin6_flowinfo = flowLabel_;
490        if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
491                perror("connect");
492                exit(1);
493        }
494        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
495#ifdef IPV6_ADD_MEMBERSHIP
496                char c;
497
498                /* turn off loopback */
499                c = 0;
500                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &c, 1) < 0) {
501                        /*
502                         * If we cannot turn off loopback (Like on the
503                         * Microsoft TCP/IP stack), then declare this
504                         * option broken so that our packets can be
505                         * filtered on the recv path.
506                         */
507                        if (c == 0)
508                                noloopback_broken_ = 1;
509                }
510                /* set the multicast TTL */
511                u_int t;
512                t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl;
513
514                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
515                               (const char*)&t, sizeof(t)) < 0) {
516                        perror("IPV6_MULTICAST_HOPS");
517                        exit(1);
518                }
519                if (ifIndex_!=-1) {
520                        if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
521                                       (const char*)&ifIndex_, sizeof(ifIndex_)) < 0) {
522                                perror("IPV6_MULTICAST_IF");
523                                exit(1);
524                        }
525                }
526#else
527                fprintf(stderr, "\
528not compiled with support for IP multicast\n\
529you must specify a unicast destination\n");
530                exit(1);
531#endif
532        }
533        /*
534         * XXX don't need this for the session socket.
535         */
536        int bufsize = 80 * 1024;
537        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
538                       sizeof(bufsize)) < 0) {
539                bufsize = 48 * 1024;
540                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
541                               sizeof(bufsize)) < 0)
542                        perror("SO_SNDBUF");
543        }
544        return (fd);
545}
546
547int IP6Network::dorecv(u_char* buf, int len, Address& from, int fd)
548{
549        sockaddr_in6 sfrom;
550        int fromlen = sizeof(sfrom);
551        int cc = ::recvfrom(fd, (char*)buf, len, 0,
552                            (sockaddr*)&sfrom, &fromlen);
553        if (cc < 0) {
554                if (errno != EWOULDBLOCK)
555                        perror("recvfrom");
556                return (-1);
557        }
558        (IP6Address&)from = sfrom.sin6_addr;
559
560        /* Check for loopback - then compare last auto conf bit of addresses
561         * XXX Probaby should do this at rtp level.
562         */
563        if (noloopback_broken_ && sfrom.sin6_port == lport_) {
564                struct in6_addr local;
565                local=(IP6Address&)local_;
566                if (((sfrom.sin6_addr.s6_addr[12] == local.s6_addr[12]) &&
567                         (sfrom.sin6_addr.s6_addr[13] == local.s6_addr[13]) &&
568                         (sfrom.sin6_addr.s6_addr[14] == local.s6_addr[14]) &&
569                         (sfrom.sin6_addr.s6_addr[15] == local.s6_addr[15])   ) ||
570                         from == local_) {
571                                return (0);
572                }
573        }
574        return (cc);
575}
576
577#endif /* HAVE_IPV6 */
Note: See TracBrowser for help on using the browser.