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

Revision 3798, 16.7 KB (checked in by piers, 8 years ago)

Fixes for SSM on Linux

  • 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                        i++;
268                }
269                s_addr_ssm[i]='\0';
270                g_addr_=++g_addr;
271                s_addr_ssm_=s_addr_ssm;
272        } else {
273                // No SSM address found - just use group host address
274                g_addr_=host;
275        }
276        g_addr_ = host;
277        port_ = port;
278        ttl_ = ttl;
279
280        ssock_ = openssock(g_addr_, port, ttl);
281        if (ssock_ < 0)
282                return (-1);
283        /*
284         * Connecting the send socket also bound the local address.
285         * On a multihomed host we need to bind the receive socket
286         * to the same local address the kernel has chosen to send on.
287         */
288        struct sockaddr_in6 local;
289        if (localname(&local) < 0) {
290                return (-1);
291        }
292        (IP6Address&)local_ = local.sin6_addr;
293
294        rsock_ = openrsock(g_addr_, s_addr_ssm_, port, local_);
295        if (rsock_ < 0) {
296                (void)::close(ssock_);
297                return (-1);
298        }
299        lport_ = local.sin6_port;
300        last_reset_ = 0;
301        return (0);
302}
303
304int IP6Network::close()
305{
306        if (ssock_ >= 0) {
307                ::close(ssock_);
308                ::close(rsock_);
309                ssock_ = rsock_ = -1;
310        }
311        return (0);
312}
313
314int IP6Network::localname(sockaddr_in6* p) {
315
316  memset((char *)p, 0, sizeof(*p));
317  p->sin6_family = AF_INET6;
318  socklen_t len = sizeof(*p), result = 0;
319
320  if ((result = getsockname(ssock_, (struct sockaddr *)p, &len)) < 0) {
321    perror("getsockname");
322    p->sin6_addr = in6addr_any;
323    p->sin6_port = 0;
324  }
325  /* This doesn't yield anything useful
326  len = sizeof(p->sin6_addr);
327  if (!getsockopt(ssock_, IPPROTO_IPV6, IP_MULTICAST_IF, (char *)&p->sin6_addr,&len))
328                        return (0);
329  */
330
331        // Use Local name if already set via command line
332        // But use port derived from getsockname
333  if (local_.is_set()) {
334                p->sin6_addr=(IP6Address&)local_;
335                return (result);
336        }
337
338#ifdef MUSICA_IPV6
339  if (IS_SAME_IN6_ADDR(p->sin6_addr, in6addr_any)) {
340#else 
341  if (IN6_ARE_ADDR_EQUAL(&(p->sin6_addr), &in6addr_any)) {
342#endif
343    result = inet6_LookupLocalAddr(&p->sin6_addr);
344  }
345
346  return (result);
347
348}
349
350void IP6Network::reset()
351{
352        time_t t = time(0);
353        int d = int(t - last_reset_);
354        if (d > 3) {
355                last_reset_ = t;
356                (void)::close(ssock_);
357                ssock_ = openssock(g_addr_, port_, ttl_);
358        }
359}
360
361int IP6Network::openrsock(Address & g_addr, Address & s_addr_ssm, u_short port, Address & local)
362{
363        int fd;
364        struct sockaddr_in6 sin;
365
366        fd = socket(AF_INET6, SOCK_DGRAM, 0);
367        if (fd < 0) {
368                perror("socket");
369                exit(1);
370        }
371        nonblock(fd);
372        int on = 1;
373        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
374                        sizeof(on)) < 0) {
375                perror("SO_REUSEADDR");
376        }
377#ifdef SO_REUSEPORT
378        on = 1;
379        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on,
380                       sizeof(on)) < 0) {
381                perror("SO_REUSEPORT");
382                exit(1);
383        }
384#endif
385        memset((char *)&sin, 0, sizeof(sin));
386        sin.sin6_family = AF_INET6;
387        sin.sin6_addr = (IP6Address&)g_addr;
388        sin.sin6_port = port;
389
390#ifdef IPV6_ADD_MEMBERSHIP
391        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
392                /*
393                 * Try to bind the multicast address as the socket
394                 * dest address.  On many systems this won't work
395                 * so fall back to a destination of INADDR_ANY if
396                 * the first bind fails.
397                 */
398/* __IPV6 memcopy address */
399                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
400                        sin.sin6_addr = in6addr_any;
401                        if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
402                                perror("bind");
403                                exit(1);
404                        }
405                }
406
407#ifdef IPV6_ADD_SOURCE_MEMBERSHIP 
408        struct ipv6_mreq_source mrs;
409                /* Check if an Src addr - as in S,G has been set */
410        if (s_addr_ssm.is_set()) {
411                mrs.ipv6mr_sourceaddr = (IP6Address&)s_addr_ssm;
412                                mrs.ipv6mr_interface = (ifIndex_<0)?0:ifIndex_;
413                                mrs.ipv6mr_multiaddr = (IP6Address&)g_addr;
414
415                                if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_SOURCE_MEMBERSHIP,
416                                (char*)&mrs, sizeof(mrs)) < 0) {
417                        perror("IPV6_ADD_SOURCE_MEMBERSHIP");
418                        exit (1);
419                }
420        } else
421                       
422#endif /* IPV6_ADD_SOURCE_MEMBERSHIP */
423                {
424                                /*
425                                * XXX This is bogus multicast setup that really
426                                * shouldn't have to be done (group membership should be
427                                * implicit in the IP class D address, route should contain
428                                * ttl & no loopback flag, etc.).  Steve Deering has promised
429                                * to fix this for the 4.4bsd release.  We're all waiting
430                                * with bated breath.
431                                */
432                                                struct ipv6_mreq mr;
433
434                /* __IPV6 memcopy address */
435                #ifdef MUSICA_IPV6
436                                mr.i6mr_interface = (ifIndex_<0)?0:ifIndex_;
437                                mr.i6mr_multiaddr = (IP6Address&)g_addr;
438                #else
439                                mr.ipv6mr_interface = (ifIndex_<0)?0:ifIndex_;
440                                mr.ipv6mr_multiaddr = (IP6Address&)g_addr;
441                #endif
442
443                                int mreqsize = sizeof(mr);
444
445                // Fix for buggy header files in Win2k
446                #ifdef WIN2K_IPV6
447                                mreqsize += 4;
448                #endif
449                                if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
450                                                (char *)&mr, mreqsize) < 0) {
451                                        perror("IPV6_ADD_MEMBERSHIP");
452                                        exit(1);
453                                }
454                }
455        } else
456#endif
457        {
458                /*
459                 * bind the local host's address to this socket.  If that
460                 * fails, another vic probably has the addresses bound so
461                 * just exit.
462                 */
463/* __IPV6 memcopy address */
464                sin.sin6_addr = (IP6Address&)local;
465                if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
466                        perror("bind");
467                        exit(1);
468                }
469                /*
470                 * Despite several attempts on our part to get this fixed,
471                 * Microsoft Windows isn't complient with the Internet Host
472                 * Requirements standard (RFC-1122) and won't let us include
473                 * the source address in the receive socket demux state.
474                 * (The consequence of this is that all conversations have
475                 * to be assigned a unique local port so the vat 'side
476                 * conversation' (middle click on site name) function is
477                 * essentially useless under windows.)
478                 */
479#ifndef WIN32
480                /*
481                 * (try to) connect the foreign host's address to this socket.
482                 */
483                sin.sin6_port = 0;
484/* __IPV6 memcopy address */
485                sin.sin6_addr = (IP6Address&)addr;
486                connect(fd, (struct sockaddr *)&sin, sizeof(sin));
487#endif
488        }
489
490        /*
491         * XXX don't need this for the session socket.
492         */     
493        int bufsize = 80 * 1024;
494        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
495                        sizeof(bufsize)) < 0) {
496                bufsize = 32 * 1024;
497                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
498                                sizeof(bufsize)) < 0)
499                        perror("SO_RCVBUF");
500        }
501        return (fd);
502}       
503
504int IP6Network::openssock(Address & addr, u_short port, int ttl)
505{
506        int fd;
507        struct sockaddr_in6 sin;
508
509
510        fd = socket(AF_INET6, SOCK_DGRAM, 0);
511        if (fd < 0) {
512                perror("socket");
513                exit(1);
514        }
515        nonblock(fd);
516
517        memset((char *)&sin, 0, sizeof(sin));
518        sin.sin6_family = AF_INET6;
519        sin.sin6_port = 0;
520        sin.sin6_flowinfo = flowLabel_;
521/* __IPV6 memcopy address */
522    // Use Local name if already set via command line
523        if (local_.isset()) {
524                sin.sin6_addr = (IP6Address&)local_;
525    } else {
526                sin.sin6_addr = in6addr_any;
527        }
528        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
529                perror("bind");
530                exit(1);
531        }
532
533        memset((char *)&sin, 0, sizeof(sin));
534        sin.sin6_family = AF_INET6;
535/* __IPV6 memcopy address */
536        sin.sin6_addr = (IP6Address&)addr;
537        sin.sin6_port = port;
538        sin.sin6_flowinfo = flowLabel_;
539        if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
540                perror("connect");
541                exit(1);
542        }
543        if (IN6_IS_ADDR_MULTICAST(&sin.sin6_addr)) {
544
545#ifdef IPV6_ADD_MEMBERSHIP
546                char c;
547
548                /* turn off loopback */
549                c = 0;
550                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &c, 1) < 0) {
551                        /*
552                         * If we cannot turn off loopback (Like on the
553                         * Microsoft TCP/IP stack), then declare this
554                         * option broken so that our packets can be
555                         * filtered on the recv path.
556                         */
557                        if (c == 0)
558                                noloopback_broken_ = 1;
559                }
560                /* set the multicast TTL */
561                u_int t;
562                t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl;
563
564                if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
565                               (const char*)&t, sizeof(t)) < 0) {
566                        perror("IPV6_MULTICAST_HOPS");
567                        exit(1);
568                }
569                if (ifIndex_!=-1) {
570                        if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
571                                       (const char*)&ifIndex_, sizeof(ifIndex_)) < 0) {
572                                perror("IPV6_MULTICAST_IF");
573                                exit(1);
574                        }
575                }
576#else
577                fprintf(stderr, "\
578not compiled with support for IP multicast\n\
579you must specify a unicast destination\n");
580                exit(1);
581#endif
582        }
583        /*
584         * XXX don't need this for the session socket.
585         */
586        int bufsize = 80 * 1024;
587        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
588                       sizeof(bufsize)) < 0) {
589                bufsize = 48 * 1024;
590                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize,
591                               sizeof(bufsize)) < 0)
592                        perror("SO_SNDBUF");
593        }
594        return (fd);
595}
596
597int IP6Network::dorecv(u_char* buf, int len, Address& from, int fd)
598{
599        sockaddr_in6 sfrom;
600        socklen_t fromlen = sizeof(sfrom);
601        int cc = ::recvfrom(fd, (char*)buf, len, 0,
602                            (sockaddr*)&sfrom, &fromlen);
603        if (cc < 0) {
604                if (errno != EWOULDBLOCK)
605                        perror("recvfrom");
606                return (-1);
607        }
608        (IP6Address&)from = sfrom.sin6_addr;
609
610        /* Check for loopback - then compare last auto conf bit of addresses
611         * XXX Probaby should do this at rtp level.
612         *
613        if (noloopback_broken_ && sfrom.sin6_port == lport_) {
614                struct in6_addr local;
615                local=(IP6Address&)local_;
616                if (((sfrom.sin6_addr.s6_addr[12] == local.s6_addr[12]) &&
617                         (sfrom.sin6_addr.s6_addr[13] == local.s6_addr[13]) &&
618                         (sfrom.sin6_addr.s6_addr[14] == local.s6_addr[14]) &&
619                         (sfrom.sin6_addr.s6_addr[15] == local.s6_addr[15])   ) ||
620                         from == local_) {
621                                return (0);
622                }
623        }*/
624        return (cc);
625}
626
627#endif /* HAVE_IPV6 */
Note: See TracBrowser for help on using the browser.