diff --git a/sys/netinet/libalias/alias_ftp.c b/sys/netinet/libalias/alias_ftp.c index 962194ec0a68..b2fcfbf2396b 100644 --- a/sys/netinet/libalias/alias_ftp.c +++ b/sys/netinet/libalias/alias_ftp.c @@ -1,767 +1,767 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_ftp.c performs special processing for FTP sessions under TCP. Specifically, when a PORT/EPRT command from the client side or 227/229 reply from the server is sent, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. For this routine to work, the message must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new message will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. References: RFC 959, RFC 2428. Initial version: August, 1996 (cjm) Version 1.6 Brian Somers and Martin Renters identified an IP checksum error for modified IP packets. Version 1.7: January 9, 1996 (cjm) Differential checksum computation for change in IP packet length. Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions within the packet aliasing module. Version 3.1: May, 2000 (eds) Add support for passive mode, alias the 227 replies. See HISTORY file for record of revisions. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #include #else #include #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define FTP_CONTROL_PORT_NUMBER 21 static void AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *, int maxpacketsize); static void AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *); static int fingerprint_out(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) return (0); return (-1); } static int fingerprint_in(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL) return (-1); if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) return (0); return (-1); } static int protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah) { AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize); return (0); } static int protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah) { AliasHandleFtpIn(la, pip, ah->lnk); return (0); } struct proto_handler handlers[] = { { .pri = 80, .dir = OUT, .proto = TCP, .fingerprint = &fingerprint_out, .protohandler = &protohandler_out }, { .pri = 80, .dir = IN, .proto = TCP, .fingerprint = &fingerprint_in, .protohandler = &protohandler_in }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_ftp", mod_handler, NULL }; #ifdef _KERNEL DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_ftp, 1); MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1); #endif #define FTP_CONTROL_PORT_NUMBER 21 #define MAX_MESSAGE_SIZE 128 /* FTP protocol flags. */ #define WAIT_CRLF 0x01 enum ftp_message_type { FTP_PORT_COMMAND, FTP_EPRT_COMMAND, FTP_227_REPLY, FTP_229_REPLY, FTP_UNKNOWN_MESSAGE }; static int ParseFtpPortCommand(struct libalias *la, char *, int); static int ParseFtpEprtCommand(struct libalias *la, char *, int); static int ParseFtp227Reply(struct libalias *la, char *, int); static int ParseFtp229Reply(struct libalias *la, char *, int); static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int); static void AliasHandleFtpOut( struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to (including headers) */ ) { int hlen, tlen, dlen, pflags; char *sptr; struct tcphdr *tc; int ftp_message_type; /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Place string pointer and beginning of data */ sptr = (char *)pip; sptr += hlen; /* * Check that data length is not too long and previous message was * properly terminated with CRLF. */ pflags = GetProtocolFlags(lnk); if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) { ftp_message_type = FTP_UNKNOWN_MESSAGE; if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { /* When aliasing a client, check for the PORT/EPRT command. */ if (ParseFtpPortCommand(la, sptr, dlen)) ftp_message_type = FTP_PORT_COMMAND; else if (ParseFtpEprtCommand(la, sptr, dlen)) ftp_message_type = FTP_EPRT_COMMAND; } else { /* When aliasing a server, check for the 227/229 reply. */ if (ParseFtp227Reply(la, sptr, dlen)) ftp_message_type = FTP_227_REPLY; else if (ParseFtp229Reply(la, sptr, dlen)) { ftp_message_type = FTP_229_REPLY; la->true_addr.s_addr = pip->ip_src.s_addr; } } if (ftp_message_type != FTP_UNKNOWN_MESSAGE) NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type); } /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ if (dlen) { /* only if there's data */ sptr = (char *)pip; /* start over at beginning */ tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */ if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') pflags &= ~WAIT_CRLF; else pflags |= WAIT_CRLF; SetProtocolFlags(lnk, pflags); } } static void AliasHandleFtpIn(struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk) /* The link to go through (aliased port) */ { int hlen, tlen, dlen, pflags; char *sptr; struct tcphdr *tc; /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Place string pointer and beginning of data */ sptr = (char *)pip; sptr += hlen; /* * Check that data length is not too long and previous message was * properly terminated with CRLF. */ pflags = GetProtocolFlags(lnk); if (dlen <= MAX_MESSAGE_SIZE && (pflags & WAIT_CRLF) == 0 && ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER && (ParseFtpPortCommand(la, sptr, dlen) != 0 || ParseFtpEprtCommand(la, sptr, dlen) != 0)) { /* * Alias active mode client requesting data from server * behind NAT. We need to alias server->client connection * to external address client is connecting to. */ AddLink(la, GetOriginalAddress(lnk), la->true_addr, GetAliasAddress(lnk), htons(FTP_CONTROL_PORT_NUMBER - 1), htons(la->true_port), GET_ALIAS_PORT, IPPROTO_TCP); } /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ if (dlen) { sptr = (char *)pip; /* start over at beginning */ tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may * have grown. */ if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') pflags &= ~WAIT_CRLF; else pflags |= WAIT_CRLF; SetProtocolFlags(lnk, pflags); } } static int ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen) { char ch; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "PORT A,D,D,R,PO,RT". */ /* Return if data length is too short. */ if (dlen < 18) return (0); if (strncasecmp("PORT ", sptr, 5)) return (0); addr = port = octet = 0; state = 0; for (i = 5; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (isspace(ch)) break; else state++; case 1: case 3: case 5: case 7: case 9: case 11: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 2: case 4: case 6: case 8: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',') { addr = (addr << 8) + octet; state++; } else return (0); break; case 10: case 12: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',' || state == 12) { port = (port << 8) + octet; state++; } else return (0); break; } } if (state == 13) { la->true_addr.s_addr = htonl(addr); la->true_port = port; return (1); } else return (0); } static int ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen) { char ch, delim; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "EPRT |1|A.D.D.R|PORT|". */ /* Return if data length is too short. */ if (dlen < 18) return (0); if (strncasecmp("EPRT ", sptr, 5)) return (0); addr = port = octet = 0; delim = '|'; /* XXX gcc -Wuninitialized */ state = 0; for (i = 5; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (!isspace(ch)) { delim = ch; state++; } break; case 1: if (ch == '1') /* IPv4 address */ state++; else return (0); break; case 2: if (ch == delim) state++; else return (0); break; case 3: case 5: case 7: case 9: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 4: case 6: case 8: case 10: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == '.' || state == 10) { addr = (addr << 8) + octet; state++; } else return (0); break; case 11: if (isdigit(ch)) { port = ch - '0'; state++; } else return (0); break; case 12: if (isdigit(ch)) port = 10 * port + ch - '0'; else if (ch == delim) state++; else return (0); break; } } if (state == 13) { la->true_addr.s_addr = htonl(addr); la->true_port = port; return (1); } else return (0); } static int ParseFtp227Reply(struct libalias *la, char *sptr, int dlen) { char ch; int i, state; u_int32_t addr; u_short port; u_int8_t octet; /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ /* Return if data length is too short. */ if (dlen < 17) return (0); if (strncmp("227 ", sptr, 4)) return (0); addr = port = octet = 0; state = 0; for (i = 4; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (ch == '(') state++; break; case 1: case 3: case 5: case 7: case 9: case 11: if (isdigit(ch)) { octet = ch - '0'; state++; } else return (0); break; case 2: case 4: case 6: case 8: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',') { addr = (addr << 8) + octet; state++; } else return (0); break; case 10: case 12: if (isdigit(ch)) octet = 10 * octet + ch - '0'; else if (ch == ',' || (state == 12 && ch == ')')) { port = (port << 8) + octet; state++; } else return (0); break; } } if (state == 13) { la->true_port = port; la->true_addr.s_addr = htonl(addr); return (1); } else return (0); } static int ParseFtp229Reply(struct libalias *la, char *sptr, int dlen) { char ch, delim; int i, state; u_short port; /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ /* Return if data length is too short. */ if (dlen < 11) return (0); if (strncmp("229 ", sptr, 4)) return (0); port = 0; delim = '|'; /* XXX gcc -Wuninitialized */ state = 0; for (i = 4; i < dlen; i++) { ch = sptr[i]; switch (state) { case 0: if (ch == '(') state++; break; case 1: delim = ch; state++; break; case 2: case 3: if (ch == delim) state++; else return (0); break; case 4: if (isdigit(ch)) { port = ch - '0'; state++; } else return (0); break; case 5: if (isdigit(ch)) port = 10 * port + ch - '0'; else if (ch == delim) state++; else return (0); break; case 6: if (ch == ')') state++; else return (0); break; } } if (state == 7) { la->true_port = port; return (1); } else return (0); } static void NewFtpMessage(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize, int ftp_message_type) { struct alias_link *ftp_lnk; /* Security checks. */ if (pip->ip_src.s_addr != la->true_addr.s_addr) return; if (la->true_port < IPPORT_RESERVED) return; /* Establish link to address and port found in FTP control message. */ ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk), GetAliasAddress(lnk), htons(la->true_port), 0, GET_ALIAS_PORT, IPPROTO_TCP); if (ftp_lnk != NULL) { int slen, hlen, tlen, dlen; struct tcphdr *tc; #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(ftp_lnk); #endif /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Create new FTP message. */ { char stemp[MAX_MESSAGE_SIZE + 1]; char *sptr; u_short alias_port; u_char *ptr; int a1, a2, a3, a4, p1, p2; struct in_addr alias_address; /* Decompose alias address into quad format */ alias_address = GetAliasAddress(lnk); ptr = (u_char *)&alias_address.s_addr; a1 = *ptr++; a2 = *ptr++; a3 = *ptr++; a4 = *ptr; alias_port = GetAliasPort(ftp_lnk); /* Prepare new command */ switch (ftp_message_type) { case FTP_PORT_COMMAND: case FTP_227_REPLY: /* Decompose alias port into pair format. */ ptr = (char *)&alias_port; p1 = *ptr++; p2 = *ptr; if (ftp_message_type == FTP_PORT_COMMAND) { /* Generate PORT command string. */ sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", a1, a2, a3, a4, p1, p2); } else { /* Generate 227 reply string. */ sprintf(stemp, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", a1, a2, a3, a4, p1, p2); } break; case FTP_EPRT_COMMAND: /* Generate EPRT command string. */ sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", a1, a2, a3, a4, ntohs(alias_port)); break; case FTP_229_REPLY: /* Generate 229 reply string. */ sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", ntohs(alias_port)); break; } /* Save string length for IP header modification */ slen = strlen(stemp); /* Copy modified buffer into IP packet. */ sptr = (char *)pip; sptr += hlen; strncpy(sptr, stemp, maxpacketsize - hlen); } /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(lnk); tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + slen - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + MIN(slen, maxpacketsize - hlen)); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); #endif } } diff --git a/sys/netinet/libalias/alias_irc.c b/sys/netinet/libalias/alias_irc.c index 32e831742048..524b70b0632c 100644 --- a/sys/netinet/libalias/alias_irc.c +++ b/sys/netinet/libalias/alias_irc.c @@ -1,486 +1,486 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* Alias_irc.c intercepts packages contain IRC CTCP commands, and changes DCC commands to export a port on the aliasing host instead of an aliased host. For this routine to work, the DCC command must fit entirely into a single TCP packet. This will usually happen, but is not guaranteed. The interception is likely to change the length of the packet. The handling of this is copied more-or-less verbatim from ftp_alias.c Initial version: Eivind Eklund (ee) 97-01-29 Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions within the packet alising module. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #include #include #else #include #include #include #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define IRC_CONTROL_PORT_NUMBER_1 6667 #define IRC_CONTROL_PORT_NUMBER_2 6668 #define PKTSIZE (IP_MAXPACKET + 1) char *newpacket; /* Local defines */ #define DBprintf(a) static void AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, int maxpacketsize); static int fingerprint(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1 || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { newpacket = malloc(PKTSIZE); if (newpacket) { AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize); free(newpacket); } return (0); } struct proto_handler handlers[] = { { .pri = 90, .dir = OUT, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_irc", mod_handler, NULL }; /* Kernel module definition. */ #ifdef _KERNEL DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_irc, 1); MODULE_DEPEND(alias_irc, libalias, 1, 1, 1); #endif static void AliasHandleIrcOut(struct libalias *la, struct ip *pip, /* IP packet to examine */ struct alias_link *lnk, /* Which link are we on? */ int maxsize /* Maximum size of IP packet including * headers */ ) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; int i; /* Iterator through the source */ /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* * Return if data length is too short - assume an entire PRIVMSG in * each packet. */ if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) return; /* Place string pointer at beginning of data */ sptr = (char *)pip; sptr += hlen; maxsize -= hlen; /* We're interested in maximum size of * data, not packet */ /* Search for a CTCP command [Note 1] */ for (i = 0; i < dlen; i++) { if (sptr[i] == '\001') goto lFOUND_CTCP; } return; /* No CTCP commands in */ /* Handle CTCP commands - the buffer may have to be copied */ lFOUND_CTCP: { unsigned int copyat = i; unsigned int iCopy = 0; /* How much data have we written to * copy-back string? */ unsigned long org_addr; /* Original IP address */ unsigned short org_port; /* Original source port * address */ lCTCP_START: if (i >= dlen || iCopy >= PKTSIZE) goto lPACKET_DONE; newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start * character */ /* Start of a CTCP */ if (i + 4 >= dlen) /* Too short for DCC */ goto lBAD_CTCP; if (sptr[i + 0] != 'D') goto lBAD_CTCP; if (sptr[i + 1] != 'C') goto lBAD_CTCP; if (sptr[i + 2] != 'C') goto lBAD_CTCP; if (sptr[i + 3] != ' ') goto lBAD_CTCP; /* We have a DCC command - handle it! */ i += 4; /* Skip "DCC " */ if (iCopy + 4 > PKTSIZE) goto lPACKET_DONE; newpacket[iCopy++] = 'D'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = 'C'; newpacket[iCopy++] = ' '; DBprintf(("Found DCC\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces\n")); goto lPACKET_DONE; } } DBprintf(("Transferring command...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= PKTSIZE) { DBprintf(("DCC packet terminated during command\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < PKTSIZE) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done command - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-command)\n")); goto lPACKET_DONE; } } DBprintf(("Transferring filename...\n")); while (sptr[i] != ' ') { newpacket[iCopy++] = sptr[i]; if (++i >= dlen || iCopy >= PKTSIZE) { DBprintf(("DCC packet terminated during filename\n")); goto lPACKET_DONE; } } /* Copy _one_ space */ if (i + 1 < dlen && iCopy < PKTSIZE) newpacket[iCopy++] = sptr[i++]; DBprintf(("Done filename - removing spaces\n")); /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); goto lPACKET_DONE; } } DBprintf(("Fetching IP address\n")); /* Fetch IP address */ org_addr = 0; while (i < dlen && isdigit(sptr[i])) { if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); goto lBAD_CTCP; } org_addr *= 10; org_addr += sptr[i++] - '0'; } DBprintf(("Skipping space\n")); if (i + 1 >= dlen || sptr[i] != ' ') { DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); goto lBAD_CTCP; } /* * Skip any extra spaces (should not occur according to * protocol, but DCC breaks CTCP protocol anyway, so we * might as well play it safe */ while (sptr[i] == ' ') { if (++i >= dlen) { DBprintf(("Packet failure - space overflow.\n")); goto lPACKET_DONE; } } DBprintf(("Fetching port number\n")); /* Fetch source port */ org_port = 0; while (i < dlen && isdigit(sptr[i])) { if (org_port > 6554) { /* Terminate on overflow * (65536/10 rounded up */ DBprintf(("DCC: port number overflow\n")); goto lBAD_CTCP; } org_port *= 10; org_port += sptr[i++] - '0'; } /* Skip illegal addresses (or early termination) */ if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { DBprintf(("Bad port termination\n")); goto lBAD_CTCP; } DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); /* We've got the address and port - now alias it */ { struct alias_link *dcc_lnk; struct in_addr destaddr; true_port = htons(org_port); true_addr.s_addr = htonl(org_addr); destaddr.s_addr = 0; /* Sanity/Security checking */ if (!org_addr || !org_port || pip->ip_src.s_addr != true_addr.s_addr || org_port < IPPORT_RESERVED) goto lBAD_CTCP; /* * Steal the FTP_DATA_PORT - it doesn't really * matter, and this would probably allow it through * at least _some_ firewalls. */ dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, true_port, 0, IPPROTO_TCP, 1); DBprintf(("Got a DCC link\n")); if (dcc_lnk) { struct in_addr alias_address; /* Address from aliasing */ u_short alias_port; /* Port given by * aliasing */ int n; #ifndef NO_FW_PUNCH /* Generate firewall hole as appropriate */ PunchFWHole(dcc_lnk); #endif alias_address = GetAliasAddress(lnk); n = snprintf(&newpacket[iCopy], PKTSIZE - iCopy, "%lu ", (u_long) htonl(alias_address.s_addr)); if (n < 0) { DBprintf(("DCC packet construct failure.\n")); goto lBAD_CTCP; } if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly * - bad news */ DBprintf(("DCC constructed packet overflow.\n")); goto lBAD_CTCP; } alias_port = GetAliasPort(dcc_lnk); n = snprintf(&newpacket[iCopy], PKTSIZE - iCopy, "%u", htons(alias_port)); if (n < 0) { DBprintf(("DCC packet construct failure.\n")); goto lBAD_CTCP; } iCopy += n; /* * Done - truncated cases will be taken * care of by lBAD_CTCP */ DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); } } /* * An uninteresting CTCP - state entered right after '\001' * has been pushed. Also used to copy the rest of a DCC, * after IP address and port has been handled */ lBAD_CTCP: for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ if (sptr[i] == '\001') { goto lNORMAL_TEXT; } } goto lPACKET_DONE; /* Normal text */ lNORMAL_TEXT: for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ if (sptr[i] == '\001') { goto lCTCP_START; } } /* Handle the end of a packet */ lPACKET_DONE: iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; memcpy(sptr + copyat, newpacket, iCopy); /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(lnk); tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + iCopy + copyat); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif return; } } /* Notes: [Note 1] The initial search will most often fail; it could be replaced with a 32-bit specific search. Such a search would be done for 32-bit unsigned value V: V ^= 0x01010101; (Search is for null bytes) if( ((V-0x01010101)^V) & 0x80808080 ) { (found a null bytes which was a 01 byte) } To assert that the processor is 32-bits, do extern int ircdccar[32]; (32 bits) extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; which will generate a type-error on all but 32-bit machines. [Note 2] This routine really ought to be replaced with one that creates a transparent proxy on the aliasing host, to allow arbitrary changes in the TCP stream. This should not be too difficult given this base; I (ee) will try to do this some time later. */ diff --git a/sys/netinet/libalias/alias_proxy.c b/sys/netinet/libalias/alias_proxy.c index 9b75b22a74b3..7efab1fdc8db 100644 --- a/sys/netinet/libalias/alias_proxy.c +++ b/sys/netinet/libalias/alias_proxy.c @@ -1,862 +1,862 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Charles Mott * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* file: alias_proxy.c This file encapsulates special operations related to transparent proxy redirection. This is where packets with a particular destination, usually tcp port 80, are redirected to a proxy server. When packets are proxied, the destination address and port are modified. In certain cases, it is necessary to somehow encode the original address/port info into the packet. Two methods are presently supported: addition of a [DEST addr port] string at the beginning of a tcp stream, or inclusion of an optional field in the IP header. There is one public API function: PacketAliasProxyRule() -- Adds and deletes proxy rules. Rules are stored in a linear linked list, so lookup efficiency won't be too good for large lists. Initial development: April, 1998 (cjm) */ /* System includes */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #include #include #endif #include #ifdef _KERNEL #include #include #include #else #include #include "alias.h" /* Public API functions for libalias */ #include "alias_local.h" /* Functions used by alias*.c */ #endif /* Data structures */ /* * A linked list of arbitrary length, based on struct proxy_entry is * used to store proxy rules. */ struct proxy_entry { struct libalias *la; #define PROXY_TYPE_ENCODE_NONE 1 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 #define PROXY_TYPE_ENCODE_IPHDR 3 int rule_index; int proxy_type; u_char proto; u_short proxy_port; u_short server_port; struct in_addr server_addr; struct in_addr src_addr; struct in_addr src_mask; struct in_addr dst_addr; struct in_addr dst_mask; struct proxy_entry *next; struct proxy_entry *last; }; /* File scope variables */ /* Local (static) functions: IpMask() -- Utility function for creating IP masks from integer (1-32) specification. IpAddr() -- Utility function for converting string to IP address IpPort() -- Utility function for converting string to port number RuleAdd() -- Adds an element to the rule list. RuleDelete() -- Removes an element from the rule list. RuleNumberDelete() -- Removes all elements from the rule list having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true destination of a proxied IP packet */ static int IpMask(int, struct in_addr *); static int IpAddr(char *, struct in_addr *); static int IpPort(char *, int, int *); static void RuleAdd(struct libalias *la, struct proxy_entry *); static void RuleDelete(struct proxy_entry *); static int RuleNumberDelete(struct libalias *la, int); static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); static void ProxyEncodeIpHeader(struct ip *, int); static int IpMask(int nbits, struct in_addr *mask) { int i; u_int imask; if (nbits < 0 || nbits > 32) return (-1); imask = 0; for (i = 0; i < nbits; i++) imask = (imask >> 1) + 0x80000000; mask->s_addr = htonl(imask); return (0); } static int IpAddr(char *s, struct in_addr *addr) { if (inet_aton(s, addr) == 0) return (-1); else return (0); } static int IpPort(char *s, int proto, int *port) { int n; n = sscanf(s, "%d", port); if (n != 1) #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */ { struct servent *se; if (proto == IPPROTO_TCP) se = getservbyname(s, "tcp"); else if (proto == IPPROTO_UDP) se = getservbyname(s, "udp"); else return (-1); if (se == NULL) return (-1); *port = (u_int)ntohs(se->s_port); } #else return (-1); #endif return (0); } void RuleAdd(struct libalias *la, struct proxy_entry *entry) { int rule_index; struct proxy_entry *ptr; struct proxy_entry *ptr_last; LIBALIAS_LOCK_ASSERT(la); entry->la = la; if (la->proxyList == NULL) { la->proxyList = entry; entry->last = NULL; entry->next = NULL; return; } rule_index = entry->rule_index; ptr = la->proxyList; ptr_last = NULL; while (ptr != NULL) { if (ptr->rule_index >= rule_index) { if (ptr_last == NULL) { entry->next = la->proxyList; entry->last = NULL; la->proxyList->last = entry; la->proxyList = entry; return; } ptr_last->next = entry; ptr->last = entry; entry->last = ptr->last; entry->next = ptr; return; } ptr_last = ptr; ptr = ptr->next; } ptr_last->next = entry; entry->last = ptr_last; entry->next = NULL; } static void RuleDelete(struct proxy_entry *entry) { struct libalias *la; la = entry->la; LIBALIAS_LOCK_ASSERT(la); if (entry->last != NULL) entry->last->next = entry->next; else la->proxyList = entry->next; if (entry->next != NULL) entry->next->last = entry->last; free(entry); } static int RuleNumberDelete(struct libalias *la, int rule_index) { int err; struct proxy_entry *ptr; LIBALIAS_LOCK_ASSERT(la); err = -1; ptr = la->proxyList; while (ptr != NULL) { struct proxy_entry *ptr_next; ptr_next = ptr->next; if (ptr->rule_index == rule_index) { err = 0; RuleDelete(ptr); } ptr = ptr_next; } return (err); } static void ProxyEncodeTcpStream(struct alias_link *lnk, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; char addrbuf[INET_ADDRSTRLEN]; /* Compute pointer to tcp header */ tc = (struct tcphdr *)ip_next(pip); /* Don't modify if once already modified */ if (GetAckModified(lnk)) return; /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)), (u_int)ntohs(GetProxyPort(lnk))); /* Pad string out to a multiple of two in length */ slen = strlen(buffer); switch (slen % 2) { case 0: strcat(buffer, " \n"); slen += 2; break; case 1: strcat(buffer, "\n"); slen += 1; } /* Check for packet overflow */ if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) return; /* Shift existing TCP data and insert destination string */ { int dlen; int hlen; char *p; hlen = (pip->ip_hl + tc->th_off) << 2; dlen = ntohs(pip->ip_len) - hlen; /* Modify first packet that has data in it */ if (dlen == 0) return; p = (char *)pip; p += hlen; bcopy(p, p + slen, dlen); memcpy(p, buffer, slen); } /* Save information about modified sequence number */ { int delta; SetAckModified(lnk); tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); } /* Update IP header packet length and checksum */ { int accumulate; accumulate = pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + slen); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } /* Update TCP checksum, Use TcpChecksum since so many things have already changed. */ tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif } static void ProxyEncodeIpHeader(struct ip *pip, int maxpacketsize) { #define OPTION_LEN_BYTES 8 #define OPTION_LEN_INT16 4 #define OPTION_LEN_INT32 2 _Alignas(_Alignof(u_short)) u_char option[OPTION_LEN_BYTES]; #ifdef LIBALIAS_DEBUG fprintf(stdout, " ip cksum 1 = %x\n", (u_int)IpChecksum(pip)); fprintf(stdout, "tcp cksum 1 = %x\n", (u_int)TcpChecksum(pip)); #endif (void)maxpacketsize; /* Check to see that there is room to add an IP option */ if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) return; /* Build option and copy into packet */ { u_char *ptr; struct tcphdr *tc; ptr = (u_char *) pip; ptr += 20; memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); option[0] = 0x64; /* class: 3 (reserved), option 4 */ option[1] = OPTION_LEN_BYTES; memcpy(&option[2], (u_char *)&pip->ip_dst, 4); tc = (struct tcphdr *)ip_next(pip); memcpy(&option[6], (u_char *)&tc->th_sport, 2); memcpy(ptr, option, 8); } /* Update checksum, header length and packet length */ { int i; int accumulate; u_short *sptr; sptr = (u_short *) option; accumulate = 0; for (i = 0; i < OPTION_LEN_INT16; i++) accumulate -= *(sptr++); sptr = (u_short *) pip; accumulate += *sptr; pip->ip_hl += OPTION_LEN_INT32; accumulate -= *sptr; accumulate += pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } #undef OPTION_LEN_BYTES #undef OPTION_LEN_INT16 #undef OPTION_LEN_INT32 #ifdef LIBALIAS_DEBUG fprintf(stdout, " ip cksum 2 = %x\n", (u_int)IpChecksum(pip)); fprintf(stdout, "tcp cksum 2 = %x\n", (u_int)TcpChecksum(pip)); #endif } /* Functions by other packet alias source files ProxyCheck() -- Checks whether an outgoing packet should be proxied. ProxyModify() -- Encodes the original destination address/port for a packet which is to be redirected to a proxy server. */ int ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr, u_short * proxy_server_port, struct in_addr src_addr, struct in_addr dst_addr, u_short dst_port, u_char ip_p) { struct proxy_entry *ptr; LIBALIAS_LOCK_ASSERT(la); ptr = la->proxyList; while (ptr != NULL) { u_short proxy_port; proxy_port = ptr->proxy_port; if ((dst_port == proxy_port || proxy_port == 0) && ip_p == ptr->proto && src_addr.s_addr != ptr->server_addr.s_addr) { struct in_addr src_addr_masked; struct in_addr dst_addr_masked; src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) { if ((*proxy_server_port = ptr->server_port) == 0) *proxy_server_port = dst_port; *proxy_server_addr = ptr->server_addr; return (ptr->proxy_type); } } ptr = ptr->next; } return (0); } void ProxyModify(struct libalias *la, struct alias_link *lnk, struct ip *pip, int maxpacketsize, int proxy_type) { LIBALIAS_LOCK_ASSERT(la); (void)la; switch (proxy_type) { case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(lnk, pip, maxpacketsize); break; } } /* Public API functions */ /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * Subfields can be in arbitrary order. Port numbers and addresses * must be in either numeric or symbolic form. An optional rule number * is used to control the order in which rules are searched. If two * rules have the same number, then search order cannot be guaranteed, * and the rules should be disjoint. If no rule number is specified, * then 0 is used, and group 0 rules are always checked before any * others. */ int LibAliasProxyRule(struct libalias *la, const char *cmd) { int i, n, len, ret; int cmd_len; int token_count; int state; char *token; char buffer[256]; char str_port[sizeof(buffer)]; char str_server_port[sizeof(buffer)]; char *res = buffer; int rule_index; int proto; int proxy_type; int proxy_port; int server_port; struct in_addr server_addr; struct in_addr src_addr, src_mask; struct in_addr dst_addr, dst_mask; struct proxy_entry *proxy_entry; LIBALIAS_LOCK(la); ret = 0; /* Copy command line into a buffer */ cmd += strspn(cmd, " \t"); cmd_len = strlen(cmd); if (cmd_len > (int)(sizeof(buffer) - 1)) { ret = -1; goto getout; } strcpy(buffer, cmd); /* Convert to lower case */ len = strlen(buffer); for (i = 0; i < len; i++) buffer[i] = tolower((unsigned char)buffer[i]); /* Set default proxy type */ /* Set up default values */ rule_index = 0; proxy_type = PROXY_TYPE_ENCODE_NONE; proto = IPPROTO_TCP; proxy_port = 0; server_addr.s_addr = 0; server_port = 0; src_addr.s_addr = 0; IpMask(0, &src_mask); dst_addr.s_addr = 0; IpMask(0, &dst_mask); str_port[0] = 0; str_server_port[0] = 0; /* Parse command string with state machine */ #define STATE_READ_KEYWORD 0 #define STATE_READ_TYPE 1 #define STATE_READ_PORT 2 #define STATE_READ_SERVER 3 #define STATE_READ_RULE 4 #define STATE_READ_DELETE 5 #define STATE_READ_PROTO 6 #define STATE_READ_SRC 7 #define STATE_READ_DST 8 state = STATE_READ_KEYWORD; token = strsep(&res, " \t"); token_count = 0; while (token != NULL) { token_count++; switch (state) { case STATE_READ_KEYWORD: if (strcmp(token, "type") == 0) state = STATE_READ_TYPE; else if (strcmp(token, "port") == 0) state = STATE_READ_PORT; else if (strcmp(token, "server") == 0) state = STATE_READ_SERVER; else if (strcmp(token, "rule") == 0) state = STATE_READ_RULE; else if (strcmp(token, "delete") == 0) state = STATE_READ_DELETE; else if (strcmp(token, "proto") == 0) state = STATE_READ_PROTO; else if (strcmp(token, "src") == 0) state = STATE_READ_SRC; else if (strcmp(token, "dst") == 0) state = STATE_READ_DST; else { ret = -1; goto getout; } break; case STATE_READ_TYPE: if (strcmp(token, "encode_ip_hdr") == 0) proxy_type = PROXY_TYPE_ENCODE_IPHDR; else if (strcmp(token, "encode_tcp_stream") == 0) proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM; else if (strcmp(token, "no_encode") == 0) proxy_type = PROXY_TYPE_ENCODE_NONE; else { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; case STATE_READ_PORT: strcpy(str_port, token); state = STATE_READ_KEYWORD; break; case STATE_READ_SERVER: { int err; char *p; char s[sizeof(buffer)]; p = token; while (*p != ':' && *p != 0) p++; if (*p != ':') { err = IpAddr(token, &server_addr); if (err) { ret = -1; goto getout; } } else { *p = ' '; n = sscanf(token, "%s %s", s, str_server_port); if (n != 2) { ret = -1; goto getout; } err = IpAddr(s, &server_addr); if (err) { ret = -1; goto getout; } } state = STATE_READ_KEYWORD; break; } case STATE_READ_RULE: n = sscanf(token, "%d", &rule_index); if (n != 1 || rule_index < 0) { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; case STATE_READ_DELETE: { int err; int rule_to_delete; if (token_count != 2) { ret = -1; goto getout; } n = sscanf(token, "%d", &rule_to_delete); if (n != 1) { ret = -1; goto getout; } err = RuleNumberDelete(la, rule_to_delete); if (err) ret = -1; else ret = 0; goto getout; } case STATE_READ_PROTO: if (strcmp(token, "tcp") == 0) proto = IPPROTO_TCP; else if (strcmp(token, "udp") == 0) proto = IPPROTO_UDP; else { ret = -1; goto getout; } state = STATE_READ_KEYWORD; break; case STATE_READ_SRC: case STATE_READ_DST: { int err; char *p; struct in_addr mask; struct in_addr addr; p = token; while (*p != '/' && *p != 0) p++; if (*p != '/') { IpMask(32, &mask); err = IpAddr(token, &addr); if (err) { ret = -1; goto getout; } } else { int nbits; char s[sizeof(buffer)]; *p = ' '; n = sscanf(token, "%s %d", s, &nbits); if (n != 2) { ret = -1; goto getout; } err = IpAddr(s, &addr); if (err) { ret = -1; goto getout; } err = IpMask(nbits, &mask); if (err) { ret = -1; goto getout; } } if (state == STATE_READ_SRC) { src_addr = addr; src_mask = mask; } else { dst_addr = addr; dst_mask = mask; } state = STATE_READ_KEYWORD; break; } default: ret = -1; goto getout; break; } do { token = strsep(&res, " \t"); } while (token != NULL && !*token); } #undef STATE_READ_KEYWORD #undef STATE_READ_TYPE #undef STATE_READ_PORT #undef STATE_READ_SERVER #undef STATE_READ_RULE #undef STATE_READ_DELETE #undef STATE_READ_PROTO #undef STATE_READ_SRC #undef STATE_READ_DST /* Convert port strings to numbers. This needs to be done after the string is parsed, because the prototype might not be designated before the ports (which might be symbolic entries in /etc/services) */ if (strlen(str_port) != 0) { int err; err = IpPort(str_port, proto, &proxy_port); if (err) { ret = -1; goto getout; } } else { proxy_port = 0; } if (strlen(str_server_port) != 0) { int err; err = IpPort(str_server_port, proto, &server_port); if (err) { ret = -1; goto getout; } } else { server_port = 0; } /* Check that at least the server address has been defined */ if (server_addr.s_addr == 0) { ret = -1; goto getout; } /* Add to linked list */ proxy_entry = malloc(sizeof(struct proxy_entry)); if (proxy_entry == NULL) { ret = -1; goto getout; } proxy_entry->proxy_type = proxy_type; proxy_entry->rule_index = rule_index; proxy_entry->proto = proto; proxy_entry->proxy_port = htons(proxy_port); proxy_entry->server_port = htons(server_port); proxy_entry->server_addr = server_addr; proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; proxy_entry->src_mask = src_mask; proxy_entry->dst_mask = dst_mask; RuleAdd(la, proxy_entry); getout: LIBALIAS_UNLOCK(la); return (ret); } diff --git a/sys/netinet/libalias/alias_skinny.c b/sys/netinet/libalias/alias_skinny.c index 31b33696fc20..2c664c2c58d9 100644 --- a/sys/netinet/libalias/alias_skinny.c +++ b/sys/netinet/libalias/alias_skinny.c @@ -1,446 +1,446 @@ /*- * alias_skinny.c * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002, 2003 MarcusCom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Joe Marcus Clarke * * $FreeBSD$ */ #ifdef _KERNEL #include #include #include #else #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif static void AliasHandleSkinny(struct libalias *, struct ip *, struct alias_link *); static int fingerprint(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL) return (-1); if (la->skinnyPort != 0 && (ntohs(*ah->sport) == la->skinnyPort || ntohs(*ah->dport) == la->skinnyPort)) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { AliasHandleSkinny(la, pip, ah->lnk); return (0); } struct proto_handler handlers[] = { { .pri = 110, .dir = IN|OUT, .proto = TCP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_skinny", mod_handler, NULL }; #ifdef _KERNEL DECLARE_MODULE(alias_skinny, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_skinny, 1); MODULE_DEPEND(alias_skinny, libalias, 1, 1, 1); #endif /* * alias_skinny.c handles the translation for the Cisco Skinny Station * protocol. Skinny typically uses TCP port 2000 to set up calls between * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line, * it first needs to register with the Call Manager. To do this it sends * a registration message. This message contains the IP address of the * IP phone. This message must then be translated to reflect our global * IP address. Along with the registration message (and usually in the * same packet), the phone sends an IP port message. This message indicates * the TCP port over which it will communicate. * * When a call is placed from the phone, the Call Manager will send an * Open Receive Channel message to the phone to let the caller know someone * has answered. The phone then sends back an Open Receive Channel * Acknowledgement. In this packet, the phone sends its IP address again, * and the UDP port over which the voice traffic should flow. These values * need translation. Right after the Open Receive Channel Acknowledgement, * the Call Manager sends a Start Media Transmission message indicating the * call is connected. This message contains the IP address and UDP port * number of the remote (called) party. Once this message is translated, the * call can commence. The called part sends the first UDP packet to the * calling phone at the pre-arranged UDP port in the Open Receive Channel * Acknowledgement. * * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems, * Inc. All rights reserved. */ /* #define LIBALIAS_DEBUG 1 */ /* Message types that need translating */ #define REG_MSG 0x00000001 #define IP_PORT_MSG 0x00000002 #define OPNRCVCH_ACK 0x00000022 #define START_MEDIATX 0x0000008a struct skinny_header { u_int32_t len; u_int32_t reserved; u_int32_t msgId; }; struct RegisterMessage { u_int32_t msgId; char devName [16]; u_int32_t uid; u_int32_t instance; u_int32_t ipAddr; u_char devType; u_int32_t maxStreams; }; struct IpPortMessage { u_int32_t msgId; u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port * numbers */ }; struct OpenReceiveChannelAck { u_int32_t msgId; u_int32_t status; u_int32_t ipAddr; u_int32_t port; u_int32_t passThruPartyID; }; struct StartMediaTransmission { u_int32_t msgId; u_int32_t conferenceID; u_int32_t passThruPartyID; u_int32_t remoteIpAddr; u_int32_t remotePort; u_int32_t MSPacket; u_int32_t payloadCap; u_int32_t precedence; u_int32_t silenceSuppression; u_short maxFramesPerPacket; u_int32_t G723BitRate; }; typedef enum { ClientToServer = 0, ServerToClient = 1 } ConvDirection; static int alias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, ConvDirection direction) { (void)direction; reg_msg->ipAddr = (u_int32_t)GetAliasAddress(lnk).s_addr; tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static int alias_skinny_startmedia(struct StartMediaTransmission *start_media, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, u_int32_t localIpAddr, ConvDirection direction) { struct in_addr dst, src; (void)pip; (void)tc; (void)lnk; (void)direction; dst.s_addr = start_media->remoteIpAddr; src.s_addr = localIpAddr; /* * XXX I should probably handle in bound global translations as * well. */ return (0); } static int alias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, ConvDirection direction) { (void)direction; port_msg->stationIpPort = (u_int32_t)ntohs(GetAliasPort(lnk)); tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static int alias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack, struct ip *pip, struct tcphdr *tc, struct alias_link *lnk, u_int32_t * localIpAddr, ConvDirection direction) { struct in_addr null_addr; struct alias_link *opnrcv_lnk; u_int32_t localPort; (void)lnk; (void)direction; *localIpAddr = (u_int32_t)opnrcvch_ack->ipAddr; localPort = opnrcvch_ack->port; null_addr.s_addr = INADDR_ANY; opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr, htons((u_short) opnrcvch_ack->port), 0, IPPROTO_UDP, 1); opnrcvch_ack->ipAddr = (u_int32_t)GetAliasAddress(opnrcv_lnk).s_addr; opnrcvch_ack->port = (u_int32_t)ntohs(GetAliasPort(opnrcv_lnk)); tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif return (0); } static void AliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk) { size_t hlen, tlen, dlen; struct tcphdr *tc; u_int32_t msgId, t, len, lip; struct skinny_header *sd; size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header); ConvDirection direction; lip = -1; tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; sd = (struct skinny_header *)tcp_next(tc); /* * XXX This direction is reserved for future use. I still need to * handle the scenario where the call manager is on the inside, and * the calling phone is on the global outside. */ if (ntohs(tc->th_dport) == la->skinnyPort) direction = ClientToServer; else if (ntohs(tc->th_sport) == la->skinnyPort) direction = ServerToClient; else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n"); #endif return; } orig_len = dlen; /* * Skinny packets can contain many messages. We need to loop * through the packet using len to determine message boundaries. * This comes into play big time with port messages being in the * same packet as register messages. Also, open receive channel * acks are usually buried in a packet some 400 bytes long. */ while (dlen >= skinny_hdr_len) { len = (sd->len); msgId = (sd->msgId); t = len; if (t > orig_len || t > dlen) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, invalid length \n"); #endif return; } switch (msgId) { case REG_MSG: { struct RegisterMessage *reg_mesg; if (len < (int)sizeof(struct RegisterMessage)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, bad registration message\n"); #endif return; } reg_mesg = (struct RegisterMessage *)&sd->msgId; #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received a register message"); #endif alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction); break; } case IP_PORT_MSG: { struct IpPortMessage *port_mesg; if (len < (int)sizeof(struct IpPortMessage)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, port message\n"); #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received ipport message\n"); #endif port_mesg = (struct IpPortMessage *)&sd->msgId; alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction); break; } case OPNRCVCH_ACK: { struct OpenReceiveChannelAck *opnrcvchn_ack; if (len < (int)sizeof(struct OpenReceiveChannelAck)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n"); #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received open rcv channel msg\n"); #endif opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId; alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction); break; } case START_MEDIATX: { struct StartMediaTransmission *startmedia_tx; if (len < (int)sizeof(struct StartMediaTransmission)) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n"); #endif return; } if (lip == -1) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: received a" " packet,StartMediaTx Message before" " packet,OpnRcvChnAckMsg\n" #endif return; } #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/Skinny: Received start media trans msg\n"); #endif startmedia_tx = (struct StartMediaTransmission *)&sd->msgId; alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction); break; } default: break; } /* Place the pointer at the next message in the packet. */ dlen -= len + (skinny_hdr_len - sizeof(msgId)); sd = (struct skinny_header *)(((char *)&sd->msgId) + len); } } diff --git a/sys/netinet/libalias/alias_smedia.c b/sys/netinet/libalias/alias_smedia.c index 9b5a9d673ecf..c09c8e0c6d77 100644 --- a/sys/netinet/libalias/alias_smedia.c +++ b/sys/netinet/libalias/alias_smedia.c @@ -1,525 +1,525 @@ /*- * alias_smedia.c * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause * * Copyright (c) 2000 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Copyright (c) 2000 Junichi SATOH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Authors: Erik Salander * Junichi SATOH * */ #include __FBSDID("$FreeBSD$"); /* Alias_smedia.c is meant to contain the aliasing code for streaming media protocols. It performs special processing for RSTP sessions under TCP. Specifically, when a SETUP request is sent by a client, or a 200 reply is sent by a server, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. More specifically, the "client_port" configuration parameter is parsed for SETUP requests. The "server_port" configuration parameter is parsed for 200 replies eminating from a server. This is intended to handle the unicast case. RTSP also allows a redirection of a stream to another client by using the "destination" configuration parameter. The destination config parm would indicate a different IP address. This function is NOT supported by the RTSP translation code below. The RTSP multicast functions without any address translation intervention. For this routine to work, the SETUP/200 must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new SETUP/200 will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. Initial version: May, 2000 (eds) */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #include #else #include "alias_local.h" #include "alias_mod.h" #endif #define RTSP_CONTROL_PORT_NUMBER_1 554 #define RTSP_CONTROL_PORT_NUMBER_2 7070 #define TFTP_PORT_NUMBER 69 static void AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *, int maxpacketsize); static int fingerprint(struct libalias *la, struct alias_data *ah) { if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL && ntohs(*ah->dport) == TFTP_PORT_NUMBER) return (0); if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) return (-1); if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1 || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2 || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2) return (0); return (-1); } static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) { if (ntohs(*ah->dport) == TFTP_PORT_NUMBER) FindRtspOut(la, pip->ip_src, pip->ip_dst, *ah->sport, *ah->aport, IPPROTO_UDP); else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize); return (0); } struct proto_handler handlers[] = { { .pri = 100, .dir = OUT, .proto = TCP|UDP, .fingerprint = &fingerprint, .protohandler = &protohandler }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_smedia", mod_handler, NULL }; #ifdef _KERNEL DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_smedia, 1); MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1); #endif #define RTSP_CONTROL_PORT_NUMBER_1 554 #define RTSP_CONTROL_PORT_NUMBER_2 7070 #define RTSP_PORT_GROUP 2 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9')) static int search_string(char *data, int dlen, const char *search_str) { int i, j, k; int search_str_len; search_str_len = strlen(search_str); for (i = 0; i < dlen - search_str_len; i++) { for (j = i, k = 0; j < dlen - search_str_len; j++, k++) { if (data[j] != search_str[k] && data[j] != search_str[k] - ('a' - 'A')) break; if (k == search_str_len - 1) return (j + 1); } } return (-1); } static int alias_rtsp_out(struct libalias *la, struct ip *pip, struct alias_link *lnk, char *data, const char *port_str) { int hlen, tlen, dlen; struct tcphdr *tc; int i, j, pos, state, port_dlen, new_dlen, delta; u_short p[2], new_len; u_short sport, eport, base_port; u_short salias = 0, ealias = 0, base_alias = 0; const char *transport_str = "transport:"; char newdata[2048], *port_data, *port_newdata, stemp[80]; int links_created = 0, pkt_updated = 0; struct alias_link *rtsp_lnk = NULL; struct in_addr null_addr; /* Calculate data length of TCP packet */ tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Find keyword, "Transport: " */ pos = search_string(data, dlen, transport_str); if (pos < 0) return (-1); port_data = data + pos; port_dlen = dlen - pos; memcpy(newdata, data, pos); port_newdata = newdata + pos; while (port_dlen > (int)strlen(port_str)) { /* Find keyword, appropriate port string */ pos = search_string(port_data, port_dlen, port_str); if (pos < 0) break; memcpy(port_newdata, port_data, pos + 1); port_newdata += (pos + 1); p[0] = p[1] = 0; sport = eport = 0; state = 0; for (i = pos; i < port_dlen; i++) { switch (state) { case 0: if (port_data[i] == '=') state++; break; case 1: if (ISDIGIT(port_data[i])) p[0] = p[0] * 10 + port_data[i] - '0'; else if (port_data[i] == ';') state = 3; else if (port_data[i] == '-') state++; break; case 2: if (ISDIGIT(port_data[i])) p[1] = p[1] * 10 + port_data[i] - '0'; else state++; break; case 3: base_port = p[0]; sport = htons(p[0]); eport = htons(p[1]); if (!links_created) { links_created = 1; /* * Find an even numbered port * number base that satisfies the * contiguous number of ports we * need */ null_addr.s_addr = 0; if (0 == (salias = FindNewPortGroup(la, null_addr, FindAliasAddress(la, pip->ip_src), sport, 0, RTSP_PORT_GROUP, IPPROTO_UDP, 1))) { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n"); #endif } else { base_alias = ntohs(salias); for (j = 0; j < RTSP_PORT_GROUP; j++) { /* * Establish link * to port found in * RTSP packet */ rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr, htons(base_port + j), htons(base_alias + j), IPPROTO_UDP); if (rtsp_lnk != NULL) { #ifndef NO_FW_PUNCH /* * Punch * hole in * firewall */ PunchFWHole(rtsp_lnk); #endif } else { #ifdef LIBALIAS_DEBUG fprintf(stderr, "PacketAlias/RTSP: Cannot allocate RTSP data ports\n"); #endif break; } } } ealias = htons(base_alias + (RTSP_PORT_GROUP - 1)); } if (salias && rtsp_lnk) { pkt_updated = 1; /* Copy into IP packet */ sprintf(stemp, "%d", ntohs(salias)); memcpy(port_newdata, stemp, strlen(stemp)); port_newdata += strlen(stemp); if (eport != 0) { *port_newdata = '-'; port_newdata++; /* Copy into IP packet */ sprintf(stemp, "%d", ntohs(ealias)); memcpy(port_newdata, stemp, strlen(stemp)); port_newdata += strlen(stemp); } *port_newdata = ';'; port_newdata++; } state++; break; } if (state > 3) { break; } } port_data += i; port_dlen -= i; } if (!pkt_updated) return (-1); memcpy(port_newdata, port_data, port_dlen); port_newdata += port_dlen; *port_newdata = '\0'; /* Create new packet */ new_dlen = port_newdata - newdata; memcpy(data, newdata, new_dlen); SetAckModified(lnk); tc = (struct tcphdr *)ip_next(pip); delta = GetDeltaSeqOut(tc->th_seq, lnk); AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len, tc->th_seq, tc->th_off); new_len = htons(hlen + new_dlen); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif return (0); } /* Support the protocol used by early versions of RealPlayer */ static int alias_pna_out(struct libalias *la, struct ip *pip, struct alias_link *lnk, char *data, int dlen) { struct alias_link *pna_links; u_short msg_id, msg_len; char *work; u_short alias_port, port; struct tcphdr *tc; work = data; work += 5; while (work + 4 < data + dlen) { memcpy(&msg_id, work, 2); work += 2; memcpy(&msg_len, work, 2); work += 2; if (ntohs(msg_id) == 0) /* end of options */ return (0); if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) { memcpy(&port, work, 2); pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk), port, 0, IPPROTO_UDP, 1); if (pna_links != NULL) { #ifndef NO_FW_PUNCH /* Punch hole in firewall */ PunchFWHole(pna_links); #endif tc = (struct tcphdr *)ip_next(pip); alias_port = GetAliasPort(pna_links); memcpy(work, &alias_port, 2); /* Compute TCP checksum for revised packet */ tc->th_sum = 0; #ifdef _KERNEL - tc->th_x2 = 1; + tc->th_x2 = (TH_RES1 >> 8); #else tc->th_sum = TcpChecksum(pip); #endif } } work += ntohs(msg_len); } return (0); } static void AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize) { int hlen, tlen, dlen; struct tcphdr *tc; char *data; const char *setup = "SETUP", *pna = "PNA", *str200 = "200"; const char *okstr = "OK", *client_port_str = "client_port"; const char *server_port_str = "server_port"; int i, parseOk; (void)maxpacketsize; tc = (struct tcphdr *)ip_next(pip); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; data = (char *)pip; data += hlen; /* When aliasing a client, check for the SETUP request */ if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) || (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) { if (dlen >= (int)strlen(setup) && memcmp(data, setup, strlen(setup)) == 0) { alias_rtsp_out(la, pip, lnk, data, client_port_str); return; } if (dlen >= (int)strlen(pna) && memcmp(data, pna, strlen(pna)) == 0) alias_pna_out(la, pip, lnk, data, dlen); } else { /* * When aliasing a server, check for the 200 reply * Accommodate varying number of blanks between 200 & OK */ if (dlen >= (int)strlen(str200)) { for (parseOk = 0, i = 0; i <= dlen - (int)strlen(str200); i++) if (memcmp(&data[i], str200, strlen(str200)) == 0) { parseOk = 1; break; } if (parseOk) { i += strlen(str200); /* skip string found */ while (data[i] == ' ') /* skip blank(s) */ i++; if ((dlen - i) >= (int)strlen(okstr)) if (memcmp(&data[i], okstr, strlen(okstr)) == 0) alias_rtsp_out(la, pip, lnk, data, server_port_str); } } } } diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h index 4f062b31a051..8959d15f5a31 100644 --- a/sys/netinet/tcp.h +++ b/sys/netinet/tcp.h @@ -1,439 +1,442 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp.h 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #ifndef _NETINET_TCP_H_ #define _NETINET_TCP_H_ #include #include #if __BSD_VISIBLE typedef u_int32_t tcp_seq; #define tcp6_seq tcp_seq /* for KAME src sync over BSD*'s */ #define tcp6hdr tcphdr /* for KAME src sync over BSD*'s */ /* * TCP header. * Per RFC 793, September, 1981. */ struct tcphdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN u_char th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN u_char th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_AE 0x100 /* maps into th_x2 */ +#define TH_RES3 0x200 +#define TH_RES2 0x400 +#define TH_RES1 0x800 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|TH_ECE|TH_CWR) #define PRINT_TH_FLAGS "\20\1FIN\2SYN\3RST\4PUSH\5ACK\6URG\7ECE\10CWR\11AE" u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; #define PADTCPOLEN(len) ((((len) / 4) + !!((len) % 4)) * 4) #define TCPOPT_EOL 0 #define TCPOLEN_EOL 1 #define TCPOPT_PAD 0 /* padding after EOL */ #define TCPOLEN_PAD 1 #define TCPOPT_NOP 1 #define TCPOLEN_NOP 1 #define TCPOPT_MAXSEG 2 #define TCPOLEN_MAXSEG 4 #define TCPOPT_WINDOW 3 #define TCPOLEN_WINDOW 3 #define TCPOPT_SACK_PERMITTED 4 #define TCPOLEN_SACK_PERMITTED 2 #define TCPOPT_SACK 5 #define TCPOLEN_SACKHDR 2 #define TCPOLEN_SACK 8 /* 2*sizeof(tcp_seq) */ #define TCPOPT_TIMESTAMP 8 #define TCPOLEN_TIMESTAMP 10 #define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ #define TCPOPT_SIGNATURE 19 /* Keyed MD5: RFC 2385 */ #define TCPOLEN_SIGNATURE 18 #define TCPOPT_FAST_OPEN 34 #define TCPOLEN_FAST_OPEN_EMPTY 2 #define MAX_TCPOPTLEN 40 /* Absolute maximum TCP options len */ /* Miscellaneous constants */ #define MAX_SACK_BLKS 6 /* Max # SACK blocks stored at receiver side */ #define TCP_MAX_SACK 4 /* MAX # SACKs sent in any segment */ /* * The default maximum segment size (MSS) to be used for new TCP connections * when path MTU discovery is not enabled. * * RFC879 derives the default MSS from the largest datagram size hosts are * minimally required to handle directly or through IP reassembly minus the * size of the IP and TCP header. With IPv6 the minimum MTU is specified * in RFC2460. * * For IPv4 the MSS is 576 - sizeof(struct tcpiphdr) * For IPv6 the MSS is IPV6_MMTU - sizeof(struct ip6_hdr) - sizeof(struct tcphdr) * * We use explicit numerical definition here to avoid header pollution. */ #define TCP_MSS 536 #define TCP6_MSS 1220 /* * Limit the lowest MSS we accept for path MTU discovery and the TCP SYN MSS * option. Allowing low values of MSS can consume significant resources and * be used to mount a resource exhaustion attack. * Connections requesting lower MSS values will be rounded up to this value * and the IP_DF flag will be cleared to allow fragmentation along the path. * * See tcp_subr.c tcp_minmss SYSCTL declaration for more comments. Setting * it to "0" disables the minmss check. * * The default value is fine for TCP across the Internet's smallest official * link MTU (256 bytes for AX.25 packet radio). However, a connection is very * unlikely to come across such low MTU interfaces these days (anno domini 2003). */ #define TCP_MINMSS 216 #define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ #define TTCP_CLIENT_SND_WND 4096 /* dflt send window for T/TCP client */ #define TCP_MAX_WINSHIFT 14 /* maximum window shift */ #define TCP_MAXBURST 4 /* maximum segments in a burst */ #define TCP_MAXHLEN (0xf<<2) /* max length of header in bytes */ #define TCP_MAXOLEN (TCP_MAXHLEN - sizeof(struct tcphdr)) /* max space left for options */ #define TCP_FASTOPEN_MIN_COOKIE_LEN 4 /* Per RFC7413 */ #define TCP_FASTOPEN_MAX_COOKIE_LEN 16 /* Per RFC7413 */ #define TCP_FASTOPEN_PSK_LEN 16 /* Same as TCP_FASTOPEN_KEY_LEN */ #endif /* __BSD_VISIBLE */ /* * User-settable options (used with setsockopt). These are discrete * values and are not masked together. Some values appear to be * bitmasks for historical reasons. */ #define TCP_NODELAY 1 /* don't delay send to coalesce packets */ #if __BSD_VISIBLE #define TCP_MAXSEG 2 /* set maximum segment size */ #define TCP_NOPUSH 4 /* don't push last block of write */ #define TCP_NOOPT 8 /* don't use TCP options */ #define TCP_MD5SIG 16 /* use MD5 digests (RFC2385) */ #define TCP_INFO 32 /* retrieve tcp_info structure */ #define TCP_STATS 33 /* retrieve stats blob structure */ #define TCP_LOG 34 /* configure event logging for connection */ #define TCP_LOGBUF 35 /* retrieve event log for connection */ #define TCP_LOGID 36 /* configure log ID to correlate connections */ #define TCP_LOGDUMP 37 /* dump connection log events to device */ #define TCP_LOGDUMPID 38 /* dump events from connections with same ID to device */ #define TCP_TXTLS_ENABLE 39 /* TLS framing and encryption for transmit */ #define TCP_TXTLS_MODE 40 /* Transmit TLS mode */ #define TCP_RXTLS_ENABLE 41 /* TLS framing and encryption for receive */ #define TCP_RXTLS_MODE 42 /* Receive TLS mode */ #define TCP_IWND_NB 43 /* Override initial window (units: bytes) */ #define TCP_IWND_NSEG 44 /* Override initial window (units: MSS segs) */ #define TCP_LOGID_CNT 46 /* get number of connections with the same ID */ #define TCP_LOG_TAG 47 /* configure tag for grouping logs */ #define TCP_USER_LOG 48 /* userspace log event */ #define TCP_CONGESTION 64 /* get/set congestion control algorithm */ #define TCP_CCALGOOPT 65 /* get/set cc algorithm specific options */ #define TCP_MAXUNACKTIME 68 /* maximum time without making progress (sec) */ #define TCP_MAXPEAKRATE 69 /* maximum peak rate allowed (kbps) */ #define TCP_IDLE_REDUCE 70 /* Reduce cwnd on idle input */ #define TCP_REMOTE_UDP_ENCAPS_PORT 71 /* Enable TCP over UDP tunneling via the specified port */ #define TCP_DELACK 72 /* socket option for delayed ack */ #define TCP_FIN_IS_RST 73 /* A fin from the peer is treated has a RST */ #define TCP_LOG_LIMIT 74 /* Limit to number of records in tcp-log */ #define TCP_SHARED_CWND_ALLOWED 75 /* Use of a shared cwnd is allowed */ #define TCP_PROC_ACCOUNTING 76 /* Do accounting on tcp cpu usage and counts */ #define TCP_USE_CMP_ACKS 77 /* The transport can handle the Compressed mbuf acks */ #define TCP_PERF_INFO 78 /* retrieve accounting counters */ #define TCP_KEEPINIT 128 /* N, time to establish connection */ #define TCP_KEEPIDLE 256 /* L,N,X start keeplives after this period */ #define TCP_KEEPINTVL 512 /* L,N interval between keepalives */ #define TCP_KEEPCNT 1024 /* L,N number of keepalives before close */ #define TCP_FASTOPEN 1025 /* enable TFO / was created via TFO */ #define TCP_PCAP_OUT 2048 /* number of output packets to keep */ #define TCP_PCAP_IN 4096 /* number of input packets to keep */ #define TCP_FUNCTION_BLK 8192 /* Set the tcp function pointers to the specified stack */ /* Options for Rack and BBR */ #define TCP_REUSPORT_LB_NUMA 1026 /* set listen socket numa domain */ #define TCP_RACK_MBUF_QUEUE 1050 /* Do we allow mbuf queuing if supported */ #define TCP_RACK_PROP 1051 /* RACK proportional rate reduction (bool) */ #define TCP_RACK_TLP_REDUCE 1052 /* RACK TLP cwnd reduction (bool) */ #define TCP_RACK_PACE_REDUCE 1053 /* RACK Pacingv reduction factor (divisor) */ #define TCP_RACK_PACE_MAX_SEG 1054 /* Max TSO size we will send */ #define TCP_RACK_PACE_ALWAYS 1055 /* Use the always pace method */ #define TCP_RACK_PROP_RATE 1056 /* The proportional reduction rate */ #define TCP_RACK_PRR_SENDALOT 1057 /* Allow PRR to send more than one seg */ #define TCP_RACK_MIN_TO 1058 /* Minimum time between rack t-o's in ms */ #define TCP_RACK_EARLY_RECOV 1059 /* Should recovery happen early (bool) */ #define TCP_RACK_EARLY_SEG 1060 /* If early recovery max segments */ #define TCP_RACK_REORD_THRESH 1061 /* RACK reorder threshold (shift amount) */ #define TCP_RACK_REORD_FADE 1062 /* Does reordering fade after ms time */ #define TCP_RACK_TLP_THRESH 1063 /* RACK TLP theshold i.e. srtt+(srtt/N) */ #define TCP_RACK_PKT_DELAY 1064 /* RACK added ms i.e. rack-rtt + reord + N */ #define TCP_RACK_TLP_INC_VAR 1065 /* Does TLP include rtt variance in t-o */ #define TCP_BBR_IWINTSO 1067 /* Initial TSO window for BBRs first sends */ #define TCP_BBR_RECFORCE 1068 /* Enter recovery force out a segment disregard pacer no longer valid */ #define TCP_BBR_STARTUP_PG 1069 /* Startup pacing gain */ #define TCP_BBR_DRAIN_PG 1070 /* Drain pacing gain */ #define TCP_BBR_RWND_IS_APP 1071 /* Rwnd limited is considered app limited */ #define TCP_BBR_PROBE_RTT_INT 1072 /* How long in useconds between probe-rtt */ #define TCP_BBR_ONE_RETRAN 1073 /* Is only one segment allowed out during retran */ #define TCP_BBR_STARTUP_LOSS_EXIT 1074 /* Do we exit a loss during startup if not 20% incr */ #define TCP_BBR_USE_LOWGAIN 1075 /* lower the gain in PROBE_BW enable */ #define TCP_BBR_LOWGAIN_THRESH 1076 /* Unused after 2.3 morphs to TSLIMITS >= 2.3 */ #define TCP_BBR_TSLIMITS 1076 /* Do we use experimental Timestamp limiting for our algo */ #define TCP_BBR_LOWGAIN_HALF 1077 /* Unused after 2.3 */ #define TCP_BBR_PACE_OH 1077 /* Reused in 4.2 for pacing overhead setting */ #define TCP_BBR_LOWGAIN_FD 1078 /* Unused after 2.3 */ #define TCP_BBR_HOLD_TARGET 1078 /* For 4.3 on */ #define TCP_BBR_USEDEL_RATE 1079 /* Enable use of delivery rate for loss recovery */ #define TCP_BBR_MIN_RTO 1080 /* Min RTO in milliseconds */ #define TCP_BBR_MAX_RTO 1081 /* Max RTO in milliseconds */ #define TCP_BBR_REC_OVER_HPTS 1082 /* Recovery override htps settings 0/1/3 */ #define TCP_BBR_UNLIMITED 1083 /* Not used before 2.3 and morphs to algorithm >= 2.3 */ #define TCP_BBR_ALGORITHM 1083 /* What measurement algo does BBR use netflix=0, google=1 */ #define TCP_BBR_DRAIN_INC_EXTRA 1084 /* Does the 3/4 drain target include the extra gain */ #define TCP_BBR_STARTUP_EXIT_EPOCH 1085 /* what epoch gets us out of startup */ #define TCP_BBR_PACE_PER_SEC 1086 #define TCP_BBR_PACE_DEL_TAR 1087 #define TCP_BBR_PACE_SEG_MAX 1088 #define TCP_BBR_PACE_SEG_MIN 1089 #define TCP_BBR_PACE_CROSS 1090 #define TCP_RACK_IDLE_REDUCE_HIGH 1092 /* Reduce the highest cwnd seen to IW on idle */ #define TCP_RACK_MIN_PACE 1093 /* Do we enforce rack min pace time */ #define TCP_RACK_MIN_PACE_SEG 1094 /* If so what is the seg threshould */ #define TCP_RACK_GP_INCREASE 1094 /* After 4.1 its the GP increase in older rack */ #define TCP_RACK_TLP_USE 1095 #define TCP_BBR_ACK_COMP_ALG 1096 /* Not used */ #define TCP_BBR_TMR_PACE_OH 1096 /* Recycled in 4.2 */ #define TCP_BBR_EXTRA_GAIN 1097 #define TCP_RACK_DO_DETECTION 1097 /* Recycle of extra gain for rack, attack detection */ #define TCP_BBR_RACK_RTT_USE 1098 /* what RTT should we use 0, 1, or 2? */ #define TCP_BBR_RETRAN_WTSO 1099 #define TCP_DATA_AFTER_CLOSE 1100 #define TCP_BBR_PROBE_RTT_GAIN 1101 #define TCP_BBR_PROBE_RTT_LEN 1102 #define TCP_BBR_SEND_IWND_IN_TSO 1103 /* Do we burst out whole iwin size chunks at start? */ #define TCP_BBR_USE_RACK_RR 1104 /* Do we use the rack rapid recovery for pacing rxt's */ #define TCP_BBR_USE_RACK_CHEAT TCP_BBR_USE_RACK_RR /* Compat. */ #define TCP_BBR_HDWR_PACE 1105 /* Enable/disable hardware pacing */ #define TCP_BBR_UTTER_MAX_TSO 1106 /* Do we enforce an utter max TSO size */ #define TCP_BBR_EXTRA_STATE 1107 /* Special exit-persist catch up */ #define TCP_BBR_FLOOR_MIN_TSO 1108 /* The min tso size */ #define TCP_BBR_MIN_TOPACEOUT 1109 /* Do we suspend pacing until */ #define TCP_BBR_TSTMP_RAISES 1110 /* Can a timestamp measurement raise the b/w */ #define TCP_BBR_POLICER_DETECT 1111 /* Turn on/off google mode policer detection */ #define TCP_BBR_RACK_INIT_RATE 1112 /* Set an initial pacing rate for when we have no b/w in kbits per sec */ #define TCP_RACK_RR_CONF 1113 /* Rack rapid recovery configuration control*/ #define TCP_RACK_CHEAT_NOT_CONF_RATE TCP_RACK_RR_CONF #define TCP_RACK_GP_INCREASE_CA 1114 /* GP increase for Congestion Avoidance */ #define TCP_RACK_GP_INCREASE_SS 1115 /* GP increase for Slow Start */ #define TCP_RACK_GP_INCREASE_REC 1116 /* GP increase for Recovery */ #define TCP_RACK_FORCE_MSEG 1117 /* Override to use the user set max-seg value */ #define TCP_RACK_PACE_RATE_CA 1118 /* Pacing rate for Congestion Avoidance */ #define TCP_RACK_PACE_RATE_SS 1119 /* Pacing rate for Slow Start */ #define TCP_RACK_PACE_RATE_REC 1120 /* Pacing rate for Recovery */ #define TCP_NO_PRR 1122 /* If pacing, don't use prr */ #define TCP_RACK_NONRXT_CFG_RATE 1123 /* In recovery does a non-rxt use the cfg rate */ #define TCP_SHARED_CWND_ENABLE 1124 /* Use a shared cwnd if allowed */ #define TCP_TIMELY_DYN_ADJ 1125 /* Do we attempt dynamic multipler adjustment with timely. */ #define TCP_RACK_NO_PUSH_AT_MAX 1126 /* For timely do not push if we are over max rtt */ #define TCP_RACK_PACE_TO_FILL 1127 /* If we are not in recovery, always pace to fill the cwnd in 1 RTT */ #define TCP_SHARED_CWND_TIME_LIMIT 1128 /* we should limit to low time values the scwnd life */ #define TCP_RACK_PROFILE 1129 /* Select a profile that sets multiple options */ #define TCP_HDWR_RATE_CAP 1130 /* Allow hardware rates to cap pacing rate */ #define TCP_PACING_RATE_CAP 1131 /* Highest rate allowed in pacing in bytes per second (uint64_t) */ #define TCP_HDWR_UP_ONLY 1132 /* Allow the pacing rate to climb but not descend (with the exception of fill-cw */ #define TCP_RACK_ABC_VAL 1133 /* Set a local ABC value different then the system default */ #define TCP_REC_ABC_VAL 1134 /* Do we use the ABC value for recovery or the override one from sysctl */ #define TCP_RACK_MEASURE_CNT 1135 /* How many measurements are required in GP pacing */ #define TCP_DEFER_OPTIONS 1136 /* Defer options until the proper number of measurements occur, does not defer TCP_RACK_MEASURE_CNT */ #define TCP_FAST_RSM_HACK 1137 /* Do we do the broken thing where we don't twiddle the TLP bits properly in fast_rsm_output? */ #define TCP_RACK_PACING_BETA 1138 /* Changing the beta for pacing */ #define TCP_RACK_PACING_BETA_ECN 1139 /* Changing the beta for ecn with pacing */ #define TCP_RACK_TIMER_SLOP 1140 /* Set or get the timer slop used */ /* Start of reserved space for third-party user-settable options. */ #define TCP_VENDOR SO_VENDOR #define TCP_CA_NAME_MAX 16 /* max congestion control name length */ #define TCPI_OPT_TIMESTAMPS 0x01 #define TCPI_OPT_SACK 0x02 #define TCPI_OPT_WSCALE 0x04 #define TCPI_OPT_ECN 0x08 #define TCPI_OPT_TOE 0x10 #define TCPI_OPT_TFO 0x20 /* Maximum length of log ID. */ #define TCP_LOG_ID_LEN 64 /* * The TCP_INFO socket option comes from the Linux 2.6 TCP API, and permits * the caller to query certain information about the state of a TCP * connection. We provide an overlapping set of fields with the Linux * implementation, but since this is a fixed size structure, room has been * left for growth. In order to maximize potential future compatibility with * the Linux API, the same variable names and order have been adopted, and * padding left to make room for omitted fields in case they are added later. * * XXX: This is currently an unstable ABI/API, in that it is expected to * change. */ struct tcp_info { u_int8_t tcpi_state; /* TCP FSM state. */ u_int8_t __tcpi_ca_state; u_int8_t __tcpi_retransmits; u_int8_t __tcpi_probes; u_int8_t __tcpi_backoff; u_int8_t tcpi_options; /* Options enabled on conn. */ u_int8_t tcpi_snd_wscale:4, /* RFC1323 send shift value. */ tcpi_rcv_wscale:4; /* RFC1323 recv shift value. */ u_int32_t tcpi_rto; /* Retransmission timeout (usec). */ u_int32_t __tcpi_ato; u_int32_t tcpi_snd_mss; /* Max segment size for send. */ u_int32_t tcpi_rcv_mss; /* Max segment size for receive. */ u_int32_t __tcpi_unacked; u_int32_t __tcpi_sacked; u_int32_t __tcpi_lost; u_int32_t __tcpi_retrans; u_int32_t __tcpi_fackets; /* Times; measurements in usecs. */ u_int32_t __tcpi_last_data_sent; u_int32_t __tcpi_last_ack_sent; /* Also unimpl. on Linux? */ u_int32_t tcpi_last_data_recv; /* Time since last recv data. */ u_int32_t __tcpi_last_ack_recv; /* Metrics; variable units. */ u_int32_t __tcpi_pmtu; u_int32_t __tcpi_rcv_ssthresh; u_int32_t tcpi_rtt; /* Smoothed RTT in usecs. */ u_int32_t tcpi_rttvar; /* RTT variance in usecs. */ u_int32_t tcpi_snd_ssthresh; /* Slow start threshold. */ u_int32_t tcpi_snd_cwnd; /* Send congestion window. */ u_int32_t __tcpi_advmss; u_int32_t __tcpi_reordering; u_int32_t __tcpi_rcv_rtt; u_int32_t tcpi_rcv_space; /* Advertised recv window. */ /* FreeBSD extensions to tcp_info. */ u_int32_t tcpi_snd_wnd; /* Advertised send window. */ u_int32_t tcpi_snd_bwnd; /* No longer used. */ u_int32_t tcpi_snd_nxt; /* Next egress seqno */ u_int32_t tcpi_rcv_nxt; /* Next ingress seqno */ u_int32_t tcpi_toe_tid; /* HWTID for TOE endpoints */ u_int32_t tcpi_snd_rexmitpack; /* Retransmitted packets */ u_int32_t tcpi_rcv_ooopack; /* Out-of-order packets */ u_int32_t tcpi_snd_zerowin; /* Zero-sized windows sent */ /* Padding to grow without breaking ABI. */ u_int32_t __tcpi_pad[26]; /* Padding. */ }; /* * If this structure is provided when setting the TCP_FASTOPEN socket * option, and the enable member is non-zero, a subsequent connect will use * pre-shared key (PSK) mode using the provided key. */ struct tcp_fastopen { int enable; uint8_t psk[TCP_FASTOPEN_PSK_LEN]; }; #endif #define TCP_FUNCTION_NAME_LEN_MAX 32 struct tcp_function_set { char function_set_name[TCP_FUNCTION_NAME_LEN_MAX]; uint32_t pcbcnt; }; /* TLS modes for TCP_TXTLS_MODE */ #define TCP_TLS_MODE_NONE 0 #define TCP_TLS_MODE_SW 1 #define TCP_TLS_MODE_IFNET 2 #define TCP_TLS_MODE_TOE 3 /* * TCP Control message types */ #define TLS_SET_RECORD_TYPE 1 #define TLS_GET_RECORD 2 /* * TCP specific variables of interest for tp->t_stats stats(9) accounting. */ #define VOI_TCP_TXPB 0 /* Transmit payload bytes */ #define VOI_TCP_RETXPB 1 /* Retransmit payload bytes */ #define VOI_TCP_FRWIN 2 /* Foreign receive window */ #define VOI_TCP_LCWIN 3 /* Local congesiton window */ #define VOI_TCP_RTT 4 /* Round trip time */ #define VOI_TCP_CSIG 5 /* Congestion signal */ #define VOI_TCP_GPUT 6 /* Goodput */ #define VOI_TCP_CALCFRWINDIFF 7 /* Congestion avoidance LCWIN - FRWIN */ #define VOI_TCP_GPUT_ND 8 /* Goodput normalised delta */ #define VOI_TCP_ACKLEN 9 /* Average ACKed bytes per ACK */ #define TCP_REUSPORT_LB_NUMA_NODOM (-2) /* remove numa binding */ #define TCP_REUSPORT_LB_NUMA_CURDOM (-1) /* bind to current domain */ #endif /* !_NETINET_TCP_H_ */ diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index d7b31c29d4ec..4dfe45494e2c 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -1,1248 +1,1248 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Paolo Pisati * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for in_cksum */ struct cfg_spool { LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ struct in_addr addr; uint16_t port; }; /* Nat redirect configuration. */ struct cfg_redir { LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ uint16_t mode; /* type of redirect mode */ uint16_t proto; /* protocol: tcp/udp */ struct in_addr laddr; /* local ip address */ struct in_addr paddr; /* public ip address */ struct in_addr raddr; /* remote ip address */ uint16_t lport; /* local port */ uint16_t pport; /* public port */ uint16_t rport; /* remote port */ uint16_t pport_cnt; /* number of public ports */ uint16_t rport_cnt; /* number of remote ports */ struct alias_link **alink; u_int16_t spool_cnt; /* num of entry in spool chain */ /* chain of spool instances */ LIST_HEAD(spool_chain, cfg_spool) spool_chain; }; /* Nat configuration data struct. */ struct cfg_nat { /* chain of nat instances */ LIST_ENTRY(cfg_nat) _next; int id; /* nat id */ struct in_addr ip; /* nat ip address */ struct libalias *lib; /* libalias instance */ int mode; /* aliasing mode */ int redir_cnt; /* number of entry in spool chain */ /* chain of redir instances */ LIST_HEAD(redir_chain, cfg_redir) redir_chain; char if_name[IF_NAMESIZE]; /* interface name */ u_short alias_port_lo; /* low range for port aliasing */ u_short alias_port_hi; /* high range for port aliasing */ }; static eventhandler_tag ifaddr_event_tag; static void ifaddr_change(void *arg __unused, struct ifnet *ifp) { struct cfg_nat *ptr; struct ifaddr *ifa; struct ip_fw_chain *chain; KASSERT(curvnet == ifp->if_vnet, ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) return; chain = &V_layer3_chain; IPFW_UH_WLOCK(chain); /* Check every nat entry... */ LIST_FOREACH(ptr, &chain->nat, _next) { struct epoch_tracker et; /* ...using nic 'ifp->if_xname' as dynamic alias address. */ if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) continue; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_addr->sa_family != AF_INET) continue; IPFW_WLOCK(chain); ptr->ip = ((struct sockaddr_in *) (ifa->ifa_addr))->sin_addr; LibAliasSetAddress(ptr->lib, ptr->ip); IPFW_WUNLOCK(chain); } NET_EPOCH_EXIT(et); } IPFW_UH_WUNLOCK(chain); } /* * delete the pointers for nat entry ix, or all of them if ix < 0 */ static void flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) { ipfw_insn_nat *cmd; int i; IPFW_WLOCK_ASSERT(chain); for (i = 0; i < chain->n_rules; i++) { cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]); if (cmd->o.opcode == O_NAT && cmd->nat != NULL && (ix < 0 || cmd->nat->id == ix)) cmd->nat = NULL; } } static void del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) { struct cfg_redir *r, *tmp_r; struct cfg_spool *s, *tmp_s; int i, num; LIST_FOREACH_SAFE(r, head, _next, tmp_r) { num = 1; /* Number of alias_link to delete. */ switch (r->mode) { case NAT44_REDIR_PORT: num = r->pport_cnt; /* FALLTHROUGH */ case NAT44_REDIR_ADDR: case NAT44_REDIR_PROTO: /* Delete all libalias redirect entry. */ for (i = 0; i < num; i++) LibAliasRedirectDelete(n->lib, r->alink[i]); /* Del spool cfg if any. */ LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { LIST_REMOVE(s, _next); free(s, M_IPFW); } free(r->alink, M_IPFW); LIST_REMOVE(r, _next); free(r, M_IPFW); break; default: printf("unknown redirect mode: %u\n", r->mode); /* XXX - panic?!?!? */ break; } } } static int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) { struct cfg_redir *r; struct cfg_spool *s; struct nat44_cfg_redir *ser_r; struct nat44_cfg_spool *ser_s; int cnt, off, i; for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { ser_r = (struct nat44_cfg_redir *)&buf[off]; r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); r->mode = ser_r->mode; r->laddr = ser_r->laddr; r->paddr = ser_r->paddr; r->raddr = ser_r->raddr; r->lport = ser_r->lport; r->pport = ser_r->pport; r->rport = ser_r->rport; r->pport_cnt = ser_r->pport_cnt; r->rport_cnt = ser_r->rport_cnt; r->proto = ser_r->proto; r->spool_cnt = ser_r->spool_cnt; //memcpy(r, ser_r, SOF_REDIR); LIST_INIT(&r->spool_chain); off += sizeof(struct nat44_cfg_redir); r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, M_IPFW, M_WAITOK | M_ZERO); switch (r->mode) { case NAT44_REDIR_ADDR: r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, r->paddr); break; case NAT44_REDIR_PORT: for (i = 0 ; i < r->pport_cnt; i++) { /* If remotePort is all ports, set it to 0. */ u_short remotePortCopy = r->rport + i; if (r->rport_cnt == 1 && r->rport == 0) remotePortCopy = 0; r->alink[i] = LibAliasRedirectPort(ptr->lib, r->laddr, htons(r->lport + i), r->raddr, htons(remotePortCopy), r->paddr, htons(r->pport + i), r->proto); if (r->alink[i] == NULL) { r->alink[0] = NULL; break; } } break; case NAT44_REDIR_PROTO: r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, r->raddr, r->paddr, r->proto); break; default: printf("unknown redirect mode: %u\n", r->mode); break; } if (r->alink[0] == NULL) { printf("LibAliasRedirect* returned NULL\n"); free(r->alink, M_IPFW); free(r, M_IPFW); return (EINVAL); } /* LSNAT handling. */ for (i = 0; i < r->spool_cnt; i++) { ser_s = (struct nat44_cfg_spool *)&buf[off]; s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); s->addr = ser_s->addr; s->port = ser_s->port; LibAliasAddServer(ptr->lib, r->alink[0], s->addr, htons(s->port)); off += sizeof(struct nat44_cfg_spool); /* Hook spool entry. */ LIST_INSERT_HEAD(&r->spool_chain, s, _next); } /* And finally hook this redir entry. */ LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); } return (0); } static void free_nat_instance(struct cfg_nat *ptr) { del_redir_spool_cfg(ptr, &ptr->redir_chain); LibAliasUninit(ptr->lib); free(ptr, M_IPFW); } /* * ipfw_nat - perform mbuf header translation. * * Note V_layer3_chain has to be locked while calling ipfw_nat() in * 'global' operation mode (t == NULL). * */ static int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) { struct mbuf *mcl; struct ip *ip; /* XXX - libalias duct tape */ int ldt, retval, found; struct ip_fw_chain *chain; char *c; ldt = 0; retval = 0; mcl = m_megapullup(m, m->m_pkthdr.len); if (mcl == NULL) { args->m = NULL; return (IP_FW_DENY); } M_ASSERTMAPPED(mcl); ip = mtod(mcl, struct ip *); /* * XXX - Libalias checksum offload 'duct tape': * * locally generated packets have only pseudo-header checksum * calculated and libalias will break it[1], so mark them for * later fix. Moreover there are cases when libalias modifies * tcp packet data[2], mark them for later fix too. * * [1] libalias was never meant to run in kernel, so it does * not have any knowledge about checksum offloading, and * expects a packet with a full internet checksum. * Unfortunately, packets generated locally will have just the * pseudo header calculated, and when libalias tries to adjust * the checksum it will actually compute a wrong value. * * [2] when libalias modifies tcp's data content, full TCP * checksum has to be recomputed: the problem is that * libalias does not have any idea about checksum offloading. * To work around this, we do not do checksumming in LibAlias, * but only mark the packets in th_x2 field. If we receive a * marked packet, we calculate correct checksum for it * aware of offloading. Why such a terrible hack instead of * recalculating checksum for each packet? * Because the previous checksum was not checked! * Recalculating checksums for EVERY packet will hide ALL * transmission errors. Yes, marked packets still suffer from * this problem. But, sigh, natd(8) has this problem, too. * * TODO: -make libalias mbuf aware (so * it can handle delayed checksum and tso) */ if (mcl->m_pkthdr.rcvif == NULL && mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) ldt = 1; c = mtod(mcl, char *); /* Check if this is 'global' instance */ if (t == NULL) { if (args->flags & IPFW_ARGS_IN) { /* Wrong direction, skip processing */ args->m = mcl; return (IP_FW_NAT); } found = 0; chain = &V_layer3_chain; IPFW_RLOCK_ASSERT(chain); /* Check every nat entry... */ LIST_FOREACH(t, &chain->nat, _next) { if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) continue; retval = LibAliasOutTry(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl), 0); if (retval == PKT_ALIAS_OK) { /* Nat instance recognises state */ found = 1; break; } } if (found != 1) { /* No instance found, return ignore */ args->m = mcl; return (IP_FW_NAT); } } else { if (args->flags & IPFW_ARGS_IN) retval = LibAliasIn(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl)); else retval = LibAliasOut(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl)); } /* * We drop packet when: * 1. libalias returns PKT_ALIAS_ERROR; * 2. For incoming packets: * a) for unresolved fragments; * b) libalias returns PKT_ALIAS_IGNORED and * PKT_ALIAS_DENY_INCOMING flag is set. */ if (retval == PKT_ALIAS_ERROR || ((args->flags & IPFW_ARGS_IN) && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || (retval == PKT_ALIAS_IGNORED && (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { /* XXX - should i add some logging? */ m_free(mcl); args->m = NULL; return (IP_FW_DENY); } if (retval == PKT_ALIAS_RESPOND) mcl->m_flags |= M_SKIP_FIREWALL; mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); /* * XXX - libalias checksum offload * 'duct tape' (see above) */ if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && ip->ip_p == IPPROTO_TCP) { struct tcphdr *th; th = (struct tcphdr *)(ip + 1); - if (th->th_x2) + if (th->th_x2 & (TH_RES1 >> 8)) ldt = 1; } if (ldt) { struct tcphdr *th; struct udphdr *uh; uint16_t ip_len, cksum; ip_len = ntohs(ip->ip_len); cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); switch (ip->ip_p) { case IPPROTO_TCP: th = (struct tcphdr *)(ip + 1); /* * Maybe it was set in * libalias... */ - th->th_x2 = 0; + th->th_x2 &= ~(TH_RES1 >> 8); th->th_sum = cksum; mcl->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); break; case IPPROTO_UDP: uh = (struct udphdr *)(ip + 1); uh->uh_sum = cksum; mcl->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); break; } /* No hw checksum offloading: do it ourselves */ if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { in_delayed_cksum(mcl); mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } } args->m = mcl; return (IP_FW_NAT); } static struct cfg_nat * lookup_nat(struct nat_list *l, int nat_id) { struct cfg_nat *res; LIST_FOREACH(res, l, _next) { if (res->id == nat_id) break; } return res; } static struct cfg_nat * lookup_nat_name(struct nat_list *l, char *name) { struct cfg_nat *res; int id; char *errptr; id = strtol(name, &errptr, 10); if (id == 0 || *errptr != '\0') return (NULL); LIST_FOREACH(res, l, _next) { if (res->id == id) break; } return (res); } /* IP_FW3 configuration routines */ static void nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) { struct cfg_nat *ptr, *tcfg; int gencnt; /* * Find/create nat rule. */ IPFW_UH_WLOCK(chain); gencnt = chain->gencnt; ptr = lookup_nat_name(&chain->nat, ucfg->name); if (ptr == NULL) { IPFW_UH_WUNLOCK(chain); /* New rule: allocate and init new instance. */ ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); ptr->lib = LibAliasInit(NULL); LIST_INIT(&ptr->redir_chain); } else { /* Entry already present: temporarily unhook it. */ IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); } /* * Basic nat (re)configuration. */ ptr->id = strtol(ucfg->name, NULL, 10); /* * XXX - what if this rule doesn't nat any ip and just * redirect? * do we set aliasaddress to 0.0.0.0? */ ptr->ip = ucfg->ip; ptr->redir_cnt = ucfg->redir_cnt; ptr->mode = ucfg->mode; ptr->alias_port_lo = ucfg->alias_port_lo; ptr->alias_port_hi = ucfg->alias_port_hi; strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); LibAliasSetMode(ptr->lib, ptr->mode, ~0); LibAliasSetAddress(ptr->lib, ptr->ip); LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi); /* * Redir and LSNAT configuration. */ /* Delete old cfgs. */ del_redir_spool_cfg(ptr, &ptr->redir_chain); /* Add new entries. */ add_redir_spool_cfg((char *)(ucfg + 1), ptr); IPFW_UH_WLOCK(chain); /* Extra check to avoid race with another ipfw_nat_cfg() */ tcfg = NULL; if (gencnt != chain->gencnt) tcfg = lookup_nat_name(&chain->nat, ucfg->name); IPFW_WLOCK(chain); if (tcfg != NULL) LIST_REMOVE(tcfg, _next); LIST_INSERT_HEAD(&chain->nat, ptr, _next); IPFW_WUNLOCK(chain); chain->gencnt++; IPFW_UH_WUNLOCK(chain); if (tcfg != NULL) free_nat_instance(ptr); } /* * Creates/configure nat44 instance * Data layout (v0)(current): * Request: [ ipfw_obj_header nat44_cfg_nat .. ] * * Returns 0 on success */ static int nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; struct nat44_cfg_nat *ucfg; int id; size_t read; char *errptr; /* Check minimum header size */ if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) return (EINVAL); oh = (ipfw_obj_header *)sd->kbuf; /* Basic length checks for TLVs */ if (oh->ntlv.head.length != sizeof(oh->ntlv)) return (EINVAL); ucfg = (struct nat44_cfg_nat *)(oh + 1); /* Check if name is properly terminated and looks like number */ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) return (EINVAL); id = strtol(ucfg->name, &errptr, 10); if (id == 0 || *errptr != '\0') return (EINVAL); read = sizeof(*oh) + sizeof(*ucfg); /* Check number of redirs */ if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) return (EINVAL); nat44_config(chain, ucfg); return (0); } /* * Destroys given nat instances. * Data layout (v0)(current): * Request: [ ipfw_obj_header ] * * Returns 0 on success */ static int nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; struct cfg_nat *ptr; ipfw_obj_ntlv *ntlv; /* Check minimum header size */ if (sd->valsize < sizeof(*oh)) return (EINVAL); oh = (ipfw_obj_header *)sd->kbuf; /* Basic length checks for TLVs */ if (oh->ntlv.head.length != sizeof(oh->ntlv)) return (EINVAL); ntlv = &oh->ntlv; /* Check if name is properly terminated */ if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) return (EINVAL); IPFW_UH_WLOCK(chain); ptr = lookup_nat_name(&chain->nat, ntlv->name); if (ptr == NULL) { IPFW_UH_WUNLOCK(chain); return (ESRCH); } IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); free_nat_instance(ptr); return (0); } static void export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) { snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); ucfg->ip = ptr->ip; ucfg->redir_cnt = ptr->redir_cnt; ucfg->mode = ptr->mode; ucfg->alias_port_lo = ptr->alias_port_lo; ucfg->alias_port_hi = ptr->alias_port_hi; strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); } /* * Gets config for given nat instance * Data layout (v0)(current): * Request: [ ipfw_obj_header nat44_cfg_nat .. ] * * Returns 0 on success */ static int nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; struct nat44_cfg_nat *ucfg; struct cfg_nat *ptr; struct cfg_redir *r; struct cfg_spool *s; struct nat44_cfg_redir *ser_r; struct nat44_cfg_spool *ser_s; size_t sz; sz = sizeof(*oh) + sizeof(*ucfg); /* Check minimum header size */ if (sd->valsize < sz) return (EINVAL); oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); /* Basic length checks for TLVs */ if (oh->ntlv.head.length != sizeof(oh->ntlv)) return (EINVAL); ucfg = (struct nat44_cfg_nat *)(oh + 1); /* Check if name is properly terminated */ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) return (EINVAL); IPFW_UH_RLOCK(chain); ptr = lookup_nat_name(&chain->nat, ucfg->name); if (ptr == NULL) { IPFW_UH_RUNLOCK(chain); return (ESRCH); } export_nat_cfg(ptr, ucfg); /* Estimate memory amount */ sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat); LIST_FOREACH(r, &ptr->redir_chain, _next) { sz += sizeof(struct nat44_cfg_redir); LIST_FOREACH(s, &r->spool_chain, _next) sz += sizeof(struct nat44_cfg_spool); } ucfg->size = sz; if (sd->valsize < sz) { /* * Submitted buffer size is not enough. * WE've already filled in @ucfg structure with * relevant info including size, so we * can return. Buffer will be flushed automatically. */ IPFW_UH_RUNLOCK(chain); return (ENOMEM); } /* Size OK, let's copy data */ LIST_FOREACH(r, &ptr->redir_chain, _next) { ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, sizeof(*ser_r)); ser_r->mode = r->mode; ser_r->laddr = r->laddr; ser_r->paddr = r->paddr; ser_r->raddr = r->raddr; ser_r->lport = r->lport; ser_r->pport = r->pport; ser_r->rport = r->rport; ser_r->pport_cnt = r->pport_cnt; ser_r->rport_cnt = r->rport_cnt; ser_r->proto = r->proto; ser_r->spool_cnt = r->spool_cnt; LIST_FOREACH(s, &r->spool_chain, _next) { ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( sd, sizeof(*ser_s)); ser_s->addr = s->addr; ser_s->port = s->port; } } IPFW_UH_RUNLOCK(chain); return (0); } /* * Lists all nat44 instances currently available in kernel. * Data layout (v0)(current): * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] * * Returns 0 on success */ static int nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_lheader *olh; struct nat44_cfg_nat *ucfg; struct cfg_nat *ptr; int nat_count; /* Check minimum header size */ if (sd->valsize < sizeof(ipfw_obj_lheader)) return (EINVAL); olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); IPFW_UH_RLOCK(chain); nat_count = 0; LIST_FOREACH(ptr, &chain->nat, _next) nat_count++; olh->count = nat_count; olh->objsize = sizeof(struct nat44_cfg_nat); olh->size = sizeof(*olh) + olh->count * olh->objsize; if (sd->valsize < olh->size) { IPFW_UH_RUNLOCK(chain); return (ENOMEM); } LIST_FOREACH(ptr, &chain->nat, _next) { ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, sizeof(*ucfg)); export_nat_cfg(ptr, ucfg); } IPFW_UH_RUNLOCK(chain); return (0); } /* * Gets log for given nat instance * Data layout (v0)(current): * Request: [ ipfw_obj_header nat44_cfg_nat ] * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] * * Returns 0 on success */ static int nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; struct nat44_cfg_nat *ucfg; struct cfg_nat *ptr; void *pbuf; size_t sz; sz = sizeof(*oh) + sizeof(*ucfg); /* Check minimum header size */ if (sd->valsize < sz) return (EINVAL); oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); /* Basic length checks for TLVs */ if (oh->ntlv.head.length != sizeof(oh->ntlv)) return (EINVAL); ucfg = (struct nat44_cfg_nat *)(oh + 1); /* Check if name is properly terminated */ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) return (EINVAL); IPFW_UH_RLOCK(chain); ptr = lookup_nat_name(&chain->nat, ucfg->name); if (ptr == NULL) { IPFW_UH_RUNLOCK(chain); return (ESRCH); } if (ptr->lib->logDesc == NULL) { IPFW_UH_RUNLOCK(chain); return (ENOENT); } export_nat_cfg(ptr, ucfg); /* Estimate memory amount */ ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; if (sd->valsize < sz + sizeof(*oh)) { /* * Submitted buffer size is not enough. * WE've already filled in @ucfg structure with * relevant info including size, so we * can return. Buffer will be flushed automatically. */ IPFW_UH_RUNLOCK(chain); return (ENOMEM); } pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); IPFW_UH_RUNLOCK(chain); return (0); } static struct ipfw_sopt_handler scodes[] = { { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, }; /* * Legacy configuration routines */ struct cfg_spool_legacy { LIST_ENTRY(cfg_spool_legacy) _next; struct in_addr addr; u_short port; }; struct cfg_redir_legacy { LIST_ENTRY(cfg_redir) _next; u_int16_t mode; struct in_addr laddr; struct in_addr paddr; struct in_addr raddr; u_short lport; u_short pport; u_short rport; u_short pport_cnt; u_short rport_cnt; int proto; struct alias_link **alink; u_int16_t spool_cnt; LIST_HEAD(, cfg_spool_legacy) spool_chain; }; struct cfg_nat_legacy { LIST_ENTRY(cfg_nat_legacy) _next; int id; struct in_addr ip; char if_name[IF_NAMESIZE]; int mode; struct libalias *lib; int redir_cnt; LIST_HEAD(, cfg_redir_legacy) redir_chain; }; static int ipfw_nat_cfg(struct sockopt *sopt) { struct cfg_nat_legacy *cfg; struct nat44_cfg_nat *ucfg; struct cfg_redir_legacy *rdir; struct nat44_cfg_redir *urdir; char *buf; size_t len, len2; int error, i; len = sopt->sopt_valsize; len2 = len + 128; /* * Allocate 2x buffer to store converted structures. * new redir_cfg has shrunk, so we're sure that * new buffer size is enough. */ buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); if (error != 0) goto out; cfg = (struct cfg_nat_legacy *)buf; if (cfg->id < 0) { error = EINVAL; goto out; } ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); ucfg->ip = cfg->ip; ucfg->mode = cfg->mode; ucfg->redir_cnt = cfg->redir_cnt; if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { error = EINVAL; goto out; } urdir = (struct nat44_cfg_redir *)(ucfg + 1); rdir = (struct cfg_redir_legacy *)(cfg + 1); for (i = 0; i < cfg->redir_cnt; i++) { urdir->mode = rdir->mode; urdir->laddr = rdir->laddr; urdir->paddr = rdir->paddr; urdir->raddr = rdir->raddr; urdir->lport = rdir->lport; urdir->pport = rdir->pport; urdir->rport = rdir->rport; urdir->pport_cnt = rdir->pport_cnt; urdir->rport_cnt = rdir->rport_cnt; urdir->proto = rdir->proto; urdir->spool_cnt = rdir->spool_cnt; urdir++; rdir++; } nat44_config(&V_layer3_chain, ucfg); out: free(buf, M_TEMP); return (error); } static int ipfw_nat_del(struct sockopt *sopt) { struct cfg_nat *ptr; struct ip_fw_chain *chain = &V_layer3_chain; int i; sooptcopyin(sopt, &i, sizeof i, sizeof i); /* XXX validate i */ IPFW_UH_WLOCK(chain); ptr = lookup_nat(&chain->nat, i); if (ptr == NULL) { IPFW_UH_WUNLOCK(chain); return (EINVAL); } IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); flush_nat_ptrs(chain, i); IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); free_nat_instance(ptr); return (0); } static int ipfw_nat_get_cfg(struct sockopt *sopt) { struct ip_fw_chain *chain = &V_layer3_chain; struct cfg_nat *n; struct cfg_nat_legacy *ucfg; struct cfg_redir *r; struct cfg_spool *s; struct cfg_redir_legacy *ser_r; struct cfg_spool_legacy *ser_s; char *data; int gencnt, nat_cnt, len, error; nat_cnt = 0; len = sizeof(nat_cnt); IPFW_UH_RLOCK(chain); retry: gencnt = chain->gencnt; /* Estimate memory amount */ LIST_FOREACH(n, &chain->nat, _next) { nat_cnt++; len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) len += sizeof(struct cfg_spool_legacy); } } IPFW_UH_RUNLOCK(chain); data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); bcopy(&nat_cnt, data, sizeof(nat_cnt)); nat_cnt = 0; len = sizeof(nat_cnt); IPFW_UH_RLOCK(chain); if (gencnt != chain->gencnt) { free(data, M_TEMP); goto retry; } /* Serialize all the data. */ LIST_FOREACH(n, &chain->nat, _next) { ucfg = (struct cfg_nat_legacy *)&data[len]; ucfg->id = n->id; ucfg->ip = n->ip; ucfg->redir_cnt = n->redir_cnt; ucfg->mode = n->mode; strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { ser_r = (struct cfg_redir_legacy *)&data[len]; ser_r->mode = r->mode; ser_r->laddr = r->laddr; ser_r->paddr = r->paddr; ser_r->raddr = r->raddr; ser_r->lport = r->lport; ser_r->pport = r->pport; ser_r->rport = r->rport; ser_r->pport_cnt = r->pport_cnt; ser_r->rport_cnt = r->rport_cnt; ser_r->proto = r->proto; ser_r->spool_cnt = r->spool_cnt; len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) { ser_s = (struct cfg_spool_legacy *)&data[len]; ser_s->addr = s->addr; ser_s->port = s->port; len += sizeof(struct cfg_spool_legacy); } } } IPFW_UH_RUNLOCK(chain); error = sooptcopyout(sopt, data, len); free(data, M_TEMP); return (error); } static int ipfw_nat_get_log(struct sockopt *sopt) { uint8_t *data; struct cfg_nat *ptr; int i, size; struct ip_fw_chain *chain; IPFW_RLOCK_TRACKER; chain = &V_layer3_chain; IPFW_RLOCK(chain); /* one pass to count, one to copy the data */ i = 0; LIST_FOREACH(ptr, &chain->nat, _next) { if (ptr->lib->logDesc == NULL) continue; i++; } size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); if (data == NULL) { IPFW_RUNLOCK(chain); return (ENOSPC); } i = 0; LIST_FOREACH(ptr, &chain->nat, _next) { if (ptr->lib->logDesc == NULL) continue; bcopy(&ptr->id, &data[i], sizeof(int)); i += sizeof(int); bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); i += LIBALIAS_BUF_SIZE; } IPFW_RUNLOCK(chain); sooptcopyout(sopt, data, size); free(data, M_IPFW); return(0); } static int vnet_ipfw_nat_init(const void *arg __unused) { V_ipfw_nat_ready = 1; return (0); } static int vnet_ipfw_nat_uninit(const void *arg __unused) { struct cfg_nat *ptr, *ptr_temp; struct ip_fw_chain *chain; chain = &V_layer3_chain; IPFW_WLOCK(chain); V_ipfw_nat_ready = 0; LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { LIST_REMOVE(ptr, _next); free_nat_instance(ptr); } flush_nat_ptrs(chain, -1 /* flush all */); IPFW_WUNLOCK(chain); return (0); } static void ipfw_nat_init(void) { /* init ipfw hooks */ ipfw_nat_ptr = ipfw_nat; lookup_nat_ptr = lookup_nat; ipfw_nat_cfg_ptr = ipfw_nat_cfg; ipfw_nat_del_ptr = ipfw_nat_del; ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; ipfw_nat_get_log_ptr = ipfw_nat_get_log; IPFW_ADD_SOPT_HANDLER(1, scodes); ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, NULL, EVENTHANDLER_PRI_ANY); } static void ipfw_nat_destroy(void) { EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); /* deregister ipfw_nat */ IPFW_DEL_SOPT_HANDLER(1, scodes); ipfw_nat_ptr = NULL; lookup_nat_ptr = NULL; ipfw_nat_cfg_ptr = NULL; ipfw_nat_del_ptr = NULL; ipfw_nat_get_cfg_ptr = NULL; ipfw_nat_get_log_ptr = NULL; } static int ipfw_nat_modevent(module_t mod, int type, void *unused) { int err = 0; switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: break; default: return EOPNOTSUPP; break; } return err; } static moduledata_t ipfw_nat_mod = { "ipfw_nat", ipfw_nat_modevent, 0 }; /* Define startup order. */ #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); MODULE_VERSION(ipfw_nat, 1); SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, ipfw_nat_init, NULL); VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_init, NULL); SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, ipfw_nat_destroy, NULL); VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); /* end of file */