/***************************************************************************** * 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"); } }
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; }