3 This file encapsulates special operations related to transparent
4 proxy redirection. This is where packets with a particular destination,
5 usually tcp port 80, are redirected to a proxy server.
7 When packets are proxied, the destination address and port are
8 modified. In certain cases, it is necessary to somehow encode
9 the original address/port info into the packet. Two methods are
10 presently supported: addition of a [DEST addr port] string at the
11 beginning a of tcp stream, or inclusion of an optional field
14 There is one public API function:
16 PacketAliasProxyRule() -- Adds and deletes proxy
19 Rules are stored in a linear linked list, so lookup efficiency
20 won't be too good for large lists.
23 Initial development: April, 1998 (cjm)
34 #include <sys/types.h>
35 #include <sys/socket.h>
37 /* BSD IPV4 includes */
38 #include <netinet/in_systm.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/tcp.h>
43 #include <arpa/inet.h>
45 #include "alias_local.h" /* Functions used by alias*.c */
46 #include "alias.h" /* Public API functions for libalias */
55 * A linked list of arbitrary length, based on struct proxy_entry is
56 * used to store proxy rules.
60 #define PROXY_TYPE_ENCODE_NONE 1
61 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
62 #define PROXY_TYPE_ENCODE_IPHDR 3
69 struct in_addr server_addr
;
71 struct in_addr src_addr
;
72 struct in_addr src_mask
;
74 struct in_addr dst_addr
;
75 struct in_addr dst_mask
;
77 struct proxy_entry
*next
;
78 struct proxy_entry
*last
;
87 static struct proxy_entry
*proxyList
;
91 /* Local (static) functions:
93 IpMask() -- Utility function for creating IP
94 masks from integer (1-32) specification.
95 IpAddr() -- Utility function for converting string
97 IpPort() -- Utility function for converting string
99 RuleAdd() -- Adds an element to the rule list.
100 RuleDelete() -- Removes an element from the rule list.
101 RuleNumberDelete() -- Removes all elements from the rule list
102 having a certain rule number.
103 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
105 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
106 destination of a proxied IP packet
109 static int IpMask(int, struct in_addr
*);
110 static int IpAddr(char *, struct in_addr
*);
111 static int IpPort(char *, int, int *);
112 static void RuleAdd(struct proxy_entry
*);
113 static void RuleDelete(struct proxy_entry
*);
114 static int RuleNumberDelete(int);
115 static void ProxyEncodeTcpStream(struct alias_link
*, struct ip
*, int);
116 static void ProxyEncodeIpHeader(struct ip
*, int);
119 IpMask(int nbits
, struct in_addr
*mask
)
124 if (nbits
< 0 || nbits
> 32)
128 for (i
=0; i
<nbits
; i
++)
129 imask
= (imask
>> 1) + 0x80000000;
130 mask
->s_addr
= htonl(imask
);
136 IpAddr(char *s
, struct in_addr
*addr
)
138 if (inet_aton(s
, addr
) == 0)
145 IpPort(char *s
, int proto
, int *port
)
149 n
= sscanf(s
, "%d", port
);
154 if (proto
== IPPROTO_TCP
)
155 se
= getservbyname(s
, "tcp");
156 else if (proto
== IPPROTO_UDP
)
157 se
= getservbyname(s
, "udp");
164 *port
= (u_int
) ntohs(se
->s_port
);
171 RuleAdd(struct proxy_entry
*entry
)
174 struct proxy_entry
*ptr
;
175 struct proxy_entry
*ptr_last
;
177 if (proxyList
== NULL
)
185 rule_index
= entry
->rule_index
;
190 if (ptr
->rule_index
>= rule_index
)
192 if (ptr_last
== NULL
)
194 entry
->next
= proxyList
;
196 proxyList
->last
= entry
;
201 ptr_last
->next
= entry
;
203 entry
->last
= ptr
->last
;
211 ptr_last
->next
= entry
;
212 entry
->last
= ptr_last
;
217 RuleDelete(struct proxy_entry
*entry
)
219 if (entry
->last
!= NULL
)
220 entry
->last
->next
= entry
->next
;
222 proxyList
= entry
->next
;
224 if (entry
->next
!= NULL
)
225 entry
->next
->last
= entry
->last
;
231 RuleNumberDelete(int rule_index
)
234 struct proxy_entry
*ptr
;
240 struct proxy_entry
*ptr_next
;
242 ptr_next
= ptr
->next
;
243 if (ptr
->rule_index
== rule_index
)
256 ProxyEncodeTcpStream(struct alias_link
*link
,
264 /* Compute pointer to tcp header */
265 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
267 /* Don't modify if once already modified */
269 if (GetAckModified (link
))
272 /* Translate destination address and port to string form */
273 snprintf(buffer
, sizeof(buffer
) - 2, "[DEST %s %d]",
274 inet_ntoa(GetProxyAddress (link
)), (u_int
) ntohs(GetProxyPort (link
)));
276 /* Pad string out to a multiple of two in length */
277 slen
= strlen(buffer
);
281 strcat(buffer
, " \n");
285 strcat(buffer
, "\n");
289 /* Check for packet overflow */
290 if ((ntohs(pip
->ip_len
) + strlen(buffer
)) > maxpacketsize
)
293 /* Shift existing TCP data and insert destination string */
299 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
300 dlen
= ntohs (pip
->ip_len
) - hlen
;
302 /* Modify first packet that has data in it */
310 memmove(p
+ slen
, p
, dlen
);
311 memcpy(p
, buffer
, slen
);
314 /* Save information about modfied sequence number */
318 SetAckModified(link
);
319 delta
= GetDeltaSeqOut(pip
, link
);
320 AddSeq(pip
, link
, delta
+slen
);
323 /* Update IP header packet length and checksum */
327 accumulate
= pip
->ip_len
;
328 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + slen
);
329 accumulate
-= pip
->ip_len
;
331 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
334 /* Update TCP checksum, Use TcpChecksum since so many things have
338 tc
->th_sum
= TcpChecksum (pip
);
342 ProxyEncodeIpHeader(struct ip
*pip
,
345 #define OPTION_LEN_BYTES 8
346 #define OPTION_LEN_INT16 4
347 #define OPTION_LEN_INT32 2
348 u_char option
[OPTION_LEN_BYTES
];
351 fprintf(stdout
, " ip cksum 1 = %x\n", (u_int
) IpChecksum(pip
));
352 fprintf(stdout
, "tcp cksum 1 = %x\n", (u_int
) TcpChecksum(pip
));
355 /* Check to see that there is room to add an IP option */
356 if (pip
->ip_hl
> (0x0f - OPTION_LEN_INT32
))
359 /* Build option and copy into packet */
364 ptr
= (u_char
*) pip
;
366 memcpy(ptr
+ OPTION_LEN_BYTES
, ptr
, ntohs(pip
->ip_len
) - 20);
368 option
[0] = 0x64; /* class: 3 (reserved), option 4 */
369 option
[1] = OPTION_LEN_BYTES
;
371 memcpy(&option
[2], (u_char
*) &pip
->ip_dst
, 4);
373 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
374 memcpy(&option
[6], (u_char
*) &tc
->th_sport
, 2);
376 memcpy(ptr
, option
, 8);
379 /* Update checksum, header length and packet length */
385 sptr
= (u_short
*) option
;
387 for (i
=0; i
<OPTION_LEN_INT16
; i
++)
388 accumulate
-= *(sptr
++);
390 sptr
= (u_short
*) pip
;
392 pip
->ip_hl
+= OPTION_LEN_INT32
;
395 accumulate
+= pip
->ip_len
;
396 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + OPTION_LEN_BYTES
);
397 accumulate
-= pip
->ip_len
;
399 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
401 #undef OPTION_LEN_BYTES
402 #undef OPTION_LEN_INT16
403 #undef OPTION_LEN_INT32
405 fprintf(stdout
, " ip cksum 2 = %x\n", (u_int
) IpChecksum(pip
));
406 fprintf(stdout
, "tcp cksum 2 = %x\n", (u_int
) TcpChecksum(pip
));
411 /* Functions by other packet alias source files
413 ProxyCheck() -- Checks whether an outgoing packet should
415 ProxyModify() -- Encodes the original destination address/port
416 for a packet which is to be redirected to
421 ProxyCheck(struct ip
*pip
,
422 struct in_addr
*proxy_server_addr
,
423 u_short
*proxy_server_port
)
426 struct in_addr src_addr
;
427 struct in_addr dst_addr
;
428 struct proxy_entry
*ptr
;
430 src_addr
= pip
->ip_src
;
431 dst_addr
= pip
->ip_dst
;
432 dst_port
= ((struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2)))
440 proxy_port
= ptr
->proxy_port
;
441 if ((dst_port
== proxy_port
|| proxy_port
== 0)
442 && pip
->ip_p
== ptr
->proto
443 && src_addr
.s_addr
!= ptr
->server_addr
.s_addr
)
445 struct in_addr src_addr_masked
;
446 struct in_addr dst_addr_masked
;
448 src_addr_masked
.s_addr
= src_addr
.s_addr
& ptr
->src_mask
.s_addr
;
449 dst_addr_masked
.s_addr
= dst_addr
.s_addr
& ptr
->dst_mask
.s_addr
;
451 if ((src_addr_masked
.s_addr
== ptr
->src_addr
.s_addr
)
452 && (dst_addr_masked
.s_addr
== ptr
->dst_addr
.s_addr
))
454 if ((*proxy_server_port
= ptr
->server_port
) == 0)
455 *proxy_server_port
= dst_port
;
456 *proxy_server_addr
= ptr
->server_addr
;
457 return ptr
->proxy_type
;
467 ProxyModify(struct alias_link
*link
,
474 case PROXY_TYPE_ENCODE_IPHDR
:
475 ProxyEncodeIpHeader(pip
, maxpacketsize
);
478 case PROXY_TYPE_ENCODE_TCPSTREAM
:
479 ProxyEncodeTcpStream(link
, pip
, maxpacketsize
);
490 PacketAliasProxyRule(const char *cmd
)
493 * This function takes command strings of the form:
495 * server <addr>[:<port>]
501 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
503 * delete <rule number>
505 * Subfields can be in arbitrary order. Port numbers and addresses
506 * must be in either numeric or symbolic form. An optional rule number
507 * is used to control the order in which rules are searched. If two
508 * rules have the same number, then search order cannot be guaranteed,
509 * and the rules should be disjoint. If no rule number is specified,
510 * then 0 is used, and group 0 rules are always checked before any
519 char str_port
[sizeof(buffer
)];
520 char str_server_port
[sizeof(buffer
)];
527 struct in_addr server_addr
;
528 struct in_addr src_addr
, src_mask
;
529 struct in_addr dst_addr
, dst_mask
;
530 struct proxy_entry
*proxy_entry
;
532 /* Copy command line into a buffer */
533 cmd_len
= strlen(cmd
);
534 if (cmd_len
> (sizeof(buffer
) - 1))
538 /* Convert to lower case */
539 len
= strlen(buffer
);
540 for (i
=0; i
<len
; i
++)
541 buffer
[i
] = tolower(buffer
[i
]);
543 /* Set default proxy type */
545 /* Set up default values */
547 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
550 server_addr
.s_addr
= 0;
553 IpMask(0, &src_mask
);
555 IpMask(0, &dst_mask
);
558 str_server_port
[0] = 0;
560 /* Parse command string with state machine */
561 #define STATE_READ_KEYWORD 0
562 #define STATE_READ_TYPE 1
563 #define STATE_READ_PORT 2
564 #define STATE_READ_SERVER 3
565 #define STATE_READ_RULE 4
566 #define STATE_READ_DELETE 5
567 #define STATE_READ_PROTO 6
568 #define STATE_READ_SRC 7
569 #define STATE_READ_DST 8
570 state
= STATE_READ_KEYWORD
;
571 token
= strtok(buffer
, " \t");
573 while (token
!= NULL
)
578 case STATE_READ_KEYWORD
:
579 if (strcmp(token
, "type") == 0)
580 state
= STATE_READ_TYPE
;
581 else if (strcmp(token
, "port") == 0)
582 state
= STATE_READ_PORT
;
583 else if (strcmp(token
, "server") == 0)
584 state
= STATE_READ_SERVER
;
585 else if (strcmp(token
, "rule") == 0)
586 state
= STATE_READ_RULE
;
587 else if (strcmp(token
, "delete") == 0)
588 state
= STATE_READ_DELETE
;
589 else if (strcmp(token
, "proto") == 0)
590 state
= STATE_READ_PROTO
;
591 else if (strcmp(token
, "src") == 0)
592 state
= STATE_READ_SRC
;
593 else if (strcmp(token
, "dst") == 0)
594 state
= STATE_READ_DST
;
599 case STATE_READ_TYPE
:
600 if (strcmp(token
, "encode_ip_hdr") == 0)
601 proxy_type
= PROXY_TYPE_ENCODE_IPHDR
;
602 else if (strcmp(token
, "encode_tcp_stream") == 0)
603 proxy_type
= PROXY_TYPE_ENCODE_TCPSTREAM
;
604 else if (strcmp(token
, "no_encode") == 0)
605 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
608 state
= STATE_READ_KEYWORD
;
611 case STATE_READ_PORT
:
612 strcpy(str_port
, token
);
613 state
= STATE_READ_KEYWORD
;
616 case STATE_READ_SERVER
:
620 char s
[sizeof(buffer
)];
623 while (*p
!= ':' && *p
!= 0)
628 err
= IpAddr(token
, &server_addr
);
636 n
= sscanf(token
, "%s %s", s
, str_server_port
);
640 err
= IpAddr(s
, &server_addr
);
645 state
= STATE_READ_KEYWORD
;
648 case STATE_READ_RULE
:
649 n
= sscanf(token
, "%d", &rule_index
);
650 if (n
!= 1 || rule_index
< 0)
652 state
= STATE_READ_KEYWORD
;
655 case STATE_READ_DELETE
:
660 if (token_count
!= 2)
663 n
= sscanf(token
, "%d", &rule_to_delete
);
666 err
= RuleNumberDelete(rule_to_delete
);
672 case STATE_READ_PROTO
:
673 if (strcmp(token
, "tcp") == 0)
675 else if (strcmp(token
, "udp") == 0)
679 state
= STATE_READ_KEYWORD
;
691 while (*p
!= '/' && *p
!= 0)
697 err
= IpAddr(token
, &addr
);
705 char s
[sizeof(buffer
)];
708 n
= sscanf(token
, "%s %d", s
, &nbits
);
712 err
= IpAddr(s
, &addr
);
716 err
= IpMask(nbits
, &mask
);
721 if (state
== STATE_READ_SRC
)
732 state
= STATE_READ_KEYWORD
;
740 token
= strtok(NULL
, " \t");
742 #undef STATE_READ_KEYWORD
743 #undef STATE_READ_TYPE
744 #undef STATE_READ_PORT
745 #undef STATE_READ_SERVER
746 #undef STATE_READ_RULE
747 #undef STATE_READ_DELETE
748 #undef STATE_READ_PROTO
749 #undef STATE_READ_SRC
750 #undef STATE_READ_DST
752 /* Convert port strings to numbers. This needs to be done after
753 the string is parsed, because the prototype might not be designated
754 before the ports (which might be symbolic entries in /etc/services) */
756 if (strlen(str_port
) != 0)
760 err
= IpPort(str_port
, proto
, &proxy_port
);
769 if (strlen(str_server_port
) != 0)
773 err
= IpPort(str_server_port
, proto
, &server_port
);
782 /* Check that at least the server address has been defined */
783 if (server_addr
.s_addr
== 0)
786 /* Add to linked list */
787 proxy_entry
= malloc(sizeof(struct proxy_entry
));
788 if (proxy_entry
== NULL
)
791 proxy_entry
->proxy_type
= proxy_type
;
792 proxy_entry
->rule_index
= rule_index
;
793 proxy_entry
->proto
= proto
;
794 proxy_entry
->proxy_port
= htons(proxy_port
);
795 proxy_entry
->server_port
= htons(server_port
);
796 proxy_entry
->server_addr
= server_addr
;
797 proxy_entry
->src_addr
.s_addr
= src_addr
.s_addr
& src_mask
.s_addr
;
798 proxy_entry
->dst_addr
.s_addr
= dst_addr
.s_addr
& dst_mask
.s_addr
;
799 proxy_entry
->src_mask
= src_mask
;
800 proxy_entry
->dst_mask
= dst_mask
;
802 RuleAdd(proxy_entry
);