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
26 * $FreeBSD: src/lib/libalias/alias_proxy.c,v 1.4.2.5 2001/11/03 11:34:33 brian Exp $
29 /* file: alias_proxy.c
31 This file encapsulates special operations related to transparent
32 proxy redirection. This is where packets with a particular destination,
33 usually tcp port 80, are redirected to a proxy server.
35 When packets are proxied, the destination address and port are
36 modified. In certain cases, it is necessary to somehow encode
37 the original address/port info into the packet. Two methods are
38 presently supported: addition of a [DEST addr port] string at the
39 beginning a of tcp stream, or inclusion of an optional field
42 There is one public API function:
44 PacketAliasProxyRule() -- Adds and deletes proxy
47 Rules are stored in a linear linked list, so lookup efficiency
48 won't be too good for large lists.
51 Initial development: April, 1998 (cjm)
62 #include <sys/types.h>
63 #include <sys/socket.h>
65 /* BSD IPV4 includes */
66 #include <netinet/in_systm.h>
67 #include <netinet/in.h>
68 #include <netinet/ip.h>
69 #include <netinet/tcp.h>
71 #include <arpa/inet.h>
73 #include "alias_local.h" /* Functions used by alias*.c */
74 #include "alias.h" /* Public API functions for libalias */
83 * A linked list of arbitrary length, based on struct proxy_entry is
84 * used to store proxy rules.
88 #define PROXY_TYPE_ENCODE_NONE 1
89 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
90 #define PROXY_TYPE_ENCODE_IPHDR 3
97 struct in_addr server_addr;
99 struct in_addr src_addr;
100 struct in_addr src_mask;
102 struct in_addr dst_addr;
103 struct in_addr dst_mask;
105 struct proxy_entry *next;
106 struct proxy_entry *last;
115 static struct proxy_entry *proxyList;
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 proxy_entry *);
141 static void RuleDelete(struct proxy_entry *);
142 static int RuleNumberDelete(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);
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);
199 RuleAdd(struct proxy_entry *entry)
202 struct proxy_entry *ptr;
203 struct proxy_entry *ptr_last;
205 if (proxyList == NULL)
213 rule_index = entry->rule_index;
218 if (ptr->rule_index >= rule_index)
220 if (ptr_last == NULL)
222 entry->next = proxyList;
224 proxyList->last = 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)
247 if (entry->last != NULL)
248 entry->last->next = entry->next;
250 proxyList = entry->next;
252 if (entry->next != NULL)
253 entry->next->last = entry->last;
259 RuleNumberDelete(int rule_index)
262 struct proxy_entry *ptr;
268 struct proxy_entry *ptr_next;
270 ptr_next = ptr->next;
271 if (ptr->rule_index == rule_index)
284 ProxyEncodeTcpStream(struct alias_link *link,
292 /* Compute pointer to tcp header */
293 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
295 /* Don't modify if once already modified */
297 if (GetAckModified (link))
300 /* Translate destination address and port to string form */
301 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
302 inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link)));
304 /* Pad string out to a multiple of two in length */
305 slen = strlen(buffer);
309 strcat(buffer, " \n");
313 strcat(buffer, "\n");
317 /* Check for packet overflow */
318 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
321 /* Shift existing TCP data and insert destination string */
327 hlen = (pip->ip_hl + tc->th_off) << 2;
328 dlen = ntohs (pip->ip_len) - hlen;
330 /* Modify first packet that has data in it */
338 memmove(p + slen, p, dlen);
339 memcpy(p, buffer, slen);
342 /* Save information about modfied sequence number */
346 SetAckModified(link);
347 delta = GetDeltaSeqOut(pip, link);
348 AddSeq(pip, link, delta+slen);
351 /* Update IP header packet length and checksum */
355 accumulate = pip->ip_len;
356 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
357 accumulate -= pip->ip_len;
359 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
362 /* Update TCP checksum, Use TcpChecksum since so many things have
366 tc->th_sum = TcpChecksum (pip);
370 ProxyEncodeIpHeader(struct ip *pip,
373 #define OPTION_LEN_BYTES 8
374 #define OPTION_LEN_INT16 4
375 #define OPTION_LEN_INT32 2
376 u_char option[OPTION_LEN_BYTES];
379 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
380 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
383 /* Check to see that there is room to add an IP option */
384 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
387 /* Build option and copy into packet */
392 ptr = (u_char *) pip;
394 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
396 option[0] = 0x64; /* class: 3 (reserved), option 4 */
397 option[1] = OPTION_LEN_BYTES;
399 memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
401 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
402 memcpy(&option[6], (u_char *) &tc->th_sport, 2);
404 memcpy(ptr, option, 8);
407 /* Update checksum, header length and packet length */
413 sptr = (u_short *) option;
415 for (i=0; i<OPTION_LEN_INT16; i++)
416 accumulate -= *(sptr++);
418 sptr = (u_short *) pip;
420 pip->ip_hl += OPTION_LEN_INT32;
423 accumulate += pip->ip_len;
424 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
425 accumulate -= pip->ip_len;
427 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
429 #undef OPTION_LEN_BYTES
430 #undef OPTION_LEN_INT16
431 #undef OPTION_LEN_INT32
433 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
434 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
439 /* Functions by other packet alias source files
441 ProxyCheck() -- Checks whether an outgoing packet should
443 ProxyModify() -- Encodes the original destination address/port
444 for a packet which is to be redirected to
449 ProxyCheck(struct ip *pip,
450 struct in_addr *proxy_server_addr,
451 u_short *proxy_server_port)
454 struct in_addr src_addr;
455 struct in_addr dst_addr;
456 struct proxy_entry *ptr;
458 src_addr = pip->ip_src;
459 dst_addr = pip->ip_dst;
460 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
468 proxy_port = ptr->proxy_port;
469 if ((dst_port == proxy_port || proxy_port == 0)
470 && pip->ip_p == ptr->proto
471 && 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))
482 if ((*proxy_server_port = ptr->server_port) == 0)
483 *proxy_server_port = dst_port;
484 *proxy_server_addr = ptr->server_addr;
485 return ptr->proxy_type;
495 ProxyModify(struct alias_link *link,
502 case PROXY_TYPE_ENCODE_IPHDR:
503 ProxyEncodeIpHeader(pip, maxpacketsize);
506 case PROXY_TYPE_ENCODE_TCPSTREAM:
507 ProxyEncodeTcpStream(link, pip, maxpacketsize);
518 PacketAliasProxyRule(const char *cmd)
521 * This function takes command strings of the form:
523 * server <addr>[:<port>]
529 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
531 * delete <rule number>
533 * Subfields can be in arbitrary order. Port numbers and addresses
534 * must be in either numeric or symbolic form. An optional rule number
535 * is used to control the order in which rules are searched. If two
536 * rules have the same number, then search order cannot be guaranteed,
537 * and the rules should be disjoint. If no rule number is specified,
538 * then 0 is used, and group 0 rules are always checked before any
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;
561 /* Copy command line into a buffer */
562 cmd += strspn(cmd, " \t");
563 cmd_len = strlen(cmd);
564 if (cmd_len > (sizeof(buffer) - 1))
568 /* Convert to lower case */
569 len = strlen(buffer);
570 for (i=0; i<len; i++)
571 buffer[i] = tolower((unsigned char)buffer[i]);
573 /* Set default proxy type */
575 /* Set up default values */
577 proxy_type = PROXY_TYPE_ENCODE_NONE;
580 server_addr.s_addr = 0;
583 IpMask(0, &src_mask);
585 IpMask(0, &dst_mask);
588 str_server_port[0] = 0;
590 /* Parse command string with state machine */
591 #define STATE_READ_KEYWORD 0
592 #define STATE_READ_TYPE 1
593 #define STATE_READ_PORT 2
594 #define STATE_READ_SERVER 3
595 #define STATE_READ_RULE 4
596 #define STATE_READ_DELETE 5
597 #define STATE_READ_PROTO 6
598 #define STATE_READ_SRC 7
599 #define STATE_READ_DST 8
600 state = STATE_READ_KEYWORD;
601 token = strsep(&res, " \t");
603 while (token != NULL)
608 case STATE_READ_KEYWORD:
609 if (strcmp(token, "type") == 0)
610 state = STATE_READ_TYPE;
611 else if (strcmp(token, "port") == 0)
612 state = STATE_READ_PORT;
613 else if (strcmp(token, "server") == 0)
614 state = STATE_READ_SERVER;
615 else if (strcmp(token, "rule") == 0)
616 state = STATE_READ_RULE;
617 else if (strcmp(token, "delete") == 0)
618 state = STATE_READ_DELETE;
619 else if (strcmp(token, "proto") == 0)
620 state = STATE_READ_PROTO;
621 else if (strcmp(token, "src") == 0)
622 state = STATE_READ_SRC;
623 else if (strcmp(token, "dst") == 0)
624 state = STATE_READ_DST;
629 case STATE_READ_TYPE:
630 if (strcmp(token, "encode_ip_hdr") == 0)
631 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
632 else if (strcmp(token, "encode_tcp_stream") == 0)
633 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
634 else if (strcmp(token, "no_encode") == 0)
635 proxy_type = PROXY_TYPE_ENCODE_NONE;
638 state = STATE_READ_KEYWORD;
641 case STATE_READ_PORT:
642 strcpy(str_port, token);
643 state = STATE_READ_KEYWORD;
646 case STATE_READ_SERVER:
650 char s[sizeof(buffer)];
653 while (*p != ':' && *p != 0)
658 err = IpAddr(token, &server_addr);
666 n = sscanf(token, "%s %s", s, str_server_port);
670 err = IpAddr(s, &server_addr);
675 state = STATE_READ_KEYWORD;
678 case STATE_READ_RULE:
679 n = sscanf(token, "%d", &rule_index);
680 if (n != 1 || rule_index < 0)
682 state = STATE_READ_KEYWORD;
685 case STATE_READ_DELETE:
690 if (token_count != 2)
693 n = sscanf(token, "%d", &rule_to_delete);
696 err = RuleNumberDelete(rule_to_delete);
702 case STATE_READ_PROTO:
703 if (strcmp(token, "tcp") == 0)
705 else if (strcmp(token, "udp") == 0)
709 state = STATE_READ_KEYWORD;
721 while (*p != '/' && *p != 0)
727 err = IpAddr(token, &addr);
734 char s[sizeof(buffer)];
737 n = sscanf(token, "%s %d", s, &nbits);
741 err = IpAddr(s, &addr);
745 err = IpMask(nbits, &mask);
750 if (state == STATE_READ_SRC)
761 state = STATE_READ_KEYWORD;
770 token = strsep(&res, " \t");
771 } while (token != NULL && !*token);
773 #undef STATE_READ_KEYWORD
774 #undef STATE_READ_TYPE
775 #undef STATE_READ_PORT
776 #undef STATE_READ_SERVER
777 #undef STATE_READ_RULE
778 #undef STATE_READ_DELETE
779 #undef STATE_READ_PROTO
780 #undef STATE_READ_SRC
781 #undef STATE_READ_DST
783 /* Convert port strings to numbers. This needs to be done after
784 the string is parsed, because the prototype might not be designated
785 before the ports (which might be symbolic entries in /etc/services) */
787 if (strlen(str_port) != 0)
791 err = IpPort(str_port, proto, &proxy_port);
800 if (strlen(str_server_port) != 0)
804 err = IpPort(str_server_port, proto, &server_port);
813 /* Check that at least the server address has been defined */
814 if (server_addr.s_addr == 0)
817 /* Add to linked list */
818 proxy_entry = malloc(sizeof(struct proxy_entry));
819 if (proxy_entry == NULL)
822 proxy_entry->proxy_type = proxy_type;
823 proxy_entry->rule_index = rule_index;
824 proxy_entry->proto = proto;
825 proxy_entry->proxy_port = htons(proxy_port);
826 proxy_entry->server_port = htons(server_port);
827 proxy_entry->server_addr = server_addr;
828 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
829 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
830 proxy_entry->src_mask = src_mask;
831 proxy_entry->dst_mask = dst_mask;
833 RuleAdd(proxy_entry);