Deleted Added
full compact
1/*-
2 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_proxy.c 99207 2002-07-01 11:19:40Z brian $");
28__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_proxy.c 108533 2003-01-01 18:49:04Z schweikh $");
29
30/* file: alias_proxy.c
31
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.
35
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
40 beginning of a tcp stream, or inclusion of an optional field
41 in the IP header.
42
43 There is one public API function:
44
45 PacketAliasProxyRule() -- Adds and deletes proxy
46 rules.
47
48 Rules are stored in a linear linked list, so lookup efficiency
49 won't be too good for large lists.
50
51
52 Initial development: April, 1998 (cjm)
53*/
54
55
56/* System includes */
57#include <ctype.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <netdb.h>
62
63#include <sys/types.h>
64#include <sys/socket.h>
65
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>
71
72#include <arpa/inet.h>
73
74#include "alias_local.h" /* Functions used by alias*.c */
75#include "alias.h" /* Public API functions for libalias */
76
77
78
79/*
80 Data structures
81 */
82
83/*
84 * A linked list of arbitrary length, based on struct proxy_entry is
85 * used to store proxy rules.
86 */
87struct proxy_entry
88{
89#define PROXY_TYPE_ENCODE_NONE 1
90#define PROXY_TYPE_ENCODE_TCPSTREAM 2
91#define PROXY_TYPE_ENCODE_IPHDR 3
92 int rule_index;
93 int proxy_type;
94 u_char proto;
95 u_short proxy_port;
96 u_short server_port;
97
98 struct in_addr server_addr;
99
100 struct in_addr src_addr;
101 struct in_addr src_mask;
102
103 struct in_addr dst_addr;
104 struct in_addr dst_mask;
105
106 struct proxy_entry *next;
107 struct proxy_entry *last;
108};
109
110
111
112/*
113 File scope variables
114*/
115
116static struct proxy_entry *proxyList;
117
118
119
120/* Local (static) functions:
121
122 IpMask() -- Utility function for creating IP
123 masks from integer (1-32) specification.
124 IpAddr() -- Utility function for converting string
125 to IP address
126 IpPort() -- Utility function for converting string
127 to port number
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
133 of a TCP stream.
134 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
135 destination of a proxied IP packet
136*/
137
138static int IpMask(int, struct in_addr *);
139static int IpAddr(char *, struct in_addr *);
140static int IpPort(char *, int, int *);
141static void RuleAdd(struct proxy_entry *);
142static void RuleDelete(struct proxy_entry *);
143static int RuleNumberDelete(int);
144static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
145static void ProxyEncodeIpHeader(struct ip *, int);
146
147static int
148IpMask(int nbits, struct in_addr *mask)
149{
150 int i;
151 u_int imask;
152
153 if (nbits < 0 || nbits > 32)
154 return -1;
155
156 imask = 0;
157 for (i=0; i<nbits; i++)
158 imask = (imask >> 1) + 0x80000000;
159 mask->s_addr = htonl(imask);
160
161 return 0;
162}
163
164static int
165IpAddr(char *s, struct in_addr *addr)
166{
167 if (inet_aton(s, addr) == 0)
168 return -1;
169 else
170 return 0;
171}
172
173static int
174IpPort(char *s, int proto, int *port)
175{
176 int n;
177
178 n = sscanf(s, "%d", port);
179 if (n != 1)
180 {
181 struct servent *se;
182
183 if (proto == IPPROTO_TCP)
184 se = getservbyname(s, "tcp");
185 else if (proto == IPPROTO_UDP)
186 se = getservbyname(s, "udp");
187 else
188 return -1;
189
190 if (se == NULL)
191 return -1;
192
193 *port = (u_int) ntohs(se->s_port);
194 }
195
196 return 0;
197}
198
199void
200RuleAdd(struct proxy_entry *entry)
201{
202 int rule_index;
203 struct proxy_entry *ptr;
204 struct proxy_entry *ptr_last;
205
206 if (proxyList == NULL)
207 {
208 proxyList = entry;
209 entry->last = NULL;
210 entry->next = NULL;
211 return;
212 }
213
214 rule_index = entry->rule_index;
215 ptr = proxyList;
216 ptr_last = NULL;
217 while (ptr != NULL)
218 {
219 if (ptr->rule_index >= rule_index)
220 {
221 if (ptr_last == NULL)
222 {
223 entry->next = proxyList;
224 entry->last = NULL;
225 proxyList->last = entry;
226 proxyList = entry;
227 return;
228 }
229
230 ptr_last->next = entry;
231 ptr->last = entry;
232 entry->last = ptr->last;
233 entry->next = ptr;
234 return;
235 }
236 ptr_last = ptr;
237 ptr = ptr->next;
238 }
239
240 ptr_last->next = entry;
241 entry->last = ptr_last;
242 entry->next = NULL;
243}
244
245static void
246RuleDelete(struct proxy_entry *entry)
247{
248 if (entry->last != NULL)
249 entry->last->next = entry->next;
250 else
251 proxyList = entry->next;
252
253 if (entry->next != NULL)
254 entry->next->last = entry->last;
255
256 free(entry);
257}
258
259static int
260RuleNumberDelete(int rule_index)
261{
262 int err;
263 struct proxy_entry *ptr;
264
265 err = -1;
266 ptr = proxyList;
267 while (ptr != NULL)
268 {
269 struct proxy_entry *ptr_next;
270
271 ptr_next = ptr->next;
272 if (ptr->rule_index == rule_index)
273 {
274 err = 0;
275 RuleDelete(ptr);
276 }
277
278 ptr = ptr_next;
279 }
280
281 return err;
282}
283
284static void
285ProxyEncodeTcpStream(struct alias_link *link,
286 struct ip *pip,
287 int maxpacketsize)
288{
289 int slen;
290 char buffer[40];
291 struct tcphdr *tc;
292
293/* Compute pointer to tcp header */
294 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
295
296/* Don't modify if once already modified */
297
298 if (GetAckModified (link))
299 return;
300
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)));
304
305/* Pad string out to a multiple of two in length */
306 slen = strlen(buffer);
307 switch (slen % 2)
308 {
309 case 0:
310 strcat(buffer, " \n");
311 slen += 2;
312 break;
313 case 1:
314 strcat(buffer, "\n");
315 slen += 1;
316 }
317
318/* Check for packet overflow */
319 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
320 return;
321
322/* Shift existing TCP data and insert destination string */
323 {
324 int dlen;
325 int hlen;
326 u_char *p;
327
328 hlen = (pip->ip_hl + tc->th_off) << 2;
329 dlen = ntohs (pip->ip_len) - hlen;
330
331/* Modify first packet that has data in it */
332
333 if (dlen == 0)
334 return;
335
336 p = (char *) pip;
337 p += hlen;
338
339 memmove(p + slen, p, dlen);
340 memcpy(p, buffer, slen);
341 }
342
343/* Save information about modfied sequence number */
344 {
345 int delta;
346
347 SetAckModified(link);
348 delta = GetDeltaSeqOut(pip, link);
349 AddSeq(pip, link, delta+slen);
350 }
351
352/* Update IP header packet length and checksum */
353 {
354 int accumulate;
355
356 accumulate = pip->ip_len;
357 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
358 accumulate -= pip->ip_len;
359
360 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
361 }
362
363/* Update TCP checksum, Use TcpChecksum since so many things have
364 already changed. */
365
366 tc->th_sum = 0;
367 tc->th_sum = TcpChecksum (pip);
368}
369
370static void
371ProxyEncodeIpHeader(struct ip *pip,
372 int maxpacketsize)
373{
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];
378
379#ifdef DEBUG
380 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
381 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
382#endif
383
384/* Check to see that there is room to add an IP option */
385 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
386 return;
387
388/* Build option and copy into packet */
389 {
390 u_char *ptr;
391 struct tcphdr *tc;
392
393 ptr = (u_char *) pip;
394 ptr += 20;
395 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
396
397 option[0] = 0x64; /* class: 3 (reserved), option 4 */
398 option[1] = OPTION_LEN_BYTES;
399
400 memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
401
402 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
403 memcpy(&option[6], (u_char *) &tc->th_sport, 2);
404
405 memcpy(ptr, option, 8);
406 }
407
408/* Update checksum, header length and packet length */
409 {
410 int i;
411 int accumulate;
412 u_short *sptr;
413
414 sptr = (u_short *) option;
415 accumulate = 0;
416 for (i=0; i<OPTION_LEN_INT16; i++)
417 accumulate -= *(sptr++);
418
419 sptr = (u_short *) pip;
420 accumulate += *sptr;
421 pip->ip_hl += OPTION_LEN_INT32;
422 accumulate -= *sptr;
423
424 accumulate += pip->ip_len;
425 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
426 accumulate -= pip->ip_len;
427
428 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
429 }
430#undef OPTION_LEN_BYTES
431#undef OPTION_LEN_INT16
432#undef OPTION_LEN_INT32
433#ifdef DEBUG
434 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
435 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
436#endif
437}
438
439
440/* Functions by other packet alias source files
441
442 ProxyCheck() -- Checks whether an outgoing packet should
443 be proxied.
444 ProxyModify() -- Encodes the original destination address/port
445 for a packet which is to be redirected to
446 a proxy server.
447*/
448
449int
450ProxyCheck(struct ip *pip,
451 struct in_addr *proxy_server_addr,
452 u_short *proxy_server_port)
453{
454 u_short dst_port;
455 struct in_addr src_addr;
456 struct in_addr dst_addr;
457 struct proxy_entry *ptr;
458
459 src_addr = pip->ip_src;
460 dst_addr = pip->ip_dst;
461 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
462 ->th_dport;
463
464 ptr = proxyList;
465 while (ptr != NULL)
466 {
467 u_short proxy_port;
468
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)
473 {
474 struct in_addr src_addr_masked;
475 struct in_addr dst_addr_masked;
476
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;
479
480 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
481 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr))
482 {
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;
487 }
488 }
489 ptr = ptr->next;
490 }
491
492 return 0;
493}
494
495void
496ProxyModify(struct alias_link *link,
497 struct ip *pip,
498 int maxpacketsize,
499 int proxy_type)
500{
501 switch (proxy_type)
502 {
503 case PROXY_TYPE_ENCODE_IPHDR:
504 ProxyEncodeIpHeader(pip, maxpacketsize);
505 break;
506
507 case PROXY_TYPE_ENCODE_TCPSTREAM:
508 ProxyEncodeTcpStream(link, pip, maxpacketsize);
509 break;
510 }
511}
512
513
514/*
515 Public API functions
516*/
517
518int
519PacketAliasProxyRule(const char *cmd)
520{
521/*
522 * This function takes command strings of the form:
523 *
524 * server <addr>[:<port>]
525 * [port <port>]
526 * [rule n]
527 * [proto tcp|udp]
528 * [src <addr>[/n]]
529 * [dst <addr>[/n]]
530 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
531 *
532 * delete <rule number>
533 *
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
540 * others.
541 */
542 int i, n, len;
543 int cmd_len;
544 int token_count;
545 int state;
546 char *token;
547 char buffer[256];
548 char str_port[sizeof(buffer)];
549 char str_server_port[sizeof(buffer)];
550 char *res = buffer;
551
552 int rule_index;
553 int proto;
554 int proxy_type;
555 int proxy_port;
556 int server_port;
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;
561
562/* Copy command line into a buffer */
563 cmd += strspn(cmd, " \t");
564 cmd_len = strlen(cmd);
565 if (cmd_len > (sizeof(buffer) - 1))
566 return -1;
567 strcpy(buffer, cmd);
568
569/* Convert to lower case */
570 len = strlen(buffer);
571 for (i=0; i<len; i++)
572 buffer[i] = tolower((unsigned char)buffer[i]);
573
574/* Set default proxy type */
575
576/* Set up default values */
577 rule_index = 0;
578 proxy_type = PROXY_TYPE_ENCODE_NONE;
579 proto = IPPROTO_TCP;
580 proxy_port = 0;
581 server_addr.s_addr = 0;
582 server_port = 0;
583 src_addr.s_addr = 0;
584 IpMask(0, &src_mask);
585 dst_addr.s_addr = 0;
586 IpMask(0, &dst_mask);
587
588 str_port[0] = 0;
589 str_server_port[0] = 0;
590
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");
603 token_count = 0;
604 while (token != NULL)
605 {
606 token_count++;
607 switch (state)
608 {
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;
626 else
627 return -1;
628 break;
629
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;
637 else
638 return -1;
639 state = STATE_READ_KEYWORD;
640 break;
641
642 case STATE_READ_PORT:
643 strcpy(str_port, token);
644 state = STATE_READ_KEYWORD;
645 break;
646
647 case STATE_READ_SERVER:
648 {
649 int err;
650 char *p;
651 char s[sizeof(buffer)];
652
653 p = token;
654 while (*p != ':' && *p != 0)
655 p++;
656
657 if (*p != ':')
658 {
659 err = IpAddr(token, &server_addr);
660 if (err)
661 return -1;
662 }
663 else
664 {
665 *p = ' ';
666
667 n = sscanf(token, "%s %s", s, str_server_port);
668 if (n != 2)
669 return -1;
670
671 err = IpAddr(s, &server_addr);
672 if (err)
673 return -1;
674 }
675 }
676 state = STATE_READ_KEYWORD;
677 break;
678
679 case STATE_READ_RULE:
680 n = sscanf(token, "%d", &rule_index);
681 if (n != 1 || rule_index < 0)
682 return -1;
683 state = STATE_READ_KEYWORD;
684 break;
685
686 case STATE_READ_DELETE:
687 {
688 int err;
689 int rule_to_delete;
690
691 if (token_count != 2)
692 return -1;
693
694 n = sscanf(token, "%d", &rule_to_delete);
695 if (n != 1)
696 return -1;
697 err = RuleNumberDelete(rule_to_delete);
698 if (err)
699 return -1;
700 return 0;
701 }
702
703 case STATE_READ_PROTO:
704 if (strcmp(token, "tcp") == 0)
705 proto = IPPROTO_TCP;
706 else if (strcmp(token, "udp") == 0)
707 proto = IPPROTO_UDP;
708 else
709 return -1;
710 state = STATE_READ_KEYWORD;
711 break;
712
713 case STATE_READ_SRC:
714 case STATE_READ_DST:
715 {
716 int err;
717 char *p;
718 struct in_addr mask;
719 struct in_addr addr;
720
721 p = token;
722 while (*p != '/' && *p != 0)
723 p++;
724
725 if (*p != '/')
726 {
727 IpMask(32, &mask);
728 err = IpAddr(token, &addr);
729 if (err)
730 return -1;
731 }
732 else
733 {
734 int nbits;
735 char s[sizeof(buffer)];
736
737 *p = ' ';
738 n = sscanf(token, "%s %d", s, &nbits);
739 if (n != 2)
740 return -1;
741
742 err = IpAddr(s, &addr);
743 if (err)
744 return -1;
745
746 err = IpMask(nbits, &mask);
747 if (err)
748 return -1;
749 }
750
751 if (state == STATE_READ_SRC)
752 {
753 src_addr = addr;
754 src_mask = mask;
755 }
756 else
757 {
758 dst_addr = addr;
759 dst_mask = mask;
760 }
761 }
762 state = STATE_READ_KEYWORD;
763 break;
764
765 default:
766 return -1;
767 break;
768 }
769
770 do {
771 token = strsep(&res, " \t");
772 } while (token != NULL && !*token);
773 }
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
783
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) */
787
788 if (strlen(str_port) != 0)
789 {
790 int err;
791
792 err = IpPort(str_port, proto, &proxy_port);
793 if (err)
794 return -1;
795 }
796 else
797 {
798 proxy_port = 0;
799 }
800
801 if (strlen(str_server_port) != 0)
802 {
803 int err;
804
805 err = IpPort(str_server_port, proto, &server_port);
806 if (err)
807 return -1;
808 }
809 else
810 {
811 server_port = 0;
812 }
813
814/* Check that at least the server address has been defined */
815 if (server_addr.s_addr == 0)
816 return -1;
817
818/* Add to linked list */
819 proxy_entry = malloc(sizeof(struct proxy_entry));
820 if (proxy_entry == NULL)
821 return -1;
822
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;
833
834 RuleAdd(proxy_entry);
835
836 return 0;
837}