int main (int argc, char** argv) { int divertIn; int divertOut; int divertInOut; int routeSock; struct sockaddr_in addr; fd_set readMask; fd_set writeMask; int fdMax; /* * Initialize packet aliasing software. * Done already here to be able to alter option bits * during command line and configuration file processing. */ PacketAliasInit (); /* * Parse options. */ inPort = 0; outPort = 0; verbose = 0; inOutPort = 0; ifName = NULL; ifMTU = -1; background = 0; running = 1; assignAliasAddr = 0; aliasAddr.s_addr = INADDR_NONE; aliasOverhead = 12; dynamicMode = 0; logDropped = 0; logFacility = LOG_DAEMON; /* * Mark packet buffer empty. */ packetSock = -1; packetDirection = DONT_KNOW; ParseArgs (argc, argv); /* * Open syslog channel. */ openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), logFacility); /* * Check that valid aliasing address has been given. */ if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) errx (1, "aliasing address not given"); if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) errx (1, "both alias address and interface " "name are not allowed"); /* * Check that valid port number is known. */ if (inPort != 0 || outPort != 0) if (inPort == 0 || outPort == 0) errx (1, "both input and output ports are required"); if (inPort == 0 && outPort == 0 && inOutPort == 0) ParseOption ("port", DEFAULT_SERVICE); /* * Check if ignored packets should be dropped. */ dropIgnoredIncoming = PacketAliasSetMode (0, 0); dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; /* * Create divert sockets. Use only one socket if -p was specified * on command line. Otherwise, create separate sockets for * outgoing and incoming connnections. */ if (inOutPort) { divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (divertInOut == -1) Quit ("Unable to create divert socket."); divertIn = -1; divertOut = -1; /* * Bind socket. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = inOutPort; if (bind (divertInOut, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind divert socket."); } else { divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (divertIn == -1) Quit ("Unable to create incoming divert socket."); divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); if (divertOut == -1) Quit ("Unable to create outgoing divert socket."); divertInOut = -1; /* * Bind divert sockets. */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = inPort; if (bind (divertIn, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind incoming divert socket."); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = outPort; if (bind (divertOut, (struct sockaddr*) &addr, sizeof addr) == -1) Quit ("Unable to bind outgoing divert socket."); } /* * Create routing socket if interface name specified and in dynamic mode. */ routeSock = -1; if (ifName) { if (dynamicMode) { routeSock = socket (PF_ROUTE, SOCK_RAW, 0); if (routeSock == -1) Quit ("Unable to create routing info socket."); assignAliasAddr = 1; } else SetAliasAddressFromIfName (ifName); } /* * Create socket for sending ICMP messages. */ icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); if (icmpSock == -1) Quit ("Unable to create ICMP socket."); /* * And disable reads for the socket, otherwise it slowly fills * up with received icmps which we do not use. */ shutdown(icmpSock, SHUT_RD); /* * Become a daemon unless verbose mode was requested. */ if (!verbose) DaemonMode (); /* * Catch signals to manage shutdown and * refresh of interface address. */ siginterrupt(SIGTERM, 1); siginterrupt(SIGHUP, 1); signal (SIGTERM, InitiateShutdown); signal (SIGHUP, RefreshAddr); /* * Set alias address if it has been given. */ if (aliasAddr.s_addr != INADDR_NONE) PacketAliasSetAddress (aliasAddr); /* * We need largest descriptor number for select. */ fdMax = -1; if (divertIn > fdMax) fdMax = divertIn; if (divertOut > fdMax) fdMax = divertOut; if (divertInOut > fdMax) fdMax = divertInOut; if (routeSock > fdMax) fdMax = routeSock; while (running) { if (divertInOut != -1 && !ifName && packetSock == -1) { /* * When using only one socket, just call * DoAliasing repeatedly to process packets. */ DoAliasing (divertInOut, DONT_KNOW); continue; } /* * Build read mask from socket descriptors to select. */ FD_ZERO (&readMask); FD_ZERO (&writeMask); /* * If there is unsent packet in buffer, use select * to check when socket comes writable again. */ if (packetSock != -1) { FD_SET (packetSock, &writeMask); } else { /* * No unsent packet exists - safe to check if * new ones are available. */ if (divertIn != -1) FD_SET (divertIn, &readMask); if (divertOut != -1) FD_SET (divertOut, &readMask); if (divertInOut != -1) FD_SET (divertInOut, &readMask); } /* * Routing info is processed always. */ if (routeSock != -1) FD_SET (routeSock, &readMask); if (select (fdMax + 1, &readMask, &writeMask, NULL, NULL) == -1) { if (errno == EINTR) continue; Quit ("Select failed."); } if (packetSock != -1) if (FD_ISSET (packetSock, &writeMask)) FlushPacketBuffer (packetSock); if (divertIn != -1) if (FD_ISSET (divertIn, &readMask)) DoAliasing (divertIn, INPUT); if (divertOut != -1) if (FD_ISSET (divertOut, &readMask)) DoAliasing (divertOut, OUTPUT); if (divertInOut != -1) if (FD_ISSET (divertInOut, &readMask)) DoAliasing (divertInOut, DONT_KNOW); if (routeSock != -1) if (FD_ISSET (routeSock, &readMask)) HandleRoutingInfo (routeSock); } if (background) unlink (PIDFILE); return 0; }
static void DoAliasing (int fd, int direction) { int bytes; int origBytes; int status; int addrSize; struct ip* ip; if (assignAliasAddr) { SetAliasAddressFromIfName (ifName); assignAliasAddr = 0; } /* * Get packet from socket. */ addrSize = sizeof packetAddr; origBytes = recvfrom (fd, packetBuf, sizeof packetBuf, 0, (struct sockaddr*) &packetAddr, &addrSize); if (origBytes == -1) { if (errno != EINTR) Warn ("read from divert socket failed"); return; } /* * This is a IP packet. */ ip = (struct ip*) packetBuf; if (direction == DONT_KNOW) { if (packetAddr.sin_addr.s_addr == INADDR_ANY) direction = OUTPUT; else direction = INPUT; } if (verbose) { /* * Print packet direction and protocol type. */ printf (direction == OUTPUT ? "Out " : "In "); switch (ip->ip_p) { case IPPROTO_TCP: printf ("[TCP] "); break; case IPPROTO_UDP: printf ("[UDP] "); break; case IPPROTO_ICMP: printf ("[ICMP] "); break; default: printf ("[%d] ", ip->ip_p); break; } /* * Print addresses. */ PrintPacket (ip); } if (direction == OUTPUT) { /* * Outgoing packets. Do aliasing. */ PacketAliasOut (packetBuf, IP_MAXPACKET); } else { /* * Do aliasing. */ status = PacketAliasIn (packetBuf, IP_MAXPACKET); if (status == PKT_ALIAS_IGNORED && dropIgnoredIncoming) { if (verbose) printf (" dropped.\n"); if (logDropped) SyslogPacket (ip, LOG_WARNING, "denied"); return; } } /* * Length might have changed during aliasing. */ bytes = ntohs (ip->ip_len); /* * Update alias overhead size for outgoing packets. */ if (direction == OUTPUT && bytes - origBytes > aliasOverhead) aliasOverhead = bytes - origBytes; if (verbose) { /* * Print addresses after aliasing. */ printf (" aliased to\n"); printf (" "); PrintPacket (ip); printf ("\n"); } packetLen = bytes; packetSock = fd; packetDirection = direction; FlushPacketBuffer (fd); }
static void DoAliasing(int fd, int direction) { int bytes; int origBytes; char buf[IP_MAXPACKET]; struct sockaddr_in addr; int wrote; int status; int addrSize; struct ip* ip; char msgBuf[80]; if (assignAliasAddr) { SetAliasAddressFromIfName(ifName); assignAliasAddr = 0; } /* * Get packet from socket. */ addrSize = sizeof addr; origBytes = recvfrom(fd, buf, sizeof buf, 0, (struct sockaddr *)&addr, &addrSize); if (origBytes == -1) { if (errno != EINTR) Warn("read from divert socket failed"); return; } /* * This is a IP packet. */ ip = (struct ip *)buf; if (direction == DONT_KNOW) { if (addr.sin_addr.s_addr == INADDR_ANY) direction = OUTPUT; else direction = INPUT; } if (verbose) { /* * Print packet direction and protocol type. */ printf(direction == OUTPUT ? "Out " : "In "); switch (ip->ip_p) { case IPPROTO_TCP: printf("[TCP] "); break; case IPPROTO_UDP: printf("[UDP] "); break; case IPPROTO_ICMP: printf("[ICMP] "); break; default: printf("[%d] ", ip->ip_p); break; } /* * Print addresses. */ PrintPacket(ip); } if (direction == OUTPUT) { /* * Outgoing packets. Do aliasing. */ PacketAliasOut(buf, IP_MAXPACKET); } else { /* * Do aliasing. */ status = PacketAliasIn(buf, IP_MAXPACKET); if (status == PKT_ALIAS_IGNORED && dropIgnoredIncoming) { if (verbose) printf(" dropped.\n"); if (logDropped) SyslogPacket(ip, LOG_WARNING, "denied"); return; } } /* * Length might have changed during aliasing. */ bytes = ntohs(ip->ip_len); /* * Update alias overhead size for outgoing packets. */ if (direction == OUTPUT && bytes - origBytes > aliasOverhead) aliasOverhead = bytes - origBytes; if (verbose) { /* * Print addresses after aliasing. */ printf(" aliased to\n"); printf(" "); PrintPacket(ip); printf("\n"); } /* * Put packet back for processing. */ wrote = sendto(fd, buf, bytes, 0, (struct sockaddr *)&addr, sizeof addr); if (wrote != bytes) { if (errno == EMSGSIZE) { if (direction == OUTPUT && ifMTU != -1) SendNeedFragIcmp(icmpSock, (struct ip *)buf, ifMTU - aliasOverhead); } else if (errno == EACCES && logIpfwDenied) { sprintf(msgBuf, "failed to write packet back"); Warn(msgBuf); } } }