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/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;
122 /* Local (static) functions:
124 IpMask() -- Utility function for creating IP
125 masks from integer (1-32) specification.
126 IpAddr() -- Utility function for converting string
128 IpPort() -- Utility function for converting string
130 RuleAdd() -- Adds an element to the rule list.
131 RuleDelete() -- Removes an element from the rule list.
132 RuleNumberDelete() -- Removes all elements from the rule list
133 having a certain rule number.
134 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
136 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
137 destination of a proxied IP packet
140 static int IpMask(int, struct in_addr *);
141 static int IpAddr(char *, struct in_addr *);
142 static int IpPort(char *, int, int *);
143 static void RuleAdd(struct libalias *la, struct proxy_entry *);
144 static void RuleDelete(struct proxy_entry *);
145 static int RuleNumberDelete(struct libalias *la, int);
146 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
147 static void ProxyEncodeIpHeader(struct ip *, int);
150 IpMask(int nbits, struct in_addr *mask)
155 if (nbits < 0 || nbits > 32)
159 for (i = 0; i < nbits; i++)
160 imask = (imask >> 1) + 0x80000000;
161 mask->s_addr = htonl(imask);
167 IpAddr(char *s, struct in_addr *addr)
169 if (inet_aton(s, addr) == 0)
176 IpPort(char *s, int proto, int *port)
180 n = sscanf(s, "%d", port);
182 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
186 if (proto == IPPROTO_TCP)
187 se = getservbyname(s, "tcp");
188 else if (proto == IPPROTO_UDP)
189 se = getservbyname(s, "udp");
196 *port = (u_int) ntohs(se->s_port);
205 RuleAdd(struct libalias *la, struct proxy_entry *entry)
208 struct proxy_entry *ptr;
209 struct proxy_entry *ptr_last;
211 LIBALIAS_LOCK_ASSERT(la);
214 if (la->proxyList == NULL) {
215 la->proxyList = entry;
221 rule_index = entry->rule_index;
224 while (ptr != NULL) {
225 if (ptr->rule_index >= rule_index) {
226 if (ptr_last == NULL) {
227 entry->next = la->proxyList;
229 la->proxyList->last = entry;
230 la->proxyList = entry;
233 ptr_last->next = entry;
235 entry->last = ptr->last;
243 ptr_last->next = entry;
244 entry->last = ptr_last;
249 RuleDelete(struct proxy_entry *entry)
254 LIBALIAS_LOCK_ASSERT(la);
255 if (entry->last != NULL)
256 entry->last->next = entry->next;
258 la->proxyList = entry->next;
260 if (entry->next != NULL)
261 entry->next->last = entry->last;
267 RuleNumberDelete(struct libalias *la, int rule_index)
270 struct proxy_entry *ptr;
272 LIBALIAS_LOCK_ASSERT(la);
275 while (ptr != NULL) {
276 struct proxy_entry *ptr_next;
278 ptr_next = ptr->next;
279 if (ptr->rule_index == rule_index) {
290 ProxyEncodeTcpStream(struct alias_link *lnk,
298 /* Compute pointer to tcp header */
299 tc = (struct tcphdr *)ip_next(pip);
301 /* Don't modify if once already modified */
303 if (GetAckModified(lnk))
306 /* Translate destination address and port to string form */
307 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
308 inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
310 /* Pad string out to a multiple of two in length */
311 slen = strlen(buffer);
314 strcat(buffer, " \n");
318 strcat(buffer, "\n");
322 /* Check for packet overflow */
323 if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
326 /* Shift existing TCP data and insert destination string */
332 hlen = (pip->ip_hl + tc->th_off) << 2;
333 dlen = ntohs(pip->ip_len) - hlen;
335 /* Modify first packet that has data in it */
343 bcopy(p, p + slen, dlen);
344 memcpy(p, buffer, slen);
347 /* Save information about modfied sequence number */
352 tc = (struct tcphdr *)ip_next(pip);
353 delta = GetDeltaSeqOut(tc->th_seq, lnk);
354 AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
358 /* Update IP header packet length and checksum */
362 accumulate = pip->ip_len;
363 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
364 accumulate -= pip->ip_len;
366 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
369 /* Update TCP checksum, Use TcpChecksum since so many things have
376 tc->th_sum = TcpChecksum(pip);
381 ProxyEncodeIpHeader(struct ip *pip,
384 #define OPTION_LEN_BYTES 8
385 #define OPTION_LEN_INT16 4
386 #define OPTION_LEN_INT32 2
387 u_char option[OPTION_LEN_BYTES];
389 #ifdef LIBALIAS_DEBUG
390 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
391 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
396 /* Check to see that there is room to add an IP option */
397 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
400 /* Build option and copy into packet */
405 ptr = (u_char *) pip;
407 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
409 option[0] = 0x64; /* class: 3 (reserved), option 4 */
410 option[1] = OPTION_LEN_BYTES;
412 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
414 tc = (struct tcphdr *)ip_next(pip);
415 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
417 memcpy(ptr, option, 8);
420 /* Update checksum, header length and packet length */
426 sptr = (u_short *) option;
428 for (i = 0; i < OPTION_LEN_INT16; i++)
429 accumulate -= *(sptr++);
431 sptr = (u_short *) pip;
433 pip->ip_hl += OPTION_LEN_INT32;
436 accumulate += pip->ip_len;
437 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
438 accumulate -= pip->ip_len;
440 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
442 #undef OPTION_LEN_BYTES
443 #undef OPTION_LEN_INT16
444 #undef OPTION_LEN_INT32
445 #ifdef LIBALIAS_DEBUG
446 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
447 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
452 /* Functions by other packet alias source files
454 ProxyCheck() -- Checks whether an outgoing packet should
456 ProxyModify() -- Encodes the original destination address/port
457 for a packet which is to be redirected to
462 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
463 u_short * proxy_server_port, struct in_addr src_addr,
464 struct in_addr dst_addr, u_short dst_port, u_char ip_p)
466 struct proxy_entry *ptr;
468 LIBALIAS_LOCK_ASSERT(la);
471 while (ptr != NULL) {
474 proxy_port = ptr->proxy_port;
475 if ((dst_port == proxy_port || proxy_port == 0)
476 && ip_p == ptr->proto
477 && src_addr.s_addr != ptr->server_addr.s_addr) {
478 struct in_addr src_addr_masked;
479 struct in_addr dst_addr_masked;
481 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
482 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
484 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
485 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
486 if ((*proxy_server_port = ptr->server_port) == 0)
487 *proxy_server_port = dst_port;
488 *proxy_server_addr = ptr->server_addr;
489 return (ptr->proxy_type);
499 ProxyModify(struct libalias *la, struct alias_link *lnk,
505 LIBALIAS_LOCK_ASSERT(la);
508 switch (proxy_type) {
509 case PROXY_TYPE_ENCODE_IPHDR:
510 ProxyEncodeIpHeader(pip, maxpacketsize);
513 case PROXY_TYPE_ENCODE_TCPSTREAM:
514 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
525 LibAliasProxyRule(struct libalias *la, const char *cmd)
528 * This function takes command strings of the form:
530 * server <addr>[:<port>]
536 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
538 * delete <rule number>
540 * Subfields can be in arbitrary order. Port numbers and addresses
541 * must be in either numeric or symbolic form. An optional rule number
542 * is used to control the order in which rules are searched. If two
543 * rules have the same number, then search order cannot be guaranteed,
544 * and the rules should be disjoint. If no rule number is specified,
545 * then 0 is used, and group 0 rules are always checked before any
554 char str_port[sizeof(buffer)];
555 char str_server_port[sizeof(buffer)];
563 struct in_addr server_addr;
564 struct in_addr src_addr, src_mask;
565 struct in_addr dst_addr, dst_mask;
566 struct proxy_entry *proxy_entry;
570 /* Copy command line into a buffer */
571 cmd += strspn(cmd, " \t");
572 cmd_len = strlen(cmd);
573 if (cmd_len > (int)(sizeof(buffer) - 1)) {
579 /* Convert to lower case */
580 len = strlen(buffer);
581 for (i = 0; i < len; i++)
582 buffer[i] = tolower((unsigned char)buffer[i]);
584 /* Set default proxy type */
586 /* Set up default values */
588 proxy_type = PROXY_TYPE_ENCODE_NONE;
591 server_addr.s_addr = 0;
594 IpMask(0, &src_mask);
596 IpMask(0, &dst_mask);
599 str_server_port[0] = 0;
601 /* Parse command string with state machine */
602 #define STATE_READ_KEYWORD 0
603 #define STATE_READ_TYPE 1
604 #define STATE_READ_PORT 2
605 #define STATE_READ_SERVER 3
606 #define STATE_READ_RULE 4
607 #define STATE_READ_DELETE 5
608 #define STATE_READ_PROTO 6
609 #define STATE_READ_SRC 7
610 #define STATE_READ_DST 8
611 state = STATE_READ_KEYWORD;
612 token = strsep(&res, " \t");
614 while (token != NULL) {
617 case STATE_READ_KEYWORD:
618 if (strcmp(token, "type") == 0)
619 state = STATE_READ_TYPE;
620 else if (strcmp(token, "port") == 0)
621 state = STATE_READ_PORT;
622 else if (strcmp(token, "server") == 0)
623 state = STATE_READ_SERVER;
624 else if (strcmp(token, "rule") == 0)
625 state = STATE_READ_RULE;
626 else if (strcmp(token, "delete") == 0)
627 state = STATE_READ_DELETE;
628 else if (strcmp(token, "proto") == 0)
629 state = STATE_READ_PROTO;
630 else if (strcmp(token, "src") == 0)
631 state = STATE_READ_SRC;
632 else if (strcmp(token, "dst") == 0)
633 state = STATE_READ_DST;
640 case STATE_READ_TYPE:
641 if (strcmp(token, "encode_ip_hdr") == 0)
642 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
643 else if (strcmp(token, "encode_tcp_stream") == 0)
644 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
645 else if (strcmp(token, "no_encode") == 0)
646 proxy_type = PROXY_TYPE_ENCODE_NONE;
651 state = STATE_READ_KEYWORD;
654 case STATE_READ_PORT:
655 strcpy(str_port, token);
656 state = STATE_READ_KEYWORD;
659 case STATE_READ_SERVER:
663 char s[sizeof(buffer)];
666 while (*p != ':' && *p != 0)
670 err = IpAddr(token, &server_addr);
678 n = sscanf(token, "%s %s", s, str_server_port);
684 err = IpAddr(s, &server_addr);
691 state = STATE_READ_KEYWORD;
694 case STATE_READ_RULE:
695 n = sscanf(token, "%d", &rule_index);
696 if (n != 1 || rule_index < 0) {
700 state = STATE_READ_KEYWORD;
703 case STATE_READ_DELETE:
708 if (token_count != 2) {
713 n = sscanf(token, "%d", &rule_to_delete);
718 err = RuleNumberDelete(la, rule_to_delete);
725 case STATE_READ_PROTO:
726 if (strcmp(token, "tcp") == 0)
728 else if (strcmp(token, "udp") == 0)
734 state = STATE_READ_KEYWORD;
746 while (*p != '/' && *p != 0)
751 err = IpAddr(token, &addr);
758 char s[sizeof(buffer)];
761 n = sscanf(token, "%s %d", s, &nbits);
767 err = IpAddr(s, &addr);
773 err = IpMask(nbits, &mask);
780 if (state == STATE_READ_SRC) {
788 state = STATE_READ_KEYWORD;
798 token = strsep(&res, " \t");
799 } while (token != NULL && !*token);
801 #undef STATE_READ_KEYWORD
802 #undef STATE_READ_TYPE
803 #undef STATE_READ_PORT
804 #undef STATE_READ_SERVER
805 #undef STATE_READ_RULE
806 #undef STATE_READ_DELETE
807 #undef STATE_READ_PROTO
808 #undef STATE_READ_SRC
809 #undef STATE_READ_DST
811 /* Convert port strings to numbers. This needs to be done after
812 the string is parsed, because the prototype might not be designated
813 before the ports (which might be symbolic entries in /etc/services) */
815 if (strlen(str_port) != 0) {
818 err = IpPort(str_port, proto, &proxy_port);
827 if (strlen(str_server_port) != 0) {
830 err = IpPort(str_server_port, proto, &server_port);
839 /* Check that at least the server address has been defined */
840 if (server_addr.s_addr == 0) {
845 /* Add to linked list */
846 proxy_entry = malloc(sizeof(struct proxy_entry));
847 if (proxy_entry == NULL) {
852 proxy_entry->proxy_type = proxy_type;
853 proxy_entry->rule_index = rule_index;
854 proxy_entry->proto = proto;
855 proxy_entry->proxy_port = htons(proxy_port);
856 proxy_entry->server_port = htons(server_port);
857 proxy_entry->server_addr = server_addr;
858 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
859 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
860 proxy_entry->src_mask = src_mask;
861 proxy_entry->dst_mask = dst_mask;
863 RuleAdd(la, proxy_entry);