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 $
27 * $DragonFly: src/lib/libalias/alias_proxy.c,v 1.3 2004/08/20 00:08:17 joerg Exp $
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 a of 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)
57 #include <sys/param.h>
58 #include <sys/socket.h>
66 /* BSD IPV4 includes */
67 #include <netinet/in_systm.h>
68 #include <netinet/in.h>
69 #include <netinet/ip.h>
70 #include <netinet/tcp.h>
72 #include <arpa/inet.h>
74 #include "alias_local.h" /* Functions used by alias*.c */
75 #include "alias.h" /* Public API functions for libalias */
84 * A linked list of arbitrary length, based on struct proxy_entry is
85 * used to store proxy rules.
89 #define PROXY_TYPE_ENCODE_NONE 1
90 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
91 #define PROXY_TYPE_ENCODE_IPHDR 3
98 struct in_addr server_addr
;
100 struct in_addr src_addr
;
101 struct in_addr src_mask
;
103 struct in_addr dst_addr
;
104 struct in_addr dst_mask
;
106 struct proxy_entry
*next
;
107 struct proxy_entry
*last
;
116 static struct proxy_entry
*proxyList
;
120 /* Local (static) functions:
122 IpMask() -- Utility function for creating IP
123 masks from integer (1-32) specification.
124 IpAddr() -- Utility function for converting string
126 IpPort() -- Utility function for converting string
128 RuleAdd() -- Adds an element to the rule list.
129 RuleDelete() -- Removes an element from the rule list.
130 RuleNumberDelete() -- Removes all elements from the rule list
131 having a certain rule number.
132 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
134 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
135 destination of a proxied IP packet
138 static int IpMask(int, struct in_addr
*);
139 static int IpAddr(char *, struct in_addr
*);
140 static int IpPort(char *, int, int *);
141 static void RuleAdd(struct proxy_entry
*);
142 static void RuleDelete(struct proxy_entry
*);
143 static int RuleNumberDelete(int);
144 static void ProxyEncodeTcpStream(struct alias_link
*, struct ip
*, int);
145 static void ProxyEncodeIpHeader(struct ip
*, int);
148 IpMask(int nbits
, struct in_addr
*mask
)
153 if (nbits
< 0 || nbits
> 32)
157 for (i
=0; i
<nbits
; i
++)
158 imask
= (imask
>> 1) + 0x80000000;
159 mask
->s_addr
= htonl(imask
);
165 IpAddr(char *s
, struct in_addr
*addr
)
167 if (inet_aton(s
, addr
) == 0)
174 IpPort(char *s
, int proto
, int *port
)
178 n
= sscanf(s
, "%d", port
);
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
);
200 RuleAdd(struct proxy_entry
*entry
)
203 struct proxy_entry
*ptr
;
204 struct proxy_entry
*ptr_last
;
206 if (proxyList
== NULL
)
214 rule_index
= entry
->rule_index
;
219 if (ptr
->rule_index
>= rule_index
)
221 if (ptr_last
== NULL
)
223 entry
->next
= proxyList
;
225 proxyList
->last
= 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
)
248 if (entry
->last
!= NULL
)
249 entry
->last
->next
= entry
->next
;
251 proxyList
= entry
->next
;
253 if (entry
->next
!= NULL
)
254 entry
->next
->last
= entry
->last
;
260 RuleNumberDelete(int rule_index
)
263 struct proxy_entry
*ptr
;
269 struct proxy_entry
*ptr_next
;
271 ptr_next
= ptr
->next
;
272 if (ptr
->rule_index
== rule_index
)
285 ProxyEncodeTcpStream(struct alias_link
*link
,
293 /* Compute pointer to tcp header */
294 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
296 /* Don't modify if once already modified */
298 if (GetAckModified (link
))
301 /* Translate destination address and port to string form */
302 snprintf(buffer
, sizeof(buffer
) - 2, "[DEST %s %d]",
303 inet_ntoa(GetProxyAddress (link
)), (u_int
) ntohs(GetProxyPort (link
)));
305 /* Pad string out to a multiple of two in length */
306 slen
= strlen(buffer
);
310 strcat(buffer
, " \n");
314 strcat(buffer
, "\n");
318 /* Check for packet overflow */
319 if ((ntohs(pip
->ip_len
) + strlen(buffer
)) > maxpacketsize
)
322 /* Shift existing TCP data and insert destination string */
328 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
329 dlen
= ntohs (pip
->ip_len
) - hlen
;
331 /* Modify first packet that has data in it */
339 memmove(p
+ slen
, p
, dlen
);
340 memcpy(p
, buffer
, slen
);
343 /* Save information about modfied sequence number */
347 SetAckModified(link
);
348 delta
= GetDeltaSeqOut(pip
, link
);
349 AddSeq(pip
, link
, delta
+slen
);
352 /* Update IP header packet length and checksum */
356 accumulate
= pip
->ip_len
;
357 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + slen
);
358 accumulate
-= pip
->ip_len
;
360 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
363 /* Update TCP checksum, Use TcpChecksum since so many things have
367 tc
->th_sum
= TcpChecksum (pip
);
371 ProxyEncodeIpHeader(struct ip
*pip
,
374 #define OPTION_LEN_BYTES 8
375 #define OPTION_LEN_INT16 4
376 #define OPTION_LEN_INT32 2
377 u_char option
[OPTION_LEN_BYTES
];
380 fprintf(stdout
, " ip cksum 1 = %x\n", (u_int
) IpChecksum(pip
));
381 fprintf(stdout
, "tcp cksum 1 = %x\n", (u_int
) TcpChecksum(pip
));
384 /* Check to see that there is room to add an IP option */
385 if (pip
->ip_hl
> (0x0f - OPTION_LEN_INT32
))
388 /* Build option and copy into packet */
393 ptr
= (u_char
*) pip
;
395 memcpy(ptr
+ OPTION_LEN_BYTES
, ptr
, ntohs(pip
->ip_len
) - 20);
397 option
[0] = 0x64; /* class: 3 (reserved), option 4 */
398 option
[1] = OPTION_LEN_BYTES
;
400 memcpy(&option
[2], (u_char
*) &pip
->ip_dst
, 4);
402 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
403 memcpy(&option
[6], (u_char
*) &tc
->th_sport
, 2);
405 memcpy(ptr
, option
, 8);
408 /* Update checksum, header length and packet length */
414 sptr
= (u_short
*) option
;
416 for (i
=0; i
<OPTION_LEN_INT16
; i
++)
417 accumulate
-= *(sptr
++);
419 sptr
= (u_short
*) pip
;
421 pip
->ip_hl
+= OPTION_LEN_INT32
;
424 accumulate
+= pip
->ip_len
;
425 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + OPTION_LEN_BYTES
);
426 accumulate
-= pip
->ip_len
;
428 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
430 #undef OPTION_LEN_BYTES
431 #undef OPTION_LEN_INT16
432 #undef OPTION_LEN_INT32
434 fprintf(stdout
, " ip cksum 2 = %x\n", (u_int
) IpChecksum(pip
));
435 fprintf(stdout
, "tcp cksum 2 = %x\n", (u_int
) TcpChecksum(pip
));
440 /* Functions by other packet alias source files
442 ProxyCheck() -- Checks whether an outgoing packet should
444 ProxyModify() -- Encodes the original destination address/port
445 for a packet which is to be redirected to
450 ProxyCheck(struct ip
*pip
,
451 struct in_addr
*proxy_server_addr
,
452 u_short
*proxy_server_port
)
455 struct in_addr src_addr
;
456 struct in_addr dst_addr
;
457 struct proxy_entry
*ptr
;
459 src_addr
= pip
->ip_src
;
460 dst_addr
= pip
->ip_dst
;
461 dst_port
= ((struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2)))
469 proxy_port
= ptr
->proxy_port
;
470 if ((dst_port
== proxy_port
|| proxy_port
== 0)
471 && pip
->ip_p
== ptr
->proto
472 && src_addr
.s_addr
!= ptr
->server_addr
.s_addr
)
474 struct in_addr src_addr_masked
;
475 struct in_addr dst_addr_masked
;
477 src_addr_masked
.s_addr
= src_addr
.s_addr
& ptr
->src_mask
.s_addr
;
478 dst_addr_masked
.s_addr
= dst_addr
.s_addr
& ptr
->dst_mask
.s_addr
;
480 if ((src_addr_masked
.s_addr
== ptr
->src_addr
.s_addr
)
481 && (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 alias_link
*link
,
503 case PROXY_TYPE_ENCODE_IPHDR
:
504 ProxyEncodeIpHeader(pip
, maxpacketsize
);
507 case PROXY_TYPE_ENCODE_TCPSTREAM
:
508 ProxyEncodeTcpStream(link
, pip
, maxpacketsize
);
519 PacketAliasProxyRule(const char *cmd
)
522 * This function takes command strings of the form:
524 * server <addr>[:<port>]
530 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
532 * delete <rule number>
534 * Subfields can be in arbitrary order. Port numbers and addresses
535 * must be in either numeric or symbolic form. An optional rule number
536 * is used to control the order in which rules are searched. If two
537 * rules have the same number, then search order cannot be guaranteed,
538 * and the rules should be disjoint. If no rule number is specified,
539 * then 0 is used, and group 0 rules are always checked before any
548 char str_port
[sizeof(buffer
)];
549 char str_server_port
[sizeof(buffer
)];
557 struct in_addr server_addr
;
558 struct in_addr src_addr
, src_mask
;
559 struct in_addr dst_addr
, dst_mask
;
560 struct proxy_entry
*proxy_entry
;
562 /* Copy command line into a buffer */
563 cmd
+= strspn(cmd
, " \t");
564 cmd_len
= strlen(cmd
);
565 if (cmd_len
> (sizeof(buffer
) - 1))
569 /* Convert to lower case */
570 len
= strlen(buffer
);
571 for (i
=0; i
<len
; i
++)
572 buffer
[i
] = tolower((unsigned char)buffer
[i
]);
574 /* Set default proxy type */
576 /* Set up default values */
578 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
581 server_addr
.s_addr
= 0;
584 IpMask(0, &src_mask
);
586 IpMask(0, &dst_mask
);
589 str_server_port
[0] = 0;
591 /* Parse command string with state machine */
592 #define STATE_READ_KEYWORD 0
593 #define STATE_READ_TYPE 1
594 #define STATE_READ_PORT 2
595 #define STATE_READ_SERVER 3
596 #define STATE_READ_RULE 4
597 #define STATE_READ_DELETE 5
598 #define STATE_READ_PROTO 6
599 #define STATE_READ_SRC 7
600 #define STATE_READ_DST 8
601 state
= STATE_READ_KEYWORD
;
602 token
= strsep(&res
, " \t");
604 while (token
!= NULL
)
609 case STATE_READ_KEYWORD
:
610 if (strcmp(token
, "type") == 0)
611 state
= STATE_READ_TYPE
;
612 else if (strcmp(token
, "port") == 0)
613 state
= STATE_READ_PORT
;
614 else if (strcmp(token
, "server") == 0)
615 state
= STATE_READ_SERVER
;
616 else if (strcmp(token
, "rule") == 0)
617 state
= STATE_READ_RULE
;
618 else if (strcmp(token
, "delete") == 0)
619 state
= STATE_READ_DELETE
;
620 else if (strcmp(token
, "proto") == 0)
621 state
= STATE_READ_PROTO
;
622 else if (strcmp(token
, "src") == 0)
623 state
= STATE_READ_SRC
;
624 else if (strcmp(token
, "dst") == 0)
625 state
= STATE_READ_DST
;
630 case STATE_READ_TYPE
:
631 if (strcmp(token
, "encode_ip_hdr") == 0)
632 proxy_type
= PROXY_TYPE_ENCODE_IPHDR
;
633 else if (strcmp(token
, "encode_tcp_stream") == 0)
634 proxy_type
= PROXY_TYPE_ENCODE_TCPSTREAM
;
635 else if (strcmp(token
, "no_encode") == 0)
636 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
639 state
= STATE_READ_KEYWORD
;
642 case STATE_READ_PORT
:
643 strcpy(str_port
, token
);
644 state
= STATE_READ_KEYWORD
;
647 case STATE_READ_SERVER
:
651 char s
[sizeof(buffer
)];
654 while (*p
!= ':' && *p
!= 0)
659 err
= IpAddr(token
, &server_addr
);
667 n
= sscanf(token
, "%s %s", s
, str_server_port
);
671 err
= IpAddr(s
, &server_addr
);
676 state
= STATE_READ_KEYWORD
;
679 case STATE_READ_RULE
:
680 n
= sscanf(token
, "%d", &rule_index
);
681 if (n
!= 1 || rule_index
< 0)
683 state
= STATE_READ_KEYWORD
;
686 case STATE_READ_DELETE
:
691 if (token_count
!= 2)
694 n
= sscanf(token
, "%d", &rule_to_delete
);
697 err
= RuleNumberDelete(rule_to_delete
);
703 case STATE_READ_PROTO
:
704 if (strcmp(token
, "tcp") == 0)
706 else if (strcmp(token
, "udp") == 0)
710 state
= STATE_READ_KEYWORD
;
722 while (*p
!= '/' && *p
!= 0)
728 err
= IpAddr(token
, &addr
);
735 char s
[sizeof(buffer
)];
738 n
= sscanf(token
, "%s %d", s
, &nbits
);
742 err
= IpAddr(s
, &addr
);
746 err
= IpMask(nbits
, &mask
);
751 if (state
== STATE_READ_SRC
)
762 state
= STATE_READ_KEYWORD
;
771 token
= strsep(&res
, " \t");
772 } while (token
!= NULL
&& !*token
);
774 #undef STATE_READ_KEYWORD
775 #undef STATE_READ_TYPE
776 #undef STATE_READ_PORT
777 #undef STATE_READ_SERVER
778 #undef STATE_READ_RULE
779 #undef STATE_READ_DELETE
780 #undef STATE_READ_PROTO
781 #undef STATE_READ_SRC
782 #undef STATE_READ_DST
784 /* Convert port strings to numbers. This needs to be done after
785 the string is parsed, because the prototype might not be designated
786 before the ports (which might be symbolic entries in /etc/services) */
788 if (strlen(str_port
) != 0)
792 err
= IpPort(str_port
, proto
, &proxy_port
);
801 if (strlen(str_server_port
) != 0)
805 err
= IpPort(str_server_port
, proto
, &server_port
);
814 /* Check that at least the server address has been defined */
815 if (server_addr
.s_addr
== 0)
818 /* Add to linked list */
819 proxy_entry
= malloc(sizeof(struct proxy_entry
));
820 if (proxy_entry
== NULL
)
823 proxy_entry
->proxy_type
= proxy_type
;
824 proxy_entry
->rule_index
= rule_index
;
825 proxy_entry
->proto
= proto
;
826 proxy_entry
->proxy_port
= htons(proxy_port
);
827 proxy_entry
->server_port
= htons(server_port
);
828 proxy_entry
->server_addr
= server_addr
;
829 proxy_entry
->src_addr
.s_addr
= src_addr
.s_addr
& src_mask
.s_addr
;
830 proxy_entry
->dst_addr
.s_addr
= dst_addr
.s_addr
& dst_mask
.s_addr
;
831 proxy_entry
->src_mask
= src_mask
;
832 proxy_entry
->dst_mask
= dst_mask
;
834 RuleAdd(proxy_entry
);