2 * SPDX-License-Identifier: BSD-2-Clause
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>
80 #include "alias.h" /* Public API functions for libalias */
81 #include "alias_local.h" /* Functions used by alias*.c */
89 * A linked list of arbitrary length, based on struct proxy_entry is
90 * used to store proxy rules.
94 #define PROXY_TYPE_ENCODE_NONE 1
95 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
96 #define PROXY_TYPE_ENCODE_IPHDR 3
103 struct in_addr server_addr;
105 struct in_addr src_addr;
106 struct in_addr src_mask;
108 struct in_addr dst_addr;
109 struct in_addr dst_mask;
111 struct proxy_entry *next;
112 struct proxy_entry *last;
119 /* Local (static) functions:
121 IpMask() -- Utility function for creating IP
122 masks from integer (1-32) specification.
123 IpAddr() -- Utility function for converting string
125 IpPort() -- Utility function for converting string
127 RuleAdd() -- Adds an element to the rule list.
128 RuleDelete() -- Removes an element from the rule list.
129 RuleNumberDelete() -- Removes all elements from the rule list
130 having a certain rule number.
131 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
133 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
134 destination of a proxied IP packet
137 static int IpMask(int, struct in_addr *);
138 static int IpAddr(char *, struct in_addr *);
139 static int IpPort(char *, int, int *);
140 static void RuleAdd(struct libalias *la, struct proxy_entry *);
141 static void RuleDelete(struct proxy_entry *);
142 static int RuleNumberDelete(struct libalias *la, int);
143 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
144 static void ProxyEncodeIpHeader(struct ip *, int);
147 IpMask(int nbits, struct in_addr *mask)
152 if (nbits < 0 || nbits > 32)
156 for (i = 0; i < nbits; i++)
157 imask = (imask >> 1) + 0x80000000;
158 mask->s_addr = htonl(imask);
164 IpAddr(char *s, struct in_addr *addr)
166 if (inet_aton(s, addr) == 0)
173 IpPort(char *s, int proto, int *port)
177 n = sscanf(s, "%d", port);
179 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
183 if (proto == IPPROTO_TCP)
184 se = getservbyname(s, "tcp");
185 else if (proto == IPPROTO_UDP)
186 se = getservbyname(s, "udp");
193 *port = (u_int)ntohs(se->s_port);
202 RuleAdd(struct libalias *la, struct proxy_entry *entry)
205 struct proxy_entry *ptr;
206 struct proxy_entry *ptr_last;
208 LIBALIAS_LOCK_ASSERT(la);
211 if (la->proxyList == NULL) {
212 la->proxyList = entry;
218 rule_index = entry->rule_index;
221 while (ptr != NULL) {
222 if (ptr->rule_index >= rule_index) {
223 if (ptr_last == NULL) {
224 entry->next = la->proxyList;
226 la->proxyList->last = entry;
227 la->proxyList = entry;
230 ptr_last->next = entry;
232 entry->last = ptr->last;
240 ptr_last->next = entry;
241 entry->last = ptr_last;
246 RuleDelete(struct proxy_entry *entry)
251 LIBALIAS_LOCK_ASSERT(la);
252 if (entry->last != NULL)
253 entry->last->next = entry->next;
255 la->proxyList = entry->next;
257 if (entry->next != NULL)
258 entry->next->last = entry->last;
264 RuleNumberDelete(struct libalias *la, int rule_index)
267 struct proxy_entry *ptr;
269 LIBALIAS_LOCK_ASSERT(la);
272 while (ptr != NULL) {
273 struct proxy_entry *ptr_next;
275 ptr_next = ptr->next;
276 if (ptr->rule_index == rule_index) {
287 ProxyEncodeTcpStream(struct alias_link *lnk,
294 char addrbuf[INET_ADDRSTRLEN];
296 /* Compute pointer to tcp header */
297 tc = (struct tcphdr *)ip_next(pip);
299 /* 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 */
340 bcopy(p, p + slen, dlen);
341 memcpy(p, buffer, slen);
344 /* Save information about modified sequence number */
349 tc = (struct tcphdr *)ip_next(pip);
350 delta = GetDeltaSeqOut(tc->th_seq, lnk);
351 AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
355 /* Update IP header packet length and checksum */
359 accumulate = pip->ip_len;
360 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
361 accumulate -= pip->ip_len;
363 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
366 /* Update TCP checksum, Use TcpChecksum since so many things have
371 tc->th_x2 = (TH_RES1 >> 8);
373 tc->th_sum = TcpChecksum(pip);
378 ProxyEncodeIpHeader(struct ip *pip, int maxpacketsize)
380 #define OPTION_LEN_BYTES 8
381 #define OPTION_LEN_INT16 4
382 #define OPTION_LEN_INT32 2
383 _Alignas(_Alignof(u_short)) u_char option[OPTION_LEN_BYTES];
385 #ifdef LIBALIAS_DEBUG
386 fprintf(stdout, " ip cksum 1 = %x\n", (u_int)IpChecksum(pip));
387 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int)TcpChecksum(pip));
392 /* Check to see that there is room to add an IP option */
393 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
396 /* Build option and copy into packet */
401 ptr = (u_char *) pip;
403 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
405 option[0] = 0x64; /* class: 3 (reserved), option 4 */
406 option[1] = OPTION_LEN_BYTES;
408 memcpy(&option[2], (u_char *)&pip->ip_dst, 4);
410 tc = (struct tcphdr *)ip_next(pip);
411 memcpy(&option[6], (u_char *)&tc->th_sport, 2);
413 memcpy(ptr, option, 8);
416 /* Update checksum, header length and packet length */
422 sptr = (u_short *) option;
424 for (i = 0; i < OPTION_LEN_INT16; i++)
425 accumulate -= *(sptr++);
427 sptr = (u_short *) pip;
429 pip->ip_hl += OPTION_LEN_INT32;
432 accumulate += pip->ip_len;
433 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
434 accumulate -= pip->ip_len;
436 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
438 #undef OPTION_LEN_BYTES
439 #undef OPTION_LEN_INT16
440 #undef OPTION_LEN_INT32
441 #ifdef LIBALIAS_DEBUG
442 fprintf(stdout, " ip cksum 2 = %x\n", (u_int)IpChecksum(pip));
443 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int)TcpChecksum(pip));
447 /* Functions by other packet alias source files
449 ProxyCheck() -- Checks whether an outgoing packet should
451 ProxyModify() -- Encodes the original destination address/port
452 for a packet which is to be redirected to
457 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
458 u_short * proxy_server_port, struct in_addr src_addr,
459 struct in_addr dst_addr, u_short dst_port, u_char ip_p)
461 struct proxy_entry *ptr;
463 LIBALIAS_LOCK_ASSERT(la);
466 while (ptr != NULL) {
469 proxy_port = ptr->proxy_port;
470 if ((dst_port == proxy_port || proxy_port == 0)
471 && ip_p == ptr->proto
472 && src_addr.s_addr != ptr->server_addr.s_addr) {
473 struct in_addr src_addr_masked;
474 struct in_addr dst_addr_masked;
476 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
477 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
479 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
480 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
481 if ((*proxy_server_port = ptr->server_port) == 0)
482 *proxy_server_port = dst_port;
483 *proxy_server_addr = ptr->server_addr;
484 return (ptr->proxy_type);
494 ProxyModify(struct libalias *la, struct alias_link *lnk,
499 LIBALIAS_LOCK_ASSERT(la);
502 switch (proxy_type) {
503 case PROXY_TYPE_ENCODE_IPHDR:
504 ProxyEncodeIpHeader(pip, maxpacketsize);
507 case PROXY_TYPE_ENCODE_TCPSTREAM:
508 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
518 * This function takes command strings of the form:
520 * server <addr>[:<port>]
526 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
528 * delete <rule number>
530 * Subfields can be in arbitrary order. Port numbers and addresses
531 * must be in either numeric or symbolic form. An optional rule number
532 * is used to control the order in which rules are searched. If two
533 * rules have the same number, then search order cannot be guaranteed,
534 * and the rules should be disjoint. If no rule number is specified,
535 * then 0 is used, and group 0 rules are always checked before any
539 LibAliasProxyRule(struct libalias *la, const char *cmd)
547 char str_port[sizeof(buffer)];
548 char str_server_port[sizeof(buffer)];
556 struct in_addr server_addr;
557 struct in_addr src_addr, src_mask;
558 struct in_addr dst_addr, dst_mask;
559 struct proxy_entry *proxy_entry;
564 /* Copy command line into a buffer */
565 cmd += strspn(cmd, " \t");
566 cmd_len = strlen(cmd);
567 if (cmd_len > (int)(sizeof(buffer) - 1)) {
573 /* Convert to lower case */
574 len = strlen(buffer);
575 for (i = 0; i < len; i++)
576 buffer[i] = tolower((unsigned char)buffer[i]);
578 /* Set default proxy type */
580 /* Set up default values */
582 proxy_type = PROXY_TYPE_ENCODE_NONE;
585 server_addr.s_addr = 0;
588 IpMask(0, &src_mask);
590 IpMask(0, &dst_mask);
593 str_server_port[0] = 0;
595 /* Parse command string with state machine */
596 #define STATE_READ_KEYWORD 0
597 #define STATE_READ_TYPE 1
598 #define STATE_READ_PORT 2
599 #define STATE_READ_SERVER 3
600 #define STATE_READ_RULE 4
601 #define STATE_READ_DELETE 5
602 #define STATE_READ_PROTO 6
603 #define STATE_READ_SRC 7
604 #define STATE_READ_DST 8
605 state = STATE_READ_KEYWORD;
606 token = strsep(&res, " \t");
608 while (token != NULL) {
611 case STATE_READ_KEYWORD:
612 if (strcmp(token, "type") == 0)
613 state = STATE_READ_TYPE;
614 else if (strcmp(token, "port") == 0)
615 state = STATE_READ_PORT;
616 else if (strcmp(token, "server") == 0)
617 state = STATE_READ_SERVER;
618 else if (strcmp(token, "rule") == 0)
619 state = STATE_READ_RULE;
620 else if (strcmp(token, "delete") == 0)
621 state = STATE_READ_DELETE;
622 else if (strcmp(token, "proto") == 0)
623 state = STATE_READ_PROTO;
624 else if (strcmp(token, "src") == 0)
625 state = STATE_READ_SRC;
626 else if (strcmp(token, "dst") == 0)
627 state = STATE_READ_DST;
634 case STATE_READ_TYPE:
635 if (strcmp(token, "encode_ip_hdr") == 0)
636 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
637 else if (strcmp(token, "encode_tcp_stream") == 0)
638 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
639 else if (strcmp(token, "no_encode") == 0)
640 proxy_type = PROXY_TYPE_ENCODE_NONE;
645 state = STATE_READ_KEYWORD;
648 case STATE_READ_PORT:
649 strcpy(str_port, token);
650 state = STATE_READ_KEYWORD;
653 case STATE_READ_SERVER: {
656 char s[sizeof(buffer)];
659 while (*p != ':' && *p != 0)
663 err = IpAddr(token, &server_addr);
671 n = sscanf(token, "%s %s", s, str_server_port);
677 err = IpAddr(s, &server_addr);
684 state = STATE_READ_KEYWORD;
687 case STATE_READ_RULE:
688 n = sscanf(token, "%d", &rule_index);
689 if (n != 1 || rule_index < 0) {
693 state = STATE_READ_KEYWORD;
696 case STATE_READ_DELETE: {
700 if (token_count != 2) {
705 n = sscanf(token, "%d", &rule_to_delete);
710 err = RuleNumberDelete(la, rule_to_delete);
718 case STATE_READ_PROTO:
719 if (strcmp(token, "tcp") == 0)
721 else if (strcmp(token, "udp") == 0)
727 state = STATE_READ_KEYWORD;
731 case STATE_READ_DST: {
738 while (*p != '/' && *p != 0)
743 err = IpAddr(token, &addr);
750 char s[sizeof(buffer)];
753 n = sscanf(token, "%s %d", s, &nbits);
759 err = IpAddr(s, &addr);
765 err = IpMask(nbits, &mask);
772 if (state == STATE_READ_SRC) {
780 state = STATE_READ_KEYWORD;
791 token = strsep(&res, " \t");
792 } while (token != NULL && !*token);
794 #undef STATE_READ_KEYWORD
795 #undef STATE_READ_TYPE
796 #undef STATE_READ_PORT
797 #undef STATE_READ_SERVER
798 #undef STATE_READ_RULE
799 #undef STATE_READ_DELETE
800 #undef STATE_READ_PROTO
801 #undef STATE_READ_SRC
802 #undef STATE_READ_DST
804 /* Convert port strings to numbers.
805 This needs to be done after the string is parsed, because
806 the prototype might not be designated before the ports
807 (which might be symbolic entries in /etc/services) */
809 if (strlen(str_port) != 0) {
812 err = IpPort(str_port, proto, &proxy_port);
821 if (strlen(str_server_port) != 0) {
824 err = IpPort(str_server_port, proto, &server_port);
833 /* Check that at least the server address has been defined */
834 if (server_addr.s_addr == 0) {
839 /* Add to linked list */
840 proxy_entry = malloc(sizeof(struct proxy_entry));
841 if (proxy_entry == NULL) {
846 proxy_entry->proxy_type = proxy_type;
847 proxy_entry->rule_index = rule_index;
848 proxy_entry->proto = proto;
849 proxy_entry->proxy_port = htons(proxy_port);
850 proxy_entry->server_port = htons(server_port);
851 proxy_entry->server_addr = server_addr;
852 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
853 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
854 proxy_entry->src_mask = src_mask;
855 proxy_entry->dst_mask = dst_mask;
857 RuleAdd(la, proxy_entry);