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

Revision 3746, 15.4 KB (checked in by socrates, 8 years ago)

Commited a number of fixes mainly by peeking into Debian, FreeBSD,
and Gentoo ports.

The set of fixes can be identified by the marker "//SV-XXX: FreeBSD",
or "//SV-XXX: Debian" (will be removed in the next major release).

The set of fixes is part of the "sumover-1Qb-release" and these fixes
are described in detail on the wiki:

 https://frostie.cs.ucl.ac.uk/nets/mmedia/milestone/sumover-1Qb-release

and related tickets:

37, 41, 42, 43, 44, 45, 46

Summary of main fixes:

  • fixed rat man page problem for Debian
  • using intptr_t casts for 64-bit architectures in vic
  • added missing #includes, and removed redundant ones
  • added some missing function prototypes
  • better fix for GETTIMEOFDAY_TO_NTP_OFFSET
  • using delete[] for some objects allocated with "new"
  • using various casts, including (sig_t) for unix signals
  • now using intmax_t cast for time_t struct
  • use srandomdev() instead for srandom() in FreeBSD
  • use openssl's md5 instead of vic's own, link to -lcrypto
  • 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//#include "inet_ntop.h" //SV-XXX: FreeBSD
63
64#include "inet6.h"
65#include "net-addr.h"
66
67/* This is pretty nasty but it's the simplest way to get round */
68/* the Detexis bug that means their MUSICA IPv6 stack uses     */
69/* IPPROTO_IP instead of IPPROTO_IPV6 in setsockopt calls      */
70/* We also need to define in6addr_any */
71#ifdef  MUSICA_IPV6
72#define IPPROTO_IPV6    IPPROTO_IP
73struct  in6_addr                in6addr_any = {IN6ADDR_ANY_INIT};
74#endif
75
76#if defined(FreeBSD)
77#ifndef IPV6_ADD_MEMBERSHIP
78#define IPV6_ADD_MEMBERSHIP     IPV6_JOIN_GROUP
79#endif
80#endif
81
82#ifndef INET6_ADDRSTRLEN
83#define INET6_ADDRSTRLEN (46)
84#endif
85
86class IP6Address : public Address {
87  public:
88        IP6Address() { text_ = new char[INET6_ADDRSTRLEN]; text_[0]='\0';};
89        virtual int operator=(const char*);
90        int operator=(const struct in6_addr& addr);
91
92        virtual Address* copy() const;
93        virtual size_t length() const { return sizeof(addr_);}
94        virtual operator const void*() const { return &addr_;}
95
96        // conversion from IP6Address to type struct in6_addr
97        operator struct in6_addr() const { return addr_; }
98        operator const struct in6_addr&() { return addr_; }
99
100  protected:
101        struct in6_addr addr_;
102};
103
104
105static class IP6AddressType : public AddressType {
106public:
107  virtual Address* resolve(const char* name) {
108  struct in6_addr addr;
109    IP6Address * result = 0;
110    if (inet6_LookupHostAddr(&addr, name) >= 0) {
111      result = new IP6Address;
112      *result = addr;
113    } else {
114      result = 0;
115    }
116    return (result);
117  }
118} ip6_address_type;
119
120
121class IP6Network : public Network {
122  public:
123          IP6Network() : Network(*(new IP6Address), *(new IP6Address))  {;}
124        virtual int command(int argc, const char*const* argv);
125        virtual void reset();
126        virtual Address* alloc(const char* name) {
127                struct in6_addr addr;
128                IP6Address * result = 0;
129                if (inet6_LookupHostAddr(&addr, name) >= 0) {
130                  result = new IP6Address;
131                  *result = addr;
132                } else {
133                  result = 0;
134                }
135                return (result);
136        }
137  protected:
138        virtual int dorecv(u_char* buf, int len, Address& from, int fd);
139        int open(const char * host, int port, int ttl);
140        int close();
141        int localname(sockaddr_in6*);
142        int openssock(Address & addr, u_short port, int ttl);
143        int openrsock(Address & addr, u_short port, Address & local);
144        time_t last_reset_;
145        unsigned int flowLabel_;        /* Flowlabel for all traffic */
146        int ifIndex_;           /* Interface index to bind to on all layers */
147};
148
149static class IP6NetworkMatcher : public Matcher {
150    public:
151        IP6NetworkMatcher() : Matcher("network") {}
152        TclObject* match(const char* id) {
153                if (strcasecmp(id, "ip6") == 0)
154                        return (new IP6Network);
155                else
156                        return (0);
157        }
158} nm_ip6;
159
160int IP6Address::operator=(const char* text)  {
161  if (!inet6_LookupHostAddr(&addr_, text))
162        return ((inet_ntop(AF_INET6, &addr_, text_, INET6_ADDRSTRLEN) != 0) ?
163          (0) : (1));
164  else {
165        fprintf(stderr,"Error looking up: %s\n",text);
166        exit(1);
167  }
168}
169
170Address * IP6Address::copy() const {
171  IP6Address * result = new IP6Address;
172  *result = addr_;
173  return (result);
174}
175
176int IP6Address::operator=(const struct in6_addr& addr) {
177  memcpy(&addr_, &addr, sizeof(addr));
178  return ((inet_ntop(AF_INET6, &addr_, text_, INET6_ADDRSTRLEN) != 0) ?
179          (0) : (1));
180}
181
182
183int IP6Network::command(int argc, const char*const* argv)
184{
185        Tcl& tcl = Tcl::instance();
186        if (argc == 2) {
187                if (strcmp(argv[1], "close") == 0) {
188                        close();
189                        return (TCL_OK);
190                }
191                char* cp = tcl.result();
192                if (strcmp(argv[1], "addr") == 0) {
193/* __IPV6 use Address */
194                        strcpy(cp, addr_);
195                        return (TCL_OK);
196                }
197                if (strcmp(argv[1], "interface") == 0) {
198/* __IPV6 use Address */
199                        strcpy(cp, local_);
200                        return (TCL_OK);
201                }
202                if (strcmp(argv[1], "port") == 0) {
203                        sprintf(cp, "%d", ntohs(port_));
204                        return (TCL_OK);
205                }
206                if (strcmp(argv[1], "localport") == 0) {
207                        sprintf(cp, "%d", ntohs(lport_));
208                        return (TCL_OK);
209                }
210                if (strcmp(argv[1], "ttl") == 0) {
211                        sprintf(cp, "%d", ttl_);
212                        return (TCL_OK);
213                }
214/* __IPV6 use IN6_IS_ADDR */
215                if (strcmp(argv[1], "ismulticast") == 0) {
216                        const in6_addr & addr = (IP6Address&)addr_;
217                        tcl.result(IN6_IS_ADDR_MULTICAST(&addr)? "1" : "0");
218                        return (TCL_OK);
219/* __IPV6 user IPV6_MULTICAST_LOOP */
220                }
221        } else if (argc == 3) {
222                if (strcmp(argv[1], "loopback") == 0) {
223                        char c = atoi(argv[2]);
224                        if (setsockopt(ssock_, IPPROTO_IPV6,
225                                       IPV6_MULTICAST_LOOP, &c, 1) < 0) {
226                                /*
227                                 * If we cannot turn off loopback (Like on the
228                                 * Microsoft TCP/IP stack), then declare this
229                                 * option broken so that our packets can be
230                                 * filtered on the recv path.
231                                 */
232                                if (c == 0)
233                                        noloopback_broken_ = 1;
234                        }
235                        return (TCL_OK);
236                }
237        } else if (argc == 5) {
238                if (strcmp(argv[1], "open") == 0) {
239/* __IPV6 use v6 lookup */
240                        const char * host = argv[2];
241                        int port = htons(atoi(argv[3]));
242                        int ttl = atoi(argv[4]);
243                        flowLabel_ = htonl(atoi(tcl.attr("flowLabel")));
244                        ifIndex_ = atoi(tcl.attr("ifIndex"));
245                        if (strlen(tcl.attr("ifAddr"))>1)
246                                (IP6Address&)local_ = tcl.attr("ifAddr");
247                        if (open(host, port, ttl) < 0)
248                                tcl.result("0");
249                        else
250                                tcl.result("1");
251                        return (TCL_OK);
252                }
253        }
254        return (Network::command(argc, argv));
255}
256
257int IP6Network::open(const char * host, int port, int ttl)
258{
259        addr_ = host;
260        port_ = port;
261        ttl_ = ttl;
262
263        ssock_ = openssock(addr_, port, ttl);
264        if (ssock_ < 0)
265                return (-1);
266        /*
267         * Connecting the send socket also bound the local address.
268         * On a multihomed host we need to bind the receive socket
269         * to the same local address the kernel has chosen to send on.
270         */
271        struct sockaddr_in6 local;
272        if (localname(&local) < 0) {
273                return (-1);
274        }
275        (IP6Address&)local_ = local.sin6_addr;
276
277        rsock_ = openrsock(addr_, port, local_);
278        if (rsock_ < 0) {
279                (void)::close(ssock_);
280                return (-1);
281        }
282        lport_ = local.sin6_port;
283        last_reset_ = 0;
284        return (0);
285}
286
287int IP6Network::close()
288{
289        if (ssock_ >= 0) {
290                ::close(ssock_);
291                ::close(rsock_);
292                ssock_ = rsock_ = -1;
293        }
294        return (0);
295}
296
297int IP6Network::localname(sockaddr_in6* p) {
298
299  memset((char *)p, 0, sizeof(*p));
300  p->sin6_family = AF_INET6;
301  socklen_t len = sizeof(*p), result = 0;
302
303  if ((result = getsockname(ssock_, (struct sockaddr *)p, &len)) < 0) {
304    perror("getsockname");
305    p->sin6_addr = in6addr_any;
306    p->sin6_port = 0;
307  }
308  /* This doesn't yield anything useful
309  len = sizeof(p->sin6_addr);
310  if (!getsockopt(ssock_, IPPROTO_IPV6, IP_MULTICAST_IF, (char *)&p->sin6_addr,&len))
311                        return (0);
312  */
313
314        // Use Local name if already set via command line
315        // But use port derived from getsockname
316        if (((const char*)local_)[0]!='\0') {
317                p->sin6_addr=(IP6Address&)local_;
318                return (result);
319        }
320
321#ifdef MUSICA_IPV6
322  if (IS_SAME_IN6_ADDR(p->sin6_addr, in6addr_any)) {
323#else 
324  if (IN6_ARE_ADDR_EQUAL(&(p->sin6_addr), &in6addr_any)) {
325#endif
326    result = inet6_LookupLocalAddr(&p->sin6_addr);
327  }
328
329  return (result);
330
331}
332
333void IP6Network::reset()
334{
335        time_t t = time(0);
336        int d = int(t - last_reset_);
337        if (d > 3) {
338                last_reset_ = t;
339                (void)::close(ssock_);
340                ssock_ = openssock(addr_, port_, ttl_);
341        }
342}
343
344int IP6Network::openrsock(Address & addr, u_short port, Address & local)
345{
346        int fd;
347        struct sockaddr_in6 sin;
348
349        fd = socket(AF_INET6, SOCK_DGRAM, 0);
350        if (fd < 0) {
351                perror("socket");
352                exit(1);
353        }
354        nonblock(fd);
355        int on = 1;
356        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
357                        sizeof(on)) < 0) {
358                perror("SO_REUSEADDR");
359        }
360#ifdef SO_REUSEPORT
361        on = 1;
362        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on,
363                       sizeof(on)) < 0) {
364                perror("SO_REUSEPORT");
365                exit(1);
366        }
367#endif
368        memset((char *)&sin, 0, sizeof(sin));
369        sin.sin6_family = AF_INET6;
370        sin.sin6_addr = (IP6Address&)addr;
371        sin.sin6_port = port;
372#ifdef IPV6_ADD_MEMBERSHIP
373        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
374                /*
375                 * Try to bind the multicast address as the socket
376                 * dest address.  On many systems this won't work
377                 * so fall back to a destination of INADDR_ANY if
378                 * the first bind fails.
379                 */
380/* __IPV6 memcopy address */
381                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
382                        sin.sin6_addr = in6addr_any;
383                        if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
384                                perror("bind");
385                                exit(1);
386                        }
387                }
388                /*
389                 * XXX This is bogus multicast setup that really
390                 * shouldn't have to be done (group membership should be
391                 * implicit in the IP class D address, route should contain
392                 * ttl & no loopback flag, etc.).  Steve Deering has promised
393                 * to fix this for the 4.4bsd release.  We're all waiting
394                 * with bated breath.
395                 */
396                struct ipv6_mreq mr;
397
398/* __IPV6 memcopy address */
399#ifdef MUSICA_IPV6
400                mr.i6mr_interface = (ifIndex_<0)?0:ifIndex_;
401                mr.i6mr_multiaddr = (IP6Address&)addr;
402#else
403                mr.ipv6mr_interface = (ifIndex_<0)?0:ifIndex_;
404                mr.ipv6mr_multiaddr = (IP6Address&)addr;
405#endif
406
407                int mreqsize = sizeof(mr);
408#ifdef WIN2K_IPV6
409                mreqsize += 4;
410#endif
411                if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
412                               (char *)&mr, mreqsize) < 0) {
413                        perror("IPV6_ADD_MEMBERSHIP");
414                        exit(1);
415                }
416        } else
417#endif
418        {
419                /*
420                 * bind the local host's address to this socket.  If that
421                 * fails, another vic probably has the addresses bound so
422                 * just exit.
423                 */
424/* __IPV6 memcopy address */
425                sin.sin6_addr = (IP6Address&)local;
426                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
427                        perror("bind");
428                        exit(1);
429                }
430                /*
431                 * Despite several attempts on our part to get this fixed,
432                 * Microsoft Windows isn't complient with the Internet Host
433                 * Requirements standard (RFC-1122) and won't let us include
434                 * the source address in the receive socket demux state.
435                 * (The consequence of this is that all conversations have
436                 * to be assigned a unique local port so the vat 'side
437                 * conversation' (middle click on site name) function is
438                 * essentially useless under windows.)
439                 */
440#ifndef WIN32
441                /*
442                 * (try to) connect the foreign host's address to this socket.
443                 */
444                sin.sin6_port = 0;
445/* __IPV6 memcopy address */
446                sin.sin6_addr = (IP6Address&)addr;
447                connect(fd, (struct sockaddr *)&sin, sizeof(sin));
448#endif
449        }
450
451        /*
452         * XXX don't need this for the session socket.
453         */     
454        int bufsize = 80 * 1024;
455        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
456                        sizeof(bufsize)) < 0) {
457                bufsize = 32 * 1024;
458                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
459                                sizeof(bufsize)) < 0)
460                        perror("SO_RCVBUF");
461        }
462        return (fd);
463}       
464
465int IP6Network::openssock(Address & addr, u_short port, int ttl)
466{
467        int fd;
468        struct sockaddr_in6 sin;
469
470
471        fd = socket(AF_INET6, SOCK_DGRAM, 0);
472        if (fd < 0) {
473                perror("socket");
474                exit(1);
475        }
476        nonblock(fd);
477
478        memset((char *)&sin, 0, sizeof(sin));
479        sin.sin6_family = AF_INET6;
480        sin.sin6_port = 0;
481        sin.sin6_flowinfo = flowLabel_;
482/* __IPV6 memcopy address */
483        // Use Local name if already set via command line
484        if (((const char*)local_)[0]!='\0') {
485                sin.sin6_addr = (IP6Address&)local_;
486        } else {
487                sin.sin6_addr = in6addr_any;
488        }
489        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
490                perror("bind");
491                exit(1);
492        }
493
494        memset((char *)&sin, 0, sizeof(sin));
495        sin.sin6_family = AF_INET6;
496/* __IPV6 memcopy address */
497        sin.sin6_addr = (IP6Address&)addr;
498        sin.sin6_port = port;
499        sin.sin6_flowinfo = flowLabel_;
500        if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
501                perror("connect");
502                exit(1);
503        }
504        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
505#ifdef IPV6_ADD_MEMBERSHIP
506                char c;
507
508                /* turn off loopback */
509                c = 0;
510                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &c, 1) < 0) {
511                        /*
512                         * If we cannot turn off loopback (Like on the
513                         * Microsoft TCP/IP stack), then declare this
514                         * option broken so that our packets can be
515                         * filtered on the recv path.
516                         */
517                        if (c == 0)
518                                noloopback_broken_ = 1;
519                }
520                /* set the multicast TTL */
521                u_int t;
522                t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl;
523
524                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
525                               (const char*)&t, sizeof(t)) < 0) {
526                        perror("IPV6_MULTICAST_HOPS");
527                        exit(1);
528                }
529                if (ifIndex_!=-1) {
530                        if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
531                                       (const char*)&ifIndex_, sizeof(ifIndex_)) < 0) {
532                                perror("IPV6_MULTICAST_IF");
533                                exit(1);
534                        }
535                }
536#else
537                fprintf(stderr, "\
538not compiled with support for IP multicast\n\
539you must specify a unicast destination\n");
540                exit(1);
541#endif
542        }
543        /*
544         * XXX don't need this for the session socket.
545         */
546        int bufsize = 80 * 1024;
547        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
548                       sizeof(bufsize)) < 0) {
549                bufsize = 48 * 1024;
550                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
551                               sizeof(bufsize)) < 0)
552                        perror("SO_SNDBUF");
553        }
554        return (fd);
555}
556
557int IP6Network::dorecv(u_char* buf, int len, Address& from, int fd)
558{
559        sockaddr_in6 sfrom;
560        socklen_t fromlen = sizeof(sfrom);
561        int cc = ::recvfrom(fd, (char*)buf, len, 0,
562                            (sockaddr*)&sfrom, &fromlen);
563        if (cc < 0) {
564                if (errno != EWOULDBLOCK)
565                        perror("recvfrom");
566                return (-1);
567        }
568        (IP6Address&)from = sfrom.sin6_addr;
569
570        /* Check for loopback - then compare last auto conf bit of addresses
571         * XXX Probaby should do this at rtp level.
572         *
573        if (noloopback_broken_ && sfrom.sin6_port == lport_) {
574                struct in6_addr local;
575                local=(IP6Address&)local_;
576                if (((sfrom.sin6_addr.s6_addr[12] == local.s6_addr[12]) &&
577                         (sfrom.sin6_addr.s6_addr[13] == local.s6_addr[13]) &&
578                         (sfrom.sin6_addr.s6_addr[14] == local.s6_addr[14]) &&
579                         (sfrom.sin6_addr.s6_addr[15] == local.s6_addr[15])   ) ||
580                         from == local_) {
581                                return (0);
582                }
583        }*/
584        return (cc);
585}
586
587#endif /* HAVE_IPV6 */
Note: See TracBrowser for help on using the browser.