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)
36 #include <sys/types.h>
37 #include <sys/socket.h>
39 /* BSD IPV4 includes */
40 #include <netinet/in_systm.h>
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/tcp.h>
45 #include <arpa/inet.h>
47 #include "alias_local.h" /* Functions used by alias*.c */
48 #include "alias.h" /* Public API functions for libalias */
57 * A linked list of arbitrary length, based on struct proxy_entry is
58 * used to store proxy rules.
62 #define PROXY_TYPE_ENCODE_NONE 1
63 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
64 #define PROXY_TYPE_ENCODE_IPHDR 3
71 struct in_addr server_addr;
73 struct in_addr src_addr;
74 struct in_addr src_mask;
76 struct in_addr dst_addr;
77 struct in_addr dst_mask;
79 struct proxy_entry *next;
80 struct proxy_entry *last;
89 static struct proxy_entry *proxyList;
93 /* Local (static) functions:
95 IpMask() -- Utility function for creating IP
96 masks from integer (1-32) specification.
97 IpAddr() -- Utility function for converting string
99 IpPort() -- Utility function for converting string
101 RuleAdd() -- Adds an element to the rule list.
102 RuleDelete() -- Removes an element from the rule list.
103 RuleNumberDelete() -- Removes all elements from the rule list
104 having a certain rule number.
105 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
107 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
108 destination of a proxied IP packet
111 static int IpMask(int, struct in_addr *);
112 static int IpAddr(char *, struct in_addr *);
113 static int IpPort(char *, int, int *);
114 static void RuleAdd(struct proxy_entry *);
115 static void RuleDelete(struct proxy_entry *);
116 static int RuleNumberDelete(int);
117 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
118 static void ProxyEncodeIpHeader(struct ip *, int);
121 IpMask(int nbits, struct in_addr *mask)
126 if (nbits < 0 || nbits > 32)
130 for (i=0; i<nbits; i++)
131 imask = (imask >> 1) + 0x80000000;
132 mask->s_addr = htonl(imask);
138 IpAddr(char *s, struct in_addr *addr)
140 if (inet_aton(s, addr) == 0)
147 IpPort(char *s, int proto, int *port)
151 n = sscanf(s, "%d", port);
156 if (proto == IPPROTO_TCP)
157 se = getservbyname(s, "tcp");
158 else if (proto == IPPROTO_UDP)
159 se = getservbyname(s, "udp");
166 *port = (u_int) ntohs(se->s_port);
173 RuleAdd(struct proxy_entry *entry)
176 struct proxy_entry *ptr;
177 struct proxy_entry *ptr_last;
179 if (proxyList == NULL)
187 rule_index = entry->rule_index;
192 if (ptr->rule_index >= rule_index)
194 if (ptr_last == NULL)
196 entry->next = proxyList;
198 proxyList->last = entry;
203 ptr_last->next = entry;
205 entry->last = ptr->last;
213 ptr_last->next = entry;
214 entry->last = ptr_last;
219 RuleDelete(struct proxy_entry *entry)
221 if (entry->last != NULL)
222 entry->last->next = entry->next;
224 proxyList = entry->next;
226 if (entry->next != NULL)
227 entry->next->last = entry->last;
233 RuleNumberDelete(int rule_index)
236 struct proxy_entry *ptr;
242 struct proxy_entry *ptr_next;
244 ptr_next = ptr->next;
245 if (ptr->rule_index == rule_index)
258 ProxyEncodeTcpStream(struct alias_link *link,
266 /* Compute pointer to tcp header */
267 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
269 /* Don't modify if once already modified */
271 if (GetAckModified (link))
274 /* Translate destination address and port to string form */
275 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
276 inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link)));
278 /* Pad string out to a multiple of two in length */
279 slen = strlen(buffer);
283 strcat(buffer, " \n");
287 strcat(buffer, "\n");
291 /* Check for packet overflow */
292 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
295 /* Shift existing TCP data and insert destination string */
301 hlen = (pip->ip_hl + tc->th_off) << 2;
302 dlen = ntohs (pip->ip_len) - hlen;
304 /* Modify first packet that has data in it */
312 memmove(p + slen, p, dlen);
313 memcpy(p, buffer, slen);
316 /* Save information about modfied sequence number */
320 SetAckModified(link);
321 delta = GetDeltaSeqOut(pip, link);
322 AddSeq(pip, link, delta+slen);
325 /* Update IP header packet length and checksum */
329 accumulate = pip->ip_len;
330 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
331 accumulate -= pip->ip_len;
333 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
336 /* Update TCP checksum, Use TcpChecksum since so many things have
340 tc->th_sum = TcpChecksum (pip);
344 ProxyEncodeIpHeader(struct ip *pip,
347 #define OPTION_LEN_BYTES 8
348 #define OPTION_LEN_INT16 4
349 #define OPTION_LEN_INT32 2
350 u_char option[OPTION_LEN_BYTES];
353 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
354 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
357 /* Check to see that there is room to add an IP option */
358 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
361 /* Build option and copy into packet */
366 ptr = (u_char *) pip;
368 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
370 option[0] = 0x64; /* class: 3 (reserved), option 4 */
371 option[1] = OPTION_LEN_BYTES;
373 memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
375 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
376 memcpy(&option[6], (u_char *) &tc->th_sport, 2);
378 memcpy(ptr, option, 8);
381 /* Update checksum, header length and packet length */
387 sptr = (u_short *) option;
389 for (i=0; i<OPTION_LEN_INT16; i++)
390 accumulate -= *(sptr++);
392 sptr = (u_short *) pip;
394 pip->ip_hl += OPTION_LEN_INT32;
397 accumulate += pip->ip_len;
398 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
399 accumulate -= pip->ip_len;
401 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
403 #undef OPTION_LEN_BYTES
404 #undef OPTION_LEN_INT16
405 #undef OPTION_LEN_INT32
407 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
408 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
413 /* Functions by other packet alias source files
415 ProxyCheck() -- Checks whether an outgoing packet should
417 ProxyModify() -- Encodes the original destination address/port
418 for a packet which is to be redirected to
423 ProxyCheck(struct ip *pip,
424 struct in_addr *proxy_server_addr,
425 u_short *proxy_server_port)
428 struct in_addr src_addr;
429 struct in_addr dst_addr;
430 struct proxy_entry *ptr;
432 src_addr = pip->ip_src;
433 dst_addr = pip->ip_dst;
434 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
442 proxy_port = ptr->proxy_port;
443 if ((dst_port == proxy_port || proxy_port == 0)
444 && pip->ip_p == ptr->proto
445 && src_addr.s_addr != ptr->server_addr.s_addr)
447 struct in_addr src_addr_masked;
448 struct in_addr dst_addr_masked;
450 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
451 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
453 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
454 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr))
456 if ((*proxy_server_port = ptr->server_port) == 0)
457 *proxy_server_port = dst_port;
458 *proxy_server_addr = ptr->server_addr;
459 return ptr->proxy_type;
469 ProxyModify(struct alias_link *link,
476 case PROXY_TYPE_ENCODE_IPHDR:
477 ProxyEncodeIpHeader(pip, maxpacketsize);
480 case PROXY_TYPE_ENCODE_TCPSTREAM:
481 ProxyEncodeTcpStream(link, pip, maxpacketsize);
492 PacketAliasProxyRule(const char *cmd)
495 * This function takes command strings of the form:
497 * server <addr>[:<port>]
503 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
505 * delete <rule number>
507 * Subfields can be in arbitrary order. Port numbers and addresses
508 * must be in either numeric or symbolic form. An optional rule number
509 * is used to control the order in which rules are searched. If two
510 * rules have the same number, then search order cannot be guaranteed,
511 * and the rules should be disjoint. If no rule number is specified,
512 * then 0 is used, and group 0 rules are always checked before any
521 char str_port[sizeof(buffer)];
522 char str_server_port[sizeof(buffer)];
529 struct in_addr server_addr;
530 struct in_addr src_addr, src_mask;
531 struct in_addr dst_addr, dst_mask;
532 struct proxy_entry *proxy_entry;
534 /* Copy command line into a buffer */
535 cmd_len = strlen(cmd);
536 if (cmd_len > (sizeof(buffer) - 1))
540 /* Convert to lower case */
541 len = strlen(buffer);
542 for (i=0; i<len; i++)
543 buffer[i] = tolower(buffer[i]);
545 /* Set default proxy type */
547 /* Set up default values */
549 proxy_type = PROXY_TYPE_ENCODE_NONE;
552 server_addr.s_addr = 0;
555 IpMask(0, &src_mask);
557 IpMask(0, &dst_mask);
560 str_server_port[0] = 0;
562 /* Parse command string with state machine */
563 #define STATE_READ_KEYWORD 0
564 #define STATE_READ_TYPE 1
565 #define STATE_READ_PORT 2
566 #define STATE_READ_SERVER 3
567 #define STATE_READ_RULE 4
568 #define STATE_READ_DELETE 5
569 #define STATE_READ_PROTO 6
570 #define STATE_READ_SRC 7
571 #define STATE_READ_DST 8
572 state = STATE_READ_KEYWORD;
573 token = strtok(buffer, " \t");
575 while (token != NULL)
580 case STATE_READ_KEYWORD:
581 if (strcmp(token, "type") == 0)
582 state = STATE_READ_TYPE;
583 else if (strcmp(token, "port") == 0)
584 state = STATE_READ_PORT;
585 else if (strcmp(token, "server") == 0)
586 state = STATE_READ_SERVER;
587 else if (strcmp(token, "rule") == 0)
588 state = STATE_READ_RULE;
589 else if (strcmp(token, "delete") == 0)
590 state = STATE_READ_DELETE;
591 else if (strcmp(token, "proto") == 0)
592 state = STATE_READ_PROTO;
593 else if (strcmp(token, "src") == 0)
594 state = STATE_READ_SRC;
595 else if (strcmp(token, "dst") == 0)
596 state = STATE_READ_DST;
601 case STATE_READ_TYPE:
602 if (strcmp(token, "encode_ip_hdr") == 0)
603 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
604 else if (strcmp(token, "encode_tcp_stream") == 0)
605 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
606 else if (strcmp(token, "no_encode") == 0)
607 proxy_type = PROXY_TYPE_ENCODE_NONE;
610 state = STATE_READ_KEYWORD;
613 case STATE_READ_PORT:
614 strcpy(str_port, token);
615 state = STATE_READ_KEYWORD;
618 case STATE_READ_SERVER:
622 char s[sizeof(buffer)];
625 while (*p != ':' && *p != 0)
630 err = IpAddr(token, &server_addr);
638 n = sscanf(token, "%s %s", s, str_server_port);
642 err = IpAddr(s, &server_addr);
647 state = STATE_READ_KEYWORD;
650 case STATE_READ_RULE:
651 n = sscanf(token, "%d", &rule_index);
652 if (n != 1 || rule_index < 0)
654 state = STATE_READ_KEYWORD;
657 case STATE_READ_DELETE:
662 if (token_count != 2)
665 n = sscanf(token, "%d", &rule_to_delete);
668 err = RuleNumberDelete(rule_to_delete);
674 case STATE_READ_PROTO:
675 if (strcmp(token, "tcp") == 0)
677 else if (strcmp(token, "udp") == 0)
681 state = STATE_READ_KEYWORD;
693 while (*p != '/' && *p != 0)
699 err = IpAddr(token, &addr);
707 char s[sizeof(buffer)];
710 n = sscanf(token, "%s %d", s, &nbits);
714 err = IpAddr(s, &addr);
718 err = IpMask(nbits, &mask);
723 if (state == STATE_READ_SRC)
734 state = STATE_READ_KEYWORD;
742 token = strtok(NULL, " \t");
744 #undef STATE_READ_KEYWORD
745 #undef STATE_READ_TYPE
746 #undef STATE_READ_PORT
747 #undef STATE_READ_SERVER
748 #undef STATE_READ_RULE
749 #undef STATE_READ_DELETE
750 #undef STATE_READ_PROTO
751 #undef STATE_READ_SRC
752 #undef STATE_READ_DST
754 /* Convert port strings to numbers. This needs to be done after
755 the string is parsed, because the prototype might not be designated
756 before the ports (which might be symbolic entries in /etc/services) */
758 if (strlen(str_port) != 0)
762 err = IpPort(str_port, proto, &proxy_port);
771 if (strlen(str_server_port) != 0)
775 err = IpPort(str_server_port, proto, &server_port);
784 /* Check that at least the server address has been defined */
785 if (server_addr.s_addr == 0)
788 /* Add to linked list */
789 proxy_entry = malloc(sizeof(struct proxy_entry));
790 if (proxy_entry == NULL)
793 proxy_entry->proxy_type = proxy_type;
794 proxy_entry->rule_index = rule_index;
795 proxy_entry->proto = proto;
796 proxy_entry->proxy_port = htons(proxy_port);
797 proxy_entry->server_port = htons(server_port);
798 proxy_entry->server_addr = server_addr;
799 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
800 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
801 proxy_entry->src_mask = src_mask;
802 proxy_entry->dst_mask = dst_mask;
804 RuleAdd(proxy_entry);