예제 #1
0
파일: ip6.c 프로젝트: ju5t/npd6
/*****************************************************************************
 * processNS
 *  Takes a received Neighbor Solicitation and handles it. Main logic is:
 *      - bit of extra validation.
 *      - determine who it's asking about.
 *      - see if that matches the prefix we are looking after.
 *      - if it does, send a Neighbor Advertisement.
 *
 *  For more, see the inline comments - There's a lot going on here.
 *
 * Inputs:
 *  char *msg
 *      The received NS.
 *  int len
 *      The length of the received (candidate) NS.
 *      *** This has already been sanity checked back in the callers ***
 *
 * Outputs:
 *  Potentially, sends the Neighbor Advertisement.
 *
 * Return:
 *      void
 *
 */
void processNS( int ifIndex,
                unsigned char *msg,
                unsigned int len)
{
    // String representations of the various addresses
    char                        targetaddr_str[INET6_ADDRSTRLEN];
    char                        prefixaddr_str[INET6_ADDRSTRLEN];
    char                        srcaddr_str[INET6_ADDRSTRLEN];
    char                        dstaddr_str[INET6_ADDRSTRLEN];
    
    // Offsets into the received packet
    struct ip6_hdr              *ip6h =
    (struct ip6_hdr *)(msg + ETH_HLEN);
    struct icmp6_hdr            *icmph =
    (struct icmp6_hdr *)(msg + ETH_HLEN + sizeof( struct ip6_hdr));
    struct nd_neighbor_solicit  *ns =
    (struct nd_neighbor_solicit *)(msg + ETH_HLEN + sizeof( struct ip6_hdr));
    
    // For the interfaceIdx
    struct  in6_addr            prefixaddr = interfaces[ifIndex].prefix;
    int                         prefixaddrlen = interfaces[ifIndex].prefixLen;
    unsigned char               *linkAddr = interfaces[ifIndex].linkAddr;
    int                         interfaceIdx = interfaces[ifIndex].index;
    
    // Extracted from the received packet
    struct in6_addr             *srcaddr;
    struct in6_addr             *dstaddr;
    struct in6_addr             *targetaddr;
    unsigned int                multicastNS;
    
    // For outgoing NA
    struct in6_addr             srcLinkAddr = IN6ADDR_ANY_INIT;
    struct in6_pktinfo          *pkt_info;
    struct sockaddr_in6         sockaddr;
    unsigned char               nabuff[MAX_PKT_BUFF];
    struct nd_neighbor_advert   *nad;
    size_t                      iovlen=0;
    struct iovec                iov;
    struct cmsghdr              *cmsg;
    char __attribute__((aligned(8))) chdr[CMSG_SPACE(sizeof(struct in6_pktinfo))];
    struct msghdr               mhdr;
    ssize_t                     err;
    struct nd_opt_hdr           *opthdr;
    void                        *optdata;
    
    
    // Validate ICMP packet type, to ensure filter was correct
    // In theory not required, as the filter CAN'T be wrong...!
    if ( icmph->icmp6_type == ND_NEIGHBOR_SOLICIT )
    {
        flog(LOG_DEBUG2, "Confirmed packet as icmp6 Neighbor Solicitation.");
        srcaddr = &ip6h->ip6_src;
        dstaddr = &ip6h->ip6_dst;
        if (debug)
        {
            print_addr(srcaddr, srcaddr_str);
            print_addr(dstaddr, dstaddr_str);
            flog( LOG_DEBUG, "src addr = %s", srcaddr_str);
            flog( LOG_DEBUG, "dst addr = %s", dstaddr_str);
        }
    }
    else
    {
        flog(LOG_ERR, "Received impossible packet... filter failed. Oooops.");
        return;
    }
    
    // Bug 27 - Handle DAD NS as per RFC4862, 5.4.3
    if ( IN6_IS_ADDR_UNSPECIFIED(srcaddr) )
    { 
        flog(LOG_DEBUG, "Unspecified src addr - DAD activity. Ignoring NS.");
        return;
    }
    
    // Based upon the dstaddr, record if this was a unicast or multicast NS.
    // If unicast, we'll use that later when we decide whether to add the
    // target link-layer option to any outgoing NA.
    if ( IN6_IS_ADDR_MULTICAST(dstaddr) )
    {
        // This was a multicast NS
        flog(LOG_DEBUG2, "Multicast NS");
        multicastNS = 1;
    }else
    {
        // This was a unicast NS
        flog(LOG_DEBUG2, "Unicast NS");
        multicastNS=0;
    }
    
    // Within the NS, who are they looking for?
    targetaddr = (struct in6_addr *)&(ns->nd_ns_target);
    if (debug || listLog)
    {
        print_addr16(targetaddr, targetaddr_str);
        print_addr16(&prefixaddr, prefixaddr_str);
        flog(LOG_DEBUG, "NS target addr: %s", targetaddr_str);
        flog(LOG_DEBUG, "Local prefix: %s", prefixaddr_str);
    }
    
    // If tgt-addr == dst-addr then ignore this, as the automatic mechanisms
    // will reply themselves - we don't need to.
    if ( nsIgnoreLocal && IN6_ARE_ADDR_EQUAL(targetaddr, dstaddr) )
    {
        flog(LOG_DEBUG, "tgt==dst - Ignore.");
        return;
    }
    
    // Check for black or white listing compliance
    switch (listType) {
        case NOLIST:
            flog(LOG_DEBUG2, "Neither white nor black listing in operation.");
            break;
            
        case BLACKLIST:
            // See if the address matches an expression
            if((compareExpression(targetaddr) == 1))
            {
                flog(LISTLOGGING, "NS for blacklisted EXPR address: %s", targetaddr_str);
                return; // Abandon
            }
            // If active and tgt is in the list, bail.
            if ( tfind( (void *)targetaddr, &lRoot, tCompare) )
            {
                flog(LISTLOGGING, "NS for blacklisted specific addr: %s", targetaddr_str);
                return; //Abandon
            }
            break;
            
        case WHITELIST:
            // See if the address matches an expression
            if((compareExpression(targetaddr) == 1))
            {
                flog(LISTLOGGING, "NS for whitelisted EXPR: %s", targetaddr_str);
                break;	// Don't check further - we got a hit.
            }
            
            // If active and tgt is NOT in the list (and didn't match an expr above), bail.
            if ( tfind( (void *)targetaddr, &lRoot, tCompare) )
            {
                flog(LISTLOGGING, "NS for specific addr whitelisted: %s", targetaddr_str);
                break;
            }
            else
            {
                // We have whitelisting in operation but failed to match either type. 
                // Log it if required.
                flog(LOG_DEBUG, "No whitelist match for: %s", targetaddr_str);
                return;
            }
            break;
    }
    
    // Does it match our configured prefix that we're interested in?
    if (! addr6match( targetaddr, &prefixaddr, prefixaddrlen) )
    {
        flog(LOG_DEBUG, "Target/:prefix - Ignore NS.");
        return;
    }
    else
    {
        flog(LOG_DEBUG, "Target:prefix - Build NA response.");
        
        // If configured, log target to list
        if (collectTargets)
        {
            flog(LOG_DEBUG, "Store target to list.");
            storeTarget( targetaddr );
        }

        // Start building up the header for the packet
        memset(( void *)&sockaddr, 0, sizeof(struct sockaddr_in6));
        sockaddr.sin6_family = AF_INET6;
        sockaddr.sin6_port = htons(IPPROTO_ICMPV6);
        
        // Set the destination of the NA
        memcpy(&sockaddr.sin6_addr, srcaddr, sizeof(struct in6_addr));
        
        // Set up the NA itself
        memset( nabuff, 0, sizeof(nabuff) );
        nad = (struct nd_neighbor_advert *)nabuff;
        nad->nd_na_type = ND_NEIGHBOR_ADVERT;
        nad->nd_na_code = 0;
        nad->nd_na_cksum = 0;
        if (naRouter)
        {
            nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED | ND_NA_FLAG_ROUTER;
        }
        else
        {
            nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED;
        }
        
        memcpy(&(nad->nd_na_target), targetaddr, sizeof(struct in6_addr) );
        
        if (multicastNS || naLinkOptFlag)
        {
            // If the NS that came in was to a multicast address
            // or if we have forced the option for all packets anyway
            // then add a target link-layer option to the outgoing NA.
            // Per rfc, we must add dest link-addr option for NSs that came
            // to the multicast group addr.
            opthdr = (struct nd_opt_hdr *)&nabuff[sizeof(struct nd_neighbor_advert)] ;
            opthdr->nd_opt_type = ND_OPT_TARGET_LINKADDR;
            opthdr->nd_opt_len = 1; // Units of 8-octets
            optdata = (unsigned char *) (opthdr + 1);
            memcpy( optdata, linkAddr, 6);
            
            // Build the io vector
            iovlen = sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + ETH_ALEN;
            iov.iov_len = iovlen;
            iov.iov_base = (caddr_t) nabuff;
        } else
        {
            // The NS was unicast AND the config option was unset.
            // Build the io vector
            iovlen = sizeof(struct nd_neighbor_advert);
            iov.iov_len = iovlen;
            iov.iov_base = (caddr_t) nabuff;
        }
        
        // Build the cmsg
        memset(chdr, 0, sizeof(chdr) );
        cmsg = (struct cmsghdr *) chdr;
        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo) );
        cmsg->cmsg_level = IPPROTO_IPV6;
        cmsg->cmsg_type = IPV6_PKTINFO;
        
        pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
        
        // Set src (sending) addr and outgoing i/f for the datagram            
        memcpy(&pkt_info->ipi6_addr, &srcLinkAddr, sizeof(struct in6_addr) );
        pkt_info->ipi6_ifindex = interfaceIdx;        
        
        // Build the mhdr
        memset(&mhdr, 0, sizeof(mhdr) );
        mhdr.msg_name = (caddr_t)&sockaddr;
        mhdr.msg_namelen = sizeof(sockaddr);
        mhdr.msg_iov = &iov;
        mhdr.msg_iovlen = 1;
        mhdr.msg_control = (void *) cmsg;
        mhdr.msg_controllen = sizeof(chdr);
        
        flog(LOG_DEBUG2, "Outbound message built");
        
        err = sendmsg( interfaces[ifIndex].icmpSock, &mhdr, 0);
        if (err < 0)
            flog(LOG_ERR, "sendmsg returned with error %d = %s", errno, strerror(errno));
        else
            flog(LOG_DEBUG2, "sendmsg completed OK");
        
    }
}
예제 #2
0
int main(int argc, char *argv[])
{
    char logfile[FILENAME_MAX] = "";
    char switchifname[IFNAMSIZ] = {0};
    int c, err;
    int pid = 0;
    FILE *pidfp = NULL;

    // Default some globals
    strncpy(configfile, NPD6_CONF, FILENAME_MAX);
    strncpy( interfacestr, NULLSTR, sizeof(NULLSTR));
    memset( prefixaddrstr, 0, sizeof(prefixaddrstr));
    interfaceIdx = -1;
    daemonize = 0;
    // Default black/whitelisting to OFF
    listType = NOLIST;
    // Default config file values as required
    naLinkOptFlag = 0;
    nsIgnoreLocal = 1;
    naRouter = 1;
    debug = 0;
    maxHops = MAXMAXHOPS;

    /* Parse the args */
    while ((c = getopt_long(argc, argv, OPTIONS_STR, prog_opt, NULL)) > 0)
    {
        if (c==-1)
            break;

        switch (c) {
        case 'c':
            strcpy(configfile, optarg);
            break;
        case 'l':
            strcpy(logfile, optarg);
            break;
        case 'd':
            debug=1;
            break;
        case 'D':
            debug=2;
            break;
        case 'b':
            daemonize=1;
            break;
        case 'v':
            showVersion();
            return 0;
            break;
        case 'h':
            showUsage();
            return 0;
            break;
        }
    }
    
	/* Seems like about the right time to daemonize (or not) */
    if (daemonize)
    {
        if (daemon(0, 0) < 0 )
        {
            flog(LOG_ERR, "Failed to daemonize. Error: %s", strerror(errno) );
            exit(1);
        }
    }
	
	pid = getpid();
	if ((pidfp = fopen(NDP6PROXY_PIDFILE, "w")) != NULL) {
		fprintf(pidfp, "%d\n", pid);
		fclose(pidfp);
	}
    else
    {
        printf("**************** Open Pid file faild *********************** \r\n");
    }
	
    while (0 >= strlen(prefixaddrstr))
    {
        addr6match(NULL, NULL, 0);
        sleep(1);
    }
    /* Sort out where to log */
    if ( strlen(logfile) )
    {
        if (strlen(logfile) == 1 && (logfile[0]='-')  )
            logging = USE_STD;
        else
            logging = USE_FILE;
    }
    else
    {
        logging = USE_SYSLOG;
    }

    /* Open the log and config*/
    if ( (logging == USE_FILE) && (openLog(logfile) < 0)  )
    {
        printf("Exiting. Error in setting up logging correctly.\n");
        exit (1);
    }
    flog(LOG_INFO, "*********************** npd6 *****************************");

    if ( readConfig(configfile) )
    {
        flog(LOG_ERR, "Error in config file: %s", configfile);
        return 1;
    }

    flog(LOG_INFO, "Using normalised prefix %s/%d", prefixaddrstr, prefixaddrlen);
    flog(LOG_DEBUG2, "ifIndex for %s is: %d", interfacestr, interfaceIdx);

    err = init_sockets();
    if (err) {
        flog(LOG_ERR, "init_sockets: failed to initialise one or both sockets.");
        exit(1);
    }

    /* Set allmulti on the interface */
    while (0 > npd6getwan(switchifname))
    {
        sleep(1);
    }
    if_allmulti(switchifname, TRUE);
    if_allmulti(interfacestr, TRUE);
    /* Set up signal handlers */
    signal(SIGUSR1, usersignal);
    signal(SIGUSR2, usersignal);
    signal(SIGHUP, usersignal);
    signal(SIGINT, usersignal);
    signal(SIGTERM, usersignal);    // Typically used by init.d scripts

    /* And off we go... */
    dispatcher();

    flog(LOG_ERR, "Fell back out of dispatcher... This is impossible.");
    return 0;
}