2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 /* file: alias_proxy.c
34 This file encapsulates special operations related to transparent
35 proxy redirection. This is where packets with a particular destination,
36 usually tcp port 80, are redirected to a proxy server.
38 When packets are proxied, the destination address and port are
39 modified. In certain cases, it is necessary to somehow encode
40 the original address/port info into the packet. Two methods are
41 presently supported: addition of a [DEST addr port] string at the
42 beginning of a tcp stream, or inclusion of an optional field
45 There is one public API function:
47 PacketAliasProxyRule() -- Adds and deletes proxy
50 Rules are stored in a linear linked list, so lookup efficiency
51 won't be too good for large lists.
54 Initial development: April, 1998 (cjm)
60 #include <sys/param.h>
61 #include <sys/ctype.h>
62 #include <sys/libkern.h>
63 #include <sys/limits.h>
65 #include <sys/types.h>
73 #include <netinet/tcp.h>
76 #include <netinet/libalias/alias.h>
77 #include <netinet/libalias/alias_local.h>
78 #include <netinet/libalias/alias_mod.h>
80 #include <arpa/inet.h>
81 #include "alias.h" /* Public API functions for libalias */
82 #include "alias_local.h" /* Functions used by alias*.c */
90 * A linked list of arbitrary length, based on struct proxy_entry is
91 * used to store proxy rules.
95 #define PROXY_TYPE_ENCODE_NONE 1
96 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
97 #define PROXY_TYPE_ENCODE_IPHDR 3
104 struct in_addr server_addr;
106 struct in_addr src_addr;
107 struct in_addr src_mask;
109 struct in_addr dst_addr;
110 struct in_addr dst_mask;
112 struct proxy_entry *next;
113 struct proxy_entry *last;
124 /* Local (static) functions:
126 IpMask() -- Utility function for creating IP
127 masks from integer (1-32) specification.
128 IpAddr() -- Utility function for converting string
130 IpPort() -- Utility function for converting string
132 RuleAdd() -- Adds an element to the rule list.
133 RuleDelete() -- Removes an element from the rule list.
134 RuleNumberDelete() -- Removes all elements from the rule list
135 having a certain rule number.
136 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
138 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
139 destination of a proxied IP packet
142 static int IpMask(int, struct in_addr *);
143 static int IpAddr(char *, struct in_addr *);
144 static int IpPort(char *, int, int *);
145 static void RuleAdd(struct libalias *la, struct proxy_entry *);
146 static void RuleDelete(struct proxy_entry *);
147 static int RuleNumberDelete(struct libalias *la, int);
148 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
149 static void ProxyEncodeIpHeader(struct ip *, int);
152 IpMask(int nbits, struct in_addr *mask)
157 if (nbits < 0 || nbits > 32)
161 for (i = 0; i < nbits; i++)
162 imask = (imask >> 1) + 0x80000000;
163 mask->s_addr = htonl(imask);
169 IpAddr(char *s, struct in_addr *addr)
171 if (inet_aton(s, addr) == 0)
178 IpPort(char *s, int proto, int *port)
182 n = sscanf(s, "%d", port);
184 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
188 if (proto == IPPROTO_TCP)
189 se = getservbyname(s, "tcp");
190 else if (proto == IPPROTO_UDP)
191 se = getservbyname(s, "udp");
198 *port = (u_int) ntohs(se->s_port);
207 RuleAdd(struct libalias *la, struct proxy_entry *entry)
210 struct proxy_entry *ptr;
211 struct proxy_entry *ptr_last;
213 LIBALIAS_LOCK_ASSERT(la);
216 if (la->proxyList == NULL) {
217 la->proxyList = entry;
223 rule_index = entry->rule_index;
226 while (ptr != NULL) {
227 if (ptr->rule_index >= rule_index) {
228 if (ptr_last == NULL) {
229 entry->next = la->proxyList;
231 la->proxyList->last = entry;
232 la->proxyList = entry;
235 ptr_last->next = entry;
237 entry->last = ptr->last;
245 ptr_last->next = entry;
246 entry->last = ptr_last;
251 RuleDelete(struct proxy_entry *entry)
256 LIBALIAS_LOCK_ASSERT(la);
257 if (entry->last != NULL)
258 entry->last->next = entry->next;
260 la->proxyList = entry->next;
262 if (entry->next != NULL)
263 entry->next->last = entry->last;
269 RuleNumberDelete(struct libalias *la, int rule_index)
272 struct proxy_entry *ptr;
274 LIBALIAS_LOCK_ASSERT(la);
277 while (ptr != NULL) {
278 struct proxy_entry *ptr_next;
280 ptr_next = ptr->next;
281 if (ptr->rule_index == rule_index) {
292 ProxyEncodeTcpStream(struct alias_link *lnk,
299 char addrbuf[INET_ADDRSTRLEN];
301 /* Compute pointer to tcp header */
302 tc = (struct tcphdr *)ip_next(pip);
304 /* Don't modify if once already modified */
306 if (GetAckModified(lnk))
309 /* Translate destination address and port to string form */
310 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
311 inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)),
312 (u_int) ntohs(GetProxyPort(lnk)));
314 /* Pad string out to a multiple of two in length */
315 slen = strlen(buffer);
318 strcat(buffer, " \n");
322 strcat(buffer, "\n");
326 /* Check for packet overflow */
327 if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
330 /* Shift existing TCP data and insert destination string */
336 hlen = (pip->ip_hl + tc->th_off) << 2;
337 dlen = ntohs(pip->ip_len) - hlen;
339 /* Modify first packet that has data in it */
347 bcopy(p, p + slen, dlen);
348 memcpy(p, buffer, slen);
351 /* Save information about modfied sequence number */
356 tc = (struct tcphdr *)ip_next(pip);
357 delta = GetDeltaSeqOut(tc->th_seq, lnk);
358 AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
362 /* Update IP header packet length and checksum */
366 accumulate = pip->ip_len;
367 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
368 accumulate -= pip->ip_len;
370 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
373 /* Update TCP checksum, Use TcpChecksum since so many things have
380 tc->th_sum = TcpChecksum(pip);
385 ProxyEncodeIpHeader(struct ip *pip,
388 #define OPTION_LEN_BYTES 8
389 #define OPTION_LEN_INT16 4
390 #define OPTION_LEN_INT32 2
391 u_char option[OPTION_LEN_BYTES];
393 #ifdef LIBALIAS_DEBUG
394 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
395 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
400 /* Check to see that there is room to add an IP option */
401 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
404 /* Build option and copy into packet */
409 ptr = (u_char *) pip;
411 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
413 option[0] = 0x64; /* class: 3 (reserved), option 4 */
414 option[1] = OPTION_LEN_BYTES;
416 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
418 tc = (struct tcphdr *)ip_next(pip);
419 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
421 memcpy(ptr, option, 8);
424 /* Update checksum, header length and packet length */
430 sptr = (u_short *) option;
432 for (i = 0; i < OPTION_LEN_INT16; i++)
433 accumulate -= *(sptr++);
435 sptr = (u_short *) pip;
437 pip->ip_hl += OPTION_LEN_INT32;
440 accumulate += pip->ip_len;
441 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
442 accumulate -= pip->ip_len;
444 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
446 #undef OPTION_LEN_BYTES
447 #undef OPTION_LEN_INT16
448 #undef OPTION_LEN_INT32
449 #ifdef LIBALIAS_DEBUG
450 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
451 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
456 /* Functions by other packet alias source files
458 ProxyCheck() -- Checks whether an outgoing packet should
460 ProxyModify() -- Encodes the original destination address/port
461 for a packet which is to be redirected to
466 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
467 u_short * proxy_server_port, struct in_addr src_addr,
468 struct in_addr dst_addr, u_short dst_port, u_char ip_p)
470 struct proxy_entry *ptr;
472 LIBALIAS_LOCK_ASSERT(la);
475 while (ptr != NULL) {
478 proxy_port = ptr->proxy_port;
479 if ((dst_port == proxy_port || proxy_port == 0)
480 && ip_p == ptr->proto
481 && src_addr.s_addr != ptr->server_addr.s_addr) {
482 struct in_addr src_addr_masked;
483 struct in_addr dst_addr_masked;
485 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
486 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
488 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
489 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
490 if ((*proxy_server_port = ptr->server_port) == 0)
491 *proxy_server_port = dst_port;
492 *proxy_server_addr = ptr->server_addr;
493 return (ptr->proxy_type);
503 ProxyModify(struct libalias *la, struct alias_link *lnk,
509 LIBALIAS_LOCK_ASSERT(la);
512 switch (proxy_type) {
513 case PROXY_TYPE_ENCODE_IPHDR:
514 ProxyEncodeIpHeader(pip, maxpacketsize);
517 case PROXY_TYPE_ENCODE_TCPSTREAM:
518 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
529 LibAliasProxyRule(struct libalias *la, const char *cmd)
532 * This function takes command strings of the form:
534 * server <addr>[:<port>]
540 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
542 * delete <rule number>
544 * Subfields can be in arbitrary order. Port numbers and addresses
545 * must be in either numeric or symbolic form. An optional rule number
546 * is used to control the order in which rules are searched. If two
547 * rules have the same number, then search order cannot be guaranteed,
548 * and the rules should be disjoint. If no rule number is specified,
549 * then 0 is used, and group 0 rules are always checked before any
558 char str_port[sizeof(buffer)];
559 char str_server_port[sizeof(buffer)];
567 struct in_addr server_addr;
568 struct in_addr src_addr, src_mask;
569 struct in_addr dst_addr, dst_mask;
570 struct proxy_entry *proxy_entry;
574 /* Copy command line into a buffer */
575 cmd += strspn(cmd, " \t");
576 cmd_len = strlen(cmd);
577 if (cmd_len > (int)(sizeof(buffer) - 1)) {
583 /* Convert to lower case */
584 len = strlen(buffer);
585 for (i = 0; i < len; i++)
586 buffer[i] = tolower((unsigned char)buffer[i]);
588 /* Set default proxy type */
590 /* Set up default values */
592 proxy_type = PROXY_TYPE_ENCODE_NONE;
595 server_addr.s_addr = 0;
598 IpMask(0, &src_mask);
600 IpMask(0, &dst_mask);
603 str_server_port[0] = 0;
605 /* Parse command string with state machine */
606 #define STATE_READ_KEYWORD 0
607 #define STATE_READ_TYPE 1
608 #define STATE_READ_PORT 2
609 #define STATE_READ_SERVER 3
610 #define STATE_READ_RULE 4
611 #define STATE_READ_DELETE 5
612 #define STATE_READ_PROTO 6
613 #define STATE_READ_SRC 7
614 #define STATE_READ_DST 8
615 state = STATE_READ_KEYWORD;
616 token = strsep(&res, " \t");
618 while (token != NULL) {
621 case STATE_READ_KEYWORD:
622 if (strcmp(token, "type") == 0)
623 state = STATE_READ_TYPE;
624 else if (strcmp(token, "port") == 0)
625 state = STATE_READ_PORT;
626 else if (strcmp(token, "server") == 0)
627 state = STATE_READ_SERVER;
628 else if (strcmp(token, "rule") == 0)
629 state = STATE_READ_RULE;
630 else if (strcmp(token, "delete") == 0)
631 state = STATE_READ_DELETE;
632 else if (strcmp(token, "proto") == 0)
633 state = STATE_READ_PROTO;
634 else if (strcmp(token, "src") == 0)
635 state = STATE_READ_SRC;
636 else if (strcmp(token, "dst") == 0)
637 state = STATE_READ_DST;
644 case STATE_READ_TYPE:
645 if (strcmp(token, "encode_ip_hdr") == 0)
646 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
647 else if (strcmp(token, "encode_tcp_stream") == 0)
648 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
649 else if (strcmp(token, "no_encode") == 0)
650 proxy_type = PROXY_TYPE_ENCODE_NONE;
655 state = STATE_READ_KEYWORD;
658 case STATE_READ_PORT:
659 strcpy(str_port, token);
660 state = STATE_READ_KEYWORD;
663 case STATE_READ_SERVER:
667 char s[sizeof(buffer)];
670 while (*p != ':' && *p != 0)
674 err = IpAddr(token, &server_addr);
682 n = sscanf(token, "%s %s", s, str_server_port);
688 err = IpAddr(s, &server_addr);
695 state = STATE_READ_KEYWORD;
698 case STATE_READ_RULE:
699 n = sscanf(token, "%d", &rule_index);
700 if (n != 1 || rule_index < 0) {
704 state = STATE_READ_KEYWORD;
707 case STATE_READ_DELETE:
712 if (token_count != 2) {
717 n = sscanf(token, "%d", &rule_to_delete);
722 err = RuleNumberDelete(la, rule_to_delete);
730 case STATE_READ_PROTO:
731 if (strcmp(token, "tcp") == 0)
733 else if (strcmp(token, "udp") == 0)
739 state = STATE_READ_KEYWORD;
751 while (*p != '/' && *p != 0)
756 err = IpAddr(token, &addr);
763 char s[sizeof(buffer)];
766 n = sscanf(token, "%s %d", s, &nbits);
772 err = IpAddr(s, &addr);
778 err = IpMask(nbits, &mask);
785 if (state == STATE_READ_SRC) {
793 state = STATE_READ_KEYWORD;
803 token = strsep(&res, " \t");
804 } while (token != NULL && !*token);
806 #undef STATE_READ_KEYWORD
807 #undef STATE_READ_TYPE
808 #undef STATE_READ_PORT
809 #undef STATE_READ_SERVER
810 #undef STATE_READ_RULE
811 #undef STATE_READ_DELETE
812 #undef STATE_READ_PROTO
813 #undef STATE_READ_SRC
814 #undef STATE_READ_DST
816 /* Convert port strings to numbers. This needs to be done after
817 the string is parsed, because the prototype might not be designated
818 before the ports (which might be symbolic entries in /etc/services) */
820 if (strlen(str_port) != 0) {
823 err = IpPort(str_port, proto, &proxy_port);
832 if (strlen(str_server_port) != 0) {
835 err = IpPort(str_server_port, proto, &server_port);
844 /* Check that at least the server address has been defined */
845 if (server_addr.s_addr == 0) {
850 /* Add to linked list */
851 proxy_entry = malloc(sizeof(struct proxy_entry));
852 if (proxy_entry == NULL) {
857 proxy_entry->proxy_type = proxy_type;
858 proxy_entry->rule_index = rule_index;
859 proxy_entry->proto = proto;
860 proxy_entry->proxy_port = htons(proxy_port);
861 proxy_entry->server_port = htons(server_port);
862 proxy_entry->server_addr = server_addr;
863 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
864 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
865 proxy_entry->src_mask = src_mask;
866 proxy_entry->dst_mask = dst_mask;
868 RuleAdd(la, proxy_entry);