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.
53 Initial development: April, 1998 (cjm)
58 #include <sys/param.h>
59 #include <sys/ctype.h>
60 #include <sys/libkern.h>
61 #include <sys/limits.h>
63 #include <sys/types.h>
71 #include <netinet/tcp.h>
74 #include <netinet/libalias/alias.h>
75 #include <netinet/libalias/alias_local.h>
76 #include <netinet/libalias/alias_mod.h>
78 #include <arpa/inet.h>
79 #include "alias.h" /* Public API functions for libalias */
80 #include "alias_local.h" /* Functions used by alias*.c */
88 * A linked list of arbitrary length, based on struct proxy_entry is
89 * used to store proxy rules.
93 #define PROXY_TYPE_ENCODE_NONE 1
94 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
95 #define PROXY_TYPE_ENCODE_IPHDR 3
102 struct in_addr server_addr;
104 struct in_addr src_addr;
105 struct in_addr src_mask;
107 struct in_addr dst_addr;
108 struct in_addr dst_mask;
110 struct proxy_entry *next;
111 struct proxy_entry *last;
118 /* Local (static) functions:
120 IpMask() -- Utility function for creating IP
121 masks from integer (1-32) specification.
122 IpAddr() -- Utility function for converting string
124 IpPort() -- Utility function for converting string
126 RuleAdd() -- Adds an element to the rule list.
127 RuleDelete() -- Removes an element from the rule list.
128 RuleNumberDelete() -- Removes all elements from the rule list
129 having a certain rule number.
130 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
132 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
133 destination of a proxied IP packet
136 static int IpMask(int, struct in_addr *);
137 static int IpAddr(char *, struct in_addr *);
138 static int IpPort(char *, int, int *);
139 static void RuleAdd(struct libalias *la, struct proxy_entry *);
140 static void RuleDelete(struct proxy_entry *);
141 static int RuleNumberDelete(struct libalias *la, int);
142 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
143 static void ProxyEncodeIpHeader(struct ip *, int);
146 IpMask(int nbits, struct in_addr *mask)
151 if (nbits < 0 || nbits > 32)
155 for (i = 0; i < nbits; i++)
156 imask = (imask >> 1) + 0x80000000;
157 mask->s_addr = htonl(imask);
163 IpAddr(char *s, struct in_addr *addr)
165 if (inet_aton(s, addr) == 0)
172 IpPort(char *s, int proto, int *port)
176 n = sscanf(s, "%d", port);
178 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
182 if (proto == IPPROTO_TCP)
183 se = getservbyname(s, "tcp");
184 else if (proto == IPPROTO_UDP)
185 se = getservbyname(s, "udp");
192 *port = (u_int) ntohs(se->s_port);
201 RuleAdd(struct libalias *la, struct proxy_entry *entry)
204 struct proxy_entry *ptr;
205 struct proxy_entry *ptr_last;
207 LIBALIAS_LOCK_ASSERT(la);
210 if (la->proxyList == NULL) {
211 la->proxyList = entry;
217 rule_index = entry->rule_index;
220 while (ptr != NULL) {
221 if (ptr->rule_index >= rule_index) {
222 if (ptr_last == NULL) {
223 entry->next = la->proxyList;
225 la->proxyList->last = entry;
226 la->proxyList = entry;
229 ptr_last->next = entry;
231 entry->last = ptr->last;
239 ptr_last->next = entry;
240 entry->last = ptr_last;
245 RuleDelete(struct proxy_entry *entry)
250 LIBALIAS_LOCK_ASSERT(la);
251 if (entry->last != NULL)
252 entry->last->next = entry->next;
254 la->proxyList = entry->next;
256 if (entry->next != NULL)
257 entry->next->last = entry->last;
263 RuleNumberDelete(struct libalias *la, int rule_index)
266 struct proxy_entry *ptr;
268 LIBALIAS_LOCK_ASSERT(la);
271 while (ptr != NULL) {
272 struct proxy_entry *ptr_next;
274 ptr_next = ptr->next;
275 if (ptr->rule_index == rule_index) {
286 ProxyEncodeTcpStream(struct alias_link *lnk,
293 char addrbuf[INET_ADDRSTRLEN];
295 /* Compute pointer to tcp header */
296 tc = (struct tcphdr *)ip_next(pip);
298 /* Don't modify if once already modified */
300 if (GetAckModified(lnk))
303 /* Translate destination address and port to string form */
304 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
305 inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)),
306 (u_int) ntohs(GetProxyPort(lnk)));
308 /* Pad string out to a multiple of two in length */
309 slen = strlen(buffer);
312 strcat(buffer, " \n");
316 strcat(buffer, "\n");
320 /* Check for packet overflow */
321 if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
324 /* Shift existing TCP data and insert destination string */
330 hlen = (pip->ip_hl + tc->th_off) << 2;
331 dlen = ntohs(pip->ip_len) - hlen;
333 /* Modify first packet that has data in it */
341 bcopy(p, p + slen, dlen);
342 memcpy(p, buffer, slen);
345 /* Save information about modfied sequence number */
350 tc = (struct tcphdr *)ip_next(pip);
351 delta = GetDeltaSeqOut(tc->th_seq, lnk);
352 AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
356 /* Update IP header packet length and checksum */
360 accumulate = pip->ip_len;
361 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
362 accumulate -= pip->ip_len;
364 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
367 /* Update TCP checksum, Use TcpChecksum since so many things have
374 tc->th_sum = TcpChecksum(pip);
379 ProxyEncodeIpHeader(struct ip *pip,
382 #define OPTION_LEN_BYTES 8
383 #define OPTION_LEN_INT16 4
384 #define OPTION_LEN_INT32 2
385 u_char option[OPTION_LEN_BYTES];
387 #ifdef LIBALIAS_DEBUG
388 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
389 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
394 /* Check to see that there is room to add an IP option */
395 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
398 /* Build option and copy into packet */
403 ptr = (u_char *) pip;
405 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
407 option[0] = 0x64; /* class: 3 (reserved), option 4 */
408 option[1] = OPTION_LEN_BYTES;
410 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
412 tc = (struct tcphdr *)ip_next(pip);
413 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
415 memcpy(ptr, option, 8);
418 /* Update checksum, header length and packet length */
424 sptr = (u_short *) option;
426 for (i = 0; i < OPTION_LEN_INT16; i++)
427 accumulate -= *(sptr++);
429 sptr = (u_short *) pip;
431 pip->ip_hl += OPTION_LEN_INT32;
434 accumulate += pip->ip_len;
435 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
436 accumulate -= pip->ip_len;
438 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
440 #undef OPTION_LEN_BYTES
441 #undef OPTION_LEN_INT16
442 #undef OPTION_LEN_INT32
443 #ifdef LIBALIAS_DEBUG
444 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
445 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
449 /* Functions by other packet alias source files
451 ProxyCheck() -- Checks whether an outgoing packet should
453 ProxyModify() -- Encodes the original destination address/port
454 for a packet which is to be redirected to
459 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
460 u_short * proxy_server_port, struct in_addr src_addr,
461 struct in_addr dst_addr, u_short dst_port, u_char ip_p)
463 struct proxy_entry *ptr;
465 LIBALIAS_LOCK_ASSERT(la);
468 while (ptr != NULL) {
471 proxy_port = ptr->proxy_port;
472 if ((dst_port == proxy_port || proxy_port == 0)
473 && ip_p == ptr->proto
474 && src_addr.s_addr != ptr->server_addr.s_addr) {
475 struct in_addr src_addr_masked;
476 struct in_addr dst_addr_masked;
478 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
479 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
481 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
482 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
483 if ((*proxy_server_port = ptr->server_port) == 0)
484 *proxy_server_port = dst_port;
485 *proxy_server_addr = ptr->server_addr;
486 return (ptr->proxy_type);
496 ProxyModify(struct libalias *la, struct alias_link *lnk,
502 LIBALIAS_LOCK_ASSERT(la);
505 switch (proxy_type) {
506 case PROXY_TYPE_ENCODE_IPHDR:
507 ProxyEncodeIpHeader(pip, maxpacketsize);
510 case PROXY_TYPE_ENCODE_TCPSTREAM:
511 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
521 LibAliasProxyRule(struct libalias *la, const char *cmd)
524 * This function takes command strings of the form:
526 * server <addr>[:<port>]
532 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
534 * delete <rule number>
536 * Subfields can be in arbitrary order. Port numbers and addresses
537 * must be in either numeric or symbolic form. An optional rule number
538 * is used to control the order in which rules are searched. If two
539 * rules have the same number, then search order cannot be guaranteed,
540 * and the rules should be disjoint. If no rule number is specified,
541 * then 0 is used, and group 0 rules are always checked before any
550 char str_port[sizeof(buffer)];
551 char str_server_port[sizeof(buffer)];
559 struct in_addr server_addr;
560 struct in_addr src_addr, src_mask;
561 struct in_addr dst_addr, dst_mask;
562 struct proxy_entry *proxy_entry;
566 /* Copy command line into a buffer */
567 cmd += strspn(cmd, " \t");
568 cmd_len = strlen(cmd);
569 if (cmd_len > (int)(sizeof(buffer) - 1)) {
575 /* Convert to lower case */
576 len = strlen(buffer);
577 for (i = 0; i < len; i++)
578 buffer[i] = tolower((unsigned char)buffer[i]);
580 /* Set default proxy type */
582 /* Set up default values */
584 proxy_type = PROXY_TYPE_ENCODE_NONE;
587 server_addr.s_addr = 0;
590 IpMask(0, &src_mask);
592 IpMask(0, &dst_mask);
595 str_server_port[0] = 0;
597 /* Parse command string with state machine */
598 #define STATE_READ_KEYWORD 0
599 #define STATE_READ_TYPE 1
600 #define STATE_READ_PORT 2
601 #define STATE_READ_SERVER 3
602 #define STATE_READ_RULE 4
603 #define STATE_READ_DELETE 5
604 #define STATE_READ_PROTO 6
605 #define STATE_READ_SRC 7
606 #define STATE_READ_DST 8
607 state = STATE_READ_KEYWORD;
608 token = strsep(&res, " \t");
610 while (token != NULL) {
613 case STATE_READ_KEYWORD:
614 if (strcmp(token, "type") == 0)
615 state = STATE_READ_TYPE;
616 else if (strcmp(token, "port") == 0)
617 state = STATE_READ_PORT;
618 else if (strcmp(token, "server") == 0)
619 state = STATE_READ_SERVER;
620 else if (strcmp(token, "rule") == 0)
621 state = STATE_READ_RULE;
622 else if (strcmp(token, "delete") == 0)
623 state = STATE_READ_DELETE;
624 else if (strcmp(token, "proto") == 0)
625 state = STATE_READ_PROTO;
626 else if (strcmp(token, "src") == 0)
627 state = STATE_READ_SRC;
628 else if (strcmp(token, "dst") == 0)
629 state = STATE_READ_DST;
636 case STATE_READ_TYPE:
637 if (strcmp(token, "encode_ip_hdr") == 0)
638 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
639 else if (strcmp(token, "encode_tcp_stream") == 0)
640 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
641 else if (strcmp(token, "no_encode") == 0)
642 proxy_type = PROXY_TYPE_ENCODE_NONE;
647 state = STATE_READ_KEYWORD;
650 case STATE_READ_PORT:
651 strcpy(str_port, token);
652 state = STATE_READ_KEYWORD;
655 case STATE_READ_SERVER:
659 char s[sizeof(buffer)];
662 while (*p != ':' && *p != 0)
666 err = IpAddr(token, &server_addr);
674 n = sscanf(token, "%s %s", s, str_server_port);
680 err = IpAddr(s, &server_addr);
687 state = STATE_READ_KEYWORD;
690 case STATE_READ_RULE:
691 n = sscanf(token, "%d", &rule_index);
692 if (n != 1 || rule_index < 0) {
696 state = STATE_READ_KEYWORD;
699 case STATE_READ_DELETE:
704 if (token_count != 2) {
709 n = sscanf(token, "%d", &rule_to_delete);
714 err = RuleNumberDelete(la, rule_to_delete);
722 case STATE_READ_PROTO:
723 if (strcmp(token, "tcp") == 0)
725 else if (strcmp(token, "udp") == 0)
731 state = STATE_READ_KEYWORD;
743 while (*p != '/' && *p != 0)
748 err = IpAddr(token, &addr);
755 char s[sizeof(buffer)];
758 n = sscanf(token, "%s %d", s, &nbits);
764 err = IpAddr(s, &addr);
770 err = IpMask(nbits, &mask);
777 if (state == STATE_READ_SRC) {
785 state = STATE_READ_KEYWORD;
795 token = strsep(&res, " \t");
796 } while (token != NULL && !*token);
798 #undef STATE_READ_KEYWORD
799 #undef STATE_READ_TYPE
800 #undef STATE_READ_PORT
801 #undef STATE_READ_SERVER
802 #undef STATE_READ_RULE
803 #undef STATE_READ_DELETE
804 #undef STATE_READ_PROTO
805 #undef STATE_READ_SRC
806 #undef STATE_READ_DST
808 /* Convert port strings to numbers. This needs to be done after
809 the string is parsed, because the prototype might not be designated
810 before the ports (which might be symbolic entries in /etc/services) */
812 if (strlen(str_port) != 0) {
815 err = IpPort(str_port, proto, &proxy_port);
824 if (strlen(str_server_port) != 0) {
827 err = IpPort(str_server_port, proto, &server_port);
836 /* Check that at least the server address has been defined */
837 if (server_addr.s_addr == 0) {
842 /* Add to linked list */
843 proxy_entry = malloc(sizeof(struct proxy_entry));
844 if (proxy_entry == NULL) {
849 proxy_entry->proxy_type = proxy_type;
850 proxy_entry->rule_index = rule_index;
851 proxy_entry->proto = proto;
852 proxy_entry->proxy_port = htons(proxy_port);
853 proxy_entry->server_port = htons(server_port);
854 proxy_entry->server_addr = server_addr;
855 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
856 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
857 proxy_entry->src_mask = src_mask;
858 proxy_entry->dst_mask = dst_mask;
860 RuleAdd(la, proxy_entry);