2 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 /* file: alias_proxy.c
32 This file encapsulates special operations related to transparent
33 proxy redirection. This is where packets with a particular destination,
34 usually tcp port 80, are redirected to a proxy server.
36 When packets are proxied, the destination address and port are
37 modified. In certain cases, it is necessary to somehow encode
38 the original address/port info into the packet. Two methods are
39 presently supported: addition of a [DEST addr port] string at the
40 beginning of a tcp stream, or inclusion of an optional field
43 There is one public API function:
45 PacketAliasProxyRule() -- Adds and deletes proxy
48 Rules are stored in a linear linked list, so lookup efficiency
49 won't be too good for large lists.
52 Initial development: April, 1998 (cjm)
58 #include <sys/param.h>
59 #include <sys/ctype.h>
60 #include <sys/libkern.h>
61 #include <sys/kernel.h>
62 #include <sys/malloc.h>
63 #include <sys/limits.h>
65 #include <sys/types.h>
66 #include <sys/socket.h>
72 #include <arpa/inet.h>
75 /* BSD IPV4 includes */
76 #include <netinet/in_systm.h>
77 #include <netinet/in.h>
78 #include <netinet/ip.h>
79 #include <netinet/tcp.h>
82 #include <netinet/libalias/alias.h>
83 #include <netinet/libalias/alias_local.h>
85 #include "alias.h" /* Public API functions for libalias */
86 #include "alias_local.h" /* Functions used by alias*.c */
94 * A linked list of arbitrary length, based on struct proxy_entry is
95 * used to store proxy rules.
99 #define PROXY_TYPE_ENCODE_NONE 1
100 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
101 #define PROXY_TYPE_ENCODE_IPHDR 3
108 struct in_addr server_addr;
110 struct in_addr src_addr;
111 struct in_addr src_mask;
113 struct in_addr dst_addr;
114 struct in_addr dst_mask;
116 struct proxy_entry *next;
117 struct proxy_entry *last;
128 /* Local (static) functions:
130 IpMask() -- Utility function for creating IP
131 masks from integer (1-32) specification.
132 IpAddr() -- Utility function for converting string
134 IpPort() -- Utility function for converting string
136 RuleAdd() -- Adds an element to the rule list.
137 RuleDelete() -- Removes an element from the rule list.
138 RuleNumberDelete() -- Removes all elements from the rule list
139 having a certain rule number.
140 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
142 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
143 destination of a proxied IP packet
146 #ifdef _KERNEL /* XXX: can it be moved to libkern? */
147 static int inet_aton(const char *cp, struct in_addr *addr);
149 static int IpMask(int, struct in_addr *);
150 static int IpAddr(char *, struct in_addr *);
151 static int IpPort(char *, int, int *);
152 static void RuleAdd(struct libalias *la, struct proxy_entry *);
153 static void RuleDelete(struct proxy_entry *);
154 static int RuleNumberDelete(struct libalias *la, int);
155 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
156 static void ProxyEncodeIpHeader(struct ip *, int);
162 struct in_addr *addr;
170 c = (const char *)cp;
173 * Run through the string, grabbing numbers until
174 * the end of the string, or some error
180 l = strtoul(c, &endptr, 0);
182 if (l == ULONG_MAX || l == 0)
187 * If the whole string is invalid, endptr will equal
188 * c.. this way we can make sure someone hasn't
189 * gone '.12' or something which would get past
197 /* Check the next character past the previous number's end */
200 /* Make sure we only do 3 dots .. */
201 if (n == 3) /* Whoops. Quit. */
212 if (isspace((unsigned char)*c)) {
216 return (0); /* Invalid character, so fail */
222 * Concoct the address according to
223 * the number of parts specified.
227 case 0: /* a -- 32 bits */
229 * Nothing is necessary here. Overflow checking was
230 * already done in strtoul().
233 case 1: /* a.b -- 8.24 bits */
234 if (val > 0xffffff || parts[0] > 0xff)
236 val |= parts[0] << 24;
239 case 2: /* a.b.c -- 8.8.16 bits */
240 if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
242 val |= (parts[0] << 24) | (parts[1] << 16);
245 case 3: /* a.b.c.d -- 8.8.8.8 bits */
246 if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
249 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
254 addr->s_addr = htonl(val);
260 IpMask(int nbits, struct in_addr *mask)
265 if (nbits < 0 || nbits > 32)
269 for (i = 0; i < nbits; i++)
270 imask = (imask >> 1) + 0x80000000;
271 mask->s_addr = htonl(imask);
277 IpAddr(char *s, struct in_addr *addr)
279 if (inet_aton(s, addr) == 0)
286 IpPort(char *s, int proto, int *port)
290 n = sscanf(s, "%d", port);
292 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
296 if (proto == IPPROTO_TCP)
297 se = getservbyname(s, "tcp");
298 else if (proto == IPPROTO_UDP)
299 se = getservbyname(s, "udp");
306 *port = (u_int) ntohs(se->s_port);
315 RuleAdd(struct libalias *la, struct proxy_entry *entry)
318 struct proxy_entry *ptr;
319 struct proxy_entry *ptr_last;
321 if (la->proxyList == NULL) {
322 la->proxyList = entry;
329 rule_index = entry->rule_index;
332 while (ptr != NULL) {
333 if (ptr->rule_index >= rule_index) {
334 if (ptr_last == NULL) {
335 entry->next = la->proxyList;
337 la->proxyList->last = entry;
338 la->proxyList = entry;
341 ptr_last->next = entry;
343 entry->last = ptr->last;
351 ptr_last->next = entry;
352 entry->last = ptr_last;
357 RuleDelete(struct proxy_entry *entry)
362 if (entry->last != NULL)
363 entry->last->next = entry->next;
365 la->proxyList = entry->next;
367 if (entry->next != NULL)
368 entry->next->last = entry->last;
374 RuleNumberDelete(struct libalias *la, int rule_index)
377 struct proxy_entry *ptr;
381 while (ptr != NULL) {
382 struct proxy_entry *ptr_next;
384 ptr_next = ptr->next;
385 if (ptr->rule_index == rule_index) {
396 ProxyEncodeTcpStream(struct alias_link *lnk,
404 /* Compute pointer to tcp header */
405 tc = (struct tcphdr *)ip_next(pip);
407 /* Don't modify if once already modified */
409 if (GetAckModified(lnk))
412 /* Translate destination address and port to string form */
413 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
414 inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
416 /* Pad string out to a multiple of two in length */
417 slen = strlen(buffer);
420 strcat(buffer, " \n");
424 strcat(buffer, "\n");
428 /* Check for packet overflow */
429 if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
432 /* Shift existing TCP data and insert destination string */
438 hlen = (pip->ip_hl + tc->th_off) << 2;
439 dlen = ntohs(pip->ip_len) - hlen;
441 /* Modify first packet that has data in it */
449 bcopy(p, p + slen, dlen);
450 memcpy(p, buffer, slen);
453 /* Save information about modfied sequence number */
458 delta = GetDeltaSeqOut(pip, lnk);
459 AddSeq(pip, lnk, delta + slen);
462 /* Update IP header packet length and checksum */
466 accumulate = pip->ip_len;
467 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
468 accumulate -= pip->ip_len;
470 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
473 /* Update TCP checksum, Use TcpChecksum since so many things have
480 tc->th_sum = TcpChecksum(pip);
485 ProxyEncodeIpHeader(struct ip *pip,
488 #define OPTION_LEN_BYTES 8
489 #define OPTION_LEN_INT16 4
490 #define OPTION_LEN_INT32 2
491 u_char option[OPTION_LEN_BYTES];
493 #ifdef LIBALIAS_DEBUG
494 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
495 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
500 /* Check to see that there is room to add an IP option */
501 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
504 /* Build option and copy into packet */
509 ptr = (u_char *) pip;
511 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
513 option[0] = 0x64; /* class: 3 (reserved), option 4 */
514 option[1] = OPTION_LEN_BYTES;
516 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
518 tc = (struct tcphdr *)ip_next(pip);
519 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
521 memcpy(ptr, option, 8);
524 /* Update checksum, header length and packet length */
530 sptr = (u_short *) option;
532 for (i = 0; i < OPTION_LEN_INT16; i++)
533 accumulate -= *(sptr++);
535 sptr = (u_short *) pip;
537 pip->ip_hl += OPTION_LEN_INT32;
540 accumulate += pip->ip_len;
541 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
542 accumulate -= pip->ip_len;
544 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
546 #undef OPTION_LEN_BYTES
547 #undef OPTION_LEN_INT16
548 #undef OPTION_LEN_INT32
549 #ifdef LIBALIAS_DEBUG
550 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
551 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
556 /* Functions by other packet alias source files
558 ProxyCheck() -- Checks whether an outgoing packet should
560 ProxyModify() -- Encodes the original destination address/port
561 for a packet which is to be redirected to
566 ProxyCheck(struct libalias *la, struct ip *pip,
567 struct in_addr *proxy_server_addr,
568 u_short * proxy_server_port)
571 struct in_addr src_addr;
572 struct in_addr dst_addr;
573 struct proxy_entry *ptr;
575 src_addr = pip->ip_src;
576 dst_addr = pip->ip_dst;
577 dst_port = ((struct tcphdr *)ip_next(pip))
581 while (ptr != NULL) {
584 proxy_port = ptr->proxy_port;
585 if ((dst_port == proxy_port || proxy_port == 0)
586 && pip->ip_p == ptr->proto
587 && src_addr.s_addr != ptr->server_addr.s_addr) {
588 struct in_addr src_addr_masked;
589 struct in_addr dst_addr_masked;
591 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
592 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
594 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
595 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
596 if ((*proxy_server_port = ptr->server_port) == 0)
597 *proxy_server_port = dst_port;
598 *proxy_server_addr = ptr->server_addr;
599 return (ptr->proxy_type);
609 ProxyModify(struct libalias *la, struct alias_link *lnk,
617 switch (proxy_type) {
618 case PROXY_TYPE_ENCODE_IPHDR:
619 ProxyEncodeIpHeader(pip, maxpacketsize);
622 case PROXY_TYPE_ENCODE_TCPSTREAM:
623 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
634 LibAliasProxyRule(struct libalias *la, const char *cmd)
637 * This function takes command strings of the form:
639 * server <addr>[:<port>]
645 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
647 * delete <rule number>
649 * Subfields can be in arbitrary order. Port numbers and addresses
650 * must be in either numeric or symbolic form. An optional rule number
651 * is used to control the order in which rules are searched. If two
652 * rules have the same number, then search order cannot be guaranteed,
653 * and the rules should be disjoint. If no rule number is specified,
654 * then 0 is used, and group 0 rules are always checked before any
663 char str_port[sizeof(buffer)];
664 char str_server_port[sizeof(buffer)];
672 struct in_addr server_addr;
673 struct in_addr src_addr, src_mask;
674 struct in_addr dst_addr, dst_mask;
675 struct proxy_entry *proxy_entry;
677 /* Copy command line into a buffer */
678 cmd += strspn(cmd, " \t");
679 cmd_len = strlen(cmd);
680 if (cmd_len > (int)(sizeof(buffer) - 1))
684 /* Convert to lower case */
685 len = strlen(buffer);
686 for (i = 0; i < len; i++)
687 buffer[i] = tolower((unsigned char)buffer[i]);
689 /* Set default proxy type */
691 /* Set up default values */
693 proxy_type = PROXY_TYPE_ENCODE_NONE;
696 server_addr.s_addr = 0;
699 IpMask(0, &src_mask);
701 IpMask(0, &dst_mask);
704 str_server_port[0] = 0;
706 /* Parse command string with state machine */
707 #define STATE_READ_KEYWORD 0
708 #define STATE_READ_TYPE 1
709 #define STATE_READ_PORT 2
710 #define STATE_READ_SERVER 3
711 #define STATE_READ_RULE 4
712 #define STATE_READ_DELETE 5
713 #define STATE_READ_PROTO 6
714 #define STATE_READ_SRC 7
715 #define STATE_READ_DST 8
716 state = STATE_READ_KEYWORD;
717 token = strsep(&res, " \t");
719 while (token != NULL) {
722 case STATE_READ_KEYWORD:
723 if (strcmp(token, "type") == 0)
724 state = STATE_READ_TYPE;
725 else if (strcmp(token, "port") == 0)
726 state = STATE_READ_PORT;
727 else if (strcmp(token, "server") == 0)
728 state = STATE_READ_SERVER;
729 else if (strcmp(token, "rule") == 0)
730 state = STATE_READ_RULE;
731 else if (strcmp(token, "delete") == 0)
732 state = STATE_READ_DELETE;
733 else if (strcmp(token, "proto") == 0)
734 state = STATE_READ_PROTO;
735 else if (strcmp(token, "src") == 0)
736 state = STATE_READ_SRC;
737 else if (strcmp(token, "dst") == 0)
738 state = STATE_READ_DST;
743 case STATE_READ_TYPE:
744 if (strcmp(token, "encode_ip_hdr") == 0)
745 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
746 else if (strcmp(token, "encode_tcp_stream") == 0)
747 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
748 else if (strcmp(token, "no_encode") == 0)
749 proxy_type = PROXY_TYPE_ENCODE_NONE;
752 state = STATE_READ_KEYWORD;
755 case STATE_READ_PORT:
756 strcpy(str_port, token);
757 state = STATE_READ_KEYWORD;
760 case STATE_READ_SERVER:
764 char s[sizeof(buffer)];
767 while (*p != ':' && *p != 0)
771 err = IpAddr(token, &server_addr);
777 n = sscanf(token, "%s %s", s, str_server_port);
781 err = IpAddr(s, &server_addr);
786 state = STATE_READ_KEYWORD;
789 case STATE_READ_RULE:
790 n = sscanf(token, "%d", &rule_index);
791 if (n != 1 || rule_index < 0)
793 state = STATE_READ_KEYWORD;
796 case STATE_READ_DELETE:
801 if (token_count != 2)
804 n = sscanf(token, "%d", &rule_to_delete);
807 err = RuleNumberDelete(la, rule_to_delete);
813 case STATE_READ_PROTO:
814 if (strcmp(token, "tcp") == 0)
816 else if (strcmp(token, "udp") == 0)
820 state = STATE_READ_KEYWORD;
832 while (*p != '/' && *p != 0)
837 err = IpAddr(token, &addr);
842 char s[sizeof(buffer)];
845 n = sscanf(token, "%s %d", s, &nbits);
849 err = IpAddr(s, &addr);
853 err = IpMask(nbits, &mask);
858 if (state == STATE_READ_SRC) {
866 state = STATE_READ_KEYWORD;
875 token = strsep(&res, " \t");
876 } while (token != NULL && !*token);
878 #undef STATE_READ_KEYWORD
879 #undef STATE_READ_TYPE
880 #undef STATE_READ_PORT
881 #undef STATE_READ_SERVER
882 #undef STATE_READ_RULE
883 #undef STATE_READ_DELETE
884 #undef STATE_READ_PROTO
885 #undef STATE_READ_SRC
886 #undef STATE_READ_DST
888 /* Convert port strings to numbers. This needs to be done after
889 the string is parsed, because the prototype might not be designated
890 before the ports (which might be symbolic entries in /etc/services) */
892 if (strlen(str_port) != 0) {
895 err = IpPort(str_port, proto, &proxy_port);
902 if (strlen(str_server_port) != 0) {
905 err = IpPort(str_server_port, proto, &server_port);
912 /* Check that at least the server address has been defined */
913 if (server_addr.s_addr == 0)
916 /* Add to linked list */
917 proxy_entry = malloc(sizeof(struct proxy_entry));
918 if (proxy_entry == NULL)
921 proxy_entry->proxy_type = proxy_type;
922 proxy_entry->rule_index = rule_index;
923 proxy_entry->proto = proto;
924 proxy_entry->proxy_port = htons(proxy_port);
925 proxy_entry->server_port = htons(server_port);
926 proxy_entry->server_addr = server_addr;
927 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
928 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
929 proxy_entry->src_mask = src_mask;
930 proxy_entry->dst_mask = dst_mask;
932 RuleAdd(la, proxy_entry);