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

Revision 3796, 16.6 KB (checked in by piers, 8 years ago)

Added Source-Specific Multicast (SSM) support - tested IPv4 on WinXP.

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