alias_proxy.c revision 84195
1/*-
2 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
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 84195 2001-09-30 21:03:33Z dillon $");
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
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}
838